every-plugin 3.0.0-rc.11 → 3.0.0-rc.12

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.
Files changed (51) hide show
  1. package/dist/build/artifact-names.cjs +9 -1
  2. package/dist/build/artifact-names.cjs.map +1 -1
  3. package/dist/build/artifact-names.d.cts +1 -0
  4. package/dist/build/artifact-names.d.cts.map +1 -1
  5. package/dist/build/artifact-names.d.mts +1 -0
  6. package/dist/build/artifact-names.d.mts.map +1 -1
  7. package/dist/build/artifact-names.mjs +9 -1
  8. package/dist/build/artifact-names.mjs.map +1 -1
  9. package/dist/build/rspack/build-report-plugin.cjs +9 -3
  10. package/dist/build/rspack/build-report-plugin.cjs.map +1 -1
  11. package/dist/build/rspack/build-report-plugin.d.cts +12 -2
  12. package/dist/build/rspack/build-report-plugin.d.cts.map +1 -1
  13. package/dist/build/rspack/build-report-plugin.d.mts +12 -2
  14. package/dist/build/rspack/build-report-plugin.d.mts.map +1 -1
  15. package/dist/build/rspack/build-report-plugin.mjs +9 -3
  16. package/dist/build/rspack/build-report-plugin.mjs.map +1 -1
  17. package/dist/build/rspack/plugin.cjs +4 -2
  18. package/dist/build/rspack/plugin.cjs.map +1 -1
  19. package/dist/build/rspack/plugin.d.cts.map +1 -1
  20. package/dist/build/rspack/plugin.d.mts.map +1 -1
  21. package/dist/build/rspack/plugin.mjs +4 -2
  22. package/dist/build/rspack/plugin.mjs.map +1 -1
  23. package/dist/build/ui/hash-artifacts-plugin.cjs +4 -1
  24. package/dist/build/ui/hash-artifacts-plugin.cjs.map +1 -1
  25. package/dist/build/ui/hash-artifacts-plugin.d.cts +6 -0
  26. package/dist/build/ui/hash-artifacts-plugin.d.cts.map +1 -1
  27. package/dist/build/ui/hash-artifacts-plugin.d.mts +6 -0
  28. package/dist/build/ui/hash-artifacts-plugin.d.mts.map +1 -1
  29. package/dist/build/ui/hash-artifacts-plugin.mjs +4 -1
  30. package/dist/build/ui/hash-artifacts-plugin.mjs.map +1 -1
  31. package/dist/build/ui/rsbuild-config.cjs +5 -2
  32. package/dist/build/ui/rsbuild-config.cjs.map +1 -1
  33. package/dist/build/ui/rsbuild-config.mjs +6 -3
  34. package/dist/build/ui/rsbuild-config.mjs.map +1 -1
  35. package/dist/runtime/mf-config.cjs +1 -1
  36. package/dist/runtime/mf-config.mjs +1 -1
  37. package/dist/shared-deps-spec.cjs +13 -0
  38. package/dist/shared-deps-spec.cjs.map +1 -1
  39. package/dist/shared-deps-spec.d.cts +13 -1
  40. package/dist/shared-deps-spec.d.cts.map +1 -1
  41. package/dist/shared-deps-spec.d.mts +13 -1
  42. package/dist/shared-deps-spec.d.mts.map +1 -1
  43. package/dist/shared-deps-spec.mjs +13 -1
  44. package/dist/shared-deps-spec.mjs.map +1 -1
  45. package/package.json +1 -1
  46. package/src/build/artifact-names.ts +8 -0
  47. package/src/build/rspack/build-report-plugin.ts +20 -3
  48. package/src/build/rspack/plugin.ts +6 -2
  49. package/src/build/ui/hash-artifacts-plugin.ts +14 -1
  50. package/src/build/ui/rsbuild-config.ts +4 -1
  51. package/src/shared-deps-spec.ts +24 -0
