every-plugin 3.0.0-rc.11 → 3.0.0-rc.13
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/dist/build/artifact-names.cjs +9 -1
- package/dist/build/artifact-names.cjs.map +1 -1
- package/dist/build/artifact-names.d.cts +1 -0
- package/dist/build/artifact-names.d.cts.map +1 -1
- package/dist/build/artifact-names.d.mts +1 -0
- package/dist/build/artifact-names.d.mts.map +1 -1
- package/dist/build/artifact-names.mjs +9 -1
- package/dist/build/artifact-names.mjs.map +1 -1
- package/dist/build/rspack/build-report-plugin.cjs +9 -3
- package/dist/build/rspack/build-report-plugin.cjs.map +1 -1
- package/dist/build/rspack/build-report-plugin.d.cts +12 -2
- package/dist/build/rspack/build-report-plugin.d.cts.map +1 -1
- package/dist/build/rspack/build-report-plugin.d.mts +12 -2
- package/dist/build/rspack/build-report-plugin.d.mts.map +1 -1
- package/dist/build/rspack/build-report-plugin.mjs +9 -3
- package/dist/build/rspack/build-report-plugin.mjs.map +1 -1
- package/dist/build/rspack/plugin.cjs +4 -2
- package/dist/build/rspack/plugin.cjs.map +1 -1
- package/dist/build/rspack/plugin.d.cts.map +1 -1
- package/dist/build/rspack/plugin.d.mts.map +1 -1
- package/dist/build/rspack/plugin.mjs +4 -2
- package/dist/build/rspack/plugin.mjs.map +1 -1
- package/dist/build/ui/hash-artifacts-plugin.cjs +4 -1
- package/dist/build/ui/hash-artifacts-plugin.cjs.map +1 -1
- package/dist/build/ui/hash-artifacts-plugin.d.cts +6 -0
- package/dist/build/ui/hash-artifacts-plugin.d.cts.map +1 -1
- package/dist/build/ui/hash-artifacts-plugin.d.mts +6 -0
- package/dist/build/ui/hash-artifacts-plugin.d.mts.map +1 -1
- package/dist/build/ui/hash-artifacts-plugin.mjs +4 -1
- package/dist/build/ui/hash-artifacts-plugin.mjs.map +1 -1
- package/dist/build/ui/rsbuild-config.cjs +5 -2
- package/dist/build/ui/rsbuild-config.cjs.map +1 -1
- package/dist/build/ui/rsbuild-config.mjs +6 -3
- package/dist/build/ui/rsbuild-config.mjs.map +1 -1
- package/dist/runtime/mf-config.cjs +1 -1
- package/dist/runtime/mf-config.mjs +1 -1
- package/dist/shared-deps-spec.cjs +26 -4
- package/dist/shared-deps-spec.cjs.map +1 -1
- package/dist/shared-deps-spec.d.cts +23 -3
- package/dist/shared-deps-spec.d.cts.map +1 -1
- package/dist/shared-deps-spec.d.mts +23 -3
- package/dist/shared-deps-spec.d.mts.map +1 -1
- package/dist/shared-deps-spec.mjs +26 -5
- package/dist/shared-deps-spec.mjs.map +1 -1
- package/package.json +1 -1
- package/src/build/artifact-names.ts +8 -0
- package/src/build/rspack/build-report-plugin.ts +20 -3
- package/src/build/rspack/plugin.ts +6 -2
- package/src/build/ui/hash-artifacts-plugin.ts +14 -1
- package/src/build/ui/rsbuild-config.ts +4 -1
- package/src/shared-deps-spec.ts +47 -6
|
@@ -66,7 +66,15 @@ function findHashedEntry(assetNames, base) {
|
|
|
66
66
|
const BuildEntryReportSchema = zod.z.object({
|
|
67
67
|
entry: zod.z.string().min(1),
|
|
68
68
|
browserManifest: zod.z.string().min(1).optional(),
|
|
69
|
-
css: zod.z.string().min(1).optional()
|
|
69
|
+
css: zod.z.string().min(1).optional(),
|
|
70
|
+
/**
|
|
71
|
+
* Shared-singleton `name → version` this build embedded — resolved from the
|
|
72
|
+
* building workspace's node_modules at config time and stamped by both the
|
|
73
|
+
* rspack and rsbuild build paths. The deploy leg records it into the
|
|
74
|
+
* workspace's version manifest, so a slot's pin carries shared-dep
|
|
75
|
+
* semantics and consumers can detect a build skew before loading (#546).
|
|
76
|
+
*/
|
|
77
|
+
shared: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
70
78
|
});
|
|
71
79
|
/**
|
|
72
80
|
* The additive-copy plan for one compiled dist root: each supplied fixed-name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-names.cjs","names":["crypto","z"],"sources":["../../src/build/artifact-names.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport { z } from \"zod\";\n\n/** Dev-mode entry names: the in-memory dev server serves these exact names,\n * and dev consumers (the host's html shell, the compose client payload, the\n * readiness probe) append them — dev has no build report to discover hashed\n * names from. */\nexport const DEV_ENTRY_FILENAME = \"remoteEntry.js\";\nexport const DEV_SERVER_ENTRY_FILENAME = \"remoteEntry.server.js\";\n/** Fixed-name build outputs that get additive immutable hashed copies. */\nexport const MF_MANIFEST_FILENAME = \"mf-manifest.json\";\nexport const STYLE_FILENAME = \"style.css\";\nexport const HASHED_ENTRY_PATTERN = \"remoteEntry.[contenthash].js\";\nexport const HASHED_SERVER_ENTRY_PATTERN = \"remoteEntry.server.[contenthash].js\";\n\n/** The dev-stack signal: everything a dev server spawns (watch or no-watch\n * one-shot builds) serves the fixed dev entry names, because every dev\n * consumer appends them and no build report exists to discover hashed names\n * from. Every other invocation — local builds, host-test production builds,\n * regression container builds, deploys — is a build and emits content-hashed\n * entries + build reports. NODE_ENV cannot carry this decision (vitest runs\n * as \"test\", bundler CLIs default \"production\" even for dev watches), and\n * DEPLOY=true only marks deploy builds — so the dev server stamps its own\n * children. */\nexport function isBuildInvocation(): boolean {\n return process.env.BOS_DEV_SERVER !== \"1\";\n}\n\nexport function uiEntryFilename(input: { isBuild: boolean; server?: boolean }): string {\n if (!input.isBuild) return input.server ? DEV_SERVER_ENTRY_FILENAME : DEV_ENTRY_FILENAME;\n return input.server ? HASHED_SERVER_ENTRY_PATTERN : HASHED_ENTRY_PATTERN;\n}\n\nconst HASH_SEGMENT_PATTERN = /\\.[a-f0-9]{8,}\\./;\n\n/**\n * Serving classification for bundle objects: anything whose name carries a\n * content-hash segment (including hashed entrypoints like\n * `remoteEntry.8f3a….js`) is immutable by construction; fixed-name\n * entrypoints and non-hashed files a redeploy replaces in place must\n * revalidate.\n */\nexport function isImmutableBundlePath(name: string): boolean {\n const base = name.split(\"/\").pop() ?? name;\n return HASH_SEGMENT_PATTERN.test(base);\n}\n\nexport function cacheControlOf(name: string): string {\n return isImmutableBundlePath(name)\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n}\n\n/** Short content hash used to mint additive hashed copies of fixed-name artifacts. */\nexport function contentHashOf(content: string | Uint8Array): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nexport function hashedArtifactName(base: string, hash: string, ext: string): string {\n return `${base}.${hash}.${ext.replace(/^\\./, \"\")}`;\n}\n\n/** Find the content-hashed entry asset for a base name (\"remoteEntry\", \"remoteEntry.server\"). */\nexport function findHashedEntry(assetNames: string[], base: string): string | null {\n const hashed = assetNames.find((name) => {\n const rest = name.startsWith(`${base}.`) ? name.slice(base.length + 1) : null;\n if (rest === null) return false;\n const hash = rest.slice(0, -3);\n return name.endsWith(\".js\") && /^[a-f0-9]{8,}$/.test(hash);\n });\n return hashed ?? null;\n}\n\nexport const BuildEntryReportSchema = z.object({\n entry: z.string().min(1),\n browserManifest: z.string().min(1).optional(),\n css: z.string().min(1).optional(),\n});\nexport type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;\n\nexport interface ArtifactCopy {\n from: string;\n to: string;\n}\n\n/**\n * The additive-copy plan for one compiled dist root: each supplied fixed-name\n * artifact gets an immutable hashed copy. The hashed entry itself needs no\n * copy — the build report carries its name to the deploy leg. Returns null\n * when the build predates hashed entry names (only fixed names emitted).\n */\nexport function planArtifactCopies(input: {\n assetNames: string[];\n entryBase: string;\n contents?: Record<string, string | Uint8Array | undefined>;\n}): { copies: Array<ArtifactCopy>; report: BuildEntryReport } | null {\n const entry = findHashedEntry(input.assetNames, input.entryBase);\n if (!entry) return null;\n\n const copies: Array<ArtifactCopy> = [];\n const report: BuildEntryReport = { entry };\n\n const hashedCopy = (fixedName: string, reportField: \"browserManifest\" | \"css\") => {\n const content = input.contents?.[fixedName];\n if (content === undefined) return;\n const dot = fixedName.lastIndexOf(\".\");\n const base = fixedName.slice(0, dot);\n const ext = fixedName.slice(dot + 1);\n const hashed = hashedArtifactName(base, contentHashOf(content), ext);\n copies.push({ from: fixedName, to: hashed });\n report[reportField] = hashed;\n };\n hashedCopy(MF_MANIFEST_FILENAME, \"browserManifest\");\n hashedCopy(STYLE_FILENAME, \"css\");\n\n return { copies, report };\n}\n"],"mappings":";;;;;;;;;;;AAOA,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;;AAEzC,MAAa,uBAAuB;AACpC,MAAa,iBAAiB;AAC9B,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;;;;;;;;;;AAW3C,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,SAAgB,gBAAgB,OAAuD;CACrF,IAAI,CAAC,MAAM,SAAS,OAAO,MAAM,SAAS,4BAA4B;CACtE,OAAO,MAAM,SAAS,8BAA8B;AACtD;AAEA,MAAM,uBAAuB;;;;;;;;AAS7B,SAAgB,sBAAsB,MAAuB;CAC3D,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;CACtC,OAAO,qBAAqB,KAAK,IAAI;AACvC;AAEA,SAAgB,eAAe,MAAsB;CACnD,OAAO,sBAAsB,IAAI,IAC7B,wCACA;AACN;;AAGA,SAAgB,cAAc,SAAsC;CAClE,OAAOA,oBAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAgB,mBAAmB,MAAc,MAAc,KAAqB;CAClF,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,QAAQ,OAAO,EAAE;AACjD;;AAGA,SAAgB,gBAAgB,YAAsB,MAA6B;CAOjF,OANe,WAAW,MAAM,SAAS;EACvC,MAAM,OAAO,KAAK,WAAW,GAAG,KAAK,EAAE,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI;EACzE,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;EAC7B,OAAO,KAAK,SAAS,KAAK,KAAK,iBAAiB,KAAK,IAAI;CAC3D,CACY,KAAK;AACnB;AAEA,MAAa,yBAAyBC,MAAE,OAAO;CAC7C,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,iBAAiBA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC5C,KAAKA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"artifact-names.cjs","names":["crypto","z"],"sources":["../../src/build/artifact-names.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport { z } from \"zod\";\n\n/** Dev-mode entry names: the in-memory dev server serves these exact names,\n * and dev consumers (the host's html shell, the compose client payload, the\n * readiness probe) append them — dev has no build report to discover hashed\n * names from. */\nexport const DEV_ENTRY_FILENAME = \"remoteEntry.js\";\nexport const DEV_SERVER_ENTRY_FILENAME = \"remoteEntry.server.js\";\n/** Fixed-name build outputs that get additive immutable hashed copies. */\nexport const MF_MANIFEST_FILENAME = \"mf-manifest.json\";\nexport const STYLE_FILENAME = \"style.css\";\nexport const HASHED_ENTRY_PATTERN = \"remoteEntry.[contenthash].js\";\nexport const HASHED_SERVER_ENTRY_PATTERN = \"remoteEntry.server.[contenthash].js\";\n\n/** The dev-stack signal: everything a dev server spawns (watch or no-watch\n * one-shot builds) serves the fixed dev entry names, because every dev\n * consumer appends them and no build report exists to discover hashed names\n * from. Every other invocation — local builds, host-test production builds,\n * regression container builds, deploys — is a build and emits content-hashed\n * entries + build reports. NODE_ENV cannot carry this decision (vitest runs\n * as \"test\", bundler CLIs default \"production\" even for dev watches), and\n * DEPLOY=true only marks deploy builds — so the dev server stamps its own\n * children. */\nexport function isBuildInvocation(): boolean {\n return process.env.BOS_DEV_SERVER !== \"1\";\n}\n\nexport function uiEntryFilename(input: { isBuild: boolean; server?: boolean }): string {\n if (!input.isBuild) return input.server ? DEV_SERVER_ENTRY_FILENAME : DEV_ENTRY_FILENAME;\n return input.server ? HASHED_SERVER_ENTRY_PATTERN : HASHED_ENTRY_PATTERN;\n}\n\nconst HASH_SEGMENT_PATTERN = /\\.[a-f0-9]{8,}\\./;\n\n/**\n * Serving classification for bundle objects: anything whose name carries a\n * content-hash segment (including hashed entrypoints like\n * `remoteEntry.8f3a….js`) is immutable by construction; fixed-name\n * entrypoints and non-hashed files a redeploy replaces in place must\n * revalidate.\n */\nexport function isImmutableBundlePath(name: string): boolean {\n const base = name.split(\"/\").pop() ?? name;\n return HASH_SEGMENT_PATTERN.test(base);\n}\n\nexport function cacheControlOf(name: string): string {\n return isImmutableBundlePath(name)\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n}\n\n/** Short content hash used to mint additive hashed copies of fixed-name artifacts. */\nexport function contentHashOf(content: string | Uint8Array): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nexport function hashedArtifactName(base: string, hash: string, ext: string): string {\n return `${base}.${hash}.${ext.replace(/^\\./, \"\")}`;\n}\n\n/** Find the content-hashed entry asset for a base name (\"remoteEntry\", \"remoteEntry.server\"). */\nexport function findHashedEntry(assetNames: string[], base: string): string | null {\n const hashed = assetNames.find((name) => {\n const rest = name.startsWith(`${base}.`) ? name.slice(base.length + 1) : null;\n if (rest === null) return false;\n const hash = rest.slice(0, -3);\n return name.endsWith(\".js\") && /^[a-f0-9]{8,}$/.test(hash);\n });\n return hashed ?? null;\n}\n\nexport const BuildEntryReportSchema = z.object({\n entry: z.string().min(1),\n browserManifest: z.string().min(1).optional(),\n css: z.string().min(1).optional(),\n /**\n * Shared-singleton `name → version` this build embedded — resolved from the\n * building workspace's node_modules at config time and stamped by both the\n * rspack and rsbuild build paths. The deploy leg records it into the\n * workspace's version manifest, so a slot's pin carries shared-dep\n * semantics and consumers can detect a build skew before loading (#546).\n */\n shared: z.record(z.string(), z.string()).optional(),\n});\nexport type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;\n\nexport interface ArtifactCopy {\n from: string;\n to: string;\n}\n\n/**\n * The additive-copy plan for one compiled dist root: each supplied fixed-name\n * artifact gets an immutable hashed copy. The hashed entry itself needs no\n * copy — the build report carries its name to the deploy leg. Returns null\n * when the build predates hashed entry names (only fixed names emitted).\n */\nexport function planArtifactCopies(input: {\n assetNames: string[];\n entryBase: string;\n contents?: Record<string, string | Uint8Array | undefined>;\n}): { copies: Array<ArtifactCopy>; report: BuildEntryReport } | null {\n const entry = findHashedEntry(input.assetNames, input.entryBase);\n if (!entry) return null;\n\n const copies: Array<ArtifactCopy> = [];\n const report: BuildEntryReport = { entry };\n\n const hashedCopy = (fixedName: string, reportField: \"browserManifest\" | \"css\") => {\n const content = input.contents?.[fixedName];\n if (content === undefined) return;\n const dot = fixedName.lastIndexOf(\".\");\n const base = fixedName.slice(0, dot);\n const ext = fixedName.slice(dot + 1);\n const hashed = hashedArtifactName(base, contentHashOf(content), ext);\n copies.push({ from: fixedName, to: hashed });\n report[reportField] = hashed;\n };\n hashedCopy(MF_MANIFEST_FILENAME, \"browserManifest\");\n hashedCopy(STYLE_FILENAME, \"css\");\n\n return { copies, report };\n}\n"],"mappings":";;;;;;;;;;;AAOA,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;;AAEzC,MAAa,uBAAuB;AACpC,MAAa,iBAAiB;AAC9B,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;;;;;;;;;;AAW3C,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,SAAgB,gBAAgB,OAAuD;CACrF,IAAI,CAAC,MAAM,SAAS,OAAO,MAAM,SAAS,4BAA4B;CACtE,OAAO,MAAM,SAAS,8BAA8B;AACtD;AAEA,MAAM,uBAAuB;;;;;;;;AAS7B,SAAgB,sBAAsB,MAAuB;CAC3D,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;CACtC,OAAO,qBAAqB,KAAK,IAAI;AACvC;AAEA,SAAgB,eAAe,MAAsB;CACnD,OAAO,sBAAsB,IAAI,IAC7B,wCACA;AACN;;AAGA,SAAgB,cAAc,SAAsC;CAClE,OAAOA,oBAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAgB,mBAAmB,MAAc,MAAc,KAAqB;CAClF,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,QAAQ,OAAO,EAAE;AACjD;;AAGA,SAAgB,gBAAgB,YAAsB,MAA6B;CAOjF,OANe,WAAW,MAAM,SAAS;EACvC,MAAM,OAAO,KAAK,WAAW,GAAG,KAAK,EAAE,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI;EACzE,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;EAC7B,OAAO,KAAK,SAAS,KAAK,KAAK,iBAAiB,KAAK,IAAI;CAC3D,CACY,KAAK;AACnB;AAEA,MAAa,yBAAyBC,MAAE,OAAO;CAC7C,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,iBAAiBA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC5C,KAAKA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;;;;;;;;CAQhC,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAGA,MAAE,OAAO,CAAC,CAAC,CAAC,SAAS;AACpD,CAAC;;;;;;;AAcD,SAAgB,mBAAmB,OAIkC;CACnE,MAAM,QAAQ,gBAAgB,MAAM,YAAY,MAAM,SAAS;CAC/D,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,SAA8B,CAAC;CACrC,MAAM,SAA2B,EAAE,MAAM;CAEzC,MAAM,cAAc,WAAmB,gBAA2C;EAChF,MAAM,UAAU,MAAM,WAAW;EACjC,IAAI,YAAY,QAAW;EAC3B,MAAM,MAAM,UAAU,YAAY,GAAG;EACrC,MAAM,OAAO,UAAU,MAAM,GAAG,GAAG;EACnC,MAAM,MAAM,UAAU,MAAM,MAAM,CAAC;EACnC,MAAM,SAAS,mBAAmB,MAAM,cAAc,OAAO,GAAG,GAAG;EACnE,OAAO,KAAK;GAAE,MAAM;GAAW,IAAI;EAAO,CAAC;EAC3C,OAAO,eAAe;CACxB;CACA,WAAW,sBAAsB,iBAAiB;CAClD,WAAW,gBAAgB,KAAK;CAEhC,OAAO;EAAE;EAAQ;CAAO;AAC1B"}
|
|
@@ -43,6 +43,7 @@ declare const BuildEntryReportSchema: z.ZodObject<{
|
|
|
43
43
|
entry: z.ZodString;
|
|
44
44
|
browserManifest: z.ZodOptional<z.ZodString>;
|
|
45
45
|
css: z.ZodOptional<z.ZodString>;
|
|
46
|
+
shared: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
46
47
|
}, z.core.$strip>;
|
|
47
48
|
type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;
|
|
48
49
|
interface ArtifactCopy {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-names.d.cts","names":[],"sources":["../../src/build/artifact-names.ts"],"mappings":";;;;;;cAOa;cACA;;cAEA;cACA;cACA;cACA;;;;;;;;;;iBAWG;iBAIA,gBAAgB;EAAS;EAAkB;;;;;;;;;iBAc3C,sBAAsB;iBAKtB,eAAe;;iBAOf,cAAc,kBAAkB;iBAIhC,mBAAmB,cAAc,cAAc;;iBAK/C,gBAAgB,sBAAsB;cAUzC,wBAAsB,EAAA
|
|
1
|
+
{"version":3,"file":"artifact-names.d.cts","names":[],"sources":["../../src/build/artifact-names.ts"],"mappings":";;;;;;cAOa;cACA;;cAEA;cACA;cACA;cACA;;;;;;;;;;iBAWG;iBAIA,gBAAgB;EAAS;EAAkB;;;;;;;;;iBAc3C,sBAAsB;iBAKtB,eAAe;;iBAOf,cAAc,kBAAkB;iBAIhC,mBAAmB,cAAc,cAAc;;iBAK/C,gBAAgB,sBAAsB;cAUzC,wBAAsB,EAAA;;;;;GAYjC,EAAA,KAAA;KACU,mBAAmB,EAAE,aAAa;UAE7B;EACf;EACA;;;;;;;;iBASc,mBAAmB;EACjC;EACA;EACA,WAAW,wBAAwB;;EAC/B,QAAQ,MAAM;EAAe,QAAQ"}
|
|
@@ -43,6 +43,7 @@ declare const BuildEntryReportSchema: z.ZodObject<{
|
|
|
43
43
|
entry: z.ZodString;
|
|
44
44
|
browserManifest: z.ZodOptional<z.ZodString>;
|
|
45
45
|
css: z.ZodOptional<z.ZodString>;
|
|
46
|
+
shared: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
46
47
|
}, z.core.$strip>;
|
|
47
48
|
type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;
|
|
48
49
|
interface ArtifactCopy {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-names.d.mts","names":[],"sources":["../../src/build/artifact-names.ts"],"mappings":";;;;;;cAOa;cACA;;cAEA;cACA;cACA;cACA;;;;;;;;;;iBAWG;iBAIA,gBAAgB;EAAS;EAAkB;;;;;;;;;iBAc3C,sBAAsB;iBAKtB,eAAe;;iBAOf,cAAc,kBAAkB;iBAIhC,mBAAmB,cAAc,cAAc;;iBAK/C,gBAAgB,sBAAsB;cAUzC,wBAAsB,EAAA
|
|
1
|
+
{"version":3,"file":"artifact-names.d.mts","names":[],"sources":["../../src/build/artifact-names.ts"],"mappings":";;;;;;cAOa;cACA;;cAEA;cACA;cACA;cACA;;;;;;;;;;iBAWG;iBAIA,gBAAgB;EAAS;EAAkB;;;;;;;;;iBAc3C,sBAAsB;iBAKtB,eAAe;;iBAOf,cAAc,kBAAkB;iBAIhC,mBAAmB,cAAc,cAAc;;iBAK/C,gBAAgB,sBAAsB;cAUzC,wBAAsB,EAAA;;;;;GAYjC,EAAA,KAAA;KACU,mBAAmB,EAAE,aAAa;UAE7B;EACf;EACA;;;;;;;;iBASc,mBAAmB;EACjC;EACA;EACA,WAAW,wBAAwB;;EAC/B,QAAQ,MAAM;EAAe,QAAQ"}
|
|
@@ -63,7 +63,15 @@ function findHashedEntry(assetNames, base) {
|
|
|
63
63
|
const BuildEntryReportSchema = z.object({
|
|
64
64
|
entry: z.string().min(1),
|
|
65
65
|
browserManifest: z.string().min(1).optional(),
|
|
66
|
-
css: z.string().min(1).optional()
|
|
66
|
+
css: z.string().min(1).optional(),
|
|
67
|
+
/**
|
|
68
|
+
* Shared-singleton `name → version` this build embedded — resolved from the
|
|
69
|
+
* building workspace's node_modules at config time and stamped by both the
|
|
70
|
+
* rspack and rsbuild build paths. The deploy leg records it into the
|
|
71
|
+
* workspace's version manifest, so a slot's pin carries shared-dep
|
|
72
|
+
* semantics and consumers can detect a build skew before loading (#546).
|
|
73
|
+
*/
|
|
74
|
+
shared: z.record(z.string(), z.string()).optional()
|
|
67
75
|
});
|
|
68
76
|
/**
|
|
69
77
|
* The additive-copy plan for one compiled dist root: each supplied fixed-name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-names.mjs","names":[],"sources":["../../src/build/artifact-names.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport { z } from \"zod\";\n\n/** Dev-mode entry names: the in-memory dev server serves these exact names,\n * and dev consumers (the host's html shell, the compose client payload, the\n * readiness probe) append them — dev has no build report to discover hashed\n * names from. */\nexport const DEV_ENTRY_FILENAME = \"remoteEntry.js\";\nexport const DEV_SERVER_ENTRY_FILENAME = \"remoteEntry.server.js\";\n/** Fixed-name build outputs that get additive immutable hashed copies. */\nexport const MF_MANIFEST_FILENAME = \"mf-manifest.json\";\nexport const STYLE_FILENAME = \"style.css\";\nexport const HASHED_ENTRY_PATTERN = \"remoteEntry.[contenthash].js\";\nexport const HASHED_SERVER_ENTRY_PATTERN = \"remoteEntry.server.[contenthash].js\";\n\n/** The dev-stack signal: everything a dev server spawns (watch or no-watch\n * one-shot builds) serves the fixed dev entry names, because every dev\n * consumer appends them and no build report exists to discover hashed names\n * from. Every other invocation — local builds, host-test production builds,\n * regression container builds, deploys — is a build and emits content-hashed\n * entries + build reports. NODE_ENV cannot carry this decision (vitest runs\n * as \"test\", bundler CLIs default \"production\" even for dev watches), and\n * DEPLOY=true only marks deploy builds — so the dev server stamps its own\n * children. */\nexport function isBuildInvocation(): boolean {\n return process.env.BOS_DEV_SERVER !== \"1\";\n}\n\nexport function uiEntryFilename(input: { isBuild: boolean; server?: boolean }): string {\n if (!input.isBuild) return input.server ? DEV_SERVER_ENTRY_FILENAME : DEV_ENTRY_FILENAME;\n return input.server ? HASHED_SERVER_ENTRY_PATTERN : HASHED_ENTRY_PATTERN;\n}\n\nconst HASH_SEGMENT_PATTERN = /\\.[a-f0-9]{8,}\\./;\n\n/**\n * Serving classification for bundle objects: anything whose name carries a\n * content-hash segment (including hashed entrypoints like\n * `remoteEntry.8f3a….js`) is immutable by construction; fixed-name\n * entrypoints and non-hashed files a redeploy replaces in place must\n * revalidate.\n */\nexport function isImmutableBundlePath(name: string): boolean {\n const base = name.split(\"/\").pop() ?? name;\n return HASH_SEGMENT_PATTERN.test(base);\n}\n\nexport function cacheControlOf(name: string): string {\n return isImmutableBundlePath(name)\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n}\n\n/** Short content hash used to mint additive hashed copies of fixed-name artifacts. */\nexport function contentHashOf(content: string | Uint8Array): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nexport function hashedArtifactName(base: string, hash: string, ext: string): string {\n return `${base}.${hash}.${ext.replace(/^\\./, \"\")}`;\n}\n\n/** Find the content-hashed entry asset for a base name (\"remoteEntry\", \"remoteEntry.server\"). */\nexport function findHashedEntry(assetNames: string[], base: string): string | null {\n const hashed = assetNames.find((name) => {\n const rest = name.startsWith(`${base}.`) ? name.slice(base.length + 1) : null;\n if (rest === null) return false;\n const hash = rest.slice(0, -3);\n return name.endsWith(\".js\") && /^[a-f0-9]{8,}$/.test(hash);\n });\n return hashed ?? null;\n}\n\nexport const BuildEntryReportSchema = z.object({\n entry: z.string().min(1),\n browserManifest: z.string().min(1).optional(),\n css: z.string().min(1).optional(),\n});\nexport type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;\n\nexport interface ArtifactCopy {\n from: string;\n to: string;\n}\n\n/**\n * The additive-copy plan for one compiled dist root: each supplied fixed-name\n * artifact gets an immutable hashed copy. The hashed entry itself needs no\n * copy — the build report carries its name to the deploy leg. Returns null\n * when the build predates hashed entry names (only fixed names emitted).\n */\nexport function planArtifactCopies(input: {\n assetNames: string[];\n entryBase: string;\n contents?: Record<string, string | Uint8Array | undefined>;\n}): { copies: Array<ArtifactCopy>; report: BuildEntryReport } | null {\n const entry = findHashedEntry(input.assetNames, input.entryBase);\n if (!entry) return null;\n\n const copies: Array<ArtifactCopy> = [];\n const report: BuildEntryReport = { entry };\n\n const hashedCopy = (fixedName: string, reportField: \"browserManifest\" | \"css\") => {\n const content = input.contents?.[fixedName];\n if (content === undefined) return;\n const dot = fixedName.lastIndexOf(\".\");\n const base = fixedName.slice(0, dot);\n const ext = fixedName.slice(dot + 1);\n const hashed = hashedArtifactName(base, contentHashOf(content), ext);\n copies.push({ from: fixedName, to: hashed });\n report[reportField] = hashed;\n };\n hashedCopy(MF_MANIFEST_FILENAME, \"browserManifest\");\n hashedCopy(STYLE_FILENAME, \"css\");\n\n return { copies, report };\n}\n"],"mappings":";;;;;;;;AAOA,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;;AAEzC,MAAa,uBAAuB;AACpC,MAAa,iBAAiB;AAC9B,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;;;;;;;;;;AAW3C,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,SAAgB,gBAAgB,OAAuD;CACrF,IAAI,CAAC,MAAM,SAAS,OAAO,MAAM,SAAS,4BAA4B;CACtE,OAAO,MAAM,SAAS,8BAA8B;AACtD;AAEA,MAAM,uBAAuB;;;;;;;;AAS7B,SAAgB,sBAAsB,MAAuB;CAC3D,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;CACtC,OAAO,qBAAqB,KAAK,IAAI;AACvC;AAEA,SAAgB,eAAe,MAAsB;CACnD,OAAO,sBAAsB,IAAI,IAC7B,wCACA;AACN;;AAGA,SAAgB,cAAc,SAAsC;CAClE,OAAO,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAgB,mBAAmB,MAAc,MAAc,KAAqB;CAClF,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,QAAQ,OAAO,EAAE;AACjD;;AAGA,SAAgB,gBAAgB,YAAsB,MAA6B;CAOjF,OANe,WAAW,MAAM,SAAS;EACvC,MAAM,OAAO,KAAK,WAAW,GAAG,KAAK,EAAE,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI;EACzE,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;EAC7B,OAAO,KAAK,SAAS,KAAK,KAAK,iBAAiB,KAAK,IAAI;CAC3D,CACY,KAAK;AACnB;AAEA,MAAa,yBAAyB,EAAE,OAAO;CAC7C,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,iBAAiB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC5C,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"artifact-names.mjs","names":[],"sources":["../../src/build/artifact-names.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport { z } from \"zod\";\n\n/** Dev-mode entry names: the in-memory dev server serves these exact names,\n * and dev consumers (the host's html shell, the compose client payload, the\n * readiness probe) append them — dev has no build report to discover hashed\n * names from. */\nexport const DEV_ENTRY_FILENAME = \"remoteEntry.js\";\nexport const DEV_SERVER_ENTRY_FILENAME = \"remoteEntry.server.js\";\n/** Fixed-name build outputs that get additive immutable hashed copies. */\nexport const MF_MANIFEST_FILENAME = \"mf-manifest.json\";\nexport const STYLE_FILENAME = \"style.css\";\nexport const HASHED_ENTRY_PATTERN = \"remoteEntry.[contenthash].js\";\nexport const HASHED_SERVER_ENTRY_PATTERN = \"remoteEntry.server.[contenthash].js\";\n\n/** The dev-stack signal: everything a dev server spawns (watch or no-watch\n * one-shot builds) serves the fixed dev entry names, because every dev\n * consumer appends them and no build report exists to discover hashed names\n * from. Every other invocation — local builds, host-test production builds,\n * regression container builds, deploys — is a build and emits content-hashed\n * entries + build reports. NODE_ENV cannot carry this decision (vitest runs\n * as \"test\", bundler CLIs default \"production\" even for dev watches), and\n * DEPLOY=true only marks deploy builds — so the dev server stamps its own\n * children. */\nexport function isBuildInvocation(): boolean {\n return process.env.BOS_DEV_SERVER !== \"1\";\n}\n\nexport function uiEntryFilename(input: { isBuild: boolean; server?: boolean }): string {\n if (!input.isBuild) return input.server ? DEV_SERVER_ENTRY_FILENAME : DEV_ENTRY_FILENAME;\n return input.server ? HASHED_SERVER_ENTRY_PATTERN : HASHED_ENTRY_PATTERN;\n}\n\nconst HASH_SEGMENT_PATTERN = /\\.[a-f0-9]{8,}\\./;\n\n/**\n * Serving classification for bundle objects: anything whose name carries a\n * content-hash segment (including hashed entrypoints like\n * `remoteEntry.8f3a….js`) is immutable by construction; fixed-name\n * entrypoints and non-hashed files a redeploy replaces in place must\n * revalidate.\n */\nexport function isImmutableBundlePath(name: string): boolean {\n const base = name.split(\"/\").pop() ?? name;\n return HASH_SEGMENT_PATTERN.test(base);\n}\n\nexport function cacheControlOf(name: string): string {\n return isImmutableBundlePath(name)\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=0, must-revalidate\";\n}\n\n/** Short content hash used to mint additive hashed copies of fixed-name artifacts. */\nexport function contentHashOf(content: string | Uint8Array): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nexport function hashedArtifactName(base: string, hash: string, ext: string): string {\n return `${base}.${hash}.${ext.replace(/^\\./, \"\")}`;\n}\n\n/** Find the content-hashed entry asset for a base name (\"remoteEntry\", \"remoteEntry.server\"). */\nexport function findHashedEntry(assetNames: string[], base: string): string | null {\n const hashed = assetNames.find((name) => {\n const rest = name.startsWith(`${base}.`) ? name.slice(base.length + 1) : null;\n if (rest === null) return false;\n const hash = rest.slice(0, -3);\n return name.endsWith(\".js\") && /^[a-f0-9]{8,}$/.test(hash);\n });\n return hashed ?? null;\n}\n\nexport const BuildEntryReportSchema = z.object({\n entry: z.string().min(1),\n browserManifest: z.string().min(1).optional(),\n css: z.string().min(1).optional(),\n /**\n * Shared-singleton `name → version` this build embedded — resolved from the\n * building workspace's node_modules at config time and stamped by both the\n * rspack and rsbuild build paths. The deploy leg records it into the\n * workspace's version manifest, so a slot's pin carries shared-dep\n * semantics and consumers can detect a build skew before loading (#546).\n */\n shared: z.record(z.string(), z.string()).optional(),\n});\nexport type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;\n\nexport interface ArtifactCopy {\n from: string;\n to: string;\n}\n\n/**\n * The additive-copy plan for one compiled dist root: each supplied fixed-name\n * artifact gets an immutable hashed copy. The hashed entry itself needs no\n * copy — the build report carries its name to the deploy leg. Returns null\n * when the build predates hashed entry names (only fixed names emitted).\n */\nexport function planArtifactCopies(input: {\n assetNames: string[];\n entryBase: string;\n contents?: Record<string, string | Uint8Array | undefined>;\n}): { copies: Array<ArtifactCopy>; report: BuildEntryReport } | null {\n const entry = findHashedEntry(input.assetNames, input.entryBase);\n if (!entry) return null;\n\n const copies: Array<ArtifactCopy> = [];\n const report: BuildEntryReport = { entry };\n\n const hashedCopy = (fixedName: string, reportField: \"browserManifest\" | \"css\") => {\n const content = input.contents?.[fixedName];\n if (content === undefined) return;\n const dot = fixedName.lastIndexOf(\".\");\n const base = fixedName.slice(0, dot);\n const ext = fixedName.slice(dot + 1);\n const hashed = hashedArtifactName(base, contentHashOf(content), ext);\n copies.push({ from: fixedName, to: hashed });\n report[reportField] = hashed;\n };\n hashedCopy(MF_MANIFEST_FILENAME, \"browserManifest\");\n hashedCopy(STYLE_FILENAME, \"css\");\n\n return { copies, report };\n}\n"],"mappings":";;;;;;;;AAOA,MAAa,qBAAqB;AAClC,MAAa,4BAA4B;;AAEzC,MAAa,uBAAuB;AACpC,MAAa,iBAAiB;AAC9B,MAAa,uBAAuB;AACpC,MAAa,8BAA8B;;;;;;;;;;AAW3C,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,mBAAmB;AACxC;AAEA,SAAgB,gBAAgB,OAAuD;CACrF,IAAI,CAAC,MAAM,SAAS,OAAO,MAAM,SAAS,4BAA4B;CACtE,OAAO,MAAM,SAAS,8BAA8B;AACtD;AAEA,MAAM,uBAAuB;;;;;;;;AAS7B,SAAgB,sBAAsB,MAAuB;CAC3D,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;CACtC,OAAO,qBAAqB,KAAK,IAAI;AACvC;AAEA,SAAgB,eAAe,MAAsB;CACnD,OAAO,sBAAsB,IAAI,IAC7B,wCACA;AACN;;AAGA,SAAgB,cAAc,SAAsC;CAClE,OAAO,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAgB,mBAAmB,MAAc,MAAc,KAAqB;CAClF,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,QAAQ,OAAO,EAAE;AACjD;;AAGA,SAAgB,gBAAgB,YAAsB,MAA6B;CAOjF,OANe,WAAW,MAAM,SAAS;EACvC,MAAM,OAAO,KAAK,WAAW,GAAG,KAAK,EAAE,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI;EACzE,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;EAC7B,OAAO,KAAK,SAAS,KAAK,KAAK,iBAAiB,KAAK,IAAI;CAC3D,CACY,KAAK;AACnB;AAEA,MAAa,yBAAyB,EAAE,OAAO;CAC7C,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,iBAAiB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC5C,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;;;;;;;;CAQhC,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;AACpD,CAAC;;;;;;;AAcD,SAAgB,mBAAmB,OAIkC;CACnE,MAAM,QAAQ,gBAAgB,MAAM,YAAY,MAAM,SAAS;CAC/D,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,SAA8B,CAAC;CACrC,MAAM,SAA2B,EAAE,MAAM;CAEzC,MAAM,cAAc,WAAmB,gBAA2C;EAChF,MAAM,UAAU,MAAM,WAAW;EACjC,IAAI,YAAY,QAAW;EAC3B,MAAM,MAAM,UAAU,YAAY,GAAG;EACrC,MAAM,OAAO,UAAU,MAAM,GAAG,GAAG;EACnC,MAAM,MAAM,UAAU,MAAM,MAAM,CAAC;EACnC,MAAM,SAAS,mBAAmB,MAAM,cAAc,OAAO,GAAG,GAAG;EACnE,OAAO,KAAK;GAAE,MAAM;GAAW,IAAI;EAAO,CAAC;EAC3C,OAAO,eAAe;CACxB;CACA,WAAW,sBAAsB,iBAAiB;CAClD,WAAW,gBAAgB,KAAK;CAEhC,OAAO;EAAE;EAAQ;CAAO;AAC1B"}
|
|
@@ -10,9 +10,11 @@ const require_build_artifact_names = require('../artifact-names.cjs');
|
|
|
10
10
|
*/
|
|
11
11
|
var BuildReportPlugin = class {
|
|
12
12
|
entryBase;
|
|
13
|
+
options;
|
|
13
14
|
name = "BuildReportPlugin";
|
|
14
|
-
constructor(entryBase = "remoteEntry") {
|
|
15
|
+
constructor(entryBase = "remoteEntry", options = {}) {
|
|
15
16
|
this.entryBase = entryBase;
|
|
17
|
+
this.options = options;
|
|
16
18
|
}
|
|
17
19
|
apply(compiler) {
|
|
18
20
|
compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
|
|
@@ -23,9 +25,13 @@ var BuildReportPlugin = class {
|
|
|
23
25
|
name: this.name,
|
|
24
26
|
stage
|
|
25
27
|
}, (assets) => {
|
|
26
|
-
const
|
|
28
|
+
const entryBase = this.options.entryBase ?? this.entryBase;
|
|
29
|
+
const entry = require_build_artifact_names.findHashedEntry(Object.keys(assets), entryBase);
|
|
27
30
|
if (!entry || !rawSource) return;
|
|
28
|
-
const report = require_build_artifact_names.BuildEntryReportSchema.parse({
|
|
31
|
+
const report = require_build_artifact_names.BuildEntryReportSchema.parse({
|
|
32
|
+
entry,
|
|
33
|
+
...this.options.shared ? { shared: this.options.shared } : {}
|
|
34
|
+
});
|
|
29
35
|
compilation.emitAsset("build-report.json", new rawSource(`${JSON.stringify(report, null, 2)}\n`));
|
|
30
36
|
});
|
|
31
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report-plugin.cjs","names":["findHashedEntry","BuildEntryReportSchema"],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"sourcesContent":["import type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { BuildEntryReportSchema, findHashedEntry } from \"../artifact-names\";\n\n/**\n * Emits `build-report.json` for the deploy leg: the container entry is hashed\n * by the build's filename template (`remoteEntry.[contenthash].js`), and the\n * report names it — in-compilation, so watch-mode cleans can't open a window\n * where the report 404s (mirrors the `output.clean` rationale on the build\n * plugin).\n */\nexport class BuildReportPlugin implements RspackPluginInstance {\n name = \"BuildReportPlugin\";\n\n constructor(private entryBase = \"remoteEntry\") {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {\n const entry = findHashedEntry(Object.keys(assets),
|
|
1
|
+
{"version":3,"file":"build-report-plugin.cjs","names":["findHashedEntry","BuildEntryReportSchema"],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"sourcesContent":["import type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { BuildEntryReportSchema, findHashedEntry } from \"../artifact-names\";\n\nexport interface BuildReportOptions {\n entryBase?: string;\n /**\n * The shared-singleton `name → version` map this build embedded — recorded\n * in build-report.json so the deploy leg's version manifest carries\n * shared-dep semantics (#546).\n */\n shared?: Record<string, string>;\n}\n\n/**\n * Emits `build-report.json` for the deploy leg: the container entry is hashed\n * by the build's filename template (`remoteEntry.[contenthash].js`), and the\n * report names it — in-compilation, so watch-mode cleans can't open a window\n * where the report 404s (mirrors the `output.clean` rationale on the build\n * plugin).\n */\nexport class BuildReportPlugin implements RspackPluginInstance {\n name = \"BuildReportPlugin\";\n\n constructor(\n private entryBase = \"remoteEntry\",\n private options: BuildReportOptions = {},\n ) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {\n const entryBase = this.options.entryBase ?? this.entryBase;\n const entry = findHashedEntry(Object.keys(assets), entryBase);\n if (!entry || !rawSource) return;\n\n const report = BuildEntryReportSchema.parse({\n entry,\n ...(this.options.shared ? { shared: this.options.shared } : {}),\n });\n compilation.emitAsset(\n \"build-report.json\",\n new rawSource(`${JSON.stringify(report, null, 2)}\\n`),\n );\n });\n });\n }\n}\n"],"mappings":";;;;;;;;;;AAoBA,IAAa,oBAAb,MAA+D;CAInD;CACA;CAJV,OAAO;CAEP,YACE,AAAQ,YAAY,eACpB,AAAQ,UAA8B,CAAC,GACvC;EAFQ;EACA;CACP;CAEH,MAAM,UAAoB;EACxB,SAAS,MAAM,gBAAgB,IAAI,KAAK,OAAO,gBAAgB;GAC7D,MAAM,UAAW,SAA0C;GAC3D,MAAM,YAAY,SAAS,SAAS;GACpC,MAAM,QAAQ,SAAS,aAAa,kCAAkC;GAEtE,YAAY,MAAM,cAAc,IAAI;IAAE,MAAM,KAAK;IAAM;GAAM,IAAI,WAAW;IAC1E,MAAM,YAAY,KAAK,QAAQ,aAAa,KAAK;IACjD,MAAM,QAAQA,6CAAgB,OAAO,KAAK,MAAM,GAAG,SAAS;IAC5D,IAAI,CAAC,SAAS,CAAC,WAAW;IAE1B,MAAM,SAASC,oDAAuB,MAAM;KAC1C;KACA,GAAI,KAAK,QAAQ,SAAS,EAAE,QAAQ,KAAK,QAAQ,OAAO,IAAI,CAAC;IAC/D,CAAC;IACD,YAAY,UACV,qBACA,IAAI,UAAU,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,GAAG,CACtD;GACF,CAAC;EACH,CAAC;CACH;AACF"}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
2
2
|
//#region src/build/rspack/build-report-plugin.d.ts
|
|
3
|
+
interface BuildReportOptions {
|
|
4
|
+
entryBase?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The shared-singleton `name → version` map this build embedded — recorded
|
|
7
|
+
* in build-report.json so the deploy leg's version manifest carries
|
|
8
|
+
* shared-dep semantics (#546).
|
|
9
|
+
*/
|
|
10
|
+
shared?: Record<string, string>;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* Emits `build-report.json` for the deploy leg: the container entry is hashed
|
|
5
14
|
* by the build's filename template (`remoteEntry.[contenthash].js`), and the
|
|
@@ -9,10 +18,11 @@ import { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
|
9
18
|
*/
|
|
10
19
|
declare class BuildReportPlugin implements RspackPluginInstance {
|
|
11
20
|
private entryBase;
|
|
21
|
+
private options;
|
|
12
22
|
name: string;
|
|
13
|
-
constructor(entryBase?: string);
|
|
23
|
+
constructor(entryBase?: string, options?: BuildReportOptions);
|
|
14
24
|
apply(compiler: Compiler): void;
|
|
15
25
|
}
|
|
16
26
|
//#endregion
|
|
17
|
-
export { BuildReportPlugin };
|
|
27
|
+
export { BuildReportOptions, BuildReportPlugin };
|
|
18
28
|
//# sourceMappingURL=build-report-plugin.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report-plugin.d.cts","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"build-report-plugin.d.cts","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"mappings":";;UAGiB;EACf;;;;;;EAMA,SAAS;;;;;;;;;cAUE,6BAA6B;UAI9B;UACA;EAJV;EAEA,YACU,oBACA,UAAS;EAGnB,MAAM,UAAU"}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
2
2
|
//#region src/build/rspack/build-report-plugin.d.ts
|
|
3
|
+
interface BuildReportOptions {
|
|
4
|
+
entryBase?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The shared-singleton `name → version` map this build embedded — recorded
|
|
7
|
+
* in build-report.json so the deploy leg's version manifest carries
|
|
8
|
+
* shared-dep semantics (#546).
|
|
9
|
+
*/
|
|
10
|
+
shared?: Record<string, string>;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* Emits `build-report.json` for the deploy leg: the container entry is hashed
|
|
5
14
|
* by the build's filename template (`remoteEntry.[contenthash].js`), and the
|
|
@@ -9,10 +18,11 @@ import { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
|
9
18
|
*/
|
|
10
19
|
declare class BuildReportPlugin implements RspackPluginInstance {
|
|
11
20
|
private entryBase;
|
|
21
|
+
private options;
|
|
12
22
|
name: string;
|
|
13
|
-
constructor(entryBase?: string);
|
|
23
|
+
constructor(entryBase?: string, options?: BuildReportOptions);
|
|
14
24
|
apply(compiler: Compiler): void;
|
|
15
25
|
}
|
|
16
26
|
//#endregion
|
|
17
|
-
export { BuildReportPlugin };
|
|
27
|
+
export { BuildReportOptions, BuildReportPlugin };
|
|
18
28
|
//# sourceMappingURL=build-report-plugin.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report-plugin.d.mts","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"build-report-plugin.d.mts","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"mappings":";;UAGiB;EACf;;;;;;EAMA,SAAS;;;;;;;;;cAUE,6BAA6B;UAI9B;UACA;EAJV;EAEA,YACU,oBACA,UAAS;EAGnB,MAAM,UAAU"}
|
|
@@ -10,9 +10,11 @@ import { BuildEntryReportSchema, findHashedEntry } from "../artifact-names.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
var BuildReportPlugin = class {
|
|
12
12
|
entryBase;
|
|
13
|
+
options;
|
|
13
14
|
name = "BuildReportPlugin";
|
|
14
|
-
constructor(entryBase = "remoteEntry") {
|
|
15
|
+
constructor(entryBase = "remoteEntry", options = {}) {
|
|
15
16
|
this.entryBase = entryBase;
|
|
17
|
+
this.options = options;
|
|
16
18
|
}
|
|
17
19
|
apply(compiler) {
|
|
18
20
|
compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
|
|
@@ -23,9 +25,13 @@ var BuildReportPlugin = class {
|
|
|
23
25
|
name: this.name,
|
|
24
26
|
stage
|
|
25
27
|
}, (assets) => {
|
|
26
|
-
const
|
|
28
|
+
const entryBase = this.options.entryBase ?? this.entryBase;
|
|
29
|
+
const entry = findHashedEntry(Object.keys(assets), entryBase);
|
|
27
30
|
if (!entry || !rawSource) return;
|
|
28
|
-
const report = BuildEntryReportSchema.parse({
|
|
31
|
+
const report = BuildEntryReportSchema.parse({
|
|
32
|
+
entry,
|
|
33
|
+
...this.options.shared ? { shared: this.options.shared } : {}
|
|
34
|
+
});
|
|
29
35
|
compilation.emitAsset("build-report.json", new rawSource(`${JSON.stringify(report, null, 2)}\n`));
|
|
30
36
|
});
|
|
31
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report-plugin.mjs","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"sourcesContent":["import type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { BuildEntryReportSchema, findHashedEntry } from \"../artifact-names\";\n\n/**\n * Emits `build-report.json` for the deploy leg: the container entry is hashed\n * by the build's filename template (`remoteEntry.[contenthash].js`), and the\n * report names it — in-compilation, so watch-mode cleans can't open a window\n * where the report 404s (mirrors the `output.clean` rationale on the build\n * plugin).\n */\nexport class BuildReportPlugin implements RspackPluginInstance {\n name = \"BuildReportPlugin\";\n\n constructor(private entryBase = \"remoteEntry\") {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {\n const entry = findHashedEntry(Object.keys(assets),
|
|
1
|
+
{"version":3,"file":"build-report-plugin.mjs","names":[],"sources":["../../../src/build/rspack/build-report-plugin.ts"],"sourcesContent":["import type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { BuildEntryReportSchema, findHashedEntry } from \"../artifact-names\";\n\nexport interface BuildReportOptions {\n entryBase?: string;\n /**\n * The shared-singleton `name → version` map this build embedded — recorded\n * in build-report.json so the deploy leg's version manifest carries\n * shared-dep semantics (#546).\n */\n shared?: Record<string, string>;\n}\n\n/**\n * Emits `build-report.json` for the deploy leg: the container entry is hashed\n * by the build's filename template (`remoteEntry.[contenthash].js`), and the\n * report names it — in-compilation, so watch-mode cleans can't open a window\n * where the report 404s (mirrors the `output.clean` rationale on the build\n * plugin).\n */\nexport class BuildReportPlugin implements RspackPluginInstance {\n name = \"BuildReportPlugin\";\n\n constructor(\n private entryBase = \"remoteEntry\",\n private options: BuildReportOptions = {},\n ) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {\n const entryBase = this.options.entryBase ?? this.entryBase;\n const entry = findHashedEntry(Object.keys(assets), entryBase);\n if (!entry || !rawSource) return;\n\n const report = BuildEntryReportSchema.parse({\n entry,\n ...(this.options.shared ? { shared: this.options.shared } : {}),\n });\n compilation.emitAsset(\n \"build-report.json\",\n new rawSource(`${JSON.stringify(report, null, 2)}\\n`),\n );\n });\n });\n }\n}\n"],"mappings":";;;;;;;;;;AAoBA,IAAa,oBAAb,MAA+D;CAInD;CACA;CAJV,OAAO;CAEP,YACE,AAAQ,YAAY,eACpB,AAAQ,UAA8B,CAAC,GACvC;EAFQ;EACA;CACP;CAEH,MAAM,UAAoB;EACxB,SAAS,MAAM,gBAAgB,IAAI,KAAK,OAAO,gBAAgB;GAC7D,MAAM,UAAW,SAA0C;GAC3D,MAAM,YAAY,SAAS,SAAS;GACpC,MAAM,QAAQ,SAAS,aAAa,kCAAkC;GAEtE,YAAY,MAAM,cAAc,IAAI;IAAE,MAAM,KAAK;IAAM;GAAM,IAAI,WAAW;IAC1E,MAAM,YAAY,KAAK,QAAQ,aAAa,KAAK;IACjD,MAAM,QAAQ,gBAAgB,OAAO,KAAK,MAAM,GAAG,SAAS;IAC5D,IAAI,CAAC,SAAS,CAAC,WAAW;IAE1B,MAAM,SAAS,uBAAuB,MAAM;KAC1C;KACA,GAAI,KAAK,QAAQ,SAAS,EAAE,QAAQ,KAAK,QAAQ,OAAO,IAAI,CAAC;IAC/D,CAAC;IACD,YAAY,UACV,qBACA,IAAI,UAAU,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,GAAG,CACtD;GACF,CAAC;EACH,CAAC;CACH;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_build_artifact_names = require('../artifact-names.cjs');
|
|
3
|
+
const require_shared_deps_spec = require('../../shared-deps-spec.cjs');
|
|
3
4
|
const require_build_report_plugin = require('./build-report-plugin.cjs');
|
|
4
5
|
const require_contract_types = require('../contract-types.cjs');
|
|
5
6
|
const require_chunk_completeness_plugin = require('./chunk-completeness-plugin.cjs');
|
|
@@ -95,6 +96,7 @@ var EveryPluginBuild = class {
|
|
|
95
96
|
apply(compiler) {
|
|
96
97
|
const pluginInfo = require_utils.getPluginInfo(compiler.options.context || process.cwd());
|
|
97
98
|
this.configureDefaults(compiler, pluginInfo);
|
|
99
|
+
const sharedDependencies = require_module_federation.buildSharedDependencies(pluginInfo);
|
|
98
100
|
new _module_federation_enhanced_rspack.ModuleFederationPlugin({
|
|
99
101
|
name: pluginInfo.normalizedName,
|
|
100
102
|
filename: require_build_artifact_names.uiEntryFilename({ isBuild: require_build_artifact_names.isBuildInvocation() }),
|
|
@@ -103,10 +105,10 @@ var EveryPluginBuild = class {
|
|
|
103
105
|
runtimePlugins: [require.resolve("@module-federation/node/runtimePlugin")],
|
|
104
106
|
library: { type: "commonjs-module" },
|
|
105
107
|
exposes: { "./plugin": "./src/index.ts" },
|
|
106
|
-
shared:
|
|
108
|
+
shared: sharedDependencies,
|
|
107
109
|
shareStrategy: "version-first"
|
|
108
110
|
}).apply(compiler);
|
|
109
|
-
new require_build_report_plugin.BuildReportPlugin().apply(compiler);
|
|
111
|
+
new require_build_report_plugin.BuildReportPlugin("remoteEntry", { shared: require_shared_deps_spec.sharedVersionRecord(sharedDependencies) }).apply(compiler);
|
|
110
112
|
new require_chunk_completeness_plugin.ChunkCompletenessPlugin().apply(compiler);
|
|
111
113
|
if (this.options.dts === false) compiler.options.plugins = (compiler.options.plugins ?? []).filter((p) => !p || typeof p !== "object" || p.name !== "MFDevPlugin" && p.name !== "ModuleFederationDtsPlugin");
|
|
112
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs","names":["getPluginInfo","generateContractTypes","path","CONTRACT_TYPES_FILE","fs","crypto","findHashedEntry","ModuleFederationPlugin","uiEntryFilename","isBuildInvocation","buildSharedDependencies","BuildReportPlugin","ChunkCompletenessPlugin"],"sources":["../../../src/build/rspack/plugin.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ModuleFederationPlugin } from \"@module-federation/enhanced/rspack\";\nimport type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport {\n DEV_ENTRY_FILENAME,\n findHashedEntry,\n isBuildInvocation,\n uiEntryFilename,\n} from \"../artifact-names\";\nimport { CONTRACT_TYPES_FILE, generateContractTypes } from \"../contract-types\";\nimport { BuildReportPlugin } from \"./build-report-plugin\";\nimport { ChunkCompletenessPlugin } from \"./chunk-completeness-plugin\";\nimport { buildSharedDependencies } from \"./module-federation\";\nimport { getPluginInfo } from \"./utils\";\n\nexport interface EveryPluginBuildOptions {\n dts?: boolean;\n}\n\nexport interface PluginManifestEmitterOptions {\n manifestFileName?: string;\n contractFileName?: string;\n}\n\nexport class EmitPluginManifest implements RspackPluginInstance {\n name = \"EmitPluginManifest\";\n\n constructor(private options: PluginManifestEmitterOptions = {}) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tapPromise({ name: this.name, stage }, async () => {\n const context = compiler.options.context || process.cwd();\n const pluginInfo = getPluginInfo(context);\n const contractFileName = this.options.contractFileName ?? \"contract.d.ts\";\n const manifestFileName = this.options.manifestFileName ?? \"plugin.manifest.json\";\n\n let generationError: string | null = null;\n try {\n const status = await generateContractTypes(context);\n if (status === \"generated\") {\n console.log(`[EmitPluginManifest] Contract types regenerated (${context}).`);\n }\n } catch (error) {\n generationError = error instanceof Error ? error.message : String(error);\n }\n\n if (generationError) {\n console.warn(`[EmitPluginManifest] Skipping manifest generation — ${generationError}`);\n return;\n }\n\n const sourceContractPath = path.join(context, CONTRACT_TYPES_FILE);\n\n const tryReadFile = async (filePath: string): Promise<string | null> => {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n const stats = fs.statSync(filePath);\n if (!stats.isFile()) {\n return null;\n }\n try {\n return await fs.promises.readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n };\n\n const contractTypes = (await tryReadFile(sourceContractPath)) ?? \"\";\n\n if (!contractTypes) {\n console.warn(\n `[EmitPluginManifest] No contract types at ${sourceContractPath} ` +\n `(no src/contract.ts in this workspace?). Skipping manifest generation.`,\n );\n return;\n }\n\n const contractSha256 = crypto.createHash(\"sha256\").update(contractTypes).digest(\"hex\");\n const hashedEntry = findHashedEntry(Object.keys(compilation.assets), \"remoteEntry\");\n const manifest: Record<string, unknown> = {\n schemaVersion: 1,\n kind: \"every-plugin/manifest\",\n plugin: {\n name: pluginInfo.name,\n version: pluginInfo.version,\n },\n runtime: {\n remoteEntry: `./${hashedEntry ?? DEV_ENTRY_FILENAME}`,\n },\n contract: {\n kind: \"orpc\",\n types: {\n path: `./types/${contractFileName}`,\n exportName: \"contract\",\n typeName: \"ContractType\",\n sha256: contractSha256,\n },\n },\n };\n\n if (rawSource) {\n compilation.emitAsset(\n manifestFileName,\n new rawSource(`${JSON.stringify(manifest, null, 2)}\\n`),\n );\n compilation.emitAsset(`types/${contractFileName}`, new rawSource(`${contractTypes}`));\n }\n });\n });\n }\n}\n\nexport class EveryPluginBuild implements RspackPluginInstance {\n name = \"EveryPluginBuild\";\n\n constructor(private options: EveryPluginBuildOptions = {}) {}\n\n apply(compiler: Compiler) {\n const pluginInfo = getPluginInfo(compiler.options.context || process.cwd());\n\n this.configureDefaults(compiler, pluginInfo);\n\n new ModuleFederationPlugin({\n name: pluginInfo.normalizedName,\n filename: uiEntryFilename({ isBuild: isBuildInvocation() }),\n dts: this.options.dts !== false,\n manifest: {},\n runtimePlugins: [require.resolve(\"@module-federation/node/runtimePlugin\")],\n library: { type: \"commonjs-module\" },\n exposes: {\n \"./plugin\": \"./src/index.ts\",\n },\n shared: buildSharedDependencies(pluginInfo),\n shareStrategy: \"version-first\",\n }).apply(compiler);\n\n new BuildReportPlugin().apply(compiler);\n new ChunkCompletenessPlugin().apply(compiler);\n\n if (this.options.dts === false) {\n compiler.options.plugins = (compiler.options.plugins ?? []).filter(\n (p) =>\n !p ||\n typeof p !== \"object\" ||\n ((p as any).name !== \"MFDevPlugin\" && (p as any).name !== \"ModuleFederationDtsPlugin\"),\n );\n }\n }\n\n private configureDefaults(compiler: Compiler, pluginInfo: any) {\n const context = compiler.options.context || process.cwd();\n\n if (!compiler.options.output) {\n compiler.options.output = {};\n }\n compiler.options.output.uniqueName = pluginInfo.normalizedName;\n compiler.options.output.publicPath = \"auto\";\n compiler.options.output.path = path.resolve(context, \"dist\");\n // Watch rebuilds must not wipe dist: the dev serve static handler serves\n // this directory, and a clean on every rebuild opens a window where\n // remoteEntry.js 404s for any consumer that loads mid-rebuild.\n compiler.options.output.clean = !compiler.options.watch;\n compiler.options.output.library = { type: \"commonjs-module\" };\n\n if (!compiler.options.target) {\n compiler.options.target = \"async-node\";\n }\n\n if (!compiler.options.mode) {\n compiler.options.mode = process.env.NODE_ENV === \"development\" ? \"development\" : \"production\";\n }\n\n if (compiler.options.devtool === undefined) {\n compiler.options.devtool = \"source-map\";\n }\n\n if (!compiler.options.infrastructureLogging) {\n compiler.options.infrastructureLogging = {\n level: \"warn\",\n };\n }\n\n this.ensureTypeScriptLoader(compiler);\n\n if (!compiler.options.optimization) {\n compiler.options.optimization = {};\n }\n // rspack 2.x splitChunks re-ids the final chunk graph while the federation\n // get-factory codegen keeps pre-split ids — the entry then references\n // chunks that were never emitted (ChunkCompletenessPlugin's exact failure\n // mode). Splitting off keeps the emitted bundle self-consistent.\n compiler.options.optimization.splitChunks = false;\n // The default deterministic module ids hash module identifiers that embed\n // absolute resource paths, so the same source builds to different id\n // spaces on different machines (host deploy train vs the image's\n // dist-builder stage). A mixed-generation load — CDN-pinned entry +\n // staged chunks — then splices two incompatible id spaces and fails with\n // \"__webpack_modules__[r] is not a function\". Named ids derive from\n // context-relative requests, keeping builds machine-independent and the\n // two generations semantically identical.\n compiler.options.optimization.moduleIds = \"named\";\n\n if (!compiler.options.resolve) {\n compiler.options.resolve = {};\n }\n compiler.options.resolve.extensions = [\"...\", \".tsx\", \".ts\"];\n // Source-first for local flows: resolve framework packages through the\n // `development` export condition (TS source) unless DEPLOY=true — the\n // manual dist-first switch deploy-identical builds opt into. (NODE_ENV\n // is unusable as the gate here: the rspack CLI defaults it to\n // \"production\" for every `build` invocation, including local dev watch.)\n // byDependency entries inherit the root conditions via \"...\", so\n // dropping the strip lets esm/cjs deps pick up `development` too.\n const sourceFirst = process.env.DEPLOY !== \"true\";\n compiler.options.resolve.conditionNames = [\n ...(sourceFirst ? [\"development\"] : []),\n \"webpack\",\n \"import\",\n \"module\",\n \"require\",\n \"node\",\n \"default\",\n ];\n if (sourceFirst) {\n // Source-resolved TS packages (e.g. better-near-auth) use node-style\n // `.js` specifiers for their own relative imports; map them to the\n // on-disk `.ts` sources. `.js` stays first in the expansion: the alias\n // picks the first target that exists on disk, and .ts-first made\n // packages that also ship index.ts (e.g. @scure/base) resolve their\n // TypeScript over their real JavaScript entry.\n compiler.options.resolve.extensionAlias = {\n \".js\": [\".js\", \".ts\", \".tsx\"],\n };\n }\n if (!sourceFirst && compiler.options.resolve.byDependency) {\n for (const depType of Object.keys(compiler.options.resolve.byDependency)) {\n const depConfig = (\n compiler.options.resolve.byDependency as Record<string, { conditionNames?: string[] }>\n )[depType];\n if (depConfig?.conditionNames) {\n depConfig.conditionNames = depConfig.conditionNames.filter((c) => c !== \"development\");\n }\n }\n }\n compiler.options.resolve.fallback = {\n ...compiler.options.resolve.fallback,\n bufferutil: false,\n \"utf-8-validate\": false,\n };\n }\n\n private ensureTypeScriptLoader(compiler: Compiler) {\n if (!compiler.options.module) {\n compiler.options.module = { rules: [] } as any;\n }\n\n if (!compiler.options.module.rules) {\n compiler.options.module.rules = [];\n }\n\n // Source-first resolution pulls every-plugin src into the graph, which\n // imports package.json (runtime/mf-config.ts). The MF shared chunks\n // must emit it as a json module, not parse it as JavaScript.\n const hasJsonRule = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".json\"),\n );\n\n if (!hasJsonRule) {\n compiler.options.module.rules.push({\n test: /\\.json$/,\n type: \"json\",\n } as any);\n }\n\n const hasTsLoader = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".ts\"),\n );\n\n if (!hasTsLoader) {\n compiler.options.module.rules.push({\n test: /\\.tsx?$/,\n use: \"builtin:swc-loader\",\n exclude: /node_modules/,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs","names":["getPluginInfo","generateContractTypes","path","CONTRACT_TYPES_FILE","fs","crypto","findHashedEntry","buildSharedDependencies","ModuleFederationPlugin","uiEntryFilename","isBuildInvocation","BuildReportPlugin","sharedVersionRecord","ChunkCompletenessPlugin"],"sources":["../../../src/build/rspack/plugin.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ModuleFederationPlugin } from \"@module-federation/enhanced/rspack\";\nimport type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { sharedVersionRecord } from \"../../shared-deps-spec\";\nimport {\n DEV_ENTRY_FILENAME,\n findHashedEntry,\n isBuildInvocation,\n uiEntryFilename,\n} from \"../artifact-names\";\nimport { CONTRACT_TYPES_FILE, generateContractTypes } from \"../contract-types\";\nimport { BuildReportPlugin } from \"./build-report-plugin\";\nimport { ChunkCompletenessPlugin } from \"./chunk-completeness-plugin\";\nimport { buildSharedDependencies } from \"./module-federation\";\nimport { getPluginInfo } from \"./utils\";\n\nexport interface EveryPluginBuildOptions {\n dts?: boolean;\n}\n\nexport interface PluginManifestEmitterOptions {\n manifestFileName?: string;\n contractFileName?: string;\n}\n\nexport class EmitPluginManifest implements RspackPluginInstance {\n name = \"EmitPluginManifest\";\n\n constructor(private options: PluginManifestEmitterOptions = {}) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tapPromise({ name: this.name, stage }, async () => {\n const context = compiler.options.context || process.cwd();\n const pluginInfo = getPluginInfo(context);\n const contractFileName = this.options.contractFileName ?? \"contract.d.ts\";\n const manifestFileName = this.options.manifestFileName ?? \"plugin.manifest.json\";\n\n let generationError: string | null = null;\n try {\n const status = await generateContractTypes(context);\n if (status === \"generated\") {\n console.log(`[EmitPluginManifest] Contract types regenerated (${context}).`);\n }\n } catch (error) {\n generationError = error instanceof Error ? error.message : String(error);\n }\n\n if (generationError) {\n console.warn(`[EmitPluginManifest] Skipping manifest generation — ${generationError}`);\n return;\n }\n\n const sourceContractPath = path.join(context, CONTRACT_TYPES_FILE);\n\n const tryReadFile = async (filePath: string): Promise<string | null> => {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n const stats = fs.statSync(filePath);\n if (!stats.isFile()) {\n return null;\n }\n try {\n return await fs.promises.readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n };\n\n const contractTypes = (await tryReadFile(sourceContractPath)) ?? \"\";\n\n if (!contractTypes) {\n console.warn(\n `[EmitPluginManifest] No contract types at ${sourceContractPath} ` +\n `(no src/contract.ts in this workspace?). Skipping manifest generation.`,\n );\n return;\n }\n\n const contractSha256 = crypto.createHash(\"sha256\").update(contractTypes).digest(\"hex\");\n const hashedEntry = findHashedEntry(Object.keys(compilation.assets), \"remoteEntry\");\n const manifest: Record<string, unknown> = {\n schemaVersion: 1,\n kind: \"every-plugin/manifest\",\n plugin: {\n name: pluginInfo.name,\n version: pluginInfo.version,\n },\n runtime: {\n remoteEntry: `./${hashedEntry ?? DEV_ENTRY_FILENAME}`,\n },\n contract: {\n kind: \"orpc\",\n types: {\n path: `./types/${contractFileName}`,\n exportName: \"contract\",\n typeName: \"ContractType\",\n sha256: contractSha256,\n },\n },\n };\n\n if (rawSource) {\n compilation.emitAsset(\n manifestFileName,\n new rawSource(`${JSON.stringify(manifest, null, 2)}\\n`),\n );\n compilation.emitAsset(`types/${contractFileName}`, new rawSource(`${contractTypes}`));\n }\n });\n });\n }\n}\n\nexport class EveryPluginBuild implements RspackPluginInstance {\n name = \"EveryPluginBuild\";\n\n constructor(private options: EveryPluginBuildOptions = {}) {}\n\n apply(compiler: Compiler) {\n const pluginInfo = getPluginInfo(compiler.options.context || process.cwd());\n\n this.configureDefaults(compiler, pluginInfo);\n\n const sharedDependencies = buildSharedDependencies(pluginInfo);\n new ModuleFederationPlugin({\n name: pluginInfo.normalizedName,\n filename: uiEntryFilename({ isBuild: isBuildInvocation() }),\n dts: this.options.dts !== false,\n manifest: {},\n runtimePlugins: [require.resolve(\"@module-federation/node/runtimePlugin\")],\n library: { type: \"commonjs-module\" },\n exposes: {\n \"./plugin\": \"./src/index.ts\",\n },\n shared: sharedDependencies,\n shareStrategy: \"version-first\",\n }).apply(compiler);\n\n new BuildReportPlugin(\"remoteEntry\", {\n shared: sharedVersionRecord(sharedDependencies),\n }).apply(compiler);\n new ChunkCompletenessPlugin().apply(compiler);\n\n if (this.options.dts === false) {\n compiler.options.plugins = (compiler.options.plugins ?? []).filter(\n (p) =>\n !p ||\n typeof p !== \"object\" ||\n ((p as any).name !== \"MFDevPlugin\" && (p as any).name !== \"ModuleFederationDtsPlugin\"),\n );\n }\n }\n\n private configureDefaults(compiler: Compiler, pluginInfo: any) {\n const context = compiler.options.context || process.cwd();\n\n if (!compiler.options.output) {\n compiler.options.output = {};\n }\n compiler.options.output.uniqueName = pluginInfo.normalizedName;\n compiler.options.output.publicPath = \"auto\";\n compiler.options.output.path = path.resolve(context, \"dist\");\n // Watch rebuilds must not wipe dist: the dev serve static handler serves\n // this directory, and a clean on every rebuild opens a window where\n // remoteEntry.js 404s for any consumer that loads mid-rebuild.\n compiler.options.output.clean = !compiler.options.watch;\n compiler.options.output.library = { type: \"commonjs-module\" };\n\n if (!compiler.options.target) {\n compiler.options.target = \"async-node\";\n }\n\n if (!compiler.options.mode) {\n compiler.options.mode = process.env.NODE_ENV === \"development\" ? \"development\" : \"production\";\n }\n\n if (compiler.options.devtool === undefined) {\n compiler.options.devtool = \"source-map\";\n }\n\n if (!compiler.options.infrastructureLogging) {\n compiler.options.infrastructureLogging = {\n level: \"warn\",\n };\n }\n\n this.ensureTypeScriptLoader(compiler);\n\n if (!compiler.options.optimization) {\n compiler.options.optimization = {};\n }\n // rspack 2.x splitChunks re-ids the final chunk graph while the federation\n // get-factory codegen keeps pre-split ids — the entry then references\n // chunks that were never emitted (ChunkCompletenessPlugin's exact failure\n // mode). Splitting off keeps the emitted bundle self-consistent.\n compiler.options.optimization.splitChunks = false;\n // The default deterministic module ids hash module identifiers that embed\n // absolute resource paths, so the same source builds to different id\n // spaces on different machines (host deploy train vs the image's\n // dist-builder stage). A mixed-generation load — CDN-pinned entry +\n // staged chunks — then splices two incompatible id spaces and fails with\n // \"__webpack_modules__[r] is not a function\". Named ids derive from\n // context-relative requests, keeping builds machine-independent and the\n // two generations semantically identical.\n compiler.options.optimization.moduleIds = \"named\";\n\n if (!compiler.options.resolve) {\n compiler.options.resolve = {};\n }\n compiler.options.resolve.extensions = [\"...\", \".tsx\", \".ts\"];\n // Source-first for local flows: resolve framework packages through the\n // `development` export condition (TS source) unless DEPLOY=true — the\n // manual dist-first switch deploy-identical builds opt into. (NODE_ENV\n // is unusable as the gate here: the rspack CLI defaults it to\n // \"production\" for every `build` invocation, including local dev watch.)\n // byDependency entries inherit the root conditions via \"...\", so\n // dropping the strip lets esm/cjs deps pick up `development` too.\n const sourceFirst = process.env.DEPLOY !== \"true\";\n compiler.options.resolve.conditionNames = [\n ...(sourceFirst ? [\"development\"] : []),\n \"webpack\",\n \"import\",\n \"module\",\n \"require\",\n \"node\",\n \"default\",\n ];\n if (sourceFirst) {\n // Source-resolved TS packages (e.g. better-near-auth) use node-style\n // `.js` specifiers for their own relative imports; map them to the\n // on-disk `.ts` sources. `.js` stays first in the expansion: the alias\n // picks the first target that exists on disk, and .ts-first made\n // packages that also ship index.ts (e.g. @scure/base) resolve their\n // TypeScript over their real JavaScript entry.\n compiler.options.resolve.extensionAlias = {\n \".js\": [\".js\", \".ts\", \".tsx\"],\n };\n }\n if (!sourceFirst && compiler.options.resolve.byDependency) {\n for (const depType of Object.keys(compiler.options.resolve.byDependency)) {\n const depConfig = (\n compiler.options.resolve.byDependency as Record<string, { conditionNames?: string[] }>\n )[depType];\n if (depConfig?.conditionNames) {\n depConfig.conditionNames = depConfig.conditionNames.filter((c) => c !== \"development\");\n }\n }\n }\n compiler.options.resolve.fallback = {\n ...compiler.options.resolve.fallback,\n bufferutil: false,\n \"utf-8-validate\": false,\n };\n }\n\n private ensureTypeScriptLoader(compiler: Compiler) {\n if (!compiler.options.module) {\n compiler.options.module = { rules: [] } as any;\n }\n\n if (!compiler.options.module.rules) {\n compiler.options.module.rules = [];\n }\n\n // Source-first resolution pulls every-plugin src into the graph, which\n // imports package.json (runtime/mf-config.ts). The MF shared chunks\n // must emit it as a json module, not parse it as JavaScript.\n const hasJsonRule = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".json\"),\n );\n\n if (!hasJsonRule) {\n compiler.options.module.rules.push({\n test: /\\.json$/,\n type: \"json\",\n } as any);\n }\n\n const hasTsLoader = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".ts\"),\n );\n\n if (!hasTsLoader) {\n compiler.options.module.rules.push({\n test: /\\.tsx?$/,\n use: \"builtin:swc-loader\",\n exclude: /node_modules/,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"mappings":";;UAkBiB;EACf;;UAGe;EACf;EACA;;cAGW,8BAA8B;UAGrB;EAFpB;EAEA,YAAoB,UAAS;EAE7B,MAAM,UAAU;;cAyFL,4BAA4B;UAGnB;EAFpB;EAEA,YAAoB,UAAS;EAE7B,MAAM,UAAU;UAmCR;UAsGA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"mappings":";;UAkBiB;EACf;;UAGe;EACf;EACA;;cAGW,8BAA8B;UAGrB;EAFpB;EAEA,YAAoB,UAAS;EAE7B,MAAM,UAAU;;cAyFL,4BAA4B;UAGnB;EAFpB;EAEA,YAAoB,UAAS;EAE7B,MAAM,UAAU;UAmCR;UAsGA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __require } from "../../_virtual/_rolldown/runtime.mjs";
|
|
2
2
|
import { DEV_ENTRY_FILENAME, findHashedEntry, isBuildInvocation, uiEntryFilename } from "../artifact-names.mjs";
|
|
3
|
+
import { sharedVersionRecord } from "../../shared-deps-spec.mjs";
|
|
3
4
|
import { BuildReportPlugin } from "./build-report-plugin.mjs";
|
|
4
5
|
import { CONTRACT_TYPES_FILE, generateContractTypes } from "../contract-types.mjs";
|
|
5
6
|
import { ChunkCompletenessPlugin } from "./chunk-completeness-plugin.mjs";
|
|
@@ -92,6 +93,7 @@ var EveryPluginBuild = class {
|
|
|
92
93
|
apply(compiler) {
|
|
93
94
|
const pluginInfo = getPluginInfo(compiler.options.context || process.cwd());
|
|
94
95
|
this.configureDefaults(compiler, pluginInfo);
|
|
96
|
+
const sharedDependencies = buildSharedDependencies(pluginInfo);
|
|
95
97
|
new ModuleFederationPlugin({
|
|
96
98
|
name: pluginInfo.normalizedName,
|
|
97
99
|
filename: uiEntryFilename({ isBuild: isBuildInvocation() }),
|
|
@@ -100,10 +102,10 @@ var EveryPluginBuild = class {
|
|
|
100
102
|
runtimePlugins: [__require.resolve("@module-federation/node/runtimePlugin")],
|
|
101
103
|
library: { type: "commonjs-module" },
|
|
102
104
|
exposes: { "./plugin": "./src/index.ts" },
|
|
103
|
-
shared:
|
|
105
|
+
shared: sharedDependencies,
|
|
104
106
|
shareStrategy: "version-first"
|
|
105
107
|
}).apply(compiler);
|
|
106
|
-
new BuildReportPlugin().apply(compiler);
|
|
108
|
+
new BuildReportPlugin("remoteEntry", { shared: sharedVersionRecord(sharedDependencies) }).apply(compiler);
|
|
107
109
|
new ChunkCompletenessPlugin().apply(compiler);
|
|
108
110
|
if (this.options.dts === false) compiler.options.plugins = (compiler.options.plugins ?? []).filter((p) => !p || typeof p !== "object" || p.name !== "MFDevPlugin" && p.name !== "ModuleFederationDtsPlugin");
|
|
109
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ModuleFederationPlugin } from \"@module-federation/enhanced/rspack\";\nimport type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport {\n DEV_ENTRY_FILENAME,\n findHashedEntry,\n isBuildInvocation,\n uiEntryFilename,\n} from \"../artifact-names\";\nimport { CONTRACT_TYPES_FILE, generateContractTypes } from \"../contract-types\";\nimport { BuildReportPlugin } from \"./build-report-plugin\";\nimport { ChunkCompletenessPlugin } from \"./chunk-completeness-plugin\";\nimport { buildSharedDependencies } from \"./module-federation\";\nimport { getPluginInfo } from \"./utils\";\n\nexport interface EveryPluginBuildOptions {\n dts?: boolean;\n}\n\nexport interface PluginManifestEmitterOptions {\n manifestFileName?: string;\n contractFileName?: string;\n}\n\nexport class EmitPluginManifest implements RspackPluginInstance {\n name = \"EmitPluginManifest\";\n\n constructor(private options: PluginManifestEmitterOptions = {}) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tapPromise({ name: this.name, stage }, async () => {\n const context = compiler.options.context || process.cwd();\n const pluginInfo = getPluginInfo(context);\n const contractFileName = this.options.contractFileName ?? \"contract.d.ts\";\n const manifestFileName = this.options.manifestFileName ?? \"plugin.manifest.json\";\n\n let generationError: string | null = null;\n try {\n const status = await generateContractTypes(context);\n if (status === \"generated\") {\n console.log(`[EmitPluginManifest] Contract types regenerated (${context}).`);\n }\n } catch (error) {\n generationError = error instanceof Error ? error.message : String(error);\n }\n\n if (generationError) {\n console.warn(`[EmitPluginManifest] Skipping manifest generation — ${generationError}`);\n return;\n }\n\n const sourceContractPath = path.join(context, CONTRACT_TYPES_FILE);\n\n const tryReadFile = async (filePath: string): Promise<string | null> => {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n const stats = fs.statSync(filePath);\n if (!stats.isFile()) {\n return null;\n }\n try {\n return await fs.promises.readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n };\n\n const contractTypes = (await tryReadFile(sourceContractPath)) ?? \"\";\n\n if (!contractTypes) {\n console.warn(\n `[EmitPluginManifest] No contract types at ${sourceContractPath} ` +\n `(no src/contract.ts in this workspace?). Skipping manifest generation.`,\n );\n return;\n }\n\n const contractSha256 = crypto.createHash(\"sha256\").update(contractTypes).digest(\"hex\");\n const hashedEntry = findHashedEntry(Object.keys(compilation.assets), \"remoteEntry\");\n const manifest: Record<string, unknown> = {\n schemaVersion: 1,\n kind: \"every-plugin/manifest\",\n plugin: {\n name: pluginInfo.name,\n version: pluginInfo.version,\n },\n runtime: {\n remoteEntry: `./${hashedEntry ?? DEV_ENTRY_FILENAME}`,\n },\n contract: {\n kind: \"orpc\",\n types: {\n path: `./types/${contractFileName}`,\n exportName: \"contract\",\n typeName: \"ContractType\",\n sha256: contractSha256,\n },\n },\n };\n\n if (rawSource) {\n compilation.emitAsset(\n manifestFileName,\n new rawSource(`${JSON.stringify(manifest, null, 2)}\\n`),\n );\n compilation.emitAsset(`types/${contractFileName}`, new rawSource(`${contractTypes}`));\n }\n });\n });\n }\n}\n\nexport class EveryPluginBuild implements RspackPluginInstance {\n name = \"EveryPluginBuild\";\n\n constructor(private options: EveryPluginBuildOptions = {}) {}\n\n apply(compiler: Compiler) {\n const pluginInfo = getPluginInfo(compiler.options.context || process.cwd());\n\n this.configureDefaults(compiler, pluginInfo);\n\n new ModuleFederationPlugin({\n name: pluginInfo.normalizedName,\n filename: uiEntryFilename({ isBuild: isBuildInvocation() }),\n dts: this.options.dts !== false,\n manifest: {},\n runtimePlugins: [require.resolve(\"@module-federation/node/runtimePlugin\")],\n library: { type: \"commonjs-module\" },\n exposes: {\n \"./plugin\": \"./src/index.ts\",\n },\n shared: buildSharedDependencies(pluginInfo),\n shareStrategy: \"version-first\",\n }).apply(compiler);\n\n new BuildReportPlugin().apply(compiler);\n new ChunkCompletenessPlugin().apply(compiler);\n\n if (this.options.dts === false) {\n compiler.options.plugins = (compiler.options.plugins ?? []).filter(\n (p) =>\n !p ||\n typeof p !== \"object\" ||\n ((p as any).name !== \"MFDevPlugin\" && (p as any).name !== \"ModuleFederationDtsPlugin\"),\n );\n }\n }\n\n private configureDefaults(compiler: Compiler, pluginInfo: any) {\n const context = compiler.options.context || process.cwd();\n\n if (!compiler.options.output) {\n compiler.options.output = {};\n }\n compiler.options.output.uniqueName = pluginInfo.normalizedName;\n compiler.options.output.publicPath = \"auto\";\n compiler.options.output.path = path.resolve(context, \"dist\");\n // Watch rebuilds must not wipe dist: the dev serve static handler serves\n // this directory, and a clean on every rebuild opens a window where\n // remoteEntry.js 404s for any consumer that loads mid-rebuild.\n compiler.options.output.clean = !compiler.options.watch;\n compiler.options.output.library = { type: \"commonjs-module\" };\n\n if (!compiler.options.target) {\n compiler.options.target = \"async-node\";\n }\n\n if (!compiler.options.mode) {\n compiler.options.mode = process.env.NODE_ENV === \"development\" ? \"development\" : \"production\";\n }\n\n if (compiler.options.devtool === undefined) {\n compiler.options.devtool = \"source-map\";\n }\n\n if (!compiler.options.infrastructureLogging) {\n compiler.options.infrastructureLogging = {\n level: \"warn\",\n };\n }\n\n this.ensureTypeScriptLoader(compiler);\n\n if (!compiler.options.optimization) {\n compiler.options.optimization = {};\n }\n // rspack 2.x splitChunks re-ids the final chunk graph while the federation\n // get-factory codegen keeps pre-split ids — the entry then references\n // chunks that were never emitted (ChunkCompletenessPlugin's exact failure\n // mode). Splitting off keeps the emitted bundle self-consistent.\n compiler.options.optimization.splitChunks = false;\n // The default deterministic module ids hash module identifiers that embed\n // absolute resource paths, so the same source builds to different id\n // spaces on different machines (host deploy train vs the image's\n // dist-builder stage). A mixed-generation load — CDN-pinned entry +\n // staged chunks — then splices two incompatible id spaces and fails with\n // \"__webpack_modules__[r] is not a function\". Named ids derive from\n // context-relative requests, keeping builds machine-independent and the\n // two generations semantically identical.\n compiler.options.optimization.moduleIds = \"named\";\n\n if (!compiler.options.resolve) {\n compiler.options.resolve = {};\n }\n compiler.options.resolve.extensions = [\"...\", \".tsx\", \".ts\"];\n // Source-first for local flows: resolve framework packages through the\n // `development` export condition (TS source) unless DEPLOY=true — the\n // manual dist-first switch deploy-identical builds opt into. (NODE_ENV\n // is unusable as the gate here: the rspack CLI defaults it to\n // \"production\" for every `build` invocation, including local dev watch.)\n // byDependency entries inherit the root conditions via \"...\", so\n // dropping the strip lets esm/cjs deps pick up `development` too.\n const sourceFirst = process.env.DEPLOY !== \"true\";\n compiler.options.resolve.conditionNames = [\n ...(sourceFirst ? [\"development\"] : []),\n \"webpack\",\n \"import\",\n \"module\",\n \"require\",\n \"node\",\n \"default\",\n ];\n if (sourceFirst) {\n // Source-resolved TS packages (e.g. better-near-auth) use node-style\n // `.js` specifiers for their own relative imports; map them to the\n // on-disk `.ts` sources. `.js` stays first in the expansion: the alias\n // picks the first target that exists on disk, and .ts-first made\n // packages that also ship index.ts (e.g. @scure/base) resolve their\n // TypeScript over their real JavaScript entry.\n compiler.options.resolve.extensionAlias = {\n \".js\": [\".js\", \".ts\", \".tsx\"],\n };\n }\n if (!sourceFirst && compiler.options.resolve.byDependency) {\n for (const depType of Object.keys(compiler.options.resolve.byDependency)) {\n const depConfig = (\n compiler.options.resolve.byDependency as Record<string, { conditionNames?: string[] }>\n )[depType];\n if (depConfig?.conditionNames) {\n depConfig.conditionNames = depConfig.conditionNames.filter((c) => c !== \"development\");\n }\n }\n }\n compiler.options.resolve.fallback = {\n ...compiler.options.resolve.fallback,\n bufferutil: false,\n \"utf-8-validate\": false,\n };\n }\n\n private ensureTypeScriptLoader(compiler: Compiler) {\n if (!compiler.options.module) {\n compiler.options.module = { rules: [] } as any;\n }\n\n if (!compiler.options.module.rules) {\n compiler.options.module.rules = [];\n }\n\n // Source-first resolution pulls every-plugin src into the graph, which\n // imports package.json (runtime/mf-config.ts). The MF shared chunks\n // must emit it as a json module, not parse it as JavaScript.\n const hasJsonRule = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".json\"),\n );\n\n if (!hasJsonRule) {\n compiler.options.module.rules.push({\n test: /\\.json$/,\n type: \"json\",\n } as any);\n }\n\n const hasTsLoader = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".ts\"),\n );\n\n if (!hasTsLoader) {\n compiler.options.module.rules.push({\n test: /\\.tsx?$/,\n use: \"builtin:swc-loader\",\n exclude: /node_modules/,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/build/rspack/plugin.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ModuleFederationPlugin } from \"@module-federation/enhanced/rspack\";\nimport type { Compiler, RspackPluginInstance } from \"@rspack/core\";\nimport { sharedVersionRecord } from \"../../shared-deps-spec\";\nimport {\n DEV_ENTRY_FILENAME,\n findHashedEntry,\n isBuildInvocation,\n uiEntryFilename,\n} from \"../artifact-names\";\nimport { CONTRACT_TYPES_FILE, generateContractTypes } from \"../contract-types\";\nimport { BuildReportPlugin } from \"./build-report-plugin\";\nimport { ChunkCompletenessPlugin } from \"./chunk-completeness-plugin\";\nimport { buildSharedDependencies } from \"./module-federation\";\nimport { getPluginInfo } from \"./utils\";\n\nexport interface EveryPluginBuildOptions {\n dts?: boolean;\n}\n\nexport interface PluginManifestEmitterOptions {\n manifestFileName?: string;\n contractFileName?: string;\n}\n\nexport class EmitPluginManifest implements RspackPluginInstance {\n name = \"EmitPluginManifest\";\n\n constructor(private options: PluginManifestEmitterOptions = {}) {}\n\n apply(compiler: Compiler) {\n compiler.hooks.thisCompilation.tap(this.name, (compilation) => {\n const webpack = (compiler as Compiler & { webpack?: any }).webpack;\n const rawSource = webpack?.sources?.RawSource;\n const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;\n\n compilation.hooks.processAssets.tapPromise({ name: this.name, stage }, async () => {\n const context = compiler.options.context || process.cwd();\n const pluginInfo = getPluginInfo(context);\n const contractFileName = this.options.contractFileName ?? \"contract.d.ts\";\n const manifestFileName = this.options.manifestFileName ?? \"plugin.manifest.json\";\n\n let generationError: string | null = null;\n try {\n const status = await generateContractTypes(context);\n if (status === \"generated\") {\n console.log(`[EmitPluginManifest] Contract types regenerated (${context}).`);\n }\n } catch (error) {\n generationError = error instanceof Error ? error.message : String(error);\n }\n\n if (generationError) {\n console.warn(`[EmitPluginManifest] Skipping manifest generation — ${generationError}`);\n return;\n }\n\n const sourceContractPath = path.join(context, CONTRACT_TYPES_FILE);\n\n const tryReadFile = async (filePath: string): Promise<string | null> => {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n const stats = fs.statSync(filePath);\n if (!stats.isFile()) {\n return null;\n }\n try {\n return await fs.promises.readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n };\n\n const contractTypes = (await tryReadFile(sourceContractPath)) ?? \"\";\n\n if (!contractTypes) {\n console.warn(\n `[EmitPluginManifest] No contract types at ${sourceContractPath} ` +\n `(no src/contract.ts in this workspace?). Skipping manifest generation.`,\n );\n return;\n }\n\n const contractSha256 = crypto.createHash(\"sha256\").update(contractTypes).digest(\"hex\");\n const hashedEntry = findHashedEntry(Object.keys(compilation.assets), \"remoteEntry\");\n const manifest: Record<string, unknown> = {\n schemaVersion: 1,\n kind: \"every-plugin/manifest\",\n plugin: {\n name: pluginInfo.name,\n version: pluginInfo.version,\n },\n runtime: {\n remoteEntry: `./${hashedEntry ?? DEV_ENTRY_FILENAME}`,\n },\n contract: {\n kind: \"orpc\",\n types: {\n path: `./types/${contractFileName}`,\n exportName: \"contract\",\n typeName: \"ContractType\",\n sha256: contractSha256,\n },\n },\n };\n\n if (rawSource) {\n compilation.emitAsset(\n manifestFileName,\n new rawSource(`${JSON.stringify(manifest, null, 2)}\\n`),\n );\n compilation.emitAsset(`types/${contractFileName}`, new rawSource(`${contractTypes}`));\n }\n });\n });\n }\n}\n\nexport class EveryPluginBuild implements RspackPluginInstance {\n name = \"EveryPluginBuild\";\n\n constructor(private options: EveryPluginBuildOptions = {}) {}\n\n apply(compiler: Compiler) {\n const pluginInfo = getPluginInfo(compiler.options.context || process.cwd());\n\n this.configureDefaults(compiler, pluginInfo);\n\n const sharedDependencies = buildSharedDependencies(pluginInfo);\n new ModuleFederationPlugin({\n name: pluginInfo.normalizedName,\n filename: uiEntryFilename({ isBuild: isBuildInvocation() }),\n dts: this.options.dts !== false,\n manifest: {},\n runtimePlugins: [require.resolve(\"@module-federation/node/runtimePlugin\")],\n library: { type: \"commonjs-module\" },\n exposes: {\n \"./plugin\": \"./src/index.ts\",\n },\n shared: sharedDependencies,\n shareStrategy: \"version-first\",\n }).apply(compiler);\n\n new BuildReportPlugin(\"remoteEntry\", {\n shared: sharedVersionRecord(sharedDependencies),\n }).apply(compiler);\n new ChunkCompletenessPlugin().apply(compiler);\n\n if (this.options.dts === false) {\n compiler.options.plugins = (compiler.options.plugins ?? []).filter(\n (p) =>\n !p ||\n typeof p !== \"object\" ||\n ((p as any).name !== \"MFDevPlugin\" && (p as any).name !== \"ModuleFederationDtsPlugin\"),\n );\n }\n }\n\n private configureDefaults(compiler: Compiler, pluginInfo: any) {\n const context = compiler.options.context || process.cwd();\n\n if (!compiler.options.output) {\n compiler.options.output = {};\n }\n compiler.options.output.uniqueName = pluginInfo.normalizedName;\n compiler.options.output.publicPath = \"auto\";\n compiler.options.output.path = path.resolve(context, \"dist\");\n // Watch rebuilds must not wipe dist: the dev serve static handler serves\n // this directory, and a clean on every rebuild opens a window where\n // remoteEntry.js 404s for any consumer that loads mid-rebuild.\n compiler.options.output.clean = !compiler.options.watch;\n compiler.options.output.library = { type: \"commonjs-module\" };\n\n if (!compiler.options.target) {\n compiler.options.target = \"async-node\";\n }\n\n if (!compiler.options.mode) {\n compiler.options.mode = process.env.NODE_ENV === \"development\" ? \"development\" : \"production\";\n }\n\n if (compiler.options.devtool === undefined) {\n compiler.options.devtool = \"source-map\";\n }\n\n if (!compiler.options.infrastructureLogging) {\n compiler.options.infrastructureLogging = {\n level: \"warn\",\n };\n }\n\n this.ensureTypeScriptLoader(compiler);\n\n if (!compiler.options.optimization) {\n compiler.options.optimization = {};\n }\n // rspack 2.x splitChunks re-ids the final chunk graph while the federation\n // get-factory codegen keeps pre-split ids — the entry then references\n // chunks that were never emitted (ChunkCompletenessPlugin's exact failure\n // mode). Splitting off keeps the emitted bundle self-consistent.\n compiler.options.optimization.splitChunks = false;\n // The default deterministic module ids hash module identifiers that embed\n // absolute resource paths, so the same source builds to different id\n // spaces on different machines (host deploy train vs the image's\n // dist-builder stage). A mixed-generation load — CDN-pinned entry +\n // staged chunks — then splices two incompatible id spaces and fails with\n // \"__webpack_modules__[r] is not a function\". Named ids derive from\n // context-relative requests, keeping builds machine-independent and the\n // two generations semantically identical.\n compiler.options.optimization.moduleIds = \"named\";\n\n if (!compiler.options.resolve) {\n compiler.options.resolve = {};\n }\n compiler.options.resolve.extensions = [\"...\", \".tsx\", \".ts\"];\n // Source-first for local flows: resolve framework packages through the\n // `development` export condition (TS source) unless DEPLOY=true — the\n // manual dist-first switch deploy-identical builds opt into. (NODE_ENV\n // is unusable as the gate here: the rspack CLI defaults it to\n // \"production\" for every `build` invocation, including local dev watch.)\n // byDependency entries inherit the root conditions via \"...\", so\n // dropping the strip lets esm/cjs deps pick up `development` too.\n const sourceFirst = process.env.DEPLOY !== \"true\";\n compiler.options.resolve.conditionNames = [\n ...(sourceFirst ? [\"development\"] : []),\n \"webpack\",\n \"import\",\n \"module\",\n \"require\",\n \"node\",\n \"default\",\n ];\n if (sourceFirst) {\n // Source-resolved TS packages (e.g. better-near-auth) use node-style\n // `.js` specifiers for their own relative imports; map them to the\n // on-disk `.ts` sources. `.js` stays first in the expansion: the alias\n // picks the first target that exists on disk, and .ts-first made\n // packages that also ship index.ts (e.g. @scure/base) resolve their\n // TypeScript over their real JavaScript entry.\n compiler.options.resolve.extensionAlias = {\n \".js\": [\".js\", \".ts\", \".tsx\"],\n };\n }\n if (!sourceFirst && compiler.options.resolve.byDependency) {\n for (const depType of Object.keys(compiler.options.resolve.byDependency)) {\n const depConfig = (\n compiler.options.resolve.byDependency as Record<string, { conditionNames?: string[] }>\n )[depType];\n if (depConfig?.conditionNames) {\n depConfig.conditionNames = depConfig.conditionNames.filter((c) => c !== \"development\");\n }\n }\n }\n compiler.options.resolve.fallback = {\n ...compiler.options.resolve.fallback,\n bufferutil: false,\n \"utf-8-validate\": false,\n };\n }\n\n private ensureTypeScriptLoader(compiler: Compiler) {\n if (!compiler.options.module) {\n compiler.options.module = { rules: [] } as any;\n }\n\n if (!compiler.options.module.rules) {\n compiler.options.module.rules = [];\n }\n\n // Source-first resolution pulls every-plugin src into the graph, which\n // imports package.json (runtime/mf-config.ts). The MF shared chunks\n // must emit it as a json module, not parse it as JavaScript.\n const hasJsonRule = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".json\"),\n );\n\n if (!hasJsonRule) {\n compiler.options.module.rules.push({\n test: /\\.json$/,\n type: \"json\",\n } as any);\n }\n\n const hasTsLoader = compiler.options.module.rules.some(\n (rule: any) =>\n typeof rule === \"object\" &&\n rule !== null &&\n \"test\" in rule &&\n rule.test instanceof RegExp &&\n rule.test.test(\".ts\"),\n );\n\n if (!hasTsLoader) {\n compiler.options.module.rules.push({\n test: /\\.tsx?$/,\n use: \"builtin:swc-loader\",\n exclude: /node_modules/,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -50,7 +50,10 @@ function hashArtifactsPlugin(options) {
|
|
|
50
50
|
if (!from) throw new Error(`[hash-artifacts] planned copy source missing: ${copy.from}`);
|
|
51
51
|
node_fs.default.copyFileSync(from, node_path.default.join(distRoot, copy.to));
|
|
52
52
|
}
|
|
53
|
-
node_fs.default.writeFileSync(node_path.default.join(distRoot, "build-report.json"), `${JSON.stringify(require_build_artifact_names.BuildEntryReportSchema.parse(
|
|
53
|
+
node_fs.default.writeFileSync(node_path.default.join(distRoot, "build-report.json"), `${JSON.stringify(require_build_artifact_names.BuildEntryReportSchema.parse({
|
|
54
|
+
...plan.report,
|
|
55
|
+
...options.shared ? { shared: options.shared } : {}
|
|
56
|
+
}), null, 2)}\n`);
|
|
54
57
|
});
|
|
55
58
|
}
|
|
56
59
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash-artifacts-plugin.cjs","names":["fs","path","findHashedEntry","MF_MANIFEST_FILENAME","STYLE_FILENAME","planArtifactCopies","BuildEntryReportSchema"],"sources":["../../../src/build/ui/hash-artifacts-plugin.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { RsbuildPlugin } from \"@rsbuild/core\";\nimport {\n BuildEntryReportSchema,\n findHashedEntry,\n MF_MANIFEST_FILENAME,\n planArtifactCopies,\n STYLE_FILENAME,\n} from \"../artifact-names\";\n\nexport interface HashArtifactsPluginOptions {\n /** The container entry's base name in this environment's dist root. */\n entryBase: string;\n /** Absolute dist root this environment wrote (web: `dist`, node: `dist/ssr`). */\n distRoot: string;\n}\n\nfunction listFiles(dir: string, out: string[] = []): string[] {\n for (const entry of fs\n .readdirSync(dir, { withFileTypes: true })\n .sort((a, b) => a.name.localeCompare(b.name))) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) listFiles(full, out);\n else out.push(path.relative(dir, full).split(path.sep).join(\"/\"));\n }\n return out;\n}\n\n/**\n * Emits the additive hashed copies for one environment's dist root (ticket:\n * atomic-deploys 01). The entry itself is hashed by the build (filename\n * template); this plugin adds immutable hashed copies of the fixed-name\n * browser artifacts (mf-manifest.json at the root, style.css under\n * static/css), and `build-report.json` for the deploy leg. Idempotent under\n * watch: same bytes → same hashed names.\n */\nexport function hashArtifactsPlugin(options: HashArtifactsPluginOptions): RsbuildPlugin {\n return {\n name: \"hash-artifacts\",\n setup(api) {\n api.onAfterEnvironmentCompile(() => {\n const distRoot = options.distRoot;\n if (!fs.existsSync(distRoot)) return;\n\n const resolveExisting = (name: string): string | null => {\n const candidates = [\n path.join(distRoot, name),\n path.join(distRoot, \"static\", \"css\", name),\n ];\n return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;\n };\n\n const assetNames = listFiles(distRoot);\n const entry = findHashedEntry(assetNames, options.entryBase);\n if (!entry) return;\n\n const contents: Record<string, string> = {};\n for (const fixedName of [MF_MANIFEST_FILENAME, STYLE_FILENAME]) {\n const from = resolveExisting(fixedName);\n if (from) contents[fixedName] = fs.readFileSync(from, \"utf8\");\n }\n\n const plan = planArtifactCopies({ assetNames, entryBase: options.entryBase, contents });\n if (!plan) return;\n\n for (const copy of plan.copies) {\n const from = resolveExisting(copy.from);\n if (!from) throw new Error(`[hash-artifacts] planned copy source missing: ${copy.from}`);\n fs.copyFileSync(from, path.join(distRoot, copy.to));\n }\n fs.writeFileSync(\n path.join(distRoot, \"build-report.json\"),\n `${JSON.stringify(BuildEntryReportSchema.parse(plan.report)
|
|
1
|
+
{"version":3,"file":"hash-artifacts-plugin.cjs","names":["fs","path","findHashedEntry","MF_MANIFEST_FILENAME","STYLE_FILENAME","planArtifactCopies","BuildEntryReportSchema"],"sources":["../../../src/build/ui/hash-artifacts-plugin.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { RsbuildPlugin } from \"@rsbuild/core\";\nimport {\n BuildEntryReportSchema,\n findHashedEntry,\n MF_MANIFEST_FILENAME,\n planArtifactCopies,\n STYLE_FILENAME,\n} from \"../artifact-names\";\n\nexport interface HashArtifactsPluginOptions {\n /** The container entry's base name in this environment's dist root. */\n entryBase: string;\n /** Absolute dist root this environment wrote (web: `dist`, node: `dist/ssr`). */\n distRoot: string;\n /**\n * The shared-singleton `name → version` map this build embedded — recorded\n * in build-report.json so the version manifest carries shared-dep\n * semantics (#546).\n */\n shared?: Record<string, string>;\n}\n\nfunction listFiles(dir: string, out: string[] = []): string[] {\n for (const entry of fs\n .readdirSync(dir, { withFileTypes: true })\n .sort((a, b) => a.name.localeCompare(b.name))) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) listFiles(full, out);\n else out.push(path.relative(dir, full).split(path.sep).join(\"/\"));\n }\n return out;\n}\n\n/**\n * Emits the additive hashed copies for one environment's dist root (ticket:\n * atomic-deploys 01). The entry itself is hashed by the build (filename\n * template); this plugin adds immutable hashed copies of the fixed-name\n * browser artifacts (mf-manifest.json at the root, style.css under\n * static/css), and `build-report.json` for the deploy leg. Idempotent under\n * watch: same bytes → same hashed names.\n */\nexport function hashArtifactsPlugin(options: HashArtifactsPluginOptions): RsbuildPlugin {\n return {\n name: \"hash-artifacts\",\n setup(api) {\n api.onAfterEnvironmentCompile(() => {\n const distRoot = options.distRoot;\n if (!fs.existsSync(distRoot)) return;\n\n const resolveExisting = (name: string): string | null => {\n const candidates = [\n path.join(distRoot, name),\n path.join(distRoot, \"static\", \"css\", name),\n ];\n return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;\n };\n\n const assetNames = listFiles(distRoot);\n const entry = findHashedEntry(assetNames, options.entryBase);\n if (!entry) return;\n\n const contents: Record<string, string> = {};\n for (const fixedName of [MF_MANIFEST_FILENAME, STYLE_FILENAME]) {\n const from = resolveExisting(fixedName);\n if (from) contents[fixedName] = fs.readFileSync(from, \"utf8\");\n }\n\n const plan = planArtifactCopies({ assetNames, entryBase: options.entryBase, contents });\n if (!plan) return;\n\n for (const copy of plan.copies) {\n const from = resolveExisting(copy.from);\n if (!from) throw new Error(`[hash-artifacts] planned copy source missing: ${copy.from}`);\n fs.copyFileSync(from, path.join(distRoot, copy.to));\n }\n fs.writeFileSync(\n path.join(distRoot, \"build-report.json\"),\n `${JSON.stringify(\n BuildEntryReportSchema.parse({\n ...plan.report,\n ...(options.shared ? { shared: options.shared } : {}),\n }),\n null,\n 2,\n )}\\n`,\n );\n });\n },\n };\n}\n"],"mappings":";;;;;;;;AAwBA,SAAS,UAAU,KAAa,MAAgB,CAAC,GAAa;CAC5D,KAAK,MAAM,SAASA,gBACjB,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,CACzC,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;EAC/C,MAAM,OAAOC,kBAAK,KAAK,KAAK,MAAM,IAAI;EACtC,IAAI,MAAM,YAAY,GAAG,UAAU,MAAM,GAAG;OACvC,IAAI,KAAKA,kBAAK,SAAS,KAAK,IAAI,CAAC,CAAC,MAAMA,kBAAK,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;CAClE;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAgB,oBAAoB,SAAoD;CACtF,OAAO;EACL,MAAM;EACN,MAAM,KAAK;GACT,IAAI,gCAAgC;IAClC,MAAM,WAAW,QAAQ;IACzB,IAAI,CAACD,gBAAG,WAAW,QAAQ,GAAG;IAE9B,MAAM,mBAAmB,SAAgC;KAKvD,OAAO,CAHLC,kBAAK,KAAK,UAAU,IAAI,GACxBA,kBAAK,KAAK,UAAU,UAAU,OAAO,IAAI,CAE3B,CAAC,CAAC,MAAM,cAAcD,gBAAG,WAAW,SAAS,CAAC,KAAK;IACrE;IAEA,MAAM,aAAa,UAAU,QAAQ;IAErC,IAAI,CADUE,6CAAgB,YAAY,QAAQ,SACzC,GAAG;IAEZ,MAAM,WAAmC,CAAC;IAC1C,KAAK,MAAM,aAAa,CAACC,mDAAsBC,2CAAc,GAAG;KAC9D,MAAM,OAAO,gBAAgB,SAAS;KACtC,IAAI,MAAM,SAAS,aAAaJ,gBAAG,aAAa,MAAM,MAAM;IAC9D;IAEA,MAAM,OAAOK,gDAAmB;KAAE;KAAY,WAAW,QAAQ;KAAW;IAAS,CAAC;IACtF,IAAI,CAAC,MAAM;IAEX,KAAK,MAAM,QAAQ,KAAK,QAAQ;KAC9B,MAAM,OAAO,gBAAgB,KAAK,IAAI;KACtC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,iDAAiD,KAAK,MAAM;KACvF,gBAAG,aAAa,MAAMJ,kBAAK,KAAK,UAAU,KAAK,EAAE,CAAC;IACpD;IACA,gBAAG,cACDA,kBAAK,KAAK,UAAU,mBAAmB,GACvC,GAAG,KAAK,UACNK,oDAAuB,MAAM;KAC3B,GAAG,KAAK;KACR,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;IACrD,CAAC,GACD,MACA,CACF,EAAE,GACJ;GACF,CAAC;EACH;CACF;AACF"}
|
|
@@ -5,6 +5,12 @@ interface HashArtifactsPluginOptions {
|
|
|
5
5
|
entryBase: string;
|
|
6
6
|
/** Absolute dist root this environment wrote (web: `dist`, node: `dist/ssr`). */
|
|
7
7
|
distRoot: string;
|
|
8
|
+
/**
|
|
9
|
+
* The shared-singleton `name → version` map this build embedded — recorded
|
|
10
|
+
* in build-report.json so the version manifest carries shared-dep
|
|
11
|
+
* semantics (#546).
|
|
12
|
+
*/
|
|
13
|
+
shared?: Record<string, string>;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
16
|
* Emits the additive hashed copies for one environment's dist root (ticket:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash-artifacts-plugin.d.cts","names":[],"sources":["../../../src/build/ui/hash-artifacts-plugin.ts"],"mappings":";;UAWiB;;EAEf;;EAEA;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"hash-artifacts-plugin.d.cts","names":[],"sources":["../../../src/build/ui/hash-artifacts-plugin.ts"],"mappings":";;UAWiB;;EAEf;;EAEA;;;;;;EAMA,SAAS;;;;;;;;;;iBAsBK,oBAAoB,SAAS,6BAA6B"}
|