diff --git a/app.js b/app.js index a6f6055d..0093cd50 100644 --- a/app.js +++ b/app.js @@ -568,6 +568,14 @@ function handleVersion(req, res, next) { } else { return http.redirect(res, `${req.url}?ver:${vstr}`); } + } else if (!debug) { + let { path } = req.app; + if (path === '/v2/lib/mesh/work.js') { + req.url = req.app.path = '/v2/lib/pack/work.js'; + } else if (path === '/v2/lib/main/mesh.js') { + req.url = req.app.path = '/v2/lib/pack/mesh.js'; + } + next(); } else { next(); } diff --git a/package.json b/package.json index 838d4bca..cfe9b28b 100644 --- a/package.json +++ b/package.json @@ -63,11 +63,14 @@ "@docusaurus/core": "^3.7.0", "@docusaurus/preset-classic": "^3.7.0", "@electron/notarize": "^3.0.0", + "copy-webpack-plugin": "^12.0.2", "docusaurus-lunr-search": "^3.6.0", "dotenv": "latest", "electron": "^35.0.1", "electron-builder": "^24.9.1", + "esbuild": "^0.25.5", "fs-extra": "^11.2.0", + "imports-loader": "^5.0.0", "node-fetch": "^3.3.2", "terser-webpack-plugin": "^5.3.10", "webpack": "^5.92.1", @@ -91,13 +94,16 @@ "build-win-arm": "npm run build -- --win --arm64", "build-mac": "npm run build -- --mac --arm64", "build-mac-intel": "npm run build -- --mac --x64", + "mesh-build": "webpack --config webpack.config.js", + "mesh-build-dev": "webpack --config webpack.dev.config.js", + "serve-mesh": "npx serve dist/v2 -p 8181", "mklinks": "find src web -type l | xargs -I{} sh -c 'echo \"{},$(readlink {})\"' > links.csv", "mac-verify": "spctl --assess -vv --type install dist/*/*.app", "clear-cache": "rm -rf data/cache/* dist/ tmp/*", "prebuild": "node bin/electron-pre.js", "postbuild": "node bin/electron-post.js", "preinstall": "node bin/install-pre.js && npx webpack --config bin/webpack-three.js", - "preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js", + "preinstall2": "npx webpack --config src2/webpack/webpack-three-esm.js && node src2/webpack/esbuild.config.js", "docs-dev": "docusaurus start", "docs-build": "docusaurus build", "docs-serve": "docusaurus serve", @@ -194,5 +200,6 @@ ] }, "afterSign": "bin/electron-notarize.js" - } + }, + "sideEffects": true } diff --git a/src2/ext/fsave.js b/src2/ext/fsave.js deleted file mode 100644 index 9d4de197..00000000 --- a/src2/ext/fsave.js +++ /dev/null @@ -1,247 +0,0 @@ -/* FileSaver.js - * A saveAs() FileSaver implementation. - * 1.1.20151003 - * - * By Eli Grey, http://eligrey.com - * License: MIT - * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md - */ - -/*global self */ -/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ - -/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ - -"use strict"; -// IE <10 is explicitly unsupported -if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { - return; -} - -const doc = self.document; -// only get URL when necessary in case Blob.js hasn't overridden it yet -const get_URL = function() { - return self.URL || self.webkitURL || self; -}; - -const save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"); -const can_use_save_link = "download" in save_link; - -const click = function(node) { - const event = new MouseEvent("click"); - node.dispatchEvent(event); -}; - -const is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); -const webkit_req_fs = self.webkitRequestFileSystem; -const req_fs = self.requestFileSystem || webkit_req_fs || self.mozRequestFileSystem; - -const throw_outside = function(ex) { - (self.setImmediate || self.setTimeout)(function() { - throw ex; - }, 0); -}; - -const force_saveable_type = "application/octet-stream"; -const fs_min_size = 0; - -// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and -// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047 -// for the reasoning behind the timeout and revocation flow -const arbitrary_revoke_timeout = 500; // in ms - -const revoke = function(file) { - const revoker = function() { - if (typeof file === "string") { // file is an object URL - get_URL().revokeObjectURL(file); - } else { // file is a File - file.remove(); - } - }; - if (self.chrome) { - revoker(); - } else { - setTimeout(revoker, arbitrary_revoke_timeout); - } -}; - -const dispatch = function(filesaver, event_types, event) { - event_types = [].concat(event_types); - let i = event_types.length; - while (i--) { - const listener = filesaver["on" + event_types[i]]; - if (typeof listener === "function") { - try { - listener.call(filesaver, event || filesaver); - } catch (ex) { - throw_outside(ex); - } - } - } -}; - -const auto_bom = function(blob) { - // prepend BOM for UTF-8 XML and text/* types (including HTML) - if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { - return new Blob(["\ufeff", blob], {type: blob.type}); - } - return blob; -}; - -class FileSaver { - constructor(blob, name, no_auto_bom) { - if (!no_auto_bom) { - blob = auto_bom(blob); - } - // First try a.download, then web filesystem, then object URLs - const filesaver = this; - const type = blob.type; - let blob_changed = false; - let object_url; - let target_view; - const dispatch_all = function() { - dispatch(filesaver, "writestart progress write writeend".split(" ")); - }; - // on any filesys errors revert to saving with object URLs - const fs_error = function() { - if (target_view && is_safari && typeof FileReader !== "undefined") { - // Safari doesn't allow downloading of blob urls - const reader = new FileReader(); - reader.onloadend = function() { - const base64Data = reader.result; - target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/)); - filesaver.readyState = filesaver.DONE; - dispatch_all(); - }; - reader.readAsDataURL(blob); - filesaver.readyState = filesaver.INIT; - return; - } - // don't create more object URLs than needed - if (blob_changed || !object_url) { - object_url = get_URL().createObjectURL(blob); - } - if (target_view) { - target_view.location.href = object_url; - } else { - const new_tab = self.open(object_url, "_blank"); - if (new_tab == undefined && is_safari) { - //Apple do not allow window.open, see http://bit.ly/1kZffRI - self.location.href = object_url; - } - } - filesaver.readyState = filesaver.DONE; - dispatch_all(); - revoke(object_url); - }; - const abortable = function(func) { - return function() { - if (filesaver.readyState !== filesaver.DONE) { - return func.apply(this, arguments); - } - }; - }; - const create_if_not_found = {create: true, exclusive: false}; - let slice; - - filesaver.readyState = filesaver.INIT; - - if (!name) { - name = "download"; - } - - if (can_use_save_link) { - object_url = get_URL().createObjectURL(blob); - setTimeout(function() { - save_link.href = object_url; - save_link.download = name; - click(save_link); - dispatch_all(); - revoke(object_url); - filesaver.readyState = filesaver.DONE; - }); - return; - } - - // Object and web filesystem URLs have a problem saving in Google Chrome when - // viewed in a tab, so I force save with application/octet-stream - // http://code.google.com/p/chromium/issues/detail?id=91158 - // Update: Google errantly closed 91158, I submitted it again: - // https://code.google.com/p/chromium/issues/detail?id=389642 - if (self.chrome && type && type !== force_saveable_type) { - slice = blob.slice || blob.webkitSlice; - blob = slice.call(blob, 0, blob.size, force_saveable_type); - blob_changed = true; - } - - // Since I can't be sure that the guessed media type will trigger a download - // in WebKit, I append .download to the filename. - // https://bugs.webkit.org/show_bug.cgi?id=65440 - if (webkit_req_fs && name !== "download") { - name += ".download"; - } - - if (type === force_saveable_type || webkit_req_fs) { - target_view = self; - } - - if (!req_fs) { - fs_error(); - return; - } - - fs_min_size += blob.size; - req_fs(self.TEMPORARY, fs_min_size, abortable(function(fs) { - fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { - const save = function() { - dir.getFile(name, create_if_not_found, abortable(function(file) { - file.createWriter(abortable(function(writer) { - writer.onwriteend = function(event) { - target_view.location.href = file.toURL(); - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "writeend", event); - revoke(file); - }; - writer.onerror = function() { - const error = writer.error; - if (error.code !== error.ABORT_ERR) { - fs_error(); - } - }; - "writestart progress write abort".split(" ").forEach(function(event) { - writer["on" + event] = filesaver["on" + event]; - }); - writer.write(blob); - filesaver.abort = function() { - writer.abort(); - filesaver.readyState = filesaver.DONE; - }; - filesaver.readyState = filesaver.WRITING; - }), fs_error); - }), fs_error); - }; - dir.getFile(name, {create: false}, abortable(function(file) { - // delete file if it already exists - file.remove(); - save(); - }), abortable(function(ex) { - if (ex.code === ex.NOT_FOUND_ERR) { - save(); - } else { - fs_error(); - } - })); - }), fs_error); - }), fs_error); - } - - static get DONE() { return 2; } - static get INIT() { return 0; } - static get WRITING() { return 1; } -} - -export function saveAs(blob, name, no_auto_bom) { - return new FileSaver(blob, name, no_auto_bom); -} - -export { FileSaver }; diff --git a/src2/ext/pngjs.esm.js b/src2/ext/pngjs.esm.js index 14c1f600..1da89a23 100644 --- a/src2/ext/pngjs.esm.js +++ b/src2/ext/pngjs.esm.js @@ -4,4 +4,4 @@ import './pngjs.js'; // Re-export the PNG object -export const PNG = self.png.PNG; \ No newline at end of file +export const PNG = self.png ? self.png.PNG : null; \ No newline at end of file diff --git a/src2/main/mesh.js b/src2/main/mesh.js index 2060a9c8..fb528139 100644 --- a/src2/main/mesh.js +++ b/src2/main/mesh.js @@ -1,7 +1,5 @@ /** Copyright Stewart Allen -- All Rights Reserved */ -// import '../mesh/work.js'; - import '../add/array.js'; import '../add/class.js'; import '../add/three.js'; @@ -84,7 +82,7 @@ function init() { motoClient.on('ready', restore_space); // start worker - motoClient.start(`/v2/lib/mesh/work.js?${version}`); + motoClient.start('../lib/mesh/work.js?' + version); // trigger space event binding call.space_init({ space: space, platform }); @@ -776,15 +774,7 @@ broker.listeners({ set_snap_value }); -// remove version cache bust from url -window.history.replaceState({},'','/v2/mesh/'); - -// setup init() trigger when dom + scripts complete -document.onreadystatechange = function() { - if (document.readyState === 'complete') { - init(); - } -} +init(); export { version, diff --git a/src2/mesh/work.js b/src2/mesh/work.js index 88705aee..f29d5e92 100644 --- a/src2/mesh/work.js +++ b/src2/mesh/work.js @@ -1,5 +1,8 @@ /** Copyright Stewart Allen -- All Rights Reserved */ +// Import Clipper library first to ensure it's available in worker context +import "../ext/clip2.js"; + import "../ext/earcut.js"; import '../add/array.js'; import '../add/class.js'; diff --git a/src2/webpack/esbuild.config.js b/src2/webpack/esbuild.config.js new file mode 100644 index 00000000..c598874f --- /dev/null +++ b/src2/webpack/esbuild.config.js @@ -0,0 +1,70 @@ +// esbuild.config.js +import { build } from 'esbuild'; + +// Get build mode from command line argument +const mode = process.argv[2] || 'dev'; +const isProd = mode === 'prod'; + +console.log(`Building in ${mode} mode...`); + +// Dummy document shim for worker context +// const dummyDocument = ` +// // Inject dummy document for worker context +// if (typeof document === 'undefined') { +// var document = { +// createElement: () => ({}), +// getElementById: () => null, +// querySelector: () => null, +// addEventListener: () => {}, +// removeEventListener: () => {} +// }; +// } +// if (typeof window === 'undefined') { +// var window = { }; +// } +// `; + +async function buildApp() { + try { + // Bundle main app + await build({ + entryPoints: ['src2/main/mesh.js'], + bundle: true, + outfile: 'src2/pack/mesh.js', + format: 'esm', + external: ['module'], + sourcemap: !isProd, // true for dev, false for prod + minify: isProd, // false for dev, true for prod + target: 'es2020', + platform: 'browser', + define: { + 'process.env.NODE_ENV': `"${mode}"` + } + }); + + // Bundle worker + await build({ + entryPoints: ['src2/mesh/work.js'], + bundle: true, + outfile: 'src2/pack/work.js', + format: 'esm', + external: ['module'], + sourcemap: !isProd, // true for dev, false for prod + minify: isProd, // false for dev, true for prod + target: 'es2020', + platform: 'browser', + define: { + 'process.env.NODE_ENV': `"${mode}"` + } + }); + + console.log(`Build completed successfully in ${mode} mode!`); + } catch (error) { + console.error('Build failed:', error); + process.exit(1); + } +} + +buildApp(); + +// You can add more build steps or static asset copying as needed. \ No newline at end of file diff --git a/web2/mesh/index.html b/web2/mesh/index.html index 8c457652..e15fcf43 100644 --- a/web2/mesh/index.html +++ b/web2/mesh/index.html @@ -17,7 +17,7 @@ - +