@edynamix/mf-isolation 0.0.26 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/shared-load-order.d.ts +34 -0
- package/dist/shared-load-order.d.ts.map +1 -0
- package/dist/shared-load-order.js +439 -0
- package/dist/shared-load-order.js.map +1 -0
- package/dist/vite.d.ts +4 -2
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +140 -29
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,6 +197,8 @@ federation({
|
|
|
197
197
|
|
|
198
198
|
It shares every script entry point of the module's `@angular/*` dependencies (read from each installed package's `exports`), plus `rxjs` and `rxjs/operators`, at the module's own version. Entry points that stylesheets also resolve are left out: the `@angular/material` and `@angular/cdk` roots answer `@use '@angular/material'`, and Module Federation would answer the stylesheet with a script. `@angular/compiler` and `@angular/platform-browser-dynamic` are not shared: only just-in-time compilation needs them, and sharing them would put the compiler into every module's build. The versions must be exact (`19.2.25`, not `^19.2.25`). A module on a different version keeps its own copy instead of mixing two Angular versions on one page. Pass `{ root }` when the Vite config does not run from the module's package directory.
|
|
199
199
|
|
|
200
|
+
A module that starts first on a page loads its own copy of every shared entry point at start-up, in the batches the federation plugin orders them in, and an entry point reads another one through a stub that is filled only once that one has loaded. An entry point that reads another while it loads (a class extending it, a static field, a call at the top of the module) therefore needs that one to load in an earlier batch, and the plugin orders entry points only by the shared package roots their package depends on. `createMfAngularShared()` reads each entry point's source and leaves out those that would start with an undefined binding (`@angular/material-moment-adapter` extends `@angular/material/core`'s `DateAdapter`, but depends on the unshared `@angular/material` root; `@angular/cdk/listbox` provides `@angular/forms`' `NG_VALUE_ACCESSOR`, which the CDK does not depend on), together with every shared entry point that imports them, so a module never holds two copies of one entry point. The module bundles its own copy of those, as before sharing. `mfAssetManifestPlugin()` checks the built output the same way and fails the build if a shared entry point still reads another one before it is sure to have loaded.
|
|
201
|
+
|
|
200
202
|
A shared Angular means one Angular platform per page. A module bootstrapped with `createApplication()` reuses that platform already. A module that bootstraps an NgModule replaces `platformBrowserDynamic(providers)` with `createHostedAngularPlatform(providers)`:
|
|
201
203
|
|
|
202
204
|
```ts
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface IAstNode {
|
|
2
|
+
type: string;
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The identifiers a module reads while it is evaluated, with the first reason found for each: module-scope code,
|
|
7
|
+
* functions invoked on the spot, local functions called and local classes constructed while it loads, class heritage,
|
|
8
|
+
* decorators, computed keys, static fields and static blocks. Function bodies run later are left out.
|
|
9
|
+
*/
|
|
10
|
+
export declare const loadTimeReferences: (program: IAstNode) => Map<string, string>;
|
|
11
|
+
/** The module's import bindings: local name -> the specifier it is imported from. */
|
|
12
|
+
export declare const importedBindings: (program: IAstNode) => Map<string, string>;
|
|
13
|
+
export declare const packageNameOf: (specifier: string) => string;
|
|
14
|
+
/**
|
|
15
|
+
* The batch each shared key loads in, the way @module-federation/vite orders them: a key waits for the shared package
|
|
16
|
+
* roots its package depends on (package.json dependencies, peer and optional dependencies), and a root waits for its
|
|
17
|
+
* own subpaths as well. A dependency on a package whose root is not shared orders nothing.
|
|
18
|
+
*/
|
|
19
|
+
export declare const shareLoadBatches: (keys: readonly string[], packageDependencies: (packageName: string) => readonly string[]) => Map<string, number>;
|
|
20
|
+
export interface ISharedLoadOrderProblem {
|
|
21
|
+
entry: string;
|
|
22
|
+
dependency: string;
|
|
23
|
+
binding: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
file: string;
|
|
26
|
+
}
|
|
27
|
+
export declare const describeSharedLoadOrderProblem: ({ binding, dependency, entry, file, reason }: ISharedLoadOrderProblem) => string;
|
|
28
|
+
/**
|
|
29
|
+
* Checks a built hosted module the way it starts first on a page: for every shared entry point's local copy (and the
|
|
30
|
+
* chunks it imports), the bindings it reads while loading from another shared entry point's stub, against the load
|
|
31
|
+
* batches in the built remote entry. `chunks` maps output file names to code.
|
|
32
|
+
*/
|
|
33
|
+
export declare const findBundleSharedLoadOrderProblems: (chunks: ReadonlyMap<string, string>, parse: (code: string) => IAstNode) => ISharedLoadOrderProblem[];
|
|
34
|
+
//# sourceMappingURL=shared-load-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-load-order.d.ts","sourceRoot":"","sources":["../src/shared-load-order.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAsED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,QAAQ,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAqLxE,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,GAAI,SAAS,QAAQ,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CActE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,KAAG,MAIpC,CAAC;AAEf;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,EAAE,EAAE,qBAAqB,CAAC,WAAW,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CA2B7I,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,8BAA8B,GAAI,8CAA8C,uBAAuB,KAAG,MACK,CAAC;AAoD7H;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,GAC5C,QAAQ,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,OAAO,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,KAChC,uBAAuB,EA8FzB,CAAC","sourcesContent":["// When a hosted module is the first on a page, its remote entry loads its own copy of every shared entry point in\n// batches: one batch after another, the entries of a batch in parallel. An entry imports another shared entry through\n// a stub whose bindings fill in only once that entry has loaded, so a binding read while the importing entry is still\n// being evaluated (a class extending it, a static field, a call made at the top of the module) is undefined unless the\n// other entry loaded in an earlier batch. These helpers find such reads.\n\nexport interface IAstNode {\n type: string;\n [key: string]: unknown;\n}\n\nconst isNode = (value: unknown): value is IAstNode =>\n typeof value === 'object' && value !== null && typeof (value as { type?: unknown }).type === 'string';\n\nconst childNodes = (node: IAstNode): IAstNode[] => {\n const children: IAstNode[] = [];\n for (const [key, value] of Object.entries(node)) {\n if (key === 'parent') {\n continue;\n }\n if (Array.isArray(value)) {\n children.push(...value.filter(isNode));\n } else if (isNode(value)) {\n children.push(value);\n }\n }\n return children;\n};\n\n// Visits every node below `node`; returning false from `visit` skips that node's children.\nconst walk = (node: IAstNode, visit: (node: IAstNode, parent: IAstNode | null) => boolean, parent: IAstNode | null = null): void => {\n if (!visit(node, parent)) {\n return;\n }\n for (const child of childNodes(node)) {\n walk(child, visit, node);\n }\n};\n\nconst FUNCTION_TYPES = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);\n// Angular's partial declarations in published packages, as the linker compiles them for a production build: a factory's\n// dependencies and an injectable's provider become factory functions, queries become query functions, class metadata\n// and NgModule scopes become development-only code. Those parts are not read while the module loads; everything else\n// in a declaration (providers, host directives, injector imports, inputs) is. `true` defers the whole declaration.\nconst DEFERRED_ANGULAR_DECLARATION_PARTS: ReadonlyMap<string, true | ReadonlySet<string>> = new Map<string, true | ReadonlySet<string>>([\n ['ɵɵngDeclareFactory', true],\n ['ɵɵngDeclareClassMetadata', true],\n ['ɵɵngDeclareClassMetadataAsync', true],\n ['ɵɵngDeclareDirective', new Set(['queries', 'viewQueries'])],\n ['ɵɵngDeclareComponent', new Set(['queries', 'viewQueries'])],\n ['ɵɵngDeclareInjectable', new Set(['useFactory', 'useClass', 'useExisting', 'useValue', 'deps'])],\n ['ɵɵngDeclareNgModule', new Set(['declarations', 'imports', 'exports'])],\n]);\nconst calledName = (callee: unknown): string | undefined => {\n const target = unwrapParentheses(callee);\n if (!isNode(target)) {\n return undefined;\n }\n if (target.type === 'MemberExpression' && !target.computed) {\n return identifierName(target.property);\n }\n return identifierName(target);\n};\nconst CLASS_TYPES = new Set(['ClassDeclaration', 'ClassExpression']);\n// Plain checks rather than type guards: every node is already an IAstNode, and a guard would narrow the other branch\n// to never.\nconst isFunction = (node: unknown): boolean => isNode(node) && FUNCTION_TYPES.has(node.type);\nconst isClass = (node: unknown): boolean => isNode(node) && CLASS_TYPES.has(node.type);\nconst unwrapParentheses = (node: unknown): unknown => {\n let current = node;\n while (isNode(current) && current.type === 'ParenthesizedExpression') {\n current = current.expression;\n }\n return current;\n};\nconst identifierName = (node: unknown): string | undefined =>\n isNode(node) && node.type === 'Identifier' && typeof node.name === 'string' ? node.name : undefined;\nconst nodes = (value: unknown): IAstNode[] => (Array.isArray(value) ? value.filter(isNode) : []);\n\n/**\n * The identifiers a module reads while it is evaluated, with the first reason found for each: module-scope code,\n * functions invoked on the spot, local functions called and local classes constructed while it loads, class heritage,\n * decorators, computed keys, static fields and static blocks. Function bodies run later are left out.\n */\nexport const loadTimeReferences = (program: IAstNode): Map<string, string> => {\n const parents = new WeakMap<IAstNode, IAstNode>();\n walk(program, (node, parent) => {\n if (parent) {\n parents.set(node, parent);\n }\n return true;\n });\n\n const localFunctions = new Map<string, IAstNode>();\n const localClasses = new Map<string, IAstNode>();\n for (const statement of nodes(program.body)) {\n const declaration = statement.type === 'ExportNamedDeclaration' && isNode(statement.declaration) ? statement.declaration : statement;\n const name = identifierName(declaration.id);\n if (name && declaration.type === 'FunctionDeclaration') {\n localFunctions.set(name, declaration);\n }\n if (name && declaration.type === 'ClassDeclaration') {\n localClasses.set(name, declaration);\n }\n if (declaration.type === 'VariableDeclaration') {\n for (const declarator of nodes(declaration.declarations)) {\n const variable = identifierName(declarator.id);\n const init = unwrapParentheses(declarator.init);\n if (variable && isNode(init) && isFunction(init)) {\n localFunctions.set(variable, init);\n }\n if (variable && isNode(init) && isClass(init)) {\n localClasses.set(variable, init);\n }\n }\n }\n }\n\n const references = new Map<string, string>();\n const evaluated = new WeakSet<IAstNode>();\n\n const isInvoked = (fn: IAstNode): boolean => {\n let caller = parents.get(fn);\n while (caller?.type === 'ParenthesizedExpression') {\n caller = parents.get(caller);\n }\n return Boolean(\n caller && (caller.type === 'CallExpression' || caller.type === 'NewExpression') && unwrapParentheses(caller.callee) === unwrapParentheses(fn),\n );\n };\n\n const readsIn = (root: unknown, reason: string): void => {\n if (!isNode(root)) {\n return;\n }\n const rootName = identifierName(root);\n if (rootName && !references.has(rootName)) {\n references.set(rootName, reason);\n }\n walk(root, (node, parent) => {\n if (isFunction(node) && node !== root) {\n if (isInvoked(node)) {\n runBody(node, 'a function invoked while the module loads');\n }\n return false;\n }\n if (isClass(node)) {\n defineClass(node);\n return false;\n }\n const deferred = node.type === 'CallExpression' ? DEFERRED_ANGULAR_DECLARATION_PARTS.get(calledName(node.callee) ?? '') : undefined;\n if (deferred) {\n readsIn(node.callee, reason);\n if (deferred !== true) {\n for (const argument of nodes(node.arguments)) {\n const properties = argument.type === 'ObjectExpression' ? nodes(argument.properties) : [];\n if (properties.length === 0) {\n readsIn(argument, reason);\n }\n for (const property of properties) {\n const name = property.computed\n ? undefined\n : (identifierName(property.key) ?? (isNode(property.key) ? String(property.key.value) : undefined));\n if (property.type !== 'Property' || !name || !deferred.has(name)) {\n readsIn(property.type === 'Property' ? property.value : property, reason);\n }\n }\n }\n }\n return false;\n }\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const callee = identifierName(unwrapParentheses(node.callee));\n const fn = callee ? localFunctions.get(callee) : undefined;\n if (fn) {\n runBody(fn, `a call of ${callee} while the module loads`);\n }\n const cls = callee && node.type === 'NewExpression' ? localClasses.get(callee) : undefined;\n if (cls) {\n constructClass(cls, callee as string);\n }\n }\n const name = identifierName(node);\n if (name && parent) {\n const isPropertyKey =\n (parent.type === 'Property' || parent.type === 'PropertyDefinition' || parent.type === 'MethodDefinition') &&\n parent.key === node &&\n !parent.computed;\n const isMemberName = parent.type === 'MemberExpression' && parent.property === node && !parent.computed;\n if (!isPropertyKey && !isMemberName && !references.has(name)) {\n references.set(name, reason);\n }\n }\n return true;\n });\n };\n\n const runBody = (fn: IAstNode, reason: string): void => {\n if (evaluated.has(fn)) {\n return;\n }\n evaluated.add(fn);\n readsIn(fn.body, reason);\n };\n\n const defineClass = (cls: IAstNode): void => {\n readsIn(cls.superClass, 'a class extending it');\n for (const decorator of nodes(cls.decorators)) {\n readsIn(decorator, 'a class decorator');\n }\n const body = isNode(cls.body) ? nodes(cls.body.body) : [];\n for (const member of body) {\n if (member.computed) {\n readsIn(member.key, 'a computed class member name');\n }\n for (const decorator of nodes(member.decorators)) {\n readsIn(decorator, 'a member decorator');\n }\n if (member.type === 'StaticBlock') {\n readsIn({ type: 'BlockStatement', body: member.body }, 'a static block');\n }\n if (member.type === 'PropertyDefinition' && member.static) {\n readsIn(member.value, 'a static field');\n }\n }\n };\n\n const constructClass = (cls: IAstNode, name: string): void => {\n if (evaluated.has(cls)) {\n return;\n }\n evaluated.add(cls);\n const body = isNode(cls.body) ? nodes(cls.body.body) : [];\n for (const member of body) {\n if (member.type === 'PropertyDefinition' && !member.static) {\n readsIn(member.value, `a field of ${name}, constructed while the module loads`);\n }\n if (member.type === 'MethodDefinition' && member.kind === 'constructor' && isNode(member.value)) {\n readsIn(member.value.body, `the constructor of ${name}, constructed while the module loads`);\n }\n }\n };\n\n const declaresFunction = (statement: IAstNode): boolean =>\n statement.type === 'FunctionDeclaration' ||\n ((statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration') &&\n isNode(statement.declaration) &&\n statement.declaration.type === 'FunctionDeclaration');\n for (const statement of nodes(program.body)) {\n // A function declared at the top runs only when called (calls while loading are followed above).\n if (statement.type === 'ImportDeclaration' || statement.type === 'ExportAllDeclaration' || declaresFunction(statement)) {\n continue;\n }\n if (statement.type === 'ExportNamedDeclaration') {\n // `export { a as b }` re-exports a live binding without reading it.\n readsIn(statement.declaration, 'the top level of the module');\n continue;\n }\n if (statement.type === 'ExportDefaultDeclaration') {\n readsIn(statement.declaration, 'the top level of the module');\n continue;\n }\n readsIn(statement, 'the top level of the module');\n }\n return references;\n};\n\n/** The module's import bindings: local name -> the specifier it is imported from. */\nexport const importedBindings = (program: IAstNode): Map<string, string> => {\n const bindings = new Map<string, string>();\n for (const statement of nodes(program.body)) {\n if (statement.type !== 'ImportDeclaration' || !isNode(statement.source) || typeof statement.source.value !== 'string') {\n continue;\n }\n for (const specifier of nodes(statement.specifiers)) {\n const local = isNode(specifier.local) ? identifierName(specifier.local) : undefined;\n if (local) {\n bindings.set(local, statement.source.value);\n }\n }\n }\n return bindings;\n};\n\nexport const packageNameOf = (specifier: string): string =>\n specifier\n .split('/')\n .slice(0, specifier.startsWith('@') ? 2 : 1)\n .join('/');\n\n/**\n * The batch each shared key loads in, the way @module-federation/vite orders them: a key waits for the shared package\n * roots its package depends on (package.json dependencies, peer and optional dependencies), and a root waits for its\n * own subpaths as well. A dependency on a package whose root is not shared orders nothing.\n */\nexport const shareLoadBatches = (keys: readonly string[], packageDependencies: (packageName: string) => readonly string[]): Map<string, number> => {\n const keySet = new Set(keys);\n const prerequisites = (key: string): string[] => {\n const packageName = packageNameOf(key);\n const roots = packageDependencies(packageName).filter((dependency) => keySet.has(dependency) && dependency !== key);\n return key === packageName ? [...roots, ...keys.filter((other) => other !== key && packageNameOf(other) === packageName)] : roots;\n };\n const batches = new Map<string, number>();\n const visiting = new Set<string>();\n const batchOf = (key: string): number => {\n const known = batches.get(key);\n if (known !== undefined) {\n return known;\n }\n if (visiting.has(key)) {\n return 0;\n }\n visiting.add(key);\n const batch = prerequisites(key).reduce((latest, prerequisite) => Math.max(latest, batchOf(prerequisite) + 1), 0);\n visiting.delete(key);\n batches.set(key, batch);\n return batch;\n };\n for (const key of keys) {\n batchOf(key);\n }\n return batches;\n};\n\nexport interface ISharedLoadOrderProblem {\n entry: string;\n dependency: string;\n binding: string;\n reason: string;\n file: string;\n}\n\nexport const describeSharedLoadOrderProblem = ({ binding, dependency, entry, file, reason }: ISharedLoadOrderProblem): string =>\n `${entry} reads ${binding} from ${dependency} in ${reason} (${file}), but ${dependency} does not load in an earlier batch`;\n\nconst LOAD_SHARE_STUB_PATTERN = /__loadShare__(.+?)__loadShare__/;\nconst REMOTE_ENTRY_CHUNK_PATTERN = /virtual_mf-REMOTE_ENTRY_ID/;\nconst SHARED_IMPORT_MAP_CHUNK_PATTERN = /localSharedImportMap/;\n\n// @module-federation/vite's file name encoding of a package name.\nconst decodeSharedKey = (encoded: string): string =>\n encoded.replaceAll('_mf_0_', '@').replaceAll('_mf_1_', '/').replaceAll('_mf_2_', '-').replaceAll('_mf_3_', '.');\n\nconst directoryOf = (fileName: string): string => fileName.slice(0, fileName.lastIndexOf('/') + 1);\nconst resolveChunk = (fromFile: string, specifier: string): string => {\n const parts = (directoryOf(fromFile) + specifier).split('/');\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === '..') {\n resolved.pop();\n } else if (part !== '.' && part !== '') {\n resolved.push(part);\n }\n }\n return resolved.join('/');\n};\n\nconst stringValue = (node: unknown): string | undefined => {\n if (isNode(node) && node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n if (isNode(node) && node.type === 'TemplateLiteral' && nodes(node.expressions).length === 0) {\n const [quasi] = nodes(node.quasis);\n // A template element's value is `{ raw, cooked }`, not a node.\n const value = (quasi?.value as { cooked?: unknown } | undefined)?.cooked;\n return typeof value === 'string' ? value : undefined;\n }\n return undefined;\n};\n\n// A batch in the built remote entry: an array of keys, or the minifier's `\"a,b\".split(\",\")` for a long one.\nconst batchKeys = (node: IAstNode): string[] | undefined => {\n if (node.type === 'ArrayExpression') {\n const keys = nodes(node.elements).map(stringValue);\n return keys.every((key): key is string => key !== undefined) && keys.length === nodes(node.elements).length ? keys : undefined;\n }\n const callee = unwrapParentheses(node.callee);\n if (node.type === 'CallExpression' && isNode(callee) && callee.type === 'MemberExpression' && identifierName(callee.property) === 'split') {\n const joined = stringValue(callee.object);\n const separator = stringValue(nodes(node.arguments)[0]);\n return joined !== undefined && separator !== undefined ? joined.split(separator) : undefined;\n }\n return undefined;\n};\n\n/**\n * Checks a built hosted module the way it starts first on a page: for every shared entry point's local copy (and the\n * chunks it imports), the bindings it reads while loading from another shared entry point's stub, against the load\n * batches in the built remote entry. `chunks` maps output file names to code.\n */\nexport const findBundleSharedLoadOrderProblems = (\n chunks: ReadonlyMap<string, string>,\n parse: (code: string) => IAstNode,\n): ISharedLoadOrderProblem[] => {\n const importMapFile = [...chunks.keys()].find((fileName) => SHARED_IMPORT_MAP_CHUNK_PATTERN.test(fileName));\n const remoteEntryFile = [...chunks.keys()].find((fileName) => REMOTE_ENTRY_CHUNK_PATTERN.test(fileName));\n if (!importMapFile || !remoteEntryFile) {\n return [];\n }\n\n // Each shared key's local copy: the chunk its import map entry loads.\n const localCopies = new Map<string, string>();\n walk(parse(chunks.get(importMapFile) ?? ''), (node) => {\n if (node.type !== 'Property' || node.computed) {\n return true;\n }\n const key = stringValue(node.key) ?? identifierName(node.key);\n if (key === undefined) {\n return true;\n }\n walk(node.value as IAstNode, (inner) => {\n const source = inner.type === 'ImportExpression' ? stringValue(inner.source) : undefined;\n if (source?.startsWith('.') && !localCopies.has(key)) {\n localCopies.set(key, resolveChunk(importMapFile, source));\n }\n return true;\n });\n return false;\n });\n if (localCopies.size === 0) {\n return [];\n }\n\n let batches: string[][] | undefined;\n walk(parse(chunks.get(remoteEntryFile) ?? ''), (node) => {\n if (batches || node.type !== 'ArrayExpression' || nodes(node.elements).length === 0) {\n return true;\n }\n const candidate = nodes(node.elements).map(batchKeys);\n if (candidate.every((keys): keys is string[] => keys !== undefined) && candidate.flat().some((key) => localCopies.has(key))) {\n batches = candidate;\n }\n return true;\n });\n if (!batches) {\n return [\n { binding: '*', dependency: '*', entry: '*', file: remoteEntryFile, reason: 'the remote entry, whose shared load batches were not found' },\n ];\n }\n const batchOf = new Map(batches.flatMap((keys, index) => keys.map((key): [string, number] => [key, index])));\n\n const programs = new Map<string, IAstNode>();\n const programOf = (fileName: string): IAstNode => {\n let program = programs.get(fileName);\n if (!program) {\n program = parse(chunks.get(fileName) ?? '');\n programs.set(fileName, program);\n }\n return program;\n };\n\n const problems = new Map<string, ISharedLoadOrderProblem>();\n for (const [entry, localCopy] of localCopies) {\n const batch = batchOf.get(entry);\n const pending = [localCopy];\n const visited = new Set<string>();\n while (pending.length > 0) {\n const fileName = pending.pop() as string;\n if (visited.has(fileName) || !chunks.has(fileName)) {\n continue;\n }\n visited.add(fileName);\n const program = programOf(fileName);\n const stubBindings = new Map<string, string>();\n for (const [binding, source] of importedBindings(program)) {\n const stub = source.startsWith('.') ? LOAD_SHARE_STUB_PATTERN.exec(source) : null;\n if (stub?.[1]) {\n stubBindings.set(binding, decodeSharedKey(stub[1]));\n }\n }\n for (const [binding, reason] of loadTimeReferences(program)) {\n const dependency = stubBindings.get(binding);\n const dependencyBatch = dependency === undefined ? undefined : batchOf.get(dependency);\n if (dependency && dependency !== entry && (batch === undefined || dependencyBatch === undefined || dependencyBatch >= batch)) {\n problems.set(`${entry}>${dependency}>${binding}`, { binding, dependency, entry, file: fileName, reason });\n }\n }\n // Follow the chunks it imports, but not into other entry points' stubs: those are the dependencies checked above.\n for (const statement of nodes(program.body)) {\n const source = isNode(statement.source) ? stringValue(statement.source) : undefined;\n if (source?.startsWith('.') && !LOAD_SHARE_STUB_PATTERN.test(source)) {\n pending.push(resolveChunk(fileName, source));\n }\n }\n }\n }\n return [...problems.values()];\n};\n"]}
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
// When a hosted module is the first on a page, its remote entry loads its own copy of every shared entry point in
|
|
2
|
+
// batches: one batch after another, the entries of a batch in parallel. An entry imports another shared entry through
|
|
3
|
+
// a stub whose bindings fill in only once that entry has loaded, so a binding read while the importing entry is still
|
|
4
|
+
// being evaluated (a class extending it, a static field, a call made at the top of the module) is undefined unless the
|
|
5
|
+
// other entry loaded in an earlier batch. These helpers find such reads.
|
|
6
|
+
const isNode = (value) => typeof value === 'object' && value !== null && typeof value.type === 'string';
|
|
7
|
+
const childNodes = (node) => {
|
|
8
|
+
const children = [];
|
|
9
|
+
for (const [key, value] of Object.entries(node)) {
|
|
10
|
+
if (key === 'parent') {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
children.push(...value.filter(isNode));
|
|
15
|
+
}
|
|
16
|
+
else if (isNode(value)) {
|
|
17
|
+
children.push(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return children;
|
|
21
|
+
};
|
|
22
|
+
// Visits every node below `node`; returning false from `visit` skips that node's children.
|
|
23
|
+
const walk = (node, visit, parent = null) => {
|
|
24
|
+
if (!visit(node, parent)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const child of childNodes(node)) {
|
|
28
|
+
walk(child, visit, node);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const FUNCTION_TYPES = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
|
|
32
|
+
// Angular's partial declarations in published packages, as the linker compiles them for a production build: a factory's
|
|
33
|
+
// dependencies and an injectable's provider become factory functions, queries become query functions, class metadata
|
|
34
|
+
// and NgModule scopes become development-only code. Those parts are not read while the module loads; everything else
|
|
35
|
+
// in a declaration (providers, host directives, injector imports, inputs) is. `true` defers the whole declaration.
|
|
36
|
+
const DEFERRED_ANGULAR_DECLARATION_PARTS = new Map([
|
|
37
|
+
['ɵɵngDeclareFactory', true],
|
|
38
|
+
['ɵɵngDeclareClassMetadata', true],
|
|
39
|
+
['ɵɵngDeclareClassMetadataAsync', true],
|
|
40
|
+
['ɵɵngDeclareDirective', new Set(['queries', 'viewQueries'])],
|
|
41
|
+
['ɵɵngDeclareComponent', new Set(['queries', 'viewQueries'])],
|
|
42
|
+
['ɵɵngDeclareInjectable', new Set(['useFactory', 'useClass', 'useExisting', 'useValue', 'deps'])],
|
|
43
|
+
['ɵɵngDeclareNgModule', new Set(['declarations', 'imports', 'exports'])],
|
|
44
|
+
]);
|
|
45
|
+
const calledName = (callee) => {
|
|
46
|
+
const target = unwrapParentheses(callee);
|
|
47
|
+
if (!isNode(target)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (target.type === 'MemberExpression' && !target.computed) {
|
|
51
|
+
return identifierName(target.property);
|
|
52
|
+
}
|
|
53
|
+
return identifierName(target);
|
|
54
|
+
};
|
|
55
|
+
const CLASS_TYPES = new Set(['ClassDeclaration', 'ClassExpression']);
|
|
56
|
+
// Plain checks rather than type guards: every node is already an IAstNode, and a guard would narrow the other branch
|
|
57
|
+
// to never.
|
|
58
|
+
const isFunction = (node) => isNode(node) && FUNCTION_TYPES.has(node.type);
|
|
59
|
+
const isClass = (node) => isNode(node) && CLASS_TYPES.has(node.type);
|
|
60
|
+
const unwrapParentheses = (node) => {
|
|
61
|
+
let current = node;
|
|
62
|
+
while (isNode(current) && current.type === 'ParenthesizedExpression') {
|
|
63
|
+
current = current.expression;
|
|
64
|
+
}
|
|
65
|
+
return current;
|
|
66
|
+
};
|
|
67
|
+
const identifierName = (node) => isNode(node) && node.type === 'Identifier' && typeof node.name === 'string' ? node.name : undefined;
|
|
68
|
+
const nodes = (value) => (Array.isArray(value) ? value.filter(isNode) : []);
|
|
69
|
+
/**
|
|
70
|
+
* The identifiers a module reads while it is evaluated, with the first reason found for each: module-scope code,
|
|
71
|
+
* functions invoked on the spot, local functions called and local classes constructed while it loads, class heritage,
|
|
72
|
+
* decorators, computed keys, static fields and static blocks. Function bodies run later are left out.
|
|
73
|
+
*/
|
|
74
|
+
export const loadTimeReferences = (program) => {
|
|
75
|
+
const parents = new WeakMap();
|
|
76
|
+
walk(program, (node, parent) => {
|
|
77
|
+
if (parent) {
|
|
78
|
+
parents.set(node, parent);
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
const localFunctions = new Map();
|
|
83
|
+
const localClasses = new Map();
|
|
84
|
+
for (const statement of nodes(program.body)) {
|
|
85
|
+
const declaration = statement.type === 'ExportNamedDeclaration' && isNode(statement.declaration) ? statement.declaration : statement;
|
|
86
|
+
const name = identifierName(declaration.id);
|
|
87
|
+
if (name && declaration.type === 'FunctionDeclaration') {
|
|
88
|
+
localFunctions.set(name, declaration);
|
|
89
|
+
}
|
|
90
|
+
if (name && declaration.type === 'ClassDeclaration') {
|
|
91
|
+
localClasses.set(name, declaration);
|
|
92
|
+
}
|
|
93
|
+
if (declaration.type === 'VariableDeclaration') {
|
|
94
|
+
for (const declarator of nodes(declaration.declarations)) {
|
|
95
|
+
const variable = identifierName(declarator.id);
|
|
96
|
+
const init = unwrapParentheses(declarator.init);
|
|
97
|
+
if (variable && isNode(init) && isFunction(init)) {
|
|
98
|
+
localFunctions.set(variable, init);
|
|
99
|
+
}
|
|
100
|
+
if (variable && isNode(init) && isClass(init)) {
|
|
101
|
+
localClasses.set(variable, init);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const references = new Map();
|
|
107
|
+
const evaluated = new WeakSet();
|
|
108
|
+
const isInvoked = (fn) => {
|
|
109
|
+
let caller = parents.get(fn);
|
|
110
|
+
while (caller?.type === 'ParenthesizedExpression') {
|
|
111
|
+
caller = parents.get(caller);
|
|
112
|
+
}
|
|
113
|
+
return Boolean(caller && (caller.type === 'CallExpression' || caller.type === 'NewExpression') && unwrapParentheses(caller.callee) === unwrapParentheses(fn));
|
|
114
|
+
};
|
|
115
|
+
const readsIn = (root, reason) => {
|
|
116
|
+
if (!isNode(root)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const rootName = identifierName(root);
|
|
120
|
+
if (rootName && !references.has(rootName)) {
|
|
121
|
+
references.set(rootName, reason);
|
|
122
|
+
}
|
|
123
|
+
walk(root, (node, parent) => {
|
|
124
|
+
if (isFunction(node) && node !== root) {
|
|
125
|
+
if (isInvoked(node)) {
|
|
126
|
+
runBody(node, 'a function invoked while the module loads');
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (isClass(node)) {
|
|
131
|
+
defineClass(node);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const deferred = node.type === 'CallExpression' ? DEFERRED_ANGULAR_DECLARATION_PARTS.get(calledName(node.callee) ?? '') : undefined;
|
|
135
|
+
if (deferred) {
|
|
136
|
+
readsIn(node.callee, reason);
|
|
137
|
+
if (deferred !== true) {
|
|
138
|
+
for (const argument of nodes(node.arguments)) {
|
|
139
|
+
const properties = argument.type === 'ObjectExpression' ? nodes(argument.properties) : [];
|
|
140
|
+
if (properties.length === 0) {
|
|
141
|
+
readsIn(argument, reason);
|
|
142
|
+
}
|
|
143
|
+
for (const property of properties) {
|
|
144
|
+
const name = property.computed
|
|
145
|
+
? undefined
|
|
146
|
+
: (identifierName(property.key) ?? (isNode(property.key) ? String(property.key.value) : undefined));
|
|
147
|
+
if (property.type !== 'Property' || !name || !deferred.has(name)) {
|
|
148
|
+
readsIn(property.type === 'Property' ? property.value : property, reason);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
156
|
+
const callee = identifierName(unwrapParentheses(node.callee));
|
|
157
|
+
const fn = callee ? localFunctions.get(callee) : undefined;
|
|
158
|
+
if (fn) {
|
|
159
|
+
runBody(fn, `a call of ${callee} while the module loads`);
|
|
160
|
+
}
|
|
161
|
+
const cls = callee && node.type === 'NewExpression' ? localClasses.get(callee) : undefined;
|
|
162
|
+
if (cls) {
|
|
163
|
+
constructClass(cls, callee);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const name = identifierName(node);
|
|
167
|
+
if (name && parent) {
|
|
168
|
+
const isPropertyKey = (parent.type === 'Property' || parent.type === 'PropertyDefinition' || parent.type === 'MethodDefinition') &&
|
|
169
|
+
parent.key === node &&
|
|
170
|
+
!parent.computed;
|
|
171
|
+
const isMemberName = parent.type === 'MemberExpression' && parent.property === node && !parent.computed;
|
|
172
|
+
if (!isPropertyKey && !isMemberName && !references.has(name)) {
|
|
173
|
+
references.set(name, reason);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
const runBody = (fn, reason) => {
|
|
180
|
+
if (evaluated.has(fn)) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
evaluated.add(fn);
|
|
184
|
+
readsIn(fn.body, reason);
|
|
185
|
+
};
|
|
186
|
+
const defineClass = (cls) => {
|
|
187
|
+
readsIn(cls.superClass, 'a class extending it');
|
|
188
|
+
for (const decorator of nodes(cls.decorators)) {
|
|
189
|
+
readsIn(decorator, 'a class decorator');
|
|
190
|
+
}
|
|
191
|
+
const body = isNode(cls.body) ? nodes(cls.body.body) : [];
|
|
192
|
+
for (const member of body) {
|
|
193
|
+
if (member.computed) {
|
|
194
|
+
readsIn(member.key, 'a computed class member name');
|
|
195
|
+
}
|
|
196
|
+
for (const decorator of nodes(member.decorators)) {
|
|
197
|
+
readsIn(decorator, 'a member decorator');
|
|
198
|
+
}
|
|
199
|
+
if (member.type === 'StaticBlock') {
|
|
200
|
+
readsIn({ type: 'BlockStatement', body: member.body }, 'a static block');
|
|
201
|
+
}
|
|
202
|
+
if (member.type === 'PropertyDefinition' && member.static) {
|
|
203
|
+
readsIn(member.value, 'a static field');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const constructClass = (cls, name) => {
|
|
208
|
+
if (evaluated.has(cls)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
evaluated.add(cls);
|
|
212
|
+
const body = isNode(cls.body) ? nodes(cls.body.body) : [];
|
|
213
|
+
for (const member of body) {
|
|
214
|
+
if (member.type === 'PropertyDefinition' && !member.static) {
|
|
215
|
+
readsIn(member.value, `a field of ${name}, constructed while the module loads`);
|
|
216
|
+
}
|
|
217
|
+
if (member.type === 'MethodDefinition' && member.kind === 'constructor' && isNode(member.value)) {
|
|
218
|
+
readsIn(member.value.body, `the constructor of ${name}, constructed while the module loads`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const declaresFunction = (statement) => statement.type === 'FunctionDeclaration' ||
|
|
223
|
+
((statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration') &&
|
|
224
|
+
isNode(statement.declaration) &&
|
|
225
|
+
statement.declaration.type === 'FunctionDeclaration');
|
|
226
|
+
for (const statement of nodes(program.body)) {
|
|
227
|
+
// A function declared at the top runs only when called (calls while loading are followed above).
|
|
228
|
+
if (statement.type === 'ImportDeclaration' || statement.type === 'ExportAllDeclaration' || declaresFunction(statement)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (statement.type === 'ExportNamedDeclaration') {
|
|
232
|
+
// `export { a as b }` re-exports a live binding without reading it.
|
|
233
|
+
readsIn(statement.declaration, 'the top level of the module');
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (statement.type === 'ExportDefaultDeclaration') {
|
|
237
|
+
readsIn(statement.declaration, 'the top level of the module');
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
readsIn(statement, 'the top level of the module');
|
|
241
|
+
}
|
|
242
|
+
return references;
|
|
243
|
+
};
|
|
244
|
+
/** The module's import bindings: local name -> the specifier it is imported from. */
|
|
245
|
+
export const importedBindings = (program) => {
|
|
246
|
+
const bindings = new Map();
|
|
247
|
+
for (const statement of nodes(program.body)) {
|
|
248
|
+
if (statement.type !== 'ImportDeclaration' || !isNode(statement.source) || typeof statement.source.value !== 'string') {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
for (const specifier of nodes(statement.specifiers)) {
|
|
252
|
+
const local = isNode(specifier.local) ? identifierName(specifier.local) : undefined;
|
|
253
|
+
if (local) {
|
|
254
|
+
bindings.set(local, statement.source.value);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return bindings;
|
|
259
|
+
};
|
|
260
|
+
export const packageNameOf = (specifier) => specifier
|
|
261
|
+
.split('/')
|
|
262
|
+
.slice(0, specifier.startsWith('@') ? 2 : 1)
|
|
263
|
+
.join('/');
|
|
264
|
+
/**
|
|
265
|
+
* The batch each shared key loads in, the way @module-federation/vite orders them: a key waits for the shared package
|
|
266
|
+
* roots its package depends on (package.json dependencies, peer and optional dependencies), and a root waits for its
|
|
267
|
+
* own subpaths as well. A dependency on a package whose root is not shared orders nothing.
|
|
268
|
+
*/
|
|
269
|
+
export const shareLoadBatches = (keys, packageDependencies) => {
|
|
270
|
+
const keySet = new Set(keys);
|
|
271
|
+
const prerequisites = (key) => {
|
|
272
|
+
const packageName = packageNameOf(key);
|
|
273
|
+
const roots = packageDependencies(packageName).filter((dependency) => keySet.has(dependency) && dependency !== key);
|
|
274
|
+
return key === packageName ? [...roots, ...keys.filter((other) => other !== key && packageNameOf(other) === packageName)] : roots;
|
|
275
|
+
};
|
|
276
|
+
const batches = new Map();
|
|
277
|
+
const visiting = new Set();
|
|
278
|
+
const batchOf = (key) => {
|
|
279
|
+
const known = batches.get(key);
|
|
280
|
+
if (known !== undefined) {
|
|
281
|
+
return known;
|
|
282
|
+
}
|
|
283
|
+
if (visiting.has(key)) {
|
|
284
|
+
return 0;
|
|
285
|
+
}
|
|
286
|
+
visiting.add(key);
|
|
287
|
+
const batch = prerequisites(key).reduce((latest, prerequisite) => Math.max(latest, batchOf(prerequisite) + 1), 0);
|
|
288
|
+
visiting.delete(key);
|
|
289
|
+
batches.set(key, batch);
|
|
290
|
+
return batch;
|
|
291
|
+
};
|
|
292
|
+
for (const key of keys) {
|
|
293
|
+
batchOf(key);
|
|
294
|
+
}
|
|
295
|
+
return batches;
|
|
296
|
+
};
|
|
297
|
+
export const describeSharedLoadOrderProblem = ({ binding, dependency, entry, file, reason }) => `${entry} reads ${binding} from ${dependency} in ${reason} (${file}), but ${dependency} does not load in an earlier batch`;
|
|
298
|
+
const LOAD_SHARE_STUB_PATTERN = /__loadShare__(.+?)__loadShare__/;
|
|
299
|
+
const REMOTE_ENTRY_CHUNK_PATTERN = /virtual_mf-REMOTE_ENTRY_ID/;
|
|
300
|
+
const SHARED_IMPORT_MAP_CHUNK_PATTERN = /localSharedImportMap/;
|
|
301
|
+
// @module-federation/vite's file name encoding of a package name.
|
|
302
|
+
const decodeSharedKey = (encoded) => encoded.replaceAll('_mf_0_', '@').replaceAll('_mf_1_', '/').replaceAll('_mf_2_', '-').replaceAll('_mf_3_', '.');
|
|
303
|
+
const directoryOf = (fileName) => fileName.slice(0, fileName.lastIndexOf('/') + 1);
|
|
304
|
+
const resolveChunk = (fromFile, specifier) => {
|
|
305
|
+
const parts = (directoryOf(fromFile) + specifier).split('/');
|
|
306
|
+
const resolved = [];
|
|
307
|
+
for (const part of parts) {
|
|
308
|
+
if (part === '..') {
|
|
309
|
+
resolved.pop();
|
|
310
|
+
}
|
|
311
|
+
else if (part !== '.' && part !== '') {
|
|
312
|
+
resolved.push(part);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return resolved.join('/');
|
|
316
|
+
};
|
|
317
|
+
const stringValue = (node) => {
|
|
318
|
+
if (isNode(node) && node.type === 'Literal' && typeof node.value === 'string') {
|
|
319
|
+
return node.value;
|
|
320
|
+
}
|
|
321
|
+
if (isNode(node) && node.type === 'TemplateLiteral' && nodes(node.expressions).length === 0) {
|
|
322
|
+
const [quasi] = nodes(node.quasis);
|
|
323
|
+
// A template element's value is `{ raw, cooked }`, not a node.
|
|
324
|
+
const value = quasi?.value?.cooked;
|
|
325
|
+
return typeof value === 'string' ? value : undefined;
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
};
|
|
329
|
+
// A batch in the built remote entry: an array of keys, or the minifier's `"a,b".split(",")` for a long one.
|
|
330
|
+
const batchKeys = (node) => {
|
|
331
|
+
if (node.type === 'ArrayExpression') {
|
|
332
|
+
const keys = nodes(node.elements).map(stringValue);
|
|
333
|
+
return keys.every((key) => key !== undefined) && keys.length === nodes(node.elements).length ? keys : undefined;
|
|
334
|
+
}
|
|
335
|
+
const callee = unwrapParentheses(node.callee);
|
|
336
|
+
if (node.type === 'CallExpression' && isNode(callee) && callee.type === 'MemberExpression' && identifierName(callee.property) === 'split') {
|
|
337
|
+
const joined = stringValue(callee.object);
|
|
338
|
+
const separator = stringValue(nodes(node.arguments)[0]);
|
|
339
|
+
return joined !== undefined && separator !== undefined ? joined.split(separator) : undefined;
|
|
340
|
+
}
|
|
341
|
+
return undefined;
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Checks a built hosted module the way it starts first on a page: for every shared entry point's local copy (and the
|
|
345
|
+
* chunks it imports), the bindings it reads while loading from another shared entry point's stub, against the load
|
|
346
|
+
* batches in the built remote entry. `chunks` maps output file names to code.
|
|
347
|
+
*/
|
|
348
|
+
export const findBundleSharedLoadOrderProblems = (chunks, parse) => {
|
|
349
|
+
const importMapFile = [...chunks.keys()].find((fileName) => SHARED_IMPORT_MAP_CHUNK_PATTERN.test(fileName));
|
|
350
|
+
const remoteEntryFile = [...chunks.keys()].find((fileName) => REMOTE_ENTRY_CHUNK_PATTERN.test(fileName));
|
|
351
|
+
if (!importMapFile || !remoteEntryFile) {
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
354
|
+
// Each shared key's local copy: the chunk its import map entry loads.
|
|
355
|
+
const localCopies = new Map();
|
|
356
|
+
walk(parse(chunks.get(importMapFile) ?? ''), (node) => {
|
|
357
|
+
if (node.type !== 'Property' || node.computed) {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
const key = stringValue(node.key) ?? identifierName(node.key);
|
|
361
|
+
if (key === undefined) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
walk(node.value, (inner) => {
|
|
365
|
+
const source = inner.type === 'ImportExpression' ? stringValue(inner.source) : undefined;
|
|
366
|
+
if (source?.startsWith('.') && !localCopies.has(key)) {
|
|
367
|
+
localCopies.set(key, resolveChunk(importMapFile, source));
|
|
368
|
+
}
|
|
369
|
+
return true;
|
|
370
|
+
});
|
|
371
|
+
return false;
|
|
372
|
+
});
|
|
373
|
+
if (localCopies.size === 0) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
let batches;
|
|
377
|
+
walk(parse(chunks.get(remoteEntryFile) ?? ''), (node) => {
|
|
378
|
+
if (batches || node.type !== 'ArrayExpression' || nodes(node.elements).length === 0) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
const candidate = nodes(node.elements).map(batchKeys);
|
|
382
|
+
if (candidate.every((keys) => keys !== undefined) && candidate.flat().some((key) => localCopies.has(key))) {
|
|
383
|
+
batches = candidate;
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
});
|
|
387
|
+
if (!batches) {
|
|
388
|
+
return [
|
|
389
|
+
{ binding: '*', dependency: '*', entry: '*', file: remoteEntryFile, reason: 'the remote entry, whose shared load batches were not found' },
|
|
390
|
+
];
|
|
391
|
+
}
|
|
392
|
+
const batchOf = new Map(batches.flatMap((keys, index) => keys.map((key) => [key, index])));
|
|
393
|
+
const programs = new Map();
|
|
394
|
+
const programOf = (fileName) => {
|
|
395
|
+
let program = programs.get(fileName);
|
|
396
|
+
if (!program) {
|
|
397
|
+
program = parse(chunks.get(fileName) ?? '');
|
|
398
|
+
programs.set(fileName, program);
|
|
399
|
+
}
|
|
400
|
+
return program;
|
|
401
|
+
};
|
|
402
|
+
const problems = new Map();
|
|
403
|
+
for (const [entry, localCopy] of localCopies) {
|
|
404
|
+
const batch = batchOf.get(entry);
|
|
405
|
+
const pending = [localCopy];
|
|
406
|
+
const visited = new Set();
|
|
407
|
+
while (pending.length > 0) {
|
|
408
|
+
const fileName = pending.pop();
|
|
409
|
+
if (visited.has(fileName) || !chunks.has(fileName)) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
visited.add(fileName);
|
|
413
|
+
const program = programOf(fileName);
|
|
414
|
+
const stubBindings = new Map();
|
|
415
|
+
for (const [binding, source] of importedBindings(program)) {
|
|
416
|
+
const stub = source.startsWith('.') ? LOAD_SHARE_STUB_PATTERN.exec(source) : null;
|
|
417
|
+
if (stub?.[1]) {
|
|
418
|
+
stubBindings.set(binding, decodeSharedKey(stub[1]));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
for (const [binding, reason] of loadTimeReferences(program)) {
|
|
422
|
+
const dependency = stubBindings.get(binding);
|
|
423
|
+
const dependencyBatch = dependency === undefined ? undefined : batchOf.get(dependency);
|
|
424
|
+
if (dependency && dependency !== entry && (batch === undefined || dependencyBatch === undefined || dependencyBatch >= batch)) {
|
|
425
|
+
problems.set(`${entry}>${dependency}>${binding}`, { binding, dependency, entry, file: fileName, reason });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// Follow the chunks it imports, but not into other entry points' stubs: those are the dependencies checked above.
|
|
429
|
+
for (const statement of nodes(program.body)) {
|
|
430
|
+
const source = isNode(statement.source) ? stringValue(statement.source) : undefined;
|
|
431
|
+
if (source?.startsWith('.') && !LOAD_SHARE_STUB_PATTERN.test(source)) {
|
|
432
|
+
pending.push(resolveChunk(fileName, source));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return [...problems.values()];
|
|
438
|
+
};
|
|
439
|
+
//# sourceMappingURL=shared-load-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-load-order.js","sourceRoot":"","sources":["../src/shared-load-order.ts"],"names":[],"mappings":"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","sourcesContent":["// When a hosted module is the first on a page, its remote entry loads its own copy of every shared entry point in\n// batches: one batch after another, the entries of a batch in parallel. An entry imports another shared entry through\n// a stub whose bindings fill in only once that entry has loaded, so a binding read while the importing entry is still\n// being evaluated (a class extending it, a static field, a call made at the top of the module) is undefined unless the\n// other entry loaded in an earlier batch. These helpers find such reads.\n\nexport interface IAstNode {\n type: string;\n [key: string]: unknown;\n}\n\nconst isNode = (value: unknown): value is IAstNode =>\n typeof value === 'object' && value !== null && typeof (value as { type?: unknown }).type === 'string';\n\nconst childNodes = (node: IAstNode): IAstNode[] => {\n const children: IAstNode[] = [];\n for (const [key, value] of Object.entries(node)) {\n if (key === 'parent') {\n continue;\n }\n if (Array.isArray(value)) {\n children.push(...value.filter(isNode));\n } else if (isNode(value)) {\n children.push(value);\n }\n }\n return children;\n};\n\n// Visits every node below `node`; returning false from `visit` skips that node's children.\nconst walk = (node: IAstNode, visit: (node: IAstNode, parent: IAstNode | null) => boolean, parent: IAstNode | null = null): void => {\n if (!visit(node, parent)) {\n return;\n }\n for (const child of childNodes(node)) {\n walk(child, visit, node);\n }\n};\n\nconst FUNCTION_TYPES = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);\n// Angular's partial declarations in published packages, as the linker compiles them for a production build: a factory's\n// dependencies and an injectable's provider become factory functions, queries become query functions, class metadata\n// and NgModule scopes become development-only code. Those parts are not read while the module loads; everything else\n// in a declaration (providers, host directives, injector imports, inputs) is. `true` defers the whole declaration.\nconst DEFERRED_ANGULAR_DECLARATION_PARTS: ReadonlyMap<string, true | ReadonlySet<string>> = new Map<string, true | ReadonlySet<string>>([\n ['ɵɵngDeclareFactory', true],\n ['ɵɵngDeclareClassMetadata', true],\n ['ɵɵngDeclareClassMetadataAsync', true],\n ['ɵɵngDeclareDirective', new Set(['queries', 'viewQueries'])],\n ['ɵɵngDeclareComponent', new Set(['queries', 'viewQueries'])],\n ['ɵɵngDeclareInjectable', new Set(['useFactory', 'useClass', 'useExisting', 'useValue', 'deps'])],\n ['ɵɵngDeclareNgModule', new Set(['declarations', 'imports', 'exports'])],\n]);\nconst calledName = (callee: unknown): string | undefined => {\n const target = unwrapParentheses(callee);\n if (!isNode(target)) {\n return undefined;\n }\n if (target.type === 'MemberExpression' && !target.computed) {\n return identifierName(target.property);\n }\n return identifierName(target);\n};\nconst CLASS_TYPES = new Set(['ClassDeclaration', 'ClassExpression']);\n// Plain checks rather than type guards: every node is already an IAstNode, and a guard would narrow the other branch\n// to never.\nconst isFunction = (node: unknown): boolean => isNode(node) && FUNCTION_TYPES.has(node.type);\nconst isClass = (node: unknown): boolean => isNode(node) && CLASS_TYPES.has(node.type);\nconst unwrapParentheses = (node: unknown): unknown => {\n let current = node;\n while (isNode(current) && current.type === 'ParenthesizedExpression') {\n current = current.expression;\n }\n return current;\n};\nconst identifierName = (node: unknown): string | undefined =>\n isNode(node) && node.type === 'Identifier' && typeof node.name === 'string' ? node.name : undefined;\nconst nodes = (value: unknown): IAstNode[] => (Array.isArray(value) ? value.filter(isNode) : []);\n\n/**\n * The identifiers a module reads while it is evaluated, with the first reason found for each: module-scope code,\n * functions invoked on the spot, local functions called and local classes constructed while it loads, class heritage,\n * decorators, computed keys, static fields and static blocks. Function bodies run later are left out.\n */\nexport const loadTimeReferences = (program: IAstNode): Map<string, string> => {\n const parents = new WeakMap<IAstNode, IAstNode>();\n walk(program, (node, parent) => {\n if (parent) {\n parents.set(node, parent);\n }\n return true;\n });\n\n const localFunctions = new Map<string, IAstNode>();\n const localClasses = new Map<string, IAstNode>();\n for (const statement of nodes(program.body)) {\n const declaration = statement.type === 'ExportNamedDeclaration' && isNode(statement.declaration) ? statement.declaration : statement;\n const name = identifierName(declaration.id);\n if (name && declaration.type === 'FunctionDeclaration') {\n localFunctions.set(name, declaration);\n }\n if (name && declaration.type === 'ClassDeclaration') {\n localClasses.set(name, declaration);\n }\n if (declaration.type === 'VariableDeclaration') {\n for (const declarator of nodes(declaration.declarations)) {\n const variable = identifierName(declarator.id);\n const init = unwrapParentheses(declarator.init);\n if (variable && isNode(init) && isFunction(init)) {\n localFunctions.set(variable, init);\n }\n if (variable && isNode(init) && isClass(init)) {\n localClasses.set(variable, init);\n }\n }\n }\n }\n\n const references = new Map<string, string>();\n const evaluated = new WeakSet<IAstNode>();\n\n const isInvoked = (fn: IAstNode): boolean => {\n let caller = parents.get(fn);\n while (caller?.type === 'ParenthesizedExpression') {\n caller = parents.get(caller);\n }\n return Boolean(\n caller && (caller.type === 'CallExpression' || caller.type === 'NewExpression') && unwrapParentheses(caller.callee) === unwrapParentheses(fn),\n );\n };\n\n const readsIn = (root: unknown, reason: string): void => {\n if (!isNode(root)) {\n return;\n }\n const rootName = identifierName(root);\n if (rootName && !references.has(rootName)) {\n references.set(rootName, reason);\n }\n walk(root, (node, parent) => {\n if (isFunction(node) && node !== root) {\n if (isInvoked(node)) {\n runBody(node, 'a function invoked while the module loads');\n }\n return false;\n }\n if (isClass(node)) {\n defineClass(node);\n return false;\n }\n const deferred = node.type === 'CallExpression' ? DEFERRED_ANGULAR_DECLARATION_PARTS.get(calledName(node.callee) ?? '') : undefined;\n if (deferred) {\n readsIn(node.callee, reason);\n if (deferred !== true) {\n for (const argument of nodes(node.arguments)) {\n const properties = argument.type === 'ObjectExpression' ? nodes(argument.properties) : [];\n if (properties.length === 0) {\n readsIn(argument, reason);\n }\n for (const property of properties) {\n const name = property.computed\n ? undefined\n : (identifierName(property.key) ?? (isNode(property.key) ? String(property.key.value) : undefined));\n if (property.type !== 'Property' || !name || !deferred.has(name)) {\n readsIn(property.type === 'Property' ? property.value : property, reason);\n }\n }\n }\n }\n return false;\n }\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const callee = identifierName(unwrapParentheses(node.callee));\n const fn = callee ? localFunctions.get(callee) : undefined;\n if (fn) {\n runBody(fn, `a call of ${callee} while the module loads`);\n }\n const cls = callee && node.type === 'NewExpression' ? localClasses.get(callee) : undefined;\n if (cls) {\n constructClass(cls, callee as string);\n }\n }\n const name = identifierName(node);\n if (name && parent) {\n const isPropertyKey =\n (parent.type === 'Property' || parent.type === 'PropertyDefinition' || parent.type === 'MethodDefinition') &&\n parent.key === node &&\n !parent.computed;\n const isMemberName = parent.type === 'MemberExpression' && parent.property === node && !parent.computed;\n if (!isPropertyKey && !isMemberName && !references.has(name)) {\n references.set(name, reason);\n }\n }\n return true;\n });\n };\n\n const runBody = (fn: IAstNode, reason: string): void => {\n if (evaluated.has(fn)) {\n return;\n }\n evaluated.add(fn);\n readsIn(fn.body, reason);\n };\n\n const defineClass = (cls: IAstNode): void => {\n readsIn(cls.superClass, 'a class extending it');\n for (const decorator of nodes(cls.decorators)) {\n readsIn(decorator, 'a class decorator');\n }\n const body = isNode(cls.body) ? nodes(cls.body.body) : [];\n for (const member of body) {\n if (member.computed) {\n readsIn(member.key, 'a computed class member name');\n }\n for (const decorator of nodes(member.decorators)) {\n readsIn(decorator, 'a member decorator');\n }\n if (member.type === 'StaticBlock') {\n readsIn({ type: 'BlockStatement', body: member.body }, 'a static block');\n }\n if (member.type === 'PropertyDefinition' && member.static) {\n readsIn(member.value, 'a static field');\n }\n }\n };\n\n const constructClass = (cls: IAstNode, name: string): void => {\n if (evaluated.has(cls)) {\n return;\n }\n evaluated.add(cls);\n const body = isNode(cls.body) ? nodes(cls.body.body) : [];\n for (const member of body) {\n if (member.type === 'PropertyDefinition' && !member.static) {\n readsIn(member.value, `a field of ${name}, constructed while the module loads`);\n }\n if (member.type === 'MethodDefinition' && member.kind === 'constructor' && isNode(member.value)) {\n readsIn(member.value.body, `the constructor of ${name}, constructed while the module loads`);\n }\n }\n };\n\n const declaresFunction = (statement: IAstNode): boolean =>\n statement.type === 'FunctionDeclaration' ||\n ((statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration') &&\n isNode(statement.declaration) &&\n statement.declaration.type === 'FunctionDeclaration');\n for (const statement of nodes(program.body)) {\n // A function declared at the top runs only when called (calls while loading are followed above).\n if (statement.type === 'ImportDeclaration' || statement.type === 'ExportAllDeclaration' || declaresFunction(statement)) {\n continue;\n }\n if (statement.type === 'ExportNamedDeclaration') {\n // `export { a as b }` re-exports a live binding without reading it.\n readsIn(statement.declaration, 'the top level of the module');\n continue;\n }\n if (statement.type === 'ExportDefaultDeclaration') {\n readsIn(statement.declaration, 'the top level of the module');\n continue;\n }\n readsIn(statement, 'the top level of the module');\n }\n return references;\n};\n\n/** The module's import bindings: local name -> the specifier it is imported from. */\nexport const importedBindings = (program: IAstNode): Map<string, string> => {\n const bindings = new Map<string, string>();\n for (const statement of nodes(program.body)) {\n if (statement.type !== 'ImportDeclaration' || !isNode(statement.source) || typeof statement.source.value !== 'string') {\n continue;\n }\n for (const specifier of nodes(statement.specifiers)) {\n const local = isNode(specifier.local) ? identifierName(specifier.local) : undefined;\n if (local) {\n bindings.set(local, statement.source.value);\n }\n }\n }\n return bindings;\n};\n\nexport const packageNameOf = (specifier: string): string =>\n specifier\n .split('/')\n .slice(0, specifier.startsWith('@') ? 2 : 1)\n .join('/');\n\n/**\n * The batch each shared key loads in, the way @module-federation/vite orders them: a key waits for the shared package\n * roots its package depends on (package.json dependencies, peer and optional dependencies), and a root waits for its\n * own subpaths as well. A dependency on a package whose root is not shared orders nothing.\n */\nexport const shareLoadBatches = (keys: readonly string[], packageDependencies: (packageName: string) => readonly string[]): Map<string, number> => {\n const keySet = new Set(keys);\n const prerequisites = (key: string): string[] => {\n const packageName = packageNameOf(key);\n const roots = packageDependencies(packageName).filter((dependency) => keySet.has(dependency) && dependency !== key);\n return key === packageName ? [...roots, ...keys.filter((other) => other !== key && packageNameOf(other) === packageName)] : roots;\n };\n const batches = new Map<string, number>();\n const visiting = new Set<string>();\n const batchOf = (key: string): number => {\n const known = batches.get(key);\n if (known !== undefined) {\n return known;\n }\n if (visiting.has(key)) {\n return 0;\n }\n visiting.add(key);\n const batch = prerequisites(key).reduce((latest, prerequisite) => Math.max(latest, batchOf(prerequisite) + 1), 0);\n visiting.delete(key);\n batches.set(key, batch);\n return batch;\n };\n for (const key of keys) {\n batchOf(key);\n }\n return batches;\n};\n\nexport interface ISharedLoadOrderProblem {\n entry: string;\n dependency: string;\n binding: string;\n reason: string;\n file: string;\n}\n\nexport const describeSharedLoadOrderProblem = ({ binding, dependency, entry, file, reason }: ISharedLoadOrderProblem): string =>\n `${entry} reads ${binding} from ${dependency} in ${reason} (${file}), but ${dependency} does not load in an earlier batch`;\n\nconst LOAD_SHARE_STUB_PATTERN = /__loadShare__(.+?)__loadShare__/;\nconst REMOTE_ENTRY_CHUNK_PATTERN = /virtual_mf-REMOTE_ENTRY_ID/;\nconst SHARED_IMPORT_MAP_CHUNK_PATTERN = /localSharedImportMap/;\n\n// @module-federation/vite's file name encoding of a package name.\nconst decodeSharedKey = (encoded: string): string =>\n encoded.replaceAll('_mf_0_', '@').replaceAll('_mf_1_', '/').replaceAll('_mf_2_', '-').replaceAll('_mf_3_', '.');\n\nconst directoryOf = (fileName: string): string => fileName.slice(0, fileName.lastIndexOf('/') + 1);\nconst resolveChunk = (fromFile: string, specifier: string): string => {\n const parts = (directoryOf(fromFile) + specifier).split('/');\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === '..') {\n resolved.pop();\n } else if (part !== '.' && part !== '') {\n resolved.push(part);\n }\n }\n return resolved.join('/');\n};\n\nconst stringValue = (node: unknown): string | undefined => {\n if (isNode(node) && node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n if (isNode(node) && node.type === 'TemplateLiteral' && nodes(node.expressions).length === 0) {\n const [quasi] = nodes(node.quasis);\n // A template element's value is `{ raw, cooked }`, not a node.\n const value = (quasi?.value as { cooked?: unknown } | undefined)?.cooked;\n return typeof value === 'string' ? value : undefined;\n }\n return undefined;\n};\n\n// A batch in the built remote entry: an array of keys, or the minifier's `\"a,b\".split(\",\")` for a long one.\nconst batchKeys = (node: IAstNode): string[] | undefined => {\n if (node.type === 'ArrayExpression') {\n const keys = nodes(node.elements).map(stringValue);\n return keys.every((key): key is string => key !== undefined) && keys.length === nodes(node.elements).length ? keys : undefined;\n }\n const callee = unwrapParentheses(node.callee);\n if (node.type === 'CallExpression' && isNode(callee) && callee.type === 'MemberExpression' && identifierName(callee.property) === 'split') {\n const joined = stringValue(callee.object);\n const separator = stringValue(nodes(node.arguments)[0]);\n return joined !== undefined && separator !== undefined ? joined.split(separator) : undefined;\n }\n return undefined;\n};\n\n/**\n * Checks a built hosted module the way it starts first on a page: for every shared entry point's local copy (and the\n * chunks it imports), the bindings it reads while loading from another shared entry point's stub, against the load\n * batches in the built remote entry. `chunks` maps output file names to code.\n */\nexport const findBundleSharedLoadOrderProblems = (\n chunks: ReadonlyMap<string, string>,\n parse: (code: string) => IAstNode,\n): ISharedLoadOrderProblem[] => {\n const importMapFile = [...chunks.keys()].find((fileName) => SHARED_IMPORT_MAP_CHUNK_PATTERN.test(fileName));\n const remoteEntryFile = [...chunks.keys()].find((fileName) => REMOTE_ENTRY_CHUNK_PATTERN.test(fileName));\n if (!importMapFile || !remoteEntryFile) {\n return [];\n }\n\n // Each shared key's local copy: the chunk its import map entry loads.\n const localCopies = new Map<string, string>();\n walk(parse(chunks.get(importMapFile) ?? ''), (node) => {\n if (node.type !== 'Property' || node.computed) {\n return true;\n }\n const key = stringValue(node.key) ?? identifierName(node.key);\n if (key === undefined) {\n return true;\n }\n walk(node.value as IAstNode, (inner) => {\n const source = inner.type === 'ImportExpression' ? stringValue(inner.source) : undefined;\n if (source?.startsWith('.') && !localCopies.has(key)) {\n localCopies.set(key, resolveChunk(importMapFile, source));\n }\n return true;\n });\n return false;\n });\n if (localCopies.size === 0) {\n return [];\n }\n\n let batches: string[][] | undefined;\n walk(parse(chunks.get(remoteEntryFile) ?? ''), (node) => {\n if (batches || node.type !== 'ArrayExpression' || nodes(node.elements).length === 0) {\n return true;\n }\n const candidate = nodes(node.elements).map(batchKeys);\n if (candidate.every((keys): keys is string[] => keys !== undefined) && candidate.flat().some((key) => localCopies.has(key))) {\n batches = candidate;\n }\n return true;\n });\n if (!batches) {\n return [\n { binding: '*', dependency: '*', entry: '*', file: remoteEntryFile, reason: 'the remote entry, whose shared load batches were not found' },\n ];\n }\n const batchOf = new Map(batches.flatMap((keys, index) => keys.map((key): [string, number] => [key, index])));\n\n const programs = new Map<string, IAstNode>();\n const programOf = (fileName: string): IAstNode => {\n let program = programs.get(fileName);\n if (!program) {\n program = parse(chunks.get(fileName) ?? '');\n programs.set(fileName, program);\n }\n return program;\n };\n\n const problems = new Map<string, ISharedLoadOrderProblem>();\n for (const [entry, localCopy] of localCopies) {\n const batch = batchOf.get(entry);\n const pending = [localCopy];\n const visited = new Set<string>();\n while (pending.length > 0) {\n const fileName = pending.pop() as string;\n if (visited.has(fileName) || !chunks.has(fileName)) {\n continue;\n }\n visited.add(fileName);\n const program = programOf(fileName);\n const stubBindings = new Map<string, string>();\n for (const [binding, source] of importedBindings(program)) {\n const stub = source.startsWith('.') ? LOAD_SHARE_STUB_PATTERN.exec(source) : null;\n if (stub?.[1]) {\n stubBindings.set(binding, decodeSharedKey(stub[1]));\n }\n }\n for (const [binding, reason] of loadTimeReferences(program)) {\n const dependency = stubBindings.get(binding);\n const dependencyBatch = dependency === undefined ? undefined : batchOf.get(dependency);\n if (dependency && dependency !== entry && (batch === undefined || dependencyBatch === undefined || dependencyBatch >= batch)) {\n problems.set(`${entry}>${dependency}>${binding}`, { binding, dependency, entry, file: fileName, reason });\n }\n }\n // Follow the chunks it imports, but not into other entry points' stubs: those are the dependencies checked above.\n for (const statement of nodes(program.body)) {\n const source = isNode(statement.source) ? stringValue(statement.source) : undefined;\n if (source?.startsWith('.') && !LOAD_SHARE_STUB_PATTERN.test(source)) {\n pending.push(resolveChunk(fileName, source));\n }\n }\n }\n }\n return [...problems.values()];\n};\n"]}
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Plugin } from 'vite';
|
|
2
2
|
import { type IMfNavigationDefinition, type IMfNavigationManifest } from './navigation.js';
|
|
3
3
|
export interface IMfNavigationPluginOptions {
|
|
4
4
|
fileName?: string;
|
|
@@ -15,7 +15,9 @@ export interface IMfAngularSharedOptions {
|
|
|
15
15
|
* The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`
|
|
16
16
|
* packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions
|
|
17
17
|
* load these once per page and share them; a module on another version keeps its own copy instead of mixing two
|
|
18
|
-
* Angular versions.
|
|
18
|
+
* Angular versions. An entry point that reads another one while it loads stays unshared unless that one is sure to
|
|
19
|
+
* load first (`@angular/material-moment-adapter` extends `@angular/material/core`'s DateAdapter, but the federation
|
|
20
|
+
* plugin orders it only after `@angular/core`); the module bundles its own copy, as before sharing.
|
|
19
21
|
*/
|
|
20
22
|
export declare const createMfAngularShared: (dependencies: Readonly<Record<string, string>>, { root }?: IMfAngularSharedOptions) => Record<string, IMfSharedDependency>;
|
|
21
23
|
export declare const MF_ASSET_MANIFEST_CONTRACT_VERSION: 1;
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAsB,KAAK,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAQ/G,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,KAAK,CAAC;CAClB;AAgBD,MAAM,WAAW,uBAAuB;IAEtC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC9C,WAA0B,uBAA4B,KACrD,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAepC,CAAC;AAEF,eAAO,MAAM,kCAAkC,EAAG,CAAU,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,eAAe,EAAE,OAAO,kCAAkC,CAAC;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD,eAAO,MAAM,0BAA0B,GAAI,YAAY,uBAAuB,KAAG,QAAQ,CAAC,qBAAqB,CAa9G,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,YAAY,uBAAuB,KAAG,MAEnF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,0BAA8C,0BAA0B,KAAG,MA4B7G,CAAC;AAmEF,eAAO,MAAM,wBAAwB,GAAI,UAAU,gBAAgB,KAAG,MAAkD,CAAC;AAEzH,eAAO,MAAM,qBAAqB,GAAI,wBAAkD,6BAA6B,KAAG,MAqCvH,CAAC","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport type { Plugin } from 'vite';\nimport { defineMfNavigation, type IMfNavigationDefinition, type IMfNavigationManifest } from './navigation.js';\n\nconst DEFAULT_FILE_NAME = 'navigation.json';\nconst DEFAULT_ASSET_FILE_NAME = 'module-assets.json';\nconst FILE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\\.json$/;\nconst MODULE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst CONTENT_HASHED_ASSET_PATTERN = /(?:^|\\/)[^/]+-[a-zA-Z0-9_-]{8,}\\.[^/]+$/;\n\nexport interface IMfNavigationPluginOptions {\n fileName?: string;\n navigation: IMfNavigationDefinition;\n}\n\nexport interface IMfSharedDependency {\n requiredVersion: string;\n singleton: false;\n}\n\n// The Angular packages are shared entry point by entry point: each one is a flat ESM file importing the others by\n// package name. An entry point stylesheets also resolve (the `sass` root of @angular/material and @angular/cdk, which\n// `@use '@angular/material'` reaches) is left out, because Module Federation would answer the stylesheet with a script.\n// RxJS subpaths reach its internals by relative path: only its two public entry points are shared, each complete, so a\n// page never mixes RxJS internals from two modules' builds.\nconst SHARED_ANGULAR_SCOPE = '@angular/';\n// Only just-in-time compilation needs these, and the modules are compiled ahead of time. Shared, they would put the\n// compiler (with its ShadowCss placeholders) into every module's build; the modules that bootstrap through\n// platformBrowserDynamic keep their own copy, as before.\nconst UNSHARED_ANGULAR_PACKAGES = new Set(['@angular/compiler', '@angular/platform-browser-dynamic']);\nconst SHARED_RXJS_ENTRY_POINTS = ['rxjs', 'rxjs/operators'] as const;\nconst EXACT_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/;\nconst SCRIPT_ENTRY_CONDITIONS = 'default,types';\n\nexport interface IMfAngularSharedOptions {\n // The directory whose node_modules holds the module's packages; the Vite config's directory by default.\n root?: string;\n}\n\n// The import and re-export statements of a flat ESM file, which start their line; comments mentioning imports do not.\nconst BARE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"]([^'\"./][^'\"]*)['\"]/gm;\nconst RELATIVE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"](\\.\\.?\\/[^'\"]*)['\"]/gm;\n\nconst bareImportPackage = (specifier: string): string =>\n specifier\n .split('/')\n .slice(0, specifier.startsWith('@') ? 2 : 1)\n .join('/');\n\n// The packages an entry point's local copy would contain: what its file imports, and what the files it imports by\n// relative path (its own chunks) import in turn.\nconst importedPackages = (entryFile: string): Set<string> => {\n const packages = new Set<string>();\n const visited = new Set<string>();\n const visit = (file: string): void => {\n if (visited.has(file) || !existsSync(file)) {\n return;\n }\n visited.add(file);\n const source = readFileSync(file, 'utf8');\n for (const [, specifier] of source.matchAll(BARE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n packages.add(bareImportPackage(specifier));\n }\n }\n for (const [, specifier] of source.matchAll(RELATIVE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n visit(join(dirname(file), specifier));\n }\n }\n };\n visit(entryFile);\n return packages;\n};\n\n// Module Federation builds a local copy of every shared entry point, used or not, so an entry point is shared only\n// when every package it imports is installed (`@angular/common/upgrade` needs `@angular/upgrade`, which modules do not\n// install, and `@angular/platform-browser/animations` needs `@angular/animations`) and none of them is a package left\n// unshared (the build-time `@angular/localize/tools` reaches the compiler through its own chunks).\nconst angularScriptEntryPoints = (packageName: string, root: string): string[] => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const packageJsonPath = resolvePackage.resolve(`${packageName}/package.json`);\n const isInstalled = (dependency: string): boolean => {\n try {\n resolvePackage.resolve(`${dependency}/package.json`);\n return true;\n } catch {\n return false;\n }\n };\n const { exports } = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };\n return Object.entries(exports ?? {})\n .filter(([subpath, target]) => {\n if (\n subpath.includes('*') ||\n subpath.split('/').includes('testing') ||\n typeof target !== 'object' ||\n target === null ||\n Object.keys(target).sort().join(',') !== SCRIPT_ENTRY_CONDITIONS\n ) {\n return false;\n }\n const entryFile = join(dirname(packageJsonPath), (target as { default: string }).default);\n return [...importedPackages(entryFile)].every((dependency) => isInstalled(dependency) && !UNSHARED_ANGULAR_PACKAGES.has(dependency));\n })\n .map(([subpath]) => (subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}`));\n};\n\n/**\n * The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`\n * packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions\n * load these once per page and share them; a module on another version keeps its own copy instead of mixing two\n * Angular versions.\n */\nexport const createMfAngularShared = (\n dependencies: Readonly<Record<string, string>>,\n { root = process.cwd() }: IMfAngularSharedOptions = {},\n): Record<string, IMfSharedDependency> => {\n const shared: Record<string, IMfSharedDependency> = {};\n for (const [name, version] of Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))) {\n const isAngular = name.startsWith(SHARED_ANGULAR_SCOPE) && !UNSHARED_ANGULAR_PACKAGES.has(name);\n if (!isAngular && name !== 'rxjs') {\n continue;\n }\n if (!EXACT_VERSION_PATTERN.test(version)) {\n throw new Error(`Shared dependency ${name} must be pinned to an exact version, not \"${version}\".`);\n }\n for (const entryPoint of isAngular ? angularScriptEntryPoints(name, root) : SHARED_RXJS_ENTRY_POINTS) {\n shared[entryPoint] = { requiredVersion: version, singleton: false };\n }\n }\n return shared;\n};\n\nexport const MF_ASSET_MANIFEST_CONTRACT_VERSION = 1 as const;\n\nexport interface IMfAssetManifest {\n assets: readonly string[];\n contractVersion: typeof MF_ASSET_MANIFEST_CONTRACT_VERSION;\n moduleId: string;\n revision: string;\n}\n\nexport interface IMfAssetManifestPluginOptions {\n fileName?: string;\n moduleId: string;\n}\n\ninterface IMfOutputAsset {\n fileName: string;\n source: string | Uint8Array;\n type: 'asset';\n}\n\ninterface IMfOutputChunk {\n code: string;\n fileName: string;\n imports: readonly string[];\n modules: Readonly<Record<string, unknown>>;\n referencedFiles?: readonly string[];\n type: 'chunk';\n viteMetadata?: { importedAssets?: ReadonlySet<string>; importedCss?: ReadonlySet<string> };\n}\n\ntype TMfOutputBundle = Readonly<Record<string, IMfOutputAsset | IMfOutputChunk>>;\n\nconst serializeDefinition = (definition: Readonly<IMfNavigationDefinition>): string => JSON.stringify(definition);\n\nexport const createMfNavigationManifest = (navigation: IMfNavigationDefinition): Readonly<IMfNavigationManifest> => {\n const definition = defineMfNavigation(navigation);\n const revision = createHash('sha256').update(serializeDefinition(definition)).digest('hex');\n\n return Object.freeze({\n applicationId: definition.applicationId,\n contractVersion: definition.contractVersion,\n items: definition.items,\n label: definition.label,\n landingPath: definition.landingPath,\n moduleId: definition.moduleId,\n revision,\n });\n};\n\nexport const serializeMfNavigationManifest = (navigation: IMfNavigationDefinition): string => {\n return `${JSON.stringify(createMfNavigationManifest(navigation), null, 2)}\\n`;\n};\n\nexport const mfNavigationPlugin = ({ fileName = DEFAULT_FILE_NAME, navigation }: IMfNavigationPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend navigation fileName must be a safe JSON file name.');\n }\n\n const source = serializeMfNavigationManifest(navigation);\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-navigation',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n const requestPath = request.url?.split('?', 1)[0];\n if (requestPath !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(source);\n });\n },\n generateBundle() {\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n\nconst isLifecycleModule = (moduleId: string): boolean => /(?:^|\\/)src\\/lifecycle(?:-impl)?\\.[cm]?[jt]sx?$/.test(moduleId.replaceAll('\\\\', '/'));\n\nconst addChunkDependencies = (bundle: TMfOutputBundle, fileName: string, assets: Set<string>): void => {\n if (assets.has(fileName)) {\n return;\n }\n\n const output = bundle[fileName];\n if (output?.type !== 'chunk') {\n return;\n }\n\n assets.add(fileName);\n for (const importedFileName of output.imports) {\n addChunkDependencies(bundle, importedFileName, assets);\n }\n\n const metadata = output.viteMetadata;\n for (const referencedFileName of [...(metadata?.importedAssets ?? []), ...(metadata?.importedCss ?? []), ...(output.referencedFiles ?? [])]) {\n if (bundle[referencedFileName]) {\n assets.add(referencedFileName);\n }\n }\n};\n\nconst outputBytes = (output: IMfOutputAsset | IMfOutputChunk): string | Uint8Array => (output.type === 'chunk' ? output.code : output.source);\n\nconst createMfAssetManifest = (moduleId: string, bundle: TMfOutputBundle): Readonly<IMfAssetManifest> => {\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const lifecycleChunks = Object.values(bundle).filter(\n (output): output is IMfOutputChunk => output.type === 'chunk' && Object.keys(output.modules).some(isLifecycleModule),\n );\n if (lifecycleChunks.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit a lifecycle chunk.`);\n }\n\n const discoveredAssets = new Set<string>();\n for (const chunk of lifecycleChunks) {\n addChunkDependencies(bundle, chunk.fileName, discoveredAssets);\n }\n const assets = [...discoveredAssets].filter((fileName) => CONTENT_HASHED_ASSET_PATTERN.test(fileName)).sort();\n if (assets.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit content-hashed lifecycle assets.`);\n }\n\n const revisionHash = createHash('sha256');\n for (const fileName of assets) {\n const output = bundle[fileName];\n if (!output) {\n throw new Error(`Microfrontend ${moduleId} asset ${fileName} is missing from the output bundle.`);\n }\n revisionHash.update(fileName).update('\\0').update(outputBytes(output)).update('\\0');\n }\n\n return Object.freeze({\n assets: Object.freeze(assets),\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: revisionHash.digest('hex'),\n });\n};\n\nexport const serializeMfAssetManifest = (manifest: IMfAssetManifest): string => `${JSON.stringify(manifest, null, 2)}\\n`;\n\nexport const mfAssetManifestPlugin = ({ fileName = DEFAULT_ASSET_FILE_NAME, moduleId }: IMfAssetManifestPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend asset fileName must be a safe JSON file name.');\n }\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const developmentManifest = serializeMfAssetManifest({\n assets: [],\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: 'development',\n });\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-assets',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n if (request.url?.split('?', 1)[0] !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Access-Control-Allow-Origin', '*');\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(developmentManifest);\n });\n },\n generateBundle(_options, bundle) {\n const source = serializeMfAssetManifest(createMfAssetManifest(moduleId, bundle as unknown as TMfOutputBundle));\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAsB,KAAK,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAkB/G,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,KAAK,CAAC;CAClB;AAgBD,MAAM,WAAW,uBAAuB;IAEtC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkID;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC9C,WAA0B,uBAA4B,KACrD,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAiEpC,CAAC;AAEF,eAAO,MAAM,kCAAkC,EAAG,CAAU,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,eAAe,EAAE,OAAO,kCAAkC,CAAC;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD,eAAO,MAAM,0BAA0B,GAAI,YAAY,uBAAuB,KAAG,QAAQ,CAAC,qBAAqB,CAa9G,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,YAAY,uBAAuB,KAAG,MAEnF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,0BAA8C,0BAA0B,KAAG,MA4B7G,CAAC;AAmEF,eAAO,MAAM,wBAAwB,GAAI,UAAU,gBAAgB,KAAG,MAAkD,CAAC;AAEzH,eAAO,MAAM,qBAAqB,GAAI,wBAAkD,6BAA6B,KAAG,MAwDvH,CAAC","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { type Plugin, parseSync } from 'vite';\nimport { defineMfNavigation, type IMfNavigationDefinition, type IMfNavigationManifest } from './navigation.js';\nimport {\n describeSharedLoadOrderProblem,\n findBundleSharedLoadOrderProblems,\n type IAstNode,\n type ISharedLoadOrderProblem,\n importedBindings,\n loadTimeReferences,\n packageNameOf,\n shareLoadBatches,\n} from './shared-load-order.js';\n\nconst DEFAULT_FILE_NAME = 'navigation.json';\nconst DEFAULT_ASSET_FILE_NAME = 'module-assets.json';\nconst FILE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\\.json$/;\nconst MODULE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst CONTENT_HASHED_ASSET_PATTERN = /(?:^|\\/)[^/]+-[a-zA-Z0-9_-]{8,}\\.[^/]+$/;\n\nexport interface IMfNavigationPluginOptions {\n fileName?: string;\n navigation: IMfNavigationDefinition;\n}\n\nexport interface IMfSharedDependency {\n requiredVersion: string;\n singleton: false;\n}\n\n// The Angular packages are shared entry point by entry point: each one is a flat ESM file importing the others by\n// package name. An entry point stylesheets also resolve (the `sass` root of @angular/material and @angular/cdk, which\n// `@use '@angular/material'` reaches) is left out, because Module Federation would answer the stylesheet with a script.\n// RxJS subpaths reach its internals by relative path: only its two public entry points are shared, each complete, so a\n// page never mixes RxJS internals from two modules' builds.\nconst SHARED_ANGULAR_SCOPE = '@angular/';\n// Only just-in-time compilation needs these, and the modules are compiled ahead of time. Shared, they would put the\n// compiler (with its ShadowCss placeholders) into every module's build; the modules that bootstrap through\n// platformBrowserDynamic keep their own copy, as before.\nconst UNSHARED_ANGULAR_PACKAGES = new Set(['@angular/compiler', '@angular/platform-browser-dynamic']);\nconst SHARED_RXJS_ENTRY_POINTS = ['rxjs', 'rxjs/operators'] as const;\nconst EXACT_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/;\nconst SCRIPT_ENTRY_CONDITIONS = 'default,types';\n\nexport interface IMfAngularSharedOptions {\n // The directory whose node_modules holds the module's packages; the Vite config's directory by default.\n root?: string;\n}\n\n// The import and re-export statements of a flat ESM file, which start their line; comments mentioning imports do not.\nconst BARE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"]([^'\"./][^'\"]*)['\"]/gm;\nconst RELATIVE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"](\\.\\.?\\/[^'\"]*)['\"]/gm;\n\ninterface IAngularEntryPoint {\n key: string;\n // The entry file and the files it imports by relative path (its own chunks): what its local copy contains.\n files: readonly string[];\n}\n\nconst entryPointFiles = (entryFile: string): string[] => {\n const files: string[] = [];\n const visit = (file: string): void => {\n if (files.includes(file) || !existsSync(file)) {\n return;\n }\n files.push(file);\n for (const [, specifier] of readFileSync(file, 'utf8').matchAll(RELATIVE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n visit(join(dirname(file), specifier));\n }\n }\n };\n visit(entryFile);\n return files;\n};\n\nconst importedPackages = (files: readonly string[]): Set<string> => {\n const packages = new Set<string>();\n for (const file of files) {\n for (const [, specifier] of readFileSync(file, 'utf8').matchAll(BARE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n packages.add(packageNameOf(specifier));\n }\n }\n }\n return packages;\n};\n\n// Module Federation builds a local copy of every shared entry point, used or not, so an entry point is shared only\n// when every package it imports is installed (`@angular/common/upgrade` needs `@angular/upgrade`, which modules do not\n// install, and `@angular/platform-browser/animations` needs `@angular/animations`) and none of them is a package left\n// unshared (the build-time `@angular/localize/tools` reaches the compiler through its own chunks).\nconst angularScriptEntryPoints = (packageName: string, root: string): IAngularEntryPoint[] => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const packageJsonPath = resolvePackage.resolve(`${packageName}/package.json`);\n const isInstalled = (dependency: string): boolean => {\n try {\n resolvePackage.resolve(`${dependency}/package.json`);\n return true;\n } catch {\n return false;\n }\n };\n const { exports } = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };\n const entryPoints: IAngularEntryPoint[] = [];\n for (const [subpath, target] of Object.entries(exports ?? {})) {\n if (\n subpath.includes('*') ||\n subpath.split('/').includes('testing') ||\n typeof target !== 'object' ||\n target === null ||\n Object.keys(target).sort().join(',') !== SCRIPT_ENTRY_CONDITIONS\n ) {\n continue;\n }\n const files = entryPointFiles(join(dirname(packageJsonPath), (target as { default: string }).default));\n if ([...importedPackages(files)].every((dependency) => isInstalled(dependency) && !UNSHARED_ANGULAR_PACKAGES.has(dependency))) {\n entryPoints.push({ files, key: subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}` });\n }\n }\n return entryPoints;\n};\n\n// What the federation plugin orders shared keys by: the names in a package's dependencies, peer and optional\n// dependencies.\nconst packageDependencyNames = (root: string): ((packageName: string) => readonly string[]) => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const cache = new Map<string, readonly string[]>();\n return (packageName) => {\n let names = cache.get(packageName);\n if (!names) {\n try {\n const manifest = JSON.parse(readFileSync(resolvePackage.resolve(`${packageName}/package.json`), 'utf8')) as Record<string, unknown>;\n names = ['dependencies', 'peerDependencies', 'optionalDependencies'].flatMap((field) =>\n typeof manifest[field] === 'object' && manifest[field] !== null ? Object.keys(manifest[field]) : [],\n );\n } catch {\n names = [];\n }\n cache.set(packageName, names);\n }\n return names;\n };\n};\n\n// Entry points that read another shared entry point while they load, when that one does not load in an earlier batch:\n// shared, they would start with an undefined binding (see shared-load-order.ts).\nconst loadTimeProblems = (\n entryPoints: readonly IAngularEntryPoint[],\n keys: ReadonlySet<string>,\n batches: ReadonlyMap<string, number>,\n parse: (file: string) => IAstNode | null,\n): ISharedLoadOrderProblem[] => {\n const problems: ISharedLoadOrderProblem[] = [];\n for (const { files, key } of entryPoints) {\n if (!keys.has(key)) {\n continue;\n }\n const batch = batches.get(key) ?? 0;\n for (const file of files) {\n const program = parse(file);\n if (!program) {\n problems.push({ binding: '*', dependency: key, entry: key, file, reason: 'a file that could not be parsed' });\n continue;\n }\n const bindings = importedBindings(program);\n for (const [binding, reason] of loadTimeReferences(program)) {\n const dependency = bindings.get(binding);\n if (dependency && dependency !== key && keys.has(dependency) && (batches.get(dependency) ?? 0) >= batch) {\n problems.push({ binding, dependency, entry: key, file, reason });\n }\n }\n }\n }\n return problems;\n};\n\n/**\n * The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`\n * packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions\n * load these once per page and share them; a module on another version keeps its own copy instead of mixing two\n * Angular versions. An entry point that reads another one while it loads stays unshared unless that one is sure to\n * load first (`@angular/material-moment-adapter` extends `@angular/material/core`'s DateAdapter, but the federation\n * plugin orders it only after `@angular/core`); the module bundles its own copy, as before sharing.\n */\nexport const createMfAngularShared = (\n dependencies: Readonly<Record<string, string>>,\n { root = process.cwd() }: IMfAngularSharedOptions = {},\n): Record<string, IMfSharedDependency> => {\n const versions = new Map<string, string>();\n const angularEntryPoints: IAngularEntryPoint[] = [];\n for (const [name, version] of Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))) {\n const isAngular = name.startsWith(SHARED_ANGULAR_SCOPE) && !UNSHARED_ANGULAR_PACKAGES.has(name);\n if (!isAngular && name !== 'rxjs') {\n continue;\n }\n if (!EXACT_VERSION_PATTERN.test(version)) {\n throw new Error(`Shared dependency ${name} must be pinned to an exact version, not \"${version}\".`);\n }\n const entryPoints = isAngular ? angularScriptEntryPoints(name, root) : SHARED_RXJS_ENTRY_POINTS.map((key) => ({ files: [], key }));\n for (const entryPoint of entryPoints) {\n versions.set(entryPoint.key, version);\n angularEntryPoints.push(entryPoint);\n }\n }\n\n const programs = new Map<string, IAstNode | null>();\n const parse = (file: string): IAstNode | null => {\n if (!programs.has(file)) {\n try {\n const result = parseSync(file, readFileSync(file, 'utf8'));\n programs.set(file, result.errors.length === 0 ? (result.program as unknown as IAstNode) : null);\n } catch {\n programs.set(file, null);\n }\n }\n return programs.get(file) ?? null;\n };\n const dependencyNames = packageDependencyNames(root);\n const keys = new Set(versions.keys());\n // A shared entry point importing an unshared one would bundle its own copy of it, so a module would hold two copies\n // of its classes and injection tokens: an entry point is unshared together with every shared one importing it.\n const importers = (key: string): string[] =>\n angularEntryPoints\n .filter((entryPoint) => keys.has(entryPoint.key) && entryPoint.key !== key)\n .filter((entryPoint) =>\n entryPoint.files.some((file) => {\n const program = parse(file);\n return program !== null && [...importedBindings(program).values()].includes(key);\n }),\n )\n .map((entryPoint) => entryPoint.key);\n for (;;) {\n const unsafe = loadTimeProblems(angularEntryPoints, keys, shareLoadBatches([...keys], dependencyNames), parse).map((problem) => problem.entry);\n if (unsafe.length === 0) {\n break;\n }\n const pending = [...new Set(unsafe)];\n while (pending.length > 0) {\n const key = pending.pop() as string;\n if (keys.delete(key)) {\n pending.push(...importers(key));\n }\n }\n }\n\n const shared: Record<string, IMfSharedDependency> = {};\n for (const [key, version] of versions) {\n if (keys.has(key)) {\n shared[key] = { requiredVersion: version, singleton: false };\n }\n }\n return shared;\n};\n\nexport const MF_ASSET_MANIFEST_CONTRACT_VERSION = 1 as const;\n\nexport interface IMfAssetManifest {\n assets: readonly string[];\n contractVersion: typeof MF_ASSET_MANIFEST_CONTRACT_VERSION;\n moduleId: string;\n revision: string;\n}\n\nexport interface IMfAssetManifestPluginOptions {\n fileName?: string;\n moduleId: string;\n}\n\ninterface IMfOutputAsset {\n fileName: string;\n source: string | Uint8Array;\n type: 'asset';\n}\n\ninterface IMfOutputChunk {\n code: string;\n fileName: string;\n imports: readonly string[];\n modules: Readonly<Record<string, unknown>>;\n referencedFiles?: readonly string[];\n type: 'chunk';\n viteMetadata?: { importedAssets?: ReadonlySet<string>; importedCss?: ReadonlySet<string> };\n}\n\ntype TMfOutputBundle = Readonly<Record<string, IMfOutputAsset | IMfOutputChunk>>;\n\nconst serializeDefinition = (definition: Readonly<IMfNavigationDefinition>): string => JSON.stringify(definition);\n\nexport const createMfNavigationManifest = (navigation: IMfNavigationDefinition): Readonly<IMfNavigationManifest> => {\n const definition = defineMfNavigation(navigation);\n const revision = createHash('sha256').update(serializeDefinition(definition)).digest('hex');\n\n return Object.freeze({\n applicationId: definition.applicationId,\n contractVersion: definition.contractVersion,\n items: definition.items,\n label: definition.label,\n landingPath: definition.landingPath,\n moduleId: definition.moduleId,\n revision,\n });\n};\n\nexport const serializeMfNavigationManifest = (navigation: IMfNavigationDefinition): string => {\n return `${JSON.stringify(createMfNavigationManifest(navigation), null, 2)}\\n`;\n};\n\nexport const mfNavigationPlugin = ({ fileName = DEFAULT_FILE_NAME, navigation }: IMfNavigationPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend navigation fileName must be a safe JSON file name.');\n }\n\n const source = serializeMfNavigationManifest(navigation);\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-navigation',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n const requestPath = request.url?.split('?', 1)[0];\n if (requestPath !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(source);\n });\n },\n generateBundle() {\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n\nconst isLifecycleModule = (moduleId: string): boolean => /(?:^|\\/)src\\/lifecycle(?:-impl)?\\.[cm]?[jt]sx?$/.test(moduleId.replaceAll('\\\\', '/'));\n\nconst addChunkDependencies = (bundle: TMfOutputBundle, fileName: string, assets: Set<string>): void => {\n if (assets.has(fileName)) {\n return;\n }\n\n const output = bundle[fileName];\n if (output?.type !== 'chunk') {\n return;\n }\n\n assets.add(fileName);\n for (const importedFileName of output.imports) {\n addChunkDependencies(bundle, importedFileName, assets);\n }\n\n const metadata = output.viteMetadata;\n for (const referencedFileName of [...(metadata?.importedAssets ?? []), ...(metadata?.importedCss ?? []), ...(output.referencedFiles ?? [])]) {\n if (bundle[referencedFileName]) {\n assets.add(referencedFileName);\n }\n }\n};\n\nconst outputBytes = (output: IMfOutputAsset | IMfOutputChunk): string | Uint8Array => (output.type === 'chunk' ? output.code : output.source);\n\nconst createMfAssetManifest = (moduleId: string, bundle: TMfOutputBundle): Readonly<IMfAssetManifest> => {\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const lifecycleChunks = Object.values(bundle).filter(\n (output): output is IMfOutputChunk => output.type === 'chunk' && Object.keys(output.modules).some(isLifecycleModule),\n );\n if (lifecycleChunks.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit a lifecycle chunk.`);\n }\n\n const discoveredAssets = new Set<string>();\n for (const chunk of lifecycleChunks) {\n addChunkDependencies(bundle, chunk.fileName, discoveredAssets);\n }\n const assets = [...discoveredAssets].filter((fileName) => CONTENT_HASHED_ASSET_PATTERN.test(fileName)).sort();\n if (assets.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit content-hashed lifecycle assets.`);\n }\n\n const revisionHash = createHash('sha256');\n for (const fileName of assets) {\n const output = bundle[fileName];\n if (!output) {\n throw new Error(`Microfrontend ${moduleId} asset ${fileName} is missing from the output bundle.`);\n }\n revisionHash.update(fileName).update('\\0').update(outputBytes(output)).update('\\0');\n }\n\n return Object.freeze({\n assets: Object.freeze(assets),\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: revisionHash.digest('hex'),\n });\n};\n\nexport const serializeMfAssetManifest = (manifest: IMfAssetManifest): string => `${JSON.stringify(manifest, null, 2)}\\n`;\n\nexport const mfAssetManifestPlugin = ({ fileName = DEFAULT_ASSET_FILE_NAME, moduleId }: IMfAssetManifestPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend asset fileName must be a safe JSON file name.');\n }\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const developmentManifest = serializeMfAssetManifest({\n assets: [],\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: 'development',\n });\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-assets',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n if (request.url?.split('?', 1)[0] !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Access-Control-Allow-Origin', '*');\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(developmentManifest);\n });\n },\n generateBundle(_options, bundle) {\n // A module that starts first on a page loads its own copies of the shared entry points; one that reads another\n // before that one has loaded would start with an undefined binding (see createMfAngularShared).\n const chunks = new Map(\n Object.values(bundle as unknown as TMfOutputBundle)\n .filter((output): output is IMfOutputChunk => output.type === 'chunk')\n .map((chunk): [string, string] => [chunk.fileName, chunk.code]),\n );\n const problems = findBundleSharedLoadOrderProblems(chunks, (code) => {\n const result = parseSync('chunk.js', code);\n if (result.errors.length > 0) {\n throw new Error(`Microfrontend ${moduleId} emitted a chunk that could not be parsed: ${result.errors[0]?.message}`);\n }\n return result.program as unknown as IAstNode;\n });\n if (problems.length > 0) {\n this.error(\n `Microfrontend ${moduleId} shares entry points that read another one before it has loaded:\\n${problems.map(describeSharedLoadOrderProblem).join('\\n')}`,\n );\n }\n const source = serializeMfAssetManifest(createMfAssetManifest(moduleId, bundle as unknown as TMfOutputBundle));\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n"]}
|
package/dist/vite.js
CHANGED
|
@@ -2,7 +2,9 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { parseSync } from 'vite';
|
|
5
6
|
import { defineMfNavigation } from './navigation.js';
|
|
7
|
+
import { describeSharedLoadOrderProblem, findBundleSharedLoadOrderProblems, importedBindings, loadTimeReferences, packageNameOf, shareLoadBatches, } from './shared-load-order.js';
|
|
6
8
|
const DEFAULT_FILE_NAME = 'navigation.json';
|
|
7
9
|
const DEFAULT_ASSET_FILE_NAME = 'module-assets.json';
|
|
8
10
|
const FILE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\.json$/;
|
|
@@ -24,33 +26,31 @@ const SCRIPT_ENTRY_CONDITIONS = 'default,types';
|
|
|
24
26
|
// The import and re-export statements of a flat ESM file, which start their line; comments mentioning imports do not.
|
|
25
27
|
const BARE_IMPORT_PATTERN = /^(?:import|export)\b[^;'"]*?(?:\bfrom\s*)?['"]([^'"./][^'"]*)['"]/gm;
|
|
26
28
|
const RELATIVE_IMPORT_PATTERN = /^(?:import|export)\b[^;'"]*?(?:\bfrom\s*)?['"](\.\.?\/[^'"]*)['"]/gm;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
.slice(0, specifier.startsWith('@') ? 2 : 1)
|
|
30
|
-
.join('/');
|
|
31
|
-
// The packages an entry point's local copy would contain: what its file imports, and what the files it imports by
|
|
32
|
-
// relative path (its own chunks) import in turn.
|
|
33
|
-
const importedPackages = (entryFile) => {
|
|
34
|
-
const packages = new Set();
|
|
35
|
-
const visited = new Set();
|
|
29
|
+
const entryPointFiles = (entryFile) => {
|
|
30
|
+
const files = [];
|
|
36
31
|
const visit = (file) => {
|
|
37
|
-
if (
|
|
32
|
+
if (files.includes(file) || !existsSync(file)) {
|
|
38
33
|
return;
|
|
39
34
|
}
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
for (const [, specifier] of source.matchAll(BARE_IMPORT_PATTERN)) {
|
|
43
|
-
if (specifier !== undefined) {
|
|
44
|
-
packages.add(bareImportPackage(specifier));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
for (const [, specifier] of source.matchAll(RELATIVE_IMPORT_PATTERN)) {
|
|
35
|
+
files.push(file);
|
|
36
|
+
for (const [, specifier] of readFileSync(file, 'utf8').matchAll(RELATIVE_IMPORT_PATTERN)) {
|
|
48
37
|
if (specifier !== undefined) {
|
|
49
38
|
visit(join(dirname(file), specifier));
|
|
50
39
|
}
|
|
51
40
|
}
|
|
52
41
|
};
|
|
53
42
|
visit(entryFile);
|
|
43
|
+
return files;
|
|
44
|
+
};
|
|
45
|
+
const importedPackages = (files) => {
|
|
46
|
+
const packages = new Set();
|
|
47
|
+
for (const file of files) {
|
|
48
|
+
for (const [, specifier] of readFileSync(file, 'utf8').matchAll(BARE_IMPORT_PATTERN)) {
|
|
49
|
+
if (specifier !== undefined) {
|
|
50
|
+
packages.add(packageNameOf(specifier));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
54
|
return packages;
|
|
55
55
|
};
|
|
56
56
|
// Module Federation builds a local copy of every shared entry point, used or not, so an entry point is shared only
|
|
@@ -70,28 +70,79 @@ const angularScriptEntryPoints = (packageName, root) => {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
const { exports } = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const entryPoints = [];
|
|
74
|
+
for (const [subpath, target] of Object.entries(exports ?? {})) {
|
|
75
75
|
if (subpath.includes('*') ||
|
|
76
76
|
subpath.split('/').includes('testing') ||
|
|
77
77
|
typeof target !== 'object' ||
|
|
78
78
|
target === null ||
|
|
79
79
|
Object.keys(target).sort().join(',') !== SCRIPT_ENTRY_CONDITIONS) {
|
|
80
|
-
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const files = entryPointFiles(join(dirname(packageJsonPath), target.default));
|
|
83
|
+
if ([...importedPackages(files)].every((dependency) => isInstalled(dependency) && !UNSHARED_ANGULAR_PACKAGES.has(dependency))) {
|
|
84
|
+
entryPoints.push({ files, key: subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}` });
|
|
81
85
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
}
|
|
87
|
+
return entryPoints;
|
|
88
|
+
};
|
|
89
|
+
// What the federation plugin orders shared keys by: the names in a package's dependencies, peer and optional
|
|
90
|
+
// dependencies.
|
|
91
|
+
const packageDependencyNames = (root) => {
|
|
92
|
+
const resolvePackage = createRequire(join(root, 'package.json'));
|
|
93
|
+
const cache = new Map();
|
|
94
|
+
return (packageName) => {
|
|
95
|
+
let names = cache.get(packageName);
|
|
96
|
+
if (!names) {
|
|
97
|
+
try {
|
|
98
|
+
const manifest = JSON.parse(readFileSync(resolvePackage.resolve(`${packageName}/package.json`), 'utf8'));
|
|
99
|
+
names = ['dependencies', 'peerDependencies', 'optionalDependencies'].flatMap((field) => typeof manifest[field] === 'object' && manifest[field] !== null ? Object.keys(manifest[field]) : []);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
names = [];
|
|
103
|
+
}
|
|
104
|
+
cache.set(packageName, names);
|
|
105
|
+
}
|
|
106
|
+
return names;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
// Entry points that read another shared entry point while they load, when that one does not load in an earlier batch:
|
|
110
|
+
// shared, they would start with an undefined binding (see shared-load-order.ts).
|
|
111
|
+
const loadTimeProblems = (entryPoints, keys, batches, parse) => {
|
|
112
|
+
const problems = [];
|
|
113
|
+
for (const { files, key } of entryPoints) {
|
|
114
|
+
if (!keys.has(key)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const batch = batches.get(key) ?? 0;
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
const program = parse(file);
|
|
120
|
+
if (!program) {
|
|
121
|
+
problems.push({ binding: '*', dependency: key, entry: key, file, reason: 'a file that could not be parsed' });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const bindings = importedBindings(program);
|
|
125
|
+
for (const [binding, reason] of loadTimeReferences(program)) {
|
|
126
|
+
const dependency = bindings.get(binding);
|
|
127
|
+
if (dependency && dependency !== key && keys.has(dependency) && (batches.get(dependency) ?? 0) >= batch) {
|
|
128
|
+
problems.push({ binding, dependency, entry: key, file, reason });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return problems;
|
|
86
134
|
};
|
|
87
135
|
/**
|
|
88
136
|
* The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`
|
|
89
137
|
* packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions
|
|
90
138
|
* load these once per page and share them; a module on another version keeps its own copy instead of mixing two
|
|
91
|
-
* Angular versions.
|
|
139
|
+
* Angular versions. An entry point that reads another one while it loads stays unshared unless that one is sure to
|
|
140
|
+
* load first (`@angular/material-moment-adapter` extends `@angular/material/core`'s DateAdapter, but the federation
|
|
141
|
+
* plugin orders it only after `@angular/core`); the module bundles its own copy, as before sharing.
|
|
92
142
|
*/
|
|
93
143
|
export const createMfAngularShared = (dependencies, { root = process.cwd() } = {}) => {
|
|
94
|
-
const
|
|
144
|
+
const versions = new Map();
|
|
145
|
+
const angularEntryPoints = [];
|
|
95
146
|
for (const [name, version] of Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))) {
|
|
96
147
|
const isAngular = name.startsWith(SHARED_ANGULAR_SCOPE) && !UNSHARED_ANGULAR_PACKAGES.has(name);
|
|
97
148
|
if (!isAngular && name !== 'rxjs') {
|
|
@@ -100,8 +151,53 @@ export const createMfAngularShared = (dependencies, { root = process.cwd() } = {
|
|
|
100
151
|
if (!EXACT_VERSION_PATTERN.test(version)) {
|
|
101
152
|
throw new Error(`Shared dependency ${name} must be pinned to an exact version, not "${version}".`);
|
|
102
153
|
}
|
|
103
|
-
|
|
104
|
-
|
|
154
|
+
const entryPoints = isAngular ? angularScriptEntryPoints(name, root) : SHARED_RXJS_ENTRY_POINTS.map((key) => ({ files: [], key }));
|
|
155
|
+
for (const entryPoint of entryPoints) {
|
|
156
|
+
versions.set(entryPoint.key, version);
|
|
157
|
+
angularEntryPoints.push(entryPoint);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const programs = new Map();
|
|
161
|
+
const parse = (file) => {
|
|
162
|
+
if (!programs.has(file)) {
|
|
163
|
+
try {
|
|
164
|
+
const result = parseSync(file, readFileSync(file, 'utf8'));
|
|
165
|
+
programs.set(file, result.errors.length === 0 ? result.program : null);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
programs.set(file, null);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return programs.get(file) ?? null;
|
|
172
|
+
};
|
|
173
|
+
const dependencyNames = packageDependencyNames(root);
|
|
174
|
+
const keys = new Set(versions.keys());
|
|
175
|
+
// A shared entry point importing an unshared one would bundle its own copy of it, so a module would hold two copies
|
|
176
|
+
// of its classes and injection tokens: an entry point is unshared together with every shared one importing it.
|
|
177
|
+
const importers = (key) => angularEntryPoints
|
|
178
|
+
.filter((entryPoint) => keys.has(entryPoint.key) && entryPoint.key !== key)
|
|
179
|
+
.filter((entryPoint) => entryPoint.files.some((file) => {
|
|
180
|
+
const program = parse(file);
|
|
181
|
+
return program !== null && [...importedBindings(program).values()].includes(key);
|
|
182
|
+
}))
|
|
183
|
+
.map((entryPoint) => entryPoint.key);
|
|
184
|
+
for (;;) {
|
|
185
|
+
const unsafe = loadTimeProblems(angularEntryPoints, keys, shareLoadBatches([...keys], dependencyNames), parse).map((problem) => problem.entry);
|
|
186
|
+
if (unsafe.length === 0) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const pending = [...new Set(unsafe)];
|
|
190
|
+
while (pending.length > 0) {
|
|
191
|
+
const key = pending.pop();
|
|
192
|
+
if (keys.delete(key)) {
|
|
193
|
+
pending.push(...importers(key));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const shared = {};
|
|
198
|
+
for (const [key, version] of versions) {
|
|
199
|
+
if (keys.has(key)) {
|
|
200
|
+
shared[key] = { requiredVersion: version, singleton: false };
|
|
105
201
|
}
|
|
106
202
|
}
|
|
107
203
|
return shared;
|
|
@@ -233,6 +329,21 @@ export const mfAssetManifestPlugin = ({ fileName = DEFAULT_ASSET_FILE_NAME, modu
|
|
|
233
329
|
});
|
|
234
330
|
},
|
|
235
331
|
generateBundle(_options, bundle) {
|
|
332
|
+
// A module that starts first on a page loads its own copies of the shared entry points; one that reads another
|
|
333
|
+
// before that one has loaded would start with an undefined binding (see createMfAngularShared).
|
|
334
|
+
const chunks = new Map(Object.values(bundle)
|
|
335
|
+
.filter((output) => output.type === 'chunk')
|
|
336
|
+
.map((chunk) => [chunk.fileName, chunk.code]));
|
|
337
|
+
const problems = findBundleSharedLoadOrderProblems(chunks, (code) => {
|
|
338
|
+
const result = parseSync('chunk.js', code);
|
|
339
|
+
if (result.errors.length > 0) {
|
|
340
|
+
throw new Error(`Microfrontend ${moduleId} emitted a chunk that could not be parsed: ${result.errors[0]?.message}`);
|
|
341
|
+
}
|
|
342
|
+
return result.program;
|
|
343
|
+
});
|
|
344
|
+
if (problems.length > 0) {
|
|
345
|
+
this.error(`Microfrontend ${moduleId} shares entry points that read another one before it has loaded:\n${problems.map(describeSharedLoadOrderProblem).join('\n')}`);
|
|
346
|
+
}
|
|
236
347
|
const source = serializeMfAssetManifest(createMfAssetManifest(moduleId, bundle));
|
|
237
348
|
this.emitFile({ fileName, source, type: 'asset' });
|
|
238
349
|
},
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport type { Plugin } from 'vite';\nimport { defineMfNavigation, type IMfNavigationDefinition, type IMfNavigationManifest } from './navigation.js';\n\nconst DEFAULT_FILE_NAME = 'navigation.json';\nconst DEFAULT_ASSET_FILE_NAME = 'module-assets.json';\nconst FILE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\\.json$/;\nconst MODULE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst CONTENT_HASHED_ASSET_PATTERN = /(?:^|\\/)[^/]+-[a-zA-Z0-9_-]{8,}\\.[^/]+$/;\n\nexport interface IMfNavigationPluginOptions {\n fileName?: string;\n navigation: IMfNavigationDefinition;\n}\n\nexport interface IMfSharedDependency {\n requiredVersion: string;\n singleton: false;\n}\n\n// The Angular packages are shared entry point by entry point: each one is a flat ESM file importing the others by\n// package name. An entry point stylesheets also resolve (the `sass` root of @angular/material and @angular/cdk, which\n// `@use '@angular/material'` reaches) is left out, because Module Federation would answer the stylesheet with a script.\n// RxJS subpaths reach its internals by relative path: only its two public entry points are shared, each complete, so a\n// page never mixes RxJS internals from two modules' builds.\nconst SHARED_ANGULAR_SCOPE = '@angular/';\n// Only just-in-time compilation needs these, and the modules are compiled ahead of time. Shared, they would put the\n// compiler (with its ShadowCss placeholders) into every module's build; the modules that bootstrap through\n// platformBrowserDynamic keep their own copy, as before.\nconst UNSHARED_ANGULAR_PACKAGES = new Set(['@angular/compiler', '@angular/platform-browser-dynamic']);\nconst SHARED_RXJS_ENTRY_POINTS = ['rxjs', 'rxjs/operators'] as const;\nconst EXACT_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/;\nconst SCRIPT_ENTRY_CONDITIONS = 'default,types';\n\nexport interface IMfAngularSharedOptions {\n // The directory whose node_modules holds the module's packages; the Vite config's directory by default.\n root?: string;\n}\n\n// The import and re-export statements of a flat ESM file, which start their line; comments mentioning imports do not.\nconst BARE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"]([^'\"./][^'\"]*)['\"]/gm;\nconst RELATIVE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"](\\.\\.?\\/[^'\"]*)['\"]/gm;\n\nconst bareImportPackage = (specifier: string): string =>\n specifier\n .split('/')\n .slice(0, specifier.startsWith('@') ? 2 : 1)\n .join('/');\n\n// The packages an entry point's local copy would contain: what its file imports, and what the files it imports by\n// relative path (its own chunks) import in turn.\nconst importedPackages = (entryFile: string): Set<string> => {\n const packages = new Set<string>();\n const visited = new Set<string>();\n const visit = (file: string): void => {\n if (visited.has(file) || !existsSync(file)) {\n return;\n }\n visited.add(file);\n const source = readFileSync(file, 'utf8');\n for (const [, specifier] of source.matchAll(BARE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n packages.add(bareImportPackage(specifier));\n }\n }\n for (const [, specifier] of source.matchAll(RELATIVE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n visit(join(dirname(file), specifier));\n }\n }\n };\n visit(entryFile);\n return packages;\n};\n\n// Module Federation builds a local copy of every shared entry point, used or not, so an entry point is shared only\n// when every package it imports is installed (`@angular/common/upgrade` needs `@angular/upgrade`, which modules do not\n// install, and `@angular/platform-browser/animations` needs `@angular/animations`) and none of them is a package left\n// unshared (the build-time `@angular/localize/tools` reaches the compiler through its own chunks).\nconst angularScriptEntryPoints = (packageName: string, root: string): string[] => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const packageJsonPath = resolvePackage.resolve(`${packageName}/package.json`);\n const isInstalled = (dependency: string): boolean => {\n try {\n resolvePackage.resolve(`${dependency}/package.json`);\n return true;\n } catch {\n return false;\n }\n };\n const { exports } = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };\n return Object.entries(exports ?? {})\n .filter(([subpath, target]) => {\n if (\n subpath.includes('*') ||\n subpath.split('/').includes('testing') ||\n typeof target !== 'object' ||\n target === null ||\n Object.keys(target).sort().join(',') !== SCRIPT_ENTRY_CONDITIONS\n ) {\n return false;\n }\n const entryFile = join(dirname(packageJsonPath), (target as { default: string }).default);\n return [...importedPackages(entryFile)].every((dependency) => isInstalled(dependency) && !UNSHARED_ANGULAR_PACKAGES.has(dependency));\n })\n .map(([subpath]) => (subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}`));\n};\n\n/**\n * The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`\n * packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions\n * load these once per page and share them; a module on another version keeps its own copy instead of mixing two\n * Angular versions.\n */\nexport const createMfAngularShared = (\n dependencies: Readonly<Record<string, string>>,\n { root = process.cwd() }: IMfAngularSharedOptions = {},\n): Record<string, IMfSharedDependency> => {\n const shared: Record<string, IMfSharedDependency> = {};\n for (const [name, version] of Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))) {\n const isAngular = name.startsWith(SHARED_ANGULAR_SCOPE) && !UNSHARED_ANGULAR_PACKAGES.has(name);\n if (!isAngular && name !== 'rxjs') {\n continue;\n }\n if (!EXACT_VERSION_PATTERN.test(version)) {\n throw new Error(`Shared dependency ${name} must be pinned to an exact version, not \"${version}\".`);\n }\n for (const entryPoint of isAngular ? angularScriptEntryPoints(name, root) : SHARED_RXJS_ENTRY_POINTS) {\n shared[entryPoint] = { requiredVersion: version, singleton: false };\n }\n }\n return shared;\n};\n\nexport const MF_ASSET_MANIFEST_CONTRACT_VERSION = 1 as const;\n\nexport interface IMfAssetManifest {\n assets: readonly string[];\n contractVersion: typeof MF_ASSET_MANIFEST_CONTRACT_VERSION;\n moduleId: string;\n revision: string;\n}\n\nexport interface IMfAssetManifestPluginOptions {\n fileName?: string;\n moduleId: string;\n}\n\ninterface IMfOutputAsset {\n fileName: string;\n source: string | Uint8Array;\n type: 'asset';\n}\n\ninterface IMfOutputChunk {\n code: string;\n fileName: string;\n imports: readonly string[];\n modules: Readonly<Record<string, unknown>>;\n referencedFiles?: readonly string[];\n type: 'chunk';\n viteMetadata?: { importedAssets?: ReadonlySet<string>; importedCss?: ReadonlySet<string> };\n}\n\ntype TMfOutputBundle = Readonly<Record<string, IMfOutputAsset | IMfOutputChunk>>;\n\nconst serializeDefinition = (definition: Readonly<IMfNavigationDefinition>): string => JSON.stringify(definition);\n\nexport const createMfNavigationManifest = (navigation: IMfNavigationDefinition): Readonly<IMfNavigationManifest> => {\n const definition = defineMfNavigation(navigation);\n const revision = createHash('sha256').update(serializeDefinition(definition)).digest('hex');\n\n return Object.freeze({\n applicationId: definition.applicationId,\n contractVersion: definition.contractVersion,\n items: definition.items,\n label: definition.label,\n landingPath: definition.landingPath,\n moduleId: definition.moduleId,\n revision,\n });\n};\n\nexport const serializeMfNavigationManifest = (navigation: IMfNavigationDefinition): string => {\n return `${JSON.stringify(createMfNavigationManifest(navigation), null, 2)}\\n`;\n};\n\nexport const mfNavigationPlugin = ({ fileName = DEFAULT_FILE_NAME, navigation }: IMfNavigationPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend navigation fileName must be a safe JSON file name.');\n }\n\n const source = serializeMfNavigationManifest(navigation);\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-navigation',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n const requestPath = request.url?.split('?', 1)[0];\n if (requestPath !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(source);\n });\n },\n generateBundle() {\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n\nconst isLifecycleModule = (moduleId: string): boolean => /(?:^|\\/)src\\/lifecycle(?:-impl)?\\.[cm]?[jt]sx?$/.test(moduleId.replaceAll('\\\\', '/'));\n\nconst addChunkDependencies = (bundle: TMfOutputBundle, fileName: string, assets: Set<string>): void => {\n if (assets.has(fileName)) {\n return;\n }\n\n const output = bundle[fileName];\n if (output?.type !== 'chunk') {\n return;\n }\n\n assets.add(fileName);\n for (const importedFileName of output.imports) {\n addChunkDependencies(bundle, importedFileName, assets);\n }\n\n const metadata = output.viteMetadata;\n for (const referencedFileName of [...(metadata?.importedAssets ?? []), ...(metadata?.importedCss ?? []), ...(output.referencedFiles ?? [])]) {\n if (bundle[referencedFileName]) {\n assets.add(referencedFileName);\n }\n }\n};\n\nconst outputBytes = (output: IMfOutputAsset | IMfOutputChunk): string | Uint8Array => (output.type === 'chunk' ? output.code : output.source);\n\nconst createMfAssetManifest = (moduleId: string, bundle: TMfOutputBundle): Readonly<IMfAssetManifest> => {\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const lifecycleChunks = Object.values(bundle).filter(\n (output): output is IMfOutputChunk => output.type === 'chunk' && Object.keys(output.modules).some(isLifecycleModule),\n );\n if (lifecycleChunks.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit a lifecycle chunk.`);\n }\n\n const discoveredAssets = new Set<string>();\n for (const chunk of lifecycleChunks) {\n addChunkDependencies(bundle, chunk.fileName, discoveredAssets);\n }\n const assets = [...discoveredAssets].filter((fileName) => CONTENT_HASHED_ASSET_PATTERN.test(fileName)).sort();\n if (assets.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit content-hashed lifecycle assets.`);\n }\n\n const revisionHash = createHash('sha256');\n for (const fileName of assets) {\n const output = bundle[fileName];\n if (!output) {\n throw new Error(`Microfrontend ${moduleId} asset ${fileName} is missing from the output bundle.`);\n }\n revisionHash.update(fileName).update('\\0').update(outputBytes(output)).update('\\0');\n }\n\n return Object.freeze({\n assets: Object.freeze(assets),\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: revisionHash.digest('hex'),\n });\n};\n\nexport const serializeMfAssetManifest = (manifest: IMfAssetManifest): string => `${JSON.stringify(manifest, null, 2)}\\n`;\n\nexport const mfAssetManifestPlugin = ({ fileName = DEFAULT_ASSET_FILE_NAME, moduleId }: IMfAssetManifestPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend asset fileName must be a safe JSON file name.');\n }\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const developmentManifest = serializeMfAssetManifest({\n assets: [],\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: 'development',\n });\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-assets',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n if (request.url?.split('?', 1)[0] !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Access-Control-Allow-Origin', '*');\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(developmentManifest);\n });\n },\n generateBundle(_options, bundle) {\n const source = serializeMfAssetManifest(createMfAssetManifest(moduleId, bundle as unknown as TMfOutputBundle));\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { type Plugin, parseSync } from 'vite';\nimport { defineMfNavigation, type IMfNavigationDefinition, type IMfNavigationManifest } from './navigation.js';\nimport {\n describeSharedLoadOrderProblem,\n findBundleSharedLoadOrderProblems,\n type IAstNode,\n type ISharedLoadOrderProblem,\n importedBindings,\n loadTimeReferences,\n packageNameOf,\n shareLoadBatches,\n} from './shared-load-order.js';\n\nconst DEFAULT_FILE_NAME = 'navigation.json';\nconst DEFAULT_ASSET_FILE_NAME = 'module-assets.json';\nconst FILE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\\.json$/;\nconst MODULE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst CONTENT_HASHED_ASSET_PATTERN = /(?:^|\\/)[^/]+-[a-zA-Z0-9_-]{8,}\\.[^/]+$/;\n\nexport interface IMfNavigationPluginOptions {\n fileName?: string;\n navigation: IMfNavigationDefinition;\n}\n\nexport interface IMfSharedDependency {\n requiredVersion: string;\n singleton: false;\n}\n\n// The Angular packages are shared entry point by entry point: each one is a flat ESM file importing the others by\n// package name. An entry point stylesheets also resolve (the `sass` root of @angular/material and @angular/cdk, which\n// `@use '@angular/material'` reaches) is left out, because Module Federation would answer the stylesheet with a script.\n// RxJS subpaths reach its internals by relative path: only its two public entry points are shared, each complete, so a\n// page never mixes RxJS internals from two modules' builds.\nconst SHARED_ANGULAR_SCOPE = '@angular/';\n// Only just-in-time compilation needs these, and the modules are compiled ahead of time. Shared, they would put the\n// compiler (with its ShadowCss placeholders) into every module's build; the modules that bootstrap through\n// platformBrowserDynamic keep their own copy, as before.\nconst UNSHARED_ANGULAR_PACKAGES = new Set(['@angular/compiler', '@angular/platform-browser-dynamic']);\nconst SHARED_RXJS_ENTRY_POINTS = ['rxjs', 'rxjs/operators'] as const;\nconst EXACT_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/;\nconst SCRIPT_ENTRY_CONDITIONS = 'default,types';\n\nexport interface IMfAngularSharedOptions {\n // The directory whose node_modules holds the module's packages; the Vite config's directory by default.\n root?: string;\n}\n\n// The import and re-export statements of a flat ESM file, which start their line; comments mentioning imports do not.\nconst BARE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"]([^'\"./][^'\"]*)['\"]/gm;\nconst RELATIVE_IMPORT_PATTERN = /^(?:import|export)\\b[^;'\"]*?(?:\\bfrom\\s*)?['\"](\\.\\.?\\/[^'\"]*)['\"]/gm;\n\ninterface IAngularEntryPoint {\n key: string;\n // The entry file and the files it imports by relative path (its own chunks): what its local copy contains.\n files: readonly string[];\n}\n\nconst entryPointFiles = (entryFile: string): string[] => {\n const files: string[] = [];\n const visit = (file: string): void => {\n if (files.includes(file) || !existsSync(file)) {\n return;\n }\n files.push(file);\n for (const [, specifier] of readFileSync(file, 'utf8').matchAll(RELATIVE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n visit(join(dirname(file), specifier));\n }\n }\n };\n visit(entryFile);\n return files;\n};\n\nconst importedPackages = (files: readonly string[]): Set<string> => {\n const packages = new Set<string>();\n for (const file of files) {\n for (const [, specifier] of readFileSync(file, 'utf8').matchAll(BARE_IMPORT_PATTERN)) {\n if (specifier !== undefined) {\n packages.add(packageNameOf(specifier));\n }\n }\n }\n return packages;\n};\n\n// Module Federation builds a local copy of every shared entry point, used or not, so an entry point is shared only\n// when every package it imports is installed (`@angular/common/upgrade` needs `@angular/upgrade`, which modules do not\n// install, and `@angular/platform-browser/animations` needs `@angular/animations`) and none of them is a package left\n// unshared (the build-time `@angular/localize/tools` reaches the compiler through its own chunks).\nconst angularScriptEntryPoints = (packageName: string, root: string): IAngularEntryPoint[] => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const packageJsonPath = resolvePackage.resolve(`${packageName}/package.json`);\n const isInstalled = (dependency: string): boolean => {\n try {\n resolvePackage.resolve(`${dependency}/package.json`);\n return true;\n } catch {\n return false;\n }\n };\n const { exports } = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };\n const entryPoints: IAngularEntryPoint[] = [];\n for (const [subpath, target] of Object.entries(exports ?? {})) {\n if (\n subpath.includes('*') ||\n subpath.split('/').includes('testing') ||\n typeof target !== 'object' ||\n target === null ||\n Object.keys(target).sort().join(',') !== SCRIPT_ENTRY_CONDITIONS\n ) {\n continue;\n }\n const files = entryPointFiles(join(dirname(packageJsonPath), (target as { default: string }).default));\n if ([...importedPackages(files)].every((dependency) => isInstalled(dependency) && !UNSHARED_ANGULAR_PACKAGES.has(dependency))) {\n entryPoints.push({ files, key: subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}` });\n }\n }\n return entryPoints;\n};\n\n// What the federation plugin orders shared keys by: the names in a package's dependencies, peer and optional\n// dependencies.\nconst packageDependencyNames = (root: string): ((packageName: string) => readonly string[]) => {\n const resolvePackage = createRequire(join(root, 'package.json'));\n const cache = new Map<string, readonly string[]>();\n return (packageName) => {\n let names = cache.get(packageName);\n if (!names) {\n try {\n const manifest = JSON.parse(readFileSync(resolvePackage.resolve(`${packageName}/package.json`), 'utf8')) as Record<string, unknown>;\n names = ['dependencies', 'peerDependencies', 'optionalDependencies'].flatMap((field) =>\n typeof manifest[field] === 'object' && manifest[field] !== null ? Object.keys(manifest[field]) : [],\n );\n } catch {\n names = [];\n }\n cache.set(packageName, names);\n }\n return names;\n };\n};\n\n// Entry points that read another shared entry point while they load, when that one does not load in an earlier batch:\n// shared, they would start with an undefined binding (see shared-load-order.ts).\nconst loadTimeProblems = (\n entryPoints: readonly IAngularEntryPoint[],\n keys: ReadonlySet<string>,\n batches: ReadonlyMap<string, number>,\n parse: (file: string) => IAstNode | null,\n): ISharedLoadOrderProblem[] => {\n const problems: ISharedLoadOrderProblem[] = [];\n for (const { files, key } of entryPoints) {\n if (!keys.has(key)) {\n continue;\n }\n const batch = batches.get(key) ?? 0;\n for (const file of files) {\n const program = parse(file);\n if (!program) {\n problems.push({ binding: '*', dependency: key, entry: key, file, reason: 'a file that could not be parsed' });\n continue;\n }\n const bindings = importedBindings(program);\n for (const [binding, reason] of loadTimeReferences(program)) {\n const dependency = bindings.get(binding);\n if (dependency && dependency !== key && keys.has(dependency) && (batches.get(dependency) ?? 0) >= batch) {\n problems.push({ binding, dependency, entry: key, file, reason });\n }\n }\n }\n }\n return problems;\n};\n\n/**\n * The Module Federation `shared` configuration of a hosted Angular module: every script entry point of the `@angular/*`\n * packages among the module's dependencies, and RxJS, at the module's own exact versions. Modules on the same versions\n * load these once per page and share them; a module on another version keeps its own copy instead of mixing two\n * Angular versions. An entry point that reads another one while it loads stays unshared unless that one is sure to\n * load first (`@angular/material-moment-adapter` extends `@angular/material/core`'s DateAdapter, but the federation\n * plugin orders it only after `@angular/core`); the module bundles its own copy, as before sharing.\n */\nexport const createMfAngularShared = (\n dependencies: Readonly<Record<string, string>>,\n { root = process.cwd() }: IMfAngularSharedOptions = {},\n): Record<string, IMfSharedDependency> => {\n const versions = new Map<string, string>();\n const angularEntryPoints: IAngularEntryPoint[] = [];\n for (const [name, version] of Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))) {\n const isAngular = name.startsWith(SHARED_ANGULAR_SCOPE) && !UNSHARED_ANGULAR_PACKAGES.has(name);\n if (!isAngular && name !== 'rxjs') {\n continue;\n }\n if (!EXACT_VERSION_PATTERN.test(version)) {\n throw new Error(`Shared dependency ${name} must be pinned to an exact version, not \"${version}\".`);\n }\n const entryPoints = isAngular ? angularScriptEntryPoints(name, root) : SHARED_RXJS_ENTRY_POINTS.map((key) => ({ files: [], key }));\n for (const entryPoint of entryPoints) {\n versions.set(entryPoint.key, version);\n angularEntryPoints.push(entryPoint);\n }\n }\n\n const programs = new Map<string, IAstNode | null>();\n const parse = (file: string): IAstNode | null => {\n if (!programs.has(file)) {\n try {\n const result = parseSync(file, readFileSync(file, 'utf8'));\n programs.set(file, result.errors.length === 0 ? (result.program as unknown as IAstNode) : null);\n } catch {\n programs.set(file, null);\n }\n }\n return programs.get(file) ?? null;\n };\n const dependencyNames = packageDependencyNames(root);\n const keys = new Set(versions.keys());\n // A shared entry point importing an unshared one would bundle its own copy of it, so a module would hold two copies\n // of its classes and injection tokens: an entry point is unshared together with every shared one importing it.\n const importers = (key: string): string[] =>\n angularEntryPoints\n .filter((entryPoint) => keys.has(entryPoint.key) && entryPoint.key !== key)\n .filter((entryPoint) =>\n entryPoint.files.some((file) => {\n const program = parse(file);\n return program !== null && [...importedBindings(program).values()].includes(key);\n }),\n )\n .map((entryPoint) => entryPoint.key);\n for (;;) {\n const unsafe = loadTimeProblems(angularEntryPoints, keys, shareLoadBatches([...keys], dependencyNames), parse).map((problem) => problem.entry);\n if (unsafe.length === 0) {\n break;\n }\n const pending = [...new Set(unsafe)];\n while (pending.length > 0) {\n const key = pending.pop() as string;\n if (keys.delete(key)) {\n pending.push(...importers(key));\n }\n }\n }\n\n const shared: Record<string, IMfSharedDependency> = {};\n for (const [key, version] of versions) {\n if (keys.has(key)) {\n shared[key] = { requiredVersion: version, singleton: false };\n }\n }\n return shared;\n};\n\nexport const MF_ASSET_MANIFEST_CONTRACT_VERSION = 1 as const;\n\nexport interface IMfAssetManifest {\n assets: readonly string[];\n contractVersion: typeof MF_ASSET_MANIFEST_CONTRACT_VERSION;\n moduleId: string;\n revision: string;\n}\n\nexport interface IMfAssetManifestPluginOptions {\n fileName?: string;\n moduleId: string;\n}\n\ninterface IMfOutputAsset {\n fileName: string;\n source: string | Uint8Array;\n type: 'asset';\n}\n\ninterface IMfOutputChunk {\n code: string;\n fileName: string;\n imports: readonly string[];\n modules: Readonly<Record<string, unknown>>;\n referencedFiles?: readonly string[];\n type: 'chunk';\n viteMetadata?: { importedAssets?: ReadonlySet<string>; importedCss?: ReadonlySet<string> };\n}\n\ntype TMfOutputBundle = Readonly<Record<string, IMfOutputAsset | IMfOutputChunk>>;\n\nconst serializeDefinition = (definition: Readonly<IMfNavigationDefinition>): string => JSON.stringify(definition);\n\nexport const createMfNavigationManifest = (navigation: IMfNavigationDefinition): Readonly<IMfNavigationManifest> => {\n const definition = defineMfNavigation(navigation);\n const revision = createHash('sha256').update(serializeDefinition(definition)).digest('hex');\n\n return Object.freeze({\n applicationId: definition.applicationId,\n contractVersion: definition.contractVersion,\n items: definition.items,\n label: definition.label,\n landingPath: definition.landingPath,\n moduleId: definition.moduleId,\n revision,\n });\n};\n\nexport const serializeMfNavigationManifest = (navigation: IMfNavigationDefinition): string => {\n return `${JSON.stringify(createMfNavigationManifest(navigation), null, 2)}\\n`;\n};\n\nexport const mfNavigationPlugin = ({ fileName = DEFAULT_FILE_NAME, navigation }: IMfNavigationPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend navigation fileName must be a safe JSON file name.');\n }\n\n const source = serializeMfNavigationManifest(navigation);\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-navigation',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n const requestPath = request.url?.split('?', 1)[0];\n if (requestPath !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(source);\n });\n },\n generateBundle() {\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n\nconst isLifecycleModule = (moduleId: string): boolean => /(?:^|\\/)src\\/lifecycle(?:-impl)?\\.[cm]?[jt]sx?$/.test(moduleId.replaceAll('\\\\', '/'));\n\nconst addChunkDependencies = (bundle: TMfOutputBundle, fileName: string, assets: Set<string>): void => {\n if (assets.has(fileName)) {\n return;\n }\n\n const output = bundle[fileName];\n if (output?.type !== 'chunk') {\n return;\n }\n\n assets.add(fileName);\n for (const importedFileName of output.imports) {\n addChunkDependencies(bundle, importedFileName, assets);\n }\n\n const metadata = output.viteMetadata;\n for (const referencedFileName of [...(metadata?.importedAssets ?? []), ...(metadata?.importedCss ?? []), ...(output.referencedFiles ?? [])]) {\n if (bundle[referencedFileName]) {\n assets.add(referencedFileName);\n }\n }\n};\n\nconst outputBytes = (output: IMfOutputAsset | IMfOutputChunk): string | Uint8Array => (output.type === 'chunk' ? output.code : output.source);\n\nconst createMfAssetManifest = (moduleId: string, bundle: TMfOutputBundle): Readonly<IMfAssetManifest> => {\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const lifecycleChunks = Object.values(bundle).filter(\n (output): output is IMfOutputChunk => output.type === 'chunk' && Object.keys(output.modules).some(isLifecycleModule),\n );\n if (lifecycleChunks.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit a lifecycle chunk.`);\n }\n\n const discoveredAssets = new Set<string>();\n for (const chunk of lifecycleChunks) {\n addChunkDependencies(bundle, chunk.fileName, discoveredAssets);\n }\n const assets = [...discoveredAssets].filter((fileName) => CONTENT_HASHED_ASSET_PATTERN.test(fileName)).sort();\n if (assets.length === 0) {\n throw new Error(`Microfrontend ${moduleId} did not emit content-hashed lifecycle assets.`);\n }\n\n const revisionHash = createHash('sha256');\n for (const fileName of assets) {\n const output = bundle[fileName];\n if (!output) {\n throw new Error(`Microfrontend ${moduleId} asset ${fileName} is missing from the output bundle.`);\n }\n revisionHash.update(fileName).update('\\0').update(outputBytes(output)).update('\\0');\n }\n\n return Object.freeze({\n assets: Object.freeze(assets),\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: revisionHash.digest('hex'),\n });\n};\n\nexport const serializeMfAssetManifest = (manifest: IMfAssetManifest): string => `${JSON.stringify(manifest, null, 2)}\\n`;\n\nexport const mfAssetManifestPlugin = ({ fileName = DEFAULT_ASSET_FILE_NAME, moduleId }: IMfAssetManifestPluginOptions): Plugin => {\n if (!FILE_NAME_PATTERN.test(fileName)) {\n throw new Error('Microfrontend asset fileName must be a safe JSON file name.');\n }\n if (!MODULE_ID_PATTERN.test(moduleId)) {\n throw new Error('Microfrontend asset moduleId must use lowercase letters, numbers, and hyphens.');\n }\n\n const developmentManifest = serializeMfAssetManifest({\n assets: [],\n contractVersion: MF_ASSET_MANIFEST_CONTRACT_VERSION,\n moduleId,\n revision: 'development',\n });\n const publicPath = `/${fileName}`;\n\n return {\n name: 'edynamix-mf-assets',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n if (request.url?.split('?', 1)[0] !== publicPath) {\n next();\n return;\n }\n\n response.statusCode = 200;\n response.setHeader('Access-Control-Allow-Origin', '*');\n response.setHeader('Cache-Control', 'no-cache');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(developmentManifest);\n });\n },\n generateBundle(_options, bundle) {\n // A module that starts first on a page loads its own copies of the shared entry points; one that reads another\n // before that one has loaded would start with an undefined binding (see createMfAngularShared).\n const chunks = new Map(\n Object.values(bundle as unknown as TMfOutputBundle)\n .filter((output): output is IMfOutputChunk => output.type === 'chunk')\n .map((chunk): [string, string] => [chunk.fileName, chunk.code]),\n );\n const problems = findBundleSharedLoadOrderProblems(chunks, (code) => {\n const result = parseSync('chunk.js', code);\n if (result.errors.length > 0) {\n throw new Error(`Microfrontend ${moduleId} emitted a chunk that could not be parsed: ${result.errors[0]?.message}`);\n }\n return result.program as unknown as IAstNode;\n });\n if (problems.length > 0) {\n this.error(\n `Microfrontend ${moduleId} shares entry points that read another one before it has loaded:\\n${problems.map(describeSharedLoadOrderProblem).join('\\n')}`,\n );\n }\n const source = serializeMfAssetManifest(createMfAssetManifest(moduleId, bundle as unknown as TMfOutputBundle));\n this.emitFile({ fileName, source, type: 'asset' });\n },\n };\n};\n"]}
|