Skip to content

Commit

Permalink
Examples build fixes (#6102)
Browse files Browse the repository at this point in the history
* added prebuild to occur during rollup process; fixed prod build to use new bundled modules

* rollup file cleanup

* fixed env bug

* Update examples/rollup.config.js

* jsdoc fixes and line wrapping

* Update examples/utils.mjs

Co-authored-by: Will Eastcott <[email protected]>

---------

Co-authored-by: Will Eastcott <[email protected]>
  • Loading branch information
kpal81xd and willeastcott authored Mar 5, 2024
1 parent d75dba5 commit 8202ad8
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 101 deletions.
2 changes: 1 addition & 1 deletion examples/jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
4 changes: 2 additions & 2 deletions examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
"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",
"build:thumbnails": "node ./scripts/thumbnails.mjs",
"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\"",
Expand Down
169 changes: 74 additions & 95 deletions examples/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -12,19 +12,29 @@ 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 staticFiles = [

const STATIC_FILES = [
// static main page src
{ src: './src/static', dest: 'dist/' },

Expand All @@ -51,59 +61,36 @@ 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' }
];

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;
}
// TODO: fflate will not be needed once extras module is rolled up
{ src: '../node_modules/fflate/esm/', dest: 'dist/modules/fflate/esm' },

const { NODE_ENV = '' } = process.env;
let { ENGINE_PATH = '' } = process.env;
// engine path
...getEnginePathFiles()
];

// 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';
}
function getEnginePathFiles() {
if (!ENGINE_PATH) {
return [];
}

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
const isUnpacked = isModuleWithExternalDependencies(content);
if (isUnpacked) {
const srcDir = path.dirname(src);
const dest = 'dist/iframe/ENGINE_PATH';
staticFiles.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 });
return [{ src: srcDir, dest }];
}

// packed module builds
const dest = 'dist/iframe/ENGINE_PATH/index.js';
return [{ src, dest }];
}

/**
* @returns {RollupPlugin} The plugin.
*/
function timestamp() {
return {
name: 'timestamp',
Expand All @@ -114,7 +101,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) {
Expand All @@ -138,7 +125,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) {
Expand Down Expand Up @@ -181,21 +168,45 @@ 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',
Expand All @@ -204,7 +215,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({
Expand All @@ -217,43 +234,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;
6 changes: 3 additions & 3 deletions examples/scripts/standalone-html.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}

/**
Expand Down
21 changes: 21 additions & 0 deletions examples/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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,
];

/**
* 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';
* `);
*/
export const isModuleWithExternalDependencies = (content) => regexPatterns.some(pattern => pattern.test(content));

0 comments on commit 8202ad8

Please sign in to comment.