From d31d18cc042bc4a7e8d643d22ef288a25a2da8c6 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 29 Feb 2024 13:37:59 +0000 Subject: [PATCH 1/6] added prebuild to occur during rollup process; fixed prod build to use new bundled modules --- examples/package.json | 4 +- examples/rollup.config.js | 111 +++++++++++++-------------- examples/scripts/standalone-html.mjs | 6 +- 3 files changed, 57 insertions(+), 64 deletions(-) diff --git a/examples/package.json b/examples/package.json index 0a96f36f5fb..fa3d8ba75d6 100644 --- a/examples/package.json +++ b/examples/package.json @@ -5,8 +5,8 @@ "main": "index.js", "type": "module", "scripts": { - "prebuild": "npm run -s build:metadata && npm run -s build:standalone && npm run -s build:sharing", "build": "cross-env NODE_ENV=production rollup -c", + "build:pre": "npm run -s build:metadata && npm run -s build:standalone && npm run -s build:sharing", "build:metadata": "node ./scripts/metadata.mjs", "build:sharing": "node ./scripts/sharing-html.mjs", "build:standalone": "node ./scripts/standalone-html.mjs", @@ -14,7 +14,7 @@ "rollup:watch": "cross-env NODE_ENV=development rollup -c -w", "serve": "serve dist -l 5000 --no-request-logging --config ../serve.json", "lint": "eslint . --ext .js,.ts,.tsx", - "watch": "npm run prebuild && cross-env concurrently --kill-others \"npm run rollup:watch\"", + "watch": "cross-env concurrently --kill-others \"npm run rollup:watch\"", "watch:debug": "cross-env ENGINE_PATH=../build/playcanvas.dbg.js npm run watch", "watch:profiler": "cross-env ENGINE_PATH=../build/playcanvas.prf.js npm run watch", "develop": "cross-env NODE_ENV=development concurrently --kill-others \"npm run watch\" \"npm run serve\"", diff --git a/examples/rollup.config.js b/examples/rollup.config.js index 718afbc5dc4..e9cf29674ac 100644 --- a/examples/rollup.config.js +++ b/examples/rollup.config.js @@ -3,7 +3,7 @@ import * as fs from 'node:fs'; import fse from 'fs-extra'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -import { exec } from 'node:child_process'; +import { execSync } from 'node:child_process'; // 1st party Rollup plugins import alias from '@rollup/plugin-alias'; @@ -24,7 +24,7 @@ const PCUI_PATH = process.env.PCUI_PATH || 'node_modules/@playcanvas/pcui'; const PCUI_REACT_PATH = path.resolve(PCUI_PATH, 'react'); const PCUI_STYLES_PATH = path.resolve(PCUI_PATH, 'styles'); -const staticFiles = [ +const STATIC_FILES = [ // static main page src { src: './src/static', dest: 'dist/' }, @@ -51,6 +51,7 @@ const staticFiles = [ // modules (N.B. destination folder is 'modules' as 'node_modules' are automatically excluded by git pages) { src: './node_modules/monaco-editor/min/vs', dest: 'dist/modules/monaco-editor/min/vs' }, + // N.B. fflate will not be needed once extras module is rolled up { src: '../node_modules/fflate/esm/', dest: 'dist/modules/fflate/esm' } ]; @@ -86,21 +87,20 @@ let { ENGINE_PATH = '' } = process.env; if (!ENGINE_PATH && NODE_ENV === 'development') { ENGINE_PATH = '../src/index.js'; } - if (ENGINE_PATH) { const src = path.resolve(ENGINE_PATH); const content = fs.readFileSync(src, 'utf8'); const copyDir = isModuleWithExternalDependencies(content); if (copyDir) { - // Copy entire folder for MJS versions with external dependencies + // unpacked module builds const srcDir = path.dirname(src); const dest = 'dist/iframe/ENGINE_PATH'; - staticFiles.push({ src: srcDir, dest }); + STATIC_FILES.push({ src: srcDir, dest }); } else { - // This can be both UMD/ESM as a single file - const entryPoint = ENGINE_PATH.split("/").pop(); - const dest = 'dist/iframe/ENGINE_PATH/' + entryPoint; - staticFiles.push({ src, dest }); + // packed module builds + ENGINE_PATH.split("/").pop(); + const dest = 'dist/iframe/ENGINE_PATH/index.js'; + STATIC_FILES.push({ src, dest }); } } @@ -138,7 +138,7 @@ function watch(plugin, src) { /** * This plugin copies static files from source to destination. * - * @param {staticFiles} targets - Array of source and destination objects. + * @param {STATIC_FILES} targets - Array of source and destination objects. * @returns {RollupPlugin} The plugin. */ function copyStaticFiles(targets) { @@ -181,21 +181,46 @@ function buildAndWatchStandaloneExamples() { } }, generateBundle() { - const cmd = `cross-env NODE_ENV=${NODE_ENV} ENGINE_PATH=${ENGINE_PATH} npm run build:standalone`; + const cmd = `cross-env NODE_ENV=${NODE_ENV} ENGINE_PATH=${ENGINE_PATH} npm run build:pre`; console.log(cmd); - exec(cmd); + execSync(cmd, { stdio: 'inherit' }); } }; } -// define supported module overrides -const aliasEntries = { - '@playcanvas/pcui/react': PCUI_REACT_PATH, - '@playcanvas/pcui/styles': PCUI_STYLES_PATH -}; +function getEngineTargets() { + const targets = [ + // Outputs: dist/iframe/playcanvas-extras.mjs + scriptTargetEs6('pcx', '../extras/index.js', 'dist/iframe/playcanvas-extras.mjs') + ]; + if (NODE_ENV === 'production') { + // Outputs: dist/iframe/playcanvas.mjs + targets.push(buildTarget('release', 'es6', '../src/index.js', 'dist/iframe')); + } + if (NODE_ENV === 'production' || NODE_ENV === 'development') { + // Outputs: dist/iframe/playcanvas.dbg.mjs + targets.push(buildTarget('debug', 'es6', '../src/index.js', 'dist/iframe')); + } + if (NODE_ENV === 'production' || NODE_ENV === 'profiler') { + // Outputs: dist/iframe/playcanvas.prf.mjs + targets.push(buildTarget('profiler', 'es6', '../src/index.js', 'dist/iframe')); + } + return targets; +} + -/** @type {RollupOptions[]} */ -const targets = [ +export default [ + { + input: 'src/static/index.html', + output: { + file: `dist/copy.tmp` + }, + plugins: [ + buildAndWatchStandaloneExamples(), + copyStaticFiles(STATIC_FILES), + timestamp() + ] + }, { // A debug build is ~2.3MB and a release build ~0.6MB input: 'src/app/index.mjs', @@ -204,7 +229,13 @@ const targets = [ format: 'umd' }, plugins: [ - alias({ entries: aliasEntries }), + alias({ + entries: { + // define supported module overrides + '@playcanvas/pcui/react': PCUI_REACT_PATH, + '@playcanvas/pcui/styles': PCUI_STYLES_PATH + } + }), commonjs(), resolve(), replace({ @@ -217,43 +248,5 @@ const targets = [ timestamp() ] }, - { - input: 'src/static/index.html', - output: { - file: `dist/copy.tmp` - }, - plugins: [ - copyStaticFiles(staticFiles), - buildAndWatchStandaloneExamples(), - timestamp() - ] - }, - scriptTargetEs6('pcx', '../extras/index.js', 'dist/iframe/playcanvas-extras.mjs') + ...getEngineTargets() ]; - -// We skip building PlayCanvas ourselves when ENGINE_PATH is given. -// In that case we have a watcher which copies all necessary files. -if (ENGINE_PATH === '') { - /** @type {buildTarget} */ - const pushTarget = (...args) => { - targets.push(buildTarget(...args)); - }; - if (NODE_ENV === 'production') { - // Outputs: dist/iframe/playcanvas.mjs - pushTarget('release', 'es6', '../src/index.js', 'dist/iframe'); - // Outputs: dist/iframe/playcanvas.dbg.mjs - pushTarget('debug', 'es6', '../src/index.js', 'dist/iframe'); - // Outputs: dist/iframe/playcanvas.prf.mjs - pushTarget('profiler', 'es6', '../src/index.js', 'dist/iframe'); - } else if (NODE_ENV === 'development') { - // Outputs: dist/iframe/playcanvas.dbg.mjs - pushTarget('debug', 'es6', '../src/index.js', 'dist/iframe'); - } else if (NODE_ENV === 'profiler') { - // Outputs: dist/iframe/playcanvas.prf.mjs - pushTarget('profiler', 'es6', '../src/index.js', 'dist/iframe'); - } else { - console.warn("NODE_ENV is neither production, development nor profiler."); - } -} - -export default targets; diff --git a/examples/scripts/standalone-html.mjs b/examples/scripts/standalone-html.mjs index d100ce282f0..40979190610 100644 --- a/examples/scripts/standalone-html.mjs +++ b/examples/scripts/standalone-html.mjs @@ -33,11 +33,11 @@ function engineFor(type) { case 'DEVELOPMENT': return './ENGINE_PATH/index.js'; case 'PERFORMANCE': - return './playcanvas.prf.mjs/index.js'; + return './playcanvas.prf.mjs'; case 'DEBUG': - return './playcanvas.dbg.mjs/index.js'; + return './playcanvas.dbg.mjs'; } - return './playcanvas.mjs/index.js'; + return './playcanvas.mjs'; } /** From cbf1a26593aaebd7d5640040405282d8c672d359 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 29 Feb 2024 14:08:49 +0000 Subject: [PATCH 2/6] rollup file cleanup --- examples/jsconfig.json | 2 +- examples/rollup.config.js | 77 ++++++++++++++++----------------------- examples/utils.mjs | 22 +++++++++++ 3 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 examples/utils.mjs diff --git a/examples/jsconfig.json b/examples/jsconfig.json index 857eb430977..6fd2d97d2fc 100644 --- a/examples/jsconfig.json +++ b/examples/jsconfig.json @@ -16,6 +16,6 @@ "esModuleInterop" : true, "moduleResolution" : "node" }, - "include": ["src", "scripts", "iframe", "examples.config.mjs"], + "include": ["src", "scripts", "iframe", "utils.mjs"], "exclude": ["node_modules", "src/lib"] } diff --git a/examples/rollup.config.js b/examples/rollup.config.js index e9cf29674ac..b7d2d483ed7 100644 --- a/examples/rollup.config.js +++ b/examples/rollup.config.js @@ -12,18 +12,26 @@ import replace from '@rollup/plugin-replace'; import resolve from "@rollup/plugin-node-resolve"; import terser from '@rollup/plugin-terser'; +// engine rollup utils import { buildTarget } from '../utils/rollup-build-target.mjs'; import { scriptTargetEs6 } from '../utils/rollup-script-target-es6.mjs'; +// util functions +import { isModuleWithExternalDependencies } from './utils.mjs'; + /** @typedef {import('rollup').RollupOptions} RollupOptions */ /** @typedef {import('rollup').Plugin} RollupPlugin */ const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const NODE_ENV = process.env.NODE_ENV; +const ENGINE_PATH = !process.env.ENGINE_PATH && NODE_ENV === 'development' ? '../src/index.js' : process.env.ENGINE_PATH; + const PCUI_PATH = process.env.PCUI_PATH || 'node_modules/@playcanvas/pcui'; const PCUI_REACT_PATH = path.resolve(PCUI_PATH, 'react'); const PCUI_STYLES_PATH = path.resolve(PCUI_PATH, 'styles'); + const STATIC_FILES = [ // static main page src { src: './src/static', dest: 'dist/' }, @@ -52,58 +60,36 @@ const STATIC_FILES = [ // modules (N.B. destination folder is 'modules' as 'node_modules' are automatically excluded by git pages) { src: './node_modules/monaco-editor/min/vs', dest: 'dist/modules/monaco-editor/min/vs' }, - // N.B. fflate will not be needed once extras module is rolled up - { src: '../node_modules/fflate/esm/', dest: 'dist/modules/fflate/esm' } -]; + // TODO: fflate will not be needed once extras module is rolled up + { src: '../node_modules/fflate/esm/', dest: 'dist/modules/fflate/esm' }, -const regexpExportStarFrom = /^\s*export\s*\*\s*from\s*.+\s*;\s*$/gm; -const regexpExportFrom = /^\s*export\s*{.*}\s*from\s*.+\s*;\s*$/gm; -const regexpImport = /^\s*import\s*.+\s*;\s*$/gm; -/** - * If one of this RegExp's match, it's likely an ESM with external dependencies. - * @example - * isModuleWithExternalDependencies(` - * // Testing variants: - * export * from './index.mjs'; - * export { Ray } from './core/shape/ray.js'; - * import './polyfill/OESVertexArrayObject.js'; - *`); - * @param {string} content - The file content to test. - * @returns {boolean} Whether content is a module. - */ -function isModuleWithExternalDependencies(content) { - const a = regexpExportStarFrom.test(content); - const b = regexpExportFrom.test(content); - const c = regexpImport.test(content); - // console.log('isModuleWithExternalDependencies', { a, b, c }); - return a || b || c; -} + // engine path + ...getEnginePathFiles() +]; -const { NODE_ENV = '' } = process.env; -let { ENGINE_PATH = '' } = process.env; +function getEnginePathFiles() { + if (!ENGINE_PATH) { + return []; + } -// If we don't set ENGINE_PATH and NODE_ENV is 'development', we use ../src/index.js, which -// requires no additional build shells. -if (!ENGINE_PATH && NODE_ENV === 'development') { - ENGINE_PATH = '../src/index.js'; -} -if (ENGINE_PATH) { const src = path.resolve(ENGINE_PATH); const content = fs.readFileSync(src, 'utf8'); - const copyDir = isModuleWithExternalDependencies(content); - if (copyDir) { - // unpacked module builds + const isUnpacked = isModuleWithExternalDependencies(content); + if (isUnpacked) { const srcDir = path.dirname(src); const dest = 'dist/iframe/ENGINE_PATH'; - STATIC_FILES.push({ src: srcDir, dest }); - } else { - // packed module builds - ENGINE_PATH.split("/").pop(); - const dest = 'dist/iframe/ENGINE_PATH/index.js'; - STATIC_FILES.push({ src, dest }); + return [{ src: srcDir, dest }]; } + + // packed module builds + ENGINE_PATH.split("/").pop(); + const dest = 'dist/iframe/ENGINE_PATH/index.js'; + return [{ src, dest }]; } +/** + * @returns {RollupPlugin} - The plugin. + */ function timestamp() { return { name: 'timestamp', @@ -114,7 +100,7 @@ function timestamp() { } /** - * @param {import('rollup').Plugin} plugin - The Rollup plugin. + * @param {RollupPlugin} plugin - The Rollup plugin. * @param {string} src - File or path to watch. */ function watch(plugin, src) { @@ -139,7 +125,7 @@ function watch(plugin, src) { * This plugin copies static files from source to destination. * * @param {STATIC_FILES} targets - Array of source and destination objects. - * @returns {RollupPlugin} The plugin. + * @returns {RollupPlugin} - The plugin. */ function copyStaticFiles(targets) { return { @@ -168,7 +154,7 @@ function copyStaticFiles(targets) { /** * This plugin builds the standalone html files. * - * @returns {RollupPlugin} The plugin. + * @returns {RollupPlugin} - The plugin. */ function buildAndWatchStandaloneExamples() { return { @@ -208,7 +194,6 @@ function getEngineTargets() { return targets; } - export default [ { input: 'src/static/index.html', diff --git a/examples/utils.mjs b/examples/utils.mjs new file mode 100644 index 00000000000..5657c2ae68a --- /dev/null +++ b/examples/utils.mjs @@ -0,0 +1,22 @@ +const regexpExportStarFrom = /^\s*export\s*\*\s*from\s*.+\s*;\s*$/gm; +const regexpExportFrom = /^\s*export\s*{.*}\s*from\s*.+\s*;\s*$/gm; +const regexpImport = /^\s*import\s*.+\s*;\s*$/gm; +/** + * If one of this RegExp's match, it's likely an ESM with external dependencies. + * @example + * isModuleWithExternalDependencies(` + * // Testing variants: + * export * from './index.mjs'; + * export { Ray } from './core/shape/ray.js'; + * import './polyfill/OESVertexArrayObject.js'; + *`); + * @param {string} content - The file content to test. + * @returns {boolean} Whether content is a module. + */ +export function isModuleWithExternalDependencies(content) { + const a = regexpExportStarFrom.test(content); + const b = regexpExportFrom.test(content); + const c = regexpImport.test(content); + // console.log('isModuleWithExternalDependencies', { a, b, c }); + return a || b || c; +} From 618a66f41ac414872142100eec0f455ca047f5ad Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 29 Feb 2024 14:12:42 +0000 Subject: [PATCH 3/6] fixed env bug --- examples/rollup.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rollup.config.js b/examples/rollup.config.js index b7d2d483ed7..2657fa886f1 100644 --- a/examples/rollup.config.js +++ b/examples/rollup.config.js @@ -24,8 +24,8 @@ import { isModuleWithExternalDependencies } from './utils.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const NODE_ENV = process.env.NODE_ENV; -const ENGINE_PATH = !process.env.ENGINE_PATH && NODE_ENV === 'development' ? '../src/index.js' : process.env.ENGINE_PATH; +const NODE_ENV = process.env.NODE_ENV ?? ''; +const ENGINE_PATH = !process.env.ENGINE_PATH && NODE_ENV === 'development' ? '../src/index.js' : process.env.ENGINE_PATH ?? ''; const PCUI_PATH = process.env.PCUI_PATH || 'node_modules/@playcanvas/pcui'; const PCUI_REACT_PATH = path.resolve(PCUI_PATH, 'react'); From 15871df22ff3cf8ae0b20abdd7f54bb18b57084a Mon Sep 17 00:00:00 2001 From: KPal <48248865+kpal81xd@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:49:34 +0000 Subject: [PATCH 4/6] Update examples/rollup.config.js --- examples/rollup.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/rollup.config.js b/examples/rollup.config.js index 2657fa886f1..a8abd599bbf 100644 --- a/examples/rollup.config.js +++ b/examples/rollup.config.js @@ -82,7 +82,6 @@ function getEnginePathFiles() { } // packed module builds - ENGINE_PATH.split("/").pop(); const dest = 'dist/iframe/ENGINE_PATH/index.js'; return [{ src, dest }]; } From 17e83a78675382f753679bb8ba745ed2deff8737 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 1 Mar 2024 14:51:47 +0000 Subject: [PATCH 5/6] jsdoc fixes and line wrapping --- examples/rollup.config.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/rollup.config.js b/examples/rollup.config.js index a8abd599bbf..defb8e7460a 100644 --- a/examples/rollup.config.js +++ b/examples/rollup.config.js @@ -25,7 +25,9 @@ import { isModuleWithExternalDependencies } from './utils.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const NODE_ENV = process.env.NODE_ENV ?? ''; -const ENGINE_PATH = !process.env.ENGINE_PATH && NODE_ENV === 'development' ? '../src/index.js' : process.env.ENGINE_PATH ?? ''; +const ENGINE_PATH = !process.env.ENGINE_PATH && NODE_ENV === 'development' ? + '../src/index.js' : + process.env.ENGINE_PATH ?? ''; const PCUI_PATH = process.env.PCUI_PATH || 'node_modules/@playcanvas/pcui'; const PCUI_REACT_PATH = path.resolve(PCUI_PATH, 'react'); @@ -87,7 +89,7 @@ function getEnginePathFiles() { } /** - * @returns {RollupPlugin} - The plugin. + * @returns {RollupPlugin} The plugin. */ function timestamp() { return { @@ -124,7 +126,7 @@ function watch(plugin, src) { * This plugin copies static files from source to destination. * * @param {STATIC_FILES} targets - Array of source and destination objects. - * @returns {RollupPlugin} - The plugin. + * @returns {RollupPlugin} The plugin. */ function copyStaticFiles(targets) { return { @@ -153,7 +155,7 @@ function copyStaticFiles(targets) { /** * This plugin builds the standalone html files. * - * @returns {RollupPlugin} - The plugin. + * @returns {RollupPlugin} The plugin. */ function buildAndWatchStandaloneExamples() { return { From 2163a00526617db00d4490dfbf7d72533eabfe11 Mon Sep 17 00:00:00 2001 From: KPal <48248865+kpal81xd@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:17:54 +0000 Subject: [PATCH 6/6] Update examples/utils.mjs Co-authored-by: Will Eastcott --- examples/utils.mjs | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/utils.mjs b/examples/utils.mjs index 5657c2ae68a..37940885d4d 100644 --- a/examples/utils.mjs +++ b/examples/utils.mjs @@ -1,22 +1,21 @@ -const regexpExportStarFrom = /^\s*export\s*\*\s*from\s*.+\s*;\s*$/gm; -const regexpExportFrom = /^\s*export\s*{.*}\s*from\s*.+\s*;\s*$/gm; -const regexpImport = /^\s*import\s*.+\s*;\s*$/gm; +const regexPatterns = [ + /^\s*export\s*\*\s*from\s*.+\s*;\s*$/gm, + /^\s*export\s*{.*}\s*from\s*.+\s*;\s*$/gm, + /^\s*import\s*.+\s*;\s*$/gm, +]; + /** - * If one of this RegExp's match, it's likely an ESM with external dependencies. + * Checks if the provided content matches any of a set of patterns indicative of an ES Module with external dependencies. + * Patterns checked include certain export and import statement formats. + * + * @param {string} content The file content to test. + * @returns {boolean} Whether the content is likely an ES Module with external dependencies. * @example * isModuleWithExternalDependencies(` - * // Testing variants: - * export * from './index.mjs'; - * export { Ray } from './core/shape/ray.js'; - * import './polyfill/OESVertexArrayObject.js'; - *`); - * @param {string} content - The file content to test. - * @returns {boolean} Whether content is a module. + * // Testing variants: + * export * from './index.mjs'; + * export { Ray } from './core/shape/ray.js'; + * import './polyfill/OESVertexArrayObject.js'; + * `); */ -export function isModuleWithExternalDependencies(content) { - const a = regexpExportStarFrom.test(content); - const b = regexpExportFrom.test(content); - const c = regexpImport.test(content); - // console.log('isModuleWithExternalDependencies', { a, b, c }); - return a || b || c; -} +export const isModuleWithExternalDependencies = (content) => regexPatterns.some(pattern => pattern.test(content));