Skip to content

Commit

Permalink
Standalone build support (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin authored Oct 20, 2021
1 parent 4c1e821 commit d9e8c6d
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 19 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ Some assorted tips for people who want to fork this project (it's really easy):
- Set the environment variable ENABLE_SERVICE_WORKER=1 to enable service worker for offline support
- Be aware of the license of this project (see below)

## Standalone builds

To make a standalone build, run:

```
NODE_ENV=production STANDALONE=1 npm run build
node src/build/generate-standalone.js
```

Output will be located in dist/standalone.html

## License

Copyright (C) 2021 Thomas Weber
Expand Down
29 changes: 29 additions & 0 deletions src/build/generate-standalone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const pathUtil = require('path');
const fs = require('fs');
const glob = require('glob');

const dist = pathUtil.join(__dirname, '..', '..', 'dist');
console.log(`dist: ${dist}`);

const scaffoldingFiles = glob.sync('scaffolding/*.js', {
cwd: dist
});
console.log(`scaffolding: ${scaffoldingFiles.join(', ')}`);
const scaffoldingAssets = {};
for (const path of scaffoldingFiles) {
scaffoldingAssets[path] = fs.readFileSync(pathUtil.join(dist, path), 'utf-8');
}

const indexPath = pathUtil.join(dist, 'index.html');
console.log(`index.html: ${indexPath}`);
let indexContent = fs.readFileSync(indexPath, 'utf8');
const jsPath = pathUtil.join(dist, indexContent.match(/<script src="(.*)"><\/script>/)[1]);
console.log(`packager.js: ${jsPath}`);
let jsContent = fs.readFileSync(jsPath, 'utf-8');
jsContent = `window.__ASSETS__=${JSON.stringify(scaffoldingAssets)};${jsContent}`;
jsContent = jsContent.replace(/<\/script>/g, '\\u003c/script>');
indexContent = indexContent.replace(/<script src=".*"><\/script>/, () => `<script>${jsContent}</script>`);

const standalonePath = pathUtil.join(dist, 'standalone.html');
console.log(`standalone.html: ${standalonePath}`);
fs.writeFileSync(standalonePath, indexContent);
7 changes: 6 additions & 1 deletion src/build/p4-worker-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ module.exports.pitch = function (request) {
compiler.runAsChild((err, entries, compilation) => {
if (err) return callback(err);
const file = entries[0].files[0];
const inline = !!process.env.STANDALONE;
// extra whitespace here won't matter
const source = `
import {wrap} from 'comlink';
const createWorker = () => {
const worker = new Worker(__webpack_public_path__ + ${JSON.stringify(file)});
${inline ? `const source = ${JSON.stringify(compilation.assets[file].source())};
const blob = new Blob([source]);
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
URL.revokeObjectURL(url);` : `const worker = new Worker(__webpack_public_path__ + ${JSON.stringify(file)});`}
const terminate = () => {
worker.terminate();
};
Expand Down
22 changes: 22 additions & 0 deletions src/build/standalone-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Plugin to modify webpack to interpret all dynamic imports as webpackMode: "eager"

const patchParser = (parser) => {
const originalParseCommentOptions = parser.parseCommentOptions;
parser.parseCommentOptions = function (...args) {
const result = originalParseCommentOptions.call(this, ...args);
result.options.webpackMode = 'eager';
return result;
};
};

class TWStandalonePlugin {
apply (compiler) {
compiler.hooks.normalModuleFactory.tap('TWStandalonePlugin', (normalModuleFactory) => {
normalModuleFactory.hooks.parser.for('javascript/auto').tap('TWStandalonePlugin', patchParser);
normalModuleFactory.hooks.parser.for('javascript/dynamic').tap('TWStandalonePlugin', patchParser);
normalModuleFactory.hooks.parser.for('javascript/esm').tap('TWStandalonePlugin', patchParser);
});
}
}

module.exports = TWStandalonePlugin;
15 changes: 9 additions & 6 deletions src/p4/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const origin = process.env.STANDALONE ? '*' : location.origin;

const source = `<!DOCTYPE html>
<html>
<head>
Expand Down Expand Up @@ -50,6 +52,7 @@ const source = `<!DOCTYPE html>
</div>
<script>
(function() {
const origin = ${JSON.stringify(origin)};
const err = (message) => {
document.querySelector(".preview-error").hidden = false;
document.querySelector(".preview-error-message").textContent = "Error: " + message;
Expand All @@ -62,7 +65,7 @@ const source = `<!DOCTYPE html>
const progressBar = document.querySelector(".preview-progress-inner");
const progressText = document.querySelector(".preview-progress-text");
window.addEventListener("message", (e) => {
if (e.origin !== location.origin) return;
if (origin !== "*" && e.origin !== location.origin) return;
if (hasRun) return;
if (e.data.blob) {
hasRun = true;
Expand All @@ -83,7 +86,7 @@ const source = `<!DOCTYPE html>
});
window.opener.postMessage({
preview: "hello"
}, location.origin);
}, origin);
})();
</script>
</body>
Expand All @@ -107,14 +110,14 @@ class Preview {
windowToBlobMap.set(this.window, content);
this.window.postMessage({
blob: content
}, location.origin);
}, origin);
}

setProgress (progress, text) {
this.window.postMessage({
progress,
text
}, location.origin);
}, origin);
}

close () {
Expand All @@ -123,7 +126,7 @@ class Preview {
}

window.addEventListener('message', (e) => {
if (e.origin !== location.origin) {
if (origin !== '*' && e.origin !== location.origin) {
return;
}
const data = e.data;
Expand All @@ -133,7 +136,7 @@ window.addEventListener('message', (e) => {
if (blob) {
source.postMessage({
blob
}, location.origin);
}, origin);
}
}
});
Expand Down
12 changes: 7 additions & 5 deletions src/p4/template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/favicon.ico?v=2" />
<% const {LONG_NAME, WEBSITE} = require('../packager/brand.js'); %>
<title><%= LONG_NAME %></title>
<meta name="description" content="Converts Scratch projects into HTML files, zip archives, or executable programs for Windows, macOS, and Linux.">
<meta property="og:type" content="website" />
<meta property="og:title" content="<%= LONG_NAME %>" />
<meta property="og:description" content="Converts Scratch projects into HTML files, zip archives, or executable programs for Windows, macOS, and Linux." />
<meta property="og:url" content="<%= WEBSITE %>" />
<% if (!process.env.STANDALONE) { %>
<link rel="shortcut icon" href="/favicon.ico?v=2" />
<meta property="og:type" content="website" />
<meta property="og:title" content="<%= LONG_NAME %>" />
<meta property="og:description" content="Converts Scratch projects into HTML files, zip archives, or executable programs for Windows, macOS, and Linux." />
<meta property="og:url" content="<%= WEBSITE %>" />
<% } %>
<style>
body[p4-splash-theme="dark"]:not([p4-loaded]) {
background-color: #111;
Expand Down
3 changes: 3 additions & 0 deletions src/packager/packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class Packager extends EventTarget {
if (!asset) {
throw new Error(`Invalid asset: ${name}`);
}
if (process.env.STANDALONE && window.__ASSETS__ && window.__ASSETS__[asset.src]) {
return window.__ASSETS__[asset.src];
}
const dispatchProgress = (progress) => this.dispatchEvent(new CustomEvent('large-asset-fetch', {
detail: {
asset: name,
Expand Down
17 changes: 10 additions & 7 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const CopyWebpackPlugin = require('copy-webpack-plugin');
const AddBuildIDToOutputPlugin = require('./src/build/add-build-id-to-output-plugin');
const GenerateServiceWorkerPlugin = require('./src/build/generate-service-worker-plugin');
const StandalonePlugin = require('./src/build/standalone-plugin');

const isProduction = process.env.NODE_ENV === 'production';
const base = {
Expand Down Expand Up @@ -119,14 +120,14 @@ const makeWebsite = () => ({
rules: [
{
test: /\.png$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/[name].[contenthash].[ext]'
}
use: process.env.STANDALONE ? {
loader: 'url-loader'
} : {
loader: 'file-loader',
options: {
name: 'assets/[name].[contenthash].[ext]'
}
]
}
},
{
test: /\.(html|svelte)$/,
Expand All @@ -146,6 +147,7 @@ const makeWebsite = () => ({
'process.env.SCAFFOLDING_BUILD_ID': buildId ? JSON.stringify(buildId) : 'Math.random().toString()',
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.ENABLE_SERVICE_WORKER': JSON.stringify(process.env.ENABLE_SERVICE_WORKER),
'process.env.STANDALONE': JSON.stringify(process.env.STANDALONE),
'process.env.PLAUSIBLE_API': JSON.stringify(process.env.PLAUSIBLE_API),
'process.env.PLAUSIBLE_DOMAIN': JSON.stringify(process.env.PLAUSIBLE_DOMAIN),
}),
Expand All @@ -155,6 +157,7 @@ const makeWebsite = () => ({
chunks: ['packager']
}),
new GenerateServiceWorkerPlugin(),
...(process.env.STANDALONE ? [new StandalonePlugin()] : []),
...(process.env.BUNDLE_ANALYZER === '3' ? [new BundleAnalyzerPlugin()] : [])
],
devServer: {
Expand Down

0 comments on commit d9e8c6d

Please sign in to comment.