@@ -1 +1 @@
1
- {"version":3,"file":"shared-deps-spec.mjs","names":[],"sources":["../src/shared-deps-spec.ts"],"sourcesContent":["/**\n * SharedDependencySpec: the one module owning shared-dependency names,\n * criticality, version resolution, and fallback policy across the Module\n * Federation graph. Four consumers derive from it — the rspack config\n * factory (plugin builds), the rsbuild config factory (ui remotes), the\n * runtime mf-config (the every-plugin MF instance), and the host's shared\n * pre-registration — so a name or criticality can never disagree between\n * them, and an unresolved version fails the build loudly instead of\n * silently degrading to \"*\"/\"latest\" (which disables the strict-singleton\n * guard).\n *\n * Lists:\n * - core: shared by the host and server-side plugin remotes (every-plugin,\n * effect, zod, @orpc/*). Effect-critical entries pin the exact installed\n * version (strictVersion) — the version-check machinery in\n * runtime/services/shared-identity.ts relies on it.\n * - ui: shared by the core ui provider and ui remotes (react, react-dom,\n * @orpc/client, @orpc/contract, TanStack, lingui, the everything-dev\n * session/i18n subpath modules). All strict singletons; consumers add\n * `import: false`.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Data } from \"effect\";\n\nconst require = createRequire(import.meta.url);\n\nexport class SharedDependencyResolutionError extends Data.TaggedError(\n \"SharedDependencyResolutionError\",\n)<{\n readonly packageName: string;\n readonly searched: string[];\n}> {\n constructor(packageName: string, searched: string[]) {\n super({\n packageName,\n searched,\n });\n }\n\n override get message() {\n return (\n `Could not resolve shared dependency \"${this.packageName}\". Looked in:\\n` +\n this.searched.map((location) => ` - ${location}`).join(\"\\n\") +\n `\\nUnresolved shared dependencies fail loudly (no \"*\", no \"latest\" fallback) — ` +\n `install the package in the resolving workspace or pin a resolvable version.`\n );\n }\n}\n\nexport type SharedDependencyResolution = \"self\" | \"package\" | \"subpath\";\n\nexport interface SharedDependencySpec {\n name: string;\n singleton: true;\n /** exact-version pin (requiredVersion = resolved version, strictVersion = true) */\n critical: boolean;\n resolution: SharedDependencyResolution;\n}\n\nexport const CORE_SHARED_DEPS = [\n { name: \"every-plugin\", singleton: true, critical: true, resolution: \"self\" },\n { name: \"effect\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"zod\", singleton: true, critical: false, resolution: \"package\" },\n { name: \"@orpc/contract\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/client\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/server\", singleton: true, critical: true, resolution: \"package\" },\n // v1's proven sharing set, restored: @orpc/openapi, @orpc/experimental-effect,\n // and @orpc/publisher joined this list in the v2 fleet-spec pass and their\n // share-scope consumption broke plugin loading at runtime\n // (__webpack_modules__[r] is not a function — the runtime's import()-based\n // provides hand the consumer an ESM namespace where a module factory is\n // expected). They stay declared in each workspace's package.json and are\n // bundled per workspace instead.\n] as const satisfies readonly SharedDependencySpec[];\n\nexport type CoreSharedDepName = (typeof CORE_SHARED_DEPS)[number][\"name\"];\n\nconst coreCriticalNames = CORE_SHARED_DEPS.flatMap((spec) => (spec.critical ? [spec.name] : []));\n\n/**\n * Effect-critical shared names, derived from the core list's `critical: true`\n * entries — the one source of criticality (`strictVersion` pinning in the\n * runtime's version-check machinery keys off this list).\n */\nexport const EFFECT_CRITICAL_SHARED_DEPS: readonly EffectCriticalSharedDepName[] =\n coreCriticalNames;\n\nexport type EffectCriticalSharedDepName = (typeof coreCriticalNames)[number];\n\nexport const isEffectCriticalSharedDep = (name: string): boolean =>\n (EFFECT_CRITICAL_SHARED_DEPS as readonly string[]).includes(name);\n\nexport const UI_SHARED_DEPS = [\n { name: \"react\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"react-dom\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/client\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/contract\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@tanstack/react-query\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@tanstack/react-router\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@lingui/core\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@lingui/react\", singleton: true, critical: true, resolution: \"package\" },\n {\n name: \"everything-dev/ui/auth\",\n singleton: true,\n critical: true,\n resolution: \"subpath\",\n },\n {\n name: \"everything-dev/ui/i18n\",\n singleton: true,\n critical: true,\n resolution: \"subpath\",\n },\n] as const satisfies readonly SharedDependencySpec[];\n\nexport const SHARED_DEP_SPECS: readonly SharedDependencySpec[] = (() => {\n const byName = new Map<string, SharedDependencySpec>();\n for (const spec of [...CORE_SHARED_DEPS, ...UI_SHARED_DEPS]) {\n const existing = byName.get(spec.name);\n if (existing) {\n if (existing.critical !== spec.critical) {\n throw new Error(\n `Shared dependency spec conflict for \"${spec.name}\": criticality disagrees between the core and ui lists`,\n );\n }\n continue;\n }\n byName.set(spec.name, spec);\n }\n return [...byName.values()];\n})();\n\nexport interface SharedDependencyConfig {\n version: string;\n requiredVersion: string | false;\n singleton: boolean;\n strictVersion: boolean;\n eager: boolean;\n shareScope: string;\n}\n\nexport type SharedDependencies = Record<string, SharedDependencyConfig>;\n\nexport type SharedConfigInput = {\n version: string;\n requiredVersion?: string | false;\n singleton?: boolean;\n strictVersion?: boolean;\n eager?: boolean;\n shareScope?: string;\n};\n\nexport interface SharedDependencyResolutionOptions {\n /** the building workspace to resolve from; default is this module's own location */\n workspaceRoot?: string;\n resolution?: \"package\" | \"subpath\";\n}\n\nfunction searchedNote(target: string): string {\n return `require.resolve(\"${target}\")`;\n}\n\nfunction findPackageJsonVersion(startDir: string, searched: string[]): string | null {\n let currentDir = startDir;\n for (let i = 0; i < 5; i += 1) {\n const packageJsonPath = join(currentDir, \"package.json\");\n searched.push(packageJsonPath);\n if (existsSync(packageJsonPath)) {\n try {\n const version = (JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: string })\n .version;\n if (version) return version;\n } catch {}\n }\n const parent = dirname(currentDir);\n if (parent === currentDir) break;\n currentDir = parent;\n }\n return null;\n}\n\nfunction resolveSubpathVersion(\n request: string,\n workspaceRoot: string | undefined,\n searched: string[],\n): string | null {\n if (request.startsWith(\"@\")) {\n throw new Error(\n `Scoped-package subpath \"${request}\" is not supported — subpath version resolution ` +\n `handles unscoped specifiers only (the known subpath specs, everything-dev/ui/*, are unscoped).`,\n );\n }\n const packageName = request.split(\"/\")[0] ?? request;\n const subpath = request.split(\"/\").slice(1).join(\"/\");\n const candidates: string[] = [];\n if (workspaceRoot) {\n candidates.push(join(workspaceRoot, \"node_modules\", packageName, \"package.json\"));\n }\n const resolveRequests: Array<{ request: string; paths?: string[] }> = [\n { request: `${packageName}/${subpath}`, ...(workspaceRoot ? { paths: [workspaceRoot] } : {}) },\n { request: `${packageName}/${subpath}` },\n {\n request: `${packageName}/package.json`,\n ...(workspaceRoot ? { paths: [workspaceRoot] } : {}),\n },\n { request: `${packageName}/package.json` },\n ];\n for (const { request: target, paths } of resolveRequests) {\n try {\n const resolved = require.resolve(target, paths ? { paths } : undefined);\n searched.push(resolved);\n candidates.push(\n resolved.endsWith(\"package.json\")\n ? resolved\n : resolve(dirname(resolved), \"..\", \"..\", \"package.json\"),\n );\n } catch {\n searched.push(searchedNote(target));\n }\n }\n for (const candidate of candidates) {\n if (!existsSync(candidate)) continue;\n try {\n const version = (JSON.parse(readFileSync(candidate, \"utf8\")) as { version?: string }).version;\n if (version) return version;\n } catch {}\n }\n return null;\n}\n\nexport function resolveSharedVersion(\n name: string,\n options: SharedDependencyResolutionOptions = {},\n): string {\n const searched: string[] = [];\n const { workspaceRoot, resolution = \"package\" } = options;\n\n if (resolution === \"subpath\") {\n const version = resolveSubpathVersion(name, workspaceRoot, searched);\n if (version) return version;\n throw new SharedDependencyResolutionError(name, searched);\n }\n\n const roots: Array<string | undefined> = workspaceRoot ? [workspaceRoot, undefined] : [undefined];\n for (const root of roots) {\n try {\n const resolved = root ? require.resolve(name, { paths: [root] }) : require.resolve(name);\n searched.push(resolved);\n const version = findPackageJsonVersion(dirname(resolved), searched);\n if (version) return version;\n } catch {\n searched.push(searchedNote(name));\n }\n }\n throw new SharedDependencyResolutionError(name, searched);\n}\n\nfunction resolveOwnVersion(searched: string[]): string {\n const localPackageJson = join(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n searched.push(localPackageJson);\n if (existsSync(localPackageJson)) {\n try {\n const version = (JSON.parse(readFileSync(localPackageJson, \"utf8\")) as { version?: string })\n .version;\n if (version) return version;\n } catch {}\n }\n try {\n const resolved = require.resolve(\"every-plugin/package.json\");\n searched.push(resolved);\n const version = (JSON.parse(readFileSync(resolved, \"utf8\")) as { version?: string }).version;\n if (version) return version;\n } catch {\n searched.push(searchedNote(\"every-plugin/package.json\"));\n }\n throw new SharedDependencyResolutionError(\"every-plugin\", searched);\n}\n\nexport function shareConfigFor(\n spec: SharedDependencySpec,\n version: string,\n): Omit<SharedDependencyConfig, \"version\"> {\n return spec.critical\n ? {\n requiredVersion: version,\n singleton: true,\n strictVersion: true,\n eager: false,\n shareScope: \"default\",\n }\n : {\n requiredVersion: false,\n singleton: true,\n strictVersion: false,\n eager: false,\n shareScope: \"default\",\n };\n}\n\nexport interface SharedDepsBuildOptions {\n /** the every-plugin share version; default resolves the installed package.json */\n ownVersion?: string;\n}\n\nfunction specVersion(spec: SharedDependencySpec, options: SharedDepsBuildOptions): string {\n if (spec.resolution === \"self\") {\n return options.ownVersion ?? resolveOwnVersion([`own package.json of \"${spec.name}\"`]);\n }\n return resolveSharedVersion(spec.name, {\n resolution: spec.resolution === \"subpath\" ? \"subpath\" : \"package\",\n });\n}\n\nexport function getPluginSharedDependencies(\n options: SharedDepsBuildOptions = {},\n): SharedDependencies {\n return Object.fromEntries(\n CORE_SHARED_DEPS.map((spec) => {\n const version = specVersion(spec, options);\n return [spec.name, { version, ...shareConfigFor(spec, version) }];\n }),\n );\n}\n\nexport interface HostSharedEntry {\n version: string;\n shareScope: string;\n shareConfig: {\n singleton: boolean;\n requiredVersion: string | false;\n strictVersion: boolean;\n eager: boolean;\n };\n}\n\nexport function toHostSharedEntry(name: string, config: SharedConfigInput): HostSharedEntry {\n const normalized = normalizeSharedConfig(config, name);\n return {\n version: normalized.version,\n shareScope: normalized.shareScope,\n shareConfig: {\n singleton: normalized.singleton,\n requiredVersion: normalized.requiredVersion,\n strictVersion: normalized.strictVersion,\n eager: normalized.eager,\n },\n };\n}\n\n/**\n * The runtime mf-config's core shared map: the same entries\n * `getPluginSharedDependencies` assembles, shaped through the one host mapper\n * (`toHostSharedEntry`) so both consumers derive from a single entry type.\n */\nexport function buildMfCoreSharedDependencies(\n options: SharedDepsBuildOptions = {},\n): Record<string, HostSharedEntry> {\n return Object.fromEntries(\n Object.entries(getPluginSharedDependencies(options)).map(([name, config]) => [\n name,\n toHostSharedEntry(name, config),\n ]),\n );\n}\n\nexport interface UiSharedDepEntry {\n version: string;\n requiredVersion: string | false;\n singleton: true;\n strictVersion: boolean;\n eager: false;\n shareScope: \"default\";\n /** consumer role only: no bundled fallback copy — the provider provides */\n import?: false;\n}\n\nexport interface UiSharedDepsOptions {\n /** explicit core-shell parity opt-out; default is the strict singleton */\n strictVersion?: boolean;\n role?: \"provider\" | \"consumer\";\n workspaceRoot?: string;\n}\n\nexport function createUiSharedDeps(\n options: UiSharedDepsOptions = {},\n): Record<string, UiSharedDepEntry> {\n const deps: Record<string, UiSharedDepEntry> = {};\n for (const spec of UI_SHARED_DEPS) {\n const version = resolveSharedVersion(spec.name, {\n workspaceRoot: options.workspaceRoot,\n resolution: spec.resolution === \"subpath\" ? \"subpath\" : \"package\",\n });\n const strict = options.strictVersion !== false;\n deps[spec.name] = {\n version,\n requiredVersion: strict ? version : false,\n singleton: true,\n strictVersion: strict,\n eager: false,\n shareScope: \"default\",\n ...(options.role === \"consumer\" ? { import: false as const } : {}),\n };\n }\n return deps;\n}\n\nconst UNRESOLVED_VERSIONS = new Set([\"*\", \"latest\", \"\"]);\n\nexport function assertResolvedSharedVersion(name: string, version: unknown): string {\n if (typeof version !== \"string\" || UNRESOLVED_VERSIONS.has(version.trim())) {\n throw new SharedDependencyResolutionError(name, [\n `declared version ${JSON.stringify(version ?? null)} is not resolvable — \"*\", \"latest\", and empty fail loudly instead of disabling the strict-singleton guard`,\n ]);\n }\n return version;\n}\n\nexport function normalizeSharedConfig(\n config: SharedConfigInput,\n name: string,\n): SharedDependencyConfig {\n const version = assertResolvedSharedVersion(name, config.version);\n return {\n version,\n requiredVersion: config.requiredVersion ?? false,\n singleton: config.singleton ?? false,\n strictVersion: config.strictVersion ?? false,\n eager: config.eager ?? false,\n shareScope: config.shareScope ?? \"default\",\n };\n}\n\nfunction comparableSharedConfig(config: SharedConfigInput) {\n return {\n version: config.version,\n requiredVersion: config.requiredVersion ?? false,\n singleton: config.singleton ?? false,\n strictVersion: config.strictVersion ?? false,\n eager: config.eager ?? false,\n shareScope: config.shareScope ?? \"default\",\n };\n}\n\nexport function isSameSharedConfig(a: SharedConfigInput, b: SharedConfigInput): boolean {\n const left = comparableSharedConfig(a);\n const right = comparableSharedConfig(b);\n return (\n left.version === right.version &&\n left.requiredVersion === right.requiredVersion &&\n left.singleton === right.singleton &&\n left.strictVersion === right.strictVersion &&\n left.eager === right.eager &&\n left.shareScope === right.shareScope\n );\n}\n\nexport function mergeSharedMaps(\n ...maps: Array<Record<string, SharedConfigInput> | undefined>\n): Record<string, SharedDependencyConfig> {\n const merged: Record<string, SharedDependencyConfig> = {};\n for (const map of maps) {\n if (!map) continue;\n for (const [name, config] of Object.entries(map)) {\n const normalized = normalizeSharedConfig(config, name);\n const existing = merged[name];\n if (existing && !isSameSharedConfig(existing, normalized)) {\n throw new Error(\n `Conflicting shared dependency \"${name}\" in shared dependency configuration`,\n );\n }\n merged[name] = normalized;\n }\n }\n return merged;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-deps-spec.mjs","names":[],"sources":["../src/shared-deps-spec.ts"],"sourcesContent":["/**\n * SharedDependencySpec: the one module owning shared-dependency names,\n * criticality, version resolution, and fallback policy across the Module\n * Federation graph. Four consumers derive from it — the rspack config\n * factory (plugin builds), the rsbuild config factory (ui remotes), the\n * runtime mf-config (the every-plugin MF instance), and the host's shared\n * pre-registration — so a name or criticality can never disagree between\n * them, and an unresolved version fails the build loudly instead of\n * silently degrading to \"*\"/\"latest\" (which disables the strict-singleton\n * guard).\n *\n * Lists:\n * - core: shared by the host and server-side plugin remotes (every-plugin,\n * effect, zod, @orpc/*). Effect-critical entries pin the exact installed\n * version (strictVersion) — the version-check machinery in\n * runtime/services/shared-identity.ts relies on it.\n * - ui: shared by the core ui provider and ui remotes (react, react-dom,\n * @orpc/client, @orpc/contract, TanStack, lingui, the everything-dev\n * session/i18n subpath modules). All strict singletons; consumers add\n * `import: false`.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Data } from \"effect\";\n\nconst require = createRequire(import.meta.url);\n\nexport class SharedDependencyResolutionError extends Data.TaggedError(\n \"SharedDependencyResolutionError\",\n)<{\n readonly packageName: string;\n readonly searched: string[];\n}> {\n constructor(packageName: string, searched: string[]) {\n super({\n packageName,\n searched,\n });\n }\n\n override get message() {\n return (\n `Could not resolve shared dependency \"${this.packageName}\". Looked in:\\n` +\n this.searched.map((location) => ` - ${location}`).join(\"\\n\") +\n `\\nUnresolved shared dependencies fail loudly (no \"*\", no \"latest\" fallback) — ` +\n `install the package in the resolving workspace or pin a resolvable version.`\n );\n }\n}\n\nexport type SharedDependencyResolution = \"self\" | \"package\" | \"subpath\";\n\nexport interface SharedDependencySpec {\n name: string;\n singleton: true;\n /** exact-version pin (requiredVersion = resolved version, strictVersion = true) */\n critical: boolean;\n resolution: SharedDependencyResolution;\n}\n\nexport const CORE_SHARED_DEPS = [\n { name: \"every-plugin\", singleton: true, critical: true, resolution: \"self\" },\n { name: \"effect\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"zod\", singleton: true, critical: false, resolution: \"package\" },\n { name: \"@orpc/contract\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/client\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/server\", singleton: true, critical: true, resolution: \"package\" },\n // v1's proven sharing set, restored: @orpc/openapi, @orpc/experimental-effect,\n // and @orpc/publisher joined this list in the v2 fleet-spec pass and their\n // share-scope consumption broke plugin loading at runtime\n // (__webpack_modules__[r] is not a function — the runtime's import()-based\n // provides hand the consumer an ESM namespace where a module factory is\n // expected). They stay declared in each workspace's package.json and are\n // bundled per workspace instead.\n] as const satisfies readonly SharedDependencySpec[];\n\nexport type CoreSharedDepName = (typeof CORE_SHARED_DEPS)[number][\"name\"];\n\nconst coreCriticalNames = CORE_SHARED_DEPS.flatMap((spec) => (spec.critical ? [spec.name] : []));\n\n/**\n * Effect-critical shared names, derived from the core list's `critical: true`\n * entries — the one source of criticality (`strictVersion` pinning in the\n * runtime's version-check machinery keys off this list).\n */\nexport const EFFECT_CRITICAL_SHARED_DEPS: readonly EffectCriticalSharedDepName[] =\n coreCriticalNames;\n\nexport type EffectCriticalSharedDepName = (typeof coreCriticalNames)[number];\n\nexport const isEffectCriticalSharedDep = (name: string): boolean =>\n (EFFECT_CRITICAL_SHARED_DEPS as readonly string[]).includes(name);\n\nexport const UI_SHARED_DEPS = [\n { name: \"react\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"react-dom\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/client\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@orpc/contract\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@tanstack/react-query\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@tanstack/react-router\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@lingui/core\", singleton: true, critical: true, resolution: \"package\" },\n { name: \"@lingui/react\", singleton: true, critical: true, resolution: \"package\" },\n {\n name: \"everything-dev/ui/auth\",\n singleton: true,\n critical: true,\n resolution: \"subpath\",\n },\n {\n name: \"everything-dev/ui/i18n\",\n singleton: true,\n critical: true,\n resolution: \"subpath\",\n },\n] as const satisfies readonly SharedDependencySpec[];\n\nexport const SHARED_DEP_SPECS: readonly SharedDependencySpec[] = (() => {\n const byName = new Map<string, SharedDependencySpec>();\n for (const spec of [...CORE_SHARED_DEPS, ...UI_SHARED_DEPS]) {\n const existing = byName.get(spec.name);\n if (existing) {\n if (existing.critical !== spec.critical) {\n throw new Error(\n `Shared dependency spec conflict for \"${spec.name}\": criticality disagrees between the core and ui lists`,\n );\n }\n continue;\n }\n byName.set(spec.name, spec);\n }\n return [...byName.values()];\n})();\n\nexport interface SharedDependencyConfig {\n version: string;\n requiredVersion: string | false;\n singleton: boolean;\n strictVersion: boolean;\n eager: boolean;\n shareScope: string;\n}\n\nexport type SharedDependencies = Record<string, SharedDependencyConfig>;\n\nexport type SharedConfigInput = {\n version: string;\n requiredVersion?: string | false;\n singleton?: boolean;\n strictVersion?: boolean;\n eager?: boolean;\n shareScope?: string;\n};\n\nexport interface SharedDependencyResolutionOptions {\n /** the building workspace to resolve from; default is this module's own location */\n workspaceRoot?: string;\n resolution?: \"package\" | \"subpath\";\n}\n\nfunction searchedNote(target: string): string {\n return `require.resolve(\"${target}\")`;\n}\n\nfunction findPackageJsonVersion(startDir: string, searched: string[]): string | null {\n let currentDir = startDir;\n for (let i = 0; i < 5; i += 1) {\n const packageJsonPath = join(currentDir, \"package.json\");\n searched.push(packageJsonPath);\n if (existsSync(packageJsonPath)) {\n try {\n const version = (JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: string })\n .version;\n if (version) return version;\n } catch {}\n }\n const parent = dirname(currentDir);\n if (parent === currentDir) break;\n currentDir = parent;\n }\n return null;\n}\n\nfunction resolveSubpathVersion(\n request: string,\n workspaceRoot: string | undefined,\n searched: string[],\n): string | null {\n if (request.startsWith(\"@\")) {\n throw new Error(\n `Scoped-package subpath \"${request}\" is not supported — subpath version resolution ` +\n `handles unscoped specifiers only (the known subpath specs, everything-dev/ui/*, are unscoped).`,\n );\n }\n const packageName = request.split(\"/\")[0] ?? request;\n const subpath = request.split(\"/\").slice(1).join(\"/\");\n const candidates: string[] = [];\n if (workspaceRoot) {\n candidates.push(join(workspaceRoot, \"node_modules\", packageName, \"package.json\"));\n }\n const resolveRequests: Array<{ request: string; paths?: string[] }> = [\n { request: `${packageName}/${subpath}`, ...(workspaceRoot ? { paths: [workspaceRoot] } : {}) },\n { request: `${packageName}/${subpath}` },\n {\n request: `${packageName}/package.json`,\n ...(workspaceRoot ? { paths: [workspaceRoot] } : {}),\n },\n { request: `${packageName}/package.json` },\n ];\n for (const { request: target, paths } of resolveRequests) {\n try {\n const resolved = require.resolve(target, paths ? { paths } : undefined);\n searched.push(resolved);\n candidates.push(\n resolved.endsWith(\"package.json\")\n ? resolved\n : resolve(dirname(resolved), \"..\", \"..\", \"package.json\"),\n );\n } catch {\n searched.push(searchedNote(target));\n }\n }\n for (const candidate of candidates) {\n if (!existsSync(candidate)) continue;\n try {\n const version = (JSON.parse(readFileSync(candidate, \"utf8\")) as { version?: string }).version;\n if (version) return version;\n } catch {}\n }\n return null;\n}\n\nexport function resolveSharedVersion(\n name: string,\n options: SharedDependencyResolutionOptions = {},\n): string {\n const searched: string[] = [];\n const { workspaceRoot, resolution = \"package\" } = options;\n\n if (resolution === \"subpath\") {\n const version = resolveSubpathVersion(name, workspaceRoot, searched);\n if (version) return version;\n throw new SharedDependencyResolutionError(name, searched);\n }\n\n const roots: Array<string | undefined> = workspaceRoot ? [workspaceRoot, undefined] : [undefined];\n for (const root of roots) {\n try {\n const resolved = root ? require.resolve(name, { paths: [root] }) : require.resolve(name);\n searched.push(resolved);\n const version = findPackageJsonVersion(dirname(resolved), searched);\n if (version) return version;\n } catch {\n searched.push(searchedNote(name));\n }\n }\n throw new SharedDependencyResolutionError(name, searched);\n}\n\nfunction resolveOwnVersion(searched: string[]): string {\n const localPackageJson = join(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n searched.push(localPackageJson);\n if (existsSync(localPackageJson)) {\n try {\n const version = (JSON.parse(readFileSync(localPackageJson, \"utf8\")) as { version?: string })\n .version;\n if (version) return version;\n } catch {}\n }\n try {\n const resolved = require.resolve(\"every-plugin/package.json\");\n searched.push(resolved);\n const version = (JSON.parse(readFileSync(resolved, \"utf8\")) as { version?: string }).version;\n if (version) return version;\n } catch {\n searched.push(searchedNote(\"every-plugin/package.json\"));\n }\n throw new SharedDependencyResolutionError(\"every-plugin\", searched);\n}\n\nexport function shareConfigFor(\n spec: SharedDependencySpec,\n version: string,\n): Omit<SharedDependencyConfig, \"version\"> {\n return spec.critical\n ? {\n requiredVersion: version,\n singleton: true,\n strictVersion: true,\n eager: false,\n shareScope: \"default\",\n }\n : {\n requiredVersion: false,\n singleton: true,\n strictVersion: false,\n eager: false,\n shareScope: \"default\",\n };\n}\n\nexport interface SharedDepsBuildOptions {\n /** the every-plugin share version; default resolves the installed package.json */\n ownVersion?: string;\n}\n\nfunction specVersion(spec: SharedDependencySpec, options: SharedDepsBuildOptions): string {\n if (spec.resolution === \"self\") {\n return options.ownVersion ?? resolveOwnVersion([`own package.json of \"${spec.name}\"`]);\n }\n return resolveSharedVersion(spec.name, {\n resolution: spec.resolution === \"subpath\" ? \"subpath\" : \"package\",\n });\n}\n\nexport function getPluginSharedDependencies(\n options: SharedDepsBuildOptions = {},\n): SharedDependencies {\n return Object.fromEntries(\n CORE_SHARED_DEPS.map((spec) => {\n const version = specVersion(spec, options);\n return [spec.name, { version, ...shareConfigFor(spec, version) }];\n }),\n );\n}\n\nexport interface HostSharedEntry {\n version: string;\n shareScope: string;\n shareConfig: {\n singleton: boolean;\n requiredVersion: string | false;\n strictVersion: boolean;\n eager: boolean;\n };\n}\n\nexport function toHostSharedEntry(name: string, config: SharedConfigInput): HostSharedEntry {\n const normalized = normalizeSharedConfig(config, name);\n return {\n version: normalized.version,\n shareScope: normalized.shareScope,\n shareConfig: {\n singleton: normalized.singleton,\n requiredVersion: normalized.requiredVersion,\n strictVersion: normalized.strictVersion,\n eager: normalized.eager,\n },\n };\n}\n\n/**\n * The runtime mf-config's core shared map: the same entries\n * `getPluginSharedDependencies` assembles, shaped through the one host mapper\n * (`toHostSharedEntry`) so both consumers derive from a single entry type.\n */\nexport function buildMfCoreSharedDependencies(\n options: SharedDepsBuildOptions = {},\n): Record<string, HostSharedEntry> {\n return Object.fromEntries(\n Object.entries(getPluginSharedDependencies(options)).map(([name, config]) => [\n name,\n toHostSharedEntry(name, config),\n ]),\n );\n}\n\nexport interface UiSharedDepEntry {\n version: string;\n requiredVersion: string | false;\n singleton: true;\n strictVersion: boolean;\n eager: false;\n shareScope: \"default\";\n /** consumer role only: no bundled fallback copy — the provider provides */\n import?: false;\n}\n\nexport interface UiSharedDepsOptions {\n /** explicit core-shell parity opt-out; default is the strict singleton */\n strictVersion?: boolean;\n role?: \"provider\" | \"consumer\";\n workspaceRoot?: string;\n}\n\nexport function createUiSharedDeps(\n options: UiSharedDepsOptions = {},\n): Record<string, UiSharedDepEntry> {\n const deps: Record<string, UiSharedDepEntry> = {};\n for (const spec of UI_SHARED_DEPS) {\n const version = resolveSharedVersion(spec.name, {\n workspaceRoot: options.workspaceRoot,\n resolution: spec.resolution === \"subpath\" ? \"subpath\" : \"package\",\n });\n const strict = options.strictVersion !== false;\n deps[spec.name] = {\n version,\n requiredVersion: strict ? version : false,\n singleton: true,\n strictVersion: strict,\n eager: false,\n shareScope: \"default\",\n ...(options.role === \"consumer\" ? { import: false as const } : {}),\n };\n }\n return deps;\n}\n\nconst UNRESOLVED_VERSIONS = new Set([\"*\", \"latest\", \"\"]);\n\nexport function assertResolvedSharedVersion(name: string, version: unknown): string {\n if (typeof version !== \"string\" || UNRESOLVED_VERSIONS.has(version.trim())) {\n throw new SharedDependencyResolutionError(name, [\n `declared version ${JSON.stringify(version ?? null)} is not resolvable — \"*\", \"latest\", and empty fail loudly instead of disabling the strict-singleton guard`,\n ]);\n }\n return version;\n}\n\nexport function normalizeSharedConfig(\n config: SharedConfigInput,\n name: string,\n): SharedDependencyConfig {\n const version = assertResolvedSharedVersion(name, config.version);\n return {\n version,\n requiredVersion: config.requiredVersion ?? false,\n singleton: config.singleton ?? false,\n strictVersion: config.strictVersion ?? false,\n eager: config.eager ?? false,\n shareScope: config.shareScope ?? \"default\",\n };\n}\n\nfunction comparableSharedConfig(config: SharedConfigInput) {\n return {\n version: config.version,\n requiredVersion: config.requiredVersion ?? false,\n singleton: config.singleton ?? false,\n strictVersion: config.strictVersion ?? false,\n eager: config.eager ?? false,\n shareScope: config.shareScope ?? \"default\",\n };\n}\n\nexport function isSameSharedConfig(a: SharedConfigInput, b: SharedConfigInput): boolean {\n const left = comparableSharedConfig(a);\n const right = comparableSharedConfig(b);\n return (\n left.version === right.version &&\n left.requiredVersion === right.requiredVersion &&\n left.singleton === right.singleton &&\n left.strictVersion === right.strictVersion &&\n left.eager === right.eager &&\n left.shareScope === right.shareScope\n );\n}\n\nexport function mergeSharedMaps(\n ...maps: Array<Record<string, SharedConfigInput> | undefined>\n): Record<string, SharedDependencyConfig> {\n const merged: Record<string, SharedDependencyConfig> = {};\n for (const map of maps) {\n if (!map) continue;\n for (const [name, config] of Object.entries(map)) {\n const normalized = normalizeSharedConfig(config, name);\n const existing = merged[name];\n if (existing && !isSameSharedConfig(existing, normalized)) {\n throw new Error(\n `Conflicting shared dependency \"${name}\" in shared dependency configuration`,\n );\n }\n merged[name] = normalized;\n }\n }\n return merged;\n}\n\nexport interface SharedVersionRecordEntry {\n version: string;\n singleton?: boolean;\n}\n\n/**\n * The `name → version` record of a build's shared-singleton map, for the\n * build report (and from there the version manifest's `shared` field) —\n * #546: a slot's pin must carry shared-dep semantics so consumers can detect\n * a build skew before loading. Singletons only — non-singletons never take\n * part in the negotiation a skew breaks.\n */\nexport function sharedVersionRecord(\n shared: Record<string, SharedVersionRecordEntry>,\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [name, config] of Object.entries(shared)) {\n if (config.singleton !== false && config.version) {\n out[name] = config.version;\n }\n }\n return out;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "every-plugin",
3
- "version": "3.0.0-rc.11",
3
+ "version": "3.0.0-rc.12",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -75,6 +75,14 @@ export const BuildEntryReportSchema = z.object({
75
75
  entry: z.string().min(1),
76
76
  browserManifest: z.string().min(1).optional(),
77
77
  css: z.string().min(1).optional(),
78
+ /**
79
+ * Shared-singleton `name → version` this build embedded — resolved from the
80
+ * building workspace's node_modules at config time and stamped by both the
81
+ * rspack and rsbuild build paths. The deploy leg records it into the
82
+ * workspace's version manifest, so a slot's pin carries shared-dep
83
+ * semantics and consumers can detect a build skew before loading (#546).
84
+ */
85
+ shared: z.record(z.string(), z.string()).optional(),
78
86
  });
79
87
  export type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;
80
88
 
@@ -1,6 +1,16 @@
1
1
  import type { Compiler, RspackPluginInstance } from "@rspack/core";
2
2
  import { BuildEntryReportSchema, findHashedEntry } from "../artifact-names";
3
3
 
4
+ export interface BuildReportOptions {
5
+ entryBase?: string;
6
+ /**
7
+ * The shared-singleton `name → version` map this build embedded — recorded
8
+ * in build-report.json so the deploy leg's version manifest carries
9
+ * shared-dep semantics (#546).
10
+ */
11
+ shared?: Record<string, string>;
12
+ }
13
+
4
14
  /**
5
15
  * Emits `build-report.json` for the deploy leg: the container entry is hashed
6
16
  * by the build's filename template (`remoteEntry.[contenthash].js`), and the
@@ -11,7 +21,10 @@ import { BuildEntryReportSchema, findHashedEntry } from "../artifact-names";
11
21
  export class BuildReportPlugin implements RspackPluginInstance {
12
22
  name = "BuildReportPlugin";
13
23
 
14
- constructor(private entryBase = "remoteEntry") {}
24
+ constructor(
25
+ private entryBase = "remoteEntry",
26
+ private options: BuildReportOptions = {},
27
+ ) {}
15
28
 
16
29
  apply(compiler: Compiler) {
17
30
  compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
@@ -20,10 +33,14 @@ export class BuildReportPlugin implements RspackPluginInstance {
20
33
  const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;
21
34
 
22
35
  compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {
23
- const entry = findHashedEntry(Object.keys(assets), this.entryBase);
36
+ const entryBase = this.options.entryBase ?? this.entryBase;
37
+ const entry = findHashedEntry(Object.keys(assets), entryBase);
24
38
  if (!entry || !rawSource) return;
25
39
 
26
- const report = BuildEntryReportSchema.parse({ entry });
40
+ const report = BuildEntryReportSchema.parse({
41
+ entry,
42
+ ...(this.options.shared ? { shared: this.options.shared } : {}),
43
+ });
27
44
  compilation.emitAsset(
28
45
  "build-report.json",
29
46
  new rawSource(`${JSON.stringify(report, null, 2)}\n`),
@@ -3,6 +3,7 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { ModuleFederationPlugin } from "@module-federation/enhanced/rspack";
5
5
  import type { Compiler, RspackPluginInstance } from "@rspack/core";
6
+ import { sharedVersionRecord } from "../../shared-deps-spec";
6
7
  import {
7
8
  DEV_ENTRY_FILENAME,
8
9
  findHashedEntry,
@@ -128,6 +129,7 @@ export class EveryPluginBuild implements RspackPluginInstance {
128
129
 
129
130
  this.configureDefaults(compiler, pluginInfo);
130
131
 
132
+ const sharedDependencies = buildSharedDependencies(pluginInfo);
131
133
  new ModuleFederationPlugin({
132
134
  name: pluginInfo.normalizedName,
133
135
  filename: uiEntryFilename({ isBuild: isBuildInvocation() }),
@@ -138,11 +140,13 @@ export class EveryPluginBuild implements RspackPluginInstance {
138
140
  exposes: {
139
141
  "./plugin": "./src/index.ts",
140
142
  },
141
- shared: buildSharedDependencies(pluginInfo),
143
+ shared: sharedDependencies,
142
144
  shareStrategy: "version-first",
143
145
  }).apply(compiler);
144
146
 
145
- new BuildReportPlugin().apply(compiler);
147
+ new BuildReportPlugin("remoteEntry", {
148
+ shared: sharedVersionRecord(sharedDependencies),
149
+ }).apply(compiler);
146
150
  new ChunkCompletenessPlugin().apply(compiler);
147
151
 
148
152
  if (this.options.dts === false) {
@@ -14,6 +14,12 @@ export interface HashArtifactsPluginOptions {
14
14
  entryBase: string;
15
15
  /** Absolute dist root this environment wrote (web: `dist`, node: `dist/ssr`). */
16
16
  distRoot: string;
17
+ /**
18
+ * The shared-singleton `name → version` map this build embedded — recorded
19
+ * in build-report.json so the version manifest carries shared-dep
20
+ * semantics (#546).
21
+ */
22
+ shared?: Record<string, string>;
17
23
  }
18
24
 
19
25
  function listFiles(dir: string, out: string[] = []): string[] {
@@ -71,7 +77,14 @@ export function hashArtifactsPlugin(options: HashArtifactsPluginOptions): Rsbuil
71
77
  }
72
78
  fs.writeFileSync(
73
79
  path.join(distRoot, "build-report.json"),
74
- `${JSON.stringify(BuildEntryReportSchema.parse(plan.report), null, 2)}\n`,
80
+ `${JSON.stringify(
81
+ BuildEntryReportSchema.parse({
82
+ ...plan.report,
83
+ ...(options.shared ? { shared: options.shared } : {}),
84
+ }),
85
+ null,
86
+ 2,
87
+ )}\n`,
75
88
  );
76
89
  });
