From 7fc9a5586b561f15ba52e203c35d7bf40e7ba7ab Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Thu, 9 Jul 2026 10:46:58 +0300 Subject: [PATCH 01/20] added vite bundler --- commands/pack.js | 58 + commands/serve.js | 80 +- config/vite.config.js | 479 +++++ docs/vite-isomorphic-scope.md | 127 ++ docs/vite-migration.md | 252 +++ npm-shrinkwrap.json | 3100 ++++++++++++++++++++++++++------- package.json | 3 + 7 files changed, 3478 insertions(+), 621 deletions(-) create mode 100644 config/vite.config.js create mode 100644 docs/vite-isomorphic-scope.md create mode 100644 docs/vite-migration.md diff --git a/commands/pack.js b/commands/pack.js index cb937de1..41c88493 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -52,6 +52,7 @@ function displayHelp () { console.log(' -c, --custom-skin Build with a custom skin'); console.log(' --no-linting Build without code linting'); console.log(' --no-animation Build without effects such as animation and shadow'); + console.log(' --vite [Experimental] Build with Vite instead of webpack'); console.log(' --stats Output bundle analysis file'); console.log(' --verbose Verbose log build details'); console.log(' -v, --version Display version information'); @@ -197,6 +198,56 @@ function printErrorDetails (err, handler) { } } +function useVite (opts) { + return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); +} + +// Experimental Vite build path. Mirrors the webpack `build`/`watch` behavior but +// drives Vite's JS API. Webpack-only features (isomorphic, snapshot, framework +// externals) are not yet supported here and are reported as ignored. +async function viteBuild (opts) { + const {build: viteBuildApi} = require('vite'); + + ['isomorphic', 'snapshot', 'framework', 'externals'].forEach(flag => { + if (opts[flag]) { + console.log( + chalk.yellow(`NOTICE: --${flag} is not yet supported by the Vite bundler and will be ignored.`) + ); + } + }); + + const configFactory = require('../config/vite.config'); + const config = configFactory( + opts.production ? 'production' : 'development', + !opts.linting, + opts['content-hash'], + opts.isomorphic, + !opts.animation, + !opts['split-css'], + opts['ilib-additional-path'], + opts.locales + ); + + // Entry override + if (opts.entry || app.entry) { + config.build.rollupOptions.input = {main: path.resolve(opts.entry || app.entry)}; + } + // Output override + if (opts.output) config.build.outDir = path.resolve(opts.output); + // Watch mode + if (opts.watch) { + config.build.watch = {}; + console.log('Creating a build and watching for changes...'); + } else if (process.env.NODE_ENV === 'development') { + console.log('Creating a development build...'); + } else { + console.log('Creating an optimized production build...'); + } + + await viteBuildApi(config); + if (!opts.watch) console.log(chalk.green('Compiled successfully.')); +} + // Create the production build and print the deployment instructions. function build (config) { if (process.env.NODE_ENV === 'development') { @@ -254,6 +305,12 @@ function api (opts = {}) { app.applyEnactMeta({template: path.join(__dirname, '..', 'config', 'custom-skin-template.ejs')}); } + // Experimental Vite bundler path (opt-in via `--vite` or ENACT_BUNDLER=vite). + if (useVite(opts)) { + process.env.NODE_ENV = opts.production ? 'production' : 'development'; + return viteBuild(opts); + } + // make the framework option available globally in order to be used by the eslint-webpack-plugin custom configuration process.env.FRAMEWORK = opts.framework; // Do this as the first thing so that any code reading it knows the right env. @@ -306,6 +363,7 @@ function cli (args) { 'animation', 'verbose', 'watch', + 'vite', 'help' ], string: ['externals', 'externals-public', 'locales', 'entry', 'ilib-additional-path', 'output', 'meta'], diff --git a/commands/serve.js b/commands/serve.js index 84619dd7..19ead897 100755 --- a/commands/serve.js +++ b/commands/serve.js @@ -60,6 +60,7 @@ function displayHelp () { console.log(' -f, --fast Enables experimental frast refresh'); console.log(' -p, --port Server port number'); console.log(' -m, --meta JSON to override package.json enact metadata'); + console.log(' --vite [Experimental] Serve with Vite instead of webpack'); console.log(' --no-linting Build without code linting'); console.log(' -v, --version Display version information'); console.log(' -h, --help Display help information'); @@ -251,6 +252,58 @@ function devServerConfig (host, port, protocol, publicPath, proxy, allowedHost) }; } +// Whether the experimental Vite bundler is selected (via `--vite` or ENACT_BUNDLER=vite). +function useVite (opts) { + return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); +} + +// Experimental Vite dev server path. Uses Vite's native dev server (esbuild + +// native ESM) in place of webpack-dev-server; HMR is handled by @vitejs/plugin-react. +async function viteServe (opts, host, port) { + const {createServer} = require('vite'); + const configFactory = require('../config/vite.config'); + const config = configFactory('development', !opts.linting); + + config.server = Object.assign({}, config.server, { + host, + port, + open: Boolean(opts.browser) + }); + + const server = await createServer(config); + await server.listen(); + if (process.stdout.isTTY) clearConsole(); + + // Print the same "ready" message the webpack path shows (via react-dev-utils), + // using the same `prepareUrls` helper so the Local / On Your Network URLs match. + // Vite's own `server.printUrls()` logs at 'info', which our `logLevel: 'warn'` + // suppresses, so we format it ourselves. Vite compiles on demand, so this + // reports the server is ready rather than a completed upfront compile. + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; + // Strip a trailing slash from the base so root URLs read `http://host:port` + // (matching the webpack path, which passes `publicPath.slice(0, -1)`). + const urls = prepareUrls(protocol, host, port, (app.publicUrl || '/').replace(/\/$/, '')); + console.log(chalk.green('Compiled successfully!')); + console.log(); + console.log(`You can now view ${chalk.bold(app.name)} in the browser.`); + console.log(); + console.log(` ${chalk.bold('Local:')} ${urls.localUrlForTerminal}`); + if (urls.lanUrlForTerminal) { + console.log(` ${chalk.bold('On Your Network:')} ${urls.lanUrlForTerminal}`); + } + console.log(); + console.log('Note that the development build is not optimized.'); + console.log(`To create a production build, use ${chalk.cyan('npm run pack-p')}.`); + console.log(); + + ['SIGINT', 'SIGTERM'].forEach(sig => { + process.on(sig, async () => { + await server.close(); + process.exit(); + }); + }); +} + function serve (config, host, port, open) { // We attempt to use the default port but if it is busy, we offer the user to // run on a different port. `detect()` Promise resolves to the next free port. @@ -331,6 +384,21 @@ function api (opts) { app.applyEnactMeta(meta); } + // Tools like Cloud9 rely on this. + const host = process.env.HOST || opts.host || '0.0.0.0'; + const port = parseInt(process.env.PORT || opts.port || 8080); + + // Serving is only supported for browser targets, regardless of bundler. + if (['node', 'async-node', 'webworker'].includes(app.environment)) { + return Promise.reject(new Error('Serving is not supported for non-browser apps.')); + } + + // Experimental Vite bundler path (opt-in via `--vite` or ENACT_BUNDLER=vite). + if (useVite(opts)) { + process.env.NODE_ENV = 'development'; + return viteServe(opts, host, port); + } + // We can disable the typechecker formatter since react-dev-utils includes their // own formatter in their dev client. process.env.DISABLE_TSFORMATTER = 'true'; @@ -343,22 +411,14 @@ function api (opts) { const fastRefresh = process.env.FAST_REFRESH || opts.fast; const config = hotDevServer(configFactory('development', !opts.linting), fastRefresh); - // Tools like Cloud9 rely on this. - const host = process.env.HOST || opts.host || '0.0.0.0'; - const port = parseInt(process.env.PORT || opts.port || 8080); - // Start serving - if (['node', 'async-node', 'webworker'].includes(app.environment)) { - return Promise.reject(new Error('Serving is not supported for non-browser apps.')); - } else { - return serve(config, host, port, opts.browser); - } + return serve(config, host, port, opts.browser); } function cli (args) { const opts = minimist(args, { string: ['host', 'port', 'meta'], - boolean: ['browser', 'fast', 'help', 'linting'], + boolean: ['browser', 'fast', 'help', 'linting', 'vite'], default: {linting: true}, alias: {b: 'browser', i: 'host', p: 'port', f: 'fast', m: 'meta', h: 'help'} }); diff --git a/config/vite.config.js b/config/vite.config.js new file mode 100644 index 00000000..182e6cdb --- /dev/null +++ b/config/vite.config.js @@ -0,0 +1,479 @@ +/* eslint no-console: off, no-undef: off */ +/* eslint-env node, es6 */ + +/** + * Experimental Vite configuration for @enact/cli. + * + * This is a drop-in-oriented port of `webpack.config.js`. It mirrors the same + * factory signature so the `pack`/`serve` commands can build an equivalent + * config, and it reuses the existing Enact tooling wherever a webpack-agnostic + * equivalent exists: + * - `@enact/dev-utils` optionParser -> app options (ri, accent, alias, ...) + * - `@enact/dev-utils` ViteHtmlPlugin -> HTML document (webpack: HtmlWebpackPlugin) + * - `@enact/dev-utils` ViteILibPlugin -> iLib i18n runtime + locale filtering (webpack: ILibPlugin) + * - `@enact/dev-utils` ViteWebOSMetaPlugin -> appinfo.json + assets (webpack: WebOSMetaPlugin) + * - `babel-preset-enact` -> via @vitejs/plugin-react `babel` + * - PostCSS resolution-independence -> via `css.postcss.plugins` + * - LESS accent/skin modifyVars -> via `css.preprocessorOptions.less` + * - cssModuleIdent (getLocalIdent) -> via `css.modules.generateScopedName` + * - eslint-config-enact -> via the inline `enact-eslint` plugin + * + * See `docs/vite-migration.md` for the feasibility analysis. The webpack-only + * features that remain unported (and for which `pack --vite` prints a "not + * supported, ignored" notice) are: isomorphic prerendering (see + * `docs/vite-isomorphic-scope.md`), V8 snapshot, and framework externals. + */ + +const fs = require('fs'); +const path = require('path'); +const { + optionParser: app, + cssModuleIdent: getLocalIdent, + ViteHtmlPlugin, + ViteILibPlugin, + ViteWebOSMetaPlugin +} = require('@enact/dev-utils'); + +// PostCSS plugin: resolve `~pkg` prefixes in `@import-json` at-rules to a path +// relative to the source file, so `@daltontan/postcss-import-json` can load them. +// Ported from the inline plugin in webpack.config.js (mimics webpack's `~` alias). +function tildeJsonImportPlugin () { + return { + postcssPlugin: 'postcss-import-json-tilde', + Once (root) { + root.walkAtRules('import-json', atRule => { + const src = atRule.params.slice(1, -1); // strip quotes + if (!src.startsWith('~')) return; + const packagePath = src.substring(1); + const currentFileDir = path.dirname((atRule.source.input && atRule.source.input.file) || ''); + try { + let resolvedPath; + try { + resolvedPath = require.resolve(packagePath, {paths: [currentFileDir]}); + } catch (e) { + resolvedPath = require.resolve(packagePath, {paths: [process.cwd()]}); + } + atRule.params = `"${path.relative(currentFileDir, resolvedPath)}"`; + } catch (error) { + // Fallback: walk up directories looking for the module under node_modules. + let dir = currentFileDir || process.cwd(); + while (dir !== path.parse(dir).root) { + const candidate = path.join(dir, 'node_modules', packagePath); + if (fs.existsSync(candidate)) { + atRule.params = `"${path.relative(currentFileDir, candidate)}"`; + return; + } + dir = path.dirname(dir); + } + } + }); + } + }; +} + +// Load and initialize a PostCSS plugin. Unlike postcss-loader (which resolves +// string plugin names), Vite's `css.postcss.plugins` expects instantiated +// plugins, so we require each and invoke the creator with its options. +function loadPostCss (name, opts) { + const mod = require(name); + const creator = mod && mod.default ? mod.default : mod; + return typeof creator === 'function' ? creator(opts) : creator; +} + +// Reuse the same PostCSS plugin chain as the webpack build so CSS output is +// equivalent (autoprefixing, resolution independence, JSON token imports). +function getPostCssPlugins ({useTailwind}) { + return [ + useTailwind && loadPostCss('tailwindcss'), + // Fix and adjust for known flexbox issues. See https://github.com/philipwalton/flexbugs + loadPostCss('postcss-flexbugs-fixes'), + // Transpile stage-3 CSS standards based on browserslist targets, with auto-prefixing. + loadPostCss('postcss-preset-env', { + autoprefixer: {flexbox: 'no-2009', remove: false}, + stage: 3, + features: {'custom-properties': false} + }), + // Standardize browser quirks based on the browserslist targets. + !useTailwind && loadPostCss('postcss-normalize'), + // Resolution independence support. + app.ri !== false && loadPostCss('postcss-resolution-independence', app.ri), + // Resolve `~pkg` prefixes in `@import-json` rules to real paths before the + // import-json plugin runs (ported from the webpack config's inline plugin). + tildeJsonImportPlugin(), + // Support importing JSON files in CSS (design tokens -> CSS custom properties). + loadPostCss('@daltontan/postcss-import-json', { + map: (selector, value) => { + if (typeof value === 'object' && value !== null && value.$ref) { + const tokenPath = value.$ref.split('#/')[1]; + return `var(--${tokenPath.replace(/\//g, '-')})`; + } + return value; + } + }) + ].filter(Boolean); +} + +// Vite plugin that runs ESLint against the app sources, mirroring the webpack +// build's `eslint-webpack-plugin` (same flat config in eslintWebpackPluginConfig). +// Lints once at build start: warnings are printed; errors fail production builds. +function enactEslintPlugin () { + let isBuild = true; + return { + name: 'enact-eslint', + configResolved (resolved) { + isBuild = resolved.command === 'build'; + }, + async buildStart () { + let ESLint; + try { + ({ESLint} = require('eslint')); + } catch (e) { + return; // eslint not available; skip silently + } + const eslint = new ESLint({ + cwd: app.context, + overrideConfigFile: require.resolve('./eslintWebpackPluginConfig'), + errorOnUnmatchedPattern: false, + cache: true, + cacheLocation: path.resolve('./node_modules/.cache/.eslintcache-vite') + }); + const results = await eslint.lintFiles(['src/**/*.{js,mjs,jsx,ts,tsx}']); + const errorCount = results.reduce((n, r) => n + r.errorCount, 0); + const warningCount = results.reduce((n, r) => n + r.warningCount, 0); + if (errorCount || warningCount) { + const formatter = await eslint.loadFormatter('stylish'); + const output = formatter.format(results); + if (output) console.log(output); + } + // Match webpack: errors fail the build; the dev server only warns. + if (isBuild && errorCount > 0) { + this.error(`ESLint found ${errorCount} error(s).`); + } + } + }; +} + +// Non-browser iLib platform loaders (`./lib/ilib-qt|rhino|ringo|node|….js`) and +// their `*Loader.js` helpers. iLib selects these via runtime platform detection; +// the browser branch never reaches them, but bundlers try (and fail) to resolve +// them statically. Webpack sidestepped this with ILibPlugin + WebpackLoader; here +// we neutralize them in both engines (Rollup build + esbuild dev optimizer). +const ILIB_LOADER_RE = /(?:[/\\]|^\.\/lib\/)ilib-[\w-]+\.js$|(?:Node|Rhino|Qt|Ringo)Loader(?:\.js)?$/; + +// esbuild plugin (dev dependency optimizer) that stubs the iLib loaders to empty. +const ilibStubEsbuildPlugin = { + name: 'enact-ilib-loader-stub', + setup (build) { + build.onResolve({filter: ILIB_LOADER_RE}, args => ({path: args.path, namespace: 'enact-ilib-stub'})); + build.onLoad({filter: /.*/, namespace: 'enact-ilib-stub'}, () => ({contents: 'module.exports = {};', loader: 'js'})); + } +}; + +// esbuild plugin (dev dependency optimizer) that runs babel-preset-enact on +// `@enact/*` source. @enact packages ship raw, unbuilt source as their `main` +// (JSX-in-.js, decorators, and proposals like `export default from 'ilib'`) that +// esbuild's optimizer cannot parse. The Rollup build transforms them via +// @vitejs/plugin-react; this does the equivalent for pre-bundling. ESM is +// preserved (caller.supportsStaticESM) so esbuild can still bundle/tree-shake. +const enactBabelEsbuildPlugin = { + name: 'enact-babel-optimize', + setup (build) { + let babel; + const preset = require.resolve('babel-preset-enact'); + build.onLoad({filter: /[\\/]@enact[\\/].*\.(?:jsx?|mjs)$/}, async args => { + // iLib data/loaders under @enact/i18n are not Enact source — leave them + // to esbuild (and the loader stub) rather than paying babel on big files. + if (/[\\/]ilib[\\/]/.test(args.path)) return null; + babel = babel || require('@babel/core'); + const source = fs.readFileSync(args.path, 'utf8'); + const result = await babel.transformAsync(source, { + babelrc: false, + configFile: false, + filename: args.path, + caller: {name: 'vite-optimize', supportsStaticESM: true, supportsDynamicImport: true}, + presets: [preset] + }); + return {contents: result.code, loader: 'js'}; + }); + } +}; + +// LESS `~specifier` imports (e.g. `@import '~@enact/ui/styles/core.less'`) are a +// webpack/less-loader convention that resolves the specifier from node_modules. +// Vite's LESS has no such resolver, so provide a custom Less FileManager that +// strips the `~` and resolves via Node module resolution (with sensible LESS +// extension fallbacks). Mirrors less-loader's `~` behavior. +function lessTildeImportPlugin (context) { + return { + install (less, pluginManager) { + class TildeFileManager extends less.FileManager { + supports (filename) { + return filename.charAt(0) === '~'; + } + supportsSync () { + return false; + } + loadFile (filename, currentDirectory, options, environment) { + const spec = filename.slice(1); + const paths = [currentDirectory, context].filter(Boolean); + const candidates = [spec, spec + '.less', spec + '/index.less', spec + '.css']; + let resolved; + for (const candidate of candidates) { + try { + resolved = require.resolve(candidate, {paths}); + break; + } catch (e) { + // try next candidate + } + } + if (!resolved) { + return Promise.reject({type: 'File', message: `'${filename}' wasn't found (tilde-resolve).`}); + } + return super.loadFile(resolved, currentDirectory, options, environment); + } + } + pluginManager.addFileManager(new TildeFileManager()); + } + }; +} + +// Webpack's entry is `[polyfills, appMain]`, bundled into a single `main` chunk. +// Rollup has no array-concatenation entry, so we generate a tiny combined entry +// module (in the build cache, not the source tree) that imports each in order. +// Absolute-path targets are imported by a relative path; bare specifiers (e.g. +// `core-js/stable`) are emitted as-is so Vite resolves + pre-bundles them. +function createCombinedEntry (context, targets) { + const dir = path.join(context, 'node_modules', '.cache', 'enact-vite'); + fs.mkdirSync(dir, {recursive: true}); + const file = path.join(dir, 'index.js'); + const body = + targets + .map(target => { + if (!path.isAbsolute(target)) return `import ${JSON.stringify(target)};`; + let rel = path.relative(dir, target).replace(/\\/g, '/'); + if (!rel.startsWith('.')) rel = './' + rel; + return `import ${JSON.stringify(rel)};`; + }) + .join('\n') + '\n'; + fs.writeFileSync(file, body); + return file; +} + +// Mirrors the webpack.config.js factory signature, plus a trailing `locales` +// argument (Vite-specific) for iLib locale filtering (webpack threads `-l` +// through the isomorphic mixin instead). +module.exports = function ( + env, + noLinting = false, + contentHash = false, + isomorphic = false, + noAnimation = false, + noSplitCSS = false, + ilibAdditionalResourcesPath, + locales +) { + // Lazy-require so the CLI still runs without vite installed for the webpack path. + const react = require('@vitejs/plugin-react').default || require('@vitejs/plugin-react'); + + process.chdir(app.context); + require('./dotenv').load(app.context); + app.setEnactTargetsAsDefault(); + + const useTypeScript = fs.existsSync('tsconfig.json'); + const useTailwind = fs.existsSync(path.join(app.context, 'tailwind.config.js')); + + process.env.NODE_ENV = env || process.env.NODE_ENV; + const isEnvProduction = process.env.NODE_ENV === 'production'; + const GENERATE_SOURCEMAP = process.env.GENERATE_SOURCEMAP || (isEnvProduction ? 'false' : 'true'); + const shouldUseSourceMap = GENERATE_SOURCEMAP !== 'false'; + + // Resolve the concrete app entry file (webpack resolves the package dir to its main), + // then build a combined entry that loads core-js polyfills first, then the app. + // The CLI's `polyfills.js`/`corejs-proxy.js` are CommonJS (`require('core-js/stable')`) + // which the webpack build transpiles but Vite's browser ESM can't run; so we import + // `core-js/stable` directly as an ESM bare specifier (Vite pre-bundles the CJS→ESM), + // aliased below to the CLI's copy since apps don't depend on core-js directly. + const appEntry = require.resolve(app.context); + const entry = createCombinedEntry(app.context, ['core-js/stable', appEntry]); + const coreJsDir = path.dirname(require.resolve('core-js/package.json')); + + const postcssPlugins = getPostCssPlugins({useTailwind}); + + // Backward-compatibility ilib alias, matching webpack.config.js. + const ilibAlias = fs.existsSync(path.join(app.context, 'node_modules', '@enact', 'i18n', 'ilib')) ? + {ilib: '@enact/i18n/ilib'} : + {'@enact/i18n/ilib': 'ilib'}; + + return { + root: app.context, + base: app.publicUrl || '/', + mode: isEnvProduction ? 'production' : 'development', + clearScreen: false, + logLevel: 'warn', + // Vite copies `/public` into the build output automatically (webpack: copyPublicFolder). + publicDir: 'public', + define: { + 'process.env.NODE_ENV': JSON.stringify(isEnvProduction ? 'production' : 'development'), + 'process.env.PUBLIC_URL': JSON.stringify(app.publicUrl || ''), + // Isomorphic build selects hydrateRoot vs createRoot; animation gate. + ENACT_PACK_ISOMORPHIC: JSON.stringify(!!isomorphic), + ENACT_PACK_NO_ANIMATION: JSON.stringify(!!noAnimation) + }, + resolve: { + extensions: ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.json'].filter( + ext => useTypeScript || !ext.includes('ts') + ), + // Array form so we can mix exact aliases with the regex `~` stripper. + alias: [ + ...Object.entries( + Object.assign( + {'react-is': path.dirname(require.resolve('react-is/package.json'))}, + // Resolve `core-js` (imported by the generated entry) to the CLI's copy, + // since apps don't depend on it directly. Also dedupes @enact's core-js. + {'core-js': coreJsDir}, + ilibAlias, + app.alias + ) + ).map(([find, replacement]) => ({find, replacement})), + // Strip the leading `~` from CSS `@import '~pkg'` so Vite resolves the bare + // specifier from node_modules (LESS `~` is handled by lessTildeImportPlugin). + {find: /^~/, replacement: ''} + ], + // Force a single copy of React across the app and all (possibly nested, + // e.g. @enact/limestone/node_modules/@enact/*) dependencies. Without this, + // Vite pre-bundling resolves multiple physical react copies and mixing + // components across them triggers "Invalid hook call / more than one copy + // of React". Webpack avoids this via single-tree resolution + exposing + // React on global in the isomorphic path. + dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'], + // Don't follow symlinks to their real paths (matches webpack `symlinks: false`). + preserveSymlinks: true + }, + css: { + devSourcemap: shouldUseSourceMap, + postcss: {plugins: postcssPlugins}, + // Vite treats *.module.* as CSS modules automatically. We only override the + // scoped-name generation to match the webpack cssModuleIdent output. + // cssModuleIdent expects a webpack-style loader context with `resourcePath` + // and `rootContext` (used for the ident hash); `localIdentName` is ignored. + // The result is sanitized to a valid CSS identifier: for nested @enact deps + // (e.g. @enact/limestone/node_modules/@enact/ui/…) the derived name embeds a + // literal `@`, which is invalid unescaped in a class selector. The trailing + // hash keeps names unique, so collapsing invalid chars to `_` is safe. + modules: { + generateScopedName (name, filename) { + const ident = getLocalIdent({resourcePath: filename, rootContext: app.context}, null, name); + return ident.replace(/[^a-zA-Z0-9_-]/g, '_'); + } + }, + preprocessorOptions: { + less: { + // Inject accent/skin vars and the __DEV__ flag, matching less-loader modifyVars. + modifyVars: Object.assign({__DEV__: !isEnvProduction}, app.accent), + javascriptEnabled: true, + // Resolve `~pkg` LESS imports from node_modules (webpack/less-loader behavior). + plugins: [lessTildeImportPlugin(app.context)] + } + } + }, + build: { + outDir: path.resolve('./dist'), + emptyOutDir: true, + sourcemap: shouldUseSourceMap, + minify: isEnvProduction ? 'terser' : false, + cssMinify: isEnvProduction, + // Preserve webpack-style split behaviour: single main CSS when --no-split-css. + cssCodeSplit: !noSplitCSS, + commonjsOptions: { + // Enact deps (notably iLib) mix ESM/CJS and use runtime platform detection + // that require()s Node/Qt/Rhino-only loaders. Those branches never execute + // in the browser, so ignore them at bundle time instead of failing to + // resolve. (Webpack handled iLib via ILibPlugin + WebpackLoader + node + // polyfills; a proper Vite ILib plugin is the real fix — see docs.) + transformMixedEsModules: true, + ignoreDynamicRequires: true, + ignore: id => ILIB_LOADER_RE.test(id) + }, + rollupOptions: { + // Named `main` so output is `main.js`, matching the webpack bundle name. + input: {main: entry}, + output: { + entryFileNames: contentHash ? '[name].[hash].js' : '[name].js', + chunkFileNames: contentHash ? 'chunk.[name].[hash].js' : 'chunk.[name].js', + assetFileNames: contentHash ? '[name].[hash][extname]' : '[name][extname]' + } + } + }, + esbuild: { + legalComments: 'none' + }, + optimizeDeps: { + // The dev-server dependency scanner/optimizer is esbuild-based and defaults + // the `.js` loader to `js`; Enact authors JSX inside plain `.js` files, which + // breaks the scan without this. (Request-time transforms go through + // @vitejs/plugin-react's babel, which already handles JSX-in-.js.) + esbuildOptions: { + loader: {'.js': 'jsx'}, + // Order matters: stub iLib loaders first, then babel-transform @enact source. + plugins: [ilibStubEsbuildPlugin, enactBabelEsbuildPlugin] + } + }, + server: { + host: process.env.HOST || '0.0.0.0', + port: parseInt(process.env.PORT || 8080), + hmr: true + }, + plugins: [ + react({ + // @enact/* packages ship raw source (JSX inside .js, ESM) rather than + // pre-compiled output, so they must be transpiled like app code. Mirror + // webpack's `exclude: /node_modules.(?!@enact)/`: process everything except + // non-@enact node_modules. + exclude: /[\\/]node_modules[\\/](?!@enact[\\/])/, + // Reuse the exact Enact babel preset so JSX/TS/decorator handling matches webpack. + babel: { + babelrc: false, + configFile: false, + // Advertise ESM support so babel-preset-enact's @babel/preset-env + // (`modules: 'auto'`) preserves `import`/`export` for Rollup to bundle + // and tree-shake. babel-loader sets this in the webpack path; the Vite + // react plugin does not, so without it preset-env emits CommonJS and the + // app collapses into un-bundled runtime `require()` calls. + caller: { + name: 'vite-plugin-react', + supportsStaticESM: true, + supportsDynamicImport: true, + supportsTopLevelAwait: true + }, + presets: [require.resolve('babel-preset-enact')] + } + }), + ViteHtmlPlugin({ + entry, + // Fall back to the webOS appinfo title when no app/theme title is set. + title: app.title || ViteWebOSMetaPlugin.readTitle(app.context) || '', + template: app.template || path.join(__dirname, 'html-template.ejs') + }), + // webOS metadata: emit/serve appinfo.json + referenced icon/splash assets + // and localized resources/**/appinfo.json. Skip for non-browser targets. + !['node', 'async-node', 'webworker'].includes(app.environment) && + ViteWebOSMetaPlugin({ + context: app.context, + publicPath: app.publicUrl || '/' + }), + // iLib runtime: define ILIB_* constants and make locale/resource data + // available (build: copy trees; dev: serve from source), with optional + // `-l` locale filtering. Replaces the webpack ILibPlugin. Skip for + // non-browser targets. + !['node', 'async-node', 'webworker'].includes(app.environment) && + ViteILibPlugin({ + context: app.context, + publicPath: app.publicUrl || '/', + ilibAdditionalResourcesPath, + locales + }), + // ESLint (mirrors webpack eslint-webpack-plugin); skipped with --no-linting. + !noLinting && enactEslintPlugin() + ].filter(Boolean) + }; +}; diff --git a/docs/vite-isomorphic-scope.md b/docs/vite-isomorphic-scope.md new file mode 100644 index 00000000..71ec9aea --- /dev/null +++ b/docs/vite-isomorphic-scope.md @@ -0,0 +1,127 @@ +# Scope: `--isomorphic` prerendering for the Vite path + +Port of the webpack `PrerenderPlugin` + `mixins/isomorphic.js` to the Vite +bundler path (`enact pack --isomorphic --vite`). This is the largest remaining +webpack-only feature; this doc breaks it into buildable phases with the concrete +mechanics, risks, and a validation plan. + +## Goal + +`enact pack -p -i --vite` should, for each target locale (`-l`), server-render the +app to static HTML and produce per-locale `index..html` files plus a +startup script that (a) shows the prerendered markup immediately and (b) hydrates +on the client — matching the webpack output closely enough that webOS treats it as +a prerendered app (`appinfo.usePrerendering = true`, per-locale `main`). + +## What webpack does today (reference) + +Files: [`dev-utils/mixins/isomorphic.js`](../../dev-utils/mixins/isomorphic.js), +[`dev-utils/plugins/PrerenderPlugin/`](../../dev-utils/plugins/PrerenderPlugin/) +(`index.js`, `vdom-server-render.js`, `FileXHR.js`, `templates.js`). + +1. **Build shape** — the app is built as a **UMD library** (`output.library='App'`, + `libraryTarget='umd'`, `globalObject='this'`) whose `default` export is the app + ReactElement. `src/index.js` already exports `appElement` as default and guards + `createRoot`/`hydrateRoot` behind `typeof window` + `ENACT_PACK_ISOMORPHIC`. + React is exposed on `global` (`expose-loader`) to avoid duplicate copies. +2. **Server render** (`vdom-server-render.render`, per locale): + - `global.XMLHttpRequest = FileXHR` — a synchronous fake XHR that reads iLib + locale data from **disk** so `@enact/i18n` can load it during SSR. + - `global.process.env.LANG = locale`. + - `require()` the built UMD chunk in Node → `chunk.default` = the element. + - `reactDOMServer.renderToString(chunk.default)`. + - Optional per-locale font CSS via `app.fontGenerator`, prepended to the markup. +3. **HTML assembly** (`PrerenderPlugin` html hooks + `templates.js`): + - Root CSS classes (`enact-locale-*`) are extracted from the rendered markup. + - Identical renders across locales are **deduped/aliased** (`simplifyAliases`). + - JS `` in ``, before the deferred + module entry. +- **R2 — CommonJS polyfills.** `polyfills.js` → `corejs-proxy.js` use + `require('core-js/stable')`; `require` doesn't exist in browser ESM + (`require is not defined`). Fix: the generated combined entry imports + `core-js/stable` as an ESM bare specifier (Vite pre-bundles the CJS→ESM), + aliased to the CLI's `core-js` since apps don't depend on it directly. +- **R3 — multiple copies of React** ("Invalid hook call"). Nested `@enact` deps + (`@enact/limestone/node_modules/@enact/*`) + Vite pre-bundling resolved several + physical `react` copies. Fix: `resolve.dedupe: ['react','react-dom','react/jsx-runtime','react/jsx-dev-runtime']`. + +The eight config issues (all in `config/vite.config.js` unless noted) — the +non-obvious part of the port — were: + +1. **ESM must be preserved for Rollup.** `babel-preset-enact`'s `@babel/preset-env` + uses `modules: 'auto'`, which emits **CommonJS** unless the caller advertises + ESM support. `babel-loader` sets this; `@vitejs/plugin-react` does **not**, so + the app collapsed into un-bundled runtime `require()` (a 1 kB bundle). Fix: pass + `babel.caller = { supportsStaticESM: true, … }`. +2. **PostCSS plugins must be instances.** Vite's `css.postcss.plugins` wants + instantiated plugins, not the string names `postcss-loader` resolves. Fix: + `require()` + invoke each (`loadPostCss`). +3. **`cssModuleIdent` loader context + CSS-safe names.** It reads + `context.rootContext` (for the hash); passing only `resourcePath` threw + `path.relative(undefined,…)`. Fix: pass `{resourcePath, rootContext: app.context}`. + Also, for **nested** `@enact` deps (e.g. + `@enact/limestone/node_modules/@enact/ui/…`) the derived readable name embeds a + literal `@`, invalid unescaped in a CSS class selector (108 `css-syntax-error` + warnings + broken selectors on `qa-dropdown`). Webpack avoids this by using + short hashes in production; our config uses readable names in both modes, so we + sanitize the ident to `[A-Za-z0-9_-]` (the trailing hash keeps it unique). +4. **JSX-in-`.js` for the dev scanner.** esbuild's dep scanner defaults `.js` to + the `js` loader and can't parse Enact's JSX-in-`.js`. Fix: + `optimizeDeps.esbuildOptions.loader = { '.js': 'jsx' }`. +5. **iLib non-browser loaders** (see below) break both the Rollup build and the + esbuild optimizer. Fix: a shared `ILIB_LOADER_RE` neutralizes them via + `build.commonjsOptions.ignore` and an esbuild stub plugin. +6. **`@enact/*` deps ship raw source.** Unlike most packages, `@enact/*` is + distributed unbuilt (`main` points at `src/`): JSX-in-`.js`, ESM, decorators, + and Babel proposals like `export default from 'ilib'`. It must be transpiled + like app code — exactly what webpack's `exclude: /node_modules.(?!@enact)/` does. + Two fixes, because the build and the dev pre-bundler use different engines: + (a) **Rollup build** — set the react plugin's + `exclude: /[\\/]node_modules[\\/](?!@enact[\\/])/` so babel-preset-enact runs on + `@enact/*`. (b) **Dev dependency optimizer** — esbuild can't parse the raw + syntax at all (e.g. `export default from` → `Expected ";"`), so an + `optimizeDeps` esbuild plugin (`enact-babel-optimize`) runs babel-preset-enact + on `@enact/*` source (ESM-preserving) before esbuild pre-bundles it. + (Fix (a) surfaced on Limestone; fix (b) surfaced on apps like `qa-dropdown` + whose graph pulls `@enact/i18n` into pre-bundling.) +7. **LESS/CSS `~` npm imports.** `~pkg` resolves via webpack in `less-loader`; + Vite has no equivalent. Fix: a custom Less `FileManager` (`lessTildeImportPlugin`) + for LESS `@import`s, plus a `resolve.alias` `{find: /^~/, replacement: ''}` for + plain CSS `@import`s. +8. **`~` in `@import-json` rules.** The webpack config had an inline + `postcss-import-json-tilde` plugin (which I initially missed) to resolve `~` + before `@daltontan/postcss-import-json`. Fix: ported as `tildeJsonImportPlugin`. + +## What ports cleanly (done in `vite.config.js`) + +| Concern | webpack | Vite equivalent | +| --- | --- | --- | +| JSX / TS / decorators | `babel-loader` + `babel-preset-enact` | `@vitejs/plugin-react` with `babel.presets: [babel-preset-enact]` | +| App options (ri, accent, alias, title, publicUrl…) | `optionParser` (`@enact/dev-utils`) | same `optionParser`, reused verbatim | +| PostCSS (autoprefix, flexbugs, preset-env, **resolution independence**, JSON tokens) | `postcss-loader` chain | `css.postcss.plugins` (identical array) | +| LESS accent/skin `modifyVars`, `__DEV__` | `less-loader` `lessOptions.modifyVars` | `css.preprocessorOptions.less.modifyVars` | +| SCSS/SASS | `sass-loader` | native Vite (needs `sass` dep) | +| CSS Modules scoped-name identity | `cssModuleIdent` (`getLocalIdent`) | `css.modules.generateScopedName` calling the same fn | +| `define` globals (`NODE_ENV`, `PUBLIC_URL`, `ENACT_PACK_ISOMORPHIC`, `ENACT_PACK_NO_ANIMATION`) | `DefinePlugin` | `define` | +| Content hashing / no-split-css | `output.[contenthash]`, `splitChunks` | `rollupOptions.output`, `build.cssCodeSplit` | +| Minification | Terser + CssMinimizer | `build.minify: 'terser'`, `cssMinify` | +| HTML document (no `index.html` in Enact apps) | `HtmlWebpackPlugin` + `.ejs` | `@enact/dev-utils` `ViteHtmlPlugin` renders the same template; serves it in dev, emits `index.html` (entry + CSS) in build | +| Polyfills first | `entry: [polyfills, appMain]` | generated combined entry (`node_modules/.cache/enact-vite/index.js`) | +| **iLib i18n runtime** (`ILIB_*` constants + locale/resource data) | `ILibPlugin` (`DefinePlugin` + asset emission) | `@enact/dev-utils` `ViteILibPlugin` — defines the constants (build + dev-optimizer) and copies (build) / serves (dev) the data | +| **iLib locale filtering** (`-l used/tv/signage/all/list`) | via isomorphic/prerender flow | `ViteILibPlugin` `locales` option — trims the emitted/served manifest to requested locales + shared data | +| **webOS metadata** (`appinfo.json` + icons, localized appinfo) | `WebOSMetaPlugin` | `@enact/dev-utils` `ViteWebOSMetaPlugin` — emits (build) / serves (dev) appinfo + assets; title fallback | +| **ESLint** (`eslint-config-enact`, `--no-linting`) | `eslint-webpack-plugin` | inline `enact-eslint` plugin — lints at build start; errors fail the build, dev warns | +| Source maps | `devtool` | `build.sourcemap` / `css.devSourcemap` | + +## What does NOT port yet (webpack-only, blocks a full swap) + +These live in `@enact/dev-utils/plugins` and tap the webpack `compiler`/`compilation` +lifecycle. Each needs a bespoke Vite/Rollup plugin: + +1. ~~**`ILibPlugin`**~~ — **ported** as + [`ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin). Since `@enact/i18n`'s + runtime `Loader.js` is bundler-agnostic (XHR from the `ILIB_*` constants), the + Vite plugin defines those constants (build **and** the dev dep-optimizer, via + `optimizeDeps.esbuildOptions.define`) and makes the data available — copying the + iLib `locale/` + app/theme `resources/` trees on `writeBundle` (build) and + serving them from source via middleware (dev). Non-browser iLib loaders are + neutralized separately (`ILIB_LOADER_RE`). **Locale filtering** (webpack's `-l`) + is supported via the `locales` option — `-l en-US,ko-KR` trims 70 MB → 19 MB + (6,755 → 1,988 files) and emits a trimmed manifest. **Validated:** + `/node_modules/ilib` baked into the prod bundle; full and filtered data + copied (build) / served (HTTP 200, dev). +2. ~~**`WebOSMetaPlugin`**~~ — **ported** as + [`ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin). Discovers + the root `appinfo.json` (root or `./webos-meta/`) + localized + `resources/**/appinfo.json`, emits them and their referenced icon/splash assets + (build: `writeBundle` copy; dev: middleware), and supplies the `` + fallback via `ViteWebOSMetaPlugin.readTitle`. **Validated:** `appinfo.json` + + `icon*.png` land in `dist` and serve (HTTP 200) in dev. *Remaining:* + `$`-prefixed sys-assets. +3. **`PrerenderPlugin` + isomorphic mixin** — server-renders the app to static + HTML per-locale (`enact pack --isomorphic`). *Effort: high — not ported.* + **Investigated:** Vite has native SSR (`ssrLoadModule`), but a spike hit a + concrete first blocker — the SSR module runner does not apply the JSX-in-`.js` + transform (`App.js: Unexpected token '<'`), and that is only the first hurdle. + A faithful port also needs a `window`/`document` mock, the `FileXHR` locale-data + loader, per-locale rendering, `screenTypes`/font handling, a `locale-map`, and + hydration-safe markup — i.e. a re-implementation of `vdom-server-render` + + `templates`, not a config tweak. Left on webpack. **A full implementation + scope** (phases, effort, risks, validation plan) is in + [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). +4. **`SnapshotPlugin`** — emits a V8 snapshot blob (`--snapshot`). *Not portable + here.* Requires the webOS `V8_MKSNAPSHOT` toolchain (absent in this + environment, so unverifiable) and is coupled to the webpack module runtime + + injected snapshot-helper entries. A Rollup equivalent would need its own + snapshot-safe bootstrap; deferred until the mksnapshot toolchain is available. +5. **Framework externals** (`EnactFrameworkPlugin` / `EnactFrameworkRefPlugin`, + `mixins/framework.js`, `mixins/externals.js`) — DLL-style shared `@enact/*` + + react framework bundle. *Effort: high — not ported.* Webpack's DLL maps module + requests to IDs in a prebuilt bundle via a manifest; Rollup has no equivalent. + A Vite port needs a two-build strategy (a UMD `enact_framework` lib build + + `build.rollupOptions.external` in the app build) plus runtime linking of + `@enact/*`/`react` to the external bundle (import map or UMD globals) — a real + project, not validated here. +6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not + needed under Vite (different FS handling). *Drop.* +7. **`node-polyfill-webpack-plugin`** — Node builtin polyfills for screenshot + tests. Vite: use `vite-plugin-node-polyfills` if required. +8. **`icss` mode for non-`*.module` CSS / `forceCSSModules`** — Vite auto-treats + only `*.module.*` as modules; the webpack `mode: 'icss'` nuance and the global + `forceCSSModules` toggle need a custom transform. +9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (fixes #7 and #8 above): + `lessTildeImportPlugin` (LESS), `resolve.alias /^~/` (CSS), and + `tildeJsonImportPlugin` (`@import-json`). + +## Command wiring (applied, behind a flag) + +`commands/pack.js` and `commands/serve.js` now branch to the Vite path when it is +opted into via **`--vite`** or **`ENACT_BUNDLER=vite`**; otherwise webpack runs +unchanged. Both bundlers coexist during migration. + +- `enact serve --vite` → `vite.createServer(...).listen()` (native ESM dev server, HMR via `@vitejs/plugin-react`). +- `enact pack --vite` / `enact pack -p --vite` → `vite.build(...)` (supports `--watch`, `-o/--output`, `--content-hash`, `--no-split-css`, `-l/--locales`, `--no-linting`). +- Webpack-only flags on the Vite path (`--isomorphic`, `--snapshot`, framework/externals) print a "not yet supported, ignored" notice. + +The reusable bundler plugins were **added to `@enact/dev-utils`** — mirroring how +the webpack plugins (`ILibPlugin`, `WebOSMetaPlugin`, …) live there — and are +consumed by `config/vite.config.js`: +[`ViteHtmlPlugin`](../../dev-utils/plugins/ViteHtmlPlugin), +[`ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin), and +[`ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin). The +config-level pieces (PostCSS chain incl. `~`/JSON-import handling, LESS +`modifyVars`, the ESLint plugin) stay in `cli/config`, matching where +`getStyleLoaders`/the eslint config live for webpack. + +> **dev-utils must be current.** Because these plugins live in `@enact/dev-utils`, +> the copy under `cli/node_modules/@enact/dev-utils` must include them. Symlink the +> sibling source (`npm link`/junction) or reinstall so the CLI picks up +> `ViteHtmlPlugin`, `ViteILibPlugin`, and `ViteWebOSMetaPlugin`. (They have been +> synced into the local install here.) + +## Try it + +```bash +cd cli +npm install # pulls in vite + @vitejs/plugin-react + terser +# from an Enact app dir: +enact serve --vite # or: ENACT_BUNDLER=vite enact serve +enact pack -p --vite # production build (full iLib data) +enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered +``` + +> Status: **validated** on Sandstone and Limestone (build + serve), including +> iLib i18n runtime + locale filtering, webOS metadata, and ESLint. Not yet +> exercised: the `icss`/`forceCSSModules` nuance. Node 20+ is required for +> `require()` of the ESM-only `vite` package (validated on Node 24). + +## Still not ported (webpack remains the default for these) + +- **`--isomorphic`** prerendering, **`--snapshot`**, and **framework externals** — + see gaps #3–#5 above. Each is a substantial project (not a config tweak); the + Vite path prints a "not yet supported, ignored" notice for these flags. +- **`icss` mode / `forceCSSModules`** (gap #8): Vite treats only `*.module.*` as + CSS modules; the webpack `mode: 'icss'` nuance isn't replicated. + +## Recommendation + +Adopt Vite behind a feature flag for the **browser dev/build** path first (biggest +DX win, lowest risk) — now validated end-to-end including i18n runtime, locale +filtering, webOS metadata, and ESLint. Keep webpack as the default for +`--isomorphic`, `--snapshot`, and framework-externals builds; those three are the +remaining work and each warrants its own focused effort (isomorphic first, as it's +the most-used; snapshot last, as it needs the webOS mksnapshot toolchain to even +validate). diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 3b4cc045..210e0a78 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -17,6 +17,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@vitejs/plugin-react": "^5.2.0", "babel-jest": "^30.4.1", "babel-loader": "^10.1.1", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -72,8 +73,10 @@ "strip-ansi": "^7.2.0", "style-loader": "^4.0.0", "tar": "^7.5.19", + "terser": "^5.48.0", "terser-webpack-plugin": "^5.6.1", "validate-npm-package-name": "^7.0.2", + "vite": "^7.3.6", "webpack": "^5.108.4", "webpack-dev-server": "^5.2.5" }, @@ -1572,6 +1575,36 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-pure-annotations": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", @@ -3747,8 +3780,9 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -3758,8 +3792,9 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -3768,8 +3803,9 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3779,15 +3815,17 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3858,22 +3896,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@types/json5": { "version": "0.0.29", @@ -3886,8 +3927,9 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "undici-types": "~8.3.0" } @@ -3896,8 +3938,9 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -3907,29 +3950,33 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -3940,15 +3987,17 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3960,8 +4009,9 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -3970,8 +4020,9 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "extraneous": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -3980,15 +4031,17 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4004,8 +4057,9 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -4018,8 +4072,9 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4031,8 +4086,9 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4046,8 +4102,9 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -4057,15 +4114,17 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "extraneous": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "extraneous": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/acorn": { "version": "8.17.0", @@ -4083,8 +4142,9 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10.13.0" }, @@ -4135,8 +4195,9 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -4153,8 +4214,9 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4170,15 +4232,17 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -4383,8 +4447,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "extraneous": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/brace-expansion": { "version": "1.1.15", @@ -4446,8 +4511,9 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/call-bind": { "version": "1.0.9", @@ -4512,8 +4578,9 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -4555,8 +4622,9 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.0" } @@ -4565,8 +4633,9 @@ "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "source-map": "~0.6.0" }, @@ -4598,8 +4667,9 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 12" } @@ -4663,8 +4733,9 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -4680,8 +4751,9 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">= 6" }, @@ -4821,8 +4893,9 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "utila": "~0.4" } @@ -4831,8 +4904,9 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -4846,21 +4920,23 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "extraneous": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/fb55" } ], - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/domhandler": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -4875,8 +4951,9 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -4890,8 +4967,9 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -4922,8 +5000,9 @@ "version": "5.24.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" @@ -4936,8 +5015,9 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -5053,8 +5133,9 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/es-object-atoms": { "version": "1.1.2", @@ -5204,9 +5285,11 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/eslint-config-enact/-/eslint-config-enact-5.1.2.tgz", "integrity": "sha512-nXHHZdqGiSz8GtiZ09uq3shiCugWMqL68uh89ddd98w1ybMsrqZtVBKgfPlJl6bn8l0FqGAWVgev0c/sZIi0MQ==", - "extraneous": true, + "dev": true, "hasShrinkwrap": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@babel/eslint-parser": "^7.29.7", "@babel/eslint-plugin": "^7.29.7", @@ -5234,8 +5317,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", @@ -5249,8 +5334,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5259,8 +5346,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -5290,8 +5379,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz", "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -5309,8 +5400,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/eslint-plugin/-/eslint-plugin-7.29.7.tgz", "integrity": "sha512-9qag/N0IHYLNf0nCNvHctNGBpoY4Idr7JkPeYUf2p5J9UpvHpd+Xx6VGjHDhJKmueVBa4/cq6MPl3uxj6/8knw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "eslint-rule-composer": "^0.3.0" }, @@ -5326,8 +5419,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", @@ -5343,8 +5438,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/types": "^7.29.7" }, @@ -5356,8 +5453,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", @@ -5373,8 +5472,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", @@ -5395,8 +5496,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", @@ -5413,8 +5516,10 @@ "version": "0.6.8", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -5430,8 +5535,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5440,8 +5547,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" @@ -5454,8 +5563,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" @@ -5468,8 +5579,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", @@ -5486,8 +5599,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/types": "^7.29.7" }, @@ -5499,8 +5614,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5509,8 +5626,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-wrap-function": "^7.29.7", @@ -5527,8 +5646,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", @@ -5545,8 +5666,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" @@ -5559,8 +5682,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5569,8 +5694,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5579,8 +5706,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -5589,8 +5718,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", @@ -5604,8 +5735,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" @@ -5618,8 +5751,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/types": "^7.29.7" }, @@ -5634,8 +5769,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" @@ -5651,8 +5788,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5667,8 +5806,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5683,8 +5824,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" @@ -5700,8 +5843,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", @@ -5718,8 +5863,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" @@ -5735,8 +5882,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -5753,8 +5902,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.29.7.tgz", "integrity": "sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5769,8 +5920,10 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -5782,8 +5935,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5798,8 +5953,10 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -5811,8 +5968,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5827,8 +5986,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5843,8 +6004,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5859,8 +6022,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5875,8 +6040,10 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -5892,8 +6059,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5908,8 +6077,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-remap-async-to-generator": "^7.29.7", @@ -5926,8 +6097,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -5944,8 +6117,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5960,8 +6135,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -5976,8 +6153,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -5993,8 +6172,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6010,8 +6191,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", @@ -6031,8 +6214,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/template": "^7.29.7" @@ -6048,8 +6233,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" @@ -6065,8 +6252,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6082,8 +6271,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6098,8 +6289,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6115,8 +6308,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6131,8 +6326,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7" @@ -6148,8 +6345,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6164,8 +6363,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6180,8 +6381,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" @@ -6197,8 +6400,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -6215,8 +6420,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6231,8 +6438,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6247,8 +6456,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6263,8 +6474,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6279,8 +6492,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6296,8 +6511,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6313,8 +6530,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -6332,8 +6551,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6349,8 +6570,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6366,8 +6589,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6382,8 +6607,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6398,8 +6625,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6414,8 +6643,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -6434,8 +6665,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7" @@ -6451,8 +6684,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6467,8 +6702,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" @@ -6484,8 +6721,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6500,8 +6739,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6517,8 +6758,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", @@ -6535,8 +6778,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6551,8 +6796,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6567,8 +6814,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-module-imports": "^7.29.7", @@ -6587,8 +6836,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.29.7" }, @@ -6603,8 +6854,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6620,8 +6873,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6636,8 +6891,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6653,8 +6910,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6669,8 +6928,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz", "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -6690,8 +6951,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6706,8 +6969,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" @@ -6723,8 +6988,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6739,8 +7006,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6755,8 +7024,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6771,8 +7042,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", @@ -6791,8 +7064,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -6807,8 +7082,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6824,8 +7101,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6841,8 +7120,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -6858,8 +7139,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", @@ -6944,8 +7227,10 @@ "version": "0.14.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" @@ -6958,8 +7243,10 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -6973,8 +7260,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", "integrity": "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", @@ -6994,8 +7283,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", @@ -7014,8 +7305,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -7024,8 +7317,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", @@ -7039,8 +7334,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -7058,8 +7355,10 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" @@ -7072,8 +7371,10 @@ "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -7091,8 +7392,10 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -7104,8 +7407,10 @@ "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -7114,8 +7419,10 @@ "version": "0.21.2", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -7129,15 +7436,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/@eslint/config-array/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7147,8 +7458,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7160,8 +7473,10 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -7173,8 +7488,10 @@ "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -7186,8 +7503,10 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", @@ -7210,15 +7529,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7228,8 +7551,10 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -7241,8 +7566,10 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 4" } @@ -7251,8 +7578,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7264,8 +7593,10 @@ "version": "9.39.4", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -7277,8 +7608,10 @@ "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -7287,8 +7620,10 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -7301,8 +7636,10 @@ "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@humanfs/types": "^0.15.0" }, @@ -7314,8 +7651,10 @@ "version": "0.16.8", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", @@ -7329,8 +7668,10 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=18.18.0" } @@ -7339,8 +7680,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=12.22" }, @@ -7353,8 +7696,10 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=18.18" }, @@ -7367,8 +7712,10 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -7378,8 +7725,10 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -7389,8 +7738,10 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -7399,15 +7750,19 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -7417,8 +7772,10 @@ "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "eslint-scope": "5.1.1" } @@ -7427,22 +7784,28 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/@typescript-eslint/eslint-plugin": { "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz", "integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.62.0", @@ -7470,8 +7833,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.62.0", "@typescript-eslint/types": "8.62.0", @@ -7495,8 +7860,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.62.0", "@typescript-eslint/types": "^8.62.0", @@ -7517,8 +7884,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.62.0", "@typescript-eslint/visitor-keys": "8.62.0" @@ -7535,8 +7904,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -7552,8 +7923,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz", "integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.62.0", "@typescript-eslint/typescript-estree": "8.62.0", @@ -7577,8 +7950,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -7591,8 +7966,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/project-service": "8.62.0", "@typescript-eslint/tsconfig-utils": "8.62.0", @@ -7619,8 +7996,10 @@ "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -7632,8 +8011,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz", "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.62.0", @@ -7656,8 +8037,10 @@ "version": "8.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.62.0", "eslint-visitor-keys": "^5.0.0" @@ -7674,8 +8057,10 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, @@ -7687,8 +8072,10 @@ "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7700,8 +8087,10 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -7710,8 +8099,10 @@ "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7727,8 +8118,10 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7743,15 +8136,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "extraneous": true, - "license": "Python-2.0" + "dev": true, + "license": "Python-2.0", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -7760,8 +8157,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -7777,8 +8176,10 @@ "version": "3.1.9", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -7800,8 +8201,10 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7821,8 +8224,10 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7840,8 +8245,10 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7859,8 +8266,10 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7876,8 +8285,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -7898,15 +8309,19 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -7915,8 +8330,10 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -7931,8 +8348,10 @@ "version": "4.12.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==", - "extraneous": true, + "dev": true, "license": "MPL-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -7941,8 +8360,10 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -7951,8 +8372,10 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/babel-plugin-dev-expression/-/babel-plugin-dev-expression-0.2.3.tgz", "integrity": "sha512-rP5LK9QQTzCW61nVVzw88En1oK8t8gTsIeC6E61oelxNsU842yMjF0G1MxhvUpCkxCEIj7sE8/e5ieTheT//uw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "peerDependencies": { "@babel/core": "^7.0.0" } @@ -7961,8 +8384,10 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "object.assign": "^4.1.0" } @@ -7971,8 +8396,10 @@ "version": "0.4.17", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-define-polyfill-provider": "^0.6.8", @@ -7986,8 +8413,10 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" @@ -8000,8 +8429,10 @@ "version": "0.6.8", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8" }, @@ -8013,15 +8444,19 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/babel-preset-enact": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/babel-preset-enact/-/babel-preset-enact-0.1.17.tgz", "integrity": "sha512-0x8sc//pDJazXhGvlOD4w8mecFVcUZvuiIi3JLK/1TBYZ7Q4/8jnXjii61OZyyGGwy30QCflf+WTy3A6rQubbg==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@babel/core": "^7.29.7", "@babel/plugin-proposal-decorators": "^7.29.7", @@ -8041,8 +8476,10 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": "18 || 20 || >=22" } @@ -8051,8 +8488,10 @@ "version": "2.10.38", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "bin": { "baseline-browser-mapping": "dist/cli.cjs" }, @@ -8064,8 +8503,10 @@ "version": "5.0.6", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -8077,7 +8518,7 @@ "version": "4.28.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", - "extraneous": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -8093,6 +8534,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", @@ -8111,8 +8554,10 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -8130,8 +8575,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -8144,8 +8591,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -8161,8 +8610,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -8171,7 +8622,7 @@ "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", - "extraneous": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -8186,14 +8637,18 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0" + "license": "CC-BY-4.0", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8209,8 +8664,10 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8222,29 +8679,37 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.28.1" }, @@ -8257,8 +8722,10 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -8272,15 +8739,19 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "extraneous": true, - "license": "BSD-2-Clause" + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -8297,8 +8768,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -8315,8 +8788,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -8333,8 +8808,10 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -8351,15 +8828,19 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8376,8 +8857,10 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8394,8 +8877,10 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -8407,8 +8892,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -8422,22 +8909,28 @@ "version": "1.5.377", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.377.tgz", "integrity": "sha512-cH1jZgJHoezfTnKfKwnScpHywTFVnJUNITDPREFdhNjiuD502+QFpG0Qk7G8jhsV/f+CEAFlIrzP1fT+IMb92g==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", @@ -8505,8 +8998,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", @@ -8524,8 +9019,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -8534,8 +9031,10 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -8544,8 +9043,10 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz", "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", @@ -8572,8 +9073,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -8585,8 +9088,10 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -8601,8 +9106,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -8614,8 +9121,10 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz", "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-abstract-get": "^1.0.0", "es-errors": "^1.3.0", @@ -8634,8 +9143,10 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -8644,8 +9155,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -8657,8 +9170,10 @@ "version": "9.39.4", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8717,8 +9232,10 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/eslint-plugin-enact/-/eslint-plugin-enact-2.0.4.tgz", "integrity": "sha512-BGl2iswi85FUl/fTZ9WEQq+M6oHzcP4YjK4eCxaISeO/BIMjYenp76QHzKFNG20GFjeaK5Eo/Rpah7DhSkDLeQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "doctrine": "^3.0.0", "jsx-ast-utils": "^3.3.5", @@ -8735,8 +9252,10 @@ "version": "29.15.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.2.tgz", "integrity": "sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/utils": "^8.0.0" }, @@ -8765,8 +9284,10 @@ "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -8795,15 +9316,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8813,8 +9338,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8826,8 +9353,10 @@ "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -8859,8 +9388,10 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", @@ -8879,15 +9410,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8897,8 +9432,10 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -8910,8 +9447,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8923,8 +9462,10 @@ "version": "2.0.0-next.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", @@ -8947,8 +9488,10 @@ "version": "7.16.2", "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-7.16.2.tgz", "integrity": "sha512-8gleGnQXK2ZA3hHwjCwpYTZvM+9VsrJ+/9kDI8CjqAQGAdMQOdn/rJNu7ZySENuiWlGKQWyZJ4ZjEg2zamaRHw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "^8.56.0", "@typescript-eslint/utils": "^8.56.0" @@ -8964,8 +9507,10 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4.0.0" } @@ -8974,8 +9519,10 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -8988,8 +9535,10 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -8998,8 +9547,10 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=10" } @@ -9008,15 +9559,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9026,8 +9581,10 @@ "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9043,8 +9600,10 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9056,8 +9615,10 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 4" } @@ -9066,8 +9627,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9079,8 +9642,10 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -9097,8 +9662,10 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9110,8 +9677,10 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "extraneous": true, + "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -9123,8 +9692,10 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -9136,8 +9707,10 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -9146,8 +9719,10 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9156,29 +9731,37 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12.0.0" }, @@ -9195,8 +9778,10 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -9208,8 +9793,10 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9225,8 +9812,10 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -9239,15 +9828,19 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-callable": "^1.2.7" }, @@ -9262,8 +9855,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9272,8 +9867,10 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", @@ -9296,8 +9893,10 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9306,8 +9905,10 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -9316,8 +9917,10 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -9326,8 +9929,10 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -9351,8 +9956,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -9365,8 +9972,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -9383,8 +9992,10 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -9396,8 +10007,10 @@ "version": "17.7.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -9409,8 +10022,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -9426,8 +10041,10 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9439,8 +10056,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9452,8 +10071,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -9462,8 +10083,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -9475,8 +10098,10 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "dunder-proto": "^1.0.0" }, @@ -9491,8 +10116,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9504,8 +10131,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -9520,8 +10149,10 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -9533,15 +10164,19 @@ "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/hermes-parser": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hermes-estree": "0.25.1" } @@ -9550,8 +10185,10 @@ "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 4" } @@ -9560,8 +10197,10 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -9577,8 +10216,10 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.19" } @@ -9587,8 +10228,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -9602,8 +10245,10 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -9620,8 +10265,10 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -9640,8 +10287,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-bigints": "^1.0.2" }, @@ -9656,8 +10305,10 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -9673,8 +10324,10 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9686,8 +10339,10 @@ "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hasown": "^2.0.3" }, @@ -9702,8 +10357,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -9720,8 +10377,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -9737,8 +10396,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.4" }, @@ -9753,8 +10414,10 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9763,8 +10426,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3" }, @@ -9779,8 +10444,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", @@ -9799,8 +10466,10 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -9812,8 +10481,10 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9825,8 +10496,10 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9838,8 +10511,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -9855,8 +10530,10 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -9874,8 +10551,10 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9887,8 +10566,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3" }, @@ -9903,8 +10584,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -9920,8 +10603,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -9938,8 +10623,10 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "which-typed-array": "^1.1.16" }, @@ -9954,8 +10641,10 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9967,8 +10656,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3" }, @@ -9983,8 +10674,10 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -10000,22 +10693,28 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", @@ -10032,14 +10731,16 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/js-yaml": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", - "extraneous": true, + "dev": true, "funding": [ { "type": "github", @@ -10051,6 +10752,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -10062,8 +10765,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -10075,29 +10780,37 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -10109,8 +10822,10 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -10125,8 +10840,10 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -10135,15 +10852,19 @@ "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "extraneous": true, - "license": "CC0-1.0" + "dev": true, + "license": "CC0-1.0", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/language-tags": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -10155,8 +10876,10 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -10169,8 +10892,10 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -10185,22 +10910,28 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -10212,8 +10943,10 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -10222,8 +10955,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -10232,8 +10967,10 @@ "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "extraneous": true, + "dev": true, "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^5.0.5" }, @@ -10248,22 +10985,28 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/node-exports-info": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", @@ -10281,8 +11024,10 @@ "version": "2.0.48", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -10291,8 +11036,10 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10301,8 +11048,10 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -10314,8 +11063,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -10324,8 +11075,10 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10345,8 +11098,10 @@ "version": "1.1.9", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -10361,8 +11116,10 @@ "version": "2.0.8", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10380,8 +11137,10 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10399,8 +11158,10 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -10417,8 +11178,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -10435,8 +11198,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -10451,8 +11216,10 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -10467,8 +11234,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -10480,8 +11249,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -10490,8 +11261,10 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -10500,22 +11273,28 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -10527,8 +11306,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -10537,8 +11318,10 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -10547,8 +11330,10 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -10559,8 +11344,10 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -10569,8 +11356,10 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10579,15 +11368,19 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -10609,15 +11402,19 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/regenerate-unicode-properties": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -10629,8 +11426,10 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -10650,8 +11449,10 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", @@ -10668,15 +11469,19 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/regjsparser": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "jsesc": "~3.1.0" }, @@ -10688,8 +11493,10 @@ "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", @@ -10710,8 +11517,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -10720,8 +11529,10 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", @@ -10740,8 +11551,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -10757,8 +11570,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -10775,8 +11590,10 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -10785,8 +11602,10 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10803,8 +11622,10 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10819,8 +11640,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -10834,8 +11657,10 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10847,8 +11672,10 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -10857,8 +11684,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", @@ -10877,8 +11706,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" @@ -10894,8 +11725,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -10913,8 +11746,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -10933,8 +11768,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" @@ -10947,8 +11784,10 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10962,8 +11801,10 @@ "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10990,8 +11831,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -11001,8 +11844,10 @@ "version": "1.2.11", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", @@ -11024,8 +11869,10 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", @@ -11043,8 +11890,10 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -11061,8 +11910,10 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" }, @@ -11074,8 +11925,10 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11087,8 +11940,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -11100,8 +11955,10 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" @@ -11117,8 +11974,10 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18.12" }, @@ -11130,8 +11989,10 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -11143,8 +12004,10 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -11158,8 +12021,10 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -11178,8 +12043,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -11200,8 +12067,10 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", @@ -11221,8 +12090,10 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "extraneous": true, + "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11235,8 +12106,10 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -11254,8 +12127,10 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -11264,8 +12139,10 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -11278,8 +12155,10 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -11288,8 +12167,10 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -11298,7 +12179,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "extraneous": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -11314,6 +12195,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -11329,8 +12212,10 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "extraneous": true, + "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -11339,8 +12224,10 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -11355,8 +12242,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -11375,8 +12264,10 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -11403,8 +12294,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -11422,8 +12315,10 @@ "version": "1.1.22", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", @@ -11444,8 +12339,10 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11454,15 +12351,19 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/eslint-config-enact/node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -11474,8 +12375,10 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -11484,8 +12387,10 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -11753,8 +12658,9 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -11766,8 +12672,9 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -11786,8 +12693,9 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.x" } @@ -11796,8 +12704,9 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/fast-diff": { "version": "1.3.0", @@ -11852,7 +12761,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "extraneous": true, "funding": [ { "type": "github", @@ -11863,7 +12771,9 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/fastq": { "version": "1.20.1", @@ -12135,8 +13045,9 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "extraneous": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/globals": { "version": "17.7.0", @@ -12204,8 +13115,9 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -12284,8 +13196,9 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "he": "bin/he" } @@ -12300,8 +13213,9 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "camel-case": "^4.1.2", "clean-css": "^5.2.2", @@ -12322,8 +13236,9 @@ "version": "5.6.7", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", @@ -12355,7 +13270,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "extraneous": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -12364,6 +13278,8 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -12844,8 +13760,9 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -12946,8 +13863,9 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.11.5" }, @@ -12985,8 +13903,9 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/lodash.merge": { "version": "4.6.2", @@ -13011,8 +13930,9 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.0.3" } @@ -13031,8 +13951,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/merge2": { "version": "1.4.1", @@ -13060,8 +13981,9 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.6" } @@ -13129,15 +14051,17 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -13207,8 +14131,9 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -13419,8 +14344,9 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13442,8 +14368,9 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -13561,8 +14488,9 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" @@ -13665,8 +14593,9 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.10" } @@ -13681,8 +14610,9 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", @@ -13695,8 +14625,9 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13823,8 +14754,9 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -13843,8 +14775,9 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -13860,8 +14793,9 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -13873,8 +14807,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/semver": { "version": "6.3.1", @@ -14058,8 +14993,9 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "extraneous": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14068,8 +15004,9 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -14153,8 +15090,9 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14189,8 +15127,9 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14246,8 +15185,9 @@ "version": "5.48.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -14265,8 +15205,9 @@ "version": "5.6.1", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -14326,8 +15267,9 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/to-regex-range": { "version": "5.0.1", @@ -14373,8 +15315,9 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "extraneous": true, - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/type-check": { "version": "0.4.0", @@ -14516,8 +15459,9 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/update-browserslist-db": { "version": "1.2.3", @@ -14563,15 +15507,17 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@enact/dev-utils/node_modules/watchpack": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.2" }, @@ -14589,8 +15535,9 @@ "version": "5.107.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", @@ -14690,8 +15637,9 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -14704,8 +15652,9 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "extraneous": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -14878,6 +15827,422 @@ "node": ">=20.5.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -17833,6 +19198,337 @@ } } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -18390,9 +20086,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18406,9 +20099,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -18422,9 +20112,6 @@ "cpu": [ "loong64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18438,9 +20125,6 @@ "cpu": [ "loong64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -18454,9 +20138,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18470,9 +20151,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18486,9 +20164,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -18502,9 +20177,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18518,9 +20190,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -18534,9 +20203,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -18613,6 +20279,26 @@ "win32" ] }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -20500,6 +22186,15 @@ "node": ">=8" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -21802,6 +23497,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -28512,6 +30248,23 @@ "bser": "2.1.1" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -37997,6 +39750,50 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, "node_modules/run-applescript": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", @@ -39552,6 +41349,22 @@ "node": ">=0.6.0" } }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -40123,6 +41936,80 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -40786,15 +42673,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, - "node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, "node_modules/yargs": { "version": "17.7.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", diff --git a/package.json b/package.json index d492bb41..b57d621f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@vitejs/plugin-react": "^5.2.0", "babel-jest": "^30.4.1", "babel-loader": "^10.1.1", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -106,8 +107,10 @@ "strip-ansi": "^7.2.0", "style-loader": "^4.0.0", "tar": "^7.5.19", + "terser": "^5.48.0", "terser-webpack-plugin": "^5.6.1", "validate-npm-package-name": "^7.0.2", + "vite": "^7.3.6", "webpack": "^5.108.4", "webpack-dev-server": "^5.2.5" }, From 41a9a4c469a47bc53198a90a17489fa7f45248e1 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Thu, 9 Jul 2026 15:28:34 +0300 Subject: [PATCH 02/20] added vite bundler --- commands/pack.js | 15 +++-- commands/serve.js | 17 +++--- commands/vite-utils.js | 12 ++++ config/postcss-plugins.js | 125 ++++++++++++++++++++++++++++++++++++++ config/vite.config.js | 80 +----------------------- config/webpack.config.js | 118 +---------------------------------- docs/vite-migration.md | 23 ++++--- 7 files changed, 171 insertions(+), 219 deletions(-) create mode 100644 commands/vite-utils.js create mode 100644 config/postcss-plugins.js diff --git a/commands/pack.js b/commands/pack.js index 41c88493..80f454f0 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -20,6 +20,7 @@ const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const printBuildError = require('react-dev-utils/printBuildError'); const webpack = require('webpack'); const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils'); +const {useVite} = require('./vite-utils'); let chalk; let stripAnsi; @@ -198,13 +199,13 @@ function printErrorDetails (err, handler) { } } -function useVite (opts) { - return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); -} // Experimental Vite build path. Mirrors the webpack `build`/`watch` behavior but -// drives Vite's JS API. Webpack-only features (isomorphic, snapshot, framework -// externals) are not yet supported here and are reported as ignored. +// drives Vite's JS API. The webpack-only features `--isomorphic` (prerendering), +// `--snapshot`, and `--framework`/`--externals` are not supported here: each is +// reported and then genuinely skipped. In particular `--isomorphic` is forced off +// (client render) rather than forwarded, because setting ENACT_PACK_ISOMORPHIC +// without prerendered markup would make the app hydrate an empty root. async function viteBuild (opts) { const {build: viteBuildApi} = require('vite'); @@ -221,7 +222,9 @@ async function viteBuild (opts) { opts.production ? 'production' : 'development', !opts.linting, opts['content-hash'], - opts.isomorphic, + // isomorphic forced off: prerendering isn't ported, so hydrateRoot has no + // server markup to hydrate. Client render (createRoot) is the correct fallback. + false, !opts.animation, !opts['split-css'], opts['ilib-additional-path'], diff --git a/commands/serve.js b/commands/serve.js index 19ead897..37e75ce0 100755 --- a/commands/serve.js +++ b/commands/serve.js @@ -26,6 +26,7 @@ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin' const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); const {optionParser: app} = require('@enact/dev-utils'); +const {useVite} = require('./vite-utils'); let chalk; @@ -252,10 +253,6 @@ function devServerConfig (host, port, protocol, publicPath, proxy, allowedHost) }; } -// Whether the experimental Vite bundler is selected (via `--vite` or ENACT_BUNDLER=vite). -function useVite (opts) { - return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); -} // Experimental Vite dev server path. Uses Vite's native dev server (esbuild + // native ESM) in place of webpack-dev-server; HMR is handled by @vitejs/plugin-react. @@ -270,15 +267,15 @@ async function viteServe (opts, host, port) { open: Boolean(opts.browser) }); + // Show a "building" message while the server starts up, matching the webpack path. + if (process.stdout.isTTY) clearConsole(); + console.log(chalk.cyan('Starting the development server...')); + console.log(); + const server = await createServer(config); await server.listen(); - if (process.stdout.isTTY) clearConsole(); - // Print the same "ready" message the webpack path shows (via react-dev-utils), - // using the same `prepareUrls` helper so the Local / On Your Network URLs match. - // Vite's own `server.printUrls()` logs at 'info', which our `logLevel: 'warn'` - // suppresses, so we format it ourselves. Vite compiles on demand, so this - // reports the server is ready rather than a completed upfront compile. + // Append the same "ready" message the webpack path shows const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; // Strip a trailing slash from the base so root URLs read `http://host:port` // (matching the webpack path, which passes `publicPath.slice(0, -1)`). diff --git a/commands/vite-utils.js b/commands/vite-utils.js new file mode 100644 index 00000000..848fff4b --- /dev/null +++ b/commands/vite-utils.js @@ -0,0 +1,12 @@ +/* eslint-env node, es6 */ +/** + * Shared helpers for the experimental Vite bundler path, used by both the + * `pack` and `serve` commands. + */ + +// Whether the experimental Vite bundler is selected (via `--vite` or ENACT_BUNDLER=vite). +function useVite (opts) { + return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); +} + +module.exports = {useVite}; diff --git a/config/postcss-plugins.js b/config/postcss-plugins.js new file mode 100644 index 00000000..bf241f0c --- /dev/null +++ b/config/postcss-plugins.js @@ -0,0 +1,125 @@ +/* eslint no-console: off, no-undef: off */ +/* eslint-env node, es6 */ +/** + * Shared PostCSS plugin chain used by both `webpack.config.js` (via + * `postcss-loader`) and `vite.config.js` (via `css.postcss.plugins`). Both + * bundlers accept instantiated plugins, so `getPostCssPlugins` returns them + * ready to use — a single source of truth for autoprefixing, resolution + * independence, and JSON-token imports. + */ +const fs = require('fs'); +const path = require('path'); +const {optionParser: app} = require('@enact/dev-utils'); + +// Require and initialize a PostCSS plugin (invoking the creator with its options). +function loadPostCss (name, opts) { + const mod = require(name); + const creator = mod && mod.default ? mod.default : mod; + return typeof creator === 'function' ? creator(opts) : creator; +} + +// Support importing JSON files with the `~` alias in `@import-json` rules — a +// custom plugin that resolves the `~pkg` specifier to a path relative to the +// source file (mimicking webpack's `~` alias). Must run before the import-json +// plugin below. +function tildeJsonImportPlugin () { + return { + postcssPlugin: 'postcss-import-json-tilde', + Once (root) { + // Process all @import-json rules with ~ prefix first, before other plugins + root.walkAtRules('import-json', atRule => { + let src = atRule.params.slice(1, -1); // Remove quotes + + // Only handle ~ alias paths + if (src.startsWith('~')) { + const packagePath = src.substring(1); // Remove ~ + + try { + // Use Node.js standard module resolution + // This mimics webpack's ~ alias behavior + const currentFileDir = path.dirname(atRule.source.input.file || ''); + + // Try to resolve the module using require.resolve + // This follows standard Node.js module resolution algorithm + let resolvedPath; + try { + // First try from current file's directory + resolvedPath = require.resolve(packagePath, { + paths: [currentFileDir] + }); + } catch (e) { + // Fallback to current working directory + resolvedPath = require.resolve(packagePath, { + paths: [process.cwd()] + }); + } + + // Convert to relative path for the original plugin + const relativePath = path.relative(currentFileDir, resolvedPath); + atRule.params = `"${relativePath}"`; + } catch (error) { + // If resolution fails, try manual node_modules lookup + try { + let currentDir = path.dirname(atRule.source.input.file || process.cwd()); + let found = false; + + // Walk up directories to find node_modules + while (currentDir !== path.parse(currentDir).root && !found) { + const moduleDir = path.join(currentDir, 'node_modules', packagePath); + if (fs.existsSync(moduleDir)) { + const relativePath = path.relative( + path.dirname(atRule.source.input.file || ''), + moduleDir + ); + atRule.params = `"${relativePath}"`; + found = true; + break; + } + currentDir = path.dirname(currentDir); + } + + if (!found) { + console.warn(`Could not resolve module path: ${packagePath}`); + } + } catch (fallbackError) { + console.warn(`Failed to resolve ${packagePath}:`, fallbackError.message); + } + } + } + }); + } + }; +} + +// The instantiated PostCSS plugin chain (order matters). +function getPostCssPlugins ({useTailwind}) { + return [ + useTailwind && loadPostCss('tailwindcss'), + // Fix and adjust for known flexbox issues. See https://github.com/philipwalton/flexbugs + loadPostCss('postcss-flexbugs-fixes'), + // Transpile stage-3 CSS standards based on browserslist targets, with auto-prefixing. + loadPostCss('postcss-preset-env', { + autoprefixer: {flexbox: 'no-2009', remove: false}, + stage: 3, + features: {'custom-properties': false} + }), + // Standardize browser quirks based on the browserslist targets. + !useTailwind && loadPostCss('postcss-normalize'), + // Resolution independence support. + app.ri !== false && loadPostCss('postcss-resolution-independence', app.ri), + // Resolve `~pkg` prefixes in `@import-json` rules before the import-json plugin. + tildeJsonImportPlugin(), + // Support importing JSON files in CSS (design tokens -> CSS custom properties). + loadPostCss('@daltontan/postcss-import-json', { + map: (selector, value) => { + if (typeof value === 'object' && value !== null && value.$ref) { + const tokenPath = value.$ref.split('#/')[1]; + return `var(--${tokenPath.replace(/\//g, '-')})`; + } + return value; + } + }) + ].filter(Boolean); +} + +module.exports = {getPostCssPlugins, tildeJsonImportPlugin, loadPostCss}; diff --git a/config/vite.config.js b/config/vite.config.js index 182e6cdb..60142ad4 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -34,84 +34,8 @@ const { ViteWebOSMetaPlugin } = require('@enact/dev-utils'); -// PostCSS plugin: resolve `~pkg` prefixes in `@import-json` at-rules to a path -// relative to the source file, so `@daltontan/postcss-import-json` can load them. -// Ported from the inline plugin in webpack.config.js (mimics webpack's `~` alias). -function tildeJsonImportPlugin () { - return { - postcssPlugin: 'postcss-import-json-tilde', - Once (root) { - root.walkAtRules('import-json', atRule => { - const src = atRule.params.slice(1, -1); // strip quotes - if (!src.startsWith('~')) return; - const packagePath = src.substring(1); - const currentFileDir = path.dirname((atRule.source.input && atRule.source.input.file) || ''); - try { - let resolvedPath; - try { - resolvedPath = require.resolve(packagePath, {paths: [currentFileDir]}); - } catch (e) { - resolvedPath = require.resolve(packagePath, {paths: [process.cwd()]}); - } - atRule.params = `"${path.relative(currentFileDir, resolvedPath)}"`; - } catch (error) { - // Fallback: walk up directories looking for the module under node_modules. - let dir = currentFileDir || process.cwd(); - while (dir !== path.parse(dir).root) { - const candidate = path.join(dir, 'node_modules', packagePath); - if (fs.existsSync(candidate)) { - atRule.params = `"${path.relative(currentFileDir, candidate)}"`; - return; - } - dir = path.dirname(dir); - } - } - }); - } - }; -} - -// Load and initialize a PostCSS plugin. Unlike postcss-loader (which resolves -// string plugin names), Vite's `css.postcss.plugins` expects instantiated -// plugins, so we require each and invoke the creator with its options. -function loadPostCss (name, opts) { - const mod = require(name); - const creator = mod && mod.default ? mod.default : mod; - return typeof creator === 'function' ? creator(opts) : creator; -} - -// Reuse the same PostCSS plugin chain as the webpack build so CSS output is -// equivalent (autoprefixing, resolution independence, JSON token imports). -function getPostCssPlugins ({useTailwind}) { - return [ - useTailwind && loadPostCss('tailwindcss'), - // Fix and adjust for known flexbox issues. See https://github.com/philipwalton/flexbugs - loadPostCss('postcss-flexbugs-fixes'), - // Transpile stage-3 CSS standards based on browserslist targets, with auto-prefixing. - loadPostCss('postcss-preset-env', { - autoprefixer: {flexbox: 'no-2009', remove: false}, - stage: 3, - features: {'custom-properties': false} - }), - // Standardize browser quirks based on the browserslist targets. - !useTailwind && loadPostCss('postcss-normalize'), - // Resolution independence support. - app.ri !== false && loadPostCss('postcss-resolution-independence', app.ri), - // Resolve `~pkg` prefixes in `@import-json` rules to real paths before the - // import-json plugin runs (ported from the webpack config's inline plugin). - tildeJsonImportPlugin(), - // Support importing JSON files in CSS (design tokens -> CSS custom properties). - loadPostCss('@daltontan/postcss-import-json', { - map: (selector, value) => { - if (typeof value === 'object' && value !== null && value.$ref) { - const tokenPath = value.$ref.split('#/')[1]; - return `var(--${tokenPath.replace(/\//g, '-')})`; - } - return value; - } - }) - ].filter(Boolean); -} +// PostCSS plugin chain, shared with webpack.config.js (single source of truth). +const {getPostCssPlugins} = require('./postcss-plugins'); // Vite plugin that runs ESLint against the app sources, mirroring the webpack // build's `eslint-webpack-plugin` (same flat config in eslintWebpackPluginConfig). diff --git a/config/webpack.config.js b/config/webpack.config.js index 54be65f8..5016e0f2 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -38,6 +38,7 @@ const { WebOSMetaPlugin } = require('@enact/dev-utils'); const createEnvironmentHash = require('./createEnvironmentHash'); +const {getPostCssPlugins} = require('./postcss-plugins'); // This is the production and development configuration. // It is focused on developer experience, fast rebuilds, and a minimal bundle. @@ -125,122 +126,7 @@ module.exports = function ( // Necessary for external CSS imports to work // https://github.com/facebook/create-react-app/issues/2677 ident: 'postcss', - plugins: [ - useTailwind && 'tailwindcss', - // Fix and adjust for known flexbox issues - // See https://github.com/philipwalton/flexbugs - 'postcss-flexbugs-fixes', - // Transpile stage-3 CSS standards based on browserslist targets. - // See https://preset-env.cssdb.org/features for supported features. - // Includes support for targetted auto-prefixing. - [ - 'postcss-preset-env', - { - autoprefixer: { - flexbox: 'no-2009', - remove: false - }, - stage: 3, - features: {'custom-properties': false} - } - ], - // Adds PostCSS Normalize to standardize browser quirks based on - // the browserslist targets. - !useTailwind && require('postcss-normalize'), - // Resolution indepedence support - app.ri !== false && require('postcss-resolution-independence')(app.ri), - // Support importing JSON files with ~ alias - custom plugin (must run first) - { - postcssPlugin: 'postcss-import-json-tilde', - Once (root) { - // Process all @import-json rules with ~ prefix first, before other plugins - root.walkAtRules('import-json', atRule => { - let src = atRule.params.slice(1, -1); // Remove quotes - - // Only handle ~ alias paths - if (src.startsWith('~')) { - const packagePath = src.substring(1); // Remove ~ - - try { - // Use Node.js standard module resolution - // This mimics webpack's ~ alias behavior - const currentFileDir = path.dirname(atRule.source.input.file || ''); - - // Try to resolve the module using require.resolve - // This follows standard Node.js module resolution algorithm - let resolvedPath; - try { - // First try from current file's directory - resolvedPath = require.resolve(packagePath, { - paths: [currentFileDir] - }); - } catch (e) { - // Fallback to current working directory - resolvedPath = require.resolve(packagePath, { - paths: [process.cwd()] - }); - } - - // Convert to relative path for the original plugin - const relativePath = path.relative(currentFileDir, resolvedPath); - atRule.params = `"${relativePath}"`; - } catch (error) { - // If resolution fails, try manual node_modules lookup - try { - let currentDir = path.dirname( - atRule.source.input.file || process.cwd() - ); - let found = false; - - // Walk up directories to find node_modules - while (currentDir !== path.parse(currentDir).root && !found) { - const moduleDir = path.join( - currentDir, - 'node_modules', - packagePath - ); - if (fs.existsSync(moduleDir)) { - const relativePath = path.relative( - path.dirname(atRule.source.input.file || ''), - moduleDir - ); - atRule.params = `"${relativePath}"`; - found = true; - break; - } - currentDir = path.dirname(currentDir); - } - - if (!found) { - console.warn(`Could not resolve module path: ${packagePath}`); - } - } catch (fallbackError) { - console.warn( - `Failed to resolve ${packagePath}:`, - fallbackError.message - ); - } - } - } - }); - } - }, - // Support importing JSON files in CSS - original plugin (for non-~ paths) - [ - '@daltontan/postcss-import-json', - { - map: (selector, value) => { - if (typeof value === 'object' && value !== null && value.$ref) { - const tokenPath = value.$ref.split('#/')[1]; - const cssVariableName = '--' + tokenPath.replace(/\//g, '-'); - - return `var(${cssVariableName})`; - } - return value; - } - } - ] - ].filter(Boolean) + plugins: getPostCssPlugins({useTailwind}) }, sourceMap: shouldUseSourceMap } diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 6382fa1f..f01bd019 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -131,10 +131,11 @@ non-obvious part of the port — were: | **ESLint** (`eslint-config-enact`, `--no-linting`) | `eslint-webpack-plugin` | inline `enact-eslint` plugin — lints at build start; errors fail the build, dev warns | | Source maps | `devtool` | `build.sourcemap` / `css.devSourcemap` | -## What does NOT port yet (webpack-only, blocks a full swap) +## Webpack-only concerns and their status -These live in `@enact/dev-utils/plugins` and tap the webpack `compiler`/`compilation` -lifecycle. Each needs a bespoke Vite/Rollup plugin: +Items 1–6 are `@enact/dev-utils` webpack plugins that tap the +`compiler`/`compilation` lifecycle; items 7–9 are webpack loader/config behaviors, +not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): 1. ~~**`ILibPlugin`**~~ — **ported** as [`ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin). Since `@enact/i18n`'s @@ -182,14 +183,18 @@ lifecycle. Each needs a bespoke Vite/Rollup plugin: project, not validated here. 6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not needed under Vite (different FS handling). *Drop.* -7. **`node-polyfill-webpack-plugin`** — Node builtin polyfills for screenshot - tests. Vite: use `vite-plugin-node-polyfills` if required. +7. **`node-polyfill-webpack-plugin`** — supplies Node builtins (`global`, + `process`, `Buffer`) in the browser bundle. **Partly resolved:** the runtime + `global` reference in Enact's `polyfills.js` is handled (runtime fixes R1/R2 — + `ViteHtmlPlugin` shim + core-js ESM entry), and `process.env.NODE_ENV` is + covered by `define`. Fuller coverage (`Buffer`, full `process`), mostly for + screenshot tests, is **not** wired — add `vite-plugin-node-polyfills` if needed. 8. **`icss` mode for non-`*.module` CSS / `forceCSSModules`** — Vite auto-treats only `*.module.*` as modules; the webpack `mode: 'icss'` nuance and the global - `forceCSSModules` toggle need a custom transform. -9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (fixes #7 and #8 above): - `lessTildeImportPlugin` (LESS), `resolve.alias /^~/` (CSS), and - `tildeJsonImportPlugin` (`@import-json`). + `forceCSSModules` toggle need a custom transform. *Not ported.* +9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (by config fixes #7 and #8 in + the config-issues list above): `lessTildeImportPlugin` (LESS), + `resolve.alias /^~/` (CSS), and `tildeJsonImportPlugin` (`@import-json`). ## Command wiring (applied, behind a flag) From cc3a593985213094866b71750330505c1ba43d1b Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Thu, 9 Jul 2026 17:58:25 +0300 Subject: [PATCH 03/20] added vite bundler --- commands/serve.js | 6 ++++- config/vite.config.js | 56 ++++++++++++++++++++++++++++++++++++++++-- docs/vite-migration.md | 43 +++++++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/commands/serve.js b/commands/serve.js index 37e75ce0..abb69d94 100755 --- a/commands/serve.js +++ b/commands/serve.js @@ -275,11 +275,15 @@ async function viteServe (opts, host, port) { const server = await createServer(config); await server.listen(); + // Vite falls back to the next free port when the requested one is taken + const address = server.httpServer && server.httpServer.address(); + const actualPort = (address && typeof address === 'object' && address.port) || port; + // Append the same "ready" message the webpack path shows const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; // Strip a trailing slash from the base so root URLs read `http://host:port` // (matching the webpack path, which passes `publicPath.slice(0, -1)`). - const urls = prepareUrls(protocol, host, port, (app.publicUrl || '/').replace(/\/$/, '')); + const urls = prepareUrls(protocol, host, actualPort, (app.publicUrl || '/').replace(/\/$/, '')); console.log(chalk.green('Compiled successfully!')); console.log(); console.log(`You can now view ${chalk.bold(app.name)} in the browser.`); diff --git a/config/vite.config.js b/config/vite.config.js index 60142ad4..caed87ca 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -77,6 +77,26 @@ function enactEslintPlugin () { }; } +// Vite plugin that neutralizes webpack's HMR API in app source. Some Enact apps +// guard reducer/hot-reload code with `if (module.hot) { module.hot.accept(…) }`. +// `module` exists in the webpack runtime but not in Vite's browser ESM (app source +// isn't CJS-wrapped like pre-bundled deps), so it throws `module is not defined`. +// Vite's `define` can't replace `module.hot` (esbuild treats `module` specially), +// so rewrite it to `false` here — the webpack-only block is skipped and Vite's own +// HMR (import.meta.hot) still applies to the module graph. +function enactNeutralizeWebpackHmrPlugin () { + return { + name: 'enact-neutralize-webpack-hmr', + enforce: 'pre', + transform (code, id) { + const file = id.split('?')[0]; + if (file.includes('/node_modules/') || !/\.(?:jsx?|tsx?|mjs)$/.test(file)) return null; + if (!code.includes('module.hot')) return null; + return {code: code.replace(/\bmodule\.hot\b/g, 'false'), map: null}; + } + }; +} + // Non-browser iLib platform loaders (`./lib/ilib-qt|rhino|ringo|node|….js`) and // their `*Loader.js` helpers. iLib selects these via runtime platform detection; // the browser branch never reaches them, but bundlers try (and fail) to resolve @@ -221,6 +241,19 @@ module.exports = function ( const entry = createCombinedEntry(app.context, ['core-js/stable', appEntry]); const coreJsDir = path.dirname(require.resolve('core-js/package.json')); + // Enumerate the `@enact/*` packages installed in the app so they can be deduped + // (Vite `resolve.dedupe` takes exact names, not globs). Apps like the aggregate + // `all-samples` import source from many sibling packages, each with its own + // node_modules and thus its own copy of every `@enact/*` dep — deduping collapses + // them to one copy, cutting duplicate dependency optimization and bundle bloat. + const enactDir = path.join(app.context, 'node_modules', '@enact'); + const enactPackages = fs.existsSync(enactDir) ? + fs + .readdirSync(enactDir) + .filter(name => !name.startsWith('.') && fs.statSync(path.join(enactDir, name)).isDirectory()) + .map(name => '@enact/' + name) : + []; + const postcssPlugins = getPostCssPlugins({useTailwind}); // Backward-compatibility ilib alias, matching webpack.config.js. @@ -269,7 +302,7 @@ module.exports = function ( // components across them triggers "Invalid hook call / more than one copy // of React". Webpack avoids this via single-tree resolution + exposing // React on global in the isomorphic path. - dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'], + dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime', ...enactPackages], // Don't follow symlinks to their real paths (matches webpack `symlinks: false`). preserveSymlinks: true }, @@ -332,6 +365,14 @@ module.exports = function ( legalComments: 'none' }, optimizeDeps: { + // Enact apps ship no `index.html`, so Vite's dependency scanner has no + // default entry to crawl and would otherwise discover every dependency + // lazily on first request — each new one triggering a re-optimize + + // full page reload. That churns badly for apps that import source from + // sibling packages (e.g. `all-samples`). Point the scanner at the app + // entry so it crawls the whole import graph (including cross-package + // imports) and pre-bundles everything in one pass. + entries: [path.relative(app.context, appEntry).replace(/\\/g, '/')], // The dev-server dependency scanner/optimizer is esbuild-based and defaults // the `.js` loader to `js`; Enact authors JSX inside plain `.js` files, which // breaks the scan without this. (Request-time transforms go through @@ -345,9 +386,20 @@ module.exports = function ( server: { host: process.env.HOST || '0.0.0.0', port: parseInt(process.env.PORT || 8080), - hmr: true + hmr: true, + fs: { + // Enact apps can import source/assets from sibling package directories + // outside the app root (e.g. the aggregate `all-samples` pulls views and + // fonts from neighbouring sample packages). Vite's default fs allow-list + // (the workspace root) blocks those with "outside of Vite serving allow + // list". Disable the restriction so the dev server serves any imported + // file, matching webpack-dev-server's behaviour. + strict: false + } }, plugins: [ + // Rewrite webpack's `module.hot` in app source before other transforms. + enactNeutralizeWebpackHmrPlugin(), react({ // @enact/* packages ship raw source (JSX inside .js, ESM) rather than // pre-compiled output, so they must be transpiled like app code. Mirror diff --git a/docs/vite-migration.md b/docs/vite-migration.md index f01bd019..60e455c0 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -41,13 +41,15 @@ more representative case): copied/served); **locale filtering** `-l en-US,ko-KR` trims 70 MB → 19 MB (6,755 → 1,988 files, correct locales); **webOS meta** `appinfo.json` + icons emitted/served; **ESLint** lints the sources (clean) and enforces rules. -- **Real-browser render** verified on `qa-dropdown` (nested-`@enact` app): the - app mounts and renders correctly (see runtime fixes below). Note: HTTP-200 / - transform checks do **not** execute the page JS — always load a real browser. +- **Real-browser render** verified on several apps — `qa-dropdown` (nested + `@enact`), the redux sample (webpack HMR API), and the aggregate `all-samples` + (imports source from 15 sibling packages): each mounts and renders correctly + (see runtime fixes R1–R8 below). Note: HTTP-200 / transform checks do **not** + execute the page JS — always load a real browser. -Eight config issues plus three runtime issues were found and fixed while -validating. The runtime ones (only visible when the page actually executes in a -browser) were: +Eight config issues plus eight runtime issues were found and fixed while +validating. The runtime ones (mostly only visible when the page actually executes +in a real browser — HTTP-200/transform checks don't run the page JS) were: - **R1 — `global` is not defined.** Enact's `polyfills.js`/core-js reference the Node `global`, absent in the browser (webpack supplied it via @@ -62,6 +64,35 @@ browser) were: - **R3 — multiple copies of React** ("Invalid hook call"). Nested `@enact` deps (`@enact/limestone/node_modules/@enact/*`) + Vite pre-bundling resolved several physical `react` copies. Fix: `resolve.dedupe: ['react','react-dom','react/jsx-runtime','react/jsx-dev-runtime']`. +- **R4 — webpack's HMR API in app source** (`module is not defined`). Some apps + guard reducer hot-reload with `if (module.hot) { module.hot.accept(…) }`; + `module` exists in the webpack runtime but not in Vite's browser ESM (app source + isn't CJS-wrapped like pre-bundled deps). Vite's `define` can't replace it + (esbuild treats `module` specially), so a `transform` plugin + (`enact-neutralize-webpack-hmr`) rewrites `module.hot` → `false` in app source; + the webpack-only block is dead-stripped. (Surfaced on the redux sample.) +- **R5 — Vite fs allow-list.** Apps that import source/assets from **sibling** + packages (e.g. the aggregate `all-samples`) are blocked by Vite's `server.fs` + allow-list ("outside of Vite serving allow list"). Fix: `server.fs.strict = + false`, matching webpack-dev-server's unrestricted file serving. +- **R6 — dependency-scan churn / repeated reloads.** Enact apps ship no + `index.html`, so Vite's dep scanner had no entry to crawl and discovered deps + lazily on first request — each new one triggering a re-optimize + full page + reload (severe for apps importing from many packages). Fix: + `optimizeDeps.entries = [<app entry>]` so the scanner crawls the whole import + graph (incl. cross-package imports) and pre-bundles in one pass. +- **R7 — theme i18n resource 404s.** `ViteILibPlugin` set `ILIB_<THEME>_PATH` + (used by the theme's `$L`/`ResBundle` as `basePath`) to the theme **package + root** instead of its `resources/` dir, so the loader fetched + `…/ilibmanifest.json` (404) and then blindly requested the default string paths + (`strings.json`, `en/strings.json`, … — all 404). Fix: point the constant at the + served data dir; the iLib **base** still points at the package dir (its loader + appends `locale/` itself). In `dev-utils/plugins/ViteILibPlugin`. +- **R8 — duplicate `@enact` copies.** Apps that aggregate independently-installed + sibling packages resolve a separate physical copy of every `@enact/*` dep, + multiplying dep-optimization + bundle size (and risking multiple-instance bugs, + the `@enact` analogue of R3). Fix: extend `resolve.dedupe` to the app's installed + `@enact/*` packages (collapsed e.g. 15 copies → 1 on `all-samples`). The eight config issues (all in `config/vite.config.js` unless noted) — the non-obvious part of the port — were: From c77547db959c097b8d5b0408a5db5791b46d615f Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Fri, 10 Jul 2026 10:07:40 +0300 Subject: [PATCH 04/20] added vite bundler --- commands/pack.js | 5 +++-- commands/serve.js | 5 +++-- commands/vite-utils.js | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index 80f454f0..1d221a08 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -20,7 +20,8 @@ const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const printBuildError = require('react-dev-utils/printBuildError'); const webpack = require('webpack'); const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils'); -const {useVite} = require('./vite-utils'); + +const {isViteBundler} = require('./vite-utils'); let chalk; let stripAnsi; @@ -309,7 +310,7 @@ function api (opts = {}) { } // Experimental Vite bundler path (opt-in via `--vite` or ENACT_BUNDLER=vite). - if (useVite(opts)) { + if (isViteBundler(opts)) { process.env.NODE_ENV = opts.production ? 'production' : 'development'; return viteBuild(opts); } diff --git a/commands/serve.js b/commands/serve.js index abb69d94..7dde1352 100755 --- a/commands/serve.js +++ b/commands/serve.js @@ -26,7 +26,8 @@ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin' const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); const {optionParser: app} = require('@enact/dev-utils'); -const {useVite} = require('./vite-utils'); + +const {isViteBundler} = require('./vite-utils'); let chalk; @@ -395,7 +396,7 @@ function api (opts) { } // Experimental Vite bundler path (opt-in via `--vite` or ENACT_BUNDLER=vite). - if (useVite(opts)) { + if (isViteBundler(opts)) { process.env.NODE_ENV = 'development'; return viteServe(opts, host, port); } diff --git a/commands/vite-utils.js b/commands/vite-utils.js index 848fff4b..7a9eb313 100644 --- a/commands/vite-utils.js +++ b/commands/vite-utils.js @@ -5,8 +5,8 @@ */ // Whether the experimental Vite bundler is selected (via `--vite` or ENACT_BUNDLER=vite). -function useVite (opts) { +function isViteBundler (opts) { return Boolean(opts.vite) || /^vite$/i.test(process.env.ENACT_BUNDLER || ''); } -module.exports = {useVite}; +module.exports = {isViteBundler}; From 7e14bc09abc0aa96db45660884de565ec24c7bae Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Fri, 10 Jul 2026 14:44:21 +0300 Subject: [PATCH 05/20] migrated eject command --- commands/eject.js | 68 +++++++++--- docs/vite-eject-testing.md | 211 +++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 13 deletions(-) create mode 100644 docs/vite-eject-testing.md diff --git a/commands/eject.js b/commands/eject.js index 10734c20..17e58865 100755 --- a/commands/eject.js +++ b/commands/eject.js @@ -50,6 +50,32 @@ const bareTasks = { test: 'jest --config config/jest/jest.config.js', 'test-watch': 'jest --config config/jest/jest.config.js --watch' }; +// Vite variants of the barebones setup (used with `--vite`). Vite copies `public/` +// automatically (no `cpy` step) and loads the generated root `vite.config.mjs`. +// Reuse the same `rimraf` pin as the webpack bare setup so the version lives in one place. +const bareDepsVite = {rimraf: bareDeps.rimraf}; +const bareTasksVite = { + serve: 'vite', + pack: 'vite build --mode development', + 'pack-p': 'vite build', + watch: 'vite build --watch --mode development', + clean: 'rimraf build dist', + lint: bareTasks.lint, + license: bareTasks.license, + test: bareTasks.test, + 'test-watch': bareTasks['test-watch'] +}; +// Bundler-driven scripts that understand `--vite`; used to steer a non-bare Vite eject. +const VITE_CAPABLE_SCRIPTS = ['serve', 'pack']; +// The Enact Vite config (config/vite.config.js) is a factory `(mode) => InlineConfig`, +// not the object/`{command, mode}` shape Vite's CLI expects. A bare Vite eject writes +// this thin root config to adapt it so `vite` / `vite build` work directly. +const VITE_ROOT_CONFIG = + "import {createRequire} from 'module';\n" + + "const require = createRequire(import.meta.url);\n" + + '// config/vite.config.js exports `(mode) => InlineConfig`; Vite calls with {command, mode}.\n' + + "const enactViteConfig = require('./config/vite.config.js');\n" + + "export default ({mode}) => enactViteConfig(mode || 'production');\n"; function displayHelp () { let e = 'node ' + path.relative(process.cwd(), __filename); @@ -62,6 +88,9 @@ function displayHelp () { console.log(' -b, --bare Abandon Enact CLI command enhancements'); console.log(' and eject into a a barebones setup (using'); console.log(' webpack, eslint, karma, etc. directly)'); + console.log(' --vite [Experimental] Use Vite instead of webpack:'); + console.log(' alone, points the serve/pack scripts at the'); + console.log(' Vite path; with --bare, emits a bare Vite setup'); console.log(' -v, --version Display version information'); console.log(' -h, --help Display help information'); console.log(); @@ -154,12 +183,15 @@ function copySanitizedFile ({src, dest}) { fs.writeFileSync(dest, data, {encoding: 'utf8'}); } -function configurePackage (bare) { +function configurePackage (bare, vite) { const own = require('../package.json'); const app = require(path.resolve('package.json')); const backup = JSON.stringify(app, null, 2) + os.EOL; const availScripts = fs.existsSync('./scripts') ? fs.readdirSync('./scripts').map(f => f.replace(/\.js$/, '')) : []; const enactCLI = new RegExp('enact (' + availScripts.join('|') + ')', 'g'); + // Select the webpack or Vite flavor of the barebones tasks/deps. + const tasks = vite ? bareTasksVite : bareTasks; + const deps = vite ? bareDepsVite : bareDeps; const eslintConfig = {extends: 'enact'}; const eslintIgnore = ['build/*', 'config/*', 'dist/*', 'node_modules/*', 'scripts/*']; const conflicts = []; @@ -182,9 +214,9 @@ function configurePackage (bare) { // Add any additional dependencies if (bare) { - Object.keys(bareDeps).forEach(key => { + Object.keys(deps).forEach(key => { console.log(` Adding ${chalk.cyan(key)} to devDependencies`); - app.devDependencies[key] = bareDeps[key]; + app.devDependencies[key] = deps[key]; }); } @@ -193,16 +225,20 @@ function configurePackage (bare) { // Update NPM task scripts const type = chalk.cyan('npm script'); Object.keys(app.scripts).forEach(key => { - if (bare && bareTasks[key]) { + if (bare && tasks[key]) { if (!conflicts.includes(type)) conflicts.push(type); - const bin = bareTasks[key].match(/^(?:node\s+)*(\S*)/); - const updated = (bin && bin[1]) || bareTasks[key]; + const bin = tasks[key].match(/^(?:node\s+)*(\S*)/); + const updated = (bin && bin[1]) || tasks[key]; console.log(` Updating npm task ${chalk.cyan(key)} to use ${chalk.cyan(updated)}`); - app.scripts[key] = bareTasks[key]; + app.scripts[key] = tasks[key]; } else if (!bare) { app.scripts[key] = app.scripts[key].replace(enactCLI, (match, name) => { - console.log(` Updating npm task ${chalk.cyan(key)} to use ` + chalk.cyan(`scripts/${name}.js`)); - return `node ./scripts/${name}.js`; + // In a non-bare Vite eject, steer the bundler-driven scripts down the + // Vite path so `npm run serve`/`pack` use Vite, not webpack. Only the + // commands that understand the flag (serve, pack) get it. + const viteFlag = vite && VITE_CAPABLE_SCRIPTS.includes(name) ? ' --vite' : ''; + console.log(` Updating npm task ${chalk.cyan(key)} to use ` + chalk.cyan(`scripts/${name}.js${viteFlag}`)); + return `node ./scripts/${name}.js${viteFlag}`; }); } }); @@ -259,7 +295,7 @@ function npmInstall () { }); } -function api ({bare = false} = {}) { +function api ({bare = false, vite = false} = {}) { if (bare) { assets.pop(); } @@ -270,9 +306,15 @@ function api ({bare = false} = {}) { console.log(chalk.cyan(`Copying files into ${process.cwd()}`)); assets.forEach(dir => !fs.existsSync(dir.dest) && fs.mkdirSync(dir.dest, {recursive: true})); files.forEach(copySanitizedFile); + // A bare Vite eject drives the Vite CLI directly, which loads a root + // config; write the adapter that wires it to config/vite.config.js. + if (bare && vite) { + console.log(` Adding ${chalk.cyan('vite.config.mjs')} to the project`); + fs.writeFileSync('vite.config.mjs', VITE_ROOT_CONFIG, {encoding: 'utf8'}); + } console.log(); console.log(chalk.cyan('Configuring package.json')); - const con = configurePackage(bare); + const con = configurePackage(bare, vite); console.log(); console.log(chalk.cyan('Running npm install...')); return npmInstall().then(() => { @@ -298,7 +340,7 @@ function api ({bare = false} = {}) { function cli (args) { const opts = minimist(args, { - boolean: ['bare', 'help'], + boolean: ['bare', 'vite', 'help'], alias: {b: 'bare', h: 'help'} }); if (opts.help) displayHelp(); @@ -307,7 +349,7 @@ function cli (args) { import('chalk').then(({default: _chalk}) => { chalk = _chalk; - api({bare: opts.bare}).catch(err => { + api({bare: opts.bare, vite: opts.vite}).catch(err => { console.error(chalk.red('ERROR: ') + err.message); process.exit(1); }); diff --git a/docs/vite-eject-testing.md b/docs/vite-eject-testing.md new file mode 100644 index 00000000..bf8b5e88 --- /dev/null +++ b/docs/vite-eject-testing.md @@ -0,0 +1,211 @@ +# Testing `enact eject` with Vite + +This guide walks through validating the Vite support that was added to the `eject` +command. It is written as a **manual** procedure because `eject` is destructive: it +rewrites the app's `package.json`, requires a clean git working tree, copies files +into the app, and runs `npm install`. Do not run it against an app you care about — +always use a throwaway copy in a fresh git repo. + +## What changed + +`eject` copies the CLI's `config/` (now including `vite.config.js`, `postcss-plugins.js`) +and the `commands/` (as `scripts/`, including the `--vite`-capable `pack.js`/`serve.js`). +Two eject modes exist: + +- **Default eject** (`enact eject`) — keeps the Enact scripts and rewrites the app's + npm tasks from `enact <cmd>` to `node ./scripts/<cmd>.js`, preserving any flags. Adding + `--vite` here **appends `--vite`** to the bundler-driven scripts (`serve`, `pack`, + `pack-p`, `watch`) so they run the Vite path; `clean`/`lint`/`test` are left untouched. + (Flags already present on a script — e.g. `enact serve --vite` — are preserved regardless.) +- **Bare eject** (`enact eject --bare`) — abandons the Enact scripts and rewrites the + npm tasks to invoke the underlying tools directly. This was **webpack-only**. Adding + `--vite` makes it emit a **Vite** barebones setup instead. + +New pieces in [commands/eject.js](../commands/eject.js): + +| Piece | Purpose | +|---|---| +| `--vite` flag | Non-bare: appends `--vite` to the `serve`/`pack` scripts. Bare: selects the Vite flavor of the barebones setup. | +| `VITE_CAPABLE_SCRIPTS` | The scripts that understand `--vite` (`serve`, `pack`) — only these get the flag in a non-bare Vite eject. | +| `bareTasksVite` | Vite npm scripts: `serve → vite`, `pack → vite build --mode development`, `pack-p → vite build`, `watch → vite build --watch --mode development`. | +| `bareDepsVite` | Bare Vite deps: just `rimraf` (Vite copies `public/` automatically, so no `cpy-cli`). | +| `VITE_ROOT_CONFIG` → `vite.config.mjs` | A root config the Vite CLI auto-loads. The Enact config `config/vite.config.js` is a factory `(mode) => InlineConfig`; this adapter maps Vite's `{command, mode}` call onto it. | + +## Prerequisites + +- A local build of this CLI (so `enact` resolves to your working tree). Either: + - Run via the repo's bin: `node <repo>/cli/bin/enact.js <cmd>`, or + - `npm link` the CLI once: `cd <repo>/cli && npm link`, then `enact` is on PATH. +- `git` available (eject aborts if the working tree is dirty). +- Node + npm able to reach your registry (eject runs `npm install`). + +Throughout, `enact` means "the CLI under test". Substitute `node <repo>/cli/bin/enact.js` +if you did not link. + +### Important: the ejected app needs a Vite-capable `@enact/dev-utils` + +An ejected app is standalone — its `config/vite.config.js` does +`require('@enact/dev-utils')` and resolves it from the **app's** `node_modules`, not the +CLI's. The Vite plugins (`ViteHtmlPlugin`, `ViteILibPlugin`, `ViteWebOSMetaPlugin`) live in +the **working-tree** `dev-utils` and are **not yet in any published `@enact/dev-utils`**. +So a freshly-ejected app that installed `@enact/dev-utils` from npm will fail with +`ViteHtmlPlugin is not a function`. + +Until a dev-utils with the Vite plugins is published, point the ejected app at the +working-tree copy. On Windows (junction; reversible — a later `npm install` restores the +published copy): + +```powershell +$link = "<app>\node_modules\@enact\dev-utils" +$target = "<repo>\dev-utils" +if (Test-Path $link) { (Get-Item $link).Delete() } +New-Item -ItemType Junction -Path $link -Target $target +``` + +On macOS/Linux: `rm -rf <app>/node_modules/@enact/dev-utils && ln -s <repo>/dev-utils <app>/node_modules/@enact/dev-utils` +(or `npm link @enact/dev-utils` after `npm link` in the working-tree `dev-utils`). + +## Set up a throwaway test app + +Use a **limestone** sample (per project convention, checkups run against limestone, not +sandstone). Copy it out of the monorepo so the eject can't touch tracked files, and give +it its own git repo: + +```bash +# from the repo root (…/LGE) +cp -r limestone/samples/qa-dropdown /tmp/eject-test +cd /tmp/eject-test + +# install its deps so it's runnable before ejecting +npm install + +# eject requires a clean git tree +git init -q && git add -A && git commit -qm "baseline before eject" +``` + +Sanity-check the app runs on Vite **before** ejecting (this is what eject must preserve): + +```bash +enact serve --vite # open the printed URL, confirm the app renders, then Ctrl-C +enact pack --vite -p # confirm ./dist is produced +git checkout -- . && git clean -fdq # discard the build artifacts +``` + +--- + +## Test A — Non-bare Vite eject + +Goal: `enact eject --vite` (no `--bare`) points the copied scripts at the Vite path, so +the app builds/serves with Vite. The app's original scripts can be plain webpack +(`enact serve`) — the flag is added for you. + +1. Eject with `--vite` (answer **yes** at the confirmation prompt): + ```bash + enact eject --vite + ``` + +2. **Verify** — expect: + - A `config/` dir containing **both** `webpack.config.js` **and** `vite.config.js`, + plus `postcss-plugins.js`. + - A `scripts/` dir containing `pack.js`, `serve.js`, `vite-utils.js`. + - `package.json` scripts with `--vite` appended to the bundler scripts: + `"serve": "node ./scripts/serve.js --vite"`, + `"pack": "node ./scripts/pack.js --vite"`, + `"pack-p": "node ./scripts/pack.js --vite -p"`, + `"watch": "node ./scripts/pack.js --vite --watch"`. + `clean`/`lint`/`test` have **no** `--vite`. + +3. **Run it:** + ```bash + npm run serve # → open the URL, confirm the app renders (Vite dev server) + npm run pack-p # → confirm ./dist is produced + ``` + Open the browser console: **no errors**, the sample renders as it did pre-eject. + +4. Reset for the next test: + ```bash + cd /tmp && rm -rf eject-test && cp -r <repo>/limestone/samples/qa-dropdown /tmp/eject-test + cd /tmp/eject-test && npm install && git init -q && git add -A && git commit -qm baseline + ``` + +--- + +## Test B — Bare Vite eject (the new path) + +Goal: `--bare --vite` produces a self-contained Vite setup that runs the Vite CLI directly. + +1. Eject bare + vite (answer **yes** at the prompt): + ```bash + enact eject --bare --vite + ``` + +2. **Verify files:** + - A **`vite.config.mjs`** at the app root containing the `createRequire` adapter that + re-exports `config/vite.config.js` as `({mode}) => enactViteConfig(mode || 'production')`. + - `config/vite.config.js` and `config/postcss-plugins.js` present. + - `package.json`: + - `scripts`: `serve → "vite"`, `pack → "vite build --mode development"`, + `pack-p → "vite build"`, `watch → "vite build --watch --mode development"`, + `clean → "rimraf build dist"`, plus `lint`/`test` unchanged. + - `devDependencies` include `vite`, `@vitejs/plugin-react`, `@enact/dev-utils`, + `babel-preset-enact`, the `postcss-*` packages, and `rimraf`. + - **No** `scripts/` dir and no `enact`/`node ./scripts/...` references (this is bare). + +3. **Run it** (deps were installed by eject; if you edited `package.json` after, `npm install`): + ```bash + npm run serve # → Vite dev server; open URL, confirm render + clean console + npm run pack-p # → vite build (production); confirm ./dist with hashed assets + npm run pack # → vite build --mode development; confirm ./dist (unminified) + npm run watch # → rebuilds on change; edit a source file, confirm rebuild, Ctrl-C + npm run clean # → removes build/ and dist/ + ``` + +4. **What "pass" looks like:** + - `npm run serve` serves the app and the browser renders it with no console errors. + - `npm run pack-p` exits 0 and writes `./dist/index.html` plus hashed JS/CSS. + - `./dist/index.html` references the built assets (open it via a static server, e.g. + `npx serve dist`, and confirm it renders — `file://` won't work due to module paths). + +5. **Known-good caveats to expect (not failures):** + - The app still receives webpack-related devDeps (webpack is a CLI dependency, so the + generic dep-merge copies it). They're unused by the Vite tasks — harmless bloat. + - `vite.config.mjs` uses the factory's **defaults** for locale filtering, content hash, + isomorphic, etc. Bare mode intentionally drops the CLI's flag plumbing; to customize, + edit `vite.config.mjs` to pass extra factory args, e.g. + `enactViteConfig(mode, false, true /* contentHash */, false, false, false, undefined, 'tv')`. + +--- + +## Test C — Regression: bare webpack eject still works + +Confirm the default `--bare` (no `--vite`) is unchanged. + +```bash +# fresh copy as above, then: +enact eject --bare +``` + +**Verify:** `package.json` scripts use webpack directly +(`pack-p → "webpack --env production --config config/webpack.config.js && cpy public dist"`), +`cpy-cli` + `rimraf` are in devDependencies, and **no** `vite.config.mjs` is written. +`npm run pack-p` produces `./dist`. + +--- + +## Cleanup / rollback + +Everything happens in the throwaway copy, so just delete it: + +```bash +cd /tmp && rm -rf eject-test +``` + +If you ever eject in a real repo by mistake, `git reset --hard && git clean -fd` restores +it (eject refuses to run on a dirty tree precisely so this is always possible). + +## Quick checklist + +- [ ] Test A: default eject → `node ./scripts/*.js --vite`, app serves + packs. +- [ ] Test B: `--bare --vite` → `vite.config.mjs` written; `serve`/`pack`/`pack-p`/`watch`/`clean` all work; `./dist` renders. +- [ ] Test C: `--bare` → webpack scripts unchanged, no `vite.config.mjs`. +- [ ] Browser console clean in every serve/build. From a36bd7e9ddb644478e89d4ae5741d7c90b3c6d0d Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Fri, 10 Jul 2026 15:18:40 +0300 Subject: [PATCH 06/20] migrated pack for --no-minify, --verbose, --stats --- commands/pack.js | 18 ++++++++++++++++++ docs/vite-migration.md | 14 ++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index 1d221a08..6a686d2c 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -207,6 +207,8 @@ function printErrorDetails (err, handler) { // reported and then genuinely skipped. In particular `--isomorphic` is forced off // (client render) rather than forwarded, because setting ENACT_PACK_ISOMORPHIC // without prerendered markup would make the app hydrate an empty root. +// +// Wired here (via mixins.applyVite): --no-minify, --verbose, --stats. async function viteBuild (opts) { const {build: viteBuildApi} = require('vite'); @@ -217,6 +219,18 @@ async function viteBuild (opts) { ); } }); + // These only shape the --framework/--externals output, which isn't ported, so + // they're inert on the Vite path; note it rather than fail silently. + ['externals-public', 'externals-polyfill', 'externals-corejs'].forEach(flag => { + if (opts[flag]) { + console.log( + chalk.yellow( + `NOTICE: --${flag} only applies alongside --framework/--externals, which the Vite ` + + 'bundler does not support yet; ignored.' + ) + ); + } + }); const configFactory = require('../config/vite.config'); const config = configFactory( @@ -238,6 +252,10 @@ async function viteBuild (opts) { } // Output override if (opts.output) config.build.outDir = path.resolve(opts.output); + // Apply the build-shaping flags (--no-minify, --verbose, --stats), mirroring the + // webpack path's `mixins.apply`. Runs after the output override so --stats writes + // stats.html into the final outDir. + mixins.applyVite(config, opts); // Watch mode if (opts.watch) { config.build.watch = {}; diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 60e455c0..10645f38 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -234,8 +234,18 @@ opted into via **`--vite`** or **`ENACT_BUNDLER=vite`**; otherwise webpack runs unchanged. Both bundlers coexist during migration. - `enact serve --vite` → `vite.createServer(...).listen()` (native ESM dev server, HMR via `@vitejs/plugin-react`). -- `enact pack --vite` / `enact pack -p --vite` → `vite.build(...)` (supports `--watch`, `-o/--output`, `--content-hash`, `--no-split-css`, `-l/--locales`, `--no-linting`). -- Webpack-only flags on the Vite path (`--isomorphic`, `--snapshot`, framework/externals) print a "not yet supported, ignored" notice. +- `enact pack --vite` / `enact pack -p --vite` → `vite.build(...)` (supports `--watch`, `-o/--output`, `--content-hash`, `--no-split-css`, `-l/--locales`, `--no-linting`, `--entry`). +- Build-shaping flags are wired via **`mixins.applyVite`** (the Vite counterpart to + the webpack `mixins.apply`, in `dev-utils/mixins/vite.js`): + - `--stats` → static bundle-analysis treemap `dist/stats.html` + (`rollup-plugin-visualizer`, mirroring webpack's `webpack-bundle-analyzer`). + - `--verbose` → raises Vite's log level and narrates build phases with a module + count (no percentage — Rollup has no fixed total up front, unlike webpack's `ProgressPlugin`). + - `--no-minify` (private) → Terser with `mangle:false` + beautify, keeping dead-code + removal (mirrors the webpack `unmangled` mixin). Only affects production builds. +- `enact eject --vite` wires the ejected app's scripts to the Vite path (see + [vite-eject-testing.md](vite-eject-testing.md)). +- Webpack-only flags on the Vite path (`--isomorphic`, `--snapshot`, framework/externals) print a "not yet supported, ignored" notice. `--externals-public`/`--externals-polyfill`/`--externals-corejs` only shape that (unported) framework/externals output, so they print an "ignored" notice too. The reusable bundler plugins were **added to `@enact/dev-utils`** — mirroring how the webpack plugins (`ILibPlugin`, `WebOSMetaPlugin`, …) live there — and are From 317dcb2814001ca985dfa0942cd90d445bcfc897 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 13 Jul 2026 12:28:40 +0300 Subject: [PATCH 07/20] migrated pack for --frameowrk --- commands/pack.js | 78 +++++++++++---- docs/vite-framework-externals-spike.md | 125 +++++++++++++++++++++++++ docs/vite-migration.md | 40 +++++--- 3 files changed, 212 insertions(+), 31 deletions(-) create mode 100644 docs/vite-framework-externals-spike.md diff --git a/commands/pack.js b/commands/pack.js index 6a686d2c..aecaa8ce 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -20,6 +20,7 @@ const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const printBuildError = require('react-dev-utils/printBuildError'); const webpack = require('webpack'); const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils'); +const viteFw = require('@enact/dev-utils/mixins/vite-framework'); const {isViteBundler} = require('./vite-utils'); @@ -201,36 +202,53 @@ function printErrorDetails (err, handler) { } +// Build the shared Enact framework bundle (react + ilib + all @enact) as reusable ESM +// addressed by an import map, plus a manifest. Vite counterpart to webpack `pack --framework`. +async function viteFramework (opts) { + const {createRequire} = require('module'); + const {build: viteBuildApi} = require('vite'); + const appRequire = createRequire(path.join(app.context, 'package.json')); + + const specs = viteFw.enumerateSpecifiers(app.context); + const srcDir = path.join(app.context, '.enact-framework-src'); + const {input, names} = viteFw.writeWrappers(specs, srcDir, appRequire); + + const configFactory = require('../config/vite.config'); + const config = configFactory(opts.production ? 'production' : 'development', !opts.linting); + const outDir = opts.output ? path.resolve(opts.output) : path.resolve('./dist'); + viteFw.applyFramework(config, {input, outDir}); + // --no-minify/--verbose/--stats still apply to the framework build. + mixins.applyVite(config, opts); + + console.log(`Creating the Enact framework bundle (${Object.keys(input).length} modules)...`); + await viteBuildApi(config); + const manifest = viteFw.writeManifest(outDir, names); + fs.removeSync(srcDir); + console.log( + chalk.green(`Framework compiled successfully. (${Object.keys(manifest.imports).length} specifiers)`) + ); +} + // Experimental Vite build path. Mirrors the webpack `build`/`watch` behavior but -// drives Vite's JS API. The webpack-only features `--isomorphic` (prerendering), -// `--snapshot`, and `--framework`/`--externals` are not supported here: each is -// reported and then genuinely skipped. In particular `--isomorphic` is forced off -// (client render) rather than forwarded, because setting ENACT_PACK_ISOMORPHIC -// without prerendered markup would make the app hydrate an empty root. +// drives Vite's JS API. `--isomorphic` (prerendering) and `--snapshot` are not ported and +// are reported + skipped; `--isomorphic` is forced off (client render) rather than +// forwarded, because setting ENACT_PACK_ISOMORPHIC without prerendered markup would make +// the app hydrate an empty root. // -// Wired here (via mixins.applyVite): --no-minify, --verbose, --stats. +// Wired here: --framework (shared bundle), --externals (import-map externalization), +// and via mixins.applyVite: --no-minify, --verbose, --stats. async function viteBuild (opts) { const {build: viteBuildApi} = require('vite'); - ['isomorphic', 'snapshot', 'framework', 'externals'].forEach(flag => { + if (opts.framework) return viteFramework(opts); + + ['isomorphic', 'snapshot'].forEach(flag => { if (opts[flag]) { console.log( chalk.yellow(`NOTICE: --${flag} is not yet supported by the Vite bundler and will be ignored.`) ); } }); - // These only shape the --framework/--externals output, which isn't ported, so - // they're inert on the Vite path; note it rather than fail silently. - ['externals-public', 'externals-polyfill', 'externals-corejs'].forEach(flag => { - if (opts[flag]) { - console.log( - chalk.yellow( - `NOTICE: --${flag} only applies alongside --framework/--externals, which the Vite ` + - 'bundler does not support yet; ignored.' - ) - ); - } - }); const configFactory = require('../config/vite.config'); const config = configFactory( @@ -252,6 +270,12 @@ async function viteBuild (opts) { } // Output override if (opts.output) config.build.outDir = path.resolve(opts.output); + + // --externals: externalize the shared framework specifiers out of the app build, + // collecting the ones actually imported so we can build a minimal import map. + const collected = new Set(); + if (opts.externals) viteFw.applyExternals(config, collected); + // Apply the build-shaping flags (--no-minify, --verbose, --stats), mirroring the // webpack path's `mixins.apply`. Runs after the output override so --stats writes // stats.html into the final outDir. @@ -267,6 +291,22 @@ async function viteBuild (opts) { } await viteBuildApi(config); + + // --externals post-step: resolve the collected specifiers against the framework's + // manifest and inject the import map + shared stylesheet into the built index.html. + if (opts.externals && !opts.watch) { + const frameworkPath = path.resolve(opts.externals); + const manifest = viteFw.readManifest(frameworkPath); + let base = opts['externals-public']; + if (!base) { + // No remote public path: serve the framework locally under ./framework. + fs.copySync(frameworkPath, path.join(config.build.outDir, 'framework'), {dereference: true}); + base = './framework'; + } + const n = viteFw.injectHtml(path.join(config.build.outDir, 'index.html'), manifest, collected, base); + console.log(chalk.cyan(`Externalized ${n} framework specifiers via import map (base: ${base}).`)); + } + if (!opts.watch) console.log(chalk.green('Compiled successfully.')); } diff --git a/docs/vite-framework-externals-spike.md b/docs/vite-framework-externals-spike.md new file mode 100644 index 00000000..be4d6722 --- /dev/null +++ b/docs/vite-framework-externals-spike.md @@ -0,0 +1,125 @@ +# Vite `--framework` / `--externals` — spike findings & plan + +**Status: spike complete, mechanism proven in-browser.** This documents what the +webpack feature does, the Vite-native approach, what the spike validated end-to-end, +and the plan to turn it into `enact pack --framework` / `--externals`. + +## What the webpack feature does + +A DLL-style shared bundle so multiple webOS apps share one framework payload on-device. + +- **`pack --framework`** ([dev-utils `mixins/framework.js`](../../dev-utils/mixins/framework.js) + + [`EnactFrameworkPlugin`](../../dev-utils/plugins/dll/EnactFrameworkPlugin.js)) — bundles every + `@enact/**` + all `ilib/**` + `react`/`react-dom`/`react-dom/client`/`react-dom/server` + into a UMD library `enact_framework`, where each module is addressable by a **normalized + ID** (`@enact/ui/Button`, `react`, `ilib`) through a `__webpack_require__` registry. + Emits `enact.js` + `enact.css`. +- **`pack --externals=<path>`** ([`mixins/externals.js`](../../dev-utils/mixins/externals.js) + + [`EnactFrameworkRefPlugin`](../../dev-utils/plugins/dll/EnactFrameworkRefPlugin.js)) — a + `DelegatedEnactFactoryPlugin` rewrites every `@enact`/`react`/`ilib` request into + `enact_framework('<id>')`, and injects `<script src=.../enact.js>` before the app's assets. + +The hard part: webpack maps **deep module IDs** through a runtime registry. Rollup's +`external` works at the *specifier* level and has no such registry. + +## The Vite-native approach: import maps + +The browser-native analog to the DLL registry is an **import map**, which supports +prefix mapping via trailing-slash keys (`"@enact/ui/": "/framework/@enact/ui/"`). + +- **Framework build** = a Vite build that emits the shared deps as reusable ESM files. +- **App build** = the normal app build with those specifiers in + `build.rollupOptions.external`, plus an injected `<script type="importmap">` that maps + each bare specifier to its framework file. + +## What the spike validated (qa-a11y, react/react-dom externalized) + +Two throwaway scripts drove the real CLI vite.config factory unchanged. **Browser-verified** +against `limestone/samples/qa-a11y`: + +1. **Framework bundle builds as reusable ESM with working named exports.** `react.js` loaded + as ESM exposes `useState`/`useEffect`/`createElement`/… (v19.2.6). Two non-obvious fixes + were required and found: + - **Re-export wrapper per specifier**, not the CJS file as the entry directly (a bare CJS + entry just runs `requireReact()` and exports nothing). + - **`preserveEntrySignatures: 'strict'`** — otherwise Rollup tree-shakes the entry's + re-exports off (nothing imports an entry), leaving a bare require call. + - **Enumerate export names at build time** (`Object.keys(require('react'))`) to generate + explicit `export const { … } = __m`. React 19's lazy-CJS pattern defeats Rollup's static + named-export detection, so `export *` yields nothing; enumeration is deterministic and + version-proof (44 names for react, 3 for jsx-runtime, etc.). +2. **Single React instance.** `react.js` and `react-dom-client.js` both import the same + shared `chunk-index.js` (react core) — so react-dom uses the same React. Confirmed in the + network trace (react core chunk fetched once) and, decisively, by the app **rendering with + hooks working** — two React copies would throw "Invalid hook call" and render nothing. +3. **App externalizes + boots against the import map.** The app `main.js` contains bare + `import … from "react"` / `"react-dom"` / `"react-dom/client"` / `"react/jsx-runtime"` + (react excluded from the bundle). The injected import map (first element in `<head>`, + before the module script) resolves them to `./framework/*.js`. The qa-a11y sample rendered + fully, **console clean, no 404s**. + +## Deep-import coverage — proven (extended spike) + +The extended spike (`build-fw-app.mjs`) took the react-only proof to the **full @enact +surface** on qa-a11y, reusing the real CLI vite.config factory for both builds. **Browser- +verified**: all **60** `@enact/*` + react specifiers externalized, app `main.js` shrank to +**236 KB** (from ~1.08 MB), framework = 175 shared ESM chunks + one `enact.css`, app renders +**fully styled** (4808 CSS rules applied), console clean, every framework chunk 200 OK. + +Three non-obvious findings resolved: + +1. **Exact import-map keys, not prefix.** `@enact/limestone/Button` resolves via the + component dir's `package.json` `main` (`Button/Button.js`) — which browser import maps + can't do. So the map needs one **exact** key per specifier. These are collected from the + app build (an `external(id)` function records each externalized specifier); since + externalized modules aren't crawled, the set is bounded (the app's *direct* imports), and + the framework build pulls in all transitive `@enact`/react/ilib internally as shared chunks. +2. **Wrapper entries are mandatory — never point an entry at @enact source directly.** Making + `@enact/i18n/I18nDecorator` an *entry* breaks Vite's CJS interop for @enact's CJS-in-source + (e.g. `i18n/src/zoneinfo.js` → `"default" is not exported`), because @enact is symlinked + monorepo source *outside* `node_modules` (with `preserveSymlinks`), so the commonjs plugin + skips it. Routing every entry through a small re-export **wrapper** keeps the real module + *transitive* (exactly as a normal app build sees it), and the error disappears. Wrapper + shapes: CJS (react/ilib) → enumerate named exports; @enact ESM → `export * from …` + + `export default (__m.default !== undefined ? __m.default : __m)` (safe default fallback). +3. **Single `enact.css`.** Per-chunk CSS can't be loaded via an import map (JS only), so the + framework build sets `build.cssCodeSplit = false` → one `enact.css`, injected as a `<link>` + before the app's own stylesheet. ilib needed no extra work — the factory's existing + `ILIB_LOADER_RE` commonjs-ignore handled it. + +## CLI wiring — implemented (shared-framework model) + +Wired and **browser-validated end-to-end** via the CLI on limestone/qa-a11y +(`enact pack --framework -o framework-dist` then `enact pack --externals=framework-dist`): +the 138-specifier framework + `enact.css` built, the app externalized 60 specifiers, +booted **fully styled** (5032 CSS rules), single React instance, console clean. + +Implementation: + +- **`dev-utils/mixins/vite-framework.js`** — mirrors the webpack DLL `framework`/`externals` + mixins. `enumerateSpecifiers(context)` globs the shared surface (every `@enact/<pkg>` + root + component subpath, plus react family + ilib) independent of any app; + `writeWrappers` emits re-export wrappers; `applyFramework` sets + `preserveEntrySignatures:'strict'` + `cssCodeSplit:false` and drops the HTML/webOS-meta + plugins; `writeManifest`/`readManifest` persist the specifier→file map; `applyExternals` + externalizes + collects; `injectHtml` writes the import map + stylesheet `<link>`. +- **`pack.js`** — `--framework` runs `viteFramework()` (build + manifest); `--externals=<path>` + runs the app build with externalization, then resolves collected specifiers against the + manifest and injects the map (copying the framework under `./framework`, or using + `--externals-public` as the base URL for a remotely-deployed framework). + +### Remaining refinements (non-blocking) + +- **Theme-repo `.` self-inclusion** — webpack's `--framework` in a *theme* repo (limestone) + includes the theme's own components via `libraries.push('.')`. The current enumeration + scans `node_modules/@enact`, so build the framework in a context where all `@enact` + *including the theme* are installed (an app/sample context works; qa-a11y gave 138 + specifiers incl. limestone). Adding theme-self globbing would let it run in the bare theme repo. +- **`--externals-polyfill`** — moving core-js into the framework bundle (vs. the app's + combined entry) is not yet wired. +- **`--snapshot`** interaction — deferred until `--snapshot` itself is ported. + +## Risk assessment + +**All technical risk is retired and the feature is implemented + browser-validated.** The +refinements above are optional polish, not blockers. diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 10645f38..ba7cc889 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -204,14 +204,14 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): environment, so unverifiable) and is coupled to the webpack module runtime + injected snapshot-helper entries. A Rollup equivalent would need its own snapshot-safe bootstrap; deferred until the mksnapshot toolchain is available. -5. **Framework externals** (`EnactFrameworkPlugin` / `EnactFrameworkRefPlugin`, - `mixins/framework.js`, `mixins/externals.js`) — DLL-style shared `@enact/*` + - react framework bundle. *Effort: high — not ported.* Webpack's DLL maps module - requests to IDs in a prebuilt bundle via a manifest; Rollup has no equivalent. - A Vite port needs a two-build strategy (a UMD `enact_framework` lib build + - `build.rollupOptions.external` in the app build) plus runtime linking of - `@enact/*`/`react` to the external bundle (import map or UMD globals) — a real - project, not validated here. +5. ~~**Framework externals**~~ — **ported** (`mixins/vite-framework.js` + + `pack.js` `--framework`/`--externals`). Webpack's DLL maps deep module requests to + IDs in a prebuilt bundle via a manifest; the Vite analog is a shared framework ESM + build addressed by an **import map** (exact keys per specifier, from a manifest), + with `build.rollupOptions.external` on the app build. Browser-validated end-to-end + on limestone/qa-a11y: 138-specifier framework + `enact.css`, app externalizes 60 + specifiers, boots fully styled with a single React instance, console clean. Full + findings in [`vite-framework-externals-spike.md`](./vite-framework-externals-spike.md). 6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not needed under Vite (different FS handling). *Drop.* 7. **`node-polyfill-webpack-plugin`** — supplies Node builtins (`global`, @@ -245,7 +245,16 @@ unchanged. Both bundlers coexist during migration. removal (mirrors the webpack `unmangled` mixin). Only affects production builds. - `enact eject --vite` wires the ejected app's scripts to the Vite path (see [vite-eject-testing.md](vite-eject-testing.md)). -- Webpack-only flags on the Vite path (`--isomorphic`, `--snapshot`, framework/externals) print a "not yet supported, ignored" notice. `--externals-public`/`--externals-polyfill`/`--externals-corejs` only shape that (unported) framework/externals output, so they print an "ignored" notice too. +- **`--framework` / `--externals`** are wired via **`mixins/vite-framework.js`** (the + Vite counterpart to the webpack DLL `framework`/`externals` mixins): `--framework` + builds the shared `@enact`+react+ilib bundle as reusable ESM + a specifier manifest + + one `enact.css`; `--externals=<path>` externalizes those specifiers from the app build + and injects an import map (+ the shared stylesheet) resolved from the manifest. + `--externals-public` sets the import-map base URL (remote framework path). + Browser-validated on limestone/qa-a11y. See + [vite-framework-externals-spike.md](vite-framework-externals-spike.md). +- Webpack-only flags still not ported (`--isomorphic`, `--snapshot`) print a "not yet + supported, ignored" notice. The reusable bundler plugins were **added to `@enact/dev-utils`** — mirroring how the webpack plugins (`ILibPlugin`, `WebOSMetaPlugin`, …) live there — and are @@ -281,11 +290,18 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered ## Still not ported (webpack remains the default for these) -- **`--isomorphic`** prerendering, **`--snapshot`**, and **framework externals** — - see gaps #3–#5 above. Each is a substantial project (not a config tweak); the - Vite path prints a "not yet supported, ignored" notice for these flags. +- **`--isomorphic`** prerendering and **`--snapshot`** — see gaps #3–#4 above. Each is + a substantial project (not a config tweak); the Vite path prints a "not yet + supported, ignored" notice for these flags. (**Framework externals** — gap #5 — is + now ported.) - **`icss` mode / `forceCSSModules`** (gap #8): Vite treats only `*.module.*` as CSS modules; the webpack `mode: 'icss'` nuance isn't replicated. +- **Framework refinements** (post-port): building `--framework` *in a theme repo* + (e.g. limestone) should include the theme's own components (webpack's + `libraries.push('.')` case) — the current enumeration scans `node_modules/@enact`, + so build the framework where all `@enact` incl. the theme are installed (e.g. an + app/sample context). `--externals-polyfill` (move core-js into the framework) is + not yet wired. ## Recommendation From d4cb1d4ada828e5c74a730bcfc96da9eb0336351 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 13 Jul 2026 16:37:44 +0300 Subject: [PATCH 08/20] migrated pack for --external-polyfills --- commands/pack.js | 16 ++++++++++------ docs/vite-framework-externals-spike.md | 17 +++++++++++++++-- docs/vite-migration.md | 7 ++++--- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index aecaa8ce..88fdc6a5 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -209,7 +209,7 @@ async function viteFramework (opts) { const {build: viteBuildApi} = require('vite'); const appRequire = createRequire(path.join(app.context, 'package.json')); - const specs = viteFw.enumerateSpecifiers(app.context); + const specs = viteFw.enumerateSpecifiers(app.context, {polyfill: opts['externals-polyfill']}); const srcDir = path.join(app.context, '.enact-framework-src'); const {input, names} = viteFw.writeWrappers(specs, srcDir, appRequire); @@ -272,9 +272,15 @@ async function viteBuild (opts) { if (opts.output) config.build.outDir = path.resolve(opts.output); // --externals: externalize the shared framework specifiers out of the app build, - // collecting the ones actually imported so we can build a minimal import map. + // collecting the ones actually imported so we can build a minimal import map. The + // framework manifest is read up front so externalization is manifest-aware (only + // externalize what the framework actually provides). const collected = new Set(); - if (opts.externals) viteFw.applyExternals(config, collected); + let manifest = null; + if (opts.externals) { + manifest = viteFw.readManifest(path.resolve(opts.externals)); + viteFw.applyExternals(config, collected, manifest, {polyfill: opts['externals-polyfill']}); + } // Apply the build-shaping flags (--no-minify, --verbose, --stats), mirroring the // webpack path's `mixins.apply`. Runs after the output override so --stats writes @@ -295,12 +301,10 @@ async function viteBuild (opts) { // --externals post-step: resolve the collected specifiers against the framework's // manifest and inject the import map + shared stylesheet into the built index.html. if (opts.externals && !opts.watch) { - const frameworkPath = path.resolve(opts.externals); - const manifest = viteFw.readManifest(frameworkPath); let base = opts['externals-public']; if (!base) { // No remote public path: serve the framework locally under ./framework. - fs.copySync(frameworkPath, path.join(config.build.outDir, 'framework'), {dereference: true}); + fs.copySync(path.resolve(opts.externals), path.join(config.build.outDir, 'framework'), {dereference: true}); base = './framework'; } const n = viteFw.injectHtml(path.join(config.build.outDir, 'index.html'), manifest, collected, base); diff --git a/docs/vite-framework-externals-spike.md b/docs/vite-framework-externals-spike.md index be4d6722..61f391e2 100644 --- a/docs/vite-framework-externals-spike.md +++ b/docs/vite-framework-externals-spike.md @@ -115,10 +115,23 @@ Implementation: scans `node_modules/@enact`, so build the framework in a context where all `@enact` *including the theme* are installed (an app/sample context works; qa-a11y gave 138 specifiers incl. limestone). Adding theme-self globbing would let it run in the bare theme repo. -- **`--externals-polyfill`** — moving core-js into the framework bundle (vs. the app's - combined entry) is not yet wired. - **`--snapshot`** interaction — deferred until `--snapshot` itself is ported. +### `--externals-polyfill` — wired + +`pack --framework --externals-polyfill` adds `core-js/stable` as a framework entry (a +side-effect wrapper; folds all core-js into the shared bundle, manifest key `core-js/stable`). +`pack --externals=<path> --externals-polyfill` then externalizes core-js out of the app: +because the app config *aliases* `core-js` to the CLI's copy (so the `external` fn only sees +488 resolved internals, never the bare specifier), the externals path **drops that alias** so +`core-js/stable` stays a bare specifier and externalizes as one unit → the ~488 core-js +modules leave the app bundle and resolve to the framework via the import map. Externalization +is **manifest-aware**, so core-js (and any @enact the framework doesn't provide) is only +externalized when the framework actually carries it — otherwise it stays bundled and the app +still builds. Browser-unverified but bundle-verified: with the flag, core-js internals are +absent from `main.js` and `core-js/stable` appears in the import map; without it, core-js +stays in the app. + ## Risk assessment **All technical risk is retired and the feature is implemented + browser-validated.** The diff --git a/docs/vite-migration.md b/docs/vite-migration.md index ba7cc889..2022b2ad 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -296,12 +296,13 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered now ported.) - **`icss` mode / `forceCSSModules`** (gap #8): Vite treats only `*.module.*` as CSS modules; the webpack `mode: 'icss'` nuance isn't replicated. -- **Framework refinements** (post-port): building `--framework` *in a theme repo* +- **Framework refinement** (post-port): building `--framework` *in a theme repo* (e.g. limestone) should include the theme's own components (webpack's `libraries.push('.')` case) — the current enumeration scans `node_modules/@enact`, so build the framework where all `@enact` incl. the theme are installed (e.g. an - app/sample context). `--externals-polyfill` (move core-js into the framework) is - not yet wired. + app/sample context). (`--externals-polyfill` — move core-js into the framework — **is** + wired: `pack --framework --externals-polyfill` folds core-js into the framework, and + `pack --externals=<path> --externals-polyfill` delegates it out of the app.) ## Recommendation From 3c38e2ce9049c1a249b9209c06c5391ff7879cd1 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Tue, 14 Jul 2026 11:47:34 +0300 Subject: [PATCH 09/20] migrated pack for --isomorphic --- commands/pack.js | 98 +++++++++++++++- docs/vite-isomorphic-scope.md | 206 +++++++++++++++++++++++++++++----- docs/vite-migration.md | 56 ++++----- 3 files changed, 297 insertions(+), 63 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index 88fdc6a5..735264ff 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -21,6 +21,8 @@ const printBuildError = require('react-dev-utils/printBuildError'); const webpack = require('webpack'); const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils'); const viteFw = require('@enact/dev-utils/mixins/vite-framework'); +const viteIso = require('@enact/dev-utils/mixins/vite-isomorphic'); +const {parseLocales} = require('@enact/dev-utils/plugins/PrerenderPlugin/parse-locales'); const {isViteBundler} = require('./vite-utils'); @@ -229,20 +231,104 @@ async function viteFramework (opts) { ); } +// Isomorphic (prerendered) Vite build. Client build (hydrateRoot) + a real `vite build --ssr` +// of the app entry, then per-locale server render (FileXHR for iLib data) and assembly into +// the webpack-compatible output (fallback index.html + deduped index.<variant>.html + +// locale-map.json + per-locale webOS appinfo). Vite counterpart to webpack `pack --isomorphic`. +async function viteIsomorphic (opts) { + const {createRequire} = require('module'); + const {build: viteBuildApi} = require('vite'); + const appRequire = createRequire(path.join(app.context, 'package.json')); + const configFactory = require('../config/vite.config'); + + const locales = opts.locales ? parseLocales(app.context, opts.locales) || ['en-US'] : ['en-US']; + const outDir = opts.output ? path.resolve(opts.output) : path.resolve('./dist'); + const serverEntry = path.resolve(opts.entry || app.entry || path.join(app.context, 'src/index.js')); + const ssrOut = path.join(app.context, '.enact-ssr'); + + console.log(`Creating an isomorphic production build (${locales.length} locale(s))...`); + + // 1) Client build (isomorphic ON → the app entry uses hydrateRoot). + const clientConfig = configFactory( + opts.production ? 'production' : 'development', + !opts.linting, + opts['content-hash'], + true /* isomorphic */, + !opts.animation, + !opts['split-css'], + opts['ilib-additional-path'], + opts.locales + ); + if (opts.output) clientConfig.build.outDir = outDir; + // --externals: externalize the shared framework from the CLIENT build (browser loads it via + // import map). The SSR build below always bundles @enact so it can render. CSS-module hashes + // stay consistent because both reuse the factory (same rootContext) as the framework build. + const collected = new Set(); + let manifest = null; + if (opts.externals) { + manifest = viteFw.readManifest(path.resolve(opts.externals)); + viteFw.applyExternals(clientConfig, collected, manifest, {polyfill: opts['externals-polyfill']}); + } + mixins.applyVite(clientConfig, opts); + await viteBuildApi(clientConfig); + + // Inject the framework import map (+ shared stylesheet) into the client index.html BEFORE + // the isomorphic assembly transforms it into the fallback/variant files. + if (opts.externals) { + let base = opts['externals-public']; + if (!base) { + fs.copySync(path.resolve(opts.externals), path.join(outDir, 'framework'), {dereference: true}); + base = './framework'; + } + viteFw.injectHtml(path.join(outDir, 'index.html'), manifest, collected, base); + } + + // 2) SSR build (Node-loadable CJS whose default export is the app element). + const ssrConfig = configFactory(opts.production ? 'production' : 'development', true, false, true); + viteIso.applySsrBuild(ssrConfig, {serverEntry, outDir: ssrOut}); + await viteBuildApi(ssrConfig); + + // 3) Per-locale prerender. Load the SSR bundle fresh for each locale so iLib re-initializes. + const bundlePath = path.join(ssrOut, 'app.server.cjs'); + const ssrRequire = createRequire(path.join(ssrOut, 'noop.js')); + const {renderToString} = appRequire('react-dom/server'); + const load = () => { + Object.keys(ssrRequire.cache || {}).forEach(k => { + if (k.startsWith(ssrOut)) delete ssrRequire.cache[k]; + }); + const mod = ssrRequire(bundlePath); + return mod && mod.default !== undefined ? mod.default : mod; + }; + const {prerenders, attr, aliasOf} = viteIso.prerender({ + locales, + load, + renderToString, + fontGenerator: app.fontGenerator + }); + + // 4) Assemble HTML + locale-map, then (5) webOS per-locale appinfo. + const {localeMap} = viteIso.assemble({outDir, locales, prerenders, attr, aliasOf, screenTypes: app.screenTypes || []}); + viteIso.writeAppinfo({outDir, locales, localeMap}); + + fs.removeSync(ssrOut); + console.log( + chalk.cyan(`Prerendered ${locales.length} locale(s) into ${prerenders.length} variant(s).`) + ); + console.log(chalk.green('Compiled successfully.')); +} + // Experimental Vite build path. Mirrors the webpack `build`/`watch` behavior but -// drives Vite's JS API. `--isomorphic` (prerendering) and `--snapshot` are not ported and -// are reported + skipped; `--isomorphic` is forced off (client render) rather than -// forwarded, because setting ENACT_PACK_ISOMORPHIC without prerendered markup would make -// the app hydrate an empty root. +// drives Vite's JS API. `--snapshot` is not ported (reported + skipped). // // Wired here: --framework (shared bundle), --externals (import-map externalization), -// and via mixins.applyVite: --no-minify, --verbose, --stats. +// --isomorphic (prerendering), and via mixins.applyVite: --no-minify, --verbose, --stats. async function viteBuild (opts) { const {build: viteBuildApi} = require('vite'); if (opts.framework) return viteFramework(opts); + if (opts.isomorphic) return viteIsomorphic(opts); - ['isomorphic', 'snapshot'].forEach(flag => { + ['snapshot'].forEach(flag => { if (opts[flag]) { console.log( chalk.yellow(`NOTICE: --${flag} is not yet supported by the Vite bundler and will be ignored.`) diff --git a/docs/vite-isomorphic-scope.md b/docs/vite-isomorphic-scope.md index 71ec9aea..cf7c36c2 100644 --- a/docs/vite-isomorphic-scope.md +++ b/docs/vite-isomorphic-scope.md @@ -76,52 +76,196 @@ only the webpack-runtime string rewrites in `vdom-server-render.stage` (`__webpack_require__.e`, `webpackAsyncContext`) are webpack-specific and get dropped (Vite's SSR bundle has no such runtime). +## Phase A — validated (GO) + +A timeboxed spike (`vite build --ssr` of `qa-a11y/src/index.js` reusing the CLI vite.config +factory, then load + `renderToString` for `en-US`) **passed the go/no-go gate**: the app +server-rendered to **68 KB of non-empty markup** with Enact root classes +(`enact-orientation-landscape enact-res-fhd … limestone-theme … ThemeDecorator_root`), +default export a valid React element, no throw. + +Concrete findings (the Phase A "how"): + +1. **Real `vite build --ssr`, not `ssrLoadModule`** — confirmed: the JSX-in-`.js` transform + runs and `@enact/*` (in `ssr.noExternal`) is bundled + babel-transformed. +2. **CJS output** (`output.format='cjs'` + `inlineDynamicImports`) — the bundle carries + leftover `require()` (ilib's platform loaders); an ESM (`.mjs`) bundle throws + `require is not defined`. Load it with `require()` in the prerender driver. +3. **ilib: bundle it + keep the *Node* loader.** The client config ignores **all** ilib + platform loaders (`ILIB_LOADER_RE`) because the browser can't use them; for SSR-in-Node, + override `commonjsOptions.ignore` to keep `NodeLoader`/`ilib-node.js` (ignore only + Qt/Rhino/Ringo) so ilib reads locale data from disk. Also add `/^ilib($|\/)/` to + `ssr.noExternal` (externalized, its internal dynamic imports resolve to a wrong path). +4. **No `window`/DOM shim needed.** Enact renders server-side without a DOM (matches webpack's + `vdom-server-render`, which sets only `XMLHttpRequest=FileXHR` + `LANG`, never `window`). + +## Phase B — validated (prerender driver + FileXHR); scope-doc criterion corrected + +The prerender driver (build SSR bundle once, then per-locale: set `FileXHR` global + `LANG`, +uncached-`require`, `renderToString`) works, and its output **matches the current webpack +`--isomorphic` baseline byte-for-byte modulo the CSS-module hash**. + +Findings: + +1. **FileXHR path shim — strip the leading `/`.** `@enact/i18n` builds **absolute** locale URLs + (`/node_modules/ilib/locale/ilibmanifest.json`, `/resources/ilibmanifest.json`) because + Vite's base is `/`. `FileXHR` reads relative to cwd (the app dir), so the driver wraps it to + strip the leading `/` → both manifests resolve (200) from `node_modules/ilib/locale/…` and + `resources/…`. (No `ILIB_BASE_PATH` gymnastics needed.) +2. **`enact-locale-*` is NOT emitted server-side — by design (scope-doc criterion was stale).** + The current `@enact/i18n` `I18n.getServerSnapshot()` returns **`className: null`** + deliberately; the locale class is applied on the **client** after hydration. Confirmed the + current **webpack** `--isomorphic` produces the *same* thing: its prerendered + `index.multi.html` has **no** `enact-locale-*` classes either. So "extract `enact-locale-*` + root classes" (from the older webpack behavior this doc was written against) is obsolete — + the correct success criterion is **"prerendered markup matches webpack's prerender."** +3. **Matches webpack exactly.** Vite `renderToString` → the same root as webpack's + `index.multi.html`: `enact-orientation-landscape enact-res-fhd … ThemeDecorator_root__<hash> + limestone-theme enact-unselectable enact-fit enact-text-normal` + identical panel content; + only the CSS-module hash suffix differs (`__voIZO` vs `__tejPz`), as expected across bundlers. +4. **Identical-across-locales → dedupe.** For a no-translation app (qa-a11y), every locale + renders identically, so webpack aliases them all to one file (`locale-map.json`: + both `en-US`+`ko-KR` → `index.multi.html`). Vite's per-locale renders are likewise identical + → the driver dedupes to one variant. (Apps with `$L` translations would render differently + per locale and get separate variant files.) + +Net: the render half of isomorphic is proven on Vite and output-compatible with webpack. What +remains (Phase C+) is pure **assembly** — reuse the bundler-agnostic `templates.js` / +`simplifyAliases` to emit the fallback `index.html`, the deduped `index.<variant>.html`, the +startup `<script>`, and `locale-map.json` — plus Phase D webOS per-locale `appinfo.json`. + ## Work breakdown & effort | Phase | Work | Effort | Risk | | --- | --- | --- | --- | -| A | SSR build wiring (`build.ssr`, server entry, `ssr.noExternal` for `@enact/*`; confirm `default` = element renders) | ~0.5–1 d | **High** — Enact components touching `window`/`document` at import; may need a `window` shim before requiring the bundle | -| B | Prerender driver: per-locale loop, `FileXHR` global, uncached require, `renderToString`, class extraction, dedupe/alias (reuse `simplifyAliases`) | ~1 d | Med — memory/leaks across locales (webpack uses `--expose-gc`); FileXHR path assumptions vs. Vite output layout | -| C | HTML assembly: inject markup, startup script (`templates.startup`), per-locale `index.<locale>.html`, screenTypes wiring | ~1 d | Med — `templates.startup` assumes webpack asset naming; adapt to Vite `main.js`/hashed names | -| D | webOS-meta coupling: per-locale `appinfo.json` `main` + `usePrerendering`; extend `ViteWebOSMetaPlugin` | ~0.5 d | Low | -| E | `fontGenerator` per-locale CSS; `--externals` interaction (defer — depends on framework-externals port) | ~0.5 d | Med | -| F | Validation on limestone across locales + hydration check in browser | ~0.5 d | Med | +| A | ~~SSR build wiring~~ **DONE** — `build.ssr` + CJS output + `ssr.noExternal` `@enact`/`ilib` + Node-loader-kept `commonjsOptions.ignore`; `renderToString` → 68 KB markup | ~~0.5–1 d~~ ✅ | ~~High~~ **retired** — no window shim needed | +| B | ~~Prerender driver~~ **DONE** — per-locale render + `FileXHR` (leading-`/` strip); output matches webpack. Dedupe/emit remains in C | ~~1 d~~ ✅ | ~~Med~~ resolved — render matches webpack byte-for-byte (modulo CSS hash) | +| C | ~~HTML assembly~~ **DONE (spike)** — dedupe + `templates.startup`/`update`, emit `index.html`/`index.multi.html`/`locale-map.json` (byte-identical to webpack) | ~~1 d~~ ✅ | resolved — reused `templates.js`; `main.js` loaded fine (see note) | +| D | ~~webOS-meta coupling~~ **DONE** — root `usePrerendering:true` + per-locale `resources/<lang>/<region>/appinfo.json` `main`→variant (`vite-isomorphic.writeAppinfo`) | ~~0.5 d~~ ✅ | resolved | +| E | ~~`fontGenerator` per-locale CSS; `--externals` interaction~~ **DONE** — `prerender` reads `app.fontGenerator` and prepends per-locale font CSS as a head-append block; `--isomorphic --externals` verified equivalent to plain `--isomorphic` (see below) | ~~0.5 d~~ ✅ | resolved | +| F | ~~Hydration check~~ **DONE** — the production `enact pack -p -i --vite` output hydrates in-browser with **no warnings/errors**; client applies `enact-locale-en` | ~~0.5 d~~ ✅ | resolved | + +**Implemented and browser-validated end-to-end — all phases (A–F) done**, including per-locale +font CSS and `--isomorphic --externals` (verified equivalent to plain `--isomorphic`). + +## Phase C + hydration — validated (spike) -**Total: ~4–5 focused days.** Phase A is the go/no-go gate. +The full pipeline (client build + SSR build + per-locale prerender + assembly) was driven +end-to-end and **browser-verified** on qa-a11y (`-l en-US,ko-KR`): + +- **Output matches webpack byte-for-byte in shape:** emits `index.html` (2.6 KB, empty `#root` + + inline `templates.startup` script, body module script removed), `index.multi.html` (71 KB, + prerendered markup in `#root` + `templates.update` script), and `locale-map.json` **identical** + to webpack's (`{fallback:'index.html', locales:{en-US→index.multi.html, ko-KR→index.multi.html}}`). + Both locales deduped to one variant, reusing the bundler-agnostic `templates.js`. +- **Hydration is clean (production).** Loading `/index.multi.html` in a browser: the prerendered + markup shows, the startup script loads `main.js`, the **production** (`-p`) output **React + hydrates with zero console warnings/errors**, and the client then applies `enact-locale-en` to + the root (matching the i18n design — null on the server, applied on the client). Note: a **dev** + build surfaces two dev-only React warnings (`getServerSnapshot should be cached` + a root-class + hydration mismatch) that are **by-design in `@enact/i18n`** (locale class deferred to the client) + and identical to webpack's isomorphic output; `-p` strips both. See the `--externals` section below. + +Notes for productionization (not blockers): +- `templates.startup`'s `appendScripts` adds `main.js` as a **classic** `<script>`; it worked + here (qa-a11y's single production bundle has no top-level `import`), but for robustness the + Vite path should mark it `type="module"` (Risk #3 — small `templates.startup` adapter). +- `screenTypes` was passed empty in the spike; wire limestone's real `screenTypes` (from the + theme's RI config) so the startup script's resolution scaling matches webpack. ## Key risks / unknowns -1. **SSR-safety of Enact components.** The spike proved the transform issue is - solved by a real SSR build, but not that Sandstone/Limestone render without a - DOM. Webpack relies on the app being isomorphic-aware; some components may still - need a `window`/`document` shim (jsdom or the webpack `mock-window`). **Validate - in Phase A before committing to the rest.** -2. **`FileXHR` ↔ output layout.** `FileXHR` reads locale data from disk by URL - path; it must resolve against the same paths `ViteILibPlugin` emits - (`node_modules/ilib/locale/…`, trimmed manifest). Likely needs a base-path shim. +1. ~~**SSR-safety of Enact components.**~~ **Resolved (Phase A)** — Limestone renders + server-side with **no** DOM shim; matches webpack's DOM-less `vdom-server-render`. +2. ~~**`FileXHR` ↔ output layout.**~~ **Resolved (Phase B)** — wrap `FileXHR` to strip the + leading `/` (Vite base `/`); locale manifests + data resolve from `node_modules/ilib/locale` + and `resources/` relative to the app cwd. 3. **Startup script asset names.** `templates.startup` was written for webpack chunk names; Vite uses `main.js`/`chunk.*`/hashed. Needs a small adapter. -4. **Hydration mismatch.** Client uses `hydrateRoot` when `ENACT_PACK_ISOMORPHIC`; - the prerendered markup must match the client's first render (same locale, RI - classes) or React warns/re-renders. Validate with real browser hydration. -5. **`--externals`** interplay: webpack's isomorphic + externals reroutes - `require('enact_framework')`. This depends on the **framework-externals** port, - so scope `--isomorphic --externals` as a follow-on, not part of this. +4. ~~**Hydration mismatch.**~~ **Resolved** — validated in-browser; the only mismatch is the + by-design `@enact/i18n` locale-deferral (`className: null` server-side), identical to webpack + and stripped in production. See the `--externals` section. +5. ~~**`--externals`** interplay~~ **Resolved (follow-on, now done)** — webpack's isomorphic + + externals reroutes `require('enact_framework')` to the framework's `enact.js`; the Vite path + instead bundles @enact for SSR while the client uses the framework import map, and the two are + verified to produce identical hydration. See the `--isomorphic --externals` section. ## Validation plan -- Phase A: a Node script that loads the SSR bundle and `renderToString`s the - limestone app for `en-US` → non-empty markup with `enact-locale-*` root classes. -- Phase C: `enact pack -p -i --vite -l en-US,ko-KR` → `dist/index.html`, - `index.en-US.html`, `index.ko-KR.html` each contain prerendered `#root` markup. +- ~~Phase A~~ **done**: SSR bundle `renderToString`s to non-empty markup (68 KB) matching + webpack's prerender (note: `enact-locale-*` is **not** emitted server-side by the current + i18n — that older criterion is obsolete; see Phase B). +- ~~Phase B~~ **done**: per-locale render via `FileXHR` (leading-`/` strip) → markup + byte-identical to webpack's `index.multi.html` (modulo CSS hash). +- Phase C: `enact pack -p -i --vite -l en-US,ko-KR` → the fallback `index.html` (empty `#root` + + startup script), the deduped `index.<variant>.html` with prerendered markup, and + `locale-map.json` — matching the webpack `index.html`/`index.multi.html`/`locale-map.json` shape. - Phase F: serve `dist/` and load in a browser — confirm markup shows pre-JS and React hydrates with **no hydration warnings** in the console; confirm locale switch works. -## Recommendation +## Wired into the CLI — done + +`--isomorphic` is implemented and browser-validated end-to-end: + +- **`dev-utils/mixins/vite-isomorphic.js`** — `applySsrBuild` (client config → SSR build), + `prerender` (per-locale render + `FileXHR` leading-`/` strip + `enact-locale-*` extract + + dedupe), `assemble` (fallback `index.html` + deduped `index.<variant>.html` + `locale-map.json` + via `templates.js`), `writeAppinfo` (root `usePrerendering` + per-locale appinfo `main`). +- **`pack.js`** — `viteIsomorphic(opts)`: client build (isomorphic → `hydrateRoot`) → SSR build → + per-locale prerender → assemble → appinfo; branched from `viteBuild` when `opts.isomorphic`. +- **Validated:** `enact pack -p -i --vite -l en-US,ko-KR` on qa-a11y emits `index.html`, + `index.multi.html`, `locale-map.json` (both locales → `index.multi.html`, matching webpack), + root `appinfo.json usePrerendering:true`, and `resources/en/US`+`resources/ko/KR/appinfo.json` + (`main:index.multi.html`). The **production** output hydrates in-browser with **no console + warnings/errors** (dev builds show the by-design `@enact/i18n` locale-deferral warnings — see + the `--externals` section). + +Productionization — both wired + validated: +- **ESM startup script (done).** `assemble` rewrites `templates.startup`'s dynamically-appended + script to `type="module"` (Vite emits ES modules; a single `main.js` module self-loads its + chunks). Browser-confirmed: `main.js` loads as a module and hydrates cleanly — robust for + code-split apps, unlike the classic `<script>` that only worked for a self-contained bundle. +- **Real screenTypes (done).** `pack.js` passes `app.screenTypes` (the resolved theme RI + screen-type array, e.g. limestone's 9 entries) to `assemble`; browser-confirmed the startup + script applied `enact-res-hd` from the real screenTypes (resolution scaling parity with webpack). +- **Phase E — per-locale font CSS (done).** `prerender` reads `app.fontGenerator`, calls + `generator(locale)` + `fontOverrideGenerator(locale)` per locale, prepends the CSS as a + head-append block (so it participates in dedupe); `assemble` extracts it into each variant's + `<head>`. Browser-verified: limestone's `localized-fonts` `<style>` (with `@font-face`) lands + in `index.multi.html`'s `<head>`. + +### `--isomorphic --externals` — wired, works, no divergence from plain `--isomorphic` + +The combination is wired (`viteIsomorphic`: the CLIENT build externalizes the framework + +injects the import map before assembly; the SSR build always bundles @enact to render). It +**builds and runs**: the app prerenders, is **styled** (the SSR prerender's CSS-module hashes +match the framework's `enact.css` — deterministic `cssModuleIdent`, verified `ThemeDecorator_root__voIZO` +== `enact.css`), loads @enact from the framework via the import map (205 files), and hydrates +with the locale applied. + +**Verified equivalent to plain `--isomorphic` (2026-07).** An earlier note flagged an +`--externals`-only hydration mismatch as needing follow-up. That was a **measurement artifact**: +it compared a *production* plain build (React dev warnings stripped) against a framework whose +react-dom was a *development* build (warnings live). Rebuilt apples-to-apples — a **dev** +framework + dev `--isomorphic --externals` vs. a dev plain `--isomorphic`, both on qa-a11y +(`-l en-US,ko-KR`) — the two are **byte-identical**: same hydrated root className, and the +**same two dev-only warnings** on the **same** `<ThemeDecorator>` tree: + +1. `The result of getServerSnapshot should be cached to avoid an infinite loop` +2. `A tree hydrated but some attributes … didn't match the client properties` (the root className). + +Both are **inherent to @enact's isomorphic i18n design**, not to `--externals` and not to Vite: +`@enact/i18n`'s `I18n.getServerSnapshot()` deliberately returns `className: null` server-side and +the locale class (`enact-locale-*`) is applied on the **client** after hydration — so the first +client render legitimately differs from the server markup. **Webpack's isomorphic path has the +identical by-design mismatch** (same i18n source, same `getServerSnapshot`). React's production +build strips both dev-only warnings, so shipped output (`-p`) hydrates silently in every case. -Start with **Phase A as a timeboxed spike** (SSR build + single-locale -`renderToString` of limestone). If Enact renders server-side with at most a light -`window` shim, the rest is mechanical reuse of the existing bundler-agnostic -helpers. If it needs heavy DOM emulation, reassess whether isomorphic-under-Vite is -worth it vs. keeping `--isomorphic` on webpack indefinitely. +Why webpack's SSR *looks* like it can't diverge: its prerender rewrites +`require('enact_framework')` → the framework's `enact.js` (`vdom-server-render.stage`), so one +@enact serves both SSR and client. The Vite path instead uses two @enact builds (SSR-bundled + +framework import map) — but because `cssModuleIdent` is deterministic on `resourcePath` + +`rootContext`, both builds emit **identical** CSS-module class names and identical root markup, so +the two-build approach produces the same hydration result as webpack's single-@enact approach. +**No caveat, no experimental flag** — `--isomorphic --externals` is production-correct. diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 2022b2ad..a8329932 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -12,9 +12,10 @@ starts and HMR. Several webOS-specific features that started as webpack compiler plugins have since been **re-authored as Vite/Rollup plugins** in `@enact/dev-utils` and validated: **iLib i18n runtime + locale filtering** (`ViteILibPlugin`), **webOS metadata** (`ViteWebOSMetaPlugin`), plus the HTML document (`ViteHtmlPlugin`) and -**ESLint**. Three features are **not yet ported** and still require webpack: -`--isomorphic` prerendering, `--snapshot` (V8), and framework externals (see the -gap list below). For those, `pack --vite` prints a "not supported, ignored" notice. +**ESLint**. **`--isomorphic` prerendering and framework externals are also now ported and +browser-validated** (`mixins/vite-isomorphic.js`, `mixins/vite-framework.js`). Only +**`--snapshot`** (V8) still requires webpack — it needs the webOS `V8_MKSNAPSHOT` toolchain, +unavailable here — and `pack --vite` prints a "not supported, ignored" notice for it. The Vite config lives at [`config/vite.config.js`](../config/vite.config.js); it mirrors the `webpack.config.js` factory signature and reuses the existing Enact @@ -188,17 +189,18 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): fallback via `ViteWebOSMetaPlugin.readTitle`. **Validated:** `appinfo.json` + `icon*.png` land in `dist` and serve (HTTP 200) in dev. *Remaining:* `$`-prefixed sys-assets. -3. **`PrerenderPlugin` + isomorphic mixin** — server-renders the app to static - HTML per-locale (`enact pack --isomorphic`). *Effort: high — not ported.* - **Investigated:** Vite has native SSR (`ssrLoadModule`), but a spike hit a - concrete first blocker — the SSR module runner does not apply the JSX-in-`.js` - transform (`App.js: Unexpected token '<'`), and that is only the first hurdle. - A faithful port also needs a `window`/`document` mock, the `FileXHR` locale-data - loader, per-locale rendering, `screenTypes`/font handling, a `locale-map`, and - hydration-safe markup — i.e. a re-implementation of `vdom-server-render` + - `templates`, not a config tweak. Left on webpack. **A full implementation - scope** (phases, effort, risks, validation plan) is in - [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). +3. ~~**`PrerenderPlugin` + isomorphic mixin**~~ — **ported** (`mixins/vite-isomorphic.js` + + `pack.js` `viteIsomorphic`). Uses a real **`vite build --ssr`** of the app entry (the key + correction from the first spike, which used `ssrLoadModule` and hit the JSX-in-`.js` + transform gap), then per-locale server render (`FileXHR` for iLib locale data, no DOM + shim needed) + assembly into the webpack-compatible output (fallback `index.html` + + deduped `index.<variant>.html` + `locale-map.json` + per-locale webOS `appinfo.json`), + reusing the bundler-agnostic `templates.js`/`FileXHR`. Browser-validated end-to-end on + qa-a11y (`-p -i -l en-US,ko-KR`): prerendered markup hydrates with no console warnings in the + production build. (A dev build shows two dev-only React warnings that are by-design in + `@enact/i18n` — locale class deferred to the client — and identical to webpack's isomorphic + output, including with `--externals`; `-p` strips them. Details in the scope doc.) + Full findings/phases in [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). 4. **`SnapshotPlugin`** — emits a V8 snapshot blob (`--snapshot`). *Not portable here.* Requires the webOS `V8_MKSNAPSHOT` toolchain (absent in this environment, so unverifiable) and is coupled to the webpack module runtime + @@ -253,8 +255,11 @@ unchanged. Both bundlers coexist during migration. `--externals-public` sets the import-map base URL (remote framework path). Browser-validated on limestone/qa-a11y. See [vite-framework-externals-spike.md](vite-framework-externals-spike.md). -- Webpack-only flags still not ported (`--isomorphic`, `--snapshot`) print a "not yet - supported, ignored" notice. +- **`--isomorphic`** is wired via **`mixins/vite-isomorphic.js`** + `pack.js`'s + `viteIsomorphic` (client `hydrateRoot` build + `vite build --ssr` + per-locale prerender + + webpack-compatible HTML/`locale-map.json`/`appinfo.json` assembly). Browser-validated. +- `--snapshot` is not ported (needs the webOS `V8_MKSNAPSHOT` toolchain) and prints a "not + yet supported, ignored" notice. The reusable bundler plugins were **added to `@enact/dev-utils`** — mirroring how the webpack plugins (`ILibPlugin`, `WebOSMetaPlugin`, …) live there — and are @@ -290,10 +295,11 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered ## Still not ported (webpack remains the default for these) -- **`--isomorphic`** prerendering and **`--snapshot`** — see gaps #3–#4 above. Each is - a substantial project (not a config tweak); the Vite path prints a "not yet - supported, ignored" notice for these flags. (**Framework externals** — gap #5 — is - now ported.) +- **`--snapshot`** (gap #4) — needs the webOS `V8_MKSNAPSHOT` toolchain (absent here, + so unverifiable); the Vite path prints a "not yet supported, ignored" notice. + (**`--isomorphic`** — gap #3 — and **framework externals** — gap #5 — are now ported; + isomorphic is browser-validated end-to-end via `vite build --ssr` + per-locale prerender, + see [vite-isomorphic-scope.md](vite-isomorphic-scope.md).) - **`icss` mode / `forceCSSModules`** (gap #8): Vite treats only `*.module.*` as CSS modules; the webpack `mode: 'icss'` nuance isn't replicated. - **Framework refinement** (post-port): building `--framework` *in a theme repo* @@ -307,9 +313,7 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered ## Recommendation Adopt Vite behind a feature flag for the **browser dev/build** path first (biggest -DX win, lowest risk) — now validated end-to-end including i18n runtime, locale -filtering, webOS metadata, and ESLint. Keep webpack as the default for -`--isomorphic`, `--snapshot`, and framework-externals builds; those three are the -remaining work and each warrants its own focused effort (isomorphic first, as it's -the most-used; snapshot last, as it needs the webOS mksnapshot toolchain to even -validate). +DX win, lowest risk) — validated end-to-end including i18n runtime, locale filtering, +webOS metadata, and ESLint. Beyond that, **`--isomorphic` and framework externals are +now ported and validated** too, leaving only **`--snapshot`** on webpack (it needs the +webOS `V8_MKSNAPSHOT` toolchain to build/validate — unavailable here). From 30cc9f4848af516b880e7e0d4cf2e127f5f5e5d9 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Tue, 14 Jul 2026 12:04:08 +0300 Subject: [PATCH 10/20] migrated pack for --isomorphic --- commands/pack.js | 1 + docs/vite-migration.md | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index 735264ff..b17e4342 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -297,6 +297,7 @@ async function viteIsomorphic (opts) { if (k.startsWith(ssrOut)) delete ssrRequire.cache[k]; }); const mod = ssrRequire(bundlePath); + // eslint-disable-next-line no-undefined return mod && mod.default !== undefined ? mod.default : mod; }; const {prerenders, attr, aliasOf} = viteIso.prerender({ diff --git a/docs/vite-migration.md b/docs/vite-migration.md index a8329932..7911ae85 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -187,8 +187,11 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): `resources/**/appinfo.json`, emits them and their referenced icon/splash assets (build: `writeBundle` copy; dev: middleware), and supplies the `<title>` fallback via `ViteWebOSMetaPlugin.readTitle`. **Validated:** `appinfo.json` + - `icon*.png` land in `dist` and serve (HTTP 200) in dev. *Remaining:* - `$`-prefixed sys-assets. + `icon*.png` land in `dist` and serve (HTTP 200) in dev. `$`-prefixed sys-assets + (`$icon.png` → `sys-assets/<spec>/icon.png`, emitted per-spec preserving the + layout, appinfo value left untouched) are now handled — matching the webpack + plugin; verified against a fixture (sys-assets across specs, dedup across + locales, regular assets, untouched `$` values). 3. ~~**`PrerenderPlugin` + isomorphic mixin**~~ — **ported** (`mixins/vite-isomorphic.js` + `pack.js` `viteIsomorphic`). Uses a real **`vite build --ssr`** of the app entry (the key correction from the first spike, which used `ssrLoadModule` and hit the JSX-in-`.js` From 5975c3b48c8b7927f791d3bdf22ed407a02c67d1 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Tue, 14 Jul 2026 13:54:07 +0300 Subject: [PATCH 11/20] added vite-plugin-polyfills --- config/vite.config.js | 87 ++++++++++++++++- docs/vite-migration.md | 44 ++++++--- npm-shrinkwrap.json | 206 +++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 323 insertions(+), 15 deletions(-) diff --git a/config/vite.config.js b/config/vite.config.js index caed87ca..d1e505f3 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -34,6 +34,15 @@ const { ViteWebOSMetaPlugin } = require('@enact/dev-utils'); +// Node core-module polyfills for the browser bundle — the Vite analog of the +// webpack build's `node-polyfill-webpack-plugin` (needed mainly for screenshot +// tests, which reference `Buffer`/`process`/`stream`/etc.). Injects globals +// reference-only (like webpack's ProvidePlugin — no global `typeof process` +// flip) and aliases node builtins to browser implementations. Dropped for the +// SSR/isomorphic build (see `vite-isomorphic.js` applySsrBuild), which needs the +// real Node builtins. +const {nodePolyfills} = require('vite-plugin-node-polyfills'); + // PostCSS plugin chain, shared with webpack.config.js (single source of truth). const {getPostCssPlugins} = require('./postcss-plugins'); @@ -97,6 +106,62 @@ function enactNeutralizeWebpackHmrPlugin () { }; } +// vite-plugin-node-polyfills injects bare imports of its own shims and +// `node-stdlib-browser` into app modules. Those packages live in the CLI's +// node_modules, not the app's, so an app built with `root: app.context` (e.g. a +// sample under a theme repo) can't resolve them. Resolve those specifiers from the +// CLI instead; their transitive deps then resolve from the CLI tree naturally. +function enactNodePolyfillResolverPlugin () { + const FROM_CLI = /^(?:vite-plugin-node-polyfills|node-stdlib-browser)(?:\/|$)/; + return { + name: 'enact-node-polyfill-resolver', + enforce: 'pre', + resolveId (source) { + if (!FROM_CLI.test(source)) return null; + try { + return require.resolve(source); + } catch (e) { + return null; + } + } + }; +} + +const FORCE_CSS_STYLE_RE = /\.(?:css|less|s[ac]ss)(?:\?.*)?$/; +const FORCE_CSS_MODULE_RE = /\.module\.(?:css|less|s[ac]ss)(?:\?.*)?$/; + +// The Enact `forceCSSModules` build option makes ALL css/less/scss behave as CSS +// modules (scoped), not just `*.module.*` — matching the webpack build, whose +// non-module style rules use `modules:{getLocalIdent}` (no `mode:'icss'`) when the +// option is set. Vite decides module-ness purely from the `.module.` filename infix +// (cssModuleRE) with no override hook, so we resolve each non-module style import and +// redirect it to a virtual id that carries a `.module` infix. The virtual id keeps the +// real directory (so LESS `@import`/`url()` still resolve) and `load` serves the real +// file's contents. `virtualToReal` also lets `generateScopedName` recover the real +// path for the ident hash (webpack parity); genuine `*.module.*` files are untouched. +function enactForceCSSModulesPlugin (virtualToReal) { + return { + name: 'enact-force-css-modules', + enforce: 'pre', + async resolveId (source, importer, options) { + if (!FORCE_CSS_STYLE_RE.test(source) || FORCE_CSS_MODULE_RE.test(source)) return null; + const resolved = await this.resolve(source, importer, {...options, skipSelf: true}); + if (!resolved || resolved.external || FORCE_CSS_MODULE_RE.test(resolved.id)) return resolved; + // Inject `.module` before the extension, preserving the directory + any query. + const virtual = resolved.id.replace(/(\.(?:css|less|s[ac]ss))(\?.*)?$/, '.module$1$2'); + virtualToReal.set(virtual.split('?')[0], resolved.id.split('?')[0]); + return Object.assign({}, resolved, {id: virtual}); + }, + load (id) { + const real = virtualToReal.get(id.split('?')[0]); + if (!real) return null; + // Watch the real file so edits invalidate the virtual module (dev HMR). + this.addWatchFile(real); + return fs.readFileSync(real, 'utf8'); + } + }; +} + // Non-browser iLib platform loaders (`./lib/ilib-qt|rhino|ringo|node|….js`) and // their `*Loader.js` helpers. iLib selects these via runtime platform detection; // the browser branch never reaches them, but bundlers try (and fail) to resolve @@ -241,6 +306,10 @@ module.exports = function ( const entry = createCombinedEntry(app.context, ['core-js/stable', appEntry]); const coreJsDir = path.dirname(require.resolve('core-js/package.json')); + // Maps `forceCSSModules` virtual `.module` ids back to their real style files, so + // `generateScopedName` can hash on the real path (see enactForceCSSModulesPlugin). + const forcedCSSVirtual = new Map(); + // Enumerate the `@enact/*` packages installed in the app so they can be deduped // (Vite `resolve.dedupe` takes exact names, not globs). Apps like the aggregate // `all-samples` import source from many sibling packages, each with its own @@ -319,7 +388,10 @@ module.exports = function ( // hash keeps names unique, so collapsing invalid chars to `_` is safe. modules: { generateScopedName (name, filename) { - const ident = getLocalIdent({resourcePath: filename, rootContext: app.context}, null, name); + // For `forceCSSModules` virtual ids, hash on the real path (webpack parity); + // genuine `*.module.*` files pass through unchanged. + const resourcePath = forcedCSSVirtual.get(filename.split('?')[0]) || filename; + const ident = getLocalIdent({resourcePath, rootContext: app.context}, null, name); return ident.replace(/[^a-zA-Z0-9_-]/g, '_'); } }, @@ -400,6 +472,19 @@ module.exports = function ( plugins: [ // Rewrite webpack's `module.hot` in app source before other transforms. enactNeutralizeWebpackHmrPlugin(), + // `forceCSSModules`: scope ALL css/less/scss as CSS modules (not just *.module.*). + app.forceCSSModules && enactForceCSSModulesPlugin(forcedCSSVirtual), + // Node builtin polyfills for the browser (webpack: node-polyfill-webpack-plugin + // with additionalAliases console/domain/process/stream). `global` is already + // supplied by ViteHtmlPlugin's head shim (R1), so only inject Buffer/process. + // Skip for non-browser targets. Dropped for the SSR build in applySsrBuild. + !['node', 'async-node', 'webworker'].includes(app.environment) && + enactNodePolyfillResolverPlugin(), + !['node', 'async-node', 'webworker'].includes(app.environment) && + nodePolyfills({ + globals: {Buffer: true, process: true, global: false}, + protocolImports: true + }), react({ // @enact/* packages ship raw source (JSX inside .js, ESM) rather than // pre-compiled output, so they must be transpiled like app code. Mirror diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 7911ae85..b9307e88 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -219,15 +219,34 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): findings in [`vite-framework-externals-spike.md`](./vite-framework-externals-spike.md). 6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not needed under Vite (different FS handling). *Drop.* -7. **`node-polyfill-webpack-plugin`** — supplies Node builtins (`global`, - `process`, `Buffer`) in the browser bundle. **Partly resolved:** the runtime - `global` reference in Enact's `polyfills.js` is handled (runtime fixes R1/R2 — - `ViteHtmlPlugin` shim + core-js ESM entry), and `process.env.NODE_ENV` is - covered by `define`. Fuller coverage (`Buffer`, full `process`), mostly for - screenshot tests, is **not** wired — add `vite-plugin-node-polyfills` if needed. -8. **`icss` mode for non-`*.module` CSS / `forceCSSModules`** — Vite auto-treats - only `*.module.*` as modules; the webpack `mode: 'icss'` nuance and the global - `forceCSSModules` toggle need a custom transform. *Not ported.* +7. ~~**`node-polyfill-webpack-plugin`**~~ — **ported.** `global` is supplied by + `ViteHtmlPlugin`'s head shim (R1) and `process.env.NODE_ENV` by `define`; fuller + coverage (`Buffer`, full `process`, and the node builtin modules — the webpack + plugin's `additionalAliases: console/domain/process/stream`) is now wired via + **`vite-plugin-node-polyfills`** in `config/vite.config.js`, gated to browser + targets. Globals are injected **reference-only** (like webpack's `ProvidePlugin` + — no global `typeof process` flip, no bundle bloat when unused; verified qa-a11y + doesn't bundle `buffer` and exposes no `window.Buffer`/`process`). A small + `enact-node-polyfill-resolver` `resolveId` plugin resolves the injected shim + specifiers (`vite-plugin-node-polyfills/*`, `node-stdlib-browser`) from the CLI's + `node_modules`, since apps are built with `root: app.context` and can't reach + them otherwise. For the SSR/isomorphic build, `vite-isomorphic.js`'s + `applySsrBuild` drops these plugins so the Node bundle uses the real builtins + (`path`/`fs`/`crypto`); verified the isomorphic build still prerenders cleanly. +8. ~~**`icss` mode for non-`*.module` CSS / `forceCSSModules`**~~ — **`forceCSSModules` + ported; `icss` assessed as a no-op.** The Enact `forceCSSModules` option (scope ALL + css/less/scss, not just `*.module.*`) is wired via `enactForceCSSModulesPlugin` in + `config/vite.config.js`. Vite decides module-ness only from the `.module.` filename + infix (`cssModuleRE`) with no override hook, so the plugin resolves each non-module + style import and redirects it to a **virtual `.module` id** — keeping the real + directory so LESS `@import`/`url()` still resolve, serving the real file via `load`, + and letting `generateScopedName` recover the real path for webpack-parity hashing. + Verified end-to-end (standalone): non-module CSS **and** LESS scope, export a class + map, and LESS `@import` inlines+scopes; the default path (option off) is a verified + no-op with consistent CSS↔JS hashes. The webpack `mode:'icss'` nuance — ICSS + `:export`/`:import` interop in *non-module* CSS — is **not** separately wired because + it's a no-op here: Vite already leaves non-module CSS classes global (same scoping + behavior), and `:export` blocks appear **nowhere** in @enact/limestone source. 9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (by config fixes #7 and #8 in the config-issues list above): `lessTildeImportPlugin` (LESS), `resolve.alias /^~/` (CSS), and `tildeJsonImportPlugin` (`@import-json`). @@ -292,9 +311,8 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered ``` > Status: **validated** on Sandstone and Limestone (build + serve), including -> iLib i18n runtime + locale filtering, webOS metadata, and ESLint. Not yet -> exercised: the `icss`/`forceCSSModules` nuance. Node 20+ is required for -> `require()` of the ESM-only `vite` package (validated on Node 24). +> iLib i18n runtime + locale filtering, webOS metadata, and ESLint. Node 20+ is +> required for `require()` of the ESM-only `vite` package (validated on Node 24). ## Still not ported (webpack remains the default for these) @@ -303,8 +321,6 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered (**`--isomorphic`** — gap #3 — and **framework externals** — gap #5 — are now ported; isomorphic is browser-validated end-to-end via `vite build --ssr` + per-locale prerender, see [vite-isomorphic-scope.md](vite-isomorphic-scope.md).) -- **`icss` mode / `forceCSSModules`** (gap #8): Vite treats only `*.module.*` as - CSS modules; the webpack `mode: 'icss'` nuance isn't replicated. - **Framework refinement** (post-port): building `--framework` *in a theme repo* (e.g. limestone) should include the theme's own components (webpack's `libraries.push('.')` case) — the current enumeration scans `node_modules/@enact`, diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 210e0a78..84500bb2 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -77,6 +77,7 @@ "terser-webpack-plugin": "^5.6.1", "validate-npm-package-name": "^7.0.2", "vite": "^7.3.6", + "vite-plugin-node-polyfills": "0.28.0", "webpack": "^5.108.4", "webpack-dev-server": "^5.2.5" }, @@ -19204,6 +19205,50 @@ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", "license": "MIT" }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "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/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "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/rollup-android-arm-eabi": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", @@ -21427,6 +21472,15 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "license": "MIT" }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "license": "MIT", + "dependencies": { + "resolve": "^1.17.0" + } + }, "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -22238,6 +22292,12 @@ "sha.js": "^2.4.8" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -29857,6 +29917,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -32148,6 +32214,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isomorphic-timers-promises": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", + "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -35960,6 +36035,15 @@ "lz-string": "bin/bin.js" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -36545,6 +36629,112 @@ "node": ">=18" } }, + "node_modules/node-stdlib-browser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz", + "integrity": "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==", + "license": "MIT", + "dependencies": { + "assert": "^2.0.0", + "browser-resolve": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "create-require": "^1.1.1", + "crypto-browserify": "^3.12.1", + "domain-browser": "4.22.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "isomorphic-timers-promises": "^1.0.1", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "pkg-dir": "^5.0.0", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.1", + "url": "^0.11.4", + "util": "^0.12.4", + "vm-browserify": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/node-stdlib-browser/node_modules/domain-browser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", + "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/node-stdlib-browser/node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/node-stdlib-browser/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/nopt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", @@ -42010,6 +42200,22 @@ } } }, + "node_modules/vite-plugin-node-polyfills": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.28.0.tgz", + "integrity": "sha512-NXct/ci2ef4fRyCfTb8fk2HmR80Rv7icLd+cRH41TnUugDzdKMFKqFPpZYCFUInZMMem9bkLv5pkq02+7Xu7+w==", + "license": "MIT", + "dependencies": { + "@rollup/plugin-inject": "^5.0.5", + "node-stdlib-browser": "^1.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/davidmyersdev" + }, + "peerDependencies": { + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/package.json b/package.json index b57d621f..5293ba98 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "terser-webpack-plugin": "^5.6.1", "validate-npm-package-name": "^7.0.2", "vite": "^7.3.6", + "vite-plugin-node-polyfills": "0.28.0", "webpack": "^5.108.4", "webpack-dev-server": "^5.2.5" }, From c35c99f9719fcd18c914f343445e524a8b540744 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Wed, 15 Jul 2026 14:30:55 +0300 Subject: [PATCH 12/20] addec configuration for snapshot --- commands/pack.js | 61 ++++++++++++----- config/vite.config.js | 9 +-- docs/vite-migration.md | 145 +++++++++++++++++++++++++++++++++-------- 3 files changed, 169 insertions(+), 46 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index b17e4342..bbe6fa8a 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -22,6 +22,7 @@ const webpack = require('webpack'); const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils'); const viteFw = require('@enact/dev-utils/mixins/vite-framework'); const viteIso = require('@enact/dev-utils/mixins/vite-isomorphic'); +const viteSnap = require('@enact/dev-utils/mixins/vite-snapshot'); const {parseLocales} = require('@enact/dev-utils/plugins/PrerenderPlugin/parse-locales'); const {isViteBundler} = require('./vite-utils'); @@ -212,13 +213,18 @@ async function viteFramework (opts) { const appRequire = createRequire(path.join(app.context, 'package.json')); const specs = viteFw.enumerateSpecifiers(app.context, {polyfill: opts['externals-polyfill']}); + // Building --framework inside a theme repo: also include the theme's own components + // (webpack's `libraries.push('.')`), which aren't in the repo's node_modules/@enact. + const self = viteFw.enumerateSelfSpecs(app.context); + const allSpecs = self ? specs.concat(self.specs.filter(s => !specs.includes(s))) : specs; + const selfSet = self ? new Set(self.specs) : null; const srcDir = path.join(app.context, '.enact-framework-src'); - const {input, names} = viteFw.writeWrappers(specs, srcDir, appRequire); + const {input, names} = viteFw.writeWrappers(allSpecs, srcDir, appRequire, selfSet); const configFactory = require('../config/vite.config'); const config = configFactory(opts.production ? 'production' : 'development', !opts.linting); const outDir = opts.output ? path.resolve(opts.output) : path.resolve('./dist'); - viteFw.applyFramework(config, {input, outDir}); + viteFw.applyFramework(config, {input, outDir, selfAlias: self && {find: self.name, replacement: self.root}}); // --no-minify/--verbose/--stats still apply to the framework build. mixins.applyVite(config, opts); @@ -246,7 +252,11 @@ async function viteIsomorphic (opts) { const serverEntry = path.resolve(opts.entry || app.entry || path.join(app.context, 'src/index.js')); const ssrOut = path.join(app.context, '.enact-ssr'); - console.log(`Creating an isomorphic production build (${locales.length} locale(s))...`); + console.log( + opts.snapshot + ? `Creating a V8 snapshot production build (${locales.length} locale(s))...` + : `Creating an isomorphic production build (${locales.length} locale(s))...` + ); // 1) Client build (isomorphic ON → the app entry uses hydrateRoot). const clientConfig = configFactory( @@ -269,6 +279,12 @@ async function viteIsomorphic (opts) { manifest = viteFw.readManifest(path.resolve(opts.externals)); viteFw.applyExternals(clientConfig, collected, manifest, {polyfill: opts['externals-polyfill']}); } + // --snapshot: build the client as a self-contained UMD bundle (App global) that mksnapshot + // can snapshot. Implies isomorphic; --snapshot + --externals is unsupported (the snapshot + // must embed @enact, not externalize it), matching webpack (`opts.snapshot && !opts.externals`). + if (opts.snapshot && !opts.externals) { + viteSnap.applySnapshotBuild(clientConfig, {context: app.context, appEntry: serverEntry}); + } mixins.applyVite(clientConfig, opts); await viteBuildApi(clientConfig); @@ -308,34 +324,49 @@ async function viteIsomorphic (opts) { }); // 4) Assemble HTML + locale-map, then (5) webOS per-locale appinfo. - const {localeMap} = viteIso.assemble({outDir, locales, prerenders, attr, aliasOf, screenTypes: app.screenTypes || []}); + const {localeMap} = viteIso.assemble({ + outDir, locales, prerenders, attr, aliasOf, + screenTypes: app.screenTypes || [], + snapshot: !!opts.snapshot + }); viteIso.writeAppinfo({outDir, locales, localeMap}); fs.removeSync(ssrOut); console.log( chalk.cyan(`Prerendered ${locales.length} locale(s) into ${prerenders.length} variant(s).`) ); + + // 6) V8 snapshot: run mksnapshot against the UMD main.js and record the blob in appinfo. + // Requires the webOS `V8_MKSNAPSHOT` toolchain; without it the build still succeeds and + // the startup script falls back to loading main.js (classic <script>) at runtime. + if (opts.snapshot && !opts.externals) { + const result = viteSnap.runMkSnapshot({outDir}); + if (result.ok) { + viteSnap.writeSnapshotAppinfo({outDir, blob: result.blob}); + console.log(chalk.green(`Generated V8 snapshot blob (${result.blob}) and tagged appinfo.json.`)); + } else { + console.log(chalk.yellow(`V8 snapshot blob not generated: ${result.error.message.split('\n')[0]}`)); + console.log(chalk.yellow('Set V8_MKSNAPSHOT to the webOS mksnapshot binary to emit the blob; the app runs without it.')); + } + } else if (opts.snapshot && opts.externals) { + console.log(chalk.yellow('NOTICE: --snapshot with --externals is not supported; the snapshot must embed @enact. Built without a snapshot.')); + } + console.log(chalk.green('Compiled successfully.')); } // Experimental Vite build path. Mirrors the webpack `build`/`watch` behavior but -// drives Vite's JS API. `--snapshot` is not ported (reported + skipped). +// drives Vite's JS API. // // Wired here: --framework (shared bundle), --externals (import-map externalization), -// --isomorphic (prerendering), and via mixins.applyVite: --no-minify, --verbose, --stats. +// --isomorphic (prerendering), --snapshot (V8 snapshot), and via mixins.applyVite: +// --no-minify, --verbose, --stats. async function viteBuild (opts) { const {build: viteBuildApi} = require('vite'); if (opts.framework) return viteFramework(opts); - if (opts.isomorphic) return viteIsomorphic(opts); - - ['snapshot'].forEach(flag => { - if (opts[flag]) { - console.log( - chalk.yellow(`NOTICE: --${flag} is not yet supported by the Vite bundler and will be ignored.`) - ); - } - }); + // --snapshot implies --isomorphic (the snapshot embeds the prerendered app). + if (opts.snapshot || opts.isomorphic) return viteIsomorphic(opts); const configFactory = require('../config/vite.config'); const config = configFactory( diff --git a/config/vite.config.js b/config/vite.config.js index d1e505f3..6ff31054 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -18,10 +18,11 @@ * - cssModuleIdent (getLocalIdent) -> via `css.modules.generateScopedName` * - eslint-config-enact -> via the inline `enact-eslint` plugin * - * See `docs/vite-migration.md` for the feasibility analysis. The webpack-only - * features that remain unported (and for which `pack --vite` prints a "not - * supported, ignored" notice) are: isomorphic prerendering (see - * `docs/vite-isomorphic-scope.md`), V8 snapshot, and framework externals. + * See `docs/vite-migration.md` for the feasibility analysis. Isomorphic prerendering + * (see `docs/vite-isomorphic-scope.md`), framework externals, and the V8 snapshot + * (`mixins/vite-snapshot.js`) are all ported. The only build-time combination that + * prints a "not supported" notice is `--snapshot --externals` (the snapshot must + * embed `@enact`, matching webpack). */ const fs = require('fs'); diff --git a/docs/vite-migration.md b/docs/vite-migration.md index b9307e88..a582045b 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -5,17 +5,19 @@ replaced with Vite in `@enact/cli`, then apply a new Vite configuration. ## Verdict -**Yes for the everyday browser dev/build workflow (validated end-to-end); three -webOS-packaging features remain on webpack.** Vite (Rollup + esbuild) cleanly +**Yes for the everyday browser dev/build workflow (validated end-to-end); every +webOS-packaging feature is now ported.** Vite (Rollup + esbuild) cleanly covers `enact serve` / `enact pack` for a browser app and brings much faster cold starts and HMR. Several webOS-specific features that started as webpack compiler plugins have since been **re-authored as Vite/Rollup plugins** in `@enact/dev-utils` and validated: **iLib i18n runtime + locale filtering** (`ViteILibPlugin`), **webOS metadata** (`ViteWebOSMetaPlugin`), plus the HTML document (`ViteHtmlPlugin`) and -**ESLint**. **`--isomorphic` prerendering and framework externals are also now ported and -browser-validated** (`mixins/vite-isomorphic.js`, `mixins/vite-framework.js`). Only -**`--snapshot`** (V8) still requires webpack — it needs the webOS `V8_MKSNAPSHOT` toolchain, -unavailable here — and `pack --vite` prints a "not supported, ignored" notice for it. +**ESLint**. **`--isomorphic` prerendering and framework externals are also ported and +browser-validated** (`mixins/vite-isomorphic.js`, `mixins/vite-framework.js`). +**`--snapshot`** (V8) is now ported too (`mixins/vite-snapshot.js`) and locally validated +as snapshot-safe — its only remaining step is a build + install on a webOS board with the +firmware-matched `V8_MKSNAPSHOT` toolchain (unavailable in this environment; see the +"Testing `--snapshot` on a webOS board" section). The Vite config lives at [`config/vite.config.js`](../config/vite.config.js); it mirrors the `webpack.config.js` factory signature and reuses the existing Enact @@ -204,11 +206,50 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): `@enact/i18n` — locale class deferred to the client — and identical to webpack's isomorphic output, including with `--externals`; `-p` strips them. Details in the scope doc.) Full findings/phases in [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). -4. **`SnapshotPlugin`** — emits a V8 snapshot blob (`--snapshot`). *Not portable - here.* Requires the webOS `V8_MKSNAPSHOT` toolchain (absent in this - environment, so unverifiable) and is coupled to the webpack module runtime + - injected snapshot-helper entries. A Rollup equivalent would need its own - snapshot-safe bootstrap; deferred until the mksnapshot toolchain is available. +4. ~~**`SnapshotPlugin`**~~ — **ported (build-complete; on-device validation pending + the toolchain).** `--snapshot` (which implies `--isomorphic`) now builds through + `mixins/vite-snapshot.js`: the client build becomes a **self-contained UMD** `main.js` + (`output.format:'umd'`, `name:'App'`, `preserveEntrySignatures:'strict'`, + `inlineDynamicImports`) with a `global` banner for the bare-V8 context, so the app's + default export is exposed as the `App` global — mirroring webpack's + `output.library='App'`/`libraryTarget='umd'`. The snapshot helpers are reimplemented in + **ESM** ([`snapshot-helper-esm.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-helper-esm.js) + + [`snapshot-mock.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-mock.js), reusing + the bundler-agnostic `mock-window.js` + `@enact/core/snapshot`): import order deterministically + installs the mock window before `react-dom/client` loads (Rollup hoists CJS requires, so the + original CJS helper's in-line ordering can't be reproduced), and `global.updateEnvironment` + is defined for the on-device window rebind. A resolver redirects `react-dom/client` → the + facade and no-ops absent optional deps (`fbjs` is gone in React 19; a theme may lack + `internal/$L`). After the isomorphic prerender/assembly (startup script kept **classic**, since + `main.js` is UMD), `mksnapshot` (`V8_MKSNAPSHOT`) runs against `main.js` to emit + `snapshot_blob.bin` and tag `appinfo.json` `v8SnapshotFile`. + **Validated end-to-end with a real toolchain.** Against `mksnapshot` (V8) the UMD bundle + produces a genuine, non-zero startup blob (qa-a11y: **4.6 MB**, in line with the ~4.3 MB + webpack reference). The syntax must parse in the target board's V8; the app's browserslist + drives the output by default, and `V8_SNAPSHOT_TARGET` force-lowers it for a much older + firmware than the app targets. `--snapshot --externals` is unsupported (the snapshot must + embed `@enact`), matching webpack. The blob's V8 must match the firmware — a mismatched + `mksnapshot` is rejected/unparseable (see the matrix below). + + **core-js in the snapshot — parity with webpack, verified.** core-js is included by default + (as in the webpack path). Its WeakMap-based internal state serializes fine on a **modern** + snapshot V8, but a **very old** one (~Chrome 53) can't serialize a WeakMap-with-entries — + `mksnapshot` throws `illegal access` → 0-byte blob. This is a **core-js-3 + old-V8 + limitation, not a bundler difference**: measured on the *same* `mksnapshot.53` against + qa-a11y built at `chrome 53`: + + | Bundle (chrome 53 target, core-js 3.22.8) | Result | + | --- | --- | + | **webpack** + core-js | `illegal access` → 0-byte blob | + | **Vite** + core-js | `illegal access` → 0-byte blob (identical) | + | **Vite**, `ENACT_SNAPSHOT_NO_COREJS=1` | ✅ 4.6 MB blob | + + So webpack and Vite behave identically; Vite additionally offers `ENACT_SNAPSHOT_NO_COREJS` + to still emit a blob (minus runtime builtin polyfills) on such old firmware, where webpack + emits nothing. On a firmware-matched **modern** `mksnapshot` (e.g. Chrome 132) neither the + `V8_SNAPSHOT_TARGET` lowering nor the no-core-js opt-out is needed — the default build (app + target + core-js) is correct for both bundlers. **Not validated here:** the blob on the + actual firmware (needs that firmware's `mksnapshot`) + on-device hydration. 5. ~~**Framework externals**~~ — **ported** (`mixins/vite-framework.js` + `pack.js` `--framework`/`--externals`). Webpack's DLL maps deep module requests to IDs in a prebuilt bundle via a manifest; the Vite analog is a shared framework ESM @@ -314,25 +355,75 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered > iLib i18n runtime + locale filtering, webOS metadata, and ESLint. Node 20+ is > required for `require()` of the ESM-only `vite` package (validated on Node 24). -## Still not ported (webpack remains the default for these) - -- **`--snapshot`** (gap #4) — needs the webOS `V8_MKSNAPSHOT` toolchain (absent here, - so unverifiable); the Vite path prints a "not yet supported, ignored" notice. - (**`--isomorphic`** — gap #3 — and **framework externals** — gap #5 — are now ported; - isomorphic is browser-validated end-to-end via `vite build --ssr` + per-locale prerender, - see [vite-isomorphic-scope.md](vite-isomorphic-scope.md).) -- **Framework refinement** (post-port): building `--framework` *in a theme repo* - (e.g. limestone) should include the theme's own components (webpack's - `libraries.push('.')` case) — the current enumeration scans `node_modules/@enact`, - so build the framework where all `@enact` incl. the theme are installed (e.g. an - app/sample context). (`--externals-polyfill` — move core-js into the framework — **is** - wired: `pack --framework --externals-polyfill` folds core-js into the framework, and - `pack --externals=<path> --externals-polyfill` delegates it out of the app.) +## Ported, pending on-device validation + +- **`--snapshot`** (gap #4) — **build-complete and locally validated** (the UMD `main.js` + evaluates snapshot-safe in a bare V8 and exposes the `App`/`updateEnvironment`/`ReactDOMClient` + globals); only the actual `mksnapshot` blob + on-device hydration remain, which need the + firmware-specific `V8_MKSNAPSHOT` toolchain and a webOS board. See gap #4 above and the + **Testing `--snapshot` on a webOS board** section below. + +## Everything else is ported + +- ~~**Framework self-inclusion in a theme repo**~~ — **ported.** Building `--framework` + *inside* a theme repo (e.g. limestone) now includes the theme's own components, mirroring + webpack's `libraries.push('.')`. `mixins/vite-framework.js` `enumerateSelfSpecs(context)` + detects a `@enact/*` theme package (has `ThemeDecorator`/`MoonstoneDecorator`, or is + `@enact/i18n`) and enumerates its own component subpaths as `@enact/<theme>/<component>` + specifiers; `applyFramework` adds a `resolve.alias` (`@enact/<theme>` → repo root, covering + transitive self-references) and extends `commonjsOptions` to the repo root so the theme's + own CJS-in-source (e.g. a `module.exports` `fontGenerator`) interops. Verified on limestone: + a repo-root `--framework` build emits **138 specifiers = 56 own components + 76 node_modules + `@enact` + react/ilib**, with `enact.css`. The change is gated on theme-repo detection, so a + sample/app-context build (the Jenkins path) is unaffected. (`--externals-polyfill` — move + core-js into the framework — is also wired: `pack --framework --externals-polyfill` folds + core-js in, and `pack --externals=<path> --externals-polyfill` delegates it out of the app.) ## Recommendation Adopt Vite behind a feature flag for the **browser dev/build** path first (biggest DX win, lowest risk) — validated end-to-end including i18n runtime, locale filtering, webOS metadata, and ESLint. Beyond that, **`--isomorphic` and framework externals are -now ported and validated** too, leaving only **`--snapshot`** on webpack (it needs the -webOS `V8_MKSNAPSHOT` toolchain to build/validate — unavailable here). +now ported and validated** too. **`--snapshot`** is now ported and locally validated +(snapshot-safe UMD bundle); it just needs a final on-device pass on a webOS board with +the `V8_MKSNAPSHOT` toolchain — see below. + +## Testing `--snapshot` on a webOS board + +The snapshot blob is a **build-time** artifact and requires a `mksnapshot` binary whose +V8 version **matches the target firmware's Chrome** (from the same webOS SDK/NDK release) — +e.g. a Chrome-132 board needs that firmware's `mksnapshot`, **not** an arbitrary/old one. A +mismatched binary either can't parse the modern output or produces a blob the board ignores +at load. `mksnapshot` is a **Linux** tool (32-bit for older releases); on Windows run it via +WSL or a Linux build machine (the guide's "doesn't support Windows OS" note). Steps: + +1. **Build with the firmware-matched toolchain**: + ``` + export V8_MKSNAPSHOT=/path/to/mksnapshot # matches the board's Chrome + ENACT_BUNDLER=vite enact pack -p --snapshot # add -l en-US,ko-KR for multi-locale + ``` + Expect: `Generated V8 snapshot blob (snapshot_blob.bin) and tagged appinfo.json.` + Verify `dist/snapshot_blob.bin` is **non-zero** and `dist/appinfo.json` has + `"v8SnapshotFile": "snapshot_blob.bin"`. (Without `V8_MKSNAPSHOT` the build still + succeeds and prints a skip notice; the app runs, just without the snapshot.) + *Old-firmware knobs (rarely needed):* `V8_SNAPSHOT_TARGET=chrome53` force-lowers the + syntax for a V8 older than the app targets, and `ENACT_SNAPSHOT_NO_COREJS=1` drops core-js + when that old V8 can't serialize its WeakMap state (see gap #4 — webpack hits the same + wall). On a modern firmware-matched `mksnapshot`, use neither. +2. **Package + install (developer mode, not hosted)** — the snapshot is loaded by WAM + from the app's local install dir, so it must be a packaged IPK, not a served URL: + ``` + ares-package dist + ares-install ./com.*.ipk # Developer Mode enabled on the board + ``` +3. **Confirm the snapshot is actually used** — launch the app and check it renders, then + confirm WAM loaded the blob (via WAM logs / the `--profile-deserialization` output, or a + measurable cold-start improvement) rather than silently falling back — a `mksnapshot` + whose V8 doesn't match the firmware is ignored at load, so "it rendered" alone isn't proof. + +If step 1's blob is 0-byte, check `mksnapshot`'s stderr: `Unexpected token` means the +binary is **older** than the app's syntax target (use the firmware-matched one, or +`V8_SNAPSHOT_TARGET`); `illegal access` in module init is the core-js/WeakMap case on very +old V8 (`ENACT_SNAPSHOT_NO_COREJS=1`, same limitation as webpack); a `window`/`document` +`ReferenceError` means app/framework code touched the DOM at snapshot time (the local +bare-V8 `vm` check guards this and is clean for qa-a11y). From c28cf2b70810a89c527b5fa1acc7885b2473b588 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Wed, 15 Jul 2026 16:14:21 +0300 Subject: [PATCH 13/20] addec configuration for snapshot --- commands/pack.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/pack.js b/commands/pack.js index bbe6fa8a..e22b9db8 100755 --- a/commands/pack.js +++ b/commands/pack.js @@ -120,7 +120,7 @@ function details (err, stats, output) { console.log( chalk.yellow( '\nTreating warnings as errors because process.env.CI = true. \n' + - 'Most CI servers set it automatically.\n' + 'Most CI servers set it automatically.\n' ) ); return new Error(filteredWarnings.join('\n\n')); @@ -137,7 +137,7 @@ function details (err, stats, output) { console.log( chalk.yellow( 'NOTICE: This build contains debugging functionality and may run' + - ' slower than in production mode.' + ' slower than in production mode.' ) ); } @@ -193,7 +193,7 @@ function printErrorDetails (err, handler) { console.log( chalk.yellow( 'Compiled with the following type errors (you may want to check ' + - 'these before deploying your app):\n' + 'these before deploying your app):\n' ) ); printBuildError(err); @@ -253,9 +253,9 @@ async function viteIsomorphic (opts) { const ssrOut = path.join(app.context, '.enact-ssr'); console.log( - opts.snapshot - ? `Creating a V8 snapshot production build (${locales.length} locale(s))...` - : `Creating an isomorphic production build (${locales.length} locale(s))...` + opts.snapshot ? + `Creating a V8 snapshot production build (${locales.length} locale(s))...` : + `Creating an isomorphic production build (${locales.length} locale(s))...` ); // 1) Client build (isomorphic ON → the app entry uses hydrateRoot). From 7bda44ac723c65833884f780d3e0d834fe891210 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Wed, 15 Jul 2026 17:14:25 +0300 Subject: [PATCH 14/20] addec configuration for snapshot --- config/vite.config.js | 64 +++++++++++++++++++++++++++++++++++++++++- docs/vite-migration.md | 53 +++++++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/config/vite.config.js b/config/vite.config.js index 6ff31054..b0190d8c 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -163,6 +163,66 @@ function enactForceCSSModulesPlugin (virtualToReal) { }; } +// True for a style file that Vite will NOT treat as a CSS module: a css/less/scss +// without the `.module.` infix, imported normally (not `?raw`/`?url`/`?inline`, which +// Vite already gives a default export of their own). +function isPlainStyleId (id) { + const [file, query = ''] = String(id).split('?'); + if (!FORCE_CSS_STYLE_RE.test(file) || FORCE_CSS_MODULE_RE.test(file)) return false; + return !/(?:^|&)(?:raw|url|inline)(?:&|=|$)/.test(query); +} + +// ICSS interop for non-`*.module.*` CSS — the webpack `modules:{mode:'icss'}` behaviour. +// Enact apps conventionally do `import css from './App.less'` on a PLAIN (non-module) +// stylesheet and hand the map to `kind({styles:{css, className:'app'}})`. Under webpack, +// css-loader in `icss` mode leaves the class names global but still emits a default +// export (the ICSS `:export` locals, usually `{}`), so the import resolves and +// `classnames/bind` falls back to the literal global class name. Vite emits no default +// export for plain CSS at build time, so the same import is a hard error: +// "default" is not exported by "src/App/App.less" +// These two plugins restore parity WITHOUT scoping anything (scoping stays webpack- +// identical: plain CSS remains global; only `forceCSSModules` scopes it): +// 1. `enact-icss-extract` (normal order → runs after vite:css has compiled LESS/SCSS +// to CSS, before vite:css-post builds the JS proxy): lifts `:export {…}` blocks out +// of the compiled CSS into a locals map, and strips them from the emitted CSS +// (css-loader does the same; `:export` is not valid CSS for a browser). +// 2. `enact-icss-default-export` (post order → runs after vite:css-post): appends +// `export default <locals>` when the proxy has none. Anything that already has a +// default export (dev's CSS-string proxy, `?inline`, `?url`, `?raw`) is left alone. +function enactICSSInteropPlugins () { + const icssExports = new Map(); + const key = id => String(id).split('?')[0]; + return [ + { + name: 'enact-icss-extract', + transform (code, id) { + if (!isPlainStyleId(id)) return null; + const locals = {}; + const stripped = code.replace(/:export\s*\{([^}]*)\}/g, (match, body) => { + body.split(';').forEach(decl => { + const at = decl.indexOf(':'); + if (at === -1) return; + const name = decl.slice(0, at).trim(); + if (name) locals[name] = decl.slice(at + 1).trim(); + }); + return ''; + }); + icssExports.set(key(id), locals); + return stripped === code ? null : {code: stripped, map: {mappings: ''}}; + } + }, + { + name: 'enact-icss-default-export', + enforce: 'post', + transform (code, id) { + if (!isPlainStyleId(id) || /(?:^|[;\s])export\s+default\s/.test(code)) return null; + const locals = icssExports.get(key(id)) || {}; + return {code: code + '\nexport default ' + JSON.stringify(locals) + ';\n', map: {mappings: ''}}; + } + } + ]; +} + // Non-browser iLib platform loaders (`./lib/ilib-qt|rhino|ringo|node|….js`) and // their `*Loader.js` helpers. iLib selects these via runtime platform detection; // the browser branch never reaches them, but bundlers try (and fail) to resolve @@ -474,7 +534,9 @@ module.exports = function ( // Rewrite webpack's `module.hot` in app source before other transforms. enactNeutralizeWebpackHmrPlugin(), // `forceCSSModules`: scope ALL css/less/scss as CSS modules (not just *.module.*). - app.forceCSSModules && enactForceCSSModulesPlugin(forcedCSSVirtual), + // Otherwise plain css/less/scss stays global (webpack `mode:'icss'`) and only + // needs the ICSS default export so `import css from './App.less'` resolves. + app.forceCSSModules ? enactForceCSSModulesPlugin(forcedCSSVirtual) : enactICSSInteropPlugins(), // Node builtin polyfills for the browser (webpack: node-polyfill-webpack-plugin // with additionalAliases console/domain/process/stream). `global` is already // supplied by ViteHtmlPlugin's head shim (R1), so only inject Buffer/process. diff --git a/docs/vite-migration.md b/docs/vite-migration.md index a582045b..048f48af 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -274,20 +274,45 @@ not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): them otherwise. For the SSR/isomorphic build, `vite-isomorphic.js`'s `applySsrBuild` drops these plugins so the Node bundle uses the real builtins (`path`/`fs`/`crypto`); verified the isomorphic build still prerenders cleanly. -8. ~~**`icss` mode for non-`*.module` CSS / `forceCSSModules`**~~ — **`forceCSSModules` - ported; `icss` assessed as a no-op.** The Enact `forceCSSModules` option (scope ALL - css/less/scss, not just `*.module.*`) is wired via `enactForceCSSModulesPlugin` in - `config/vite.config.js`. Vite decides module-ness only from the `.module.` filename - infix (`cssModuleRE`) with no override hook, so the plugin resolves each non-module - style import and redirects it to a **virtual `.module` id** — keeping the real - directory so LESS `@import`/`url()` still resolve, serving the real file via `load`, - and letting `generateScopedName` recover the real path for webpack-parity hashing. - Verified end-to-end (standalone): non-module CSS **and** LESS scope, export a class - map, and LESS `@import` inlines+scopes; the default path (option off) is a verified - no-op with consistent CSS↔JS hashes. The webpack `mode:'icss'` nuance — ICSS - `:export`/`:import` interop in *non-module* CSS — is **not** separately wired because - it's a no-op here: Vite already leaves non-module CSS classes global (same scoping - behavior), and `:export` blocks appear **nowhere** in @enact/limestone source. +8. ~~**`icss` mode for non-`*.module` CSS / `forceCSSModules`**~~ — **both ported.** + The Enact `forceCSSModules` option (scope ALL css/less/scss, not just `*.module.*`) + is wired via `enactForceCSSModulesPlugin` in `config/vite.config.js`. Vite decides + module-ness only from the `.module.` filename infix (`cssModuleRE`) with no override + hook, so the plugin resolves each non-module style import and redirects it to a + **virtual `.module` id** — keeping the real directory so LESS `@import`/`url()` still + resolve, serving the real file via `load`, and letting `generateScopedName` recover + the real path for webpack-parity hashing. Verified end-to-end: non-module CSS **and** + LESS scope and export a class map, matching webpack's ident (`src_App_App_app__<hash>`). + + The webpack `mode:'icss'` path (the **default**, option off) was initially assessed as + a no-op on the grounds that Vite already leaves non-module CSS global and `:export` + is unused in @enact/limestone. **That assessment was wrong** and is now fixed. Scoping + is indeed identical, but css-loader in `icss` mode still emits a **default export** + (the ICSS `:export` locals, usually `{}`), whereas Vite emits *no* default export for + plain CSS at build time. So the classic Enact idiom on a **non-module** stylesheet — + ```js + import css from './App.less'; // plain .less, global classes + kind({styles: {css, className: 'app'}}); + ``` + — is a hard Vite build error (`"default" is not exported by "src/App/App.less"`), + even though webpack builds it fine (`classnames/bind` just falls back to the literal + global class name). `limestone/samples/qa-i18n` hits exactly this; `qa-a11y` does not, + because it uses `App.module.less`. + + Parity is restored by `enactICSSInteropPlugins()` — **without scoping anything**: + - `enact-icss-extract` (normal order → after `vite:css` compiles LESS/SCSS, before + `vite:css-post` builds the JS proxy) lifts `:export {…}` blocks into a locals map + and strips them from the emitted CSS, as css-loader does. + - `enact-icss-default-export` (`enforce:'post'` → after `vite:css-post`) appends + `export default <locals>` when the proxy has none. Modules that already have a + default export (dev's CSS-string proxy, `?inline`/`?url`/`?raw`) are left alone. + + Verified against webpack on `qa-i18n` with a `.app{color:#123456}` rule plus an + `:export{brandColor:#ff0000}` block — both bundlers emit the identical + `styles:{css:{brandColor:"#ff0000"},className:"app"}`, keep `.app` **global** + (unscoped), and strip `:export` from the CSS. `*.module.*` files are untouched + (`qa-a11y` still scopes 623/660 classes; the rest are the deliberately global + `enact-locale-*`). 9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (by config fixes #7 and #8 in the config-issues list above): `lessTildeImportPlugin` (LESS), `resolve.alias /^~/` (CSS), and `tildeJsonImportPlugin` (`@import-json`). From d834f9ee5ce8e7f31fa90a7c93b67450d58131d6 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Fri, 17 Jul 2026 12:31:37 +0300 Subject: [PATCH 15/20] added changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f485395..5c310da2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## unreleased + +### pack, serve, eject + +* Added experimental support for the Vite bundler, enabled with the `--vite` option or the `ENACT_BUNDLER=vite` environment variable. + ## 7.3.3 (July 7, 2026) ### transpile From 4dd073075201b7d0b1246e939f9ecceb7f8d5ef7 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Fri, 17 Jul 2026 13:52:13 +0300 Subject: [PATCH 16/20] added changelog --- docs/vite-migration.md | 116 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/docs/vite-migration.md b/docs/vite-migration.md index 048f48af..cea16ae4 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -1,13 +1,8 @@ -# Replacing webpack with Vite in `@enact/cli`: feasibility & status - -**Task:** Look through React tooling libraries and assess whether webpack can be -replaced with Vite in `@enact/cli`, then apply a new Vite configuration. +# Replacing webpack with Vite in `@enact/cli`: feasibility and status ## Verdict -**Yes for the everyday browser dev/build workflow (validated end-to-end); every -webOS-packaging feature is now ported.** Vite (Rollup + esbuild) cleanly -covers `enact serve` / `enact pack` for a browser app and brings much faster cold +Vite (Rollup + esbuild) cleanly covers `enact serve` / `enact pack` for a browser app and brings much faster cold starts and HMR. Several webOS-specific features that started as webpack compiler plugins have since been **re-authored as Vite/Rollup plugins** in `@enact/dev-utils` and validated: **iLib i18n runtime + locale filtering** (`ViteILibPlugin`), **webOS @@ -404,10 +399,115 @@ enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered core-js into the framework — is also wired: `pack --framework --externals-polyfill` folds core-js in, and `pack --externals=<path> --externals-polyfill` delegates it out of the app.) +## Measured: webpack vs Vite, command by command + +Real measurements, not estimates. **App:** `limestone/samples/qa-a11y`. **Machine:** 22 +logical cores / 31.5 GB RAM, Windows. Both bundlers run the same `enact pack`/`enact serve` +CLI with `NODE_OPTIONS=--max-old-space-size=8192` (webpack's `--framework` build OOMs at the +default heap, so the larger heap is given to *both* for fairness). + +**Methodology** (metric set follows [rstackjs/build-tools-performance](https://github.com/rstackjs/build-tools-performance), +the reference bundler benchmark: startup, build no-cache/with-cache, memory, output size, +gzipped size): + +- **No cache** — `<app>/node_modules/.cache` (webpack's `cache:{type:'filesystem'}` + the + babel-loader cache) and `<app>/node_modules/.vite` (Vite's dep-optimizer cache) are deleted + before the run. This matters: webpack has a persistent build cache and Vite has none, so + *not* clearing it silently hands webpack a warm cache and makes the comparison meaningless. +- **With cache** — an immediate second run. Both runs build into an **empty** output dir, so + the cache is the only variable (otherwise the second run also pays to replace ~6.8k files + and reads as *slower* than the first). +- **Peak RSS** — the build process's own `process.resourceUsage().maxRSS` (exact, zero + overhead). Covers the main build process, not parallel minifier workers. +- **Gzipped** — gzip(level 9) of all emitted JS+CSS+HTML: a network-transfer proxy. +- Single run per cell (not 3-run averaged); treat differences under ~5% as noise. + +### Build & startup time + +| Command | webpack (no cache) | Vite (no cache) | webpack (cached) | Vite (cached) | +|---|---|---|---|---| +| `pack` (dev) | **33.8s** | 44.8s | **28.5s** | 40.8s | +| `pack -p` | **40.3s** | 48.0s | **36.3s** | 44.4s | +| `pack -p --no-minify` ¹ | 39.5s | 44.1s | — | — | +| `pack -p --content-hash` | **40.9s** | 47.1s | — | — | +| `pack -p -i` (isomorphic) | **53.5s** | 81.5s | — | — | +| `pack -p -i -l en-US,ko-KR` ² | **54.2s** | 73.5s | — | — | +| `pack -p --snapshot` | **54.9s** | 78.7s | — | — | +| `serve` (dev server ready) | 44.8s | **10.5s** | 38.6s | **7.5s** | + +### Peak memory (RSS) + +| Command | webpack | Vite | +|---|---|---| +| `pack` (dev) | **1162 MB** | 2355 MB | +| `pack -p` | **1310 MB** | 1854 MB | +| `pack -p -i` | **1334 MB** | 1920 MB | +| `pack -p --snapshot` | **1646 MB** | 2036 MB | +| `serve` | 1241 MB | **449 MB** | + +### Output size + +| Command | webpack files / total / main.js / gzip | Vite files / total / main.js / gzip | +|---|---|---| +| `pack` (dev) | 6779 / 71.8 MB / 5679 KB / 1011 KB | 6778 / 72.2 MB / **4283 KB** / **877 KB** | +| `pack -p` | 6778 / 61.0 MB / **1108 KB** / **385 KB** | 6777 / 61.8 MB / 1263 KB / 448 KB | +| `pack -p --no-minify` ¹ | 6778 / 61.2 MB / 1108 KB / 390 KB | 6777 / 63.8 MB / 3226 KB / 678 KB | +| `pack -p -i` | 6778 / 61.1 MB / **1108 KB** / **391 KB** | 6777 / 61.9 MB / 1263 KB / 453 KB | +| `pack -p -i -l en-US,ko-KR` ² | 6782 / 61.1 MB / 1108 KB / 392 KB | **2013 / 19.4 MB** / 1263 KB / 455 KB | +| `pack -p --snapshot` | 6778 / 61.1 MB / **1115 KB** / **393 KB** | 6777 / 61.9 MB / 1275 KB / 454 KB | + +Total output is dominated by iLib locale JSON (~60 MB); `main.js`/gzip are the figures that +track bundling quality. + +### What the numbers say + +- **Dev server is Vite's decisive win**: ready in **10.5s vs 44.8s** cold (**4.3×**) and + **7.5s vs 38.6s** warm (**5.1×**), using **449 MB vs 1241 MB** (**2.8× less**). This is the + day-to-day feedback loop and the main reason to migrate. +- **webpack is currently faster for production builds** — 16–19% on plain `-p`, and **~1.5×** + on `-i`/`--snapshot`. The isomorphic gap is structural: the Vite path runs **two** builds + (client + a real `vite build --ssr`), where webpack prerenders from a single compilation. +- **webpack's persistent cache is worth 10–16%**; Vite's dep cache buys 7–9% on builds but + ~30% on dev startup. +- **Vite uses ~40–60% more memory** to build (but ~⅓ as much to serve). +- **webpack's production bundle is smaller**: `main.js` 1108 KB vs 1263 KB (+14%), gzipped + 385 KB vs 448 KB (**+16%**). Worth a follow-up (CJS interop / tree-shaking differences); + it is the one clear regression in the Vite output. Note Vite's **dev** bundle is *smaller* + (4283 KB vs 5679 KB). + +¹ **`--no-minify` is not comparable — the webpack flag is a silent no-op.** webpack's +`main.js` is byte-identical with and without it (1108 KB, gzip 314 KB), while Vite's grows +2.6× as expected. Cause: `terser-webpack-plugin@5.6.1` normalizes constructor options into +`options.minimizer.options`, but `dev-utils/mixins/unmangled.js` writes to +`options.terserOptions` — a key v5 never reads, so `mangle` stays `{safari10:true}`. This is +a pre-existing webpack-path bug, unrelated to the Vite port. + +² **`-l` means different things in the two bundlers.** `locales` appears **nowhere** in +webpack's `ILibPlugin`: there, `-l` scopes *prerendering only* (matching `pack --help`: +"Locales for isomorphic mode") and the full iLib tree always ships. `ViteILibPlugin` +*additionally* trims the emitted locale data — hence **2013 files / 19.4 MB vs 6782 / 61.1 MB** +(**68% smaller**). Attractive for a TV app, but a **behavioral deviation**: a webpack build +made with `-l en-US,ko-KR` can still switch to any locale at runtime, whereas the Vite build +ships no data for unlisted locales and would fall back to unlocalized output. (Shared +non-locale data such as `localematch.json` is kept, so it degrades rather than crashes.) +Decide deliberately: gate it behind its own flag, or adopt it as intentional. + +### Not measured + +`pack -p --framework` and `pack -p --externals` are **excluded**. The webpack `--framework` +build alone measured **12,905s (3.6 hours)**, single-threaded, and `--externals` needs a +second framework build on top — together they dominated the run ~20:1 over every other +command combined. It also **OOMs at the default heap** (hence `--max-old-space-size=8192`). +Whether Vite's framework build is comparably slow is **unknown and worth measuring** — if it +is dramatically faster, that is likely the single strongest argument in this comparison. + +Harness: `scratchpad/bench.cjs` + `bench-run.cjs` (throwaway; not part of the repo). + ## Recommendation Adopt Vite behind a feature flag for the **browser dev/build** path first (biggest -DX win, lowest risk) — validated end-to-end including i18n runtime, locale filtering, +DX win, lowest risk — measured **4–5× faster dev server startup** at **⅓ the memory**; +see the benchmark above) — validated end-to-end including i18n runtime, locale filtering, webOS metadata, and ESLint. Beyond that, **`--isomorphic` and framework externals are now ported and validated** too. **`--snapshot`** is now ported and locally validated (snapshot-safe UMD bundle); it just needs a final on-device pass on a webOS board with From d4e8492b7b872fa87805b6715f05b6e592fe21d2 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 20 Jul 2026 13:35:01 +0300 Subject: [PATCH 17/20] addec configuration for snapshot --- config/vite.config.js | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/config/vite.config.js b/config/vite.config.js index b0190d8c..3b82d541 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -4,7 +4,7 @@ /** * Experimental Vite configuration for @enact/cli. * - * This is a drop-in-oriented port of `webpack.config.js`. It mirrors the same + * This is a vite equivalent of `webpack.config.js`. It mirrors the same * factory signature so the `pack`/`serve` commands can build an equivalent * config, and it reuses the existing Enact tooling wherever a webpack-agnostic * equivalent exists: @@ -18,11 +18,6 @@ * - cssModuleIdent (getLocalIdent) -> via `css.modules.generateScopedName` * - eslint-config-enact -> via the inline `enact-eslint` plugin * - * See `docs/vite-migration.md` for the feasibility analysis. Isomorphic prerendering - * (see `docs/vite-isomorphic-scope.md`), framework externals, and the V8 snapshot - * (`mixins/vite-snapshot.js`) are all ported. The only build-time combination that - * prints a "not supported" notice is `--snapshot --externals` (the snapshot must - * embed `@enact`, matching webpack). */ const fs = require('fs'); @@ -35,21 +30,13 @@ const { ViteWebOSMetaPlugin } = require('@enact/dev-utils'); -// Node core-module polyfills for the browser bundle — the Vite analog of the -// webpack build's `node-polyfill-webpack-plugin` (needed mainly for screenshot -// tests, which reference `Buffer`/`process`/`stream`/etc.). Injects globals -// reference-only (like webpack's ProvidePlugin — no global `typeof process` -// flip) and aliases node builtins to browser implementations. Dropped for the -// SSR/isomorphic build (see `vite-isomorphic.js` applySsrBuild), which needs the -// real Node builtins. const {nodePolyfills} = require('vite-plugin-node-polyfills'); -// PostCSS plugin chain, shared with webpack.config.js (single source of truth). +// PostCSS plugin chain, shared with webpack.config.js. const {getPostCssPlugins} = require('./postcss-plugins'); // Vite plugin that runs ESLint against the app sources, mirroring the webpack // build's `eslint-webpack-plugin` (same flat config in eslintWebpackPluginConfig). -// Lints once at build start: warnings are printed; errors fail production builds. function enactEslintPlugin () { let isBuild = true; return { @@ -87,12 +74,12 @@ function enactEslintPlugin () { }; } -// Vite plugin that neutralizes webpack's HMR API in app source. Some Enact apps +// Vite plugin that neutralizes webpack's HMR API in-app source. Some Enact apps // guard reducer/hot-reload code with `if (module.hot) { module.hot.accept(…) }`. -// `module` exists in the webpack runtime but not in Vite's browser ESM (app source +// `module` exists in the webpack runtime but not in Vite's browser ESM (the app source // isn't CJS-wrapped like pre-bundled deps), so it throws `module is not defined`. // Vite's `define` can't replace `module.hot` (esbuild treats `module` specially), -// so rewrite it to `false` here — the webpack-only block is skipped and Vite's own +// so rewrite it to `false` here. The webpack-only block is skipped, and Vite's own // HMR (import.meta.hot) still applies to the module graph. function enactNeutralizeWebpackHmrPlugin () { return { @@ -110,7 +97,7 @@ function enactNeutralizeWebpackHmrPlugin () { // vite-plugin-node-polyfills injects bare imports of its own shims and // `node-stdlib-browser` into app modules. Those packages live in the CLI's // node_modules, not the app's, so an app built with `root: app.context` (e.g. a -// sample under a theme repo) can't resolve them. Resolve those specifiers from the +// sample under a theme repo) can't resolve them. We need to resolve those specifiers from the // CLI instead; their transitive deps then resolve from the CLI tree naturally. function enactNodePolyfillResolverPlugin () { const FROM_CLI = /^(?:vite-plugin-node-polyfills|node-stdlib-browser)(?:\/|$)/; @@ -132,7 +119,7 @@ const FORCE_CSS_STYLE_RE = /\.(?:css|less|s[ac]ss)(?:\?.*)?$/; const FORCE_CSS_MODULE_RE = /\.module\.(?:css|less|s[ac]ss)(?:\?.*)?$/; // The Enact `forceCSSModules` build option makes ALL css/less/scss behave as CSS -// modules (scoped), not just `*.module.*` — matching the webpack build, whose +// modules (scoped), not just `*.module.*`, matching the webpack build, whose // non-module style rules use `modules:{getLocalIdent}` (no `mode:'icss'`) when the // option is set. Vite decides module-ness purely from the `.module.` filename infix // (cssModuleRE) with no override hook, so we resolve each non-module style import and @@ -182,7 +169,7 @@ function isPlainStyleId (id) { // "default" is not exported by "src/App/App.less" // These two plugins restore parity WITHOUT scoping anything (scoping stays webpack- // identical: plain CSS remains global; only `forceCSSModules` scopes it): -// 1. `enact-icss-extract` (normal order → runs after vite:css has compiled LESS/SCSS +// 1. `enact-icss-extract` (normal order → runs after vite: css has compiled LESS/SCSS // to CSS, before vite:css-post builds the JS proxy): lifts `:export {…}` blocks out // of the compiled CSS into a locals map, and strips them from the emitted CSS // (css-loader does the same; `:export` is not valid CSS for a browser). @@ -251,7 +238,7 @@ const enactBabelEsbuildPlugin = { let babel; const preset = require.resolve('babel-preset-enact'); build.onLoad({filter: /[\\/]@enact[\\/].*\.(?:jsx?|mjs)$/}, async args => { - // iLib data/loaders under @enact/i18n are not Enact source — leave them + // iLib data/loaders under @enact/i18n are not Enact source. Leave them // to esbuild (and the loader stub) rather than paying babel on big files. if (/[\\/]ilib[\\/]/.test(args.path)) return null; babel = babel || require('@babel/core'); @@ -426,8 +413,7 @@ module.exports = function ( // specifier from node_modules (LESS `~` is handled by lessTildeImportPlugin). {find: /^~/, replacement: ''} ], - // Force a single copy of React across the app and all (possibly nested, - // e.g. @enact/limestone/node_modules/@enact/*) dependencies. Without this, + // Force a single copy of React across the app and all dependencies. Without this, // Vite pre-bundling resolves multiple physical react copies and mixing // components across them triggers "Invalid hook call / more than one copy // of React". Webpack avoids this via single-tree resolution + exposing @@ -500,7 +486,7 @@ module.exports = function ( optimizeDeps: { // Enact apps ship no `index.html`, so Vite's dependency scanner has no // default entry to crawl and would otherwise discover every dependency - // lazily on first request — each new one triggering a re-optimize + + // lazily on the first request, each new one triggering a re-optimize + // full page reload. That churns badly for apps that import source from // sibling packages (e.g. `all-samples`). Point the scanner at the app // entry so it crawls the whole import graph (including cross-package From 311c0b77947cd52ec297a23854b9a0c0b129e277 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 20 Jul 2026 15:37:43 +0300 Subject: [PATCH 18/20] refined migration docs --- docs/vite-eject-testing.md | 116 +---- docs/vite-framework-externals-spike.md | 138 ----- docs/vite-isomorphic-scope.md | 406 ++++++--------- docs/vite-migration.md | 672 ++++++++----------------- 4 files changed, 369 insertions(+), 963 deletions(-) delete mode 100644 docs/vite-framework-externals-spike.md diff --git a/docs/vite-eject-testing.md b/docs/vite-eject-testing.md index bf8b5e88..7300a1e6 100644 --- a/docs/vite-eject-testing.md +++ b/docs/vite-eject-testing.md @@ -3,8 +3,7 @@ This guide walks through validating the Vite support that was added to the `eject` command. It is written as a **manual** procedure because `eject` is destructive: it rewrites the app's `package.json`, requires a clean git working tree, copies files -into the app, and runs `npm install`. Do not run it against an app you care about — -always use a throwaway copy in a fresh git repo. +into the app, and runs `npm install`. ## What changed @@ -12,92 +11,30 @@ always use a throwaway copy in a fresh git repo. and the `commands/` (as `scripts/`, including the `--vite`-capable `pack.js`/`serve.js`). Two eject modes exist: -- **Default eject** (`enact eject`) — keeps the Enact scripts and rewrites the app's +- **Default eject** (`enact eject`): keeps the Enact scripts and rewrites the app's npm tasks from `enact <cmd>` to `node ./scripts/<cmd>.js`, preserving any flags. Adding `--vite` here **appends `--vite`** to the bundler-driven scripts (`serve`, `pack`, `pack-p`, `watch`) so they run the Vite path; `clean`/`lint`/`test` are left untouched. - (Flags already present on a script — e.g. `enact serve --vite` — are preserved regardless.) -- **Bare eject** (`enact eject --bare`) — abandons the Enact scripts and rewrites the + (Flags already present on a script, e.g. `enact serve --vite`, are preserved regardless.) +- **Bare eject** (`enact eject --bare`): abandons the Enact scripts and rewrites the npm tasks to invoke the underlying tools directly. This was **webpack-only**. Adding `--vite` makes it emit a **Vite** barebones setup instead. New pieces in [commands/eject.js](../commands/eject.js): -| Piece | Purpose | -|---|---| -| `--vite` flag | Non-bare: appends `--vite` to the `serve`/`pack` scripts. Bare: selects the Vite flavor of the barebones setup. | -| `VITE_CAPABLE_SCRIPTS` | The scripts that understand `--vite` (`serve`, `pack`) — only these get the flag in a non-bare Vite eject. | -| `bareTasksVite` | Vite npm scripts: `serve → vite`, `pack → vite build --mode development`, `pack-p → vite build`, `watch → vite build --watch --mode development`. | -| `bareDepsVite` | Bare Vite deps: just `rimraf` (Vite copies `public/` automatically, so no `cpy-cli`). | +| Piece | Purpose | +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `--vite` flag | Non-bare: appends `--vite` to the `serve`/`pack` scripts. Bare: selects the Vite flavor of the barebones setup. | +| `VITE_CAPABLE_SCRIPTS` | The scripts that understand `--vite` (`serve`, `pack`). only these get the flag in a non-bare Vite eject. | +| `bareTasksVite` | Vite npm scripts: `serve → vite`, `pack → vite build --mode development`, `pack-p → vite build`, `watch → vite build --watch --mode development`. | +| `bareDepsVite` | Bare Vite deps: just `rimraf` (Vite copies `public/` automatically, so no `cpy-cli`). | | `VITE_ROOT_CONFIG` → `vite.config.mjs` | A root config the Vite CLI auto-loads. The Enact config `config/vite.config.js` is a factory `(mode) => InlineConfig`; this adapter maps Vite's `{command, mode}` call onto it. | -## Prerequisites - -- A local build of this CLI (so `enact` resolves to your working tree). Either: - - Run via the repo's bin: `node <repo>/cli/bin/enact.js <cmd>`, or - - `npm link` the CLI once: `cd <repo>/cli && npm link`, then `enact` is on PATH. -- `git` available (eject aborts if the working tree is dirty). -- Node + npm able to reach your registry (eject runs `npm install`). - -Throughout, `enact` means "the CLI under test". Substitute `node <repo>/cli/bin/enact.js` -if you did not link. - -### Important: the ejected app needs a Vite-capable `@enact/dev-utils` - -An ejected app is standalone — its `config/vite.config.js` does -`require('@enact/dev-utils')` and resolves it from the **app's** `node_modules`, not the -CLI's. The Vite plugins (`ViteHtmlPlugin`, `ViteILibPlugin`, `ViteWebOSMetaPlugin`) live in -the **working-tree** `dev-utils` and are **not yet in any published `@enact/dev-utils`**. -So a freshly-ejected app that installed `@enact/dev-utils` from npm will fail with -`ViteHtmlPlugin is not a function`. - -Until a dev-utils with the Vite plugins is published, point the ejected app at the -working-tree copy. On Windows (junction; reversible — a later `npm install` restores the -published copy): - -```powershell -$link = "<app>\node_modules\@enact\dev-utils" -$target = "<repo>\dev-utils" -if (Test-Path $link) { (Get-Item $link).Delete() } -New-Item -ItemType Junction -Path $link -Target $target -``` - -On macOS/Linux: `rm -rf <app>/node_modules/@enact/dev-utils && ln -s <repo>/dev-utils <app>/node_modules/@enact/dev-utils` -(or `npm link @enact/dev-utils` after `npm link` in the working-tree `dev-utils`). - -## Set up a throwaway test app - -Use a **limestone** sample (per project convention, checkups run against limestone, not -sandstone). Copy it out of the monorepo so the eject can't touch tracked files, and give -it its own git repo: - -```bash -# from the repo root (…/LGE) -cp -r limestone/samples/qa-dropdown /tmp/eject-test -cd /tmp/eject-test - -# install its deps so it's runnable before ejecting -npm install - -# eject requires a clean git tree -git init -q && git add -A && git commit -qm "baseline before eject" -``` - -Sanity-check the app runs on Vite **before** ejecting (this is what eject must preserve): - -```bash -enact serve --vite # open the printed URL, confirm the app renders, then Ctrl-C -enact pack --vite -p # confirm ./dist is produced -git checkout -- . && git clean -fdq # discard the build artifacts -``` - ---- - -## Test A — Non-bare Vite eject +## Test A: Non-bare Vite eject Goal: `enact eject --vite` (no `--bare`) points the copied scripts at the Vite path, so the app builds/serves with Vite. The app's original scripts can be plain webpack -(`enact serve`) — the flag is added for you. +(`enact serve`). 1. Eject with `--vite` (answer **yes** at the confirmation prompt): ```bash @@ -121,16 +58,9 @@ the app builds/serves with Vite. The app's original scripts can be plain webpack npm run pack-p # → confirm ./dist is produced ``` Open the browser console: **no errors**, the sample renders as it did pre-eject. - -4. Reset for the next test: - ```bash - cd /tmp && rm -rf eject-test && cp -r <repo>/limestone/samples/qa-dropdown /tmp/eject-test - cd /tmp/eject-test && npm install && git init -q && git add -A && git commit -qm baseline - ``` - --- -## Test B — Bare Vite eject (the new path) +## Test B: Bare Vite eject (the new path) Goal: `--bare --vite` produces a self-contained Vite setup that runs the Vite CLI directly. @@ -168,7 +98,7 @@ Goal: `--bare --vite` produces a self-contained Vite setup that runs the Vite CL 5. **Known-good caveats to expect (not failures):** - The app still receives webpack-related devDeps (webpack is a CLI dependency, so the - generic dep-merge copies it). They're unused by the Vite tasks — harmless bloat. + generic dep-merge copies it). They're unused by the Vite tasks. - `vite.config.mjs` uses the factory's **defaults** for locale filtering, content hash, isomorphic, etc. Bare mode intentionally drops the CLI's flag plumbing; to customize, edit `vite.config.mjs` to pass extra factory args, e.g. @@ -191,21 +121,3 @@ enact eject --bare `npm run pack-p` produces `./dist`. --- - -## Cleanup / rollback - -Everything happens in the throwaway copy, so just delete it: - -```bash -cd /tmp && rm -rf eject-test -``` - -If you ever eject in a real repo by mistake, `git reset --hard && git clean -fd` restores -it (eject refuses to run on a dirty tree precisely so this is always possible). - -## Quick checklist - -- [ ] Test A: default eject → `node ./scripts/*.js --vite`, app serves + packs. -- [ ] Test B: `--bare --vite` → `vite.config.mjs` written; `serve`/`pack`/`pack-p`/`watch`/`clean` all work; `./dist` renders. -- [ ] Test C: `--bare` → webpack scripts unchanged, no `vite.config.mjs`. -- [ ] Browser console clean in every serve/build. diff --git a/docs/vite-framework-externals-spike.md b/docs/vite-framework-externals-spike.md deleted file mode 100644 index 61f391e2..00000000 --- a/docs/vite-framework-externals-spike.md +++ /dev/null @@ -1,138 +0,0 @@ -# Vite `--framework` / `--externals` — spike findings & plan - -**Status: spike complete, mechanism proven in-browser.** This documents what the -webpack feature does, the Vite-native approach, what the spike validated end-to-end, -and the plan to turn it into `enact pack --framework` / `--externals`. - -## What the webpack feature does - -A DLL-style shared bundle so multiple webOS apps share one framework payload on-device. - -- **`pack --framework`** ([dev-utils `mixins/framework.js`](../../dev-utils/mixins/framework.js) + - [`EnactFrameworkPlugin`](../../dev-utils/plugins/dll/EnactFrameworkPlugin.js)) — bundles every - `@enact/**` + all `ilib/**` + `react`/`react-dom`/`react-dom/client`/`react-dom/server` - into a UMD library `enact_framework`, where each module is addressable by a **normalized - ID** (`@enact/ui/Button`, `react`, `ilib`) through a `__webpack_require__` registry. - Emits `enact.js` + `enact.css`. -- **`pack --externals=<path>`** ([`mixins/externals.js`](../../dev-utils/mixins/externals.js) + - [`EnactFrameworkRefPlugin`](../../dev-utils/plugins/dll/EnactFrameworkRefPlugin.js)) — a - `DelegatedEnactFactoryPlugin` rewrites every `@enact`/`react`/`ilib` request into - `enact_framework('<id>')`, and injects `<script src=.../enact.js>` before the app's assets. - -The hard part: webpack maps **deep module IDs** through a runtime registry. Rollup's -`external` works at the *specifier* level and has no such registry. - -## The Vite-native approach: import maps - -The browser-native analog to the DLL registry is an **import map**, which supports -prefix mapping via trailing-slash keys (`"@enact/ui/": "/framework/@enact/ui/"`). - -- **Framework build** = a Vite build that emits the shared deps as reusable ESM files. -- **App build** = the normal app build with those specifiers in - `build.rollupOptions.external`, plus an injected `<script type="importmap">` that maps - each bare specifier to its framework file. - -## What the spike validated (qa-a11y, react/react-dom externalized) - -Two throwaway scripts drove the real CLI vite.config factory unchanged. **Browser-verified** -against `limestone/samples/qa-a11y`: - -1. **Framework bundle builds as reusable ESM with working named exports.** `react.js` loaded - as ESM exposes `useState`/`useEffect`/`createElement`/… (v19.2.6). Two non-obvious fixes - were required and found: - - **Re-export wrapper per specifier**, not the CJS file as the entry directly (a bare CJS - entry just runs `requireReact()` and exports nothing). - - **`preserveEntrySignatures: 'strict'`** — otherwise Rollup tree-shakes the entry's - re-exports off (nothing imports an entry), leaving a bare require call. - - **Enumerate export names at build time** (`Object.keys(require('react'))`) to generate - explicit `export const { … } = __m`. React 19's lazy-CJS pattern defeats Rollup's static - named-export detection, so `export *` yields nothing; enumeration is deterministic and - version-proof (44 names for react, 3 for jsx-runtime, etc.). -2. **Single React instance.** `react.js` and `react-dom-client.js` both import the same - shared `chunk-index.js` (react core) — so react-dom uses the same React. Confirmed in the - network trace (react core chunk fetched once) and, decisively, by the app **rendering with - hooks working** — two React copies would throw "Invalid hook call" and render nothing. -3. **App externalizes + boots against the import map.** The app `main.js` contains bare - `import … from "react"` / `"react-dom"` / `"react-dom/client"` / `"react/jsx-runtime"` - (react excluded from the bundle). The injected import map (first element in `<head>`, - before the module script) resolves them to `./framework/*.js`. The qa-a11y sample rendered - fully, **console clean, no 404s**. - -## Deep-import coverage — proven (extended spike) - -The extended spike (`build-fw-app.mjs`) took the react-only proof to the **full @enact -surface** on qa-a11y, reusing the real CLI vite.config factory for both builds. **Browser- -verified**: all **60** `@enact/*` + react specifiers externalized, app `main.js` shrank to -**236 KB** (from ~1.08 MB), framework = 175 shared ESM chunks + one `enact.css`, app renders -**fully styled** (4808 CSS rules applied), console clean, every framework chunk 200 OK. - -Three non-obvious findings resolved: - -1. **Exact import-map keys, not prefix.** `@enact/limestone/Button` resolves via the - component dir's `package.json` `main` (`Button/Button.js`) — which browser import maps - can't do. So the map needs one **exact** key per specifier. These are collected from the - app build (an `external(id)` function records each externalized specifier); since - externalized modules aren't crawled, the set is bounded (the app's *direct* imports), and - the framework build pulls in all transitive `@enact`/react/ilib internally as shared chunks. -2. **Wrapper entries are mandatory — never point an entry at @enact source directly.** Making - `@enact/i18n/I18nDecorator` an *entry* breaks Vite's CJS interop for @enact's CJS-in-source - (e.g. `i18n/src/zoneinfo.js` → `"default" is not exported`), because @enact is symlinked - monorepo source *outside* `node_modules` (with `preserveSymlinks`), so the commonjs plugin - skips it. Routing every entry through a small re-export **wrapper** keeps the real module - *transitive* (exactly as a normal app build sees it), and the error disappears. Wrapper - shapes: CJS (react/ilib) → enumerate named exports; @enact ESM → `export * from …` + - `export default (__m.default !== undefined ? __m.default : __m)` (safe default fallback). -3. **Single `enact.css`.** Per-chunk CSS can't be loaded via an import map (JS only), so the - framework build sets `build.cssCodeSplit = false` → one `enact.css`, injected as a `<link>` - before the app's own stylesheet. ilib needed no extra work — the factory's existing - `ILIB_LOADER_RE` commonjs-ignore handled it. - -## CLI wiring — implemented (shared-framework model) - -Wired and **browser-validated end-to-end** via the CLI on limestone/qa-a11y -(`enact pack --framework -o framework-dist` then `enact pack --externals=framework-dist`): -the 138-specifier framework + `enact.css` built, the app externalized 60 specifiers, -booted **fully styled** (5032 CSS rules), single React instance, console clean. - -Implementation: - -- **`dev-utils/mixins/vite-framework.js`** — mirrors the webpack DLL `framework`/`externals` - mixins. `enumerateSpecifiers(context)` globs the shared surface (every `@enact/<pkg>` - root + component subpath, plus react family + ilib) independent of any app; - `writeWrappers` emits re-export wrappers; `applyFramework` sets - `preserveEntrySignatures:'strict'` + `cssCodeSplit:false` and drops the HTML/webOS-meta - plugins; `writeManifest`/`readManifest` persist the specifier→file map; `applyExternals` - externalizes + collects; `injectHtml` writes the import map + stylesheet `<link>`. -- **`pack.js`** — `--framework` runs `viteFramework()` (build + manifest); `--externals=<path>` - runs the app build with externalization, then resolves collected specifiers against the - manifest and injects the map (copying the framework under `./framework`, or using - `--externals-public` as the base URL for a remotely-deployed framework). - -### Remaining refinements (non-blocking) - -- **Theme-repo `.` self-inclusion** — webpack's `--framework` in a *theme* repo (limestone) - includes the theme's own components via `libraries.push('.')`. The current enumeration - scans `node_modules/@enact`, so build the framework in a context where all `@enact` - *including the theme* are installed (an app/sample context works; qa-a11y gave 138 - specifiers incl. limestone). Adding theme-self globbing would let it run in the bare theme repo. -- **`--snapshot`** interaction — deferred until `--snapshot` itself is ported. - -### `--externals-polyfill` — wired - -`pack --framework --externals-polyfill` adds `core-js/stable` as a framework entry (a -side-effect wrapper; folds all core-js into the shared bundle, manifest key `core-js/stable`). -`pack --externals=<path> --externals-polyfill` then externalizes core-js out of the app: -because the app config *aliases* `core-js` to the CLI's copy (so the `external` fn only sees -488 resolved internals, never the bare specifier), the externals path **drops that alias** so -`core-js/stable` stays a bare specifier and externalizes as one unit → the ~488 core-js -modules leave the app bundle and resolve to the framework via the import map. Externalization -is **manifest-aware**, so core-js (and any @enact the framework doesn't provide) is only -externalized when the framework actually carries it — otherwise it stays bundled and the app -still builds. Browser-unverified but bundle-verified: with the flag, core-js internals are -absent from `main.js` and `core-js/stable` appears in the import map; without it, core-js -stays in the app. - -## Risk assessment - -**All technical risk is retired and the feature is implemented + browser-validated.** The -refinements above are optional polish, not blockers. diff --git a/docs/vite-isomorphic-scope.md b/docs/vite-isomorphic-scope.md index cf7c36c2..6b0f8558 100644 --- a/docs/vite-isomorphic-scope.md +++ b/docs/vite-isomorphic-scope.md @@ -1,19 +1,17 @@ -# Scope: `--isomorphic` prerendering for the Vite path +# `--isomorphic` prerendering on the Vite path -Port of the webpack `PrerenderPlugin` + `mixins/isomorphic.js` to the Vite -bundler path (`enact pack --isomorphic --vite`). This is the largest remaining -webpack-only feature; this doc breaks it into buildable phases with the concrete -mechanics, risks, and a validation plan. +`enact pack -p -i --vite` server-renders the app to static HTML per target locale +(`-l`), producing per-locale variant files plus a startup script that shows the +prerendered markup immediately and hydrates on the client — matching the webpack output +closely enough that webOS treats it as a prerendered app (`appinfo.usePrerendering = true`, +per-locale `main`). -## Goal +This document describes how that path is implemented and how it maps to (and reuses) the +webpack machinery. Implementation: +[`dev-utils/mixins/vite-isomorphic.js`](../../dev-utils/mixins/vite-isomorphic.js) + +`pack.js`'s `viteIsomorphic`. -`enact pack -p -i --vite` should, for each target locale (`-l`), server-render the -app to static HTML and produce per-locale `index.<locale>.html` files plus a -startup script that (a) shows the prerendered markup immediately and (b) hydrates -on the client — matching the webpack output closely enough that webOS treats it as -a prerendered app (`appinfo.usePrerendering = true`, per-locale `main`). - -## What webpack does today (reference) +## What webpack does (reference) Files: [`dev-utils/mixins/isomorphic.js`](../../dev-utils/mixins/isomorphic.js), [`dev-utils/plugins/PrerenderPlugin/`](../../dev-utils/plugins/PrerenderPlugin/) @@ -21,251 +19,159 @@ Files: [`dev-utils/mixins/isomorphic.js`](../../dev-utils/mixins/isomorphic.js), 1. **Build shape** — the app is built as a **UMD library** (`output.library='App'`, `libraryTarget='umd'`, `globalObject='this'`) whose `default` export is the app - ReactElement. `src/index.js` already exports `appElement` as default and guards - `createRoot`/`hydrateRoot` behind `typeof window` + `ENACT_PACK_ISOMORPHIC`. - React is exposed on `global` (`expose-loader`) to avoid duplicate copies. -2. **Server render** (`vdom-server-render.render`, per locale): - - `global.XMLHttpRequest = FileXHR` — a synchronous fake XHR that reads iLib - locale data from **disk** so `@enact/i18n` can load it during SSR. - - `global.process.env.LANG = locale`. - - `require()` the built UMD chunk in Node → `chunk.default` = the element. - - `reactDOMServer.renderToString(chunk.default)`. - - Optional per-locale font CSS via `app.fontGenerator`, prepended to the markup. -3. **HTML assembly** (`PrerenderPlugin` html hooks + `templates.js`): - - Root CSS classes (`enact-locale-*`) are extracted from the rendered markup. - - Identical renders across locales are **deduped/aliased** (`simplifyAliases`). - - JS `<script>`s are removed from `<body>` and replaced by an inline - **startup script** (`templates.startup`) in `<head>` that async-loads the JS - and, at runtime, picks the correct prerendered HTML for the device - screenType/locale. - - Emits `index.html` + `index.<locale>.html` variants. -4. **webOS meta coupling** — generates per-locale `resources/<locale>/appinfo.json` - with `main: index.<locale>.html`, and sets root `usePrerendering: true`. - -## Proposed Vite architecture - -Vite has first-class SSR, which replaces the UMD-in-Node approach cleanly. The -**key correction** from the spike: do a real **`vite build --ssr`** of a server -entry (full plugin/babel pipeline runs) — do **not** use dev-time `ssrLoadModule` -(which skipped the JSX-in-`.js` transform and failed at `App.js`). + ReactElement. `src/index.js` exports `appElement` as default and guards + `createRoot`/`hydrateRoot` behind `typeof window` + `ENACT_PACK_ISOMORPHIC`. React is + exposed on `global` (`expose-loader`) to avoid duplicate copies. +2. **Server render** (`vdom-server-render.render`, per locale): `global.XMLHttpRequest = + FileXHR` (a synchronous fake XHR that reads iLib locale data from disk so `@enact/i18n` + can load it during SSR); `global.process.env.LANG = locale`; `require()` the built UMD + chunk in Node → `chunk.default` = the element; `reactDOMServer.renderToString(...)`; + optional per-locale font CSS via `app.fontGenerator`, prepended to the markup. +3. **HTML assembly** (`PrerenderPlugin` html hooks + `templates.js`): identical renders + across locales are **deduped/aliased**; JS `<script>`s are removed from `<body>` and + replaced by an inline **startup script** (`templates.startup`) in `<head>` that + async-loads the JS and, at runtime, picks the correct prerendered HTML for the device + screenType/locale; emits `index.html` + `index.<locale>.html` variants. +4. **webOS meta coupling** generates per-locale `resources/<locale>/appinfo.json` with + `main: index.<locale>.html`, and sets root `usePrerendering: true`. + +## How the Vite path works + +Vite has first-class SSR, which replaces the UMD-in-Node approach. The app is rendered +from a real **`vite build --ssr`** of a server entry (so the full plugin/babel pipeline +runs, including the JSX-in-`.js` transform) rather than dev-time `ssrLoadModule` (which +skips that transform and fails at `App.js`). Pipeline for `pack --isomorphic --vite`: -1. **Client build** (existing) → `main.js`, `main.css`, assets. Keep the current - `ViteHtmlPlugin` output as the hydration shell. -2. **SSR build** — a second Rollup build (`build.ssr = <server entry>`, - `ssr.noExternal` for `@enact/*`) producing a Node-loadable `app.server.js` - whose default export is the app element. Server entry ≈ `export {default} from - '<app main>'` (the app already exports the element; the `window` guard keeps the - client render side-effect from firing under SSR). -3. **Prerender driver** (new, run after both builds in `pack.js`'s `viteBuild`): - for each locale from `parseLocales` (already ported in `ViteILibPlugin`): - - set `global.XMLHttpRequest = FileXHR`, `process.env.LANG = locale`; - - `require(app.server.js)` fresh (uncached) → element; - - `renderToString`; extract `enact-locale-*` classes; dedupe/alias. -4. **HTML emit** — inject the prerendered markup into `<div id="root">…</div>` of - the client `index.html`, add the startup `<script>` in `<head>`, remove the - body module script (startup adds it), and write `index.<locale>.html`. -5. **webOS meta** — extend `ViteWebOSMetaPlugin` to accept a locale list + - `usePrerendering` and emit per-locale `appinfo.json` with `main`. - -Reusable as-is from `@enact/dev-utils`: `FileXHR.js`, `templates.js`, -`parseLocales`/`detectLocales` (now in `ViteILibPlugin`), the alias/dedupe logic -(`simplifyAliases`, `rootInjection`, `language`). These are **bundler-agnostic** — -only the webpack-runtime string rewrites in `vdom-server-render.stage` -(`__webpack_require__.e`, `webpackAsyncContext`) are webpack-specific and get -dropped (Vite's SSR bundle has no such runtime). - -## Phase A — validated (GO) - -A timeboxed spike (`vite build --ssr` of `qa-a11y/src/index.js` reusing the CLI vite.config -factory, then load + `renderToString` for `en-US`) **passed the go/no-go gate**: the app -server-rendered to **68 KB of non-empty markup** with Enact root classes -(`enact-orientation-landscape enact-res-fhd … limestone-theme … ThemeDecorator_root`), -default export a valid React element, no throw. - -Concrete findings (the Phase A "how"): - -1. **Real `vite build --ssr`, not `ssrLoadModule`** — confirmed: the JSX-in-`.js` transform - runs and `@enact/*` (in `ssr.noExternal`) is bundled + babel-transformed. -2. **CJS output** (`output.format='cjs'` + `inlineDynamicImports`) — the bundle carries +1. **Client build** → `main.js`, `main.css`, assets, with the `ViteHtmlPlugin` output as + the hydration shell (isomorphic → the app entry uses `hydrateRoot`). +2. **SSR build** — a second Rollup build (`build.ssr = <server entry>`, `ssr.noExternal` + for `@enact/*`) producing a Node-loadable `app.server.cjs` whose default export is the + app element. The server entry re-exports the app's default; the `window` guard keeps the + client render side-effect from firing under SSR. +3. **Prerender** — for each locale from `parseLocales` (in `ViteILibPlugin`): install the + locale XHR global, set `process.env.LANG = locale`, `require()` the SSR bundle fresh + (uncached), `renderToString`, then dedupe identical renders. +4. **HTML assembly** — inject the prerendered markup into `<div id="root">…</div>` of the + client `index.html`, add the startup `<script>` in `<head>`, remove the body module + script (the startup script adds it), and write `index.<variant>.html` + `locale-map.json`. +5. **webOS meta** — root `usePrerendering:true` + per-locale `resources/<lang>/<region>/appinfo.json` + with `main` → the locale's variant. + +Reused as-is from `@enact/dev-utils` (all bundler-agnostic): `FileXHR.js`, `templates.js`, +`parseLocales`/`detectLocales` (now in `ViteILibPlugin`), and the alias/dedupe logic. Only +the webpack-runtime string rewrites in `vdom-server-render.stage` +(`__webpack_require__.e`, `webpackAsyncContext`) are webpack-specific and are not needed — +Vite's SSR bundle has no such runtime. + +## Implementation details + +These are the specifics that make the Vite SSR render match webpack's output. + +1. **Real `vite build --ssr`, not `ssrLoadModule`.** The JSX-in-`.js` transform runs and + `@enact/*` (in `ssr.noExternal`) is bundled + babel-transformed. +2. **CJS output** (`output.format='cjs'` + `inlineDynamicImports`). The bundle carries leftover `require()` (ilib's platform loaders); an ESM (`.mjs`) bundle throws - `require is not defined`. Load it with `require()` in the prerender driver. + `require is not defined`. The prerender loads it with `require()`. 3. **ilib: bundle it + keep the *Node* loader.** The client config ignores **all** ilib platform loaders (`ILIB_LOADER_RE`) because the browser can't use them; for SSR-in-Node, - override `commonjsOptions.ignore` to keep `NodeLoader`/`ilib-node.js` (ignore only - Qt/Rhino/Ringo) so ilib reads locale data from disk. Also add `/^ilib($|\/)/` to - `ssr.noExternal` (externalized, its internal dynamic imports resolve to a wrong path). -4. **No `window`/DOM shim needed.** Enact renders server-side without a DOM (matches webpack's - `vdom-server-render`, which sets only `XMLHttpRequest=FileXHR` + `LANG`, never `window`). - -## Phase B — validated (prerender driver + FileXHR); scope-doc criterion corrected - -The prerender driver (build SSR bundle once, then per-locale: set `FileXHR` global + `LANG`, -uncached-`require`, `renderToString`) works, and its output **matches the current webpack -`--isomorphic` baseline byte-for-byte modulo the CSS-module hash**. - -Findings: - -1. **FileXHR path shim — strip the leading `/`.** `@enact/i18n` builds **absolute** locale URLs - (`/node_modules/ilib/locale/ilibmanifest.json`, `/resources/ilibmanifest.json`) because - Vite's base is `/`. `FileXHR` reads relative to cwd (the app dir), so the driver wraps it to - strip the leading `/` → both manifests resolve (200) from `node_modules/ilib/locale/…` and - `resources/…`. (No `ILIB_BASE_PATH` gymnastics needed.) -2. **`enact-locale-*` is NOT emitted server-side — by design (scope-doc criterion was stale).** - The current `@enact/i18n` `I18n.getServerSnapshot()` returns **`className: null`** - deliberately; the locale class is applied on the **client** after hydration. Confirmed the - current **webpack** `--isomorphic` produces the *same* thing: its prerendered - `index.multi.html` has **no** `enact-locale-*` classes either. So "extract `enact-locale-*` - root classes" (from the older webpack behavior this doc was written against) is obsolete — - the correct success criterion is **"prerendered markup matches webpack's prerender."** -3. **Matches webpack exactly.** Vite `renderToString` → the same root as webpack's - `index.multi.html`: `enact-orientation-landscape enact-res-fhd … ThemeDecorator_root__<hash> - limestone-theme enact-unselectable enact-fit enact-text-normal` + identical panel content; - only the CSS-module hash suffix differs (`__voIZO` vs `__tejPz`), as expected across bundlers. -4. **Identical-across-locales → dedupe.** For a no-translation app (qa-a11y), every locale - renders identically, so webpack aliases them all to one file (`locale-map.json`: - both `en-US`+`ko-KR` → `index.multi.html`). Vite's per-locale renders are likewise identical - → the driver dedupes to one variant. (Apps with `$L` translations would render differently - per locale and get separate variant files.) - -Net: the render half of isomorphic is proven on Vite and output-compatible with webpack. What -remains (Phase C+) is pure **assembly** — reuse the bundler-agnostic `templates.js` / -`simplifyAliases` to emit the fallback `index.html`, the deduped `index.<variant>.html`, the -startup `<script>`, and `locale-map.json` — plus Phase D webOS per-locale `appinfo.json`. - -## Work breakdown & effort - -| Phase | Work | Effort | Risk | -| --- | --- | --- | --- | -| A | ~~SSR build wiring~~ **DONE** — `build.ssr` + CJS output + `ssr.noExternal` `@enact`/`ilib` + Node-loader-kept `commonjsOptions.ignore`; `renderToString` → 68 KB markup | ~~0.5–1 d~~ ✅ | ~~High~~ **retired** — no window shim needed | -| B | ~~Prerender driver~~ **DONE** — per-locale render + `FileXHR` (leading-`/` strip); output matches webpack. Dedupe/emit remains in C | ~~1 d~~ ✅ | ~~Med~~ resolved — render matches webpack byte-for-byte (modulo CSS hash) | -| C | ~~HTML assembly~~ **DONE (spike)** — dedupe + `templates.startup`/`update`, emit `index.html`/`index.multi.html`/`locale-map.json` (byte-identical to webpack) | ~~1 d~~ ✅ | resolved — reused `templates.js`; `main.js` loaded fine (see note) | -| D | ~~webOS-meta coupling~~ **DONE** — root `usePrerendering:true` + per-locale `resources/<lang>/<region>/appinfo.json` `main`→variant (`vite-isomorphic.writeAppinfo`) | ~~0.5 d~~ ✅ | resolved | -| E | ~~`fontGenerator` per-locale CSS; `--externals` interaction~~ **DONE** — `prerender` reads `app.fontGenerator` and prepends per-locale font CSS as a head-append block; `--isomorphic --externals` verified equivalent to plain `--isomorphic` (see below) | ~~0.5 d~~ ✅ | resolved | -| F | ~~Hydration check~~ **DONE** — the production `enact pack -p -i --vite` output hydrates in-browser with **no warnings/errors**; client applies `enact-locale-en` | ~~0.5 d~~ ✅ | resolved | - -**Implemented and browser-validated end-to-end — all phases (A–F) done**, including per-locale -font CSS and `--isomorphic --externals` (verified equivalent to plain `--isomorphic`). - -## Phase C + hydration — validated (spike) - -The full pipeline (client build + SSR build + per-locale prerender + assembly) was driven -end-to-end and **browser-verified** on qa-a11y (`-l en-US,ko-KR`): - -- **Output matches webpack byte-for-byte in shape:** emits `index.html` (2.6 KB, empty `#root` + - inline `templates.startup` script, body module script removed), `index.multi.html` (71 KB, - prerendered markup in `#root` + `templates.update` script), and `locale-map.json` **identical** - to webpack's (`{fallback:'index.html', locales:{en-US→index.multi.html, ko-KR→index.multi.html}}`). - Both locales deduped to one variant, reusing the bundler-agnostic `templates.js`. -- **Hydration is clean (production).** Loading `/index.multi.html` in a browser: the prerendered - markup shows, the startup script loads `main.js`, the **production** (`-p`) output **React - hydrates with zero console warnings/errors**, and the client then applies `enact-locale-en` to - the root (matching the i18n design — null on the server, applied on the client). Note: a **dev** - build surfaces two dev-only React warnings (`getServerSnapshot should be cached` + a root-class - hydration mismatch) that are **by-design in `@enact/i18n`** (locale class deferred to the client) - and identical to webpack's isomorphic output; `-p` strips both. See the `--externals` section below. - -Notes for productionization (not blockers): -- `templates.startup`'s `appendScripts` adds `main.js` as a **classic** `<script>`; it worked - here (qa-a11y's single production bundle has no top-level `import`), but for robustness the - Vite path should mark it `type="module"` (Risk #3 — small `templates.startup` adapter). -- `screenTypes` was passed empty in the spike; wire limestone's real `screenTypes` (from the - theme's RI config) so the startup script's resolution scaling matches webpack. - -## Key risks / unknowns - -1. ~~**SSR-safety of Enact components.**~~ **Resolved (Phase A)** — Limestone renders - server-side with **no** DOM shim; matches webpack's DOM-less `vdom-server-render`. -2. ~~**`FileXHR` ↔ output layout.**~~ **Resolved (Phase B)** — wrap `FileXHR` to strip the - leading `/` (Vite base `/`); locale manifests + data resolve from `node_modules/ilib/locale` - and `resources/` relative to the app cwd. -3. **Startup script asset names.** `templates.startup` was written for webpack - chunk names; Vite uses `main.js`/`chunk.*`/hashed. Needs a small adapter. -4. ~~**Hydration mismatch.**~~ **Resolved** — validated in-browser; the only mismatch is the - by-design `@enact/i18n` locale-deferral (`className: null` server-side), identical to webpack - and stripped in production. See the `--externals` section. -5. ~~**`--externals`** interplay~~ **Resolved (follow-on, now done)** — webpack's isomorphic + - externals reroutes `require('enact_framework')` to the framework's `enact.js`; the Vite path - instead bundles @enact for SSR while the client uses the framework import map, and the two are - verified to produce identical hydration. See the `--isomorphic --externals` section. - -## Validation plan - -- ~~Phase A~~ **done**: SSR bundle `renderToString`s to non-empty markup (68 KB) matching - webpack's prerender (note: `enact-locale-*` is **not** emitted server-side by the current - i18n — that older criterion is obsolete; see Phase B). -- ~~Phase B~~ **done**: per-locale render via `FileXHR` (leading-`/` strip) → markup - byte-identical to webpack's `index.multi.html` (modulo CSS hash). -- Phase C: `enact pack -p -i --vite -l en-US,ko-KR` → the fallback `index.html` (empty `#root` - + startup script), the deduped `index.<variant>.html` with prerendered markup, and - `locale-map.json` — matching the webpack `index.html`/`index.multi.html`/`locale-map.json` shape. -- Phase F: serve `dist/` and load in a browser — confirm markup shows pre-JS and - React hydrates with **no hydration warnings** in the console; confirm locale - switch works. - -## Wired into the CLI — done - -`--isomorphic` is implemented and browser-validated end-to-end: - -- **`dev-utils/mixins/vite-isomorphic.js`** — `applySsrBuild` (client config → SSR build), - `prerender` (per-locale render + `FileXHR` leading-`/` strip + `enact-locale-*` extract + - dedupe), `assemble` (fallback `index.html` + deduped `index.<variant>.html` + `locale-map.json` - via `templates.js`), `writeAppinfo` (root `usePrerendering` + per-locale appinfo `main`). -- **`pack.js`** — `viteIsomorphic(opts)`: client build (isomorphic → `hydrateRoot`) → SSR build → - per-locale prerender → assemble → appinfo; branched from `viteBuild` when `opts.isomorphic`. -- **Validated:** `enact pack -p -i --vite -l en-US,ko-KR` on qa-a11y emits `index.html`, - `index.multi.html`, `locale-map.json` (both locales → `index.multi.html`, matching webpack), - root `appinfo.json usePrerendering:true`, and `resources/en/US`+`resources/ko/KR/appinfo.json` - (`main:index.multi.html`). The **production** output hydrates in-browser with **no console - warnings/errors** (dev builds show the by-design `@enact/i18n` locale-deferral warnings — see - the `--externals` section). - -Productionization — both wired + validated: -- **ESM startup script (done).** `assemble` rewrites `templates.startup`'s dynamically-appended - script to `type="module"` (Vite emits ES modules; a single `main.js` module self-loads its - chunks). Browser-confirmed: `main.js` loads as a module and hydrates cleanly — robust for - code-split apps, unlike the classic `<script>` that only worked for a self-contained bundle. -- **Real screenTypes (done).** `pack.js` passes `app.screenTypes` (the resolved theme RI - screen-type array, e.g. limestone's 9 entries) to `assemble`; browser-confirmed the startup - script applied `enact-res-hd` from the real screenTypes (resolution scaling parity with webpack). -- **Phase E — per-locale font CSS (done).** `prerender` reads `app.fontGenerator`, calls - `generator(locale)` + `fontOverrideGenerator(locale)` per locale, prepends the CSS as a - head-append block (so it participates in dedupe); `assemble` extracts it into each variant's - `<head>`. Browser-verified: limestone's `localized-fonts` `<style>` (with `@font-face`) lands - in `index.multi.html`'s `<head>`. - -### `--isomorphic --externals` — wired, works, no divergence from plain `--isomorphic` - -The combination is wired (`viteIsomorphic`: the CLIENT build externalizes the framework + -injects the import map before assembly; the SSR build always bundles @enact to render). It -**builds and runs**: the app prerenders, is **styled** (the SSR prerender's CSS-module hashes -match the framework's `enact.css` — deterministic `cssModuleIdent`, verified `ThemeDecorator_root__voIZO` -== `enact.css`), loads @enact from the framework via the import map (205 files), and hydrates -with the locale applied. - -**Verified equivalent to plain `--isomorphic` (2026-07).** An earlier note flagged an -`--externals`-only hydration mismatch as needing follow-up. That was a **measurement artifact**: -it compared a *production* plain build (React dev warnings stripped) against a framework whose -react-dom was a *development* build (warnings live). Rebuilt apples-to-apples — a **dev** -framework + dev `--isomorphic --externals` vs. a dev plain `--isomorphic`, both on qa-a11y -(`-l en-US,ko-KR`) — the two are **byte-identical**: same hydrated root className, and the -**same two dev-only warnings** on the **same** `<ThemeDecorator>` tree: + `applySsrBuild` overrides `commonjsOptions.ignore` to keep `NodeLoader`/`ilib-node.js` + (ignoring only Qt/Rhino/Ringo) so ilib reads locale data from disk, and adds + `/^ilib($|\/)/` to `ssr.noExternal` (externalized, its internal dynamic imports resolve + to a wrong path). +4. **No `window`/DOM shim needed.** Enact renders server-side without a DOM (matching + webpack's `vdom-server-render`, which sets only `XMLHttpRequest`+`LANG`, never `window`). +5. **Locale XHR — a `FileXHR` subclass that strips the leading `/`.** `@enact/i18n` builds + **absolute** locale URLs (`/node_modules/ilib/locale/ilibmanifest.json`, + `/resources/ilibmanifest.json`) because Vite's base is `/`. `FileXHR` reads relative to + cwd (the app dir), so `makeLocaleXHR()` **subclasses** `FileXHR` and overrides `open()` + to strip the leading `/`, and both manifests resolve (200) from + `node_modules/ilib/locale/…` and `resources/…`. It must **subclass**, not wrap: the + `xhr` package `@enact/i18n`'s loader uses assigns its completion handler as a property + (`xhr.onload = fn`) rather than via `addEventListener`, and `FileXHR.send()` invokes + `this.onload`. A wrapper holding a private `FileXHR` would strand that handler on the + outer object, so no locale data would load and every locale would prerender unlocalized. +6. **`enact-locale-*` is not emitted server-side — by design.** The current `@enact/i18n` + `I18n.getServerSnapshot()` returns `className: null`; the locale class is applied on the + **client** after hydration. Webpack's `--isomorphic` produces the same thing (its + prerendered `index.multi.html` has no `enact-locale-*` classes either). The success + criterion is therefore "prerendered markup matches webpack's prerender," which it does. +7. **Identical-across-locales → dedupe.** For a no-translation app (qa-a11y), every locale + renders identically, so — like webpack — the variants alias to one file + (`locale-map.json`: both `en-US`+`ko-KR` → `index.multi.html`). Apps with `$L` + translations render differently per locale and get separate variant files. + +## Output and hydration + +On qa-a11y (`-p -i -l en-US,ko-KR`) the Vite path emits output matching webpack's shape +byte-for-byte modulo the CSS-module hash suffix: + +- `index.html` (2.6 KB) — empty `#root` + inline `templates.startup` script, body module + script removed. +- `index.multi.html` (71 KB) — prerendered markup in `#root` + `templates.update` script. +- `locale-map.json` — identical to webpack's + (`{fallback:'index.html', locales:{en-US→index.multi.html, ko-KR→index.multi.html}}`); + both locales deduped to one variant, reusing `templates.js`. +- Root `appinfo.json usePrerendering:true`, and `resources/en/US`+`resources/ko/KR/appinfo.json` + (`main:index.multi.html`). + +**Hydration:** loading `/index.multi.html` in a browser shows the prerendered markup, the +startup script loads `main.js`, and the **production** (`-p`) output React-hydrates with +**zero console warnings/errors**; the client then applies `enact-locale-en` to the root +(matching the i18n design — null on the server, applied on the client). + +A **dev** build surfaces two dev-only React warnings (`getServerSnapshot should be cached` ++ a root-class hydration mismatch) that are **by-design in `@enact/i18n`** (locale class +deferred to the client), identical to webpack's isomorphic output; `-p` strips both. + +Assembly specifics wired in: + +- **ESM startup script.** `assemble` rewrites `templates.startup`'s dynamically-appended + script to `type="module"` (Vite emits ES modules; a single `main.js` module self-loads + its chunks) — robust for code-split apps, unlike the classic `<script>` that only works + for a self-contained bundle. (The snapshot path keeps it classic, since its `main.js` is + UMD.) +- **Real screenTypes.** `pack.js` passes `app.screenTypes` (the resolved theme RI + screen-type array, e.g. limestone's entries) to `assemble`, so the startup script's + resolution scaling matches webpack (browser-confirmed `enact-res-hd` applied). +- **Per-locale font CSS.** `prerender` reads `app.fontGenerator`, calls `generator(locale)` + + `fontOverrideGenerator(locale)` per locale, and prepends the CSS as a head-append block + (so it participates in dedupe); `assemble` extracts it into each variant's `<head>`. + Browser-verified: limestone's `localized-fonts` `<style>` (with `@font-face`) lands in + `index.multi.html`'s `<head>`. + +## `--isomorphic --externals` + +The combination is production-correct and produces the same result as plain +`--isomorphic`. `viteIsomorphic` externalizes the framework + injects the import map on the +**client** build before assembly; the **SSR** build always bundles `@enact` to render. The +app prerenders, is styled (the SSR prerender's CSS-module hashes match the framework's +`enact.css` — deterministic `cssModuleIdent`, e.g. `ThemeDecorator_root__voIZO` == +`enact.css`), loads `@enact` from the framework via the import map, and hydrates with the +locale applied. + +Apples-to-apples (a **dev** framework + dev `--isomorphic --externals` vs. a dev plain +`--isomorphic`, both on qa-a11y `-l en-US,ko-KR`) the two are **byte-identical**: same +hydrated root className, and the **same two dev-only warnings** on the **same** +`<ThemeDecorator>` tree: 1. `The result of getServerSnapshot should be cached to avoid an infinite loop` -2. `A tree hydrated but some attributes … didn't match the client properties` (the root className). +2. `A tree hydrated but some attributes … didn't match the client properties` (the root + className). -Both are **inherent to @enact's isomorphic i18n design**, not to `--externals` and not to Vite: -`@enact/i18n`'s `I18n.getServerSnapshot()` deliberately returns `className: null` server-side and -the locale class (`enact-locale-*`) is applied on the **client** after hydration — so the first -client render legitimately differs from the server markup. **Webpack's isomorphic path has the -identical by-design mismatch** (same i18n source, same `getServerSnapshot`). React's production -build strips both dev-only warnings, so shipped output (`-p`) hydrates silently in every case. +Both are inherent to `@enact`'s isomorphic i18n design, not to `--externals` and not to +Vite: `@enact/i18n`'s `I18n.getServerSnapshot()` deliberately returns `className: null` +server-side and the locale class (`enact-locale-*`) is applied on the client after +hydration, so the first client render legitimately differs from the server markup. +**Webpack's isomorphic path has the identical by-design mismatch** (same i18n source, same +`getServerSnapshot`). React's production build strips both dev-only warnings, so shipped +output (`-p`) hydrates silently in every case. Why webpack's SSR *looks* like it can't diverge: its prerender rewrites -`require('enact_framework')` → the framework's `enact.js` (`vdom-server-render.stage`), so one -@enact serves both SSR and client. The Vite path instead uses two @enact builds (SSR-bundled + -framework import map) — but because `cssModuleIdent` is deterministic on `resourcePath` + -`rootContext`, both builds emit **identical** CSS-module class names and identical root markup, so -the two-build approach produces the same hydration result as webpack's single-@enact approach. -**No caveat, no experimental flag** — `--isomorphic --externals` is production-correct. +`require('enact_framework')` → the framework's `enact.js` (`vdom-server-render.stage`), so +one `@enact` serves both SSR and client. The Vite path instead uses two `@enact` builds +(SSR-bundled + framework import map) — but because `cssModuleIdent` is deterministic on +`resourcePath` + `rootContext`, both builds emit **identical** CSS-module class names and +identical root markup, so the two-build approach produces the same hydration result as +webpack's single-`@enact` approach. diff --git a/docs/vite-migration.md b/docs/vite-migration.md index cea16ae4..9ebb98f6 100644 --- a/docs/vite-migration.md +++ b/docs/vite-migration.md @@ -1,147 +1,84 @@ -# Replacing webpack with Vite in `@enact/cli`: feasibility and status +# Vite bundler support in `@enact/cli` -## Verdict - -Vite (Rollup + esbuild) cleanly covers `enact serve` / `enact pack` for a browser app and brings much faster cold -starts and HMR. Several webOS-specific features that started as webpack compiler -plugins have since been **re-authored as Vite/Rollup plugins** in `@enact/dev-utils` -and validated: **iLib i18n runtime + locale filtering** (`ViteILibPlugin`), **webOS -metadata** (`ViteWebOSMetaPlugin`), plus the HTML document (`ViteHtmlPlugin`) and -**ESLint**. **`--isomorphic` prerendering and framework externals are also ported and -browser-validated** (`mixins/vite-isomorphic.js`, `mixins/vite-framework.js`). -**`--snapshot`** (V8) is now ported too (`mixins/vite-snapshot.js`) and locally validated -as snapshot-safe — its only remaining step is a build + install on a webOS board with the -firmware-matched `V8_MKSNAPSHOT` toolchain (unavailable in this environment; see the -"Testing `--snapshot` on a webOS board" section). +`@enact/cli` supports **Vite (Rollup + esbuild)** as a bundler alongside webpack for +`enact serve` and `enact pack`. The Vite path is opt-in — `--vite` or +`ENACT_BUNDLER=vite`. Webpack remains the default. The Vite config lives at [`config/vite.config.js`](../config/vite.config.js); it mirrors the `webpack.config.js` factory signature and reuses the existing Enact tooling. -### Validated end-to-end ✅ +## Overview + +Vite covers `enact serve` / `enact pack` for a browser app and brings much faster cold +starts and HMR. The webOS-specific features that exist as webpack compiler plugins are +re-authored as Vite/Rollup plugins in `@enact/dev-utils`: **iLib i18n runtime + locale +filtering** (`ViteILibPlugin`), **webOS metadata** (`ViteWebOSMetaPlugin`), the HTML +document (`ViteHtmlPlugin`), and **ESLint**. **`--isomorphic` prerendering and framework +externals** are implemented and browser-validated (`mixins/vite-isomorphic.js`, +`mixins/vite-framework.js`). **`--snapshot`** (V8) is implemented +(`mixins/vite-snapshot.js`) and produces a genuine startup blob; on-device deployment +uses the firmware-matched `V8_MKSNAPSHOT` toolchain (procedure in *Testing `--snapshot` +on a webOS board*). -Both paths were run against real apps — **Sandstone** +### Validation + +Both paths are validated against real apps — **Sandstone** (`samples/sandstone/tutorial-hello-enact`, React 18, pre-compiled deps) and **Limestone** (`samples/limestone/tutorial-hello-enact`, React 19, which ships **raw `@enact` source** with JSX-in-`.js` and uses `~` npm imports — the harder, more representative case): -- **`enact pack -p --vite`** → success on both. Limestone emits `main.js` - (~550 kB), `main.css` (~70 kB with resolution-independence applied — `px` → - `rem`, and design tokens as CSS custom properties from `@import-json`), - `index.html` linking both, and font assets. CSS-module scoping is consistent - across JS and CSS (e.g. `src_App_App_app__e2Hkq` in both), matching the webpack - `cssModuleIdent` format. -- **`enact serve --vite`** → success on both. Serves the synthesized HTML with - Vite's HMR client injected, transforms JSX-in-`.js` via the React automatic - runtime, and pre-bundles the `@enact/*` dependencies. +- **`enact pack -p --vite`** → succeeds on both. +- **`enact serve --vite`** → succeeds on both. - **iLib i18n runtime** validated (constants baked/defined, locale data copied/served); **locale filtering** `-l en-US,ko-KR` trims 70 MB → 19 MB - (6,755 → 1,988 files, correct locales); **webOS meta** `appinfo.json` + icons + (correct locales); **webOS meta** `appinfo.json` + icons emitted/served; **ESLint** lints the sources (clean) and enforces rules. -- **Real-browser render** verified on several apps — `qa-dropdown` (nested - `@enact`), the redux sample (webpack HMR API), and the aggregate `all-samples` - (imports source from 15 sibling packages): each mounts and renders correctly - (see runtime fixes R1–R8 below). Note: HTTP-200 / transform checks do **not** - execute the page JS — always load a real browser. - -Eight config issues plus eight runtime issues were found and fixed while -validating. The runtime ones (mostly only visible when the page actually executes -in a real browser — HTTP-200/transform checks don't run the page JS) were: +- **Real-browser render** verified on several apps:`qa-dropdown` (nested + `@enact`), the redux sample (webpack HMR API), and the aggregated `all-samples` -- **R1 — `global` is not defined.** Enact's `polyfills.js`/core-js reference the - Node `global`, absent in the browser (webpack supplied it via - node-polyfill-webpack-plugin). Fix: `ViteHtmlPlugin` injects a classic - `<script>globalThis.global=globalThis</script>` in `<head>`, before the deferred - module entry. -- **R2 — CommonJS polyfills.** `polyfills.js` → `corejs-proxy.js` use - `require('core-js/stable')`; `require` doesn't exist in browser ESM - (`require is not defined`). Fix: the generated combined entry imports - `core-js/stable` as an ESM bare specifier (Vite pre-bundles the CJS→ESM), - aliased to the CLI's `core-js` since apps don't depend on it directly. -- **R3 — multiple copies of React** ("Invalid hook call"). Nested `@enact` deps - (`@enact/limestone/node_modules/@enact/*`) + Vite pre-bundling resolved several - physical `react` copies. Fix: `resolve.dedupe: ['react','react-dom','react/jsx-runtime','react/jsx-dev-runtime']`. -- **R4 — webpack's HMR API in app source** (`module is not defined`). Some apps - guard reducer hot-reload with `if (module.hot) { module.hot.accept(…) }`; - `module` exists in the webpack runtime but not in Vite's browser ESM (app source - isn't CJS-wrapped like pre-bundled deps). Vite's `define` can't replace it - (esbuild treats `module` specially), so a `transform` plugin - (`enact-neutralize-webpack-hmr`) rewrites `module.hot` → `false` in app source; - the webpack-only block is dead-stripped. (Surfaced on the redux sample.) -- **R5 — Vite fs allow-list.** Apps that import source/assets from **sibling** - packages (e.g. the aggregate `all-samples`) are blocked by Vite's `server.fs` - allow-list ("outside of Vite serving allow list"). Fix: `server.fs.strict = - false`, matching webpack-dev-server's unrestricted file serving. -- **R6 — dependency-scan churn / repeated reloads.** Enact apps ship no - `index.html`, so Vite's dep scanner had no entry to crawl and discovered deps - lazily on first request — each new one triggering a re-optimize + full page - reload (severe for apps importing from many packages). Fix: - `optimizeDeps.entries = [<app entry>]` so the scanner crawls the whole import - graph (incl. cross-package imports) and pre-bundles in one pass. -- **R7 — theme i18n resource 404s.** `ViteILibPlugin` set `ILIB_<THEME>_PATH` - (used by the theme's `$L`/`ResBundle` as `basePath`) to the theme **package - root** instead of its `resources/` dir, so the loader fetched - `…/ilibmanifest.json` (404) and then blindly requested the default string paths - (`strings.json`, `en/strings.json`, … — all 404). Fix: point the constant at the - served data dir; the iLib **base** still points at the package dir (its loader - appends `locale/` itself). In `dev-utils/plugins/ViteILibPlugin`. -- **R8 — duplicate `@enact` copies.** Apps that aggregate independently-installed - sibling packages resolve a separate physical copy of every `@enact/*` dep, - multiplying dep-optimization + bundle size (and risking multiple-instance bugs, - the `@enact` analogue of R3). Fix: extend `resolve.dedupe` to the app's installed - `@enact/*` packages (collapsed e.g. 15 copies → 1 on `all-samples`). +### Config details handled -The eight config issues (all in `config/vite.config.js` unless noted) — the -non-obvious part of the port — were: +The non-obvious parts of the port (all in `config/vite.config.js` unless noted): 1. **ESM must be preserved for Rollup.** `babel-preset-enact`'s `@babel/preset-env` uses `modules: 'auto'`, which emits **CommonJS** unless the caller advertises ESM support. `babel-loader` sets this; `@vitejs/plugin-react` does **not**, so - the app collapsed into un-bundled runtime `require()` (a 1 kB bundle). Fix: pass - `babel.caller = { supportsStaticESM: true, … }`. -2. **PostCSS plugins must be instances.** Vite's `css.postcss.plugins` wants - instantiated plugins, not the string names `postcss-loader` resolves. Fix: - `require()` + invoke each (`loadPostCss`). + the config passes `babel.caller = { supportsStaticESM: true, … }`. +2. **PostCSS plugins are instances.** Vite's `css.postcss.plugins` wants + instantiated plugins, not string names — `require()` + invoke each (`loadPostCss`). 3. **`cssModuleIdent` loader context + CSS-safe names.** It reads - `context.rootContext` (for the hash); passing only `resourcePath` threw - `path.relative(undefined,…)`. Fix: pass `{resourcePath, rootContext: app.context}`. - Also, for **nested** `@enact` deps (e.g. - `@enact/limestone/node_modules/@enact/ui/…`) the derived readable name embeds a - literal `@`, invalid unescaped in a CSS class selector (108 `css-syntax-error` - warnings + broken selectors on `qa-dropdown`). Webpack avoids this by using - short hashes in production; our config uses readable names in both modes, so we - sanitize the ident to `[A-Za-z0-9_-]` (the trailing hash keeps it unique). + `context.rootContext` (for the hash), so the config passes + `{resourcePath, rootContext: app.context}`. For **nested** `@enact` deps the + derived readable name would embed a literal `@`, invalid unescaped in a CSS class + selector, so the ident is sanitized to `[A-Za-z0-9_-]` (the trailing hash keeps + it unique). (Webpack sidesteps this with short hashes in production; this config + uses readable names in both modes.) 4. **JSX-in-`.js` for the dev scanner.** esbuild's dep scanner defaults `.js` to - the `js` loader and can't parse Enact's JSX-in-`.js`. Fix: + the `js` loader and can't parse Enact's JSX-in-`.js`: `optimizeDeps.esbuildOptions.loader = { '.js': 'jsx' }`. -5. **iLib non-browser loaders** (see below) break both the Rollup build and the - esbuild optimizer. Fix: a shared `ILIB_LOADER_RE` neutralizes them via - `build.commonjsOptions.ignore` and an esbuild stub plugin. -6. **`@enact/*` deps ship raw source.** Unlike most packages, `@enact/*` is - distributed unbuilt (`main` points at `src/`): JSX-in-`.js`, ESM, decorators, - and Babel proposals like `export default from 'ilib'`. It must be transpiled - like app code — exactly what webpack's `exclude: /node_modules.(?!@enact)/` does. - Two fixes, because the build and the dev pre-bundler use different engines: - (a) **Rollup build** — set the react plugin's +5. **iLib non-browser loaders** (see item 1 in *webOS-specific features*) would break + both the Rollup build and the esbuild optimizer; a shared `ILIB_LOADER_RE` + neutralizes them via `build.commonjsOptions.ignore` and an esbuild stub plugin. +6. **`@enact/*` deps ship raw source.** `@enact/*` is distributed unbuilt (`main` + points at `src/`): JSX-in-`.js`, ESM, decorators, and Babel proposals like + `export default from 'ilib'`. It is transpiled like app code (webpack does this + via `exclude: /node_modules.(?!@enact)/`). Two mechanisms, because build and dev + pre-bundler use different engines: (a) the react plugin's `exclude: /[\\/]node_modules[\\/](?!@enact[\\/])/` so babel-preset-enact runs on - `@enact/*`. (b) **Dev dependency optimizer** — esbuild can't parse the raw - syntax at all (e.g. `export default from` → `Expected ";"`), so an - `optimizeDeps` esbuild plugin (`enact-babel-optimize`) runs babel-preset-enact - on `@enact/*` source (ESM-preserving) before esbuild pre-bundles it. - (Fix (a) surfaced on Limestone; fix (b) surfaced on apps like `qa-dropdown` - whose graph pulls `@enact/i18n` into pre-bundling.) -7. **LESS/CSS `~` npm imports.** `~pkg` resolves via webpack in `less-loader`; - Vite has no equivalent. Fix: a custom Less `FileManager` (`lessTildeImportPlugin`) - for LESS `@import`s, plus a `resolve.alias` `{find: /^~/, replacement: ''}` for - plain CSS `@import`s. -8. **`~` in `@import-json` rules.** The webpack config had an inline - `postcss-import-json-tilde` plugin (which I initially missed) to resolve `~` - before `@daltontan/postcss-import-json`. Fix: ported as `tildeJsonImportPlugin`. - -## What ports cleanly (done in `vite.config.js`) - -| Concern | webpack | Vite equivalent | + `@enact/*`; (b) an `optimizeDeps` esbuild plugin (`enact-babel-optimize`) runs + babel-preset-enact on `@enact/*` source (ESM-preserving) before esbuild + pre-bundles it (esbuild can't parse the raw syntax, e.g. `export default from`). +7. **LESS/CSS `~` npm imports.** A custom Less `FileManager` + (`lessTildeImportPlugin`) for LESS `@import`s, plus a `resolve.alias` + `{find: /^~/, replacement: ''}` for plain CSS `@import`s. +8. **`~` in `@import-json` rules.** Ported as `tildeJsonImportPlugin`, resolving `~` + before `@daltontan/postcss-import-json`. + +## webpack → Vite mapping + +| Concern | webpack | Vite | | --- | --- | --- | | JSX / TS / decorators | `babel-loader` + `babel-preset-enact` | `@vitejs/plugin-react` with `babel.presets: [babel-preset-enact]` | | App options (ri, accent, alias, title, publicUrl…) | `optionParser` (`@enact/dev-utils`) | same `optionParser`, reused verbatim | @@ -152,213 +89,181 @@ non-obvious part of the port — were: | `define` globals (`NODE_ENV`, `PUBLIC_URL`, `ENACT_PACK_ISOMORPHIC`, `ENACT_PACK_NO_ANIMATION`) | `DefinePlugin` | `define` | | Content hashing / no-split-css | `output.[contenthash]`, `splitChunks` | `rollupOptions.output`, `build.cssCodeSplit` | | Minification | Terser + CssMinimizer | `build.minify: 'terser'`, `cssMinify` | -| HTML document (no `index.html` in Enact apps) | `HtmlWebpackPlugin` + `.ejs` | `@enact/dev-utils` `ViteHtmlPlugin` renders the same template; serves it in dev, emits `index.html` (entry + CSS) in build | +| HTML document (no `index.html` in Enact apps) | `HtmlWebpackPlugin` + `.ejs` | `ViteHtmlPlugin` renders the same template; serves it in dev, emits `index.html` (entry + CSS) in build | | Polyfills first | `entry: [polyfills, appMain]` | generated combined entry (`node_modules/.cache/enact-vite/index.js`) | -| **iLib i18n runtime** (`ILIB_*` constants + locale/resource data) | `ILibPlugin` (`DefinePlugin` + asset emission) | `@enact/dev-utils` `ViteILibPlugin` — defines the constants (build + dev-optimizer) and copies (build) / serves (dev) the data | +| **iLib i18n runtime** (`ILIB_*` constants + locale/resource data) | `ILibPlugin` | `ViteILibPlugin` — defines the constants (build + dev-optimizer) and copies (build) / serves (dev) the data | | **iLib locale filtering** (`-l used/tv/signage/all/list`) | via isomorphic/prerender flow | `ViteILibPlugin` `locales` option — trims the emitted/served manifest to requested locales + shared data | -| **webOS metadata** (`appinfo.json` + icons, localized appinfo) | `WebOSMetaPlugin` | `@enact/dev-utils` `ViteWebOSMetaPlugin` — emits (build) / serves (dev) appinfo + assets; title fallback | +| **webOS metadata** (`appinfo.json` + icons, localized appinfo) | `WebOSMetaPlugin` | `ViteWebOSMetaPlugin` — emits (build) / serves (dev) appinfo + assets; title fallback | | **ESLint** (`eslint-config-enact`, `--no-linting`) | `eslint-webpack-plugin` | inline `enact-eslint` plugin — lints at build start; errors fail the build, dev warns | | Source maps | `devtool` | `build.sourcemap` / `css.devSourcemap` | -## Webpack-only concerns and their status - -Items 1–6 are `@enact/dev-utils` webpack plugins that tap the -`compiler`/`compilation` lifecycle; items 7–9 are webpack loader/config behaviors, -not dev-utils plugins. Status varies (ported / dropped / resolved / not yet): - -1. ~~**`ILibPlugin`**~~ — **ported** as - [`ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin). Since `@enact/i18n`'s - runtime `Loader.js` is bundler-agnostic (XHR from the `ILIB_*` constants), the - Vite plugin defines those constants (build **and** the dev dep-optimizer, via - `optimizeDeps.esbuildOptions.define`) and makes the data available — copying the - iLib `locale/` + app/theme `resources/` trees on `writeBundle` (build) and - serving them from source via middleware (dev). Non-browser iLib loaders are - neutralized separately (`ILIB_LOADER_RE`). **Locale filtering** (webpack's `-l`) - is supported via the `locales` option — `-l en-US,ko-KR` trims 70 MB → 19 MB - (6,755 → 1,988 files) and emits a trimmed manifest. **Validated:** - `/node_modules/ilib` baked into the prod bundle; full and filtered data - copied (build) / served (HTTP 200, dev). -2. ~~**`WebOSMetaPlugin`**~~ — **ported** as - [`ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin). Discovers - the root `appinfo.json` (root or `./webos-meta/`) + localized +## webOS-specific features and how they're implemented + +Items 1–6 correspond to `@enact/dev-utils` webpack plugins that tap the +`compiler`/`compilation` lifecycle; items 7–9 are webpack loader/config behaviors. + +1. **iLib runtime (`ILibPlugin`)** → **`ViteILibPlugin`** + ([`dev-utils/plugins/ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin)). + `@enact/i18n`'s runtime `Loader.js` is bundler-agnostic (XHR from the `ILIB_*` + constants), so the Vite plugin defines those constants (build **and** the dev + dep-optimizer, via `optimizeDeps.esbuildOptions.define`) and makes the data + available — copying the iLib `locale/` + app/theme `resources/` trees on + `writeBundle` (build) and serving them from source via middleware (dev). + Non-browser iLib loaders are neutralized separately (`ILIB_LOADER_RE`). + **Locale filtering** is supported via the `locales` option — `-l en-US,ko-KR` + trims 70 MB → 19 MB (6,755 → 1,988 files) and emits a trimmed manifest. +2. **webOS metadata (`WebOSMetaPlugin`)** → **`ViteWebOSMetaPlugin`** + ([`dev-utils/plugins/ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin)). + Discovers the root `appinfo.json` (root or `./webos-meta/`) + localized `resources/**/appinfo.json`, emits them and their referenced icon/splash assets (build: `writeBundle` copy; dev: middleware), and supplies the `<title>` - fallback via `ViteWebOSMetaPlugin.readTitle`. **Validated:** `appinfo.json` + - `icon*.png` land in `dist` and serve (HTTP 200) in dev. `$`-prefixed sys-assets + fallback via `ViteWebOSMetaPlugin.readTitle`. `$`-prefixed sys-assets (`$icon.png` → `sys-assets/<spec>/icon.png`, emitted per-spec preserving the - layout, appinfo value left untouched) are now handled — matching the webpack - plugin; verified against a fixture (sys-assets across specs, dedup across - locales, regular assets, untouched `$` values). -3. ~~**`PrerenderPlugin` + isomorphic mixin**~~ — **ported** (`mixins/vite-isomorphic.js` + - `pack.js` `viteIsomorphic`). Uses a real **`vite build --ssr`** of the app entry (the key - correction from the first spike, which used `ssrLoadModule` and hit the JSX-in-`.js` - transform gap), then per-locale server render (`FileXHR` for iLib locale data, no DOM - shim needed) + assembly into the webpack-compatible output (fallback `index.html` + - deduped `index.<variant>.html` + `locale-map.json` + per-locale webOS `appinfo.json`), - reusing the bundler-agnostic `templates.js`/`FileXHR`. Browser-validated end-to-end on - qa-a11y (`-p -i -l en-US,ko-KR`): prerendered markup hydrates with no console warnings in the - production build. (A dev build shows two dev-only React warnings that are by-design in - `@enact/i18n` — locale class deferred to the client — and identical to webpack's isomorphic - output, including with `--externals`; `-p` strips them. Details in the scope doc.) - Full findings/phases in [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). -4. ~~**`SnapshotPlugin`**~~ — **ported (build-complete; on-device validation pending - the toolchain).** `--snapshot` (which implies `--isomorphic`) now builds through - `mixins/vite-snapshot.js`: the client build becomes a **self-contained UMD** `main.js` - (`output.format:'umd'`, `name:'App'`, `preserveEntrySignatures:'strict'`, - `inlineDynamicImports`) with a `global` banner for the bare-V8 context, so the app's - default export is exposed as the `App` global — mirroring webpack's - `output.library='App'`/`libraryTarget='umd'`. The snapshot helpers are reimplemented in - **ESM** ([`snapshot-helper-esm.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-helper-esm.js) - + [`snapshot-mock.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-mock.js), reusing - the bundler-agnostic `mock-window.js` + `@enact/core/snapshot`): import order deterministically - installs the mock window before `react-dom/client` loads (Rollup hoists CJS requires, so the - original CJS helper's in-line ordering can't be reproduced), and `global.updateEnvironment` - is defined for the on-device window rebind. A resolver redirects `react-dom/client` → the - facade and no-ops absent optional deps (`fbjs` is gone in React 19; a theme may lack - `internal/$L`). After the isomorphic prerender/assembly (startup script kept **classic**, since - `main.js` is UMD), `mksnapshot` (`V8_MKSNAPSHOT`) runs against `main.js` to emit - `snapshot_blob.bin` and tag `appinfo.json` `v8SnapshotFile`. - **Validated end-to-end with a real toolchain.** Against `mksnapshot` (V8) the UMD bundle - produces a genuine, non-zero startup blob (qa-a11y: **4.6 MB**, in line with the ~4.3 MB - webpack reference). The syntax must parse in the target board's V8; the app's browserslist - drives the output by default, and `V8_SNAPSHOT_TARGET` force-lowers it for a much older - firmware than the app targets. `--snapshot --externals` is unsupported (the snapshot must - embed `@enact`), matching webpack. The blob's V8 must match the firmware — a mismatched - `mksnapshot` is rejected/unparseable (see the matrix below). - - **core-js in the snapshot — parity with webpack, verified.** core-js is included by default - (as in the webpack path). Its WeakMap-based internal state serializes fine on a **modern** - snapshot V8, but a **very old** one (~Chrome 53) can't serialize a WeakMap-with-entries — - `mksnapshot` throws `illegal access` → 0-byte blob. This is a **core-js-3 + old-V8 - limitation, not a bundler difference**: measured on the *same* `mksnapshot.53` against - qa-a11y built at `chrome 53`: + layout, appinfo value left untouched) are handled — matching the webpack plugin. +3. **Isomorphic prerendering (`PrerenderPlugin` + isomorphic mixin)** → + **`mixins/vite-isomorphic.js`** + `pack.js` `viteIsomorphic`. Uses a real + **`vite build --ssr`** of the app entry, then per-locale server render + (`FileXHR` for iLib locale data, no DOM shim needed) + assembly into the + webpack-compatible output (fallback `index.html` + deduped `index.<variant>.html` + + `locale-map.json` + per-locale webOS `appinfo.json`), reusing the + bundler-agnostic `templates.js`/`FileXHR`. Browser-validated end-to-end on + qa-a11y (`-p -i -l en-US,ko-KR`): prerendered markup hydrates with no console + warnings in the production build. (A dev build shows two dev-only React warnings + that are by-design in `@enact/i18n` — locale class deferred to the client — and + identical to webpack's isomorphic output, including with `--externals`; `-p` + strips them.) Full detail in + [`vite-isomorphic-scope.md`](./vite-isomorphic-scope.md). +4. **V8 snapshot (`SnapshotPlugin`)** → **`mixins/vite-snapshot.js`**. `--snapshot` + (which implies `--isomorphic`) builds the client as a **self-contained UMD** + `main.js` (`output.format:'umd'`, `name:'App'`, `preserveEntrySignatures:'strict'`, + `inlineDynamicImports`) with a `global` banner for the bare-V8 context, exposing + the app's default export as the `App` global — mirroring webpack's + `output.library='App'`/`libraryTarget='umd'`. The snapshot helpers are ESM + ([`snapshot-helper-esm.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-helper-esm.js) + + [`snapshot-mock.js`](../../dev-utils/plugins/SnapshotPlugin/snapshot-mock.js), + reusing `mock-window.js` + `@enact/core/snapshot`): import order deterministically + installs the mock window before `react-dom/client` loads (Rollup hoists CJS + requires, so the original CJS helper's in-line ordering can't be reproduced), and + `global.updateEnvironment` is defined for the on-device window rebind. A resolver + redirects `react-dom/client` → the facade and no-ops absent optional deps (`fbjs` + is gone in React 19; a theme may lack `internal/$L`). After the isomorphic + prerender/assembly (startup script kept **classic**, since `main.js` is UMD), + `mksnapshot` (`V8_MKSNAPSHOT`) runs against `main.js` to emit `snapshot_blob.bin` + and tag `appinfo.json` `v8SnapshotFile`. + + Against `mksnapshot` (V8) the UMD bundle produces a genuine, non-zero startup blob + (qa-a11y: **4.6 MB**, in line with the ~4.3 MB webpack reference). The syntax must + parse in the target board's V8; the app's browserslist drives the output by + default, and `V8_SNAPSHOT_TARGET` force-lowers it for firmware older than the app + targets. `--snapshot --externals` is unsupported (the snapshot must embed + `@enact`), matching webpack. + + **core-js in the snapshot: parity with webpack.** core-js is included by default + (as in the webpack path). Its WeakMap-based internal state serializes fine on a + **modern** snapshot V8, but a **very old** one (~Chrome 53) can't serialize a + WeakMap-with-entries — `mksnapshot` throws `illegal access` → 0-byte blob. This is + a **core-js-3 + old-V8 limitation, not a bundler difference**, measured on the + *same* `mksnapshot.53` against qa-a11y built at `chrome 53`: | Bundle (chrome 53 target, core-js 3.22.8) | Result | | --- | --- | | **webpack** + core-js | `illegal access` → 0-byte blob | | **Vite** + core-js | `illegal access` → 0-byte blob (identical) | - | **Vite**, `ENACT_SNAPSHOT_NO_COREJS=1` | ✅ 4.6 MB blob | - - So webpack and Vite behave identically; Vite additionally offers `ENACT_SNAPSHOT_NO_COREJS` - to still emit a blob (minus runtime builtin polyfills) on such old firmware, where webpack - emits nothing. On a firmware-matched **modern** `mksnapshot` (e.g. Chrome 132) neither the - `V8_SNAPSHOT_TARGET` lowering nor the no-core-js opt-out is needed — the default build (app - target + core-js) is correct for both bundlers. **Not validated here:** the blob on the - actual firmware (needs that firmware's `mksnapshot`) + on-device hydration. -5. ~~**Framework externals**~~ — **ported** (`mixins/vite-framework.js` + - `pack.js` `--framework`/`--externals`). Webpack's DLL maps deep module requests to - IDs in a prebuilt bundle via a manifest; the Vite analog is a shared framework ESM - build addressed by an **import map** (exact keys per specifier, from a manifest), - with `build.rollupOptions.external` on the app build. Browser-validated end-to-end - on limestone/qa-a11y: 138-specifier framework + `enact.css`, app externalizes 60 - specifiers, boots fully styled with a single React instance, console clean. Full - findings in [`vite-framework-externals-spike.md`](./vite-framework-externals-spike.md). -6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not - needed under Vite (different FS handling). *Drop.* -7. ~~**`node-polyfill-webpack-plugin`**~~ — **ported.** `global` is supplied by - `ViteHtmlPlugin`'s head shim (R1) and `process.env.NODE_ENV` by `define`; fuller - coverage (`Buffer`, full `process`, and the node builtin modules — the webpack - plugin's `additionalAliases: console/domain/process/stream`) is now wired via - **`vite-plugin-node-polyfills`** in `config/vite.config.js`, gated to browser - targets. Globals are injected **reference-only** (like webpack's `ProvidePlugin` - — no global `typeof process` flip, no bundle bloat when unused; verified qa-a11y - doesn't bundle `buffer` and exposes no `window.Buffer`/`process`). A small + | **Vite**, `ENACT_SNAPSHOT_NO_COREJS=1` | 4.6 MB blob | + + webpack and Vite behave identically; Vite additionally offers + `ENACT_SNAPSHOT_NO_COREJS` to still emit a blob (minus runtime builtin polyfills) + on such old firmware, where webpack emits nothing. On a firmware-matched **modern** + `mksnapshot` (e.g. Chrome 132) neither the `V8_SNAPSHOT_TARGET` lowering nor the + no-core-js opt-out is needed — the default build is correct for both bundlers. +5. **Framework externals** → **`mixins/vite-framework.js`** + `pack.js` + `--framework`/`--externals`. Webpack's DLL maps deep module requests to IDs in a + prebuilt bundle via a manifest; the Vite analog is a shared framework ESM build + addressed by an **import map** (exact keys per specifier, from a manifest), with + `build.rollupOptions.external` on the app build. Browser-validated end-to-end on + limestone/qa-a11y: 138-specifier framework + `enact.css`, app externalizes 60 + specifiers, boots fully styled with a single React instance, console clean. +6. **`GracefulFsPlugin`** — patches webpack's output FS to avoid EMFILE. Not needed + under Vite (different FS handling); intentionally not ported. +7. **Node polyfills (`node-polyfill-webpack-plugin`)** → **`vite-plugin-node-polyfills`**. + `global` is supplied by `ViteHtmlPlugin`'s head shim and `process.env.NODE_ENV` + by `define`; fuller coverage (`Buffer`, full `process`, and the node builtin + modules) is wired via `vite-plugin-node-polyfills`, gated to browser targets. + Globals are injected **reference-only** (like webpack's `ProvidePlugin` — no + global `typeof process` flip, no bundle bloat when unused; qa-a11y doesn't bundle + `buffer` and exposes no `window.Buffer`/`process`). A small `enact-node-polyfill-resolver` `resolveId` plugin resolves the injected shim - specifiers (`vite-plugin-node-polyfills/*`, `node-stdlib-browser`) from the CLI's - `node_modules`, since apps are built with `root: app.context` and can't reach - them otherwise. For the SSR/isomorphic build, `vite-isomorphic.js`'s + specifiers from the CLI's `node_modules`, since apps are built with + `root: app.context`. For the SSR/isomorphic build, `vite-isomorphic.js`'s `applySsrBuild` drops these plugins so the Node bundle uses the real builtins - (`path`/`fs`/`crypto`); verified the isomorphic build still prerenders cleanly. -8. ~~**`icss` mode for non-`*.module` CSS / `forceCSSModules`**~~ — **both ported.** - The Enact `forceCSSModules` option (scope ALL css/less/scss, not just `*.module.*`) - is wired via `enactForceCSSModulesPlugin` in `config/vite.config.js`. Vite decides - module-ness only from the `.module.` filename infix (`cssModuleRE`) with no override - hook, so the plugin resolves each non-module style import and redirects it to a - **virtual `.module` id** — keeping the real directory so LESS `@import`/`url()` still - resolve, serving the real file via `load`, and letting `generateScopedName` recover - the real path for webpack-parity hashing. Verified end-to-end: non-module CSS **and** - LESS scope and export a class map, matching webpack's ident (`src_App_App_app__<hash>`). - - The webpack `mode:'icss'` path (the **default**, option off) was initially assessed as - a no-op on the grounds that Vite already leaves non-module CSS global and `:export` - is unused in @enact/limestone. **That assessment was wrong** and is now fixed. Scoping - is indeed identical, but css-loader in `icss` mode still emits a **default export** - (the ICSS `:export` locals, usually `{}`), whereas Vite emits *no* default export for - plain CSS at build time. So the classic Enact idiom on a **non-module** stylesheet — + (`path`/`fs`/`crypto`). +8. **`icss` mode for non-`*.module` CSS / `forceCSSModules`.** The Enact + `forceCSSModules` option (scope ALL css/less/scss, not just `*.module.*`) is + wired via `enactForceCSSModulesPlugin` in `config/vite.config.js`. Vite decides + module-ness only from the `.module.` filename infix (`cssModuleRE`) with no + override hook, so the plugin resolves each non-module style import and redirects + it to a **virtual `.module` id** — keeping the real directory so LESS + `@import`/`url()` still resolve, serving the real file via `load`, and letting + `generateScopedName` recover the real path for webpack-parity hashing. Non-module + CSS and LESS scope and export a class map, matching webpack's ident + (`src_App_App_app__<hash>`). + + The default path (option off) matches webpack's `mode:'icss'`: css-loader in + `icss` mode leaves class names global but still emits a **default export** (the + ICSS `:export` locals, usually `{}`). Vite emits *no* default export for plain + CSS, so the classic Enact idiom on a **non-module** stylesheet — ```js import css from './App.less'; // plain .less, global classes kind({styles: {css, className: 'app'}}); ``` - — is a hard Vite build error (`"default" is not exported by "src/App/App.less"`), - even though webpack builds it fine (`classnames/bind` just falls back to the literal - global class name). `limestone/samples/qa-i18n` hits exactly this; `qa-a11y` does not, - because it uses `App.module.less`. - - Parity is restored by `enactICSSInteropPlugins()` — **without scoping anything**: - - `enact-icss-extract` (normal order → after `vite:css` compiles LESS/SCSS, before - `vite:css-post` builds the JS proxy) lifts `:export {…}` blocks into a locals map - and strips them from the emitted CSS, as css-loader does. - - `enact-icss-default-export` (`enforce:'post'` → after `vite:css-post`) appends - `export default <locals>` when the proxy has none. Modules that already have a - default export (dev's CSS-string proxy, `?inline`/`?url`/`?raw`) are left alone. - - Verified against webpack on `qa-i18n` with a `.app{color:#123456}` rule plus an - `:export{brandColor:#ff0000}` block — both bundlers emit the identical - `styles:{css:{brandColor:"#ff0000"},className:"app"}`, keep `.app` **global** - (unscoped), and strip `:export` from the CSS. `*.module.*` files are untouched - (`qa-a11y` still scopes 623/660 classes; the rest are the deliberately global - `enact-locale-*`). -9. ~~**LESS/CSS `~` npm imports**~~ — **resolved** (by config fixes #7 and #8 in - the config-issues list above): `lessTildeImportPlugin` (LESS), - `resolve.alias /^~/` (CSS), and `tildeJsonImportPlugin` (`@import-json`). - -## Command wiring (applied, behind a flag) - -`commands/pack.js` and `commands/serve.js` now branch to the Vite path when it is -opted into via **`--vite`** or **`ENACT_BUNDLER=vite`**; otherwise webpack runs -unchanged. Both bundlers coexist during migration. + would otherwise be a build error (`"default" is not exported`). Parity is provided + by `enactICSSInteropPlugins()` **without scoping anything**: `enact-icss-extract` + lifts `:export {…}` blocks into a locals map and strips them from the emitted CSS + (as css-loader does); `enact-icss-default-export` (`enforce:'post'`) appends + `export default <locals>` when the proxy has none. Verified against webpack on + `qa-i18n`: both bundlers emit identical `styles:{css:{brandColor:"#ff0000"},className:"app"}`, + keep `.app` global, and strip `:export`. `*.module.*` files are untouched. +9. **LESS/CSS `~` npm imports** — resolved by config items 7 and 8 in the *Config + details handled* list: `lessTildeImportPlugin` (LESS), `resolve.alias /^~/` (CSS), + and `tildeJsonImportPlugin` (`@import-json`). + +## Command wiring + +`commands/pack.js` and `commands/serve.js` branch to the Vite path when opted into via +**`--vite`** or **`ENACT_BUNDLER=vite`**; otherwise webpack runs unchanged. - `enact serve --vite` → `vite.createServer(...).listen()` (native ESM dev server, HMR via `@vitejs/plugin-react`). - `enact pack --vite` / `enact pack -p --vite` → `vite.build(...)` (supports `--watch`, `-o/--output`, `--content-hash`, `--no-split-css`, `-l/--locales`, `--no-linting`, `--entry`). -- Build-shaping flags are wired via **`mixins.applyVite`** (the Vite counterpart to - the webpack `mixins.apply`, in `dev-utils/mixins/vite.js`): +- Build-shaping flags via **`mixins.applyVite`** (the Vite counterpart to the webpack + `mixins.apply`, in `dev-utils/mixins/vite.js`): - `--stats` → static bundle-analysis treemap `dist/stats.html` (`rollup-plugin-visualizer`, mirroring webpack's `webpack-bundle-analyzer`). - `--verbose` → raises Vite's log level and narrates build phases with a module - count (no percentage — Rollup has no fixed total up front, unlike webpack's `ProgressPlugin`). + count (no percentage — Rollup has no fixed total up front). - `--no-minify` (private) → Terser with `mangle:false` + beautify, keeping dead-code - removal (mirrors the webpack `unmangled` mixin). Only affects production builds. + removal (mirrors the webpack `unmangled` mixin). Production builds only. - `enact eject --vite` wires the ejected app's scripts to the Vite path (see [vite-eject-testing.md](vite-eject-testing.md)). -- **`--framework` / `--externals`** are wired via **`mixins/vite-framework.js`** (the - Vite counterpart to the webpack DLL `framework`/`externals` mixins): `--framework` +- **`--framework` / `--externals`** via **`mixins/vite-framework.js`**: `--framework` builds the shared `@enact`+react+ilib bundle as reusable ESM + a specifier manifest + one `enact.css`; `--externals=<path>` externalizes those specifiers from the app build and injects an import map (+ the shared stylesheet) resolved from the manifest. `--externals-public` sets the import-map base URL (remote framework path). - Browser-validated on limestone/qa-a11y. See - [vite-framework-externals-spike.md](vite-framework-externals-spike.md). -- **`--isomorphic`** is wired via **`mixins/vite-isomorphic.js`** + `pack.js`'s - `viteIsomorphic` (client `hydrateRoot` build + `vite build --ssr` + per-locale prerender + - webpack-compatible HTML/`locale-map.json`/`appinfo.json` assembly). Browser-validated. -- `--snapshot` is not ported (needs the webOS `V8_MKSNAPSHOT` toolchain) and prints a "not - yet supported, ignored" notice. - -The reusable bundler plugins were **added to `@enact/dev-utils`** — mirroring how -the webpack plugins (`ILibPlugin`, `WebOSMetaPlugin`, …) live there — and are -consumed by `config/vite.config.js`: +- **`--isomorphic`** via **`mixins/vite-isomorphic.js`** + `pack.js`'s `viteIsomorphic` + (client `hydrateRoot` build + `vite build --ssr` + per-locale prerender + + webpack-compatible HTML/`locale-map.json`/`appinfo.json` assembly). +- **`--snapshot`** via **`mixins/vite-snapshot.js`** (see item 4 above); emits the blob + when `V8_MKSNAPSHOT` is set and prints a skip notice otherwise (the app still builds + and runs without the snapshot). + +The reusable bundler plugins live in `@enact/dev-utils` — mirroring the webpack plugins +(`ILibPlugin`, `WebOSMetaPlugin`, …) — and are consumed by `config/vite.config.js`: [`ViteHtmlPlugin`](../../dev-utils/plugins/ViteHtmlPlugin), [`ViteILibPlugin`](../../dev-utils/plugins/ViteILibPlugin), and -[`ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin). The -config-level pieces (PostCSS chain incl. `~`/JSON-import handling, LESS -`modifyVars`, the ESLint plugin) stay in `cli/config`, matching where -`getStyleLoaders`/the eslint config live for webpack. - -> **dev-utils must be current.** Because these plugins live in `@enact/dev-utils`, -> the copy under `cli/node_modules/@enact/dev-utils` must include them. Symlink the -> sibling source (`npm link`/junction) or reinstall so the CLI picks up -> `ViteHtmlPlugin`, `ViteILibPlugin`, and `ViteWebOSMetaPlugin`. (They have been -> synced into the local install here.) +[`ViteWebOSMetaPlugin`](../../dev-utils/plugins/ViteWebOSMetaPlugin). The config-level +pieces (PostCSS chain incl. `~`/JSON-import handling, LESS `modifyVars`, the ESLint +plugin) stay in `cli/config`, matching where `getStyleLoaders`/the eslint config live +for webpack. ## Try it @@ -371,184 +276,5 @@ enact pack -p --vite # production build (full iLib data) enact pack -p --vite -l en-US,ko-KR # production build, locale-filtered ``` -> Status: **validated** on Sandstone and Limestone (build + serve), including -> iLib i18n runtime + locale filtering, webOS metadata, and ESLint. Node 20+ is -> required for `require()` of the ESM-only `vite` package (validated on Node 24). - -## Ported, pending on-device validation - -- **`--snapshot`** (gap #4) — **build-complete and locally validated** (the UMD `main.js` - evaluates snapshot-safe in a bare V8 and exposes the `App`/`updateEnvironment`/`ReactDOMClient` - globals); only the actual `mksnapshot` blob + on-device hydration remain, which need the - firmware-specific `V8_MKSNAPSHOT` toolchain and a webOS board. See gap #4 above and the - **Testing `--snapshot` on a webOS board** section below. - -## Everything else is ported - -- ~~**Framework self-inclusion in a theme repo**~~ — **ported.** Building `--framework` - *inside* a theme repo (e.g. limestone) now includes the theme's own components, mirroring - webpack's `libraries.push('.')`. `mixins/vite-framework.js` `enumerateSelfSpecs(context)` - detects a `@enact/*` theme package (has `ThemeDecorator`/`MoonstoneDecorator`, or is - `@enact/i18n`) and enumerates its own component subpaths as `@enact/<theme>/<component>` - specifiers; `applyFramework` adds a `resolve.alias` (`@enact/<theme>` → repo root, covering - transitive self-references) and extends `commonjsOptions` to the repo root so the theme's - own CJS-in-source (e.g. a `module.exports` `fontGenerator`) interops. Verified on limestone: - a repo-root `--framework` build emits **138 specifiers = 56 own components + 76 node_modules - `@enact` + react/ilib**, with `enact.css`. The change is gated on theme-repo detection, so a - sample/app-context build (the Jenkins path) is unaffected. (`--externals-polyfill` — move - core-js into the framework — is also wired: `pack --framework --externals-polyfill` folds - core-js in, and `pack --externals=<path> --externals-polyfill` delegates it out of the app.) - -## Measured: webpack vs Vite, command by command - -Real measurements, not estimates. **App:** `limestone/samples/qa-a11y`. **Machine:** 22 -logical cores / 31.5 GB RAM, Windows. Both bundlers run the same `enact pack`/`enact serve` -CLI with `NODE_OPTIONS=--max-old-space-size=8192` (webpack's `--framework` build OOMs at the -default heap, so the larger heap is given to *both* for fairness). - -**Methodology** (metric set follows [rstackjs/build-tools-performance](https://github.com/rstackjs/build-tools-performance), -the reference bundler benchmark: startup, build no-cache/with-cache, memory, output size, -gzipped size): - -- **No cache** — `<app>/node_modules/.cache` (webpack's `cache:{type:'filesystem'}` + the - babel-loader cache) and `<app>/node_modules/.vite` (Vite's dep-optimizer cache) are deleted - before the run. This matters: webpack has a persistent build cache and Vite has none, so - *not* clearing it silently hands webpack a warm cache and makes the comparison meaningless. -- **With cache** — an immediate second run. Both runs build into an **empty** output dir, so - the cache is the only variable (otherwise the second run also pays to replace ~6.8k files - and reads as *slower* than the first). -- **Peak RSS** — the build process's own `process.resourceUsage().maxRSS` (exact, zero - overhead). Covers the main build process, not parallel minifier workers. -- **Gzipped** — gzip(level 9) of all emitted JS+CSS+HTML: a network-transfer proxy. -- Single run per cell (not 3-run averaged); treat differences under ~5% as noise. - -### Build & startup time - -| Command | webpack (no cache) | Vite (no cache) | webpack (cached) | Vite (cached) | -|---|---|---|---|---| -| `pack` (dev) | **33.8s** | 44.8s | **28.5s** | 40.8s | -| `pack -p` | **40.3s** | 48.0s | **36.3s** | 44.4s | -| `pack -p --no-minify` ¹ | 39.5s | 44.1s | — | — | -| `pack -p --content-hash` | **40.9s** | 47.1s | — | — | -| `pack -p -i` (isomorphic) | **53.5s** | 81.5s | — | — | -| `pack -p -i -l en-US,ko-KR` ² | **54.2s** | 73.5s | — | — | -| `pack -p --snapshot` | **54.9s** | 78.7s | — | — | -| `serve` (dev server ready) | 44.8s | **10.5s** | 38.6s | **7.5s** | - -### Peak memory (RSS) - -| Command | webpack | Vite | -|---|---|---| -| `pack` (dev) | **1162 MB** | 2355 MB | -| `pack -p` | **1310 MB** | 1854 MB | -| `pack -p -i` | **1334 MB** | 1920 MB | -| `pack -p --snapshot` | **1646 MB** | 2036 MB | -| `serve` | 1241 MB | **449 MB** | - -### Output size - -| Command | webpack files / total / main.js / gzip | Vite files / total / main.js / gzip | -|---|---|---| -| `pack` (dev) | 6779 / 71.8 MB / 5679 KB / 1011 KB | 6778 / 72.2 MB / **4283 KB** / **877 KB** | -| `pack -p` | 6778 / 61.0 MB / **1108 KB** / **385 KB** | 6777 / 61.8 MB / 1263 KB / 448 KB | -| `pack -p --no-minify` ¹ | 6778 / 61.2 MB / 1108 KB / 390 KB | 6777 / 63.8 MB / 3226 KB / 678 KB | -| `pack -p -i` | 6778 / 61.1 MB / **1108 KB** / **391 KB** | 6777 / 61.9 MB / 1263 KB / 453 KB | -| `pack -p -i -l en-US,ko-KR` ² | 6782 / 61.1 MB / 1108 KB / 392 KB | **2013 / 19.4 MB** / 1263 KB / 455 KB | -| `pack -p --snapshot` | 6778 / 61.1 MB / **1115 KB** / **393 KB** | 6777 / 61.9 MB / 1275 KB / 454 KB | - -Total output is dominated by iLib locale JSON (~60 MB); `main.js`/gzip are the figures that -track bundling quality. - -### What the numbers say - -- **Dev server is Vite's decisive win**: ready in **10.5s vs 44.8s** cold (**4.3×**) and - **7.5s vs 38.6s** warm (**5.1×**), using **449 MB vs 1241 MB** (**2.8× less**). This is the - day-to-day feedback loop and the main reason to migrate. -- **webpack is currently faster for production builds** — 16–19% on plain `-p`, and **~1.5×** - on `-i`/`--snapshot`. The isomorphic gap is structural: the Vite path runs **two** builds - (client + a real `vite build --ssr`), where webpack prerenders from a single compilation. -- **webpack's persistent cache is worth 10–16%**; Vite's dep cache buys 7–9% on builds but - ~30% on dev startup. -- **Vite uses ~40–60% more memory** to build (but ~⅓ as much to serve). -- **webpack's production bundle is smaller**: `main.js` 1108 KB vs 1263 KB (+14%), gzipped - 385 KB vs 448 KB (**+16%**). Worth a follow-up (CJS interop / tree-shaking differences); - it is the one clear regression in the Vite output. Note Vite's **dev** bundle is *smaller* - (4283 KB vs 5679 KB). - -¹ **`--no-minify` is not comparable — the webpack flag is a silent no-op.** webpack's -`main.js` is byte-identical with and without it (1108 KB, gzip 314 KB), while Vite's grows -2.6× as expected. Cause: `terser-webpack-plugin@5.6.1` normalizes constructor options into -`options.minimizer.options`, but `dev-utils/mixins/unmangled.js` writes to -`options.terserOptions` — a key v5 never reads, so `mangle` stays `{safari10:true}`. This is -a pre-existing webpack-path bug, unrelated to the Vite port. - -² **`-l` means different things in the two bundlers.** `locales` appears **nowhere** in -webpack's `ILibPlugin`: there, `-l` scopes *prerendering only* (matching `pack --help`: -"Locales for isomorphic mode") and the full iLib tree always ships. `ViteILibPlugin` -*additionally* trims the emitted locale data — hence **2013 files / 19.4 MB vs 6782 / 61.1 MB** -(**68% smaller**). Attractive for a TV app, but a **behavioral deviation**: a webpack build -made with `-l en-US,ko-KR` can still switch to any locale at runtime, whereas the Vite build -ships no data for unlisted locales and would fall back to unlocalized output. (Shared -non-locale data such as `localematch.json` is kept, so it degrades rather than crashes.) -Decide deliberately: gate it behind its own flag, or adopt it as intentional. - -### Not measured - -`pack -p --framework` and `pack -p --externals` are **excluded**. The webpack `--framework` -build alone measured **12,905s (3.6 hours)**, single-threaded, and `--externals` needs a -second framework build on top — together they dominated the run ~20:1 over every other -command combined. It also **OOMs at the default heap** (hence `--max-old-space-size=8192`). -Whether Vite's framework build is comparably slow is **unknown and worth measuring** — if it -is dramatically faster, that is likely the single strongest argument in this comparison. - -Harness: `scratchpad/bench.cjs` + `bench-run.cjs` (throwaway; not part of the repo). - -## Recommendation - -Adopt Vite behind a feature flag for the **browser dev/build** path first (biggest -DX win, lowest risk — measured **4–5× faster dev server startup** at **⅓ the memory**; -see the benchmark above) — validated end-to-end including i18n runtime, locale filtering, -webOS metadata, and ESLint. Beyond that, **`--isomorphic` and framework externals are -now ported and validated** too. **`--snapshot`** is now ported and locally validated -(snapshot-safe UMD bundle); it just needs a final on-device pass on a webOS board with -the `V8_MKSNAPSHOT` toolchain — see below. - -## Testing `--snapshot` on a webOS board - -The snapshot blob is a **build-time** artifact and requires a `mksnapshot` binary whose -V8 version **matches the target firmware's Chrome** (from the same webOS SDK/NDK release) — -e.g. a Chrome-132 board needs that firmware's `mksnapshot`, **not** an arbitrary/old one. A -mismatched binary either can't parse the modern output or produces a blob the board ignores -at load. `mksnapshot` is a **Linux** tool (32-bit for older releases); on Windows run it via -WSL or a Linux build machine (the guide's "doesn't support Windows OS" note). Steps: - -1. **Build with the firmware-matched toolchain**: - ``` - export V8_MKSNAPSHOT=/path/to/mksnapshot # matches the board's Chrome - ENACT_BUNDLER=vite enact pack -p --snapshot # add -l en-US,ko-KR for multi-locale - ``` - Expect: `Generated V8 snapshot blob (snapshot_blob.bin) and tagged appinfo.json.` - Verify `dist/snapshot_blob.bin` is **non-zero** and `dist/appinfo.json` has - `"v8SnapshotFile": "snapshot_blob.bin"`. (Without `V8_MKSNAPSHOT` the build still - succeeds and prints a skip notice; the app runs, just without the snapshot.) - *Old-firmware knobs (rarely needed):* `V8_SNAPSHOT_TARGET=chrome53` force-lowers the - syntax for a V8 older than the app targets, and `ENACT_SNAPSHOT_NO_COREJS=1` drops core-js - when that old V8 can't serialize its WeakMap state (see gap #4 — webpack hits the same - wall). On a modern firmware-matched `mksnapshot`, use neither. -2. **Package + install (developer mode, not hosted)** — the snapshot is loaded by WAM - from the app's local install dir, so it must be a packaged IPK, not a served URL: - ``` - ares-package dist - ares-install ./com.*.ipk # Developer Mode enabled on the board - ``` -3. **Confirm the snapshot is actually used** — launch the app and check it renders, then - confirm WAM loaded the blob (via WAM logs / the `--profile-deserialization` output, or a - measurable cold-start improvement) rather than silently falling back — a `mksnapshot` - whose V8 doesn't match the firmware is ignored at load, so "it rendered" alone isn't proof. - -If step 1's blob is 0-byte, check `mksnapshot`'s stderr: `Unexpected token` means the -binary is **older** than the app's syntax target (use the firmware-matched one, or -`V8_SNAPSHOT_TARGET`); `illegal access` in module init is the core-js/WeakMap case on very -old V8 (`ENACT_SNAPSHOT_NO_COREJS=1`, same limitation as webpack); a `window`/`document` -`ReferenceError` means app/framework code touched the DOM at snapshot time (the local -bare-V8 `vm` check guards this and is clean for qa-a11y). +> Node 20+ is required for `require()` of the ESM-only `vite` package (validated on +> Node 24). From acfc6f8d51ab8fefa751fc727fa84cf6b26b4230 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 20 Jul 2026 16:28:34 +0300 Subject: [PATCH 19/20] fix for non-code extensions --- config/vite.config.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/vite.config.js b/config/vite.config.js index 3b82d541..e50aefb5 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -217,6 +217,13 @@ function enactICSSInteropPlugins () { // we neutralize them in both engines (Rollup build + esbuild dev optimizer). const ILIB_LOADER_RE = /(?:[/\\]|^\.\/lib\/)ilib-[\w-]+\.js$|(?:Node|Rhino|Qt|Ringo)Loader(?:\.js)?$/; +// Catch-all `assetsInclude` regex mirroring webpack's `asset/resource` fallthrough: +// any file whose extension is NOT code (js/ts/jsx…), markup (html/ejs), JSON, a +// stylesheet, or wasm/sourcemap is emitted as a file asset, so `import cfg from +// './analytics.cfg'` resolves to the emitted file's URL instead of Rollup trying to +// parse the file as JavaScript. +const ASSET_CATCHALL_RE = /\.(?!(?:m?[jt]sx?|c[jt]s|json5?|html?|ejs|css|less|s[ac]ss|styl|wasm|map)$)[a-z0-9_-]+$/i; + // esbuild plugin (dev dependency optimizer) that stubs the iLib loaders to empty. const ilibStubEsbuildPlugin = { name: 'enact-ilib-loader-stub', @@ -386,6 +393,9 @@ module.exports = function ( logLevel: 'warn', // Vite copies `<root>/public` into the build output automatically (webpack: copyPublicFolder). publicDir: 'public', + // Treat unknown non-code extensions (e.g. `.cfg`) as emitted file assets, matching + // webpack's catch-all `asset/resource` loader + assetsInclude: ASSET_CATCHALL_RE, define: { 'process.env.NODE_ENV': JSON.stringify(isEnvProduction ? 'production' : 'development'), 'process.env.PUBLIC_URL': JSON.stringify(app.publicUrl || ''), From dfcfff4488b965ec40fda5ad6e8ac933909a70ca Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp <daniel.stoian@lgepartner.com> Date: Mon, 20 Jul 2026 16:47:02 +0300 Subject: [PATCH 20/20] fix for non-code extensions --- config/vite.config.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/config/vite.config.js b/config/vite.config.js index e50aefb5..93753da4 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -115,6 +115,36 @@ function enactNodePolyfillResolverPlugin () { }; } +// Webpack parity for `resolve.modules: [path.resolve('./node_modules'), 'node_modules']`: +// webpack adds the APP-ROOT node_modules as a global resolution root, so a bare specifier +// imported from ANY file in the graph resolves there — even source pulled in from a +// sibling directory outside the app root (e.g. the `all-samples` aggregate imports +// `../../../pattern-locale-switching/src/main`, whose code does `import {Provider} from +// 'react-redux'`; `react-redux` is a dep of all-samples, not of the sibling). Vite/Rollup +// only walk up from the importing file, so such a sibling's bare deps go unresolved. This +// plugin restores the app-root fallback: when normal resolution fails for a bare specifier, +// retry from `<app root>/node_modules`. +function enactAppModulesResolverPlugin (appContext) { + const appModules = path.join(appContext, 'node_modules'); + return { + name: 'enact-app-modules-resolver', + async resolveId (source, importer, options) { + // Only bare specifiers; skip relative/absolute/virtual ids and entries. + if (!importer || /^[./]/.test(source) || source.startsWith('\0') || path.isAbsolute(source)) { + return null; + } + // Act only as a fallback: let the normal pipeline resolve first. + const resolved = await this.resolve(source, importer, {...options, skipSelf: true}); + if (resolved) return resolved; + try { + return require.resolve(source, {paths: [appModules]}); + } catch (e) { + return null; + } + } + }; +} + const FORCE_CSS_STYLE_RE = /\.(?:css|less|s[ac]ss)(?:\?.*)?$/; const FORCE_CSS_MODULE_RE = /\.module\.(?:css|less|s[ac]ss)(?:\?.*)?$/; @@ -529,6 +559,9 @@ module.exports = function ( plugins: [ // Rewrite webpack's `module.hot` in app source before other transforms. enactNeutralizeWebpackHmrPlugin(), + // Webpack `resolve.modules` parity: resolve bare specifiers from the app-root + // node_modules when they can't be resolved from the importer + enactAppModulesResolverPlugin(app.context), // `forceCSSModules`: scope ALL css/less/scss as CSS modules (not just *.module.*). // Otherwise plain css/less/scss stays global (webpack `mode:'icss'`) and only // needs the ICSS default export so `import css from './App.less'` resolves.