diff --git a/build.mjs b/build.mjs index e7e88ad56d6..7e76e821c56 100644 --- a/build.mjs +++ b/build.mjs @@ -1,6 +1,6 @@ /** - * Build helper scripts - * Usage: node build.mjs [options] -- [rollup options] + * Build helper script using esbuild for JS targets and Rollup for types. + * Usage: node build.mjs [options] * * Options: * target[:][:][:] - Specify the target @@ -9,37 +9,177 @@ * - bundleState (unbundled, bundled) * Example: target:esm:release:bundled * - * treemap - Enable treemap build visualization (release only). - * treenet - Enable treenet build visualization (release only). - * treesun - Enable treesun build visualization (release only). - * treeflame - Enable treeflame build visualization (release only). + * -w / --watch - Enable watch mode (rebuilds on file changes). */ -import { execSync } from 'child_process'; +import fs from 'fs'; +import { buildTarget, OUT_PREFIX } from './utils/esbuild-build-target.mjs'; +import { version, revision } from './utils/rollup-version-revision.mjs'; +import { buildTypesOption } from './utils/rollup-build-target.mjs'; + +const CYAN_OUT = '\x1b[36m'; +const BLUE_OUT = '\x1b[34m'; +const GREEN_OUT = '\x1b[32m'; +const RED_OUT = '\x1b[31m'; +const BOLD_OUT = '\x1b[1m'; +const REGULAR_OUT = '\x1b[22m'; +const RESET_OUT = '\x1b[0m'; + +const BUILD_TYPES = ['release', 'debug', 'profiler', 'min']; +const MODULE_FORMAT = ['umd', 'esm']; +const BUNDLE_STATES = ['unbundled', 'bundled']; const args = process.argv.slice(2); -const ENV_START_MATCHES = [ - 'target', - 'treemap', - 'treenet', - 'treesun', - 'treeflame' -]; - -const env = []; -for (let i = 0; i < args.length; i++) { - if (ENV_START_MATCHES.some(match => args[i].startsWith(match)) && args[i - 1] !== '--environment') { - env.push(`--environment ${args[i]}`); - args.splice(i, 1); - i--; - continue; +// Extract target and flags +let envTarget = null; +let watchMode = false; +for (const arg of args) { + if (arg.startsWith('target')) { + const parts = arg.split(':'); + envTarget = parts.slice(1).join(':').toLowerCase() || null; + } + if (arg === '-w' || arg === '--watch') { + watchMode = true; + } +} + +const title = [ + 'Building PlayCanvas Engine', + `version ${BOLD_OUT}v${version}${REGULAR_OUT}`, + `revision ${BOLD_OUT}${revision}${REGULAR_OUT}`, + `target ${BOLD_OUT}${envTarget ?? 'all'}${REGULAR_OUT}` +].join('\n'); +console.log(`${BLUE_OUT}${title}${RESET_OUT}`); + +if (envTarget === null && fs.existsSync('build')) { + fs.rmSync('build', { recursive: true }); +} + +function includeBuild(buildType, moduleFormat, bundleState) { + return envTarget === null || + envTarget === buildType || + envTarget === moduleFormat || + envTarget === bundleState || + envTarget === `${moduleFormat}:${buildType}` || + envTarget === `${moduleFormat}:${bundleState}` || + envTarget === `${buildType}:${bundleState}` || + envTarget === `${moduleFormat}:${buildType}:${bundleState}`; +} + +// Collect JS build targets +const jsTargets = []; +BUILD_TYPES.forEach((buildType) => { + MODULE_FORMAT.forEach((moduleFormat) => { + BUNDLE_STATES.forEach((bundleState) => { + if (bundleState === 'unbundled' && moduleFormat === 'umd') return; + if (bundleState === 'unbundled' && buildType === 'min') return; + if (!includeBuild(buildType, moduleFormat, bundleState)) return; + + jsTargets.push({ moduleFormat, buildType, bundleState }); + }); + }); +}); + +const buildTypes = envTarget === null || envTarget === 'types'; + +if (!jsTargets.length && !buildTypes) { + console.error(`${RED_OUT}${BOLD_OUT}No targets found${RESET_OUT}`); + process.exit(1); +} + +/** + * Get the output path description for a build target (matches Rollup's display). + * + * @param {object} target - The build target. + * @returns {string} The output path. + */ +function getOutputPath(target) { + const prefix = OUT_PREFIX[target.buildType]; + const isUMD = target.moduleFormat === 'umd'; + const bundled = isUMD || target.buildType === 'min' || target.bundleState === 'bundled'; + if (bundled) { + return `build/${prefix}${isUMD ? '.js' : '.mjs'}`; } + return `build/${prefix}/`; +} + +/** + * Build all JS targets using esbuild. + */ +async function buildAllJS() { + await Promise.all(jsTargets.map(async (target) => { + const output = getOutputPath(target); + console.log(`${CYAN_OUT}${BOLD_OUT}src/index.js${REGULAR_OUT} \u2192 ${BOLD_OUT}${output}${REGULAR_OUT}...${RESET_OUT}`); + const buildStart = performance.now(); + try { + await buildTarget(target); + const elapsed = ((performance.now() - buildStart) / 1000).toFixed(1); + console.log(`${GREEN_OUT}created ${BOLD_OUT}${output}${REGULAR_OUT} in ${BOLD_OUT}${elapsed}s${REGULAR_OUT}${RESET_OUT}`); + } catch (err) { + console.error(`${RED_OUT}${BOLD_OUT}error building ${output}${REGULAR_OUT}: ${err.message}${RESET_OUT}`); + throw err; + } + })); +} + +/** + * Build TypeScript definitions using Rollup + rollup-plugin-dts. + */ +async function buildAllTypes() { + const typesOutput = 'build/playcanvas.d.ts'; + console.log(`${CYAN_OUT}${BOLD_OUT}src/index.js${REGULAR_OUT} \u2192 ${BOLD_OUT}${typesOutput}${REGULAR_OUT}...${RESET_OUT}`); + const startTime = performance.now(); + + const { rollup } = await import('rollup'); + const typesConfig = buildTypesOption(); + + const bundle = await rollup({ + input: typesConfig.input, + plugins: typesConfig.plugins + }); + + const outputOptions = Array.isArray(typesConfig.output) ? typesConfig.output : [typesConfig.output]; + await Promise.all(outputOptions.map(output => bundle.write(output))); + await bundle.close(); + + const elapsed = ((performance.now() - startTime) / 1000).toFixed(1); + console.log(`${GREEN_OUT}created ${BOLD_OUT}${typesOutput}${REGULAR_OUT} in ${BOLD_OUT}${elapsed}s${REGULAR_OUT}${RESET_OUT}`); } -const cmd = `rollup -c ${args.join(' ')} ${env.join(' ')}`; -try { - execSync(cmd, { stdio: 'inherit' }); -} catch (e) { - console.error(e.message); +// Main execution +async function main() { + try { + if (jsTargets.length) { + await buildAllJS(); + } + + if (buildTypes) { + await buildAllTypes(); + } + + if (watchMode) { + console.log(`${BLUE_OUT}Watching for changes...${RESET_OUT}`); + let debounceTimer = null; + + fs.watch('src', { recursive: true }, (eventType, filename) => { + if (!filename?.endsWith('.js')) return; + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(async () => { + console.log(`\n${BLUE_OUT}Change detected: ${filename}${RESET_OUT}`); + try { + if (jsTargets.length) await buildAllJS(); + if (buildTypes) await buildAllTypes(); + } catch (e) { + console.error(e.message); + } + }, 100); + }); + } + } catch (e) { + console.error(e.message); + if (!watchMode) process.exit(1); + } } + +main(); diff --git a/examples/rollup.config.mjs b/examples/rollup.config.mjs index 5c961b0324a..d06d51c36ba 100644 --- a/examples/rollup.config.mjs +++ b/examples/rollup.config.mjs @@ -7,7 +7,8 @@ import replace from '@rollup/plugin-replace'; import terser from '@rollup/plugin-terser'; import { exampleMetaData } from './cache/metadata.mjs'; -import { buildJSOptions, buildTypesOption } from '../utils/rollup-build-target.mjs'; +import { buildTarget } from '../utils/esbuild-build-target.mjs'; +import { buildTypesOption } from '../utils/rollup-build-target.mjs'; import { version } from '../utils/rollup-version-revision.mjs'; import { buildHtml } from './utils/plugins/rollup-build-html.mjs'; import { buildShare } from './utils/plugins/rollup-build-share.mjs'; @@ -182,18 +183,41 @@ const EXAMPLE_TARGETS = exampleMetaData.flatMap(({ categoryKebab, exampleNameKeb return options; }); -const ENGINE_TARGETS = (() => { +/** + * Build engine JS targets with esbuild before Rollup runs. + * Replaces the previous Rollup-based buildJSOptions calls. + */ +async function buildEngineJS() { + const builds = []; + const opts = { + moduleFormat: /** @type {'esm'} */ ('esm'), + bundleState: /** @type {'bundled'} */ ('bundled'), + input: '../src/index.js', + dir: 'dist/iframe' + }; + + if (NODE_ENV === 'production') { + builds.push(buildTarget({ ...opts, buildType: 'release' })); + } + if (NODE_ENV === 'production' || NODE_ENV === 'development') { + builds.push(buildTarget({ ...opts, buildType: 'debug' })); + } + if (NODE_ENV === 'production' || NODE_ENV === 'profiler') { + builds.push(buildTarget({ ...opts, buildType: 'profiler' })); + } + + await Promise.all(builds); +} + +const ENGINE_TYPES_TARGETS = (() => { /** @type {RollupOptions[]} */ const options = []; - // Types - // Outputs: dist/iframe/playcanvas.d.ts options.push(buildTypesOption({ root: '..', dir: 'dist/iframe' })); - // Sources if (ENGINE_PATH) { const src = path.resolve(ENGINE_PATH); const content = fs.readFileSync(src, 'utf8'); @@ -210,49 +234,12 @@ const ENGINE_TARGETS = (() => { dest: 'dist/iframe/ENGINE_PATH/index.js' })); } - return options; } - // Builds - if (NODE_ENV === 'production') { - // Outputs: dist/iframe/playcanvas.mjs - options.push( - ...buildJSOptions({ - moduleFormat: 'esm', - buildType: 'release', - bundleState: 'bundled', - input: '../src/index.js', - dir: 'dist/iframe' - }) - ); - } - if (NODE_ENV === 'production' || NODE_ENV === 'development') { - // Outputs: dist/iframe/playcanvas.dbg.mjs - options.push( - ...buildJSOptions({ - moduleFormat: 'esm', - buildType: 'debug', - bundleState: 'bundled', - input: '../src/index.js', - dir: 'dist/iframe' - }) - ); - } - if (NODE_ENV === 'production' || NODE_ENV === 'profiler') { - // Outputs: dist/iframe/playcanvas.prf.mjs - options.push( - ...buildJSOptions({ - moduleFormat: 'esm', - buildType: 'profiler', - bundleState: 'bundled', - input: '../src/index.js', - dir: 'dist/iframe' - }) - ); - } return options; })(); +/** @type {RollupOptions[]} */ const APP_TARGETS = [{ // A debug build is ~2.3MB and a release build ~0.6MB input: 'src/app/index.mjs', @@ -288,9 +275,15 @@ const APP_TARGETS = [{ ] }]; -export default [ - ...STATIC_TARGETS, - ...EXAMPLE_TARGETS, - ...ENGINE_TARGETS, - ...APP_TARGETS -]; +export default async () => { + if (!ENGINE_PATH) { + await buildEngineJS(); + } + + return [ + ...STATIC_TARGETS, + ...EXAMPLE_TARGETS, + ...ENGINE_TYPES_TARGETS, + ...APP_TARGETS + ]; +}; diff --git a/package-lock.json b/package-lock.json index 7a22188b4a2..12aff8137aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,31 +15,28 @@ "devDependencies": { "@playcanvas/eslint-config": "2.1.0", "@rollup/plugin-node-resolve": "16.0.3", - "@rollup/plugin-strip": "3.0.4", - "@rollup/plugin-swc": "0.4.0", "@rollup/plugin-terser": "0.4.4", - "@rollup/pluginutils": "5.3.0", - "@swc/core": "1.15.11", "@types/node": "24.10.13", "c8": "10.1.3", "chai": "6.2.2", + "esbuild": "^0.25.2", "eslint": "9.39.3", "fflate": "0.8.2", "globals": "17.3.0", + "jscc": "^1.1.1", "jsdom": "28.1.0", "mocha": "11.7.5", "nise": "6.1.1", "publint": "0.3.17", "rollup": "4.58.0", "rollup-plugin-dts": "6.3.0", - "rollup-plugin-jscc": "2.0.0", - "rollup-plugin-visualizer": "6.0.8", "serve": "14.2.5", "sinon": "21.0.1", "typedoc": "0.28.17", "typedoc-plugin-mdn-links": "5.1.1", "typedoc-plugin-missing-exports": "4.1.2", - "typescript": "5.9.3" + "typescript": "5.9.3", + "unplugin-strip": "^0.2.1" }, "engines": { "node": ">=18.0.0" @@ -269,22 +266,447 @@ ], "license": "MIT", "engines": { - "node": ">=20.19.0" + "node": ">=20.19.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.50.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz", + "integrity": "sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6", + "@typescript-eslint/types": "^8.11.0", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.50.2", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz", - "integrity": "sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6", - "@typescript-eslint/types": "^8.11.0", - "comment-parser": "1.4.1", - "esquery": "^1.6.0", - "jsdoc-type-pratt-parser": "~4.1.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=18" } @@ -692,52 +1114,6 @@ } } }, - "node_modules/@rollup/plugin-strip": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-strip/-/plugin-strip-3.0.4.tgz", - "integrity": "sha512-LDRV49ZaavxUo2YoKKMQjCxzCxugu1rCPQa0lDYBOWLj6vtzBMr8DcoJjsmg+s450RbKbe3qI9ZLaSO+O1oNbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-swc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-swc/-/plugin-swc-0.4.0.tgz", - "integrity": "sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "smob": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@swc/core": "^1.3.0", - "rollup": "^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@rollup/plugin-terser": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", @@ -1238,232 +1614,6 @@ "dev": true, "license": "(Unlicense OR Apache-2.0)" }, - "node_modules/@swc/core": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz", - "integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.25" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.11", - "@swc/core-darwin-x64": "1.15.11", - "@swc/core-linux-arm-gnueabihf": "1.15.11", - "@swc/core-linux-arm64-gnu": "1.15.11", - "@swc/core-linux-arm64-musl": "1.15.11", - "@swc/core-linux-x64-gnu": "1.15.11", - "@swc/core-linux-x64-musl": "1.15.11", - "@swc/core-win32-arm64-msvc": "1.15.11", - "@swc/core-win32-ia32-msvc": "1.15.11", - "@swc/core-win32-x64-msvc": "1.15.11" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz", - "integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz", - "integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz", - "integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz", - "integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz", - "integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz", - "integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz", - "integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz", - "integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz", - "integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz", - "integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@swc/types": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", - "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2022,22 +2172,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2639,44 +2773,14 @@ "dev": true, "license": "MIT" }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", - "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", - "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/define-data-property": { @@ -2697,19 +2801,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -2962,6 +3053,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3546,19 +3678,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4208,41 +4327,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container/node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -5327,25 +5411,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", - "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5959,184 +6024,6 @@ "typescript": "^4.5 || ^5.0" } }, - "node_modules/rollup-plugin-jscc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-jscc/-/rollup-plugin-jscc-2.0.0.tgz", - "integrity": "sha512-5jG9q79K2u5uRBTKA+GA4gqt1zA7qHQRpcabZMoVs913gr75s428O7K3r58n2vADDzwIhiOKMo7rCMhOyks6dw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jsbits/get-package-version": "^1.0.3", - "jscc": "^1.1.1", - "rollup-pluginutils": "^2.8.2" - }, - "engines": { - "node": ">=10.12.0" - }, - "peerDependencies": { - "rollup": ">=2" - } - }, - "node_modules/rollup-plugin-visualizer": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.8.tgz", - "integrity": "sha512-MmLbgYWDiDu8XKoePA1GtmRejl+4GWJTx156zjvycoxCbOq0PkNNwbepyB5tHCfDyRc8PKDLh2f/GLVGKNeV7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "open": "^10.0.0", - "picomatch": "^4.0.2", - "source-map": "^0.7.4", - "yargs": "^18.0.0" - }, - "bin": { - "rollup-plugin-visualizer": "dist/bin/cli.js" - }, - "engines": { - "node": ">=22" - }, - "peerDependencies": { - "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", - "rollup": "2.x || 3.x || 4.x" - }, - "peerDependenciesMeta": { - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup-plugin-visualizer/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -6633,16 +6520,6 @@ "dev": true, "license": "MIT" }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -7363,6 +7240,71 @@ "dev": true, "license": "MIT" }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin-strip": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/unplugin-strip/-/unplugin-strip-0.2.1.tgz", + "integrity": "sha512-Z5DgWyVhDVBs8zwyzo27g0biiu36nGJsj/xtyeUywdbB1XTHrlBudRYmoKx+a28uK18A5Mg1+tWsB7yHv8l8dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11", + "unplugin": "^1.12.0" + }, + "peerDependencies": { + "@nuxt/kit": "^3", + "@nuxt/schema": "^3", + "esbuild": "*", + "rollup": "^3", + "vite": ">=3", + "webpack": "^4 || ^5" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@nuxt/schema": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unplugin-strip/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/update-check": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", @@ -7439,6 +7381,13 @@ "node": ">=20" } }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/whatwg-mimetype": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", @@ -7704,38 +7653,6 @@ "license": "ISC", "optional": true }, - "node_modules/wsl-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", - "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wsl-utils/node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/package.json b/package.json index 20e69631c2f..a6df438bd8c 100644 --- a/package.json +++ b/package.json @@ -84,31 +84,28 @@ "devDependencies": { "@playcanvas/eslint-config": "2.1.0", "@rollup/plugin-node-resolve": "16.0.3", - "@rollup/plugin-strip": "3.0.4", - "@rollup/plugin-swc": "0.4.0", "@rollup/plugin-terser": "0.4.4", - "@rollup/pluginutils": "5.3.0", - "@swc/core": "1.15.11", "@types/node": "24.10.13", "c8": "10.1.3", "chai": "6.2.2", + "esbuild": "^0.25.2", "eslint": "9.39.3", "fflate": "0.8.2", "globals": "17.3.0", + "jscc": "^1.1.1", "jsdom": "28.1.0", "mocha": "11.7.5", "nise": "6.1.1", "publint": "0.3.17", "rollup": "4.58.0", "rollup-plugin-dts": "6.3.0", - "rollup-plugin-jscc": "2.0.0", - "rollup-plugin-visualizer": "6.0.8", "serve": "14.2.5", "sinon": "21.0.1", "typedoc": "0.28.17", "typedoc-plugin-mdn-links": "5.1.1", "typedoc-plugin-missing-exports": "4.1.2", - "typescript": "5.9.3" + "typescript": "5.9.3", + "unplugin-strip": "^0.2.1" }, "optionalDependencies": { "canvas": "3.2.1" @@ -127,7 +124,6 @@ "build:treenet": "npm run build target:release treenet", "build:treesun": "npm run build target:release treesun", "build:treeflame": "npm run build target:release treeflame", - "build:sourcemaps": "npm run build -- -m", "watch": "npm run build -- -w", "watch:release": "npm run build target:release -- -w", "watch:debug": "npm run build target:debug -- -w", diff --git a/rollup.config.mjs b/rollup.config.mjs index 98aa339171e..c48807b9db6 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,79 +1,10 @@ -import fs from 'fs'; -import { version, revision } from './utils/rollup-version-revision.mjs'; -import { buildJSOptions, buildTypesOption } from './utils/rollup-build-target.mjs'; - -/** @import { RollupOptions } from 'rollup' */ - -const BLUE_OUT = '\x1b[34m'; -const RED_OUT = '\x1b[31m'; -const BOLD_OUT = '\x1b[1m'; -const REGULAR_OUT = '\x1b[22m'; -const RESET_OUT = '\x1b[0m'; - -const BUILD_TYPES = /** @type {const} */ (['release', 'debug', 'profiler', 'min']); -const MODULE_FORMAT = /** @type {const} */ (['umd', 'esm']); -const BUNDLE_STATES = /** @type {const} */ (['unbundled', 'bundled']); - -const envTarget = process.env.target ? process.env.target.toLowerCase() : null; - -const title = [ - 'Building PlayCanvas Engine', - `version ${BOLD_OUT}v${version}${REGULAR_OUT}`, - `revision ${BOLD_OUT}${revision}${REGULAR_OUT}`, - `target ${BOLD_OUT}${envTarget ?? 'all'}${REGULAR_OUT}` -].join('\n'); -console.log(`${BLUE_OUT}${title}${RESET_OUT}`); - -if (envTarget === null && fs.existsSync('build')) { - // no targets specified, clean build directory - fs.rmSync('build', { recursive: true }); -} - -function includeBuild(buildType, moduleFormat, bundleState) { - return envTarget === null || - envTarget === buildType || - envTarget === moduleFormat || - envTarget === bundleState || - envTarget === `${moduleFormat}:${buildType}` || - envTarget === `${moduleFormat}:${bundleState}` || - envTarget === `${buildType}:${bundleState}` || - envTarget === `${moduleFormat}:${buildType}:${bundleState}`; -} - /** - * @type {RollupOptions[]} + * Rollup configuration — retained for types build only. + * JS builds are now handled by esbuild via build.mjs. + * + * This config is used by the examples build which imports buildJSOptions/buildTypesOption + * from utils/rollup-build-target.mjs. */ -const targets = []; -BUILD_TYPES.forEach((buildType) => { - MODULE_FORMAT.forEach((moduleFormat) => { - BUNDLE_STATES.forEach((bundleState) => { - if (bundleState === 'unbundled' && moduleFormat === 'umd') { - return; - } - if (bundleState === 'unbundled' && buildType === 'min') { - return; - } - - if (!includeBuild(buildType, moduleFormat, bundleState)) { - return; - } - - targets.push(...buildJSOptions({ - moduleFormat, - buildType, - bundleState - })); - }); - }); -}); - -if (envTarget === null || envTarget === 'types') { - targets.push(buildTypesOption()); -} - -if (!targets.length) { - console.error(`${RED_OUT}${BOLD_OUT}No targets found${RESET_OUT}`); - process.exit(1); -} +import { buildTypesOption } from './utils/rollup-build-target.mjs'; -export default targets; +export default [buildTypesOption()]; diff --git a/utils/esbuild-build-target.mjs b/utils/esbuild-build-target.mjs new file mode 100644 index 00000000000..6eb6350ef8d --- /dev/null +++ b/utils/esbuild-build-target.mjs @@ -0,0 +1,310 @@ +import esbuild from 'esbuild'; +import fs from 'fs'; +import { + dirname, join as pathJoin, relative as pathRelative, + resolve as pathResolve, posix, sep +} from 'path'; +import { fileURLToPath } from 'url'; + +import { importValidationPlugin } from './plugins/esbuild-import-validation.mjs'; +import { + applyTransforms, createStripTransform, transformPipelinePlugin +} from './plugins/esbuild-transform-pipeline.mjs'; +import { version, revision } from './rollup-version-revision.mjs'; +import { getBanner } from './rollup-get-banner.mjs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const rootDir = pathResolve(__dirname, '..'); + +const STRIP_FUNCTIONS = [ + 'Debug.assert', + 'Debug.assertDeprecated', + 'Debug.assertDestroyed', + 'Debug.call', + 'Debug.deprecated', + 'Debug.warn', + 'Debug.warnOnce', + 'Debug.error', + 'Debug.errorOnce', + 'Debug.log', + 'Debug.logOnce', + 'Debug.removed', + 'Debug.trace', + 'DebugHelper.setName', + 'DebugHelper.setLabel', + 'DebugHelper.setDestroyed', + 'DebugGraphics.toString', + 'DebugGraphics.clearGpuMarkers', + 'DebugGraphics.pushGpuMarker', + 'DebugGraphics.popGpuMarker', + 'WebgpuDebug.validate', + 'WebgpuDebug.memory', + 'WebgpuDebug.internal', + 'WebgpuDebug.end', + 'WebgpuDebug.endShader', + 'WorldClustersDebug.render' +]; + +const BANNER = { + debug: ' (DEBUG)', + release: ' (RELEASE)', + profiler: ' (PROFILE)', + min: ' (RELEASE)' +}; + +const OUT_PREFIX = { + debug: 'playcanvas.dbg', + release: 'playcanvas', + profiler: 'playcanvas.prf', + min: 'playcanvas.min' +}; + +/** + * Get JSCC values for a given build type. + * + * @param {'debug'|'release'|'profiler'} buildType - The build type. + * @returns {{ values: Object, keepLines: boolean }} JSCC config. + */ +function getJSCCConfig(buildType) { + const base = { + _CURRENT_SDK_VERSION: version, + _CURRENT_SDK_REVISION: revision + }; + + switch (buildType) { + case 'debug': + return { + values: { ...base, _DEBUG: 1, _PROFILER: 1 }, + keepLines: true + }; + case 'profiler': + return { + values: { ...base, _PROFILER: 1 }, + keepLines: false + }; + case 'release': + default: + return { values: base, keepLines: false }; + } +} + +/** + * Collect all .js files under a directory recursively. + * + * @param {string} dirPath - Directory to scan. + * @returns {string[]} List of file paths. + */ +function collectJSFiles(dirPath) { + const files = []; + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + for (const entry of entries) { + const full = pathJoin(dirPath, entry.name); + if (entry.isDirectory()) { + files.push(...collectJSFiles(full)); + } else if (entry.isFile() && entry.name.endsWith('.js')) { + files.push(full); + } + } + return files; +} + +/** + * Build a bundled JS target (single output file). + * + * @param {object} options - Build options. + * @param {'umd'|'esm'} options.moduleFormat - The module format. + * @param {'debug'|'release'|'profiler'|'min'} options.buildType - The build type. + * @param {string} [options.input] - Entry point (default: 'src/index.js'). + * @param {string} [options.dir] - Output directory (default: 'build'). + * @returns {Promise} + */ +async function buildBundled({ + moduleFormat, + buildType, + input = 'src/index.js', + dir = 'build' +}) { + const isUMD = moduleFormat === 'umd'; + const isDebug = buildType === 'debug'; + const isMin = buildType === 'min'; + const prefix = OUT_PREFIX[buildType]; + const outfile = `${dir}/${prefix}${isUMD ? '.js' : '.mjs'}`; + const effectiveBuildType = buildType === 'min' ? 'release' : buildType; + const jsccConfig = getJSCCConfig(effectiveBuildType); + + const banner = getBanner(BANNER[buildType]); + + const plugins = [ + transformPipelinePlugin({ + jsccValues: jsccConfig.values, + jsccKeepLines: jsccConfig.keepLines, + stripFunctions: !isDebug ? STRIP_FUNCTIONS : null, + processShaders: !isDebug, + dynamicImportLegacy: isUMD, + dynamicImportSuppress: !isUMD, + stripComments: !isDebug + }) + ]; + + if (isDebug) { + plugins.push(importValidationPlugin(input)); + } + + /** @type {import('esbuild').BuildOptions} */ + const opts = { + entryPoints: [input], + bundle: true, + outfile, + format: isUMD ? 'iife' : 'esm', + globalName: isUMD ? 'pc' : undefined, + target: 'es2020', + sourcemap: isDebug ? 'inline' : false, + minify: isMin, + legalComments: 'none', + banner: { js: banner }, + plugins, + external: ['node:worker_threads'], + logLevel: 'warning' + }; + + if (isMin) { + opts.drop = ['console']; + } + + if (isUMD) { + opts.banner = { + js: [ + banner, + '(function (root, factory) {', + '\tif (typeof module !== \'undefined\' && module.exports) {', + '\t\tmodule.exports = factory();', + '\t} else if (typeof define === \'function\' && define.amd) {', + '\t\tdefine(factory);', + '\t} else {', + '\t\troot.pc = factory();', + '\t}', + '}(typeof self !== \'undefined\' ? self : this, function () {' + ].join('\n') + }; + opts.footer = { + js: 'return pc;\n}));' + }; + opts.format = 'esm'; + } + + await esbuild.build(opts); +} + +/** + * Build an unbundled JS target (per-file transform, preserving module structure). + * + * @param {object} options - Build options. + * @param {'debug'|'release'|'profiler'} options.buildType - The build type. + * @param {string} [options.input] - Entry point directory root (default: 'src'). + * @param {string} [options.dir] - Output base directory (default: 'build'). + * @returns {Promise} + */ +async function buildUnbundled({ + buildType, + input = 'src', + dir = 'build' +}) { + const isDebug = buildType === 'debug'; + const prefix = OUT_PREFIX[buildType]; + const outDir = `${dir}/${prefix}`; + const effectiveBuildType = buildType === 'min' ? 'release' : buildType; + const jsccConfig = getJSCCConfig(effectiveBuildType); + const strip = + !isDebug ? createStripTransform(STRIP_FUNCTIONS) : null; + + const srcFiles = collectJSFiles(input); + + const transformPromises = srcFiles.map(async (srcFile) => { + let source = await fs.promises.readFile(srcFile, 'utf8'); + + source = applyTransforms(source, { + jsccValues: jsccConfig.values, + jsccKeepLines: jsccConfig.keepLines, + strip, + processShaders: !isDebug, + dynamicImportLegacy: false, + dynamicImportSuppress: true, + stripComments: !isDebug + }); + + // Rewrite bare 'fflate' import to relative modules path + if (source.includes('from \'fflate\'') || + source.includes('from "fflate"')) { + const relFromSrc = pathRelative(dirname(srcFile), input); + const modulePath = posix.join( + relFromSrc.split(sep).join('/'), + '..', 'modules', 'fflate', 'esm', 'browser.js' + ); + source = source.replace( + /from ['"]fflate['"]/g, + `from '${modulePath}'` + ); + } + + // Skip files that have no meaningful content after transforms + if (!isDebug) { + const meaningful = source + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(/\/\/.*/g, '') + .replace(/^\s*import\s.*$/gm, '') + .replace(/^\s*export\s.*$/gm, '') + .trim(); + if (meaningful.length === 0) { + return; + } + } + + const destFile = pathJoin(outDir, srcFile); + await fs.promises.mkdir(dirname(destFile), { recursive: true }); + await fs.promises.writeFile(destFile, source); + }); + + await Promise.all(transformPromises); + + // Copy fflate module into unbundled output + const fflateEntry = pathJoin( + rootDir, 'node_modules', 'fflate', 'esm', 'browser.js' + ); + const destDir = pathJoin(outDir, 'modules', 'fflate', 'esm'); + fs.mkdirSync(destDir, { recursive: true }); + fs.copyFileSync(fflateEntry, pathJoin(destDir, 'browser.js')); +} + +/** + * Build a single JS target (bundled or unbundled). + * + * @param {object} options - Build options. + * @param {'umd'|'esm'} options.moduleFormat - The module format. + * @param {'debug'|'release'|'profiler'|'min'} options.buildType - The build type. + * @param {'unbundled'|'bundled'} [options.bundleState] - The bundle state. + * @param {string} [options.input] - Entry point. + * @param {string} [options.dir] - Output directory. + * @returns {Promise} + */ +async function buildTarget({ + moduleFormat, + buildType, + bundleState, + input = 'src/index.js', + dir = 'build' +}) { + const isUMD = moduleFormat === 'umd'; + const isMin = buildType === 'min'; + const bundled = isUMD || isMin || bundleState === 'bundled'; + + if (bundled) { + await buildBundled({ moduleFormat, buildType, input, dir }); + } else { + await buildUnbundled({ + buildType, input: dirname(input), dir + }); + } +} + +export { buildTarget, OUT_PREFIX }; diff --git a/utils/plugins/esbuild-dynamic.mjs b/utils/plugins/esbuild-dynamic.mjs new file mode 100644 index 00000000000..bd40ab0be36 --- /dev/null +++ b/utils/plugins/esbuild-dynamic.mjs @@ -0,0 +1,20 @@ +/** + * Wrap dynamic `import()` calls in `new Function(...)` for legacy browser support (UMD builds). + * + * @param {string} source - Source code. + * @returns {string} Transformed source. + */ +export function applyDynamicImportLegacy(source) { + return source.replace(/(\W)import\(/g, '$1new Function("modulePath", "return import(modulePath)")('); +} + +/** + * Add bundler-suppress comments before dynamic `import()` calls + * to quiet Vite/webpack warnings (ESM builds). + * + * @param {string} source - Source code. + * @returns {string} Transformed source. + */ +export function applyDynamicImportSuppress(source) { + return source.replace(/import\(([^'])/g, 'import(/* @vite-ignore */ /* webpackIgnore: true */ $1'); +} diff --git a/utils/plugins/esbuild-import-validation.mjs b/utils/plugins/esbuild-import-validation.mjs new file mode 100644 index 00000000000..2023ba6a4c7 --- /dev/null +++ b/utils/plugins/esbuild-import-validation.mjs @@ -0,0 +1,49 @@ +import path from 'node:path'; + +/** + * esbuild plugin that validates engine layer imports. + * Warns when a lower-level module imports from a higher-level module. + * + * Hierarchy: core (0) → platform (1) → scene (2) → framework (3) → extras (4) + * + * Port of engineLayerImportValidation from rollup-import-validation.mjs. + * + * @param {string} rootFile - The root file, typically `src/index.js`. + * @returns {import('esbuild').Plugin} The esbuild plugin. + */ +export function importValidationPlugin(rootFile) { + const folderLevels = { + 'core': 0, + 'platform': 1, + 'scene': 2, + 'framework': 3, + 'extras': 4 + }; + + const rootPath = path.parse(path.resolve(rootFile)).dir; + + return { + name: 'import-validation', + setup(build) { + build.onResolve({ filter: /^\./ }, (args) => { + if (!args.importer) return undefined; + + const importerDir = path.parse(args.importer).dir; + const relImporter = path.dirname(path.relative(rootPath, args.importer)); + const folderImporter = relImporter.split(path.sep)[0]; + const levelImporter = folderLevels[folderImporter]; + + const absImported = path.resolve(path.join(importerDir, args.path)); + const relImported = path.dirname(path.relative(rootPath, absImported)); + const folderImported = relImported.split(path.sep)[0]; + const levelImported = folderLevels[folderImported]; + + if (levelImporter !== undefined && levelImported !== undefined && levelImporter < levelImported) { + console.log(`(!) Incorrect import: [${path.relative(rootPath, args.importer)}] -> [${args.path}]`); + } + + return undefined; + }); + } + }; +} diff --git a/utils/plugins/esbuild-jscc.mjs b/utils/plugins/esbuild-jscc.mjs new file mode 100644 index 00000000000..5d38e328fd6 --- /dev/null +++ b/utils/plugins/esbuild-jscc.mjs @@ -0,0 +1,17 @@ +import jscc from 'jscc'; + +/** + * Apply JSCC processing to source text. + * + * Processes `// #if _VAR` / `// #else` / `// #endif` comment-based directives + * and replaces `$_VAR` value tokens in source code. + * + * @param {string} source - Source code. + * @param {Object} values - Map of variable names to values. + * @param {boolean} keepLines - Preserve line count by replacing removed lines with blanks. + * @returns {string} Processed source. + */ +export function processJSCC(source, values, keepLines) { + const result = jscc(source, null, { values, keepLines, sourceMap: false, prefixes: ['// '] }); + return result.code; +} diff --git a/utils/plugins/esbuild-shader-chunks.mjs b/utils/plugins/esbuild-shader-chunks.mjs new file mode 100644 index 00000000000..cda46ddd5a3 --- /dev/null +++ b/utils/plugins/esbuild-shader-chunks.mjs @@ -0,0 +1,19 @@ +/** + * Minify shader code inside template literals marked with + * `/* glsl *\/` or `/* wgsl *\/` comments. + * + * @param {string} source - Source code. + * @returns {string} Processed source. + */ +export function processShaderChunks(source) { + return source.replace(/\/\* *(glsl|wgsl) *\*\/\s*(`.*?`)/gs, (match, type, code) => { + return code + .trim() + .replace(/\r/g, '') + .replace(/ {4}/g, '\t') + .replace(/[ \t]*\/\/.*/g, '') + .replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') + .concat('\n') + .replace(/\n{2,}/g, '\n'); + }); +} diff --git a/utils/plugins/esbuild-strip.mjs b/utils/plugins/esbuild-strip.mjs new file mode 100644 index 00000000000..8156e14a103 --- /dev/null +++ b/utils/plugins/esbuild-strip.mjs @@ -0,0 +1,33 @@ +import { parse } from 'acorn'; +import { unpluginFactory } from 'unplugin-strip'; + +/** + * Create a strip transform function for the given function names. + * Uses unplugin-strip (AST-based) to reliably remove function calls + * in all positions — statement-level, inline, inside template literals, etc. + * + * @param {string[]} functions - Function names to strip (e.g. 'Debug.assert'). + * @returns {(source: string) => string} Transform function. + */ +export function createStripTransform(functions) { + const plugin = unpluginFactory({ + functions, + sourceMap: false, + debugger: false, + include: '**/*.js' + }); + + const ctx = { + parse(code) { + return parse(code, { + ecmaVersion: 'latest', + sourceType: 'module' + }); + } + }; + + return function applyStrip(source) { + const result = plugin.transform.call(ctx, source, 'file.js'); + return result ? result.code : source; + }; +} diff --git a/utils/plugins/esbuild-transform-pipeline.mjs b/utils/plugins/esbuild-transform-pipeline.mjs new file mode 100644 index 00000000000..3782e2c612f --- /dev/null +++ b/utils/plugins/esbuild-transform-pipeline.mjs @@ -0,0 +1,84 @@ +import fs from 'fs'; +import { processJSCC } from './esbuild-jscc.mjs'; +import { createStripTransform } from './esbuild-strip.mjs'; +import { processShaderChunks } from './esbuild-shader-chunks.mjs'; +import { applyDynamicImportLegacy, applyDynamicImportSuppress } from './esbuild-dynamic.mjs'; + +/** + * Apply all source transforms to a string of source code. + * Shared by the esbuild pipeline plugin (bundled builds) and the + * unbundled build path which transforms files directly. + * + * @param {string} source - Source code to transform. + * @param {Object} options - Transform options. + * @param {Object} options.jsccValues - JSCC variable values. + * @param {boolean} options.jsccKeepLines - Preserve line count for JSCC. + * @param {((source: string) => string)|null} options.strip - Strip transform (null = skip). + * @param {boolean} options.processShaders - Whether to minify shader chunks. + * @param {boolean} options.dynamicImportLegacy - Wrap imports for legacy browsers. + * @param {boolean} options.dynamicImportSuppress - Add bundler-suppress comments. + * @param {boolean} options.stripComments - Strip JSDoc comments. + * @returns {string} Transformed source. + */ +export function applyTransforms(source, { + jsccValues, jsccKeepLines, strip, + processShaders: doShaders, dynamicImportLegacy, + dynamicImportSuppress, stripComments +}) { + source = processJSCC(source, jsccValues, jsccKeepLines); + if (doShaders) source = processShaderChunks(source); + if (strip) source = strip(source); + if (stripComments) source = source.replace(/\/\*\*[\s\S]*?\*\//g, ''); + if (dynamicImportLegacy) source = applyDynamicImportLegacy(source); + if (dynamicImportSuppress) source = applyDynamicImportSuppress(source); + return source; +} + +export { createStripTransform }; + +/** + * Combined esbuild plugin that applies all source transforms in a single + * onLoad handler. esbuild only runs the FIRST matching onLoad handler per + * file, so all transforms must be in one plugin to chain correctly. + * + * @param {Object} options - Pipeline options. + * @param {Object} options.jsccValues - JSCC variable values. + * @param {boolean} [options.jsccKeepLines] - Preserve line count for JSCC. + * @param {string[]} [options.stripFunctions] - Functions to strip (null = don't strip). + * @param {boolean} [options.processShaders] - Whether to minify shader chunks. + * @param {boolean} [options.dynamicImportLegacy] - Wrap imports for legacy browsers. + * @param {boolean} [options.dynamicImportSuppress] - Add bundler-suppress comments. + * @param {boolean} [options.stripComments] - Strip JSDoc comments. + * @returns {import('esbuild').Plugin} The esbuild plugin. + */ +export function transformPipelinePlugin({ + jsccValues = {}, + jsccKeepLines = false, + stripFunctions = null, + processShaders = false, + dynamicImportLegacy = false, + dynamicImportSuppress = false, + stripComments = false +} = {}) { + const strip = + stripFunctions ? createStripTransform(stripFunctions) : null; + + return { + name: 'transform-pipeline', + setup(build) { + build.onLoad({ filter: /\.js$/ }, async (args) => { + const source = await fs.promises.readFile(args.path, 'utf8'); + const result = applyTransforms(source, { + jsccValues, + jsccKeepLines, + strip, + processShaders, + dynamicImportLegacy, + dynamicImportSuppress, + stripComments + }); + return { contents: result, loader: 'js' }; + }); + } + }; +} diff --git a/utils/plugins/rollup-dynamic.mjs b/utils/plugins/rollup-dynamic.mjs deleted file mode 100644 index e7271b6ad95..00000000000 --- a/utils/plugins/rollup-dynamic.mjs +++ /dev/null @@ -1,39 +0,0 @@ -/** - * This rollup plugin transform code with dynamic import statements and wraps them - * in a `new Function('import("modulePath")')` statement, in order to avoid parsing errors in older browsers - * without support for dynamic imports. - * - * Note that whilst this will prevent parsing errors, it can trigger CSP errors. - * - * @returns {import('rollup').Plugin} The rollup plugin - */ -export function dynamicImportLegacyBrowserSupport() { - return { - name: 'dynamic-import-old-browsers', - transform(code, id) { - return { - code: code.replace(/(\W)import\(/g, '$1new Function("modulePath", "return import(modulePath)")('), - map: null - }; - } - }; -} - -/** - * This rollup plugin transform code with import statements and adds a \/* vite-ignore *\/ comment to suppress bundler warnings - * generated from dynamic-import-vars {@link https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations} - * {@link https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import} - * - * @returns {import('rollup').Plugin} The rollup plugin - */ -export function dynamicImportBundlerSuppress() { - return { - name: 'dynamic-import-bundler-suppress', - transform(code, id) { - return { - code: code.replace(/import\(([^'])/g, 'import(/* @vite-ignore */ /* webpackIgnore: true */ $1'), - map: null - }; - } - }; -} diff --git a/utils/plugins/rollup-import-validation.mjs b/utils/plugins/rollup-import-validation.mjs deleted file mode 100644 index 7d4100deb42..00000000000 --- a/utils/plugins/rollup-import-validation.mjs +++ /dev/null @@ -1,52 +0,0 @@ -import path from 'node:path'; - -/** @typedef {import('rollup').Plugin} Plugin */ - -/** - * Validate and print warning if an engine module on a lower level imports module on a higher level - * - * @param {string} rootFile - The root file, typically `src/index.js`. - * @returns {Plugin} The plugin. - */ -export function engineLayerImportValidation(rootFile) { - - const folderLevels = { - 'core': 0, - 'platform': 1, - 'scene': 2, - 'framework': 3, - 'extras': 4 - }; - - let rootPath; - - return { - name: 'engineLayerImportValidation', - - buildStart() { - rootPath = path.parse(path.resolve(rootFile)).dir; - }, - - resolveId(imported, importer) { - // skip non-relative paths, those are not our imports, for example 'rollupPluginBabelHelpers.js' - if (importer && imported && imported.includes('./')) { - - // convert importer path - const importerDir = path.parse(importer).dir; - const relImporter = path.dirname(path.relative(rootPath, importer)); - const folderImporter = relImporter.split(path.sep)[0]; - const levelImporter = folderLevels[folderImporter]; - - // convert imported path - const absImported = path.resolve(path.join(importerDir, imported)); - const relImported = path.dirname(path.relative(rootPath, absImported)); - const folderImported = relImported.split(path.sep)[0]; - const levelImported = folderLevels[folderImported]; - - if (levelImporter < levelImported) { - console.log(`(!) Incorrect import: [${path.relative(rootPath, importer)}] -> [${imported}]`); - } - } - } - }; -} diff --git a/utils/plugins/rollup-shader-chunks.mjs b/utils/plugins/rollup-shader-chunks.mjs deleted file mode 100644 index 0710b0c7d02..00000000000 --- a/utils/plugins/rollup-shader-chunks.mjs +++ /dev/null @@ -1,49 +0,0 @@ -import { createFilter } from '@rollup/pluginutils'; - -/** @typedef {import('rollup').Plugin} Plugin */ -/** @typedef {string | string[]} GlobPattern */ -/** - * @typedef {Object | null} PluginOptions - * @property {GlobPattern?} include - pattern(s array) to import - * @property {GlobPattern?} exclude - pattern(s array) to ignore - * @property {boolean?} enabled - enable the plugin - */ - -/** - * @type {readonly string[]} - */ -const DEFAULT_SHADERS = Object.freeze(['**/*.js']); - -/** - * @param {PluginOptions} options - Plugin config object - * @returns {Plugin} The plugin that converts shader code. - */ -export function shaderChunks({ - include = DEFAULT_SHADERS, - exclude = undefined -} = {}) { - const filter = createFilter(include, exclude); - - return { - name: 'shaderChunks', - transform(source, shader) { - if (!filter(shader)) return; - - source = source.replace(/\/\* *(glsl|wgsl) *\*\/\s*(`.*?`)/gs, (match, type, code) => { - return code - .trim() // trim whitespace - .replace(/\r/g, '') // Remove carriage returns - .replace(/ {4}/g, '\t') // 4 spaces to tabs - .replace(/[ \t]*\/\/.*/g, '') // remove single line comments - .replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') // remove multi line comments - .concat('\n') // ensure final new line - .replace(/\n{2,}/g, '\n'); // condense 2 or more empty lines to 1 - }); - - return { - code: source, - map: null - }; - } - }; -} diff --git a/utils/plugins/rollup-spaces-to-tabs.mjs b/utils/plugins/rollup-spaces-to-tabs.mjs deleted file mode 100644 index 3a0534e852c..00000000000 --- a/utils/plugins/rollup-spaces-to-tabs.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import { createFilter } from '@rollup/pluginutils'; - -/** @typedef {import('rollup').Plugin} Plugin */ - -/** - * This plugin converts every two spaces into one tab. Two spaces is the default the rollup plugin - * outputs, which is independent of the four spaces of the code base. - * - * @returns {Plugin} The plugin. - */ -export function spacesToTabs() { - const filter = createFilter([ - '**/*.js' - ], []); - - return { - name: 'spacesToTabs', - transform(code, id) { - if (!filter(id)) return undefined; - // ^ = start of line - // " +" = one or more spaces - // gm = find all + multiline - const regex = /^ +/gm; - code = code.replace( - regex, - startSpaces => startSpaces.replace(/ {2}/g, '\t') - ); - return { - code, - map: null - }; - } - }; -} diff --git a/utils/rollup-build-target.mjs b/utils/rollup-build-target.mjs index 06c3dbe76b2..738e05ea7c4 100644 --- a/utils/rollup-build-target.mjs +++ b/utils/rollup-build-target.mjs @@ -1,283 +1,15 @@ -// official package plugins -import resolve from '@rollup/plugin-node-resolve'; -import strip from '@rollup/plugin-strip'; -import swcPlugin from '@rollup/plugin-swc'; - -// unofficial package plugins import dts from 'rollup-plugin-dts'; -import jscc from 'rollup-plugin-jscc'; -import { visualizer } from 'rollup-plugin-visualizer'; // eslint-disable-line import/no-unresolved -// custom plugins -import { shaderChunks } from './plugins/rollup-shader-chunks.mjs'; -import { engineLayerImportValidation } from './plugins/rollup-import-validation.mjs'; -import { spacesToTabs } from './plugins/rollup-spaces-to-tabs.mjs'; -import { dynamicImportLegacyBrowserSupport, dynamicImportBundlerSuppress } from './plugins/rollup-dynamic.mjs'; import { runTsc } from './plugins/rollup-run-tsc.mjs'; import { typesFixup } from './plugins/rollup-types-fixup.mjs'; -import { version, revision } from './rollup-version-revision.mjs'; -import { getBanner } from './rollup-get-banner.mjs'; -import { swcOptions } from './rollup-swc-options.mjs'; - -import { dirname, resolve as pathResolve } from 'path'; -import { fileURLToPath } from 'url'; - -/** @import { RollupOptions, OutputOptions } from 'rollup' */ - -// Find path to the repo root -// @ts-ignore import.meta not allowed by tsconfig module:es6, but it works -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const rootDir = pathResolve(__dirname, '..'); - - -const STRIP_FUNCTIONS = [ - 'Debug.assert', - 'Debug.assertDeprecated', - 'Debug.assertDestroyed', - 'Debug.call', - 'Debug.deprecated', - 'Debug.warn', - 'Debug.warnOnce', - 'Debug.error', - 'Debug.errorOnce', - 'Debug.log', - 'Debug.logOnce', - 'Debug.removed', - 'Debug.trace', - 'DebugHelper.setName', - 'DebugHelper.setLabel', - 'DebugHelper.setDestroyed', - 'DebugGraphics.toString', - 'DebugGraphics.clearGpuMarkers', - 'DebugGraphics.pushGpuMarker', - 'DebugGraphics.popGpuMarker', - 'WebgpuDebug.validate', - 'WebgpuDebug.memory', - 'WebgpuDebug.internal', - 'WebgpuDebug.end', - 'WebgpuDebug.endShader', - 'WorldClustersDebug.render' -]; - -const BANNER = { - debug: ' (DEBUG)', - release: ' (RELEASE)', - profiler: ' (PROFILE)', - min: ' (RELEASE)' -}; - -const OUT_PREFIX = { - debug: 'playcanvas.dbg', - release: 'playcanvas', - profiler: 'playcanvas.prf', - min: 'playcanvas.min' -}; - -const HISTORY = new Map(); - -/** - * @param {'debug'|'release'|'profiler'} buildType - The build type. - * @returns {object} - The JSCC options. - */ -function getJSCCOptions(buildType) { - const options = { - debug: { - values: { - _CURRENT_SDK_VERSION: version, - _CURRENT_SDK_REVISION: revision, - _DEBUG: 1, - _PROFILER: 1 - }, - asloader: false, - keepLines: true - }, - release: { - values: { - _CURRENT_SDK_VERSION: version, - _CURRENT_SDK_REVISION: revision - }, - asloader: false - }, - profiler: { - values: { - _CURRENT_SDK_VERSION: version, - _CURRENT_SDK_REVISION: revision, - _PROFILER: 1 - }, - asloader: false - } - }; - return options[buildType]; -} - -/** - * @param {string} type - The type of the output (e.g., 'umd', 'es'). - * @returns {OutputOptions['plugins']} - The output plugins. - */ -function getOutPlugins(type) { - const plugins = []; - - if (process.env.treemap) { - plugins.push(visualizer({ - filename: `treemap.${type}.html`, - brotliSize: true, - gzipSize: true - })); - } - - if (process.env.treenet) { - plugins.push(visualizer({ - filename: `treenet.${type}.html`, - template: 'network' - })); - } - - if (process.env.treesun) { - plugins.push(visualizer({ - filename: `treesun.${type}.html`, - template: 'sunburst' - })); - } - - if (process.env.treeflame) { - plugins.push(visualizer({ - filename: `treeflame.${type}.html`, - template: 'flamegraph' - })); - } - - return plugins; -} - -/** - * Build rollup options for JS (bundled and unbundled). - * - * For faster subsequent builds, the unbundled and release builds are cached in the HISTORY map to - * be used for bundled and minified builds. They are stored in the HISTORY map with the key: - * `--`. - * - * @param {object} options - The build target options. - * @param {'umd'|'esm'} options.moduleFormat - The module format. - * @param {'debug'|'release'|'profiler'|'min'} options.buildType - The build type. - * @param {'unbundled'|'bundled'} [options.bundleState] - The bundle state. - * @param {string} [options.input] - Only used for examples to change it to `../src/index.js`. - * @param {string} [options.dir] - Only used for examples to change the output location. - * @returns {RollupOptions[]} Rollup targets. - */ -function buildJSOptions({ - moduleFormat, - buildType, - bundleState, - input = 'src/index.js', - dir = 'build' -}) { - const isUMD = moduleFormat === 'umd'; - const isDebug = buildType === 'debug'; - const isMin = buildType === 'min'; - const bundled = isUMD || isMin || bundleState === 'bundled'; - - const prefix = `${OUT_PREFIX[buildType]}`; - const file = `${prefix}${isUMD ? '.js' : '.mjs'}`; - - const targets = []; - - // bundle from unbundled - if (bundled && HISTORY.has(`${buildType}-${moduleFormat}-false`)) { - const unbundled = HISTORY.get(`${buildType}-${moduleFormat}-false`); - - /** - * @type {RollupOptions} - */ - const target = { - input: `${unbundled.output.dir}/src/index.js`, - output: { - banner: getBanner(BANNER[buildType]), - format: 'es', - indent: '\t', - sourcemap: isDebug && 'inline', - name: 'pc', - preserveModules: false, - file: `${dir}/${prefix}.mjs` - } - }; - - HISTORY.set(`${buildType}-${moduleFormat}-true`, target); - targets.push(target); - - return targets; - } - - // minify from release build - if (isMin && HISTORY.has(`release-${moduleFormat}-true`)) { - const release = HISTORY.get(`release-${moduleFormat}-true`); - - /** - * @type {RollupOptions} - */ - const target = { - input: release.output.file, - plugins: [ - swcPlugin({ swc: swcOptions(isDebug, isMin) }) - ], - output: { - banner: isUMD ? getBanner(BANNER[buildType]) : undefined, - file: `${dir}/${file}` - }, - context: isUMD ? 'this' : undefined - }; - - HISTORY.set(`${buildType}-${moduleFormat}-${bundled}`, target); - targets.push(target); - - return targets; - } - - /** - * @type {RollupOptions} - */ - const target = { - input, - output: { - banner: bundled ? getBanner(BANNER[buildType]) : undefined, - plugins: buildType === 'release' ? getOutPlugins(isUMD ? 'umd' : 'es') : undefined, - format: isUMD ? 'umd' : 'es', - indent: '\t', - sourcemap: bundled && isDebug && 'inline', - name: 'pc', - preserveModules: !bundled, - preserveModulesRoot: !bundled ? rootDir : undefined, - file: bundled ? `${dir}/${file}` : undefined, - dir: !bundled ? `${dir}/${prefix}` : undefined, - entryFileNames: chunkInfo => `${chunkInfo.name.replace(/node_modules/g, 'modules')}.js` - }, - plugins: [ - resolve(), - jscc(getJSCCOptions(isMin ? 'release' : buildType)), - isUMD ? dynamicImportLegacyBrowserSupport() : undefined, - !isDebug ? shaderChunks() : undefined, - isDebug ? engineLayerImportValidation(input) : undefined, - !isDebug ? strip({ functions: STRIP_FUNCTIONS }) : undefined, - swcPlugin({ swc: swcOptions(isDebug, isMin) }), - !isUMD ? dynamicImportBundlerSuppress() : undefined, - !isDebug ? spacesToTabs() : undefined - ] - }; - - HISTORY.set(`${buildType}-${moduleFormat}-${bundled}`, target); - targets.push(target); - - return targets; -} - /** * Build rollup options for TypeScript definitions. * * @param {object} options - The build target options. * @param {string} [options.root] - The root directory for finding the TypeScript definitions. * @param {string} [options.dir] - The output directory for the TypeScript definitions. - * @returns {RollupOptions} Rollup targets. + * @returns {import('rollup').RollupOptions} Rollup targets. */ function buildTypesOption({ root = '.', @@ -301,4 +33,4 @@ function buildTypesOption({ }; } -export { buildJSOptions, buildTypesOption }; +export { buildTypesOption }; diff --git a/utils/rollup-swc-options.mjs b/utils/rollup-swc-options.mjs deleted file mode 100644 index 5aa24acef0f..00000000000 --- a/utils/rollup-swc-options.mjs +++ /dev/null @@ -1,33 +0,0 @@ -/** @typedef {import('@swc/core').Config} SWCOptions */ - -/** - * The options for swc(...) plugin. - * - * @param {boolean} isDebug - Whether the build is for debug. - * @param {boolean} minify - Whether to minify. - * @returns {SWCOptions} The swc options. - */ -function swcOptions(isDebug, minify) { - - return { - minify, - jsc: { - target: 'es2020', - minify: { - format: { - comments: !isDebug || minify ? 'some' : 'all' - }, - mangle: minify, - compress: (!isDebug && minify) ? { - drop_console: true, - pure_funcs: [] - } : undefined - }, - externalHelpers: false, - loose: true - } - }; - -} - -export { swcOptions };