@chrisluyi/daas-cli 1.4.0 → 1.5.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.
package/README.md CHANGED
@@ -11,8 +11,8 @@ MFE apps have **no build config files** — no `rsbuild.config.ts`, no `vitest.c
11
11
  | Package | Version | Description |
12
12
  |---|---|---|
13
13
  | [`@chrisluyi/daas-cli`](https://www.npmjs.com/package/@chrisluyi/daas-cli) | 1.1.0 | CLI binary — install this as your only devDependency |
14
- | [`@chrisluyi/rsbuild-config`](https://www.npmjs.com/package/@chrisluyi/rsbuild-config) | 1.1.0 | rsbuild config factory + vitest config — arrives transitively |
15
- | [`@chrisluyi/template`](https://www.npmjs.com/package/@chrisluyi/template) | 1.0.0 | MFE scaffold templates (services / containers / components structure) |
14
+ | [`@chrisluyi/daas-rsbuild-config`](https://www.npmjs.com/package/@chrisluyi/daas-rsbuild-config) | 1.1.0 | rsbuild config factory + vitest config — arrives transitively |
15
+ | [`@chrisluyi/daas-template`](https://www.npmjs.com/package/@chrisluyi/daas-template) | 1.0.0 | MFE scaffold templates (services / containers / components structure) |
16
16
 
17
17
  ---
18
18
 
package/dist/index.js CHANGED
@@ -912,6 +912,9 @@ var require_picocolors = __commonJS((exports, module) => {
912
912
  module.exports.createColors = createColors2;
913
913
  });
914
914
 
915
+ // src/index.ts
916
+ import { createLogger as createLogger5, setLogger as setLogger5 } from "@chrisluyi/daas-logger";
917
+
915
918
  // ../../node_modules/.bun/consola@3.4.2/node_modules/consola/dist/core.mjs
916
919
  var LogLevels = {
917
920
  silent: Number.NEGATIVE_INFINITY,
@@ -2311,34 +2314,19 @@ async function runMain(cmd, opts = {}) {
2311
2314
  }
2312
2315
  }
2313
2316
 
2314
- // src/index.ts
2315
- import { createLogger as createLogger5, setLogger as setLogger5 } from "@chrisluyi/logger";
2316
-
2317
- // src/logger/consolaTransport.ts
2318
- function consolaTransport() {
2319
- return (entry) => {
2320
- switch (entry.level) {
2321
- case "debug":
2322
- consola.debug(entry.message, entry.context ?? "");
2323
- break;
2324
- case "info":
2325
- consola.info(entry.message);
2326
- break;
2327
- case "warn":
2328
- consola.warn(entry.message);
2329
- break;
2330
- case "error":
2331
- consola.fatal(entry.message);
2332
- break;
2333
- }
2334
- };
2335
- }
2336
-
2337
2317
  // src/commands/build.ts
2338
- import { createConfig } from "@chrisluyi/rsbuild-config";
2339
- import { createLogger, setLogger, apiTransport } from "@chrisluyi/logger";
2318
+ import { apiTransport, createLogger, setLogger } from "@chrisluyi/daas-logger";
2319
+ import { createConfig } from "@chrisluyi/daas-rsbuild-config";
2340
2320
  import { createRsbuild } from "@rsbuild/core";
2341
2321
 
2322
+ // src/bridge.ts
2323
+ import { mkdirSync, writeFileSync } from "fs";
2324
+ import { dirname, relative, resolve as resolve2, sep as sep2 } from "path";
2325
+ import {
2326
+ BRIDGE_EXPOSE_KEY,
2327
+ bridgeWrapperModule
2328
+ } from "@chrisluyi/daas-rsbuild-config";
2329
+
2342
2330
  // src/daas-config.ts
2343
2331
  import { existsSync, readFileSync } from "fs";
2344
2332
  import { resolve } from "path";
@@ -2356,21 +2344,64 @@ function readDaasConfig(cwd = process.cwd()) {
2356
2344
  }
2357
2345
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
2358
2346
  const daas = pkg.daas;
2359
- if (!daas?.configUrl) {
2360
- throw new ConfigError("No daas config found in package.json. Run daas init first.");
2361
- }
2362
2347
  return {
2363
- configUrl: daas.configUrl,
2364
- port: daas.port ?? 3000,
2365
- coverage: daas.coverage,
2366
- setupFiles: daas.setupFiles,
2367
- logUrl: daas.logUrl
2348
+ configUrl: daas?.configUrl,
2349
+ port: daas?.port ?? 3000,
2350
+ coverage: daas?.coverage,
2351
+ setupFiles: daas?.setupFiles,
2352
+ logUrl: daas?.logUrl,
2353
+ bridge: daas?.bridge ?? false
2354
+ };
2355
+ }
2356
+
2357
+ // src/bridge.ts
2358
+ function prepareBridgeBuild(cwd, manifest) {
2359
+ const exposeValues = Object.values(manifest.exposes);
2360
+ if (exposeValues.length !== 1) {
2361
+ throw new ConfigError(`Bridge MFEs must expose exactly one root component (found ${exposeValues.length}). ` + "A bridge remote is a single full sub-app; expose only its root in mf.manifest.json.");
2362
+ }
2363
+ const wrapperAbsPath = resolve2(cwd, ".daas", "export-app.tsx");
2364
+ const rootAbs = resolve2(cwd, exposeValues[0]);
2365
+ let rootImport = relative(dirname(wrapperAbsPath), rootAbs).split(sep2).join("/");
2366
+ if (!rootImport.startsWith("."))
2367
+ rootImport = `./${rootImport}`;
2368
+ return {
2369
+ manifest: {
2370
+ name: manifest.name,
2371
+ exposes: { [BRIDGE_EXPOSE_KEY]: "./.daas/export-app" }
2372
+ },
2373
+ wrapperAbsPath,
2374
+ wrapperContent: bridgeWrapperModule(rootImport)
2375
+ };
2376
+ }
2377
+ function writeBridgeWrapper(prep) {
2378
+ mkdirSync(dirname(prep.wrapperAbsPath), { recursive: true });
2379
+ writeFileSync(prep.wrapperAbsPath, prep.wrapperContent);
2380
+ }
2381
+
2382
+ // src/logger/consolaTransport.ts
2383
+ function consolaTransport() {
2384
+ return (entry) => {
2385
+ switch (entry.level) {
2386
+ case "debug":
2387
+ consola.debug(entry.message, entry.context ?? "");
2388
+ break;
2389
+ case "info":
2390
+ consola.info(entry.message);
2391
+ break;
2392
+ case "warn":
2393
+ consola.warn(entry.message);
2394
+ break;
2395
+ case "error":
2396
+ consola.fatal(entry.message);
2397
+ break;
2398
+ }
2368
2399
  };
2369
2400
  }
2370
2401
 
2371
2402
  // src/manifest.ts
2372
2403
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2373
- import { resolve as resolve2 } from "path";
2404
+ import { resolve as resolve3 } from "path";
2374
2405
 
2375
2406
  class ManifestError extends Error {
2376
2407
  constructor(message) {
@@ -2379,7 +2410,7 @@ class ManifestError extends Error {
2379
2410
  }
2380
2411
  }
2381
2412
  function readManifest(cwd = process.cwd()) {
2382
- const manifestPath = resolve2(cwd, "mf.manifest.json");
2413
+ const manifestPath = resolve3(cwd, "mf.manifest.json");
2383
2414
  if (existsSync2(manifestPath)) {
2384
2415
  try {
2385
2416
  const raw = JSON.parse(readFileSync2(manifestPath, "utf8"));
@@ -2393,7 +2424,7 @@ function readManifest(cwd = process.cwd()) {
2393
2424
  return deriveFromPackageJson(cwd);
2394
2425
  }
2395
2426
  function deriveFromPackageJson(cwd) {
2396
- const pkgPath = resolve2(cwd, "package.json");
2427
+ const pkgPath = resolve3(cwd, "package.json");
2397
2428
  if (!existsSync2(pkgPath)) {
2398
2429
  throw new ManifestError("No package.json found. Are you in an MFE directory?");
2399
2430
  }
@@ -2402,14 +2433,16 @@ function deriveFromPackageJson(cwd) {
2402
2433
  return { name, exposes: { "./App": "./src/App" } };
2403
2434
  }
2404
2435
  function normalizeManifest(raw, cwd) {
2405
- const base = deriveFromPackageJson(cwd);
2436
+ const hasName = typeof raw.name === "string";
2437
+ const hasExposes = raw.exposes != null;
2438
+ const base = hasName && hasExposes ? null : deriveFromPackageJson(cwd);
2406
2439
  return {
2407
- name: typeof raw.name === "string" ? raw.name : base.name,
2408
- exposes: raw.exposes != null ? raw.exposes : base.exposes
2440
+ name: hasName ? raw.name.replace(/-/g, "_") : base.name,
2441
+ exposes: hasExposes ? raw.exposes : base.exposes
2409
2442
  };
2410
2443
  }
2411
2444
  function stripScope(name) {
2412
- return name.replace(/^@[^/]+\//, "");
2445
+ return name.replace(/^@[^/]+\//, "").replace(/-/g, "_");
2413
2446
  }
2414
2447
 
2415
2448
  // src/output.ts
@@ -2440,15 +2473,15 @@ function getCliVersion() {
2440
2473
  }
2441
2474
 
2442
2475
  // src/remote-config.ts
2443
- import { SUPPORTED_CONFIG_VERSION } from "@chrisluyi/rsbuild-config";
2476
+ import { SUPPORTED_CONFIG_VERSION } from "@chrisluyi/daas-rsbuild-config";
2444
2477
  // src/fallback-config.json
2445
2478
  var fallback_config_default = {
2446
2479
  version: "1.0.0",
2447
2480
  minCliVersion: "1.0.0",
2448
2481
  releaseNotesUrl: "https://github.com/your-org/daas-cli/releases",
2449
2482
  shared: {
2450
- react: "18.3.1",
2451
- "react-dom": "18.3.1"
2483
+ react: "^19.0.0",
2484
+ "react-dom": "^19.0.0"
2452
2485
  },
2453
2486
  rsbuildConfigVersion: 1
2454
2487
  };
@@ -2525,8 +2558,8 @@ function meetsMinVersion(current, min) {
2525
2558
  }
2526
2559
 
2527
2560
  // src/target.ts
2528
- import { writeFileSync } from "fs";
2529
- import { resolve as resolve3 } from "path";
2561
+ import { writeFileSync as writeFileSync2 } from "fs";
2562
+ import { resolve as resolve4 } from "path";
2530
2563
 
2531
2564
  class TargetParseError extends Error {
2532
2565
  constructor(message) {
@@ -2557,7 +2590,7 @@ function generateTsconfigDaas(cwd, target) {
2557
2590
  }
2558
2591
  }
2559
2592
  };
2560
- writeFileSync(resolve3(cwd, "tsconfig.daas.json"), `${JSON.stringify(content, null, 2)}
2593
+ writeFileSync2(resolve4(cwd, "tsconfig.daas.json"), `${JSON.stringify(content, null, 2)}
2561
2594
  `);
