From 8395158b2ed78138784b671fe01b1e7913094939 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Thu, 28 Aug 2025 21:38:59 +0100 Subject: [PATCH 1/4] Proof of concept for modular build --- rollup.config.mjs | 46 +++++++++++---------- src/app.js | 2 +- src/color/color_conversion.js | 6 +-- src/color/p5.Color.js | 3 +- src/core/friendly_errors/param_validator.js | 26 ++++++------ src/core/index.js | 20 +++++++++ src/core/init.js | 2 +- 7 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 src/core/index.js diff --git a/rollup.config.mjs b/rollup.config.mjs index 0c48afb163..bf30a120fc 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -10,7 +10,7 @@ import alias from '@rollup/plugin-alias'; import { globSync } from 'glob'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { rmSync } from 'node:fs'; +import { rmSync, readdirSync } from 'node:fs'; const plugins = [ commonjs(), @@ -36,22 +36,30 @@ const bundleSize = (name, sourcemap) => { }); }; -const modules = ['math']; +const modules = + readdirSync('./src', { withFileTypes: true }) + .filter(dirent => { + return dirent.isDirectory() && + !['color'].includes(dirent.name); + }) + .map(dirent => dirent.name); const generateModuleBuild = () => { - return modules.map((module) => { + return modules.map(mod => { return { - input: `src/${module}/index.js`, + input: `src/${mod}/index.js`, output: [ { - file: `./lib/p5.${module}.js`, + file: `./lib/p5.${mod}.js`, format: 'iife', + name: mod === 'core' ? 'p5' : undefined, plugins: [ - bundleSize(`p5.${module}.js`) + bundleSize(`p5.${mod}.js`) ] }, { - file: `./lib/p5.${module}.min.js`, + file: `./lib/p5.${mod}.min.js`, format: 'iife', + name: mod === 'core' ? 'p5' : undefined, sourcemap: 'hidden', plugins: [ terser({ @@ -64,14 +72,7 @@ const generateModuleBuild = () => { comments: false } }), - bundleSize(`p5.${module}.min.js`) - ] - }, - { - file: `./lib/p5.${module}.esm.js`, - format: 'esm', - plugins: [ - bundleSize(`p5.${module}.esm.js`) + bundleSize(`p5.${mod}.min.js`) ] } ], @@ -83,13 +84,13 @@ const generateModuleBuild = () => { }); }; -rmSync("./dist", { +rmSync('./dist', { force: true, recursive: true }); export default [ - //// Library builds (IIFE and ESM) //// + // Library builds (IIFE and ESM) //// { input: 'src/app.js', output: [ @@ -137,7 +138,7 @@ export default [ ...plugins ] }, - //// Minified build //// + // Minified build //// { input: 'src/app.js', output: [ @@ -174,7 +175,7 @@ export default [ ...plugins ] }, - //// ESM source build //// + // ESM source build //// { input: Object.fromEntries( globSync('src/**/*.js').map(file => [ @@ -195,7 +196,8 @@ export default [ }, external: /node_modules/, plugins - } - // NOTE: comment to NOT build standalone math module - // ...generateModuleBuild() + }, + + // NOTE: comment to NOT build standalone modules + ...generateModuleBuild() ]; diff --git a/src/app.js b/src/app.js index 83129d4d30..ce3d1fcd4a 100644 --- a/src/app.js +++ b/src/app.js @@ -51,7 +51,7 @@ import webgl from './webgl'; webgl(p5); // typography -import type from './type' +import type from './type'; type(p5); import { waitForDocumentReady, waitingForTranslator, _globalInit } from './core/init'; diff --git a/src/color/color_conversion.js b/src/color/color_conversion.js index 850d24918b..d2841e7abd 100644 --- a/src/color/color_conversion.js +++ b/src/color/color_conversion.js @@ -13,8 +13,8 @@ * interchangeably. */ -import p5 from '../core/main'; -p5.ColorConversion = { +// import p5 from '../core/main'; +const ColorConversion = { /** * Convert an HSBA array to HSLA. */ @@ -267,4 +267,4 @@ p5.ColorConversion = { return [hue / 6, sat, li / 2, rgba[3]]; } }; -export default p5.ColorConversion; +export default ColorConversion; diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index b5a278f30b..25bc4a6fef 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -859,5 +859,6 @@ export default color; export { Color } if(typeof p5 !== 'undefined'){ - color(p5, p5.prototype); + // color(p5, p5.prototype); + p5.registerAddon(color); } diff --git a/src/core/friendly_errors/param_validator.js b/src/core/friendly_errors/param_validator.js index 915e084d96..7f201eada3 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/core/friendly_errors/param_validator.js @@ -124,15 +124,15 @@ function validateParams(p5, fn, lifecycles) { * * Example parameter data for function `background`: * "background": { - "overloads": [ - ["p5.Color"], - ["String", "Number?"], - ["Number", "Number?"], - ["Number", "Number", "Number", "Number?"], - ["Number[]"], - ["p5.Image", "Number?"] - ] - } + * "overloads": [ + * ["p5.Color"], + * ["String", "Number?"], + * ["Number", "Number?"], + * ["Number", "Number", "Number", "Number?"], + * ["Number[]"], + * ["p5.Image", "Number?"] + * ] + * } * Where each array in `overloads` represents a set of valid overloaded * parameters, and `?` is a shorthand for `Optional`. * @@ -174,6 +174,8 @@ function validateParams(p5, fn, lifecycles) { // All p5 objects start with `p5` in the documentation, i.e. `p5.Camera`. else if (/^p5\.[a-zA-Z0-9]+$/.exec(baseType) || baseType === 'p5') { const className = baseType.substring(baseType.indexOf('.') + 1); + // NOTE: Will need to refactor to account for classes not imported + if(!p5Constructors[className]) return z.any(); typeSchema = z.instanceof(p5Constructors[className]); } // For primitive types and web API objects. @@ -294,7 +296,7 @@ function validateParams(p5, fn, lifecycles) { return overloadSchemas.length === 1 ? overloadSchemas[0] : z.union(overloadSchemas); - } + }; /** * Finds the closest schema to the input arguments. @@ -373,7 +375,7 @@ function validateParams(p5, fn, lifecycles) { }); return closestSchema; - } + }; /** * Prints a friendly error message after parameter validation, if validation @@ -499,7 +501,7 @@ function validateParams(p5, fn, lifecycles) { console.log(message); } return message; - } + }; /** * Runs parameter validation by matching the input parameters to Zod schemas diff --git a/src/core/index.js b/src/core/index.js new file mode 100644 index 0000000000..99351a4f05 --- /dev/null +++ b/src/core/index.js @@ -0,0 +1,20 @@ +// core +import p5 from './main'; + +// color +import color from '../color'; +color(p5); + +// accessibility +import accessibility from '../accessibility'; +accessibility(p5); + +// FES +import friendlyErrors from './friendly_errors'; +friendlyErrors(p5); + +import { waitForDocumentReady, waitingForTranslator, _globalInit } from './init'; +Promise.all([waitForDocumentReady(), waitingForTranslator]).then(_globalInit); + +export default p5; + diff --git a/src/core/init.js b/src/core/init.js index 764437b1ff..fb197ade11 100644 --- a/src/core/init.js +++ b/src/core/init.js @@ -1,4 +1,4 @@ -import p5 from '../core/main'; +// import p5 from '../core/main'; import { initialize as initTranslator } from './internationalization'; /** From e7bc1e7d1de5c4200dc03380ebcc8717892da1c3 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Thu, 28 Aug 2025 21:45:37 +0100 Subject: [PATCH 2/4] Move FES to own module folder --- rollup.config.mjs | 2 +- src/app.js | 2 +- src/core/index.js | 2 +- src/{core => }/friendly_errors/browser_errors.js | 0 src/{core => }/friendly_errors/fes_core.js | 4 ++-- src/{core => }/friendly_errors/file_errors.js | 2 +- src/{core => }/friendly_errors/index.js | 0 src/{core => }/friendly_errors/param_validator.js | 4 ++-- src/{core => }/friendly_errors/sketch_verifier.js | 2 +- src/{core => }/friendly_errors/stacktrace.js | 0 test/unit/core/param_errors.js | 4 ++-- test/unit/core/sketch_overrides.js | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) rename src/{core => }/friendly_errors/browser_errors.js (100%) rename src/{core => }/friendly_errors/fes_core.js (99%) rename src/{core => }/friendly_errors/file_errors.js (97%) rename src/{core => }/friendly_errors/index.js (100%) rename src/{core => }/friendly_errors/param_validator.js (99%) rename src/{core => }/friendly_errors/sketch_verifier.js (99%) rename src/{core => }/friendly_errors/stacktrace.js (100%) diff --git a/rollup.config.mjs b/rollup.config.mjs index bf30a120fc..c7942dc3d3 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -169,7 +169,7 @@ export default [ plugins: [ alias({ entries: [ - { find: './core/friendly_errors', replacement: './core/noop' } + { find: './friendly_errors', replacement: './core/noop' } ] }), ...plugins diff --git a/src/app.js b/src/app.js index ce3d1fcd4a..9e5de2470d 100644 --- a/src/app.js +++ b/src/app.js @@ -15,7 +15,7 @@ color(p5); // core // currently, it only contains the test for parameter validation -import friendlyErrors from './core/friendly_errors'; +import friendlyErrors from './friendly_errors'; friendlyErrors(p5); // data diff --git a/src/core/index.js b/src/core/index.js index 99351a4f05..9419ddd484 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -10,7 +10,7 @@ import accessibility from '../accessibility'; accessibility(p5); // FES -import friendlyErrors from './friendly_errors'; +import friendlyErrors from '../friendly_errors'; friendlyErrors(p5); import { waitForDocumentReady, waitingForTranslator, _globalInit } from './init'; diff --git a/src/core/friendly_errors/browser_errors.js b/src/friendly_errors/browser_errors.js similarity index 100% rename from src/core/friendly_errors/browser_errors.js rename to src/friendly_errors/browser_errors.js diff --git a/src/core/friendly_errors/fes_core.js b/src/friendly_errors/fes_core.js similarity index 99% rename from src/core/friendly_errors/fes_core.js rename to src/friendly_errors/fes_core.js index 0a7e58d4fe..9de49292b0 100644 --- a/src/core/friendly_errors/fes_core.js +++ b/src/friendly_errors/fes_core.js @@ -22,9 +22,9 @@ * sequence of each function, please look at the FES Reference + Dev Notes: * https://github.com/processing/p5.js/blob/main/contributor_docs/fes_reference_dev_notes.md */ -import { translator } from '../internationalization'; +import { translator } from '../core/internationalization'; import errorTable from './browser_errors'; -import * as contants from '../constants'; +import * as contants from '../core/constants'; function fesCore(p5, fn){ // p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta diff --git a/src/core/friendly_errors/file_errors.js b/src/friendly_errors/file_errors.js similarity index 97% rename from src/core/friendly_errors/file_errors.js rename to src/friendly_errors/file_errors.js index 8f212c8355..8316074d32 100644 --- a/src/core/friendly_errors/file_errors.js +++ b/src/friendly_errors/file_errors.js @@ -2,7 +2,7 @@ * @for p5 * @requires core */ -import { translator } from '../internationalization'; +import { translator } from '../core/internationalization'; function fileErrors(p5, fn){ // mapping used by `_friendlyFileLoadError` diff --git a/src/core/friendly_errors/index.js b/src/friendly_errors/index.js similarity index 100% rename from src/core/friendly_errors/index.js rename to src/friendly_errors/index.js diff --git a/src/core/friendly_errors/param_validator.js b/src/friendly_errors/param_validator.js similarity index 99% rename from src/core/friendly_errors/param_validator.js rename to src/friendly_errors/param_validator.js index 7f201eada3..bf129317c4 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/friendly_errors/param_validator.js @@ -2,9 +2,9 @@ * @for p5 * @requires core */ -import * as constants from '../constants.js'; +import * as constants from '../core/constants.js'; import { z } from 'zod/v4'; -import dataDoc from '../../../docs/parameterData.json'; +import dataDoc from '../../docs/parameterData.json'; function validateParams(p5, fn, lifecycles) { // Cache for Zod schemas diff --git a/src/core/friendly_errors/sketch_verifier.js b/src/friendly_errors/sketch_verifier.js similarity index 99% rename from src/core/friendly_errors/sketch_verifier.js rename to src/friendly_errors/sketch_verifier.js index 66b2b7027b..db7423843b 100644 --- a/src/core/friendly_errors/sketch_verifier.js +++ b/src/friendly_errors/sketch_verifier.js @@ -1,6 +1,6 @@ import { parse } from 'acorn'; import { simple as walk } from 'acorn-walk'; -import * as constants from '../constants'; +import * as constants from '../core/constants'; // List of functions to ignore as they either are meant to be re-defined or // generate false positive outputs. diff --git a/src/core/friendly_errors/stacktrace.js b/src/friendly_errors/stacktrace.js similarity index 100% rename from src/core/friendly_errors/stacktrace.js rename to src/friendly_errors/stacktrace.js diff --git a/test/unit/core/param_errors.js b/test/unit/core/param_errors.js index 26a9efe4fb..a3cc6b79d4 100644 --- a/test/unit/core/param_errors.js +++ b/test/unit/core/param_errors.js @@ -1,4 +1,4 @@ -import validateParams from '../../../src/core/friendly_errors/param_validator.js'; +import validateParams from '../../../src/friendly_errors/param_validator.js'; import * as constants from '../../../src/core/constants.js'; suite('Validate Params', function () { @@ -278,4 +278,4 @@ suite('Validate Params', function () { assert.isFalse(result.success); }); }); -}); \ No newline at end of file +}); diff --git a/test/unit/core/sketch_overrides.js b/test/unit/core/sketch_overrides.js index 7a8f398fde..83be2cc0f9 100644 --- a/test/unit/core/sketch_overrides.js +++ b/test/unit/core/sketch_overrides.js @@ -1,6 +1,6 @@ import { verifierUtils -} from '../../../src/core/friendly_errors/sketch_verifier.js'; +} from '../../../src/friendly_errors/sketch_verifier.js'; suite('Sketch Verifier', function () { const mockP5 = { From 39a39512b7330e5355332473a632a6312b252291 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Thu, 28 Aug 2025 23:04:26 +0100 Subject: [PATCH 3/4] Split module build out into custom Rollup build --- rollup.config.mjs | 55 +--------------- rollup.modules.mjs | 89 ++++++++++++++++++++++++++ src/friendly_errors/fes_core.js | 2 - src/friendly_errors/sketch_verifier.js | 11 +--- 4 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 rollup.modules.mjs diff --git a/rollup.config.mjs b/rollup.config.mjs index c7942dc3d3..245cefac3b 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -10,7 +10,7 @@ import alias from '@rollup/plugin-alias'; import { globSync } from 'glob'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { rmSync, readdirSync } from 'node:fs'; +import { rmSync } from 'node:fs'; const plugins = [ commonjs(), @@ -36,54 +36,6 @@ const bundleSize = (name, sourcemap) => { }); }; -const modules = - readdirSync('./src', { withFileTypes: true }) - .filter(dirent => { - return dirent.isDirectory() && - !['color'].includes(dirent.name); - }) - .map(dirent => dirent.name); -const generateModuleBuild = () => { - return modules.map(mod => { - return { - input: `src/${mod}/index.js`, - output: [ - { - file: `./lib/p5.${mod}.js`, - format: 'iife', - name: mod === 'core' ? 'p5' : undefined, - plugins: [ - bundleSize(`p5.${mod}.js`) - ] - }, - { - file: `./lib/p5.${mod}.min.js`, - format: 'iife', - name: mod === 'core' ? 'p5' : undefined, - sourcemap: 'hidden', - plugins: [ - terser({ - compress: { - global_defs: { - IS_MINIFIED: true - } - }, - format: { - comments: false - } - }), - bundleSize(`p5.${mod}.min.js`) - ] - } - ], - external: ['../core/main'], - plugins: [ - ...plugins - ] - }; - }); -}; - rmSync('./dist', { force: true, recursive: true @@ -196,8 +148,5 @@ export default [ }, external: /node_modules/, plugins - }, - - // NOTE: comment to NOT build standalone modules - ...generateModuleBuild() + } ]; diff --git a/rollup.modules.mjs b/rollup.modules.mjs new file mode 100644 index 0000000000..36ee13d2e3 --- /dev/null +++ b/rollup.modules.mjs @@ -0,0 +1,89 @@ +import { readdirSync } from 'node:fs'; +import { writeFile } from 'node:fs/promises'; +import { rollup } from 'rollup'; + +import commonjs from '@rollup/plugin-commonjs'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import json from '@rollup/plugin-json'; +import { string } from 'rollup-plugin-string'; +import replace from '@rollup/plugin-replace'; +import pkg from './package.json' with { type: 'json' }; +import terser from '@rollup/plugin-terser'; + +const plugins = [ + commonjs(), + nodeResolve(), + json(), + string({ + include: 'src/webgl/shaders/**/*' + }), + replace({ + values: { + 'VERSION_WILL_BE_REPLACED_BY_BUILD': pkg.version + }, + preventAssignment: true + }) +]; + +const modules = + readdirSync('./src', { withFileTypes: true }) + .filter(dirent => { + return dirent.isDirectory() && + !['color'].includes(dirent.name); + }) + .map(dirent => dirent.name); + +const builds = modules.map(mod => { + const inputOptions = { + input: `src/${mod}/index.js`, + plugins + }; + + const outputOptions = [ + { + file: `./lib/p5.${mod}.js`, + format: 'iife', + name: mod === 'core' ? 'p5' : undefined + }, + { + file: `./lib/p5.${mod}.min.js`, + format: 'iife', + name: mod === 'core' ? 'p5' : undefined, + sourcemap: 'hidden', + plugins: [ + terser({ + compress: { + global_defs: { + IS_MINIFIED: true + } + }, + format: { + comments: false + } + }) + ] + } + ]; + + return build(inputOptions, outputOptions); +}); + +await Promise.all(builds); + +async function build(inputOptions, outputOptionsList){ + let bundle; + try { + bundle = await rollup(inputOptions); + + for(const outputOptions of outputOptionsList){ + const { output } = await bundle.generate(outputOptions); + await writeFile(outputOptions.file, output[0].code); + } + } catch(err) { + console.error(err); + } + + if(bundle){ + await bundle.close(); + } +} diff --git a/src/friendly_errors/fes_core.js b/src/friendly_errors/fes_core.js index 9de49292b0..5163fa8499 100644 --- a/src/friendly_errors/fes_core.js +++ b/src/friendly_errors/fes_core.js @@ -52,8 +52,6 @@ function fesCore(p5, fn){ } else { let doFriendlyWelcome = false; // TEMP until we get it all working LM - // const errorTable = require('./browser_errors').default; - // -- Borrowed from jQuery 1.11.3 -- const class2type = {}; const toString = class2type.toString; diff --git a/src/friendly_errors/sketch_verifier.js b/src/friendly_errors/sketch_verifier.js index db7423843b..7394b1e687 100644 --- a/src/friendly_errors/sketch_verifier.js +++ b/src/friendly_errors/sketch_verifier.js @@ -24,17 +24,10 @@ const ignoreFunction = [ 'keyPressed', 'keyReleased', 'keyTyped', - 'windowResized', - // 'name', - // 'parent', - // 'toString', - // 'print', - // 'stop', - // 'onended' + 'windowResized' ]; export const verifierUtils = { - /** * Fetches the contents of a script element in the user's sketch. * @@ -42,7 +35,7 @@ export const verifierUtils = { * @method fetchScript * @param {HTMLScriptElement} script * @returns {Promise} - */ + */ fetchScript: async function (script) { if (script.src) { try { From 6875a01cbd1b8347666cd22bc0ce29ebfa492f86 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Fri, 29 Aug 2025 10:29:11 +0100 Subject: [PATCH 4/4] Refactor build script for reusability --- rollup.modules.mjs | 108 ++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/rollup.modules.mjs b/rollup.modules.mjs index 36ee13d2e3..2ad680677e 100644 --- a/rollup.modules.mjs +++ b/rollup.modules.mjs @@ -1,7 +1,6 @@ -import { readdirSync } from 'node:fs'; +import { readdirSync, mkdirSync, rmSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; import { rollup } from 'rollup'; - import commonjs from '@rollup/plugin-commonjs'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import json from '@rollup/plugin-json'; @@ -10,66 +9,75 @@ import replace from '@rollup/plugin-replace'; import pkg from './package.json' with { type: 'json' }; import terser from '@rollup/plugin-terser'; -const plugins = [ - commonjs(), - nodeResolve(), - json(), - string({ - include: 'src/webgl/shaders/**/*' - }), - replace({ - values: { - 'VERSION_WILL_BE_REPLACED_BY_BUILD': pkg.version - }, - preventAssignment: true - }) -]; - +const coreModules = ['accessibility', 'color', 'friendly_errors']; const modules = readdirSync('./src', { withFileTypes: true }) .filter(dirent => { return dirent.isDirectory() && - !['color'].includes(dirent.name); + !coreModules.includes(dirent.name); }) .map(dirent => dirent.name); -const builds = modules.map(mod => { - const inputOptions = { - input: `src/${mod}/index.js`, - plugins - }; +await buildModules(modules); - const outputOptions = [ - { - file: `./lib/p5.${mod}.js`, - format: 'iife', - name: mod === 'core' ? 'p5' : undefined - }, - { - file: `./lib/p5.${mod}.min.js`, - format: 'iife', - name: mod === 'core' ? 'p5' : undefined, - sourcemap: 'hidden', - plugins: [ - terser({ - compress: { - global_defs: { - IS_MINIFIED: true - } - }, - format: { - comments: false - } - }) - ] - } +// Build modules given by array of module name strings +export async function buildModules(modules){ + const plugins = [ + commonjs(), + nodeResolve(), + json(), + string({ + include: 'src/webgl/shaders/**/*' + }), + replace({ + values: { + 'VERSION_WILL_BE_REPLACED_BY_BUILD': pkg.version + }, + preventAssignment: true + }) ]; - return build(inputOptions, outputOptions); -}); + const builds = modules.map(mod => { + const inputOptions = { + input: `src/${mod}/index.js`, + plugins + }; -await Promise.all(builds); + const outputOptions = [ + { + file: `./lib/modules/p5.${mod}.js`, + format: 'iife', + name: mod === 'core' ? 'p5' : undefined + }, + { + file: `./lib/modules/p5.${mod}.min.js`, + format: 'iife', + name: mod === 'core' ? 'p5' : undefined, + sourcemap: 'hidden', + plugins: [ + terser({ + compress: { + global_defs: { + IS_MINIFIED: true + } + }, + format: { + comments: false + } + }) + ] + } + ]; + + rmSync('./lib/modules', { recursive: true, force: true }); + mkdirSync('./lib/modules', { recursive: true }); + return build(inputOptions, outputOptions); + }); + + await Promise.all(builds); +} +// Rollup build simple pipeline async function build(inputOptions, outputOptionsList){ let bundle; try {