77
90
  },
@@ -4,7 +4,7 @@ import { defineConfig, type EnvironmentConfig, type RsbuildConfig, rspack } from
4
4
  import { pluginReact } from "@rsbuild/plugin-react";
5
5
  import { TanStackRouterRspack } from "@tanstack/router-plugin/rspack";
6
6
  import { FixMfDataUriPlugin } from "../../build/rspack";
7
- import { createUiSharedDeps } from "../../shared-deps-spec";
7
+ import { createUiSharedDeps, sharedVersionRecord } from "../../shared-deps-spec";
8
8
  import { sanitizeContainerName } from "../../ui/manifest/contract";
9
9
  import { isBuildInvocation, uiEntryFilename } from "../artifact-names";
10
10
  import { hashArtifactsPlugin } from "./hash-artifacts-plugin";
@@ -79,6 +79,7 @@ export function createUiRsbuildConfig(options: UiRsbuildConfigOptions): RsbuildC
79
79
  const manifestGen = () =>
80
80
  uiManifestGenPlugin({ workspaceRoot: workspaceRootAbsolute, pluginName: manifestName });
81
81
  const uiSharedDeps = createUiSharedDeps({ role, workspaceRoot: workspaceRootAbsolute });
82
+ const uiSharedVersions = sharedVersionRecord(uiSharedDeps);
82
83
 
