Replace rollup w/ webpack for the web worker

This commit is contained in:
ochafik 2024-12-31 15:54:09 +00:00 committed by Olivier Chafik
commit a0015af28e
9 changed files with 232 additions and 188 deletions

View file

@ -1,25 +0,0 @@
import typescript from 'rollup-plugin-typescript2';
import replace from '@rollup/plugin-replace';
import packageConfig from './package.json'
const LOCAL_URL = process.env.LOCAL_URL ?? 'http://localhost:4000/';
const PUBLIC_URL = process.env.PUBLIC_URL ?? packageConfig.homepage;
export default [
{
input: 'src/runner/openscad-worker.ts',
output: {
file: 'dist/openscad-worker.js',
format: 'es'
},
plugins: [
typescript({
rootDir: 'src',
}),
replace({
preventAssignment: true,
'import.meta.url': JSON.stringify(process.env.NODE_ENV !== 'production' ? LOCAL_URL : PUBLIC_URL),
})
]
},
];

View file

@ -25,10 +25,10 @@
},
"scripts": {
"test:e2e": "jest",
"start:development": "concurrently 'npx webpack serve --mode=development' 'NODE_ENV=development npx rollup --config openscad-worker.rollup.config.js --watch'",
"start:development": "npx webpack serve --mode=development",
"start:production": "NODE_ENV=production PUBLIC_URL=http://localhost:3000/dist/ npm run build && npx serve",
"start": "npm run start:development",
"build": "NODE_ENV=production npx rollup --config openscad-worker.rollup.config.js && webpack --mode=production"
"build": "NODE_ENV=production webpack --mode=production"
},
"eslintConfig": {
"extends": [
@ -52,8 +52,6 @@
"node": ">=18.12.0"
},
"devDependencies": {
"@rollup/plugin-replace": "^5.0.7",
"@rollup/plugin-typescript": "^11.1.6",
"@types/chroma-js": "^2.4.5",
"@types/debug": "^4.1.12",
"@types/filesystem": "^0.0.36",
@ -62,15 +60,13 @@
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/uzip": "^0.20201231.2",
"concurrently": "^7.6.0",
"@types/web": "^0.0.140",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^7.1.2",
"jest": "^29.7.0",
"jest-puppeteer": "^11.0.0",
"livereload": "^0.9.3",
"puppeteer": "^23.11.1",
"rollup": "^2.79.2",
"rollup-plugin-typescript2": "^0.36.0",
"serve": "^14.2.4",
"style-loader": "^4.0.0",
"ts-loader": "^9.5.1",

32
src/fs/BrowserFS.d.ts vendored
View file

@ -11,13 +11,29 @@ declare interface FS {
declare interface EmscriptenFS extends FS {}
declare type BrowserFSInterface = {
BFSRequire: (name: string) => any,
declare interface BrowserFSInterface {
EmscriptenFS: any;
BFSRequire: (module: string) => any;
install: (obj: any) => void;
configure: (config: any, cb: (e?: Error) => void) => void;
FileSystem: {
InMemory: any;
ZipFS: any;
MountableFileSystem: any;
LocalStorage: any;
XmlHttpRequest: any;
};
Buffer: {
from: (data: any, encoding?: string) => any;
alloc: (size: number) => any;
};
initialize: (config: any) => Promise<void>;
WorkerFS?: any;
}
install: (windowOrSelf: Window) => void,
configure: (options: any, callback: (e?: any) => void) => void,
declare var BrowserFS: BrowserFSInterface;
declare module 'browserfs' {
export = BrowserFS;
}
EmscriptenFS: {
new(fs: FS, path: string, errnoCodes: object): EmscriptenFS
}
};

View file

@ -61,14 +61,14 @@ window.addEventListener('load', async () => {
registerCustomAppHeightCSSProperty();
const fs = await createEditorFS({prefix: '/libraries/', allowPersistence: isInStandaloneMode});
const fs = await createEditorFS({prefix: '/libraries/', allowPersistence: isInStandaloneMode()});
await registerOpenSCADLanguage(fs, '/', zipArchives);
let statePersister: StatePersister;
let persistedState: State | null = null;
if (isInStandaloneMode) {
if (isInStandaloneMode()) {
const fs: FS = BrowserFS.BFSRequire('fs')
try {
const data = JSON.parse(new TextDecoder("utf-8").decode(fs.readFileSync('/state.json')));

View file

@ -4,26 +4,13 @@ import { MergedOutputs } from "./openscad-worker";
import { AbortablePromise } from "../utils";
import { Source } from "../state/app-state";
export function createWasmMemory({maximumMegabytes, maximumBytes}: {maximumMegabytes: number, maximumBytes: number}) {
const pageSize = 64 * 1024; // 64KB
if (!maximumBytes) {
maximumBytes = maximumMegabytes * 1024 * 1024;
}
return new WebAssembly.Memory({
initial: Math.floor(maximumBytes / 2 / pageSize),
maximum: Math.floor(maximumBytes / pageSize),
shared: true,
});
}
// Output is {outputs: [name, content][], mergedOutputs: [{(stderr|stdout|error)?: string}], exitCode: number}
export type OpenSCADInvocation = {
wasmMemory?: WebAssembly.Memory,
mountArchives: boolean,
inputs?: Source[],
args: string[],
outputPaths?: string[],
}
export type OpenSCADInvocationResults = {
exitCode?: number,
error?: string,
@ -31,12 +18,16 @@ export type OpenSCADInvocationResults = {
mergedOutputs: MergedOutputs,
elapsedMillis: number,
};
export type ProcessStreams = {stderr: string} | {stdout: string}
export type OpenSCADInvocationCallback = {result: OpenSCADInvocationResults} | ProcessStreams;
export function spawnOpenSCAD(invocation: OpenSCADInvocation, streamsCallback: (ps: ProcessStreams) => void): AbortablePromise<OpenSCADInvocationResults> {
var worker: Worker | null;
var rejection: (err: any) => void;
export function spawnOpenSCAD(
invocation: OpenSCADInvocation,
streamsCallback: (ps: ProcessStreams) => void
): AbortablePromise<OpenSCADInvocationResults> {
let worker: Worker | null;
let rejection: (err: any) => void;
function terminate() {
if (!worker) {
@ -46,10 +37,10 @@ export function spawnOpenSCAD(invocation: OpenSCADInvocation, streamsCallback: (
worker = null;
}
return AbortablePromise<OpenSCADInvocationResults>((resolve, reject) => {
worker = new Worker('./openscad-worker.js');
return AbortablePromise<OpenSCADInvocationResults>((resolve: (result: OpenSCADInvocationResults) => void, reject: (error: any) => void) => {
worker = new Worker('./openscad-worker.js');//, { type: 'module' });
rejection = reject;
worker.onmessage = (e: {data: OpenSCADInvocationCallback}) => {
worker.onmessage = (e: MessageEvent<OpenSCADInvocationCallback>) => {
if ('result' in e.data) {
resolve(e.data.result);
terminate();

View file

@ -1,38 +1,36 @@
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
/// <reference lib="webworker" />
import OpenSCAD from "../wasm/openscad.js";
import { createEditorFS, getParentDir, symlinkLibraries } from "../fs/filesystem";
import { OpenSCADInvocation, OpenSCADInvocationCallback, OpenSCADInvocationResults } from "./openscad-runner";
import { deployedArchiveNames, zipArchives } from "../fs/zip-archives";
import { fetchSource } from "../utils.js";
declare var BrowserFS: BrowserFSInterface
import { createEditorFS, getParentDir, symlinkLibraries } from "../fs/filesystem.ts";
import { OpenSCADInvocation, OpenSCADInvocationCallback, OpenSCADInvocationResults } from "./openscad-runner.ts";
import { deployedArchiveNames, zipArchives } from "../fs/zip-archives.ts";
import { fetchSource } from "../utils.ts";
importScripts("browserfs.min.js");
declare const self: DedicatedWorkerGlobalScope;
export type MergedOutputs = {stdout?: string, stderr?: string, error?: string}[];
function callback(payload: OpenSCADInvocationCallback) {
postMessage(payload);
self.postMessage(payload);
}
addEventListener('message', async (e) => {
self.addEventListener('message', async (e: MessageEvent<OpenSCADInvocation>) => {
const {
mountArchives,
inputs,
args,
outputPaths,
wasmMemory,
} = e.data as OpenSCADInvocation;
} = e.data;
const mergedOutputs: MergedOutputs = [];
let instance: any;
const start = performance.now();
try {
instance = await OpenSCAD({
wasmMemory,
buffer: wasmMemory && wasmMemory.buffer,
noInitialRun: true,
'print': (text: string) => {
console.debug('stdout: ' + text);
@ -44,9 +42,6 @@ addEventListener('message', async (e) => {
callback({stderr: text})
mergedOutputs.push({ stderr: text })
},
'ENV': {
'OPENSCADPATH': '/libraries',
},
});
if (mountArchives) {
@ -140,7 +135,6 @@ addEventListener('message', async (e) => {
}
console.debug(result);
callback({result});
} catch (e) {
const end = performance.now();

View file

@ -138,9 +138,9 @@ export function registerCustomAppHeightCSSProperty() {
}
// In PWA mode, persist files in LocalStorage instead of the hash fragment.
export const isInStandaloneMode =
// true
Boolean(('standalone' in window.navigator) && (window.navigator.standalone));
export function isInStandaloneMode() {
return Boolean(('standalone' in window.navigator) && (window.navigator.standalone));
}
export function downloadUrl(url: string, filename: string) {
const link = document.createElement('a');

View file

@ -5,10 +5,9 @@
"dom",
"dom.iterable",
"ES2022",
"WebWorker",
"WebWorker"
],
"rootDir": "src",
// "outDir": "js",
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
@ -17,17 +16,22 @@
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
// "noEmit": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"noEmit": true,
"allowImportingTsExtensions": true,
"declaration": true,
"baseUrl": "src",
"paths": {
"*": ["*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.js",
// "public"
"src/**/*.js"
],
"exclude": [
"node_modules",

View file

@ -1,111 +1,179 @@
const webpack = require('webpack');
const CopyPlugin = require("copy-webpack-plugin");
const WorkboxPlugin = require('workbox-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
const packageConfig = require('./package.json');
module.exports = [{
entry: './src/index.tsx',
// devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: [
"style-loader",
{
loader: 'css-loader',
options:{url: false},
}
]
},
// {
// test: /\.(png|gif|woff|woff2|eot|ttf|svg)$/,
// loader: "url-loader?limit=100000"
// },
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: path.join(__dirname, "dist"),
compress: true,
port: 4000,
},
plugins: [
new webpack.EnvironmentPlugin({
'process.env.NODE_ENV': 'development',
}),
...(process.env.NODE_ENV === 'production' ? [
new WorkboxPlugin.GenerateSW({
exclude: [
/(^|\/)\./,
/\.map$/,
/^manifest.*\.js$/,
],
// these options encourage the ServiceWorkers to get in there fast
// and not allow any straggling "old" SWs to hang around
swDest: path.join(__dirname, "dist", 'sw.js'),
maximumFileSizeToCacheInBytes: 200 * 1024 * 1024,
clientsClaim: true,
skipWaiting: true,
runtimeCaching: [{
urlPattern: ({request, url}) => true,
handler: 'StaleWhileRevalidate',
const LOCAL_URL = process.env.LOCAL_URL ?? 'http://localhost:4000/';
const PUBLIC_URL = process.env.PUBLIC_URL ?? packageConfig.homepage;
const isDev = process.env.NODE_ENV !== 'production';
module.exports = [
{
entry: './src/index.tsx',
devtool: isDev ? 'source-map' : 'nosources-source-map',
mode: isDev ? 'development' : 'production',
target: 'web',
// devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'ts-loader',
options: {
cacheName: 'all',
expiration: {
maxEntries: 1000,
purgeOnQuotaError: true,
},
},
}],
}),
] : []),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'public'),
toType: 'dir',
transpileOnly: true,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
target: 'ES2022',
lib: ['WebWorker', 'ES2022'],
sourceMap: isDev,
inlineSources: isDev
}
}
},
exclude: /node_modules/,
},
{
from: path.resolve(__dirname, 'node_modules/primeicons/fonts'),
to: path.resolve(__dirname, 'dist/fonts'),
toType: 'dir',
},
{
from: path.resolve(__dirname, 'src/wasm/openscad.js'),
from: path.resolve(__dirname, 'src/wasm/openscad.wasm'),
{
test: /\.css$/i,
use: [
"style-loader",
{
loader: 'css-loader',
options:{url: false},
}
]
},
// {
// test: /\.(png|gif|woff|woff2|eot|ttf|svg)$/,
// loader: "url-loader?limit=100000"
// },
],
}),
],
}/*, {
entry: './src/sw.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: path.join(__dirname, "dist"),
compress: true,
port: 4000,
},
plugins: [
new webpack.EnvironmentPlugin({
'process.env.NODE_ENV': 'development',
}),
...(process.env.NODE_ENV === 'production' ? [
new WorkboxPlugin.GenerateSW({
exclude: [
/(^|\/)\./,
/\.map$/,
/^manifest.*\.js$/,
],
// these options encourage the ServiceWorkers to get in there fast
// and not allow any straggling "old" SWs to hang around
swDest: path.join(__dirname, "dist", 'sw.js'),
maximumFileSizeToCacheInBytes: 200 * 1024 * 1024,
clientsClaim: true,
skipWaiting: true,
runtimeCaching: [{
urlPattern: ({request, url}) => true,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'all',
expiration: {
maxEntries: 1000,
purgeOnQuotaError: true,
},
},
}],
}),
] : []),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'public'),
toType: 'dir',
},
{
from: path.resolve(__dirname, 'node_modules/primeicons/fonts'),
to: path.resolve(__dirname, 'dist/fonts'),
toType: 'dir',
},
{
from: path.resolve(__dirname, 'src/wasm/openscad.js'),
from: path.resolve(__dirname, 'src/wasm/openscad.wasm'),
},
],
}),
],
},
resolve: {
extensions: ['.ts', '.js'],
{
entry: './src/runner/openscad-worker.ts',
output: {
filename: 'openscad-worker.js',
path: path.resolve(__dirname, 'dist'),
globalObject: 'self',
// library: {
// type: 'module'
// }
},
devtool: isDev ? 'source-map' : 'nosources-source-map',
mode: 'production',
// mode: isDev ? 'development' : 'production',
target: 'webworker',
// experiments: {
// outputModule: true,
// },
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
target: 'ES2022',
lib: ['WebWorker', 'ES2022'],
sourceMap: isDev,
inlineSources: isDev
}
}
},
exclude: /node_modules/,
},
{
test: /\.wasm$/,
type: 'asset/resource'
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.mjs', '.wasm'],
modules: [
path.resolve(__dirname, 'src'),
'node_modules'
],
fallback: {
fs: false,
path: false,
module: false
}
},
externals: {
'browserfs': 'BrowserFS'
},
plugins: [
new webpack.EnvironmentPlugin({
'process.env.NODE_ENV': 'development',
}),
],
},
output: {
filename: 'sw.js',
path: path.resolve(__dirname, 'dist'),
},
}*/];
];