@fulgurjs/federation 5.9.3 → 6.1.0

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 (104) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.en.md +57 -416
  3. package/README.md +56 -446
  4. package/dist/bridge-app-vue.d.cts +1 -1
  5. package/dist/bridge-app-vue.d.ts +1 -1
  6. package/dist/bridge-core.cjs +1 -1
  7. package/dist/bridge-core.js +2 -2
  8. package/dist/bridge-errors.cjs +1 -1
  9. package/dist/bridge-errors.js +1 -1
  10. package/dist/bridge-host-react.cjs +1 -1
  11. package/dist/bridge-host-react.js +3 -3
  12. package/dist/bridge-host-vue.cjs +1 -1
  13. package/dist/bridge-host-vue.js +3 -3
  14. package/dist/bridge-router-react.d.ts +22 -3
  15. package/dist/bridge-router-react.js +107 -9
  16. package/dist/bridge-router-vue.d.ts +24 -5
  17. package/dist/bridge-router-vue.js +4 -5
  18. package/dist/{chunk-YKIFZIBH.js → chunk-77JZ6U6V.js} +1 -1
  19. package/dist/{chunk-Q6V5O5Y6.js → chunk-ENT3BOTX.js} +1 -1
  20. package/dist/{chunk-WRJG2YG6.js → chunk-ZAYNS7YQ.js} +1 -1
  21. package/dist/cli.js +460 -109
  22. package/dist/index.cjs +25 -38
  23. package/dist/index.js +25 -38
  24. package/dist/react-adapter.d.cts +7 -0
  25. package/dist/react-adapter.d.ts +7 -0
  26. package/dist/react.d.ts +229 -101
  27. package/dist/react.js +4 -0
  28. package/dist/runtime-entry.d.ts +27 -174
  29. package/dist/runtime-entry.js +0 -1
  30. package/dist/runtime.js +19 -17
  31. package/dist/vue-adapter.cjs +13 -1
  32. package/dist/vue-adapter.d.cts +1 -0
  33. package/dist/vue-adapter.d.ts +1 -0
  34. package/dist/vue-adapter.js +14 -2
  35. package/dist/vue.d.ts +594 -0
  36. package/dist/vue.js +8 -0
  37. package/docs/en/migration.md +210 -0
  38. package/docs/en/reference/api.md +555 -0
  39. package/docs/en/reference/errors.md +86 -0
  40. package/docs/{P5-vite7-8 → maintainers/P5-vite7-8}/345/205/274/345/256/271/347/237/251/351/230/265.md +7 -9
  41. package/docs/{webpack-mf- → maintainers/webpack-mf-}/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +10 -10
  42. package/docs/maintainers//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +43 -0
  43. package/docs/zh/migration.md +210 -0
  44. package/docs/zh/reference/api.md +551 -0
  45. package/docs/zh/reference/errors.md +86 -0
  46. package/examples/templates/README.md +1 -1
  47. package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +7 -7
  48. package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +1 -1
  49. package/examples/templates/react-host-vue-remote/react-host/README.md +2 -2
  50. package/examples/templates/react-host-vue-remote/react-host/package.json +1 -1
  51. package/examples/templates/react-host-vue-remote/react-host/src/main.tsx +2 -2
  52. package/examples/templates/react-host-vue-remote/scripts/dev.config.json +11 -2
  53. package/examples/templates/react-host-vue-remote/vue-remote/package.json +1 -1
  54. package/examples/templates/react-host-vue-remote/vue-remote/src/bridge.ts +1 -1
  55. package/examples/templates/react-react/host/package.json +1 -1
  56. package/examples/templates/react-react/pnpm-lock.yaml +7 -7
  57. package/examples/templates/react-react/pnpm-workspace.yaml +1 -1
  58. package/examples/templates/react-react/remote/package.json +1 -1
  59. package/examples/templates/react-react/scripts/dev.config.json +11 -2
  60. package/examples/templates/showcase/README.md +6 -6
  61. package/examples/templates/showcase/pnpm-lock.yaml +11 -11
  62. package/examples/templates/showcase/pnpm-workspace.yaml +1 -1
  63. package/examples/templates/showcase/react-host/package.json +1 -1
  64. package/examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx +1 -1
  65. package/examples/templates/showcase/react-host/src/routing.ts +1 -1
  66. package/examples/templates/showcase/react-remote/package.json +1 -1
  67. package/examples/templates/showcase/react-remote/src/bridge.tsx +1 -1
  68. package/examples/templates/showcase/scripts/dev.config.json +22 -4
  69. package/examples/templates/showcase/vue-host/package.json +1 -1
  70. package/examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue +1 -1
  71. package/examples/templates/showcase/vue-host/src/routing.ts +3 -13
  72. package/examples/templates/showcase/vue-remote/package.json +1 -1
  73. package/examples/templates/showcase/vue-remote/src/bridge.ts +2 -2
  74. package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +7 -7
  75. package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +1 -1
  76. package/examples/templates/vue-host-react-remote/react-remote/package.json +1 -1
  77. package/examples/templates/vue-host-react-remote/scripts/dev.config.json +11 -2
  78. package/examples/templates/vue-host-react-remote/vue-host/README.md +2 -2
  79. package/examples/templates/vue-host-react-remote/vue-host/package.json +1 -1
  80. package/examples/templates/vue-host-react-remote/vue-host/src/App.vue +2 -2
  81. package/examples/templates/vue-vue/host/package.json +1 -1
  82. package/examples/templates/vue-vue/host/src/main.ts +28 -21
  83. package/examples/templates/vue-vue/host/src/pages/HomePage.vue +1 -1
  84. package/examples/templates/vue-vue/pnpm-lock.yaml +7 -7
  85. package/examples/templates/vue-vue/pnpm-workspace.yaml +1 -1
  86. package/examples/templates/vue-vue/remote/package.json +1 -1
  87. package/examples/templates/vue-vue/scripts/dev.config.json +11 -2
  88. package/package.json +24 -37
  89. package/dist/bridge-core-D37VanBl.d.ts +0 -99
  90. package/dist/bridge-host-react-CGymCzD2.d.ts +0 -35
  91. package/dist/bridge-host-vue-B3GHaQAC.d.ts +0 -32
  92. package/dist/bridge-react.d.ts +0 -14
  93. package/dist/bridge-react.js +0 -3
  94. package/dist/bridge-vue.d.ts +0 -8
  95. package/dist/bridge-vue.js +0 -3
  96. package/dist/bridge.d.ts +0 -18
  97. package/dist/bridge.js +0 -5
  98. package/dist/chunk-GLASM5EX.js +0 -1730
  99. package/dist/chunk-V6EASSCR.js +0 -249
  100. package/dist/chunk-X34EJB4H.js +0 -207
  101. package/docs/API.en.md +0 -339
  102. package/docs/API.md +0 -914
  103. package/docs//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +0 -45
  104. package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +0 -179
package/dist/cli.js CHANGED
@@ -215,18 +215,18 @@ function validateHostPages(value) {
215
215
  }
216
216
  return hp;
217
217
  }