83
84
  const isBuild = isBuildInvocation();
84
85
 
@@ -101,6 +102,7 @@ export function createUiRsbuildConfig(options: UiRsbuildConfigOptions): RsbuildC
101
102
  hashArtifactsPlugin({
102
103
  entryBase: "remoteEntry",
103
104
  distRoot: path.join(workspaceRootAbsolute, "dist"),
105
+ shared: uiSharedVersions,
104
106
  }),
105
107
  ]
106
108
  : []),
@@ -190,6 +192,7 @@ export function createUiRsbuildConfig(options: UiRsbuildConfigOptions): RsbuildC
190
192
  hashArtifactsPlugin({
191
193
  entryBase: "remoteEntry.server",
192
194
  distRoot: path.resolve(workspaceRootAbsolute, "dist", "ssr"),
195
+ shared: uiSharedVersions,
193
196
  }),
194
197
  ]
195
198
  : []),
@@ -476,3 +476,27 @@ export function mergeSharedMaps(
476
476
  }
477
477
  return merged;
478
478
  }
479
+
480
+ export interface SharedVersionRecordEntry {
481
+ version: string;
482
+ singleton?: boolean;
483
+ }
484
+
485
+ /**
486
+ * The `name → version` record of a build's shared-singleton map, for the
487
+ * build report (and from there the version manifest's `shared` field) —
488
+ * #546: a slot's pin must carry shared-dep semantics so consumers can detect
489
+ * a build skew before loading. Singletons only — non-singletons never take
490
+ * part in the negotiation a skew breaks.
491
+ */
492
+ export function sharedVersionRecord(
493
+ shared: Record<string, SharedVersionRecordEntry>,
494
+ ): Record<string, string> {
495
+ const out: Record<string, string> = {};
496
+ for (const [name, config] of Object.entries(shared)) {
497
+ if (config.singleton !== false && config.version) {
498
+ out[name] = config.version;
499
+ }
500
+ }
501
+ return out;
502
+ }