From 520dfb8f46779627b414ad8bf3b33194dae00ec5 Mon Sep 17 00:00:00 2001 From: zoomdong <1344492820@qq.com> Date: Tue, 13 Oct 2026 17:47:31 +0800 Subject: [PATCH 1/8] fix(pack): prevent library CSS output collisions --- .../pack-core/src/library/chunking_context.rs | 86 +- crates/pack-core/src/library/contexts.rs | 7 + .../library/extracted_css_async/assert.js | 27 + .../library/extracted_css_async/config.json | 23 + .../extracted_css_async/input/index.js | 3 + .../extracted_css_async/input/initial.css | 3 + .../extracted_css_async/input/lazy.css | 3 + .../library/extracted_css_async/input/lazy.js | 3 + .../extracted_css_async/output/library.js | 974 +++++++++++++++++ .../extracted_css_async/output/main.css | 9 + .../extracted_css_async_sourcemap/assert.js | 27 + .../extracted_css_async_sourcemap/config.json | 23 + .../input/index.js | 3 + .../input/initial.css | 3 + .../input/lazy.css | 3 + .../input/lazy.js | 3 + .../output/library.js | 977 ++++++++++++++++++ .../output/library.js.map | 11 + .../output/main.css | 11 + .../output/main.css.map | 7 + .../output/index.morpho.min.js | 4 +- ...> input_reward_less_0gaxlbg6sf3vm.css.map} | 0 .../input_reward_less_0kzvq1134_8gg.css.map | 6 + .../library/inline_css_source_maps/assert.js | 25 + .../inline_css_source_maps/config.json | 25 + .../inline_css_source_maps/input/a/style.css | 3 + .../inline_css_source_maps/input/b/style.css | 3 + .../inline_css_source_maps/input/index.js | 5 + .../inline_css_source_maps/input/shared.css | 3 + .../input_a_style_1qz099mvmqbo9.css.map | 1 + .../input_b_style_106ndmn6lt3wu.css.map | 1 + .../output/input_shared_0oa4zyfkzogs1.css.map | 1 + .../output/library.min.js | 3 + .../output/library.min.js.map | 1 + ...=> input_index_less_1wh2e9bj5ndoc.css.map} | 0 .../library/less_css_modules/output/main.js | 2 +- .../less_css_modules/output/stats.json | 10 +- 37 files changed, 2259 insertions(+), 40 deletions(-) create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/assert.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/config.json create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/input/index.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/input/initial.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/output/library.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async/output/main.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/assert.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/config.json create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/input/index.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/input/initial.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/input/lazy.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/input/lazy.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/output/library.js create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/output/library.js.map create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/output/main.css create mode 100644 crates/pack-tests/tests/snapshot/library/extracted_css_async_sourcemap/output/main.css.map rename crates/pack-tests/tests/snapshot/library/inline_css_dual_import/output/{index.morpho.min.css.map => input_reward_less_0gaxlbg6sf3vm.css.map} (100%) create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_dual_import/output/input_reward_less_0kzvq1134_8gg.css.map create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/assert.js create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/config.json create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/input/a/style.css create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/input/b/style.css create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/input/index.js create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/input/shared.css create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/output/input_a_style_1qz099mvmqbo9.css.map create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/output/input_b_style_106ndmn6lt3wu.css.map create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/output/input_shared_0oa4zyfkzogs1.css.map create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/output/library.min.js create mode 100644 crates/pack-tests/tests/snapshot/library/inline_css_source_maps/output/library.min.js.map rename crates/pack-tests/tests/snapshot/library/less_css_modules/output/{input_index.less.css.map => input_index_less_1wh2e9bj5ndoc.css.map} (100%) diff --git a/crates/pack-core/src/library/chunking_context.rs b/crates/pack-core/src/library/chunking_context.rs index 9da1b745c..452c033b6 100644 --- a/crates/pack-core/src/library/chunking_context.rs +++ b/crates/pack-core/src/library/chunking_context.rs @@ -148,6 +148,11 @@ impl LibraryChunkingContextBuilder { self } + pub fn inline_css(mut self, inline_css: bool) -> Self { + self.chunking_context.inline_css = inline_css; + self + } + pub fn css_filename(mut self, css_filename: RcStr) -> Self { self.chunking_context.css_filename = Some(css_filename); self @@ -244,6 +249,8 @@ pub struct LibraryChunkingContext { filename: Option, /// Non-entry JavaScript chunk filename template chunk_filename: Option, + /// Inline CSS chunks need distinct source map paths instead of a bundle filename. + inline_css: bool, /// Initial css chunk filename template css_filename: Option, /// CSS chunking algorithm. @@ -286,6 +293,7 @@ impl LibraryChunkingContext { unused_references: None, filename: Default::default(), chunk_filename: Default::default(), + inline_css: false, css_filename: Default::default(), style_groups_algorithm: Default::default(), asset_module_filename: Default::default(), @@ -546,24 +554,31 @@ impl ChunkingContext for LibraryChunkingContext { || ResolvedVc::try_downcast_type::(resolved_asset) .is_some() { - match &self.css_filename { - Some(filename_template) => { - let query = QString::from(ident.await?.query.as_str()); - - let name = query - .get("name") - .or_else(|| self.name.as_ref().map(|name| name.as_str())) - .unwrap_or(output_name.as_str()); - let name = escape_file_path(name); - - let mut filename = filename_template.to_string(); - - if match_name_placeholder(&filename) { - filename = replace_name_placeholder(&filename, &name); - } - - if match_content_hash_placeholder(&filename) { - let content_hash = asset + if self.inline_css { + ident + .output_name(self.root_path.clone(), None, extension.clone()) + .owned() + .await? + .to_string() + } else { + match &self.css_filename { + Some(filename_template) => { + let query = QString::from(ident.await?.query.as_str()); + + let name = query + .get("name") + .or_else(|| self.name.as_ref().map(|name| name.as_str())) + .unwrap_or(output_name.as_str()); + let name = escape_file_path(name); + + let mut filename = filename_template.to_string(); + + if match_name_placeholder(&filename) { + filename = replace_name_placeholder(&filename, &name); + } + + if match_content_hash_placeholder(&filename) { + let content_hash = asset .content_hash(no_hash_salt(), HashAlgorithm::Xxh3Hash64Hex) .owned() .await? @@ -571,13 +586,16 @@ impl ChunkingContext for LibraryChunkingContext { "chunk_path requires an asset with file content when content \ hashing is enabled", )?; - filename = - replace_content_hash_placeholder(&filename, &content_hash); - }; + filename = replace_content_hash_placeholder( + &filename, + &content_hash, + ); + }; - filename + filename + } + None => output_name, } - None => output_name, } } else if self.shared_chunks { output_name @@ -715,15 +733,19 @@ impl ChunkingContext for LibraryChunkingContext { ..Default::default() }, ); - map.insert( - ResolvedVc::upcast(Vc::::default().to_resolved().await?), - ChunkingConfig { - min_chunk_size: usize::MAX, - max_chunk_count_per_group: 1, - max_merge_chunk_size: usize::MAX, - ..Default::default() - }, - ); + // Production style grouping ignores max_chunk_count_per_group. Single-file libraries + // must use the default style path, which combines all ordered CSS items into one chunk. + if self.shared_chunks { + map.insert( + ResolvedVc::upcast(Vc::::default().to_resolved().await?), + ChunkingConfig { + min_chunk_size: usize::MAX, + max_chunk_count_per_group: 1, + max_merge_chunk_size: usize::MAX, + ..Default::default() + }, + ); + } Ok(Vc::cell(map)) } diff --git a/crates/pack-core/src/library/contexts.rs b/crates/pack-core/src/library/contexts.rs index 71984bf37..6e208bb84 100644 --- a/crates/pack-core/src/library/contexts.rs +++ b/crates/pack-core/src/library/contexts.rs @@ -113,6 +113,13 @@ pub async fn get_library_chunking_context( MinifyType::NoMinify }) .extract_comments(*config.extract_comments().await?) + .inline_css( + config + .inline_css() + .await? + .as_ref() + .is_some_and(serde_json::Value::is_object), + ) .source_maps(if *config.source_maps().await? { SourceMapsType::Full } else { diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/assert.js b/crates/pack-tests/tests/snapshot/library/extracted_css_async/assert.js new file mode 100644 index 000000000..cd0e7216d --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/assert.js @@ -0,0 +1,27 @@ +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const path = require("node:path"); + +const outputDir = path.join(__dirname, "output"); +const outputFiles = fs.readdirSync(outputDir); +const css = fs.readFileSync(path.join(outputDir, "main.css"), "utf8"); +const sourceMaps = require("./config.json").config.sourceMaps; + +assert.deepEqual(outputFiles.filter((file) => file.endsWith(".css")), ["main.css"]); +assert.deepEqual(outputFiles.filter((file) => file.endsWith(".js")), ["library.js"]); +assert.match(css, /\.initial/); +assert.match(css, /\.lazy/); +assert.ok(css.indexOf(".initial") < css.indexOf(".lazy"), "preserve CSS import order"); + +if (sourceMaps) { + assert.deepEqual(outputFiles.filter((file) => file.endsWith(".css.map")), ["main.css.map"]); + assert.match(css, /sourceMappingURL=main\.css\.map/); + const map = JSON.parse(fs.readFileSync(path.join(outputDir, "main.css.map"), "utf8")); + const maps = map.sections ? map.sections.map((section) => section.map) : [map]; + const sources = maps.flatMap((section) => section.sources); + for (const source of ["input/initial.css", "input/lazy.css"]) { + assert.ok(sources.some((file) => file.endsWith(source)), `missing ${source}`); + } +} else { + assert.ok(!outputFiles.some((file) => file.endsWith(".map"))); +} diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/config.json b/crates/pack-tests/tests/snapshot/library/extracted_css_async/config.json new file mode 100644 index 000000000..3609a6864 --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/config.json @@ -0,0 +1,23 @@ +{ + "runtimeType": "Production", + "config": { + "entry": [ + { + "import": "input/index.js", + "name": "main", + "library": { + "name": "ExtractedCssAsyncLibrary" + } + } + ], + "output": { + "filename": "library.js", + "cssFilename": "[name].css" + }, + "optimization": { + "minify": false, + "moduleIds": "named" + }, + "sourceMaps": false + } +} diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/index.js b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/index.js new file mode 100644 index 000000000..7ebb5afed --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/index.js @@ -0,0 +1,3 @@ +import "./initial.css"; + +export const load = () => import("./lazy.js"); diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/initial.css b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/initial.css new file mode 100644 index 000000000..6773157bc --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/initial.css @@ -0,0 +1,3 @@ +.initial { + color: red; +} diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.css b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.css new file mode 100644 index 000000000..d71e3d1bb --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.css @@ -0,0 +1,3 @@ +.lazy { + color: blue; +} diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.js b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.js new file mode 100644 index 000000000..a501cfd22 --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/input/lazy.js @@ -0,0 +1,3 @@ +import "./lazy.css"; + +export const value = 42; diff --git a/crates/pack-tests/tests/snapshot/library/extracted_css_async/output/library.js b/crates/pack-tests/tests/snapshot/library/extracted_css_async/output/library.js new file mode 100644 index 000000000..5bbbc71cf --- /dev/null +++ b/crates/pack-tests/tests/snapshot/library/extracted_css_async/output/library.js @@ -0,0 +1,974 @@ +((__UTOOPACK__) => { +if (!Array.isArray(__UTOOPACK__)) { + return; +} + +const CHUNK_BASE_PATH = ""; +const CHUNK_SUFFIX_PATH = ""; +const RELATIVE_ROOT_PATH = ".."; +const RUNTIME_PUBLIC_PATH = ""; +// Library builds deliberately collapse JavaScript into one chunk, so the +// component-chunk runtime path is unsupported in this custom runtime. +const SUPPORT_COMPONENT_CHUNKS = false; +/** + * This file contains runtime types and functions that are shared between all + * TurboPack ECMAScript runtimes. + * + * It will be prepended to the runtime code of each runtime. + */ /* eslint-disable @typescript-eslint/no-unused-vars */ /// +/// +/** + * Describes why a module was instantiated. + * Shared between browser and Node.js runtimes. + */ var SourceType = function(SourceType) { + /** + * The module was instantiated because it was included in an evaluated chunk's + * runtime. + * SourceData is a ChunkPath. + */ SourceType[SourceType["Runtime"] = 0] = "Runtime"; + /** + * The module was instantiated because a parent module imported it. + * SourceData is a ModuleId. + */ SourceType[SourceType["Parent"] = 1] = "Parent"; + /** + * The module was instantiated because it was included in a chunk's hot module + * update. + * SourceData is an array of ModuleIds or undefined. + */ SourceType[SourceType["Update"] = 2] = "Update"; + return SourceType; +}(SourceType || {}); +/** + * Flag indicating which module object type to create when a module is merged. Set to `true` + * by each runtime that uses ModuleWithDirection (browser dev-base.ts, nodejs dev-base.ts, + * nodejs build-base.ts). Browser production (build-base.ts) leaves it as `false` since it + * uses plain Module objects. + */ let createModuleWithDirectionFlag = false; +const REEXPORTED_OBJECTS = new WeakMap(); +/** + * Constructs the `__turbopack_context__` object for a module. + */ function Context(module, exports) { + this.m = module; + // We need to store this here instead of accessing it from the module object to: + // 1. Make it available to factories directly, since we rewrite `this` to + // `__turbopack_context__.e` in CJS modules. + // 2. Support async modules which rewrite `module.exports` to a promise, so we + // can still access the original exports object from functions like + // `esmExport` + // Ideally we could find a new approach for async modules and drop this property altogether. + this.e = exports; +} +const contextPrototype = Context.prototype; +const hasOwnProperty = Object.prototype.hasOwnProperty; +const toStringTag = typeof Symbol !== 'undefined' && Symbol.toStringTag; +function defineProp(obj, name, options) { + if (!hasOwnProperty.call(obj, name)) Object.defineProperty(obj, name, options); +} +function getOverwrittenModule(moduleCache, id) { + let module = moduleCache.get(id); + if (module === undefined) { + if (createModuleWithDirectionFlag) { + // set in development modes for hmr support + module = createModuleWithDirection(id); + } else { + module = createModuleObject(id); + } + moduleCache.set(id, module); + } + return module; +} +/** + * Creates the module object. Only done here to ensure all module objects have the same shape. + */ function createModuleObject(id) { + return { + exports: {}, + error: undefined, + id, + namespaceObject: undefined + }; +} +function createModuleWithDirection(id) { + return { + exports: {}, + error: undefined, + id, + namespaceObject: undefined, + parents: [], + children: [] + }; +} +const BindingTag_Value = 0; +/** + * Terminates a module's group of entries in an {@link EsmReexports} list. + */ const REEXPORT_GROUP_END = 0; +/** + * Adds the getters to the exports object. + */ function esm(exports, bindings, dynamic) { + defineProp(exports, '__esModule', { + value: true + }); + if (toStringTag) defineProp(exports, toStringTag, { + value: 'Module' + }); + let i = 0; + while(i < bindings.length){ + const propName = bindings[i++]; + const tagOrFunction = bindings[i++]; + if (typeof tagOrFunction === 'number') { + if (tagOrFunction === BindingTag_Value) { + defineProp(exports, propName, { + value: bindings[i++], + enumerable: true, + writable: false + }); + } else { + throw new Error(`unexpected tag: ${tagOrFunction}`); + } + } else { + const getterFn = tagOrFunction; + if (typeof bindings[i] === 'function') { + const setterFn = bindings[i++]; + defineProp(exports, propName, { + get: getterFn, + set: setterFn, + enumerable: true + }); + } else { + defineProp(exports, propName, { + get: getterFn, + enumerable: true + }); + } + } + } + // The properties defined above are already non-configurable and + // non-writable, so the namespace's existing exports are effectively + // immutable. Sealing additionally makes the object non-extensible, matching + // real ESM-namespace semantics. Modules with dynamic re-exports + // (`export *` from a CommonJS module) must stay extensible so the dynamic + // export proxy can surface keys discovered at runtime, so skip the seal for + // them. + if (!dynamic) Object.seal(exports); +} +/** + * Makes the module an ESM with exports + */ function esmExport(bindings, id, dynamic) { + let module; + let exports; + if (id != null) { + module = getOverwrittenModule(this.c, id); + exports = module.exports; + } else { + module = this.m; + exports = this.e; + } + module.namespaceObject = exports; + esm(exports, bindings, dynamic); +} +contextPrototype.s = esmExport; +/** + * Registers re-exports that all forward to properties of other modules. + * + * This is a compact spelling of the pattern + * + * ```js + * var ns = context.i(moduleId) + * context.s([exportName, () => ns[importedName], ...]) + * ``` + * + * The list is a flat sequence of groups. Each group starts with the source the exports come from, + * followed by that group's entries, and is terminated by the `0` sentinel (or the end of the list). + * + * The group head is either a **module id**, which is instantiated here: + * + * ```js + * context.S([ + * 76061, 'default', 'f', 'named', 'A', 0, + * 29842, 'otherModule', 'f', + * ]) + * ``` + * + * or the **namespace value** of a module that has already been imported, which is used directly: + * + * ```js + * var ns1 = context.i(76061) + * context.S([ns1, 'default', 'f', 'named', 'A']) + * ``` + * + * The producer picks the namespace form when it has generated the import anyway -- because some + * later import must not be reordered past it -- so nothing is instantiated twice. The two are told + * apart by type: a module id is always a string or number. A CommonJS function export produces a + * callable namespace value, so namespace heads can be functions as well as objects. + * + * Entries are `exportName, importedName` pairs, except when a group holds exactly one string. That + * string is then a comma-joined list of the same pairs, which saves the repeated quoting: + * + * ```js + * context.S([ + * 76061, 'default,f,named,A', 0, + * 29842, 'otherModule,f', + * ]) + * ``` + * + * The producer picks that spelling independently for each group whose names contain no commas, + * since that group's names are recovered by splitting on them. + * + * Groups whose head is a module id are instantiated in list order, at the point where the call + * appears, so the producer must not merge such a group across an import of another module. The + * destination reuses a source data value or getter descriptor when one exists, falling back to a + * wrapper getter for dynamic/proxy/inherited properties. + * + * `id` names the module the exports belong to when this module was merged into a scope-hoisting + * group, exactly as it does for {@link EsmExport}. + * + * Only the source descriptor's payload (value or getter) is reused. {@link esm} still defines a + * fresh enumerable, non-configurable destination property, and no source setter is ever forwarded. + */ function esmReexport(list, id) { + const bindings = []; + let i = 0; + while(i < list.length){ + const head = list[i++]; + const start = i; + while(i < list.length && list[i] !== REEXPORT_GROUP_END)i++; + const end = i; + // Skip the sentinel, if this group was terminated by one rather than by the end of the list. + i++; + // Module ids are always strings or numbers. Other values are already-imported namespaces; + // notably, interop with a CommonJS function export produces a callable namespace function. + // `esmImport` may return a promise for an async module, but re-exports of async modules keep + // going through `context.s`, so the producer never routes them here and this stays synchronous. + const namespace = typeof head === 'string' || typeof head === 'number' ? esmImport.call(this, head) : head; + if (end - start === 1) { + const pairs = list[start].split(','); + for(let j = 0; j < pairs.length; j += 2){ + appendReexportBinding(bindings, pairs[j], namespace, pairs[j + 1]); + } + } else { + for(let j = start; j < end; j += 2){ + appendReexportBinding(bindings, list[j], namespace, list[j + 1]); + } + } + } + esmExport.call(this, bindings, id); +} +contextPrototype.S = esmReexport; +function appendReexportBinding(bindings, exportedName, namespace, importedName) { + const descriptor = Reflect.getOwnPropertyDescriptor(namespace, importedName); + if (descriptor) { + if ('value' in descriptor) { + // Code generation only routes immutable imported bindings through this helper, so a data + // descriptor is a constant export and can be captured once. + bindings.push(exportedName, BindingTag_Value, descriptor.value); + return; + } + if (descriptor.get) { + // Accessors remain live by reusing the source getter. `esmReexport` is only called by + // generated code: every group head is either produced by + // `this.i` or is the namespace variable from a generated `this.i` call. Every getter on such + // a namespace is receiver-independent: ESM bindings are compiler-generated arrow functions, + // and the CommonJS/dynamic-namespace paths create arrows in `createGetter` and + // `getOwnPropertyDescriptor`. The destination can therefore reuse the exact function instead + // of allocating another wrapper getter. + bindings.push(exportedName, descriptor.get); + return; + } + } + // Dynamic/proxy/inherited CommonJS edge cases may not expose a usable own descriptor. + bindings.push(exportedName, ()=>namespace[importedName]); +} +function ensureDynamicExports(module, exports) { + let reexportedObjects = REEXPORTED_OBJECTS.get(module); + if (!reexportedObjects) { + REEXPORTED_OBJECTS.set(module, reexportedObjects = []); + // Returns the re-exported object that provides `prop` as an own property, + // or `undefined` if none does. The traps share this logic so they always + // agree on which keys are synthesized from `reexportedObjects`. `default` + // is never re-exported by `export *`, so it is never synthesized. + const reexportOwning = (prop)=>{ + if (prop !== 'default') { + for (const obj of reexportedObjects){ + if (hasOwnProperty.call(obj, prop)) return obj; + } + } + return undefined; + }; + // Modules with dynamic re-exports are not sealed by `esm()`, so the + // target beneath the namespace stays extensible. That is what lets the + // `ownKeys` and `getOwnPropertyDescriptor` traps legally report keys that + // exist on `reexportedObjects` but not on the target itself. + module.exports = module.namespaceObject = new Proxy(exports, { + get (target, prop) { + if (hasOwnProperty.call(target, prop) || prop === 'default' || prop === '__esModule') { + return Reflect.get(target, prop); + } + const obj = reexportOwning(prop); + return obj && Reflect.get(obj, prop); + }, + // The namespace is read-only, like a real esm namespace object. The + // re-exported modules can still mutate their own exports (exposed live + // via `get`), but mutating the namespace itself is rejected. Refusing + // here, rather than forwarding to the extensible target, also prevents an + // assignment/definition from shadowing a dynamic re-export. It also + // prevents delete from removing a static export. + set () { + return false; + }, + defineProperty () { + return false; + }, + deleteProperty () { + return false; + }, + // The `has` trap ensures that `'exportName' in starImports` will reflect + // the truth of whether a key is exported. + has (target, prop) { + if (Reflect.has(target, prop)) return true; + if (prop === 'default' || prop === '__esModule') return false; + return reexportOwning(prop) !== undefined; + }, + // ownKeys and getOwnPropertyDescriptor together make the keys enumerable. + // If a value is returned from `ownKeys` but its property descriptor is + // not enumerable, it will not be visible to iterator methods. + // Collectively, they allow code like the following: + // + // ``` + // // module.js re-exports dynamic CJS exports + // export * from './legacyModule.cjs' + // + // // from another JS file, reference the re-exported dynamic values + // import * as Namespace from './module.js' + // Object.keys(Namespace) + // ``` + ownKeys (target) { + const keys = Reflect.ownKeys(target); + for (const obj of reexportedObjects){ + for (const key of Reflect.ownKeys(obj)){ + if (key !== 'default' && !keys.includes(key)) keys.push(key); + } + } + return keys; + }, + getOwnPropertyDescriptor (target, prop) { + const own = Reflect.getOwnPropertyDescriptor(target, prop); + if (own || prop === 'default' || prop === '__esModule') return own; + const obj = reexportOwning(prop); + if (obj) { + // Synthetic keys don't exist on the target, so they MUST be + // reported as configurable. However the set/delete traps above will + // prevent them from actually being changed + return { + enumerable: true, + configurable: true, + get: ()=>Reflect.get(obj, prop) + }; + } + return undefined; + } + }); + } + return reexportedObjects; +} +/** + * Dynamically exports properties from an object + */ function dynamicExport(object, id) { + let module; + let exports; + if (id != null) { + module = getOverwrittenModule(this.c, id); + exports = module.exports; + } else { + module = this.m; + exports = this.e; + } + const reexportedObjects = ensureDynamicExports(module, exports); + if (typeof object === 'object' && object !== null) { + reexportedObjects.push(object); + } +} +contextPrototype.j = dynamicExport; +function exportValue(value, id) { + let module; + if (id != null) { + module = getOverwrittenModule(this.c, id); + } else { + module = this.m; + } + module.exports = value; +} +contextPrototype.v = exportValue; +function exportNamespace(namespace, id) { + let module; + if (id != null) { + module = getOverwrittenModule(this.c, id); + } else { + module = this.m; + } + module.exports = module.namespaceObject = namespace; +} +contextPrototype.n = exportNamespace; +function createGetter(obj, key) { + return ()=>obj[key]; +} +/** + * @returns prototype of the object + */ const getProto = Object.getPrototypeOf ? (obj)=>Object.getPrototypeOf(obj) : (obj)=>obj.__proto__; +/** Prototypes that are not expanded for exports */ const LEAF_PROTOTYPES = [ + null, + getProto({}), + getProto([]), + getProto(getProto) +]; +/** + * @param raw + * @param ns + * @param allowExportDefault + * * `false`: will have the raw module as default export + * * `true`: will have the default property as default export + */ function interopEsm(raw, ns, allowExportDefault) { + const bindings = []; + let defaultLocation = -1; + for(let current = raw; (typeof current === 'object' || typeof current === 'function') && !LEAF_PROTOTYPES.includes(current); current = getProto(current)){ + for (const key of Object.getOwnPropertyNames(current)){ + bindings.push(key, createGetter(raw, key)); + if (defaultLocation === -1 && key === 'default') { + defaultLocation = bindings.length - 1; + } + } + } + // this is not really correct + // we should set the `default` getter if the imported module is a `.cjs file` + if (!(allowExportDefault && defaultLocation >= 0)) { + // Replace the binding with one for the namespace itself in order to preserve iteration order. + if (defaultLocation >= 0) { + // Replace the getter with the value + bindings.splice(defaultLocation, 1, BindingTag_Value, raw); + } else { + bindings.push('default', BindingTag_Value, raw); + } + } + esm(ns, bindings); + return ns; +} +function createNS(raw) { + if (typeof raw === 'function') { + return function(...args) { + return raw.apply(this, args); + }; + } else { + return Object.create(null); + } +} +function esmImport(id) { + const module = getOrInstantiateModuleFromParent(id, this.m); + // any ES module has to have `module.namespaceObject` defined. + if (module.namespaceObject) return module.namespaceObject; + // only ESM can be an async module, so we don't need to worry about exports being a promise here. + const raw = module.exports; + return module.namespaceObject = interopEsm(raw, createNS(raw), raw && raw.__esModule); +} +contextPrototype.i = esmImport; +function asyncLoader(moduleId) { + const loader = this.r(moduleId); + return loader(esmImport.bind(this)); +} +contextPrototype.A = asyncLoader; +// Add a simple runtime require so that environments without one can still pass +// `typeof require` CommonJS checks so that exports are correctly registered. +const runtimeRequire = // @ts-ignore +typeof require === 'function' ? require : function require1() { + throw new Error('Unexpected use of runtime require'); +}; +contextPrototype.t = runtimeRequire; +function commonJsRequire(id) { + return getOrInstantiateModuleFromParent(id, this.m).exports; +} +contextPrototype.r = commonJsRequire; +/** + * Remove fragments and query parameters since they are never part of the context map keys + * + * This matches how we parse patterns at resolving time. Arguably we should only do this for + * strings passed to `import` but the resolve does it for `import` and `require` and so we do + * here as well. + */ function parseRequest(request) { + // Per the URI spec fragments can contain `?` characters, so we should trim it off first + // https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + const hashIndex = request.indexOf('#'); + if (hashIndex !== -1) { + request = request.substring(0, hashIndex); + } + const queryIndex = request.indexOf('?'); + if (queryIndex !== -1) { + request = request.substring(0, queryIndex); + } + return request; +} +/** + * `require.context` and require/import expression runtime. + */ function moduleContext(map) { + function moduleContext(id) { + id = parseRequest(id); + if (hasOwnProperty.call(map, id)) { + return map[id].module(); + } + const e = new Error(`Cannot find module '${id}'`); + e.code = 'MODULE_NOT_FOUND'; + throw e; + } + moduleContext.keys = ()=>{ + return Object.keys(map); + }; + moduleContext.resolve = (id)=>{ + id = parseRequest(id); + if (hasOwnProperty.call(map, id)) { + return map[id].id(); + } + const e = new Error(`Cannot find module '${id}'`); + e.code = 'MODULE_NOT_FOUND'; + throw e; + }; + moduleContext.import = async (id)=>{ + return await moduleContext(id); + }; + return moduleContext; +} +contextPrototype.f = moduleContext; +/** + * Returns the path of a chunk defined by its data. + */ function getChunkPath(chunkData) { + return typeof chunkData === 'string' ? chunkData : chunkData.path; +} +// Load the CompressedModuleFactories of a chunk into the `moduleFactories` Map. +// The flat format alternates one or more module IDs with their factory function. +// Strict factories can be prepended as a nested array. +function installCompressedModuleFactories(chunkModules, offset, moduleFactories, newModuleId) { + let i = offset; + const strictFactories = chunkModules[i]; + if (Array.isArray(strictFactories)) { + installCompressedModuleFactories(strictFactories, 0, moduleFactories, newModuleId); + i++; + } + while(i < chunkModules.length){ + let end = i + 1; + // Find our factory function + while(end < chunkModules.length && typeof chunkModules[end] !== 'function'){ + end++; + } + if (end === chunkModules.length) { + throw new Error('malformed chunk format, expected a factory function'); + } + // Install the factory for each module ID that doesn't already have one. + // When some IDs in this group already have a factory, reuse that existing + // group factory for the missing IDs to keep all IDs in the group consistent. + // Otherwise, install the factory from this chunk. + const moduleFactoryFn = chunkModules[end]; + let existingGroupFactory = undefined; + for(let j = i; j < end; j++){ + const id = chunkModules[j]; + const existingFactory = moduleFactories.get(id); + if (existingFactory) { + existingGroupFactory = existingFactory; + break; + } + } + const factoryToInstall = existingGroupFactory ?? moduleFactoryFn; + let didInstallFactory = false; + for(let j = i; j < end; j++){ + const id = chunkModules[j]; + if (!moduleFactories.has(id)) { + if (!didInstallFactory) { + if (factoryToInstall === moduleFactoryFn) { + applyModuleFactoryName(moduleFactoryFn); + } + didInstallFactory = true; + } + moduleFactories.set(id, factoryToInstall); + newModuleId?.(id); + } + } + i = end + 1; + } +} +/** + * A pseudo "fake" URL object to resolve to its relative path. + * + * When UrlRewriteBehavior is set to relative, calls to the `new URL()` will construct url without base using this + * runtime function to generate context-agnostic urls between different rendering context, i.e ssr / client to avoid + * hydration mismatch. + * + * This is based on webpack's existing implementation: + * https://github.com/webpack/webpack/blob/87660921808566ef3b8796f8df61bd79fc026108/lib/runtime/RelativeUrlRuntimeModule.js + */ const relativeURL = function relativeURL(inputUrl) { + const realUrl = new URL(inputUrl, 'x:/'); + const values = {}; + for(const key in realUrl)values[key] = realUrl[key]; + values.href = inputUrl; + values.pathname = inputUrl.replace(/[?#].*/, ''); + values.origin = values.protocol = ''; + values.toString = values.toJSON = (..._args)=>inputUrl; + for(const key in values)Object.defineProperty(this, key, { + enumerable: true, + configurable: true, + value: values[key] + }); +}; +relativeURL.prototype = URL.prototype; +contextPrototype.U = relativeURL; +/** + * Utility function to ensure all variants of an enum are handled. + */ function invariant(never, computeMessage) { + throw new Error(`Invariant: ${computeMessage(never)}`); +} +/** + * Constructs an error message for when a module factory is not available. + */ function factoryNotAvailableMessage(moduleId, sourceType, sourceData) { + let instantiationReason; + switch(sourceType){ + case 0: + instantiationReason = `as a runtime entry of chunk ${sourceData}`; + break; + case 1: + instantiationReason = `because it was required from module ${sourceData}`; + break; + case 2: + instantiationReason = 'because of an HMR update'; + break; + default: + invariant(sourceType, (sourceType)=>`Unknown source type: ${sourceType}`); + } + return `Module ${moduleId} was instantiated ${instantiationReason}, but the module factory is not available.`; +} +/** + * Returns a `file://` URL under a synthetic directory named after `root` + * (`ROOT` for the project root), for when the real filesystem path is unknown. + * The root name and path segments are percent-encoded so the result is always + * a valid file URI. + */ function placeholderFileUrl(modulePath, root) { + return `file:///${encodeURIComponent(root ?? 'ROOT')}/${modulePath.split('/').map(encodeURIComponent).join('/')}`; +} +/** + * A stub function to make `require` available but non-functional in ESM. + */ function requireStub(_moduleId) { + throw new Error('dynamic usage of require is not supported'); +} +contextPrototype.z = requireStub; +// Make `globalThis` available to the module in a way that cannot be shadowed by a local variable. +contextPrototype.g = globalThis; +// Each runtime keeps its own public path. Other applications on the page may +// change the global path after this runtime has started. +let runtimePublicPath = typeof globalThis !== 'undefined' ? globalThis.publicPath : undefined; +Object.defineProperty(contextPrototype, 'runtimePublicPath', { + get: ()=>runtimePublicPath, + set: (value)=>{ + runtimePublicPath = value; + } +}); +let cachedAutomaticPublicPath; +function getAutomaticPublicPath() { + if (cachedAutomaticPublicPath !== undefined) { + return cachedAutomaticPublicPath; + } + let scriptUrl; + if (typeof document === 'object') { + const currentScript = document.currentScript; + scriptUrl = currentScript?.src; + if (!scriptUrl) { + const scripts = document.getElementsByTagName('script'); + const script = scripts[scripts.length - 1]; + scriptUrl = script?.src; + } + } + if (!scriptUrl && typeof globalThis.importScripts === 'function' && globalThis.location) { + scriptUrl = String(globalThis.location); + } + cachedAutomaticPublicPath = scriptUrl ? scriptUrl.replace(/^blob:/, '').replace(/#.*$/, '').replace(/\?.*$/, '').replace(/\/[^/]*$/, '/') : ''; + return cachedAutomaticPublicPath; +} +/** + * Gets the public path for runtime assets. + * Uses this runtime's public path and falls back to "/". + */ function getPublicPath(mode) { + if (mode === 'auto') { + return getAutomaticPublicPath(); + } + if (typeof runtimePublicPath === 'string') { + const publicPath = runtimePublicPath; + return publicPath.endsWith('/') ? publicPath : `${publicPath}/`; + } + return '/'; +} +contextPrototype.p = getPublicPath; +function applyModuleFactoryName(factory) { + // Give the module factory a nice name to improve stack traces. + Object.defineProperty(factory, 'name', { + value: 'module evaluation' + }); +} +/** + * This file contains runtime types and functions that are shared between all + * Turbopack UMD library runtimes (DOM and Node.js). + * + * It will be appended to the runtime code of each runtime right after the + * shared runtime utils. + */ /* eslint-disable @typescript-eslint/no-unused-vars */ /// +/// +// Provided by build +let BACKEND; +const moduleFactories = new Map(); +contextPrototype.M = moduleFactories; +/** + * Determine the chunk to register from a registration entry. + * In library builds, chunks are always string paths or script objects. + */ function getChunkFromRegistration(chunk) { + if (typeof chunk === "string") { + return chunk; + } else if (chunk) { + return { + src: chunk.getAttribute("src") + }; + } else { + throw new Error("chunk path is empty"); + } +} +/** + * Load CommonJS externals when a UMD bundle runs in a CommonJS environment. + * Browser-targeted UMD bundles need this too because their wrapper supports + * both global and CommonJS consumers. + */ function externalRequire(id, thunk, esm = false) { + let raw; + try { + raw = thunk(); + } catch (err) { + throw new Error(`Failed to load external module ${id}: ${err}`); + } + if (!esm || raw.__esModule) { + return raw; + } + return interopEsm(raw, createNS(raw), true); +} +externalRequire.resolve = (id, options)=>{ + return require.resolve(id, options); +}; +contextPrototype.x = externalRequire; +/** + * Adds Webpack-compatible ESM metadata to external values while preserving + * native ESM live bindings. + */ function externalNamespace(mod) { + if (mod && mod.__esModule) return mod; + const ns = Object.create(null); + const isEsmNamespace = mod && toStringTag && mod[toStringTag] === "Module"; + if (mod && (typeof mod === "object" || typeof mod === "function")) { + for(const key in mod){ + if (key === "__esModule" || !isEsmNamespace && key === "default") { + continue; + } + Object.defineProperty(ns, key, { + enumerable: true, + get: createGetter(mod, key) + }); + } + } + if (!isEsmNamespace) { + Object.defineProperty(ns, "default", { + enumerable: true, + value: mod + }); + } + Object.defineProperty(ns, "__esModule", { + value: true + }); + if (toStringTag) { + Object.defineProperty(ns, toStringTag, { + value: "Module" + }); + } + return ns; +} +contextPrototype.N = externalNamespace; +/// +/// +const moduleCache = new Map(); +contextPrototype.c = moduleCache; +/** + * Gets or instantiates a runtime module. + */ // @ts-ignore +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getOrInstantiateRuntimeModule(chunkPath, moduleId) { + const module = moduleCache.get(moduleId); + if (module) { + if (module.error) { + throw module.error; + } + return module; + } + return instantiateModule(moduleId, SourceType.Runtime, chunkPath); +} +/** + * Retrieves a module from the cache, or instantiate it if it is not cached. + */ // Used by the backend +// @ts-ignore +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const getOrInstantiateModuleFromParent = (id, sourceModule)=>{ + const module = moduleCache.get(id); + if (module) { + if (module.error) { + throw module.error; + } + return module; + } + return instantiateModule(id, SourceType.Parent, sourceModule.id); +}; +function instantiateModule(id, sourceType, sourceData) { + const moduleFactory = moduleFactories.get(id); + if (typeof moduleFactory !== 'function') { + // This can happen if modules incorrectly handle HMR disposes/updates, + // e.g. when they keep a `setTimeout` around which still executes old code + // and contains e.g. a `require("something")` call. + throw new Error(factoryNotAvailableMessage(id, sourceType, sourceData)); + } + const module = createModuleObject(id); + const exports = module.exports; + moduleCache.set(id, module); + // NOTE(alexkirsz) This can fail when the module encounters a runtime error. + const context = new Context(module, exports); + try { + moduleFactory(context, module, exports); + } catch (error) { + module.error = error; + throw error; + } + if (module.namespaceObject && module.exports !== module.namespaceObject) { + // in case of a circular dependency: cjs1 -> esm2 -> cjs1 + interopEsm(module.exports, module.namespaceObject); + } + return module; +} +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function registerChunk(registration) { + // An inlined entry-only registration is a bare params object (no source chunk). + if (!Array.isArray(registration)) { + return BACKEND.registerChunk(undefined, registration); + } + const chunk = getChunkFromRegistration(registration[0]); + if (SUPPORT_COMPONENT_CHUNKS) { + markChunkComponentsAvailable(chunk); + } + let runtimeParams; + // When bootstrapping we are passed a single runtimeParams object so we can distinguish purely based on length + if (registration.length === 2) { + runtimeParams = registration[1]; + } else { + runtimeParams = undefined; + installCompressedModuleFactories(registration, /* offset= */ 1, moduleFactories); + } + return BACKEND.registerChunk(chunk, runtimeParams); +} +/** + * This file contains the runtime code specific to the Turbopack + * ECMAScript DOM runtime for library builds. + * + * It will be appended to the base runtime code in place of + * runtime-backend-node.ts when the target platform is browser/web. + * + * Since library builds produce a single, self-contained chunk, + * no dynamic chunk loading is needed. The BACKEND simply registers + * modules and instantiates runtime entries. + * + * It also provides browser file URL placeholders and `loadScript` for + * script externals loaded from CDN or other external sources. + */ /* eslint-disable @typescript-eslint/no-unused-vars */ /// +/** + * Match the browser runtime's placeholder URLs without exposing filesystem paths. + */ function resolveFileUrl(modulePath, root) { + // ImportMetaBinding escapes percent signs and control characters before codegen. + return placeholderFileUrl(decodeURIComponent(modulePath ?? ""), root); +} +contextPrototype.F = resolveFileUrl; +const loadedScripts = new Map(); +/** + * Load an external script by creating a