2562
2595
  }
2563
2596
 
@@ -2591,18 +2624,32 @@ var buildCommand = defineCommand({
2591
2624
  try {
2592
2625
  const daasConfig = readDaasConfig();
2593
2626
  setLogger(createLogger([consolaTransport(), apiTransport(daasConfig.logUrl)]));
2594
- const manifest = readManifest();
2627
+ let manifest = readManifest();
2628
+ if (daasConfig.bridge) {
2629
+ const prep = prepareBridgeBuild(process.cwd(), manifest);
2630
+ writeBridgeWrapper(prep);
2631
+ manifest = prep.manifest;
2632
+ if (!json)
2633
+ consola.info('Bridge mode: isolated React, exposing ./export-app (register host entry with loader: "bridge")');
2634
+ }
2595
2635
  if (!json)
2596
2636
  consola.start(`Building ${manifest.name}...`);
2597
- const remoteMeta = await fetchRemoteConfig(daasConfig.configUrl, true);
2598
- checkCliVersion(remoteMeta);
2599
- checkConfigVersion(remoteMeta, true);
2637
+ let remoteMeta;
2638
+ if (daasConfig.configUrl) {
2639
+ remoteMeta = await fetchRemoteConfig(daasConfig.configUrl, true);
2640
+ checkCliVersion(remoteMeta);
2641
+ checkConfigVersion(remoteMeta, true);
2642
+ } else {
2643
+ remoteMeta = loadFallback();
2644
+ consola.info("No configUrl \u2014 using built-in fallback config");
2645
+ }
2600
2646
  const target = args.target ? parseTarget(args.target) : undefined;
2601
2647
  if (target)
2602
2648
  generateTsconfigDaas(process.cwd(), target);
2603
2649
  const config = createConfig(manifest, remoteMeta, {
2604
2650
  mode: "production",
2605
- target
2651
+ target,
2652
+ bridge: daasConfig.bridge
2606
2653
  });
2607
2654
  const rsbuild = await createRsbuild({ rsbuildConfig: config });
2608
2655
  await rsbuild.build();
@@ -2647,8 +2694,8 @@ var buildCommand = defineCommand({
2647
2694
  });
2648
2695
 
2649
2696
  // src/commands/dev.ts
2650
- import { createConfig as createConfig2 } from "@chrisluyi/rsbuild-config";
2651
- import { createLogger as createLogger2, setLogger as setLogger2, apiTransport as apiTransport2 } from "@chrisluyi/logger";
2697
+ import { apiTransport as apiTransport2, createLogger as createLogger2, setLogger as setLogger2 } from "@chrisluyi/daas-logger";
2698
+ import { createConfig as createConfig2 } from "@chrisluyi/daas-rsbuild-config";
2652
2699
  import { createRsbuild as createRsbuild2 } from "@rsbuild/core";
2653
2700
  var devCommand = defineCommand({
2654
2701
  meta: { name: "dev", description: "Start MFE dev server" },
@@ -2669,12 +2716,24 @@ var devCommand = defineCommand({
2669
2716
  try {
2670
2717
  const daasConfig = readDaasConfig();
2671
2718
  setLogger2(createLogger2([consolaTransport(), apiTransport2(daasConfig.logUrl)]));
2672
- const manifest = readManifest();
2673
- const remoteMeta = await fetchRemoteConfig(daasConfig.configUrl, false);
2674
- checkCliVersion(remoteMeta);
2675
- const versionOk = checkConfigVersion(remoteMeta, false);
2676
- if (!versionOk) {
2677
- consola.warn("Config version mismatch \u2014 update daas-cli for full compatibility");
2719
+ let manifest = readManifest();
2720
+ if (daasConfig.bridge) {
2721
+ const prep = prepareBridgeBuild(process.cwd(), manifest);
2722
+ writeBridgeWrapper(prep);
2723
+ manifest = prep.manifest;
2724
+ consola.info('Bridge mode: isolated React, exposing ./export-app (register host entry with loader: "bridge")');
2725
+ }
2726
+ let remoteMeta;
2727
+ if (daasConfig.configUrl) {
2728
+ remoteMeta = await fetchRemoteConfig(daasConfig.configUrl, false);
2729
+ checkCliVersion(remoteMeta);
2730
+ const versionOk = checkConfigVersion(remoteMeta, false);
2731
+ if (!versionOk) {
2732
+ consola.warn("Config version mismatch \u2014 update daas-cli for full compatibility");
2733
+ }
2734
+ } else {
2735
+ remoteMeta = loadFallback();
2736
+ consola.info("No configUrl \u2014 using built-in fallback config");
2678
2737
  }
2679
2738
  const port = args.port ? Number(args.port) : daasConfig.port;
2680
2739
  const target = args.target ? parseTarget(args.target) : undefined;
@@ -2685,7 +2744,8 @@ var devCommand = defineCommand({
2685
2744
  const config = createConfig2(manifest, remoteMeta, {
2686
2745
  mode: "development",
2687
2746
  port,
2688
- target
2747
+ target,
2748
+ bridge: daasConfig.bridge
2689
2749
  });
2690
2750
  consola.start(`Starting ${manifest.name} dev server...`);
2691
2751
  const rsbuild = await createRsbuild2({ rsbuildConfig: config });
@@ -2726,25 +2786,86 @@ function handleError(command, e2, json) {
2726
2786
  process.exit(EXIT_CODES.GENERAL_ERROR);
2727
2787
  }
2728
2788
 
2789
+ // src/commands/host.ts
2790
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2791
+ import { resolve as resolve5 } from "path";
2792
+ import { createHostConfig } from "@chrisluyi/daas-rsbuild-config";
2793
+ import { createRsbuild as createRsbuild3 } from "@rsbuild/core";
2794
+ function sanitizeName(name) {
2795
+ return name.replace(/^@[^/]+\//, "").replace(/[^a-zA-Z0-9]/g, "_");
2796
+ }
2797
+ function readHostPackage(cwd = process.cwd()) {
2798
+ const pkgPath = resolve5(cwd, "package.json");
2799
+ if (!existsSync3(pkgPath)) {
2800
+ throw new Error("No package.json found. Are you in a host app directory?");
2801
+ }
2802
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
2803
+ const daas = pkg.daas ?? {};
2804
+ return {
2805
+ name: sanitizeName(pkg.name ?? "host"),
2806
+ port: daas.port ?? 3000
2807
+ };
2808
+ }
2809
+ var hostDevCommand = defineCommand({
2810
+ meta: { name: "dev", description: "Start host shell dev server" },
2811
+ async run() {
2812
+ try {
2813
+ const { name, port } = readHostPackage();
2814
+ const config = createHostConfig(name, { mode: "development", port });
2815
+ consola.start(`Starting ${name} dev server...`);
2816
+ const rsbuild = await createRsbuild3({ rsbuildConfig: config });
2817
+ const server = await rsbuild.createDevServer();
2818
+ await server.listen();
2819
+ consola.success(`Ready on http://localhost:${port}`);
2820
+ } catch (e2) {
2821
+ consola.fatal(e2);
2822
+ process.exit(1);
2823
+ }
2824
+ }
2825
+ });
2826
+ var hostBuildCommand = defineCommand({
2827
+ meta: { name: "build", description: "Build host shell for production" },
2828
+ async run() {
2829
+ try {
2830
+ const { name } = readHostPackage();
2831
+ const config = createHostConfig(name, { mode: "production" });
2832
+ consola.start(`Building ${name}...`);
2833
+ const rsbuild = await createRsbuild3({ rsbuildConfig: config });
2834
+ await rsbuild.build();
2835
+ consola.success("Build complete");
2836
+ } catch (e2) {
2837
+ consola.fatal(e2);
2838
+ process.exit(1);
2839
+ }
2840
+ }
2841
+ });
2842
+ var hostCommand = defineCommand({
2843
+ meta: { name: "host", description: "Commands for host shell apps" },
2844
+ subCommands: {
2845
+ dev: hostDevCommand,
2846
+ build: hostBuildCommand
2847
+ }
2848
+ });
2849
+
2729
2850
  // src/commands/info.ts
2730
- import { createLogger as createLogger3, setLogger as setLogger3, apiTransport as apiTransport3 } from "@chrisluyi/logger";
2851
+ import { apiTransport as apiTransport3, createLogger as createLogger3, setLogger as setLogger3 } from "@chrisluyi/daas-logger";
2731
2852
 
2732
2853
  // src/init/monorepo.ts
2733
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2734
- import { dirname, resolve as resolve4 } from "path";
2854
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
2855
+ import { dirname as dirname2, resolve as resolve6 } from "path";
2735
2856
  function detectMonorepo(startDir) {
2736
2857
  let dir = startDir;
2737
2858
  for (let i2 = 0;i2 < 10; i2++) {
2738
- const parent = dirname(dir);
2859
+ const parent = dirname2(dir);
2739
2860
  if (parent === dir)
2740
2861
  break;
2741
2862
  dir = parent;
2742
- if (existsSync3(resolve4(dir, "bun.lockb")))
2863
+ if (existsSync4(resolve6(dir, "bun.lockb")))
2743
2864
  return true;
2744
- const pkgPath = resolve4(dir, "package.json");
2745
- if (existsSync3(pkgPath)) {
2865
+ const pkgPath = resolve6(dir, "package.json");
2866
+ if (existsSync4(pkgPath)) {
2746
2867
  try {
2747
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
2868
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
2748
2869
  if (pkg.workspaces)
2749
2870
  return true;
2750
2871
  } catch {}
@@ -2779,9 +2900,13 @@ var infoCommand = defineCommand({
2779
2900
  }
2780
2901
  const isMonorepo = detectMonorepo(process.cwd());
2781
2902
  let remoteJson;
2782
- try {
2783
- remoteJson = await fetchRemoteConfig(daasConfig.configUrl, true);
2784
- } catch {
2903
+ if (daasConfig.configUrl) {
2904
+ try {
2905
+ remoteJson = await fetchRemoteConfig(daasConfig.configUrl, true);
2906
+ } catch {
2907
+ remoteJson = { ...loadFallback(), offline: true };
2908
+ }
2909
+ } else {
2785
2910
  remoteJson = { ...loadFallback(), offline: true };
2786
2911
  }
2787
2912
  const payload = {
@@ -2801,10 +2926,10 @@ var infoCommand = defineCommand({
2801
2926
  });
2802
2927
 
2803
2928
  // src/commands/init.ts
2804
- import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
2929
+ import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
2805
2930
  import { createRequire } from "module";
2806
- import { resolve as resolve6 } from "path";
2807
- import { scaffold } from "@chrisluyi/template";
2931
+ import { resolve as resolve8 } from "path";
2932
+ import { scaffold } from "@chrisluyi/daas-template";
2808
2933
 
2809
2934
  // ../../node_modules/.bun/@clack+core@0.3.5/node_modules/@clack/core/dist/index.mjs
2810
2935
  var import_sisteransi = __toESM(require_src(), 1);
@@ -3297,10 +3422,10 @@ var de = () => {
3297
3422
  };
3298
3423
 
3299
3424
  // src/init/copy-template.ts
3300
- import { copyFileSync, existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
3301
- import { dirname as dirname2, join } from "path";
3425
+ import { copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
3426
+ import { dirname as dirname3, join } from "path";
3302
3427
  import { fileURLToPath } from "url";
3303
- var __dirname2 = dirname2(fileURLToPath(import.meta.url));
3428
+ var __dirname2 = dirname3(fileURLToPath(import.meta.url));
3304
3429
  var TEMPLATES_DIR = join(__dirname2, "../../templates/mfe");
3305
3430
  async function copyTemplate(destDir) {
3306
3431
  const files = readdirSync(TEMPLATES_DIR);
@@ -3311,9 +3436,9 @@ async function copyTemplate(destDir) {
3311
3436
  let dest = join(destDir, destFile);
3312
3437
  if (destFile === "App.tsx" || destFile === "App.test.tsx") {
3313
3438
  dest = join(destDir, "src", destFile);
3314
- mkdirSync(join(destDir, "src"), { recursive: true });
3439
+ mkdirSync2(join(destDir, "src"), { recursive: true });
3315
3440
  }
3316
- if (existsSync4(dest)) {
3441
+ if (existsSync5(dest)) {
3317
3442
  const overwrite = await se({
3318
3443
  message: `${destFile} already exists. Overwrite?`
3319
3444
  });
@@ -3329,11 +3454,11 @@ async function copyTemplate(destDir) {
3329
3454
  }
3330
3455
 
3331
3456
  // src/init/package-json.ts
3332
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
3333
- import { resolve as resolve5 } from "path";
3457
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
3458
+ import { resolve as resolve7 } from "path";
3334
3459
  function updatePackageJson(cwd, opts) {
3335
- const pkgPath = resolve5(cwd, "package.json");
3336
- const pkg = existsSync5(pkgPath) ? JSON.parse(readFileSync4(pkgPath, "utf8")) : {};
3460
+ const pkgPath = resolve7(cwd, "package.json");
3461
+ const pkg = existsSync6(pkgPath) ? JSON.parse(readFileSync5(pkgPath, "utf8")) : {};
3337
3462
  pkg.scripts = {
3338
3463
  ...pkg.scripts ?? {},
3339
3464
  dev: "daas dev",
@@ -3342,9 +3467,10 @@ function updatePackageJson(cwd, opts) {
3342
3467
  };
3343
3468
  pkg.dependencies = {
3344
3469
  ...pkg.dependencies ?? {},
3470
+ "@module-federation/bridge-react": "*",
3345
3471
  "@radix-ui/react-tabs": "^1.1.0",
3346
3472
  ...opts.isFullTemplate ? {
3347
- "@chrisluyi/template": `^${opts.templateVersion ?? opts.cliVersion ?? "1.0.0"}`,
3473
+ "@chrisluyi/daas-template": `^${opts.templateVersion ?? opts.cliVersion ?? "1.0.0"}`,
3348
3474
  "@tanstack/react-query": "^5.0.0",
3349
3475
  zustand: "^5.0.0",
3350
3476
  immer: "^10.0.0",
@@ -3358,7 +3484,7 @@ function updatePackageJson(cwd, opts) {
3358
3484
  };
3359
3485
  }
3360
3486
  pkg.daas = { configUrl: opts.configUrl, port: opts.port };
3361
- writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
3487
+ writeFileSync3(pkgPath, `${JSON.stringify(pkg, null, 2)}
3362
3488
  `);
3363
3489
  }
3364
3490
 
@@ -3442,7 +3568,7 @@ var initCommand = defineCommand({
3442
3568
  s2.message("\u2713 Updated package.json");
3443
3569
  if (hasExtraExposes) {
3444
3570
  const manifest = { name: appName, exposes: { "./App": "./src/App" } };
3445
- writeFileSync3(resolve6(cwd, "mf.manifest.json"), `${JSON.stringify(manifest, null, 2)}
3571
+ writeFileSync4(resolve8(cwd, "mf.manifest.json"), `${JSON.stringify(manifest, null, 2)}
3446
3572
  `);
3447
3573
  s2.message("\u2713 Created mf.manifest.json (add extra exposes as needed)");
3448
3574
  }
@@ -3453,27 +3579,225 @@ var initCommand = defineCommand({
3453
3579
  function getTemplateVersion() {
3454
3580
  try {
3455
3581
  const req = createRequire(import.meta.url);
3456
- const pkg = req("@chrisluyi/template/package.json");
3582
+ const pkg = req("@chrisluyi/daas-template/package.json");
3457
3583
  return pkg.version ?? "1.0.0";
3458
3584
  } catch {
3459
3585
  return "1.0.0";
3460
3586
  }
3461
3587
  }
3462
3588
  function getPackageName(cwd) {
3463
- const pkgPath = resolve6(cwd, "package.json");
3464
- if (!existsSync6(pkgPath))
3589
+ const pkgPath = resolve8(cwd, "package.json");
3590
+ if (!existsSync7(pkgPath))
3465
3591
  return "mfe-app";
3466
3592
  try {
3467
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
3593
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
3468
3594
  return (pkg.name ?? "mfe-app").replace(/^@[^/]+\//, "");
3469
3595
  } catch {
3470
3596
  return "mfe-app";
3471
3597
  }
3472
3598
  }
3473
3599
 
3600
+ // src/region.ts
3601
+ import { spawnSync } from "child_process";
3602
+ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3603
+ import { relative as relative2, resolve as resolve9 } from "path";
3604
+
3605
+ class RegionError extends Error {
3606
+ constructor(message) {
3607
+ super(message);
3608
+ this.name = "RegionError";
3609
+ }
3610
+ }
3611
+ var DEFAULT_ALIASES = {
3612
+ "@region-config": "./src/config/regions/{region}/config",
3613
+ "@regional": "./src/config/regions/{region}/index"
3614
+ };
3615
+ function readRegionalConfig(cwd = process.cwd()) {
3616
+ const pkgPath = resolve9(cwd, "package.json");
3617
+ if (!existsSync8(pkgPath)) {
3618
+ throw new RegionError("No package.json found. Run from the project root (the daas.regional block lives there).");
3619
+ }
3620
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
3621
+ const r4 = pkg.daas?.regional ?? {};
3622
+ return {
3623
+ ...r4,
3624
+ regionsDir: r4.regionsDir ?? "src/config/regions",
3625
+ tsconfig: r4.tsconfig ?? "tsconfig.daas.json",
3626
+ extends: r4.extends ?? "./tsconfig.json",
3627
+ bunPaths: r4.bunPaths ?? false
3628
+ };
3629
+ }
3630
+ function discoverRegions(cwd, cfg) {
3631
+ if (cfg.regions && cfg.regions.length > 0)
3632
+ return cfg.regions;
3633
+ const dir = resolve9(cwd, cfg.regionsDir ?? "src/config/regions");
3634
+ if (!existsSync8(dir)) {
3635
+ throw new RegionError(`regionsDir "${cfg.regionsDir}" does not exist \u2014 set daas.regional.regionsDir or daas.regional.regions in package.json`);
3636
+ }
3637
+ const regions = readdirSync2(dir, { withFileTypes: true }).filter((e3) => e3.isDirectory()).map((e3) => e3.name).sort();
3638
+ if (regions.length === 0) {
3639
+ throw new RegionError(`regionsDir "${cfg.regionsDir}" has no region folders`);
3640
+ }
3641
+ return regions;
3642
+ }
3643
+ function renderRegionTsconfig(cwd, cfg, region) {
3644
+ const regions = discoverRegions(cwd, cfg);
3645
+ if (!regions.includes(region)) {
3646
+ throw new RegionError(`Unknown region "${region}". Known: ${regions.join(", ")}`);
3647
+ }
3648
+ const aliases = cfg.aliases ?? DEFAULT_ALIASES;
3649
+ const paths = Object.fromEntries(Object.entries(aliases).map(([k3, v3]) => [
3650
+ k3,
3651
+ [v3.replaceAll("{region}", region)]
3652
+ ]));
3653
+ const tsconfigDir = resolve9(cwd, cfg.tsconfig, "..");
3654
+ const regionsDirAbs = resolve9(cwd, cfg.regionsDir);
3655
+ const others = regions.filter((r4) => r4 !== region).map((r4) => relative2(tsconfigDir, resolve9(regionsDirAbs, r4)));
3656
+ const exclude = [...others, ...cfg.multiRegionFiles ?? []];
3657
+ const compilerOptions = {
3658
+ ...cfg.bunPaths ? { baseUrl: ".", ignoreDeprecations: "6.0" } : {},
3659
+ paths
3660
+ };
3661
+ const body = JSON.stringify({ extends: cfg.extends, compilerOptions, exclude }, null, 2);
3662
+ return `// GENERATED by \`daas region ${region}\` \u2014 do not edit; regenerate or \`daas region --reset\`.
3663
+ ${body}
3664
+ `;
3665
+ }
3666
+ function writeRegionTsconfig(cwd, cfg, region, applyTo) {
3667
+ const content = renderRegionTsconfig(cwd, cfg, region);
3668
+ const out = resolve9(cwd, cfg.tsconfig);
3669
+ writeFileSync5(out, content);
3670
+ if (applyTo) {
3671
+ writeFileSync5(resolve9(cwd, applyTo), content);
3672
+ return { generated: out, applied: resolve9(cwd, applyTo) };
3673
+ }
3674
+ return { generated: out };
3675
+ }
3676
+ function resetTsconfig(cwd, path) {
3677
+ const rel = relative2(cwd, resolve9(cwd, path));
3678
+ const show = spawnSync("git", ["show", `HEAD:${rel}`], {
3679
+ cwd,
3680
+ encoding: "utf8"
3681
+ });
3682
+ if (show.status !== 0) {
3683
+ throw new RegionError(`Cannot restore "${rel}" from git HEAD: ${show.stderr.trim() || "not a tracked file?"}`);
3684
+ }
3685
+ writeFileSync5(resolve9(cwd, path), show.stdout);
3686
+ }
3687
+ function runRegionCheck(cwd, cfg, runner = (p) => {
3688
+ const r4 = spawnSync("bunx", ["tsc", "--noEmit", "-p", p], {
3689
+ cwd,
3690
+ encoding: "utf8"
3691
+ });
3692
+ return { ok: r4.status === 0, output: `${r4.stdout ?? ""}${r4.stderr ?? ""}` };
3693
+ }) {
3694
+ const results = [];
3695
+ for (const region of discoverRegions(cwd, cfg)) {
3696
+ writeRegionTsconfig(cwd, cfg, region);
3697
+ const { ok, output } = runner(resolve9(cwd, cfg.tsconfig));
3698
+ results.push({ region, ok, output });
3699
+ }
3700
+ return results;
3701
+ }
3702
+
3703
+ // src/commands/region.ts
3704
+ var regionCommand = defineCommand({
3705
+ meta: {
3706
+ name: "region",
3707
+ description: "Region-scoped type programs: generate a tsconfig containing ONLY the selected region (mirror of the build-time region alias swap)"
3708
+ },
3709
+ args: {
3710
+ region: {
3711
+ type: "positional",
3712
+ description: "Region to select (see --list)",
3713
+ required: false
3714
+ },
3715
+ list: { type: "boolean", default: false, description: "List regions" },
3716
+ check: {
3717
+ type: "boolean",
3718
+ default: false,
3719
+ description: "Matrix gate: generate + tsc --noEmit for EVERY region; fails if any region's program fails"
3720
+ },
3721
+ apply: {
3722
+ type: "string",
3723
+ description: "Also overwrite this tsconfig with the generated content (editor + Bun follow the selection); undo with --reset"
3724
+ },
3725
+ reset: {
3726
+ type: "string",
3727
+ description: "Restore this tsconfig from git HEAD (undo --apply)"
3728
+ },
3729
+ json: {
3730
+ type: "boolean",
3731
+ default: false,
3732
+ description: "Machine-readable JSON output"
3733
+ }
3734
+ },
3735
+ async run({ args }) {
3736
+ try {
3737
+ const cwd = process.cwd();
3738
+ const cfg = readRegionalConfig(cwd);
3739
+ if (args.reset) {
3740
+ resetTsconfig(cwd, args.reset);
3741
+ if (args.json)
3742
+ writeJsonSuccess("region", { reset: args.reset });
3743
+ else
3744
+ consola.success(`Restored ${args.reset} from git HEAD`);
3745
+ return;
3746
+ }
3747
+ if (args.list) {
3748
+ const regions = discoverRegions(cwd, cfg);
3749
+ if (args.json)
3750
+ writeJsonSuccess("region", { regions });
3751
+ else
3752
+ consola.info(`Regions: ${regions.join(", ")}`);
3753
+ return;
3754
+ }
3755
+ if (args.check) {
3756
+ const results = runRegionCheck(cwd, cfg);
3757
+ for (const r4 of results) {
3758
+ if (r4.ok)
3759
+ consola.success(`region ${r4.region}`);
3760
+ else {
3761
+ consola.error(`region ${r4.region}`);
3762
+ if (!args.json && r4.output.trim())
3763
+ consola.log(r4.output.trim());
3764
+ }
3765
+ }
3766
+ const failed = results.filter((r4) => !r4.ok);
3767
+ if (args.json)
3768
+ writeJsonSuccess("region", {
3769
+ results: results.map(({ region, ok }) => ({ region, ok }))
3770
+ });
3771
+ if (failed.length > 0)
3772
+ process.exit(EXIT_CODES.GENERAL_ERROR);
3773
+ return;
3774
+ }
3775
+ if (!args.region) {
3776
+ throw new RegionError("Pass a region (see --list), or use --check / --reset");
3777
+ }
3778
+ const { generated, applied } = writeRegionTsconfig(cwd, cfg, args.region, args.apply);
3779
+ if (args.json)
3780
+ writeJsonSuccess("region", { region: args.region, generated, applied });
3781
+ else {
3782
+ consola.success(`Region-scoped tsconfig for "${args.region}" \u2192 ${generated}`);
3783
+ if (applied)
3784
+ consola.info(`Applied to ${applied} (undo: daas region --reset ${args.apply})`);
3785
+ }
3786
+ } catch (err) {
3787
+ if (err instanceof RegionError) {
3788
+ if (args.json)
3789
+ writeJsonError("region", EXIT_CODES.GENERAL_ERROR, err.message, "Check the daas.regional block in package.json (regionsDir/regions/aliases) and run with --list");
3790
+ consola.error(err.message);
3791
+ process.exit(EXIT_CODES.GENERAL_ERROR);
3792
+ }
3793
+ throw err;
3794
+ }
3795
+ }
3796
+ });
3797
+
3474
3798
  // src/commands/test.ts
3475
- import { createVitestConfig } from "@chrisluyi/rsbuild-config/vitest";
3476
- import { createLogger as createLogger4, setLogger as setLogger4, apiTransport as apiTransport4 } from "@chrisluyi/logger";
3799
+ import { apiTransport as apiTransport4, createLogger as createLogger4, setLogger as setLogger4 } from "@chrisluyi/daas-logger";
3800
+ import { createVitestConfig } from "@chrisluyi/daas-rsbuild-config/vitest";
3477
3801
  import { startVitest } from "vitest/node";
3478
3802
  var testCommand = defineCommand({
3479
3803
  meta: { name: "test", description: "Run MFE tests" },
@@ -3525,7 +3849,9 @@ var main = defineCommand({
3525
3849
  dev: devCommand,
3526
3850
  build: buildCommand,
3527
3851
  test: testCommand,
3528
- info: infoCommand
3852
+ info: infoCommand,
3853
+ host: hostCommand,
3854
+ region: regionCommand
3529
3855
  }
3530
3856
  });
3531
3857
  runMain(main);
package/dist/test.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // @bun
2
2
  // src/test.ts
3
- export * from "@chrisluyi/rsbuild-config/test";
3
+ export * from "@chrisluyi/daas-rsbuild-config/test";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrisluyi/daas-cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "daas": "./dist/index.js"
@@ -16,28 +16,25 @@
16
16
  "skill.md"
17
17
  ],
18
18
  "scripts": {
19
- "build": "bun build src/index.ts --outfile dist/index.js.tmp --target bun --external @rspack/core --external @rspack/lite-tapable --external @rsbuild/core --external @rsbuild/plugin-react --external @module-federation/rsbuild-plugin --external vitest --external @vitest/browser --external @vitest/ui --external tapable --external react-router-dom --external lightningcss --external @chrisluyi/template --external @chrisluyi/logger --external @chrisluyi/rsbuild-config && printf '%s\\n' '#!/usr/bin/env bun' | cat - dist/index.js.tmp > dist/index.js && rm dist/index.js.tmp && chmod +x dist/index.js && bun build src/test.ts --outfile dist/test.js --target bun --external vitest --external @testing-library/react --external @testing-library/user-event --external @chrisluyi/rsbuild-config",
19
+ "build": "bun build src/index.ts --outfile dist/index.js.tmp --target bun --external @rspack/core --external @rspack/lite-tapable --external @rsbuild/core --external @rsbuild/plugin-react --external @module-federation/rsbuild-plugin --external vitest --external @vitest/browser --external @vitest/ui --external tapable --external react-router-dom --external lightningcss --external @chrisluyi/daas-template --external @chrisluyi/daas-logger --external @chrisluyi/daas-rsbuild-config && printf '%s\\n' '#!/usr/bin/env bun' | cat - dist/index.js.tmp > dist/index.js && rm dist/index.js.tmp && chmod +x dist/index.js && bun build src/test.ts --outfile dist/test.js --target bun --external vitest --external @testing-library/react --external @testing-library/user-event --external @chrisluyi/daas-rsbuild-config",
20
20
  "test": "bun test --ignore 'templates/**'"
21
21
  },
22
22
  "dependencies": {
23
23
  "@clack/prompts": "^0.7.0",
24
- "@chrisluyi/logger": "workspace:*",
25
- "@chrisluyi/rsbuild-config": "workspace:*",
26
- "@chrisluyi/template": "workspace:*",
27
- "@rsbuild/core": "^1.0.0",
24
+ "@chrisluyi/daas-logger": "workspace:*",
25
+ "@chrisluyi/daas-rsbuild-config": "workspace:*",
26
+ "@chrisluyi/daas-template": "workspace:*",
27
+ "@rsbuild/core": "*",
28
28
  "citty": "^0.1.6",
29
29
  "consola": "^3.2.3",
30
- "vitest": "^2.0.0"
30
+ "vitest": "*"
31
31
  },
32
- "devDependencies": {
33
- "typescript": "^5.5.0"
32
+ "engines": {
33
+ "node": ">=20.19.0"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",
37
37
  "url": "https://github.com/Chris-LuYi/daas-cli",
38
38
  "directory": "packages/cli"
39
- },
40
- "publishConfig": {
41
- "access": "public"
42
39
  }
43
40
  }
package/skill.md CHANGED
@@ -107,7 +107,7 @@ src/
107
107
  products/default.ts ← product config layer
108
108
  ```
109
109
 
110
- ## Template Runtime (`@chrisluyi/template/runtime`)
110
+ ## Template Runtime (`@chrisluyi/daas-template/runtime`)
111
111
 
112
112
  Key utilities available in MFE components:
113
113