218
- function validatePathsInsideApp(options, appRoot, fs4, path3) {
218
+ function validatePathsInsideApp(options, appRoot, fs5, path4) {
219
219
  const checkModule = (field, value) => {
220
220
  if (typeof value !== "string" || value.trim() === "") return;
221
- if (/^(https?:)?\/\//.test(value) || path3.isAbsolute(value)) {
221
+ if (/^(https?:)?\/\//.test(value) || path4.isAbsolute(value)) {
222
222
  appConfigError(
223
223
  `${field} "${value}" \u4E0D\u662F\u672C\u9879\u76EE\u5185\u7684\u6A21\u5757\u8DEF\u5F84`,
224
224
  "exposes/setup \u7684\u503C\u662F\u76F8\u5BF9\u672C\u5E94\u7528\u6839\u76EE\u5F55\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84\uFF08URL/\u7EDD\u5BF9\u8DEF\u5F84\u65E0\u6CD5\u5728\u72EC\u7ACB\u4ED3\u5E93\u95F4\u79FB\u690D\uFF09",
225
225
  `\u6539\u4E3A\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5982 './src/views/Home.vue'\uFF08\u914D\u7F6E\u6587\u4EF6\u76EE\u5F55 = \u5E94\u7528\u6839 ${appRoot}\uFF09`
226
226
  );
227
227
  }
228
- const abs = path3.resolve(appRoot, value);
229
- if (!fs4.existsSync(abs)) {
228
+ const abs = path4.resolve(appRoot, value);
229
+ if (!fs5.existsSync(abs)) {
230
230
  appConfigError(
231
231
  `${field} \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${value}`,
232
232
  `\u6309\u5E94\u7528\u6839 ${appRoot} \u89E3\u6790\u540E\u672A\u627E\u5230\u8BE5\u6587\u4EF6`,
@@ -295,11 +295,11 @@ function resolveEsbuild(configPath) {
295
295
  );
296
296
  }
297
297
  async function loadAppConfig(configPath) {
298
- const fs4 = await import("node:fs");
298
+ const fs5 = await import("node:fs");
299
299
  const os = await import("node:os");
300
- const path3 = await import("node:path");
301
- const abs = path3.resolve(configPath);
302
- if (!fs4.existsSync(abs)) {
300
+ const path4 = await import("node:path");
301
+ const abs = path4.resolve(configPath);
302
+ if (!fs5.existsSync(abs)) {
303
303
  appConfigError(
304
304
  `\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${abs}`,
305
305
  "--config \u6307\u5411\u7684\u8DEF\u5F84\u5728\u672C\u673A\u4E0D\u5B58\u5728\uFF08\u76F8\u5BF9\u8DEF\u5F84\u4EE5 CLI \u8FDB\u7A0B cwd \u89E3\u6790\uFF09",
@@ -307,11 +307,11 @@ async function loadAppConfig(configPath) {
307
307
  );
308
308
  }
309
309
  if (abs.endsWith(".json")) {
310
- const parsed = JSON.parse(fs4.readFileSync(abs, "utf8"));
310
+ const parsed = JSON.parse(fs5.readFileSync(abs, "utf8"));
311
311
  if (isAggregateShape(parsed)) aggregateMigrationError();
312
- return finishAppShape(parsed, {}, abs, path3.dirname(abs), fs4, path3);
312
+ return finishAppShape(parsed, {}, abs, path4.dirname(abs), fs5, path4);
313
313
  }
314
- const rawSource = fs4.readFileSync(abs, "utf8");
314
+ const rawSource = fs5.readFileSync(abs, "utf8");
315
315
  if (/@fulgurjs\/federation\/config/.test(rawSource)) {
316
316
  appConfigError(
317
317
  `\u914D\u7F6E\u5BFC\u5165\u4E86\u5DF2\u5220\u9664\u7684\u5B50\u8DEF\u5F84 "@fulgurjs/federation/config"`,
@@ -342,21 +342,21 @@ async function loadAppConfig(configPath) {
342
342
  plugins: [selfRedirect],
343
343
  logLevel: "silent"
344
344
  });
345
- const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "fulgurjs-appcfg-"));
345
+ const tmpDir = fs5.mkdtempSync(path4.join(os.tmpdir(), "fulgurjs-appcfg-"));
346
346
  try {
347
- const bundleFile = path3.join(tmpDir, "fulgurjs.config.bundle.mjs");
348
- fs4.writeFileSync(bundleFile, out.outputFiles[0].text);
347
+ const bundleFile = path4.join(tmpDir, "fulgurjs.config.bundle.mjs");
348
+ fs5.writeFileSync(bundleFile, out.outputFiles[0].text);
349
349
  const mod = await import(
350
350
  /* @vite-ignore */
351
351
  `${pathToFileURL(bundleFile).href}?t=${Date.now()}`
352
352
  );
353
353
  if (isAggregateShape(mod.default)) aggregateMigrationError();
354
- return finishAppShape(mod.default, { hostPages: mod.hostPages }, abs, path3.dirname(abs), fs4, path3);
354
+ return finishAppShape(mod.default, { hostPages: mod.hostPages }, abs, path4.dirname(abs), fs5, path4);
355
355
  } finally {
356
- fs4.rmSync(tmpDir, { recursive: true, force: true });
356
+ fs5.rmSync(tmpDir, { recursive: true, force: true });
357
357
  }
358
358
  }
359
- function finishAppShape(def, named, configPath, appRoot, fs4, path3) {
359
+ function finishAppShape(def, named, configPath, appRoot, fs5, path4) {
360
360
  if (def === void 0 || def === null) {
361
361
  appConfigError(
362
362
  "fulgurjs.config.ts \u6CA1\u6709\u53EF\u7528\u7684\u9ED8\u8BA4\u5BFC\u51FA",
@@ -369,7 +369,7 @@ function finishAppShape(def, named, configPath, appRoot, fs4, path3) {
369
369
  }
370
370
  const options = def;
371
371
  validateAppOptions(options);
372
- validatePathsInsideApp(options, appRoot, fs4, path3);
372
+ validatePathsInsideApp(options, appRoot, fs5, path4);
373
373
  const hostPages = named.hostPages !== void 0 ? validateHostPages(named.hostPages) : void 0;
374
374
  return {
375
375
  kind: "app",
@@ -396,18 +396,82 @@ var init_app_config = __esm({
396
396
  // src/init.ts
397
397
  var init_exports = {};
398
398
  __export(init_exports, {
399
+ COMMON_CHECKLIST: () => COMMON_CHECKLIST,
399
400
  STARTER_CONFIG: () => STARTER_CONFIG,
401
+ checklistFor: () => checklistFor,
402
+ detectFrameworkFromPackageJson: () => detectFrameworkFromPackageJson,
400
403
  inspectConfig: () => inspectConfig,
404
+ starterConfigFor: () => starterConfigFor,
401
405
  writeConfigTemplate: () => writeConfigTemplate
402
406
  });
403
407
  import fs from "node:fs";
404
408
  import path from "node:path";
405
- async function writeConfigTemplate(target, force = false) {
409
+ function detectFrameworkFromPackageJson(pkg) {
410
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
411
+ const hasVue = "vue" in deps;
412
+ const hasReact = "react" in deps;
413
+ if (hasVue && !hasReact) return "vue";
414
+ if (hasReact && !hasVue) return "react";
415
+ return null;
416
+ }
417
+ function sharedExample(framework) {
418
+ if (framework === "vue") {
419
+ return ` // \u8DE8\u5E94\u7528\u5171\u4EAB\u7684\u5E93 singleton: true\uFF08\u5168\u5C40\u54CD\u5E94\u6027/\u8DEF\u7531/store \u6CE8\u5165\u8981\u6C42\u540C\u5B9E\u4F8B\uFF09\u3002
420
+ // \u53EA\u5217\u9879\u76EE\u5B9E\u9645\u5B89\u88C5\u4E14\u8981\u8DE8\u5E94\u7528\u5171\u4EAB\u7684\u5E93\uFF1BrequiredVersion \u6309\u9879\u76EE\u5B9E\u9645\u7248\u672C\u6536\u655B\uFF08\u53EF\u7701\u7565\uFF09\u3002
421
+ shared: {
422
+ vue: { singleton: true },
423
+ },`;
424
+ }
425
+ return ` // \u8DE8\u5E94\u7528\u5171\u4EAB\u7684\u5E93 singleton: true\uFF08Hooks \u72B6\u6001/renderer \u8981\u6C42\u540C\u5B9E\u4F8B\uFF09\u3002
426
+ // \u53EA\u5217\u9879\u76EE\u5B9E\u9645\u5B89\u88C5\u4E14\u8981\u8DE8\u5E94\u7528\u5171\u4EAB\u7684\u5E93\uFF1BrequiredVersion \u6309\u9879\u76EE\u5B9E\u9645\u7248\u672C\u6536\u655B\uFF08\u53EF\u7701\u7565\uFF09\u3002
427
+ // react-dom \u4E5F\u6309\u9700\u5171\u4EAB\uFF08\u8FDC\u7A0B\u6E32\u67D3\u8FDB\u5BBF\u4E3B DOM \u65F6\u5FC5\u987B\u540C renderer\uFF09\u3002
428
+ shared: {
429
+ react: { singleton: true },
430
+ 'react-dom': { singleton: true },
431
+ },`;
432
+ }
433
+ function starterConfigFor(scenario) {
434
+ const { framework, role } = scenario;
435
+ const wantRemotes = role === "consumer" || role === "dual";
436
+ const wantExposes = role === "provider" || role === "dual";
437
+ const lines = [];
438
+ lines.push(`// fulgurjs.config.ts \u2014\u2014 @fulgurjs/federation \u5355\u9879\u76EE\u63A5\u5165\u914D\u7F6E\uFF08\u4E00\u9879\u76EE\u4E00\u4EFD\uFF0C\u53EF\u5165\u5E93\u3001\u53EF\u590D\u8DD1\uFF09`);
439
+ lines.push(`//`);
440
+ lines.push(`// Vite \u63A5\u5165\uFF08\u672C\u9879\u76EE vite.config.ts \u53EA\u9700\u4E24\u884C\u8054\u90A6\u76F8\u5173\u4EE3\u7801\uFF09\uFF1A`);
441
+ lines.push(`// import federation from '@fulgurjs/federation'`);
442
+ lines.push(`// import fulgurjsConfig from './fulgurjs.config'`);
443
+ lines.push(`// // plugins: [ ...\u539F\u6709\u63D2\u4EF6, federation(fulgurjsConfig) ]`);
444
+ lines.push(`//`);
445
+ lines.push(`// CLI\uFF08\u7EAF\u672C\u5730\uFF0C\u65E0\u7F51\u7EDC\uFF09\uFF1A`);
446
+ lines.push(`// npx fulgurjs explain # \u89E3\u91CA\u672C\u5E94\u7528\u6709\u6548\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE`);
447
+ lines.push(`// npx fulgurjs check-pages # \u9010\u9875\u63A5\u5165\u7684\u5BBF\u4E3B\uFF1A\u9875\u9762\u8868 \u2194 \u8FDC\u7A0B manifest \u5951\u7EA6\u6838\u5BF9`);
448
+ lines.push(`// npx fulgurjs doctor --base http://<\u7AD9\u70B9> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55> # \u90E8\u7F72\u4F53\u68C0`);
449
+ lines.push(`import type { FederationOptions } from '@fulgurjs/federation'`);
450
+ lines.push("");
451
+ lines.push("export default {");
452
+ lines.push(" // \u8054\u90A6\u5BB9\u5668\u540D\uFF08\u8FDC\u7A0B\u5F15\u7528\u5B83\u65F6\u7528\u8FD9\u4E2A\u540D\uFF1B\u540C\u4E00\u9875\u9762\u5185\u552F\u4E00\uFF09");
453
+ lines.push(" name: 'my-app',");
454
+ if (wantExposes) lines.push(EXPOSES_EXAMPLE[framework]);
455
+ if (wantRemotes) lines.push(REMOTES_EXAMPLE);
456
+ lines.push(" // \u53EF\u9009\uFF1A\u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u6837\u5F0F/\u7EC4\u4EF6/locale\uFF09\u65F6\u58F0\u660E\u5165\u53E3\u6587\u4EF6\u2014\u2014");
457
+ lines.push(" // \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)");
458
+ lines.push(" // \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\u3002\u7F3A\u7701 = \u65E0\u521D\u59CB\u5316\u884C\u4E3A\uFF08\u666E\u901A expose \u8BED\u4E49\u4E0D\u53D8\uFF09");
459
+ lines.push(" // setup: './src/fulgurjs/setup.ts',");
460
+ lines.push(sharedExample(framework));
461
+ lines.push(`} satisfies FederationOptions`);
462
+ if (role === "consumer" || role === "dual") {
463
+ lines.push("");
464
+ lines.push(HOST_PAGES_COMMENT);
465
+ }
466
+ lines.push("");
467
+ return lines.join("\n");
468
+ }
469
+ async function writeConfigTemplate(target, force = false, scenario = { framework: "vue", role: "dual" }) {
406
470
  if (fs.existsSync(target) && !force) {
407
471
  return "exists";
408
472
  }
409
473
  fs.mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
410
- fs.writeFileSync(path.resolve(target), STARTER_CONFIG);
474
+ fs.writeFileSync(path.resolve(target), starterConfigFor(scenario));
411
475
  return "written";
412
476
  }
413
477
  function viteSnippetForAppMode(appRoot) {
@@ -422,6 +486,42 @@ export default defineConfig({
422
486
  ],
423
487
  })`;
424
488
  }
489
+ function checklistFor(scenario) {
490
+ const { framework, role } = scenario;
491
+ const out = [];
492
+ const provide = role === "provider" || role === "dual";
493
+ const consume = role === "consumer" || role === "dual";
494
+ out.push("1. \u5B89\u88C5\u4F9D\u8D56\uFF1Anpm/pnpm add @fulgurjs/federation");
495
+ if (provide) {
496
+ out.push("2. expose \u76EE\u6807\u6309\u7528\u9014\u5206\u7C7B\uFF1A\u666E\u901A\u7EC4\u4EF6/\u51FD\u6570\u6A21\u5757\u53EF\u76F4\u63A5 expose\uFF08\u4F7F\u7528\u65B9 remoteComponent/loadRemote \u6D88\u8D39\uFF09\uFF1B");
497
+ out.push(" \u4F5C\u4E3A\u300C\u9875\u9762\u300D\u63A5\u5165\u5BBF\u4E3B\u9875\u9762\u8DEF\u7531\u8868\u65F6\u624D\u8981\u6C42\u72EC\u7ACB\u9875\uFF08\u9875\u9762\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09\uFF1B\u7EC4\u4EF6\u5FC5\u586B props \u7ED9\u9ED8\u8BA4\u503C\uFF08BLD-003\uFF09");
498
+ }
499
+ if (consume) {
500
+ out.push("3. \u6D88\u8D39\u65B9\u5F0F\u6309\u9700\u9009\u62E9\uFF1A\u666E\u901A\u7EC4\u4EF6/\u6A21\u5757\u7528 remoteComponent/loadRemote\uFF08\u4E0D\u9700\u8981\u9875\u9762\u8868\u3001\u4E0D\u9700\u8981\u6865\u63A5\u914D\u7F6E\uFF09\uFF1B");
501
+ out.push(" \u5B8C\u6574\u5B50\u5E94\u7528\uFF08\u81EA\u5E26 Router/store\uFF09\u7528\u6865\u63A5 createVueBridgeApp/createReactBridgeApp\u2014\u2014\u5BBF\u4E3B\u53EA\u914D\u6302\u8F7D\u524D\u7F00\u4E0E\u5165\u53E3\uFF0C");
502
+ out.push(" \u5B50\u5E94\u7528\u4FDD\u7559\u81EA\u5DF1\u7684 Router \u4E0E\u4E1A\u52A1\u83DC\u5355\uFF0C\u4E0D\u9010\u9875\u767B\u8BB0\u5185\u90E8\u9875\u9762");
503
+ }
504
+ out.push("4. shared \u91CC\u53EA\u5217\u300C\u9879\u76EE\u5B9E\u9645\u5B89\u88C5\u4E14\u8DE8\u5E94\u7528\u5171\u4EAB\u300D\u7684\u5E93\uFF0C\u8DE8\u5E94\u7528\u540C\u5B9E\u4F8B\u7684\uFF08vue/react/react-dom/router/store\uFF09\u914D singleton: true\uFF1B");
505
+ out.push(" \u72EC\u7ACB Router/store \u7684\u5B8C\u6574\u5B50\u5E94\u7528\u9694\u79BB\u8BBE\u8BA1\u662F\u5408\u6CD5\u7684\u2014\u2014\u4E0D\u662F\u6240\u6709\u5171\u4EAB\u9879\u90FD\u5FC5\u987B singleton");
506
+ if (consume) {
507
+ out.push("5. \u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u7EC4\u4EF6/\u6837\u5F0F/locale \u7B49\uFF09\u65F6\uFF0C\u5728 fulgurjs.config.ts \u7684 setup \u58F0\u660E\u5165\u53E3\u6587\u4EF6\uFF1A");
508
+ out.push(" \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF08\u5BB9\u5668\u9996\u6B21\u88AB\u52A0\u8F7D\u4E1A\u52A1\u6A21\u5757\u524D\uFF09\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)");
509
+ out.push(" \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\uFF08\u6362\u8D26\u53F7/\u91CD\u767B\u81EA\u52A8\u91CD\u8DD1\uFF09\u3002\u8DE8\u5E94\u7528\u4F20\u503C\u8D70 context \u51FD\u6570\uFF1A\u5BBF\u4E3B provideAppContext \u5199\u5165");
510
+ out.push(" \uFF08\u9000\u51FA\u65F6 clearAppContext \u6E05\u7406\uFF09\uFF0C\u8FDC\u7A0B setup/onSession \u7528 getAppContext / requireAppContext \u6D88\u8D39");
511
+ }
512
+ out.push(APP_ENTRY_NOTE[framework]);
513
+ out.push('6. dev \u51B7\u542F\u52A8\u9996\u8F6E 30~60s \u6709\u9884\u6784\u5EFA\u7A97\u53E3\uFF08\u77AC\u65F6 504/"ce"\uFF0CDEV-010\uFF09\uFF1A\u5148\u771F\u5B9E\u6253\u5F00\u9875\u9762\u9884\u70ED\u518D\u505A\u65AD\u8A00');
514
+ out.push("7. \u914D\u7F6E\u89E3\u91CA\uFF1Afulgurjs explain\uFF1B\u90E8\u7F72\u4F53\u68C0\uFF1Afulgurjs doctor --base <URL> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55...>");
515
+ if (consume) {
516
+ out.push(" \uFF08--apps \u662F\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF0C\u4E0D\u662F\u5BB9\u5668\u540D\uFF09");
517
+ out.push("8. \u4EC5\u9010\u9875\u63A5\u5165\u7684\u5BBF\u4E3B\uFF1A\u9875\u9762\u5951\u7EA6\u6838\u5BF9 fulgurjs check-pages\uFF08--manifest/--site \u6307\u5B9A\u8FDC\u7A0B manifest \u6765\u6E90\uFF09\uFF1B");
518
+ out.push(" \u672A\u914D\u7F6E hostPages \u7684\u5DE5\u7A0B\u8FD0\u884C check-pages \u4F1A\u660E\u786E\u63D0\u793A\u300C\u672A\u914D\u7F6E\u8BE5\u80FD\u529B\u300D\uFF0C\u4E0D\u4EA7\u51FA\u4F2A\u6838\u5BF9");
519
+ } else {
520
+ out.push("8. \u9010\u9875\u63A5\u5165\uFF08\u5BBF\u4E3B\u9875\u9762\u8DEF\u7531\u8868\uFF09\u662F\u53EF\u9009\u9879\u2014\u2014\u7EAF\u63D0\u4F9B\u65B9\u65E0\u9700 check-pages/hostPages");
521
+ }
522
+ out.push("9. \u90E8\u7F72\u8BED\u4E49\uFF1AremoteEntry/manifest/index.html \u5FC5\u987B no-cache\uFF08\u4E25\u7981 immutable\uFF09\uFF1B\u5E26 hash \u7684 assets \u957F\u7F13\u5B58");
523
+ return out;
524
+ }
425
525
  function appModeSummary(loaded) {
426
526
  const options = loaded.options;
427
527
  const roleText = { host: "\u5BBF\u4E3B", remote: "\u8FDC\u7A0B", dual: "\u5BBF\u4E3B+\u8FDC\u7A0B\uFF08\u53CC\u89D2\u8272\uFF09" }[Object.keys(options.remotes ?? {}).length > 0 && (Object.keys(options.exposes ?? {}).length > 0 || !!options.setup) ? "dual" : Object.keys(options.remotes ?? {}).length > 0 ? "host" : "remote"];
@@ -440,87 +540,81 @@ function appModeSummary(loaded) {
440
540
  if (loaded.hostPages) {
441
541
  lines.push(` \u9875\u9762\u8DEF\u7531\u8868\uFF08hostPages \u5177\u540D\u5BFC\u51FA\uFF09\uFF1A${loaded.hostPages.pages.length} \u6761`);
442
542
  } else {
443
- lines.push(" \u9875\u9762\u8DEF\u7531\u8868\uFF1A\uFF08\u65E0 hostPages \u5BFC\u51FA\u2014\u2014\u7EAF\u8FDC\u7A0B\u5E94\u7528\u65E0\u9700\uFF1B\u5BBF\u4E3B\u5EFA\u8BAE\u63D0\u4F9B\uFF09");
543
+ lines.push(" \u9875\u9762\u8DEF\u7531\u8868\uFF1A\uFF08\u65E0 hostPages \u5BFC\u51FA\u2014\u2014\u672A\u7528\u9010\u9875\u63A5\u5165\uFF0Ccheck-pages \u4E0D\u9002\u7528\uFF09");
444
544
  }
445
545
  return lines;
446
546
  }
447
547
  async function inspectConfig(configPath) {
448
548
  const loaded = await loadAppConfig(configPath);
549
+ const options = loaded.options;
550
+ const isHost = Object.keys(options.remotes ?? {}).length > 0;
551
+ const isRemote = Object.keys(options.exposes ?? {}).length > 0 || !!options.setup;
552
+ const role = isHost && isRemote ? "dual" : isHost ? "consumer" : "provider";
553
+ let framework = "vue";
554
+ let frameworkNote = "";
555
+ try {
556
+ const pkg = JSON.parse(fs.readFileSync(path.join(loaded.appRoot, "package.json"), "utf8"));
557
+ const detected = detectFrameworkFromPackageJson(pkg);
558
+ if (detected) framework = detected;
559
+ else frameworkNote = "\n \uFF08\u672A\u80FD\u4ECE package.json \u5224\u65AD\u6846\u67B6\u2014\u2014\u4EE5\u4E0B\u5165\u53E3\u63D0\u793A\u6309 Vue \u7ED9\u51FA\uFF1BReact \u9879\u76EE\u8BF7\u6539\u7528 @fulgurjs/federation/react\uFF09";
560
+ } catch {
561
+ frameworkNote = "\n \uFF08\u672A\u8BFB\u53D6\u5230\u5E94\u7528 package.json\u2014\u2014\u4EE5\u4E0B\u5165\u53E3\u63D0\u793A\u6309 Vue \u7ED9\u51FA\uFF1BReact \u9879\u76EE\u8BF7\u6539\u7528 @fulgurjs/federation/react\uFF09";
562
+ }
449
563
  const out = [];
450
564
  out.push(`${MARK} \u914D\u7F6E\u6821\u9A8C\u901A\u8FC7\uFF1A\u5355\u9879\u76EE\u5F62\u6001\uFF08\u9ED8\u8BA4\u5BFC\u51FA = federation() \u9009\u9879\uFF09`);
451
565
  out.push("\u5E94\u7528\u6458\u8981\uFF1A");
452
566
  out.push(...appModeSummary(loaded));
453
567
  out.push("\n\u2500\u2500 vite.config.ts plugins \u63A5\u5165\u5757\uFF08\u8FD9\u5C31\u662F\u5168\u90E8\u8054\u90A6\u63A5\u5165\u4EE3\u7801\uFF09 \u2500\u2500");
454
568
  out.push(viteSnippetForAppMode(loaded.appRoot));
455
- out.push("\n\u2500\u2500 \u63A5\u5165\u6838\u5BF9\u6E05\u5355\uFF08\u8054\u90A6\u901A\u7528\u9879\uFF0C\u4E0E\u5177\u4F53\u9879\u76EE\u65E0\u5173\uFF09 \u2500\u2500");
456
- out.push(...COMMON_CHECKLIST);
569
+ out.push("\n\u2500\u2500 \u63A5\u5165\u6838\u5BF9\u6E05\u5355\uFF08\u6309\u672C\u914D\u7F6E\u7684\u89D2\u8272\u751F\u6210\uFF0C\u4E0E\u5177\u4F53\u9879\u76EE\u65E0\u5173\uFF09 \u2500\u2500");
570
+ out.push(...checklistFor({ framework, role }).map((line) => line.startsWith("5.") ? line + frameworkNote : line));
457
571
  return out.join("\n");
458
572
  }
459
- var MARK, STARTER_CONFIG, COMMON_CHECKLIST;
573
+ var MARK, REMOTES_EXAMPLE, EXPOSES_EXAMPLE, HOST_PAGES_COMMENT, STARTER_CONFIG, APP_ENTRY_NOTE, COMMON_CHECKLIST;
460
574
  var init_init = __esm({
461
575
  "src/init.ts"() {
462
576
  "use strict";
463
577
  init_app_config();
464
578
  MARK = "[fulgurjs:init]";
465
- STARTER_CONFIG = `// fulgurjs.config.ts \u2014\u2014 @fulgurjs/federation \u5355\u9879\u76EE\u63A5\u5165\u914D\u7F6E\uFF08\u4E00\u9879\u76EE\u4E00\u4EFD\uFF0C\u53EF\u5165\u5E93\u3001\u53EF\u590D\u8DD1\uFF09
466
- //
467
- // Vite \u63A5\u5165\uFF08\u672C\u9879\u76EE vite.config.ts \u53EA\u9700\u4E24\u884C\u8054\u90A6\u76F8\u5173\u4EE3\u7801\uFF09\uFF1A
468
- // import federation from '@fulgurjs/federation'
469
- // import fulgurjsConfig from './fulgurjs.config'
470
- // // plugins: [ ...\u539F\u6709\u63D2\u4EF6, federation(fulgurjsConfig) ]
471
- //
472
- // CLI\uFF08\u7EAF\u672C\u5730\uFF0C\u65E0\u7F51\u7EDC\uFF09\uFF1A
473
- // npx fulgurjs explain # \u89E3\u91CA\u672C\u5E94\u7528\u6709\u6548\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE
474
- // npx fulgurjs check-pages # \u5BBF\u4E3B\u9875\u9762\u8868 \u2194 \u8FDC\u7A0B manifest \u5951\u7EA6\u6838\u5BF9
475
- // npx fulgurjs doctor --base http://<\u7AD9\u70B9> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55> # \u90E8\u7F72\u4F53\u68C0\uFF08\u90E8\u7F72\u5728 /my-remote/ \u5C31\u5199 my-remote\uFF09
476
- import type { FederationOptions } from '@fulgurjs/federation'
477
-
478
- export default {
479
- // \u8054\u90A6\u5BB9\u5668\u540D\uFF08\u8FDC\u7A0B\u5F15\u7528\u5B83\u65F6\u7528\u8FD9\u4E2A\u540D\uFF1B\u540C\u4E00\u9875\u9762\u5185\u552F\u4E00\uFF09
480
- name: 'my-app',
481
- // \u672C\u5E94\u7528\u5BF9\u5916\u66B4\u9732\u7684\u6A21\u5757\uFF1A'./\u952E' \u2192 \u76F8\u5BF9\u672C\u9879\u76EE\u6839\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84
482
- exposes: {
483
- './pages/home': './src/views/Home.vue',
484
- },
485
- // \u672C\u5E94\u7528\u6D88\u8D39\u7684\u8FDC\u7A0B\uFF08\u952E = import \u524D\u7F00\uFF1Bdev/prod \u5730\u5740\u4E8C\u6BB5\u5F0F\uFF0C\u5355\u5730\u5740\u5B57\u7B26\u4E32\u4E5F\u884C\uFF09
579
+ REMOTES_EXAMPLE = ` // \u672C\u5E94\u7528\u6D88\u8D39\u7684\u8FDC\u7A0B\uFF08\u952E = import \u524D\u7F00\uFF1Bdev/prod \u5730\u5740\u4E8C\u6BB5\u5F0F\uFF0C\u5355\u5730\u5740\u5B57\u7B26\u4E32\u4E5F\u884C\uFF09
486
580
  remotes: {
487
581
  'remote-a': { dev: 'http://localhost:5174/remote-a', prod: '/remote-a' },
488
- },
489
- // \u53EF\u9009\uFF1A\u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u6837\u5F0F/\u7EC4\u4EF6/locale\uFF09\u65F6\u58F0\u660E\u5165\u53E3\u6587\u4EF6\u2014\u2014
490
- // \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)
491
- // \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\u3002\u7F3A\u7701 = \u65E0\u521D\u59CB\u5316\u884C\u4E3A\uFF08\u666E\u901A expose \u8BED\u4E49\u4E0D\u53D8\uFF09
492
- // setup: './src/fulgurjs/setup.ts',
493
- shared: {
494
- vue: { singleton: true, requiredVersion: '^3.4.0' },
495
- 'vue-router': { singleton: true, requiredVersion: '^4.4.5' },
496
- pinia: { singleton: true, requiredVersion: '^2.1.7' },
497
- },
498
- } satisfies FederationOptions
499
-
500
- // \u2500\u2500 \u4EE5\u4E0B\u4EC5\u4F9B CLI explain/check-pages \u8BFB\u53D6\uFF08\u4E0D\u662F federation() \u7684\u53C2\u6570\uFF09\u2500\u2500
501
- // \u5BBF\u4E3B\u5E94\u7528\uFF1A\u628A\u9875\u9762\u8DEF\u7531\u8868\u4EE5\u5177\u540D\u5BFC\u51FA hostPages \u63D0\u4F9B\u7ED9 CLI\uFF0C\u4E0E\u8FD0\u884C\u65F6 createHostPages
502
- // \u6D88\u8D39\u540C\u4E00\u4EFD\u6570\u636E\u6A21\u5757\uFF08\u552F\u4E00\u624B\u5DE5\u7EF4\u62A4\u4F4D\u7F6E\uFF1B\u7EAF\u8FDC\u7A0B\u5E94\u7528\u5220\u9664\u672C\u6BB5\u5373\u53EF\uFF09\u3002
582
+ },`;
583
+ EXPOSES_EXAMPLE = {
584
+ vue: ` // \u672C\u5E94\u7528\u5BF9\u5916\u66B4\u9732\u7684\u6A21\u5757\uFF1A'./\u952E' \u2192 \u76F8\u5BF9\u672C\u9879\u76EE\u6839\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84
585
+ // \u666E\u901A\u7EC4\u4EF6/\u51FD\u6570\u6A21\u5757\u53EF\u76F4\u63A5 expose\uFF08\u4F7F\u7528\u65B9 remoteComponent/loadRemote \u6D88\u8D39\uFF0Cprops \u89C1\u7EC4\u4EF6\u5951\u7EA6\uFF09\uFF1B
586
+ // \u53EA\u6709\u4F5C\u4E3A\u300C\u9875\u9762\u300D\u63A5\u5165\uFF08\u5BBF\u4E3B\u9875\u9762\u8DEF\u7531\u8868\uFF09\u65F6\u624D\u8981\u6C42\u72EC\u7ACB\u9875\uFF08\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09
587
+ exposes: {
588
+ './shared/user-badge': './src/components/UserBadge.vue',
589
+ },`,
590
+ react: ` // \u672C\u5E94\u7528\u5BF9\u5916\u66B4\u9732\u7684\u6A21\u5757\uFF1A'./\u952E' \u2192 \u76F8\u5BF9\u672C\u9879\u76EE\u6839\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84
591
+ // \u666E\u901A\u7EC4\u4EF6/\u51FD\u6570\u6A21\u5757\u53EF\u76F4\u63A5 expose\uFF08\u4F7F\u7528\u65B9 remoteComponent/loadRemote \u6D88\u8D39\uFF0Cprops \u89C1\u7EC4\u4EF6\u5951\u7EA6\uFF09\uFF1B
592
+ // \u53EA\u6709\u4F5C\u4E3A\u300C\u9875\u9762\u300D\u63A5\u5165\uFF08\u5BBF\u4E3B\u9875\u9762\u8DEF\u7531\u8868\uFF09\u65F6\u624D\u8981\u6C42\u72EC\u7ACB\u9875\uFF08\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09
593
+ exposes: {
594
+ './shared/user-badge': './src/components/UserBadge.tsx',
595
+ },`
596
+ };
597
+ HOST_PAGES_COMMENT = `// \u2500\u2500 \u4EE5\u4E0B\u4EC5\u4F9B\u9010\u9875\u63A5\u5165\u7684\u5BBF\u4E3B\u4F7F\u7528\uFF08\u4E0D\u662F federation() \u7684\u53C2\u6570\uFF09\u2500\u2500
598
+ // \u9010\u9875\u63A5\u5165\uFF08\u5BBF\u4E3B\u9875\u9762\u8DEF\u7531\u8868 \u2192 \u8FDC\u7A0B\u9875\u9762\uFF09\u65F6\uFF0C\u628A\u9875\u9762\u8DEF\u7531\u8868\u4EE5\u5177\u540D\u5BFC\u51FA hostPages \u63D0\u4F9B\u7ED9 CLI\uFF0C
599
+ // \u4E0E\u8FD0\u884C\u65F6 createHostPages \u6D88\u8D39\u540C\u4E00\u4EFD\u6570\u636E\u6A21\u5757\uFF08\u552F\u4E00\u624B\u5DE5\u7EF4\u62A4\u4F4D\u7F6E\uFF09\u3002
503
600
  // \u7EAF\u6570\u636E\u6A21\u5757\u793A\u4F8B src/fulgurjs/host/pages.data.ts\uFF1A
504
601
  // export const remotePrefixes = { '/remote-a/': 'remote-a' }
505
602
  // export const pages = [{ route: '/remote-a/home', name: 'Home', title: '\u9996\u9875' }]
506
603
  // import { pages, remotePrefixes } from './src/fulgurjs/host/pages.data'
507
604
  // export const hostPages = { pages, remotePrefixes }
508
- `;
509
- COMMON_CHECKLIST = [
510
- "1. \u5B89\u88C5\u4F9D\u8D56\uFF1Anpm/pnpm add @fulgurjs/federation",
511
- "2. expose \u4E00\u5F8B\u6307\u5411\u72EC\u7ACB\u9875\uFF08\u9875\u9762\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09\uFF1B\u7EC4\u4EF6\u9700\u8981\u5FC5\u586B props \u65F6\u7ED9\u9ED8\u8BA4\u503C\uFF08BLD-003\uFF09",
512
- "3. shared \u91CC vue / vue-router / pinia \u5EFA\u8BAE singleton: true\u2014\u2014\u8DE8\u5E94\u7528\u5FC5\u987B\u540C\u5B9E\u4F8B\uFF08\u5168\u5C40\u54CD\u5E94\u6027\u3001getActivePinia\u3001\u8DEF\u7531\u6CE8\u5165\uFF09",
513
- "4. \u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u7EC4\u4EF6/\u6837\u5F0F/locale \u7B49\uFF09\u65F6\uFF0C\u5728 fulgurjs.config.ts \u7684 setup \u58F0\u660E\u5165\u53E3\u6587\u4EF6\uFF1A",
514
- " \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF08\u5BB9\u5668\u9996\u6B21\u88AB\u52A0\u8F7D\u4E1A\u52A1\u6A21\u5757\u524D\uFF09\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)",
515
- " \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\uFF08\u6362\u8D26\u53F7/\u91CD\u767B\u81EA\u52A8\u91CD\u8DD1\uFF09\u3002\u5BBF\u4E3B\u65E0\u9700\u518D\u624B\u5199\u300CloadRemote \u542F\u52A8\u5668\u5E76\u8C03\u7528\u300D\uFF1B",
516
- " \u8DE8\u5E94\u7528\u4F20\u503C\u7EDF\u4E00\u8D70 @fulgurjs/federation/runtime \u7684 context \u51FD\u6570\uFF1A\u5BBF\u4E3B provideAppContext \u5199\u5165",
517
- " \uFF08\u9000\u51FA\u65F6 clearAppContext \u6E05\u7406\uFF09\uFF0C\u8FDC\u7A0B setup/onSession \u7528 getAppContext / requireAppContext \u6D88\u8D39",
518
- "5. \u5E94\u7528\u4EE3\u7801\u552F\u4E00 API \u5165\u53E3\uFF1Aimport { loadRemote, provideAppContext, getAppContext, clearAppContext, definePages, createHostPages, remoteSchema, remoteComponent } from '@fulgurjs/federation/runtime'",
519
- '6. dev \u51B7\u542F\u52A8\u9996\u8F6E 30~60s \u6709\u9884\u6784\u5EFA\u7A97\u53E3\uFF08\u77AC\u65F6 504/"ce"\uFF0CDEV-010\uFF09\uFF1A\u5148\u771F\u5B9E\u6253\u5F00\u9875\u9762\u9884\u70ED\u518D\u505A\u65AD\u8A00',
520
- "7. \u914D\u7F6E\u89E3\u91CA\uFF1Afulgurjs explain\uFF1B\u9875\u9762\u5951\u7EA6\u6838\u5BF9\uFF1Afulgurjs check-pages\uFF08\u5BBF\u4E3B\u9879\u76EE\u8FD0\u884C\uFF0C--manifest/--site \u6307\u5B9A\u8FDC\u7A0B manifest \u6765\u6E90\uFF09\uFF1B",
521
- " \u90E8\u7F72\u4F53\u68C0\uFF1Afulgurjs doctor --base <URL> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55...>\uFF08--apps \u662F\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF0C\u4E0D\u662F\u5BB9\u5668\u540D\uFF09",
522
- "8. \u90E8\u7F72\u8BED\u4E49\uFF1AremoteEntry/manifest/index.html \u5FC5\u987B no-cache\uFF08\u4E25\u7981 immutable\uFF09\uFF1B\u5E26 hash \u7684 assets \u957F\u7F13\u5B58"
523
- ];
605
+ //
606
+ // \u4E0D\u7528\u9010\u9875\u63A5\u5165\uFF08\u5B8C\u6574\u5B50\u5E94\u7528\u6865\u63A5 / \u666E\u901A\u7EC4\u4EF6\u6D88\u8D39\uFF09\u5219\u5220\u9664\u672C\u6BB5\u2014\u2014\u4E1A\u52A1\u83DC\u5355\u4E0E\u4E1A\u52A1 Router
607
+ // \u4ECD\u5F52\u5E94\u7528\u81EA\u5DF1\u7BA1\u7406\uFF0C\u8054\u90A6\u4E0D\u63A5\u7BA1\u3002`;
608
+ STARTER_CONFIG = starterConfigFor({ framework: "vue", role: "dual" });
609
+ APP_ENTRY_NOTE = {
610
+ vue: `5. Vue \u5E94\u7528\u4EE3\u7801\u552F\u4E00 API \u5165\u53E3\uFF1Aimport { loadRemote, remoteComponent, createHostPages, provideAppContext, getAppContext, clearAppContext, definePages, remoteSchema } from '@fulgurjs/federation/vue'
611
+ \uFF08\u5B8C\u6574\u5B50\u5E94\u7528\u6865\u63A5\u53E6\u52A0 createVueBridgeApp / defineBridgeApp\uFF1BURL \u540C\u6B65\u53E6\u52A0 createVueBridgeNavigation / connectVueBridgeRouter\u2014\u2014\u540C\u4E00\u5165\u53E3\u3002
612
+ \u6846\u67B6\u65E0\u5173\u6A21\u5757\uFF08\u7EAF JS/TS \u6D88\u8D39\u3001\u65E0 Vue\uFF09\u7528 @fulgurjs/federation/runtime\uFF09`,
613
+ react: `5. React \u5E94\u7528\u4EE3\u7801\u552F\u4E00 API \u5165\u53E3\uFF1Aimport { loadRemote, remoteComponent, useLoadRemote, RemoteErrorBoundary, createReactHostPages, provideAppContext, getAppContext, clearAppContext, definePages, remoteSchema } from '@fulgurjs/federation/react'
614
+ \uFF08\u5B8C\u6574\u5B50\u5E94\u7528\u6865\u63A5\u53E6\u52A0 createReactBridgeApp / defineBridgeApp\uFF1BURL \u540C\u6B65\u53E6\u52A0 createReactBridgeNavigation / createReactBridgeRouter\u2014\u2014\u540C\u4E00\u5165\u53E3\u3002
615
+ \u6846\u67B6\u65E0\u5173\u6A21\u5757\uFF08\u7EAF JS/TS \u6D88\u8D39\u3001\u65E0 React\uFF09\u7528 @fulgurjs/federation/runtime\uFF09`
616
+ };
617
+ COMMON_CHECKLIST = checklistFor({ framework: "vue", role: "dual" });
524
618
  }
525
619
  });
526
620
 
@@ -535,11 +629,13 @@ __export(create_exports, {
535
629
  defaultInstall: () => defaultInstall,
536
630
  formatCreateResult: () => formatCreateResult,
537
631
  isExcluded: () => isExcluded,
538
- nodeVersionWarning: () => nodeVersionWarning,
632
+ nodeEnginesViolation: () => nodeEnginesViolation,
539
633
  packageRootOf: () => packageRootOf,
634
+ pnpmUnavailableReason: () => pnpmUnavailableReason,
540
635
  readDevConfig: () => readDevConfig,
541
636
  resolveTemplatesRoot: () => resolveTemplatesRoot,
542
637
  scanTemplateConflicts: () => scanTemplateConflicts,
638
+ templateEnginesNode: () => templateEnginesNode,
543
639
  validateTemplateDir: () => validateTemplateDir
544
640
  });
545
641
  import fs2 from "node:fs";
@@ -669,15 +765,18 @@ function formatCreateResult(result) {
669
765
  for (const c of result.skippedConflicts) lines.push(` ${c.rel} \u2014 ${c.reason}`);
670
766
  }
671
767
  lines.push("\u540E\u7EED\u6B65\u9AA4\uFF1A");
672
- lines.push(` cd ${result.target}`);
768
+ lines.push(` cd "${result.target}"`);
673
769
  if (!result.installRan) lines.push(" pnpm install --frozen-lockfile");
674
770
  lines.push(" pnpm dev # \u6309\u542F\u52A8\u987A\u5E8F\u62C9\u8D77\u5168\u90E8\u5E94\u7528\uFF0C\u5931\u8D25\u4F1A\u6574\u7EC4\u9000\u51FA\u5E76\u8BF4\u660E\u539F\u56E0");
675
771
  lines.push("\u8BBF\u95EE\u5165\u53E3\uFF08\u8FDC\u7A0B\u5148\u4E8E\u5BBF\u4E3B\u5C31\u7EEA\uFF09\uFF1A");
676
772
  for (const app of apps) {
677
773
  lines.push(` http://localhost:${app.port}/ ${app.name}${app.dir !== app.name ? `\uFF08\u76EE\u5F55 ${app.dir}\uFF09` : ""}`);
678
774
  }
679
- const hosts = apps.slice().reverse();
680
- lines.push(`\u6D4F\u89C8\u5668\u6253\u5F00\u5BBF\u4E3B\u5165\u53E3\uFF1A${hosts.length ? `http://localhost:${hosts[0].port}/` : "\uFF08\u89C1\u6A21\u677F README\uFF09"}`);
775
+ const hosts = apps.filter((a) => a.host);
776
+ const hostList = hosts.length ? hosts : apps.slice(-1);
777
+ lines.push(
778
+ "\u6D4F\u89C8\u5668\u6253\u5F00\u5BBF\u4E3B\u5165\u53E3\uFF1A" + hostList.map((a) => `http://localhost:${a.port}/\uFF08${a.name}\uFF09`).join(" ")
779
+ );
681
780
  lines.push(" pnpm build # \u5404\u5B50\u5E94\u7528\u751F\u4EA7\u6784\u5EFA\uFF08\u8FDC\u7A0B\u751F\u6210 fulgurjs-remoteEntry.js / fulgurjs-manifest.json\uFF09");
682
781
  lines.push(" \u90E8\u7F72\u89C4\u5219\uFF08no-cache / SPA \u56DE\u9000 / base \u5BF9\u9F50\uFF09\u89C1\u6A21\u677F\u5185\u5404\u5B50\u5E94\u7528 README\uFF1B\u90E8\u7F72\u540E\u53EF\u7528 npx fulgurjs doctor --base <\u7AD9\u70B9> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55> \u4F53\u68C0");
683
782
  lines.push(" \u6539\u7AEF\u53E3\uFF08\u56DB\u5904\u540C\u6B65\uFF0C\u6F0F\u4E00\u5904\u542F\u52A8\u5668\u4F1A\u88AB\u65E7\u7AEF\u53E3\u5361\u4F4F\uFF09\uFF1A");
@@ -708,10 +807,23 @@ function formatCreateJson(result) {
708
807
  2
709
808
  );
710
809
  }
711
- function nodeVersionWarning(nodeVersion) {
810
+ function nodeEnginesViolation(nodeVersion, range) {
811
+ if (!range) return void 0;
812
+ const m = range.trim().match(/^>=?(\d+)\.(\d+)(?:\.(\d+))?/);
813
+ if (!m) return void 0;
814
+ const parse = (v) => v.replace(/^v/, "").split(/[.-]/).map((x) => Number(x) || 0);
815
+ const [curMajor, curMinor = 0, curPatch = 0] = parse(nodeVersion);
816
+ const [minMajor, minMinor = 0, minPatch = 0] = [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
817
+ const ge = (a, b) => a[0] !== b[0] ? a[0] > b[0] : a[1] !== b[1] ? a[1] > b[1] : (a[2] ?? 0) >= (b[2] ?? 0);
818
+ if (range.startsWith(">=")) {
819
+ if (!ge([curMajor, curMinor, curPatch], [minMajor, minMinor, minPatch])) {
820
+ return `\u5F53\u524D Node ${nodeVersion} \u4E0D\u6EE1\u8DB3\u6A21\u677F\u8981\u6C42 engines.node ${range}`;
821
+ }
822
+ return void 0;
823
+ }
712
824
  const major = Number(nodeVersion.replace(/^v/, "").split(".")[0]);
713
- if (Number.isFinite(major) && major < 20) {
714
- return `\u5F53\u524D Node ${nodeVersion} \u4F4E\u4E8E\u6A21\u677F\u8981\u6C42\uFF08Node \u2265 20\uFF0C\u5B9E\u6D4B 24.x\uFF09\u2014\u2014\u5B89\u88C5\u53EF\u80FD\u6210\u529F\uFF0C\u4F46\u4E0E\u6A21\u677F\u9A8C\u8BC1\u73AF\u5883\u4E0D\u7B26`;
825
+ if (Number.isFinite(major) && major < minMajor) {
826
+ return `\u5F53\u524D Node ${nodeVersion} \u4F4E\u4E8E\u6A21\u677F\u8981\u6C42 engines.node ${range}`;
715
827
  }
716
828
  return void 0;
717
829
  }
@@ -746,9 +858,22 @@ async function createProject(opts, io) {
746
858
  );
747
859
  }
748
860
  }
749
- const warnings = [];
750
- const nodeWarn = nodeVersionWarning(process.version);
751
- if (nodeWarn) warnings.push(nodeWarn);
861
+ const enginesViolation = nodeEnginesViolation(process.version, templateEnginesNode(templateDir));
862
+ if (enginesViolation) {
863
+ throw new Error(
864
+ `[fulgurjs:create] \u73AF\u5883\u6821\u9A8C\u5931\u8D25\uFF1A${enginesViolation}
865
+ \u4FEE\u6CD5\uFF1A\u5347\u7EA7 Node\uFF08nvm install <\u7248\u672C> && nvm use <\u7248\u672C>\uFF0C\u6216\u4ECE https://nodejs.org \u5B89\u88C5 LTS\uFF09\u540E\u91CD\u8BD5\uFF1B\u672A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6`
866
+ );
867
+ }
868
+ if (opts.install) {
869
+ const pnpmMissing = await pnpmUnavailableReason();
870
+ if (pnpmMissing) {
871
+ throw new Error(
872
+ `[fulgurjs:create] \u73AF\u5883\u6821\u9A8C\u5931\u8D25\uFF1A${pnpmMissing}
873
+ \u4FEE\u6CD5\uFF1Acorepack enable\uFF08Node \u81EA\u5E26\uFF09\uFF0C\u6216 npm i -g pnpm\uFF1B\u672A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6`
874
+ );
875
+ }
876
+ }
752
877
  let outcome;
753
878
  try {
754
879
  if (fs2.existsSync(target) && fs2.readdirSync(target).length > 0) {
@@ -791,7 +916,6 @@ async function createProject(opts, io) {
791
916
  } else {
792
917
  io.log("[fulgurjs:create] \u6309\u8981\u6C42\u8DF3\u8FC7\u5B89\u88C5\uFF08--no-install\uFF09\uFF1A\u8FDB\u5165\u76EE\u5F55\u540E\u5148\u6267\u884C pnpm install --frozen-lockfile");
793
918
  }
794
- for (const w of warnings) io.log(`[fulgurjs:create] \u8B66\u544A\uFF1A${w}`);
795
919
  const result = {
796
920
  template: templateName,
797
921
  target,
@@ -806,6 +930,24 @@ async function createProject(opts, io) {
806
930
  else io.log(formatCreateResult(result));
807
931
  return result;
808
932
  }
933
+ function templateEnginesNode(templateDir) {
934
+ try {
935
+ const pkg = JSON.parse(fs2.readFileSync(path2.join(templateDir, "package.json"), "utf8"));
936
+ return pkg.engines?.node;
937
+ } catch {
938
+ return void 0;
939
+ }
940
+ }
941
+ async function pnpmUnavailableReason() {
942
+ const code = await new Promise((resolve2) => {
943
+ const child = spawn("pnpm", ["--version"], { stdio: "ignore" });
944
+ child.once("error", () => resolve2(127));
945
+ child.once("close", (c) => resolve2(c ?? 1));
946
+ });
947
+ if (code === 127) return "pnpm \u672A\u5B89\u88C5\u6216\u4E0D\u5728 PATH";
948
+ if (code !== 0) return `pnpm --version \u9000\u51FA\u7801 ${code}\uFF08pnpm \u5B89\u88C5\u5F02\u5E38\uFF09`;
949
+ return void 0;
950
+ }
809
951
  function defaultInstall(cwd, output = "inherit") {
810
952
  return new Promise((resolve2) => {
811
953
  const child = output === "inherit" ? spawn("pnpm", ["install", "--frozen-lockfile"], { cwd, stdio: "inherit" }) : spawn("pnpm", ["install", "--frozen-lockfile"], { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -1284,6 +1426,110 @@ var init_commands = __esm({
1284
1426
  }
1285
1427
  });
1286
1428
 
1429
+ // src/port.ts
1430
+ var port_exports = {};
1431
+ __export(port_exports, {
1432
+ applyPortChange: () => applyPortChange,
1433
+ formatPortPlan: () => formatPortPlan,
1434
+ planPortChange: () => planPortChange
1435
+ });
1436
+ import fs4 from "node:fs";
1437
+ import path3 from "node:path";
1438
+ function readDevConfig2(root) {
1439
+ const file = path3.join(root, "scripts", "dev.config.json");
1440
+ if (!fs4.existsSync(file)) {
1441
+ throw new Error(
1442
+ "[fulgurjs:port] \u672A\u627E\u5230 scripts/dev.config.json\u2014\u2014\u672C\u547D\u4EE4\u7528\u4E8E\u6A21\u677F\u5DE5\u7A0B\uFF08create \u751F\u6210\uFF09\u3002\n \u4FEE\u6CD5\uFF1A\u5728\u6A21\u677F\u5DE5\u7A0B\u6839\u76EE\u5F55\u8FD0\u884C\uFF1B\u81EA\u5EFA\u5DE5\u7A0B\u8BF7\u6309\u6587\u6863\u56DB\u5904\u6E05\u5355\u624B\u5DE5\u6539\u7AEF\u53E3"
1443
+ );
1444
+ }
1445
+ return JSON.parse(fs4.readFileSync(file, "utf8"));
1446
+ }
1447
+ function replacePort(text, from, to) {
1448
+ const re = new RegExp(`(?<![0-9])${from}(?![0-9])`, "g");
1449
+ let hits = 0;
1450
+ const out = text.replace(re, () => {
1451
+ hits++;
1452
+ return String(to);
1453
+ });
1454
+ return { text: out, hits };
1455
+ }
1456
+ function planPortChange(root, app, to) {
1457
+ const cfg = readDevConfig2(root);
1458
+ const target = cfg.apps.find((a) => a.name === app || a.dir === app);
1459
+ if (!target) {
1460
+ throw new Error(
1461
+ `[fulgurjs:port] dev.config.json \u4E2D\u6CA1\u6709\u5E94\u7528\u300C${app}\u300D\u3002\u73B0\u6709\u5E94\u7528\uFF1A${cfg.apps.map((a) => a.name).join("\u3001")}`
1462
+ );
1463
+ }
1464
+ const from = target.port;
1465
+ if (from === to) {
1466
+ throw new Error(`[fulgurjs:port] ${app} \u5DF2\u662F\u7AEF\u53E3 ${to}\uFF0C\u65E0\u9700\u53D8\u66F4`);
1467
+ }
1468
+ const notes = [];
1469
+ const candidates = [
1470
+ { file: path3.join(root, target.dir, "package.json"), required: true },
1471
+ { file: path3.join(root, "scripts", "dev.config.json"), required: true },
1472
+ { file: path3.join(root, "fulgurjs.config.ts"), required: false },
1473
+ { file: path3.join(root, "README.md"), required: false }
1474
+ ];
1475
+ for (const a of cfg.apps) {
1476
+ if (a.host && a.dir !== target.dir) {
1477
+ candidates.push({ file: path3.join(root, a.dir, "fulgurjs.config.ts"), required: false });
1478
+ }
1479
+ }
1480
+ const plans = [];
1481
+ for (const c of candidates) {
1482
+ if (!fs4.existsSync(c.file)) {
1483
+ if (c.required) plans.push({ file: c.file, hits: 0, status: "missing-file" });
1484
+ continue;
1485
+ }
1486
+ const text = fs4.readFileSync(c.file, "utf8");
1487
+ const { hits } = replacePort(text, from, to);
1488
+ plans.push({ file: c.file, hits, status: hits > 0 ? "planned" : "not-found" });
1489
+ }
1490
+ if (!plans.some((p) => p.status === "planned")) {
1491
+ throw new Error(
1492
+ `[fulgurjs:port] \u65E7\u7AEF\u53E3 ${from} \u672A\u5728\u4EFB\u4F55\u53D7\u5F71\u54CD\u6587\u4EF6\u4E2D\u51FA\u73B0\u2014\u2014\u5DE5\u7A0B\u53EF\u80FD\u5DF2\u624B\u5DE5\u6539\u8FC7\u3002\u8BF7\u6838\u5BF9 scripts/dev.config.json \u4E0E\u6587\u6863`
1493
+ );
1494
+ }
1495
+ const notFound = plans.filter((p) => p.status === "not-found" || p.status === "missing-file");
1496
+ if (notFound.length) {
1497
+ notes.push(`\u4EE5\u4E0B\u6587\u4EF6\u672A\u53D1\u73B0\u65E7\u7AEF\u53E3 ${from}\uFF08\u53EF\u80FD\u5DF2\u6539\u6216\u5F62\u6001\u4E0D\u540C\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\uFF09\uFF1A${notFound.map((p) => path3.relative(root, p.file)).join("\u3001")}`);
1498
+ }
1499
+ return { app: target.name, dir: target.dir, from, to, plans, notes };
1500
+ }
1501
+ function applyPortChange(root, plan) {
1502
+ let written = 0;
1503
+ for (const p of plan.plans) {
1504
+ if (p.status !== "planned" || p.hits === 0) continue;
1505
+ const text = fs4.readFileSync(p.file, "utf8");
1506
+ const { text: out, hits } = replacePort(text, plan.from, plan.to);
1507
+ if (hits > 0) {
1508
+ fs4.writeFileSync(p.file, out);
1509
+ written++;
1510
+ }
1511
+ }
1512
+ return written;
1513
+ }
1514
+ function formatPortPlan(root, plan) {
1515
+ const lines = [];
1516
+ lines.push(`[fulgurjs:port] \u8BA1\u5212\uFF1A${plan.app}\uFF08\u76EE\u5F55 ${plan.dir}\uFF09\u7AEF\u53E3 ${plan.from} \u2192 ${plan.to}`);
1517
+ for (const p of plan.plans) {
1518
+ const rel = path3.relative(root, p.file);
1519
+ if (p.status === "planned") lines.push(` \u6539\u5199 ${rel}\uFF08${p.hits} \u5904\uFF09`);
1520
+ else if (p.status === "not-found") lines.push(` \u8DF3\u8FC7 ${rel}\uFF08\u672A\u53D1\u73B0\u65E7\u7AEF\u53E3 ${plan.from}\uFF09`);
1521
+ else lines.push(` \u8DF3\u8FC7 ${rel}\uFF08\u6587\u4EF6\u4E0D\u5B58\u5728\uFF09`);
1522
+ }
1523
+ for (const n of plan.notes) lines.push(` \u6CE8\uFF1A${n}`);
1524
+ lines.push("\u9884\u89C8\u6A21\u5F0F\u672A\u5199\u76D8\uFF1B\u786E\u8BA4\u65E0\u8BEF\u540E\u52A0 --write \u6267\u884C\u3002\u5199\u5165\u53EF\u7528 git checkout <file> \u56DE\u9000\u3002");
1525
+ return lines.join("\n");
1526
+ }
1527
+ var init_port = __esm({
1528
+ "src/port.ts"() {
1529
+ "use strict";
1530
+ }
1531
+ });
1532
+
1287
1533
  // src/cli.ts
1288
1534
  import { resolve } from "node:path";
1289
1535
 
@@ -1434,8 +1680,9 @@ async function runDoctor(opts) {
1434
1680
  const perAppShared = [];
1435
1681
  const chunkSample = opts.chunkSample ?? 16;
1436
1682
  for (const app of opts.apps) {
1437
- const root = `${opts.base.replace(/\/$/, "")}/${app.replace(/^\//, "")}`;
1438
- const entry = await checkStatus(`${root}/fulgurjs-remoteEntry.js`, app, "fulgurjs-remoteEntry.js", {
1683
+ const root = /^(https?:)?\/\//.test(app) ? app.replace(/\/$/, "") : app === "." || app === "" ? opts.base.replace(/\/$/, "") : `${opts.base.replace(/\/$/, "")}/${app.replace(/^\//, "")}`;
1684
+ const entryName = opts.entry ?? "fulgurjs-remoteEntry.js";
1685
+ const entry = opts.noEntry ? { res: void 0, check: void 0 } : await checkStatus(`${root}/${entryName}`, app, entryName, {
1439
1686
  expectJs: true,
1440
1687
  expectNoCache: !opts.dev
1441
1688
  });
@@ -1451,7 +1698,7 @@ async function runDoctor(opts) {
1451
1698
  fix: acao ? void 0 : 'nginx \u5BF9 remoteEntry add_header Access-Control-Allow-Origin "*"'
1452
1699
  });
1453
1700
  }
1454
- const manifestRes = await checkStatus(`${root}/fulgurjs-manifest.json`, app, "fulgurjs-manifest.json", {
1701
+ const manifestRes = opts.noManifest ? { res: void 0, check: void 0 } : await checkStatus(`${root}/fulgurjs-manifest.json`, app, "fulgurjs-manifest.json", {
1455
1702
  expectNoCache: !opts.dev
1456
1703
  });
1457
1704
  if (manifestRes.check) checks.push(manifestRes.check);
@@ -1493,12 +1740,12 @@ async function runDoctor(opts) {
1493
1740
  }
1494
1741
  }
1495
1742
  let htmlBody = "";
1496
- if (!opts.dev) {
1743
+ if (!opts.dev && !opts.noHtml) {
1497
1744
  const html = await checkStatus(`${root}/index.html`, app, "index.html", { expectNoCache: true });
1498
1745
  if (html.check) checks.push(html.check);
1499
1746
  htmlBody = html.res?.body ?? "";
1500
1747
  }
1501
- if (opts.dev) {
1748
+ if (opts.dev && !opts.noEntry) {
1502
1749
  const devEntry = await checkStatus(`${root}/@fulgurjs-entry.js`, app, "@fulgurjs-entry.js\uFF08dev \u5BB9\u5668\u5165\u53E3\uFF09", {
1503
1750
  expectJs: true
1504
1751
  });
@@ -1597,6 +1844,9 @@ function formatDoctorReport(checks) {
1597
1844
  // src/cli.ts
1598
1845
  var HELP = `fulgurjs \u2014 Vite Module Federation CLI (@fulgurjs/federation)
1599
1846
 
1847
+ \u5165\u53E3\u9009\u62E9\uFF08\u5E94\u7528\u4EE3\u7801\u5BFC\u5165\uFF09\uFF1AVue \u5E94\u7528 @fulgurjs/federation/vue \uFF5C React \u5E94\u7528 /react \uFF5C
1848
+ \u6846\u67B6\u65E0\u5173\u6A21\u5757 /runtime \uFF5C Vite \u914D\u7F6E\u7528\u5305\u6839\uFF08federation(options)\uFF09\u3002
1849
+
1600
1850
  \u65B0\u9879\u76EE\uFF1A\u4ECE\u5B8C\u6574\u6A21\u677F\u521B\u5EFA\u53EF\u72EC\u7ACB\u8FD0\u884C\u7684\u8054\u90A6\u5DE5\u7A0B\uFF08\u590D\u5236\u540E pnpm install + pnpm dev\uFF09\uFF1A
1601
1851
  fulgurjs create [--list] \u4EA4\u4E92\u9009\u62E9\u6A21\u677F\uFF08TTY\uFF09
1602
1852
  fulgurjs create <\u6A21\u677F> [--dir <\u8DEF\u5F84>] [--no-install] [--force] [--json]
@@ -1606,28 +1856,42 @@ var HELP = `fulgurjs \u2014 Vite Module Federation CLI (@fulgurjs/federation)
1606
1856
  \u4FDD\u7559\u4F60\u7684\u7248\u672C\uFF08\u7EDD\u4E0D\u6539\u5199\uFF09\uFF1B--json \u65F6 stdout \u4EC5\u8F93\u51FA\u7ED3\u679C
1607
1857
  JSON\uFF0C\u8FDB\u5EA6\u4E0E\u5B89\u88C5\u65E5\u5FD7\u8D70 stderr
1608
1858
  \u5DF2\u6709\u9879\u76EE\uFF08\u5355\u9879\u76EE fulgurjs.config.ts \u5728\u5E94\u7528\u6839\u76EE\u5F55\uFF1B\u547D\u4EE4\u9ED8\u8BA4\u8BFB ./fulgurjs.config.ts\uFF09\uFF1A
1609
- fulgurjs init [--template <path>] [--force] \u751F\u6210\u5355\u9879\u76EE fulgurjs.config.ts \u8D77\u6B65\u6A21\u677F
1610
- \uFF08\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF1B--template \u662F\u8F93\u51FA\u8DEF\u5F84\uFF09
1859
+ fulgurjs init [--out <\u8DEF\u5F84>] [--framework vue|react] [--role consumer|provider|dual] [--force]
1860
+ \u751F\u6210\u5355\u9879\u76EE fulgurjs.config.ts \u8D77\u6B65\u6A21\u677F\uFF08\u6700\u5C0F\u6709\u6548\u914D\u7F6E\uFF09\uFF1A
1861
+ \u6846\u67B6\u9ED8\u8BA4\u4ECE package.json \u4F9D\u8D56\u5224\u65AD\uFF08\u5224\u65AD\u4E0D\u4E86\u8981\u6C42\u663E\u5F0F --framework\uFF09\uFF1B
1862
+ \u89D2\u8272\u7F3A\u7701 dual\uFF1B--out \u662F\u8F93\u51FA\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ./fulgurjs.config.ts\uFF1B
1863
+ \u65E7 --template \u5199\u6CD5\u4ECD\u63A5\u53D7\uFF0C\u89C1\u8FC1\u79FB\u6307\u5357\uFF09\uFF1B--force \u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6A21\u677F
1611
1864
  fulgurjs init --config <path> \u6821\u9A8C\u914D\u7F6E\uFF1B\u8F93\u51FA federation(fulgurjsConfig) \u63A5\u5165\u5757
1612
- \u4E0E\u63A5\u5165\u6838\u5BF9\u6E05\u5355
1865
+ \u4E0E\u6309\u89D2\u8272\u7684\u63A5\u5165\u6838\u5BF9\u6E05\u5355
1613
1866
  fulgurjs explain [--config <path>] [--json] \u89E3\u91CA\u672C\u5E94\u7528\u6709\u6548\u8054\u90A6\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE\uFF08\u7EAF\u672C\u5730\uFF0C\u65E0\u7F51\u7EDC\uFF09
1614
1867
  fulgurjs check-pages [--config <path>] [--site <URL>]
1615
1868
  [--manifest <remote>=<\u8DEF\u5F84|URL>]... [--require-verified] [--json]
1616
- \u6838\u5BF9\u5BBF\u4E3B\u9875\u9762\u8868\u4E0E\u8FDC\u7A0B exposes\uFF08\u5BBF\u4E3B\u9879\u76EE\u8FD0\u884C\uFF1B
1869
+ \u6838\u5BF9\u5BBF\u4E3B\u9875\u9762\u8868\u4E0E\u8FDC\u7A0B exposes\uFF08\u9010\u9875\u63A5\u5165\u7684\u5BBF\u4E3B\u8FD0\u884C\uFF1B
1870
+ \u672A\u914D\u7F6E hostPages \u65F6\u660E\u786E\u63D0\u793A\u4E0D\u9002\u7528\uFF1B
1617
1871
  manifest \u6765\u6E90\u4F18\u5148\u7EA7 --manifest > --site/prod \u63A8\u5BFC\uFF0C
1618
1872
  \u663E\u5F0F\u6307\u5B9A\u6765\u6E90\u5931\u8D25\u4E0D\u56DE\u9000\uFF1B\u786E\u5B9A\u6027\u9519\u8BEF\u975E\u96F6\u9000\u51FA\uFF1B
1619
1873
  --require-verified \u65F6\u65E0\u6CD5\u9A8C\u8BC1\u4E5F\u975E\u96F6\uFF09
1620
- \u90E8\u7F72\u4F53\u68C0\uFF08--apps \u662F\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF0C\u8FDC\u7A0B\u90E8\u7F72\u5728 /remote-a/ \u5C31\u5199 remote-a\uFF09\uFF1A
1621
- fulgurjs doctor --base <URL> --apps <a,b,c> [--dev] [--json] [--chunk-sample N]
1874
+ \u7AEF\u53E3\u53D8\u66F4\uFF08\u6A21\u677F\u5DE5\u7A0B\uFF1B\u9ED8\u8BA4\u9884\u89C8\uFF0C--write \u624D\u5199\u5165\uFF09\uFF1A
1875
+ fulgurjs port <\u5E94\u7528> <\u65B0\u7AEF\u53E3> [--write] \u4E00\u6B21\u66F4\u65B0\u56DB\u5904\uFF1A\u5E94\u7528 package.json dev/preview\u3001
1876
+ \u5BBF\u4E3B fulgurjs.config.ts \u8BE5\u8FDC\u7A0B dev \u5730\u5740\u3001
1877
+ scripts/dev.config.json\u3001README \u7AEF\u53E3\u8868
1878
+ \u90E8\u7F72\u4F53\u68C0\uFF08--apps \u5FC5\u586B\uFF1A\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF0C\u8FDC\u7A0B\u90E8\u7F72\u5728 /remote-a/ \u5C31\u5199 remote-a\uFF1B
1879
+ '.' \u8868\u793A\u90E8\u7F72\u5728\u7AD9\u70B9\u6839\uFF1B\u6761\u76EE\u53EF\u5199\u5B8C\u6574 URL \u68C0\u67E5\u591A origin\uFF1B\u4E0D\u505A\u4EFB\u4F55\u9ED8\u8BA4\u731C\u6D4B\uFF09\uFF1A
1880
+ fulgurjs doctor --base <URL> --apps <a,b,c> [--entry <\u6587\u4EF6\u540D>] [--no-entry]
1881
+ [--no-manifest] [--no-html] [--dev] [--json] [--chunk-sample N]
1882
+ \u9ED8\u8BA4\u68C0\u67E5 remoteEntry/manifest/index.html\uFF1B
1883
+ \u7EAF\u5BBF\u4E3B\u7528 --no-entry\uFF0C\u81EA\u5B9A\u4E49\u5165\u53E3\u6587\u4EF6\u540D\u7528 --entry\uFF0C
1884
+ \u5408\u6CD5\u5173\u95ED manifest \u7528 --no-manifest\uFF0C\u65E0\u9875\u9762\u7528 --no-html
1622
1885
  fulgurjs --help
1623
1886
 
1624
1887
  \u793A\u4F8B\uFF1A
1625
1888
  fulgurjs create vue-vue --dir my-federation # \u521B\u5EFA\u5B8C\u6574\u5DE5\u7A0B\u5E76\u5B89\u88C5\u4F9D\u8D56\uFF08\u63A8\u8350\u7684\u65B0\u9879\u76EE\u8D77\u70B9\uFF09
1626
- fulgurjs init # \u5DF2\u6709\u9879\u76EE\uFF1A\u5F53\u524D\u76EE\u5F55\u5199 fulgurjs.config.ts\uFF08\u5DF2\u5B58\u5728\u5219\u62D2\u7EDD\uFF0C--force \u8986\u76D6\uFF09
1889
+ fulgurjs init --framework react --role consumer # \u5DF2\u6709 React \u9879\u76EE\u6309\u6D88\u8D39\u65B9\u751F\u6210\u8D77\u6B65\u914D\u7F6E
1627
1890
  fulgurjs explain # \u89E3\u91CA\u672C\u5E94\u7528\uFF08--config \u6307\u5411\u5176\u4ED6\u8DEF\u5F84\u65F6\u663E\u5F0F\u4F20\uFF09
1628
1891
  fulgurjs check-pages --site http://your-site # \u6309 remotes prod \u5730\u5740\u63A8\u5BFC\u8FDC\u7A0B manifest \u6838\u5BF9
1629
1892
  fulgurjs check-pages --manifest remote-a=https://cdn.example.com/remote-a/fulgurjs-manifest.json
1630
1893
  fulgurjs doctor --base http://your-site --apps my-app,remote-a
1894
+
1631
1895
  `;
1632
1896
  async function main() {
1633
1897
  const argv = process.argv.slice(2);
@@ -1653,7 +1917,7 @@ async function main() {
1653
1917
  return out;
1654
1918
  };
1655
1919
  if (cmd === "init") {
1656
- const { writeConfigTemplate: writeConfigTemplate2, inspectConfig: inspectConfig2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1920
+ const { writeConfigTemplate: writeConfigTemplate2, inspectConfig: inspectConfig2, detectFrameworkFromPackageJson: detectFrameworkFromPackageJson2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1657
1921
  const configPath = argOf("--config");
1658
1922
  if (configPath) {
1659
1923
  try {
@@ -1664,15 +1928,56 @@ async function main() {
1664
1928
  return 2;
1665
1929
  }
1666
1930
  }
1667
- const target = resolve(argOf("--template") ?? "fulgurjs.config.ts");
1668
- const result = await writeConfigTemplate2(target, has("--force"));
1931
+ if (has("--template")) {
1932
+ console.error(
1933
+ "[fulgurjs:init] --template \u5DF2\u66F4\u540D --out\uFF08\u8F93\u51FA\u8DEF\u5F84\uFF09\n\u6839\u56E0\uFF1A--template \u5728 create \u91CC\u6307\u6A21\u677F\u540D\uFF0C\u5728 init \u91CC\u5374\u662F\u8F93\u51FA\u8DEF\u5F84\u2014\u2014\u540C\u540D\u4E0D\u540C\u4E49\uFF086.0.0 \u53C2\u6570\u6536\u655B\uFF09\n\u4FEE\u6CD5\uFF1Afulgurjs init --out <\u8DEF\u5F84>\uFF08\u672C\u8F6E\u4ECD\u63A5\u53D7 --template \u4F46\u6309 --out \u89E3\u91CA\uFF0C\u540E\u7EED\u7248\u672C\u79FB\u9664\uFF09"
1934
+ );
1935
+ }
1936
+ const roleArg = argOf("--role") ?? "dual";
1937
+ if (!["consumer", "provider", "dual"].includes(roleArg)) {
1938
+ console.error(`[fulgurjs:init] \u672A\u77E5\u89D2\u8272\uFF1A${roleArg}\uFF08\u53EF\u9009 consumer=\u7EAF\u6D88\u8D39\u65B9 / provider=\u7EAF\u63D0\u4F9B\u65B9 / dual=\u53CC\u89D2\u8272\uFF09`);
1939
+ return 2;
1940
+ }
1941
+ let framework = argOf("--framework");
1942
+ if (framework && !["vue", "react"].includes(framework)) {
1943
+ console.error(`[fulgurjs:init] \u672A\u77E5\u6846\u67B6\uFF1A${framework}\uFF08\u53EF\u9009 vue / react\uFF09`);
1944
+ return 2;
1945
+ }
1946
+ if (!framework) {
1947
+ try {
1948
+ const pkg = JSON.parse(await import("node:fs").then((m) => m.promises.readFile(resolve("package.json"), "utf8")));
1949
+ framework = detectFrameworkFromPackageJson2(pkg) ?? void 0;
1950
+ } catch {
1951
+ }
1952
+ }
1953
+ if (!framework) {
1954
+ if (process.stdin.isTTY) {
1955
+ const readline = await import("node:readline/promises");
1956
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1957
+ const answer = (await rl.question("\u65E0\u6CD5\u4ECE package.json \u5224\u65AD\u6846\u67B6\uFF0C\u8BF7\u9009\u62E9\uFF08vue / react\uFF09\uFF1A")).trim();
1958
+ rl.close();
1959
+ if (answer !== "vue" && answer !== "react") {
1960
+ console.error("[fulgurjs:init] \u53EA\u652F\u6301 vue / react\uFF1B\u8BF7\u663E\u5F0F\u91CD\u8DD1 fulgurjs init --framework vue|react");
1961
+ return 2;
1962
+ }
1963
+ framework = answer;
1964
+ } else {
1965
+ console.error(
1966
+ "[fulgurjs:init] \u65E0\u6CD5\u4ECE package.json \u5224\u65AD\u6846\u67B6\uFF08vue/react \u4F9D\u8D56\u7F3A\u5931\u6216\u5E76\u5B58\uFF09\u3002\n \u4FEE\u6CD5\uFF1A\u663E\u5F0F\u6307\u5B9A fulgurjs init --framework vue|react\uFF08--role consumer|provider|dual \u53EF\u9009\uFF0C\u9ED8\u8BA4 dual\uFF09"
1967
+ );
1968
+ return 2;
1969
+ }
1970
+ }
1971
+ const target = resolve(argOf("--out") ?? argOf("--template") ?? "fulgurjs.config.ts");
1972
+ const result = await writeConfigTemplate2(target, has("--force"), { framework, role: roleArg });
1669
1973
  if (result === "exists") {
1670
1974
  console.error(`[fulgurjs:init] ${target} \u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u8986\u76D6\uFF08--force \u5F3A\u5236\u8986\u76D6\uFF09`);
1671
1975
  return 2;
1672
1976
  }
1673
- console.log(`[fulgurjs:init] \u5DF2\u751F\u6210\u5355\u9879\u76EE\u8D77\u6B65\u6A21\u677F ${target}
1977
+ const entry = framework === "react" ? "@fulgurjs/federation/react" : "@fulgurjs/federation/vue";
1978
+ console.log(`[fulgurjs:init] \u5DF2\u751F\u6210${framework === "react" ? " React" : " Vue"} ${roleArg === "dual" ? "\u53CC\u89D2\u8272" : roleArg === "consumer" ? "\u7EAF\u6D88\u8D39\u65B9" : "\u7EAF\u63D0\u4F9B\u65B9"}\u8D77\u6B65\u6A21\u677F ${target}
1674
1979
  \u540E\u7EED\u6B65\u9AA4\uFF1A
1675
- 1. \u7F16\u8F91 fulgurjs.config.ts\uFF1A\u586B\u5165\u5BB9\u5668\u540D/exposes/remotes/shared\uFF08\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF09
1980
+ 1. \u7F16\u8F91 fulgurjs.config.ts\uFF1A\u586B\u5165\u5BB9\u5668\u540D/${roleArg === "provider" ? "exposes" : roleArg === "consumer" ? "remotes" : "exposes/remotes"}/shared\uFF08\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF09
1676
1981
  2. vite.config.ts \u63A5\u5165\uFF08\u4EC5\u4E24\u884C\u8054\u90A6\u76F8\u5173\u4EE3\u7801\uFF09\uFF1A
1677
1982
  import federation from '@fulgurjs/federation'
1678
1983
  import fulgurjsConfig from './fulgurjs.config'
@@ -1680,8 +1985,10 @@ async function main() {
1680
1985
  3. npx fulgurjs explain # \u6838\u5BF9\u6709\u6548\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE
1681
1986
  4. \u90E8\u7F72\u540E\uFF1Anpx fulgurjs doctor --base <URL> --apps <\u90E8\u7F72\u5B50\u76EE\u5F55>
1682
1987
  \uFF08--apps \u662F\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF0C\u5982\u8FDC\u7A0B\u90E8\u7F72\u5728 /my-remote/ \u5C31\u5199 my-remote\uFF0C\u4E0D\u662F\u5BB9\u5668\u540D\uFF09
1683
- \u8BF4\u660E\uFF1A\u52A0\u8F7D\u666E\u901A\u7EC4\u4EF6/\u6A21\u5757\u4E0D\u9700\u8981\u9875\u9762\u8868\u3002\u53EA\u6709\u5F53\u5BBF\u4E3B\u7528\u300C\u9875\u9762\u8DEF\u7531\u8868 \u2192 \u8FDC\u7A0B\u9875\u9762\u300D\u65B9\u5F0F\u63A5\u5165\u65F6\uFF0C
1684
- \u624D\u5728 fulgurjs.config.ts \u8FFD\u52A0\u5177\u540D\u5BFC\u51FA hostPages\uFF0C\u5E76\u8FD0\u884C npx fulgurjs check-pages --site <\u7AD9\u70B9> \u6838\u5BF9\u3002`);
1988
+ \u5E94\u7528\u4EE3\u7801 API \u5165\u53E3\uFF1A${entry}\uFF08\u6846\u67B6\u65E0\u5173\u6A21\u5757\u7528 /runtime\uFF1B\u8BF4\u660E\u89C1\u6A21\u677F\u5934\u6CE8\u91CA\u4E0E docs \u6587\u6863\u4E2D\u5FC3\uFF09\u3002
1989
+ \u8BF4\u660E\uFF1A\u52A0\u8F7D\u666E\u901A\u7EC4\u4EF6/\u6A21\u5757\u4E0D\u9700\u8981\u9875\u9762\u8868\u3002\u53EA\u6709\u5F53\u5BBF\u4E3B\u7528\u300C\u9875\u9762\u8DEF\u7531\u8868 \u2192 \u8FDC\u7A0B\u9875\u9762\u300D\u65B9\u5F0F\u9010\u9875\u63A5\u5165\u65F6\uFF0C
1990
+ \u624D\u5728 fulgurjs.config.ts \u8FFD\u52A0\u5177\u540D\u5BFC\u51FA hostPages\uFF0C\u5E76\u8FD0\u884C npx fulgurjs check-pages --site <\u7AD9\u70B9> \u6838\u5BF9\uFF1B
1991
+ \u5B8C\u6574\u5B50\u5E94\u7528\u6865\u63A5\u4E0D\u9010\u9875\u767B\u8BB0\u5185\u90E8\u9875\u9762\uFF0C\u4E1A\u52A1\u83DC\u5355\u4E0E Router \u5F52\u5E94\u7528\u81EA\u5DF1\u7BA1\u7406\u3002`);
1685
1992
  return 0;
1686
1993
  }
1687
1994
  if (cmd === "create") {
@@ -1760,22 +2067,66 @@ async function main() {
1760
2067
  return 2;
1761
2068
  }
1762
2069
  }
2070
+ if (cmd === "port") {
2071
+ const app = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
2072
+ const portArg = argv[2] && !argv[2].startsWith("-") ? argv[2] : void 0;
2073
+ if (!app || !portArg || !/^\d+$/.test(portArg)) {
2074
+ console.error("[fulgurjs:port] \u7528\u6CD5\uFF1Afulgurjs port <\u5E94\u7528\u540D> <\u65B0\u7AEF\u53E3> [--write]\uFF08\u9ED8\u8BA4\u53EA\u9884\u89C8\uFF09");
2075
+ return 2;
2076
+ }
2077
+ const { planPortChange: planPortChange2, applyPortChange: applyPortChange2, formatPortPlan: formatPortPlan2 } = await Promise.resolve().then(() => (init_port(), port_exports));
2078
+ try {
2079
+ const plan = planPortChange2(process.cwd(), app, Number(portArg));
2080
+ if (has("--write")) {
2081
+ const written = applyPortChange2(process.cwd(), plan);
2082
+ console.log(`[fulgurjs:port] \u5DF2\u5199\u5165 ${written} \u4E2A\u6587\u4EF6\uFF08${app} ${plan.from} \u2192 ${plan.to}\uFF09\u3002\u56DE\u9000\uFF1Agit checkout <file>`);
2083
+ for (const n of plan.notes) console.log(` \u6CE8\uFF1A${n}`);
2084
+ } else {
2085
+ console.log(formatPortPlan2(process.cwd(), plan));
2086
+ }
2087
+ return 0;
2088
+ } catch (e) {
2089
+ console.error(String(e.message ?? e));
2090
+ return 2;
2091
+ }
2092
+ }
1763
2093
  if (cmd === "doctor") {
1764
2094
  const base = argOf("--base");
1765
- const appsRaw = argOf("--apps") ?? "main";
2095
+ const appsRaw = argOf("--apps");
1766
2096
  if (!base) {
1767
2097
  console.error("[fulgurjs:doctor] \u7F3A\u5C11 --base <URL>\uFF08\u7AD9\u70B9\u6839\u5730\u5740\uFF0C\u5982 http://your-site\uFF09");
1768
2098
  return 2;
1769
2099
  }
2100
+ if (!appsRaw) {
2101
+ console.error(
2102
+ '[fulgurjs:doctor] \u7F3A\u5C11 --apps <\u5B50\u76EE\u5F55,...>\u2014\u2014doctor \u4E0D\u731C\u9ED8\u8BA4\u5E94\u7528\u540D\u3002\n \u4FEE\u6CD5\uFF1A--apps \u4F20\u7AD9\u70B9\u6839\u4E0B\u7684\u90E8\u7F72\u5B50\u76EE\u5F55\uFF08\u8FDC\u7A0B\u90E8\u7F72\u5728 /remote-a/ \u5C31\u5199 remote-a\uFF1B\u90E8\u7F72\u5728\u7AD9\u70B9\u6839\u5199 "."\uFF1B\n \u591A origin \u76F4\u63A5\u5199\u5B8C\u6574 URL\uFF09\u3002\u7EAF\u5BBF\u4E3B\u52A0 --no-entry\uFF0C\u81EA\u5B9A\u4E49\u5165\u53E3\u6587\u4EF6\u540D\u7528 --entry <\u6587\u4EF6\u540D>'
2103
+ );
2104
+ return 2;
2105
+ }
1770
2106
  const apps = appsRaw.split(",").map((s) => s.trim()).filter(Boolean);
1771
2107
  const { checks, failed } = await runDoctor({
1772
2108
  base,
1773
2109
  apps,
1774
2110
  dev: has("--dev"),
1775
- chunkSample: argOf("--chunk-sample") ? Number(argOf("--chunk-sample")) : void 0
2111
+ chunkSample: argOf("--chunk-sample") ? Number(argOf("--chunk-sample")) : void 0,
2112
+ ...argOf("--entry") ? { entry: argOf("--entry") } : {},
2113
+ noEntry: has("--no-entry"),
2114
+ noManifest: has("--no-manifest"),
2115
+ noHtml: has("--no-html")
1776
2116
  });
1777
2117
  if (has("--json")) {
1778
- console.log(JSON.stringify({ base, apps, dev: has("--dev"), checks, failed }, null, 2));
2118
+ console.log(JSON.stringify({
2119
+ base,
2120
+ apps,
2121
+ dev: has("--dev"),
2122
+ mode: {
2123
+ entry: has("--no-entry") ? "(no-entry)" : argOf("--entry") ?? "fulgurjs-remoteEntry.js",
2124
+ manifest: has("--no-manifest") ? "(disabled)" : "fulgurjs-manifest.json",
2125
+ html: has("--no-html") ? "(skipped)" : "index.html"
2126
+ },
2127
+ checks,
2128
+ failed
2129
+ }, null, 2));
1779
2130
  } else {
1780
2131
  console.log(formatDoctorReport(checks));
1781
2132
  }