@tbox.cn/app-cli 0.1.0 → 0.1.3

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 (2) hide show
  1. package/dist/index.js +292 -68
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
+ import { createRequire as createRequire3 } from "module";
5
6
  import { fileURLToPath } from "url";
6
7
  import { realpathSync as realpathSync3 } from "fs";
7
8
 
@@ -383,8 +384,8 @@ async function createApp(opts) {
383
384
  }
384
385
 
385
386
  // src/commands/add.ts
386
- import { existsSync as existsSync19, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
387
- import path23 from "path";
387
+ import { existsSync as existsSync20, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
388
+ import path24 from "path";
388
389
 
389
390
  // src/expand.ts
390
391
  import { mkdir as mkdir2, rm, writeFile as writeFile3 } from "fs/promises";
@@ -405,15 +406,32 @@ function rewritePlatformRefs(deps) {
405
406
  if (isWorkspacePkg(name)) deps[name] = "catalog:";
406
407
  }
407
408
  }
409
+ function deriveModuleId(pkgName) {
410
+ return pkgName.replace(/^@tbox\.cn\/app-/, "").replace(/^@[^/]+\//, "");
411
+ }
412
+ function escapeRegExp(s) {
413
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
414
+ }
415
+ function rewriteLoadModuleSkillsCall(content, fromName, toName) {
416
+ return content.replaceAll(
417
+ new RegExp(`loadModuleSkills\\(\\s*['"]${escapeRegExp(fromName)}['"]`, "g"),
418
+ `loadModuleSkills('${toName}'`
419
+ );
420
+ }
408
421
  function rewriteExportsToSrc(pkg) {
409
422
  const exports = pkg.exports;
410
423
  if (!exports || typeof exports !== "object") return;
411
424
  const next = {};
412
425
  for (const [sub, val] of Object.entries(exports)) {
426
+ if (sub === "./package.json") {
427
+ next[sub] = val;
428
+ continue;
429
+ }
413
430
  if (typeof val !== "object" || val === null) continue;
414
431
  const base = sub === "." ? "./src/index.ts" : `./src/${sub.replace(/^\.\//, "")}/index.ts`;
415
432
  next[sub] = { types: base, import: base };
416
433
  }
434
+ if (next["./package.json"] === void 0) next["./package.json"] = "./package.json";
417
435
  pkg.exports = next;
418
436
  }
419
437
  async function expandCodegen(appDir, sourceDir, entry) {
@@ -421,16 +439,21 @@ async function expandCodegen(appDir, sourceDir, entry) {
421
439
  const targetDir = path5.join(appDir, "packages", entry.id);
422
440
  await rm(targetDir, { recursive: true, force: true });
423
441
  await mkdir2(targetDir, { recursive: true });
442
+ const rawPkg = JSON.parse(await src.readFile("package.json"));
443
+ const fromName = rawPkg.name ?? "";
444
+ const toName = `@app/${entry.id}`;
424
445
  for (const rel of await src.listFiles()) {
425
446
  if (rel === "package.json") continue;
426
- const content = await src.readFile(rel);
447
+ let content = await src.readFile(rel);
448
+ if (fromName && fromName !== toName && rel.endsWith(".ts")) {
449
+ content = rewriteLoadModuleSkillsCall(content, fromName, toName);
450
+ }
427
451
  const out = path5.join(targetDir, rel);
428
452
  await mkdir2(path5.dirname(out), { recursive: true });
429
453
  await writeFile3(out, content);
430
454
  }
431
- const rawPkg = await src.readFile("package.json");
432
- const pkg = JSON.parse(rawPkg);
433
- pkg.name = `@app/${entry.id}`;
455
+ const pkg = rawPkg;
456
+ pkg.name = toName;
434
457
  pkg.private = true;
435
458
  if (pkg.dependencies && typeof pkg.dependencies === "object") {
436
459
  rewritePlatformRefs(pkg.dependencies);
@@ -522,7 +545,9 @@ async function createLocalSkeleton(appDir, shortName) {
522
545
  exports: {
523
546
  ".": { types: "./src/index.ts", import: "./src/index.ts" },
524
547
  "./server": { types: "./src/server/index.ts", import: "./src/server/index.ts" },
525
- "./client": { types: "./src/client/index.ts", import: "./src/client/index.ts" }
548
+ "./client": { types: "./src/client/index.ts", import: "./src/client/index.ts" },
549
+ // 009 模块文件化前置:loadModuleSkills 包名解析需 exports 放行元数据子路径
550
+ "./package.json": "./package.json"
526
551
  },
527
552
  // 本地模块自带工具链:typecheck/test 可在生成应用内直接跑(pnpm verify 全 @app/* 范围)
528
553
  scripts: { typecheck: "tsc --noEmit", test: "vitest run --passWithNoTests" },
@@ -932,6 +957,19 @@ function probeModuleObjectExport(dir, sub, symbol) {
932
957
  return false;
933
958
  }
934
959
  }
960
+ function readServerSource(dir) {
961
+ const file = path7.join(dir, "src", "server", "index.ts");
962
+ if (!existsSync5(file)) return "";
963
+ try {
964
+ return readFileSync4(file, "utf8");
965
+ } catch {
966
+ return "";
967
+ }
968
+ }
969
+ function extractComponentId(source) {
970
+ const match = source.match(/declaration\s*:\s*\{[\s\S]*?componentId\s*:\s*['"]([^'"]+)['"]/);
971
+ return match?.[1];
972
+ }
935
973
  async function listLocalModules(appDir) {
936
974
  const packagesDir = path7.join(appDir, "packages");
937
975
  const result = [];
@@ -968,25 +1006,31 @@ async function collectDeclared(appDir) {
968
1006
  const descriptor = readComponentFile(local) ?? readDescriptorFromNodeModules(appDir, entry.package);
969
1007
  const probeDir = existsSync5(path7.join(local, "src")) ? local : path7.join(appDir, "node_modules", ...entry.package.split("/"));
970
1008
  const c = descriptor?.contributes;
1009
+ const hasServerEntry = descriptor !== null && probeModuleObjectExport(probeDir, "server", "serverModule");
971
1010
  declared.push({
972
1011
  id: entry.id,
973
1012
  pkg: entry.mode === "sdk" ? entry.package : `@app/${entry.id}`,
974
1013
  mode: entry.mode,
975
- hasServerEntry: descriptor !== null && probeModuleObjectExport(probeDir, "server", "serverModule"),
1014
+ hasServerEntry,
976
1015
  hasClientEntry: descriptor !== null && probeModuleObjectExport(probeDir, "client", "clientModule"),
977
- hasCards: (c?.cards?.length ?? 0) > 0
1016
+ hasCards: (c?.cards?.length ?? 0) > 0,
1017
+ // 011 评审 M3:declaration.componentId(deployment 对账真源;老模块无 declaration → undefined)
1018
+ ...hasServerEntry ? { componentId: extractComponentId(readServerSource(probeDir)) } : {}
978
1019
  });
979
1020
  }
980
1021
  for (const mod of await listLocalModules(appDir)) {
981
1022
  if (seen.has(mod.id)) continue;
982
1023
  const c = mod.descriptor.contributes;
1024
+ const hasServerEntry = probeModuleObjectExport(mod.dir, "server", "serverModule");
983
1025
  declared.push({
984
1026
  id: mod.id,
985
1027
  pkg: `@app/${mod.id}`,
986
1028
  mode: "local",
987
- hasServerEntry: probeModuleObjectExport(mod.dir, "server", "serverModule"),
1029
+ hasServerEntry,
988
1030
  hasClientEntry: probeModuleObjectExport(mod.dir, "client", "clientModule"),
989
- hasCards: (c.cards?.length ?? 0) > 0
1031
+ hasCards: (c.cards?.length ?? 0) > 0,
1032
+ // 011 评审 M3:declaration.componentId(同 npmModules 路径)
1033
+ ...hasServerEntry ? { componentId: extractComponentId(readServerSource(mod.dir)) } : {}
990
1034
  });
991
1035
  }
992
1036
  return declared;
@@ -2093,6 +2137,181 @@ function legacyPlatformFilesRule(ctx) {
2093
2137
  return issues;
2094
2138
  }
2095
2139
 
2140
+ // src/doctor/rules/mall-deployment.ts
2141
+ import { existsSync as existsSync19, readFileSync as readFileSync8 } from "fs";
2142
+ import { readdir as readdir11 } from "fs/promises";
2143
+ import { createRequire as createRequire2 } from "module";
2144
+ import path23 from "path";
2145
+ function resolveModuleRoot(ctx, pkg) {
2146
+ try {
2147
+ const req = createRequire2(path23.join(ctx.appDir, "package.json"));
2148
+ const resolved = req.resolve(`${pkg}/package.json`);
2149
+ return path23.dirname(resolved);
2150
+ } catch {
2151
+ return void 0;
2152
+ }
2153
+ }
2154
+ function readServerSource2(moduleRoot) {
2155
+ const candidates = [
2156
+ path23.join(moduleRoot, "src/server/index.ts"),
2157
+ path23.join(moduleRoot, "src/server/index.tsx")
2158
+ ];
2159
+ for (const c of candidates) {
2160
+ if (existsSync19(c)) return readFileSync8(c, "utf8");
2161
+ }
2162
+ return "";
2163
+ }
2164
+ function extractFeatures(source) {
2165
+ const match = source.match(/features\s*:\s*\[([^\]]*)\]/);
2166
+ if (!match) return [];
2167
+ return [...match[1].matchAll(/['"]([^'"]+)['"]/g)].map((m) => m[1]);
2168
+ }
2169
+ function extractIntentPatterns(source) {
2170
+ const patterns = [];
2171
+ for (const block of source.matchAll(/patterns\s*:\s*\[([^\]]*)\]/g)) {
2172
+ for (const m of block[1].matchAll(/['"]([^'"]+)['"]/g)) {
2173
+ patterns.push(m[1]);
2174
+ }
2175
+ }
2176
+ return patterns;
2177
+ }
2178
+ function extractFeatureRefs(source) {
2179
+ return [...source.matchAll(/isFeatureEnabled\s*\(\s*['"]([^'"]+)['"]\s*\)/g)].map((m) => m[1]);
2180
+ }
2181
+ async function deploymentConsistencyRule(ctx) {
2182
+ const issues = [];
2183
+ const deploymentDir = path23.join(ctx.appDir, "config/deployments");
2184
+ if (!existsSync19(deploymentDir)) return issues;
2185
+ const entries = await readdir11(deploymentDir);
2186
+ const jsonFiles = entries.filter((e) => e.endsWith(".json"));
2187
+ if (jsonFiles.length === 0) return issues;
2188
+ for (const file of jsonFiles) {
2189
+ let parsed;
2190
+ try {
2191
+ parsed = JSON.parse(readFileSync8(path23.join(deploymentDir, file), "utf8"));
2192
+ } catch {
2193
+ issues.push({ rule: "deployment-consistency", level: "error", message: `deployment \u914D\u7F6E\u975E\u6CD5 JSON\uFF1Aconfig/deployments/${file}` });
2194
+ continue;
2195
+ }
2196
+ const components = parsed.deployment?.components ?? [];
2197
+ const declaredPkgs = new Map(ctx.declared.map((d) => [d.componentId ?? d.id, d]));
2198
+ for (const component of components) {
2199
+ const componentId = component.componentId;
2200
+ if (!componentId) {
2201
+ issues.push({
2202
+ rule: "deployment-consistency",
2203
+ level: "error",
2204
+ message: `config/deployments/${file}\uFF1Acomponents \u9879\u7F3A componentId`
2205
+ });
2206
+ continue;
2207
+ }
2208
+ const declared = declaredPkgs.get(componentId);
2209
+ if (!declared) {
2210
+ issues.push({
2211
+ rule: "deployment-consistency",
2212
+ level: component.optional ? "warning" : "error",
2213
+ message: `config/deployments/${file}\uFF1AcomponentId "${componentId}" \u672A\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757` + (component.optional ? "\uFF08optional \u7EC4\u4EF6\uFF0C\u964D\u7EA7 warning\uFF09" : "\uFF08\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757\u6216\u6807\u8BB0 optional\uFF09")
2214
+ });
2215
+ continue;
2216
+ }
2217
+ const moduleRoot = resolveModuleRoot(ctx, declared.pkg);
2218
+ if (moduleRoot && component.enabledFeatures?.length) {
2219
+ const source = readServerSource2(moduleRoot);
2220
+ const declaredFeatures = extractFeatures(source);
2221
+ if (declaredFeatures.length > 0) {
2222
+ for (const feature of component.enabledFeatures) {
2223
+ if (!declaredFeatures.includes(feature)) {
2224
+ issues.push({
2225
+ rule: "deployment-consistency",
2226
+ level: "error",
2227
+ message: `config/deployments/${file}\uFF1Afeature "${feature}" \u4E0D\u5728 ${componentId} \u7684 declaration.features \u5185\uFF08\u5DF2\u58F0\u660E\uFF1A${declaredFeatures.join(", ") || "(\u65E0)"}\uFF09`
2228
+ });
2229
+ }
2230
+ }
2231
+ }
2232
+ }
2233
+ }
2234
+ }
2235
+ return issues;
2236
+ }
2237
+ async function intentOverlapRule(ctx) {
2238
+ const issues = [];
2239
+ const byPattern = /* @__PURE__ */ new Map();
2240
+ for (const declared of ctx.declared) {
2241
+ if (!declared.hasServerEntry) continue;
2242
+ const root = resolveModuleRoot(ctx, declared.pkg);
2243
+ if (!root) continue;
2244
+ const patterns = extractIntentPatterns(readServerSource2(root));
2245
+ for (const p of patterns) {
2246
+ const list = byPattern.get(p) ?? [];
2247
+ list.push(declared.id);
2248
+ byPattern.set(p, list);
2249
+ }
2250
+ }
2251
+ for (const [pattern, modules] of byPattern) {
2252
+ if (modules.length > 1) {
2253
+ issues.push({
2254
+ rule: "intent-overlap",
2255
+ level: "warning",
2256
+ message: `\u610F\u56FE\u8BCD "${pattern}" \u5728\u591A\u4E2A\u6A21\u5757\u58F0\u660E\uFF08${modules.join(", ")}\uFF09\u2014\u2014\u5148\u6CE8\u518C\u5148\u547D\u4E2D`
2257
+ });
2258
+ }
2259
+ }
2260
+ return issues;
2261
+ }
2262
+ async function skillNameConflictRule(ctx) {
2263
+ const issues = [];
2264
+ const byName = /* @__PURE__ */ new Map();
2265
+ for (const declared of ctx.declared) {
2266
+ if (!declared.hasServerEntry) continue;
2267
+ const root = resolveModuleRoot(ctx, declared.pkg);
2268
+ if (!root) continue;
2269
+ const skillsDir = path23.join(root, "skills");
2270
+ if (!existsSync19(skillsDir)) continue;
2271
+ try {
2272
+ const names = (await readdir11(skillsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
2273
+ for (const name of names) {
2274
+ const list = byName.get(name) ?? [];
2275
+ list.push(declared.id);
2276
+ byName.set(name, list);
2277
+ }
2278
+ } catch {
2279
+ }
2280
+ }
2281
+ for (const [name, modules] of byName) {
2282
+ if (modules.length > 1) {
2283
+ issues.push({
2284
+ rule: "skill-name-conflict",
2285
+ level: "warning",
2286
+ message: `skill "${name}" \u5728\u591A\u4E2A\u6A21\u5757\u58F0\u660E\uFF08${modules.join(", ")}\uFF09\u2014\u2014\u540E\u6CE8\u518C\u8986\u76D6`
2287
+ });
2288
+ }
2289
+ }
2290
+ return issues;
2291
+ }
2292
+ async function featureDeclarationRule(ctx) {
2293
+ const issues = [];
2294
+ for (const declared of ctx.declared) {
2295
+ if (!declared.hasServerEntry) continue;
2296
+ const root = resolveModuleRoot(ctx, declared.pkg);
2297
+ if (!root) continue;
2298
+ const source = readServerSource2(root);
2299
+ if (!source) continue;
2300
+ const declaredFeatures = extractFeatures(source);
2301
+ const refs = extractFeatureRefs(source);
2302
+ for (const ref of refs) {
2303
+ if (declaredFeatures.length > 0 && !declaredFeatures.includes(ref)) {
2304
+ issues.push({
2305
+ rule: "feature-declaration",
2306
+ level: "error",
2307
+ message: `\u6A21\u5757 ${declared.id}\uFF1AisFeatureEnabled("${ref}") \u4E0D\u5728 declaration.features \u5185\uFF08\u5DF2\u58F0\u660E\uFF1A${declaredFeatures.join(", ") || "(\u65E0)"}\uFF09`
2308
+ });
2309
+ }
2310
+ }
2311
+ }
2312
+ return issues;
2313
+ }
2314
+
2096
2315
  // src/doctor/index.ts
2097
2316
  var DOCTOR_RULES = [
2098
2317
  { name: "workspace-dag", run: workspaceDagRule },
@@ -2108,7 +2327,11 @@ var DOCTOR_RULES = [
2108
2327
  { name: "naked-write-route", run: nakedWriteRouteRule },
2109
2328
  { name: "descriptor-schema", run: descriptorSchemaRule },
2110
2329
  { name: "workspace-ref-resolved", run: workspaceRefResolvedRule },
2111
- { name: "legacy-platform-files", run: legacyPlatformFilesRule }
2330
+ { name: "legacy-platform-files", run: legacyPlatformFilesRule },
2331
+ { name: "deployment-consistency", run: deploymentConsistencyRule },
2332
+ { name: "intent-overlap", run: intentOverlapRule },
2333
+ { name: "skill-name-conflict", run: skillNameConflictRule },
2334
+ { name: "feature-declaration", run: featureDeclarationRule }
2112
2335
  ];
2113
2336
  async function runDoctor(ctx, opts) {
2114
2337
  const issues = [];
@@ -2244,10 +2467,10 @@ async function resolveDependencies(appDir, rootIds, declared, extraRoots = []) {
2244
2467
 
2245
2468
  // src/commands/add.ts
2246
2469
  function readSourceDescriptor(sourceDir) {
2247
- const file = path23.join(sourceDir, "tbox.component.json");
2248
- if (!existsSync19(file)) return null;
2470
+ const file = path24.join(sourceDir, "tbox.component.json");
2471
+ if (!existsSync20(file)) return null;
2249
2472
  try {
2250
- const parsed = parseComponentDescriptor(JSON.parse(readFileSync8(file, "utf8")));
2473
+ const parsed = parseComponentDescriptor(JSON.parse(readFileSync9(file, "utf8")));
2251
2474
  return parsed.ok ? parsed.value : null;
2252
2475
  } catch {
2253
2476
  return null;
@@ -2255,20 +2478,20 @@ function readSourceDescriptor(sourceDir) {
2255
2478
  }
2256
2479
  function resolveDepSource(sourcePath, depId) {
2257
2480
  if (!sourcePath) return null;
2258
- const dir = path23.dirname(sourcePath);
2259
- const siblingDir = path23.join(dir, depId);
2260
- if (existsSync19(path23.join(siblingDir, "package.json"))) return siblingDir;
2481
+ const dir = path24.dirname(sourcePath);
2482
+ const siblingDir = path24.join(dir, depId);
2483
+ if (existsSync20(path24.join(siblingDir, "package.json"))) return siblingDir;
2261
2484
  try {
2262
2485
  const tgz = readdirSync2(dir).find(
2263
2486
  (f) => f.endsWith(".tgz") && f.includes(`-${depId}-`)
2264
2487
  );
2265
- if (tgz) return path23.join(dir, tgz);
2488
+ if (tgz) return path24.join(dir, tgz);
2266
2489
  } catch {
2267
2490
  }
2268
2491
  return null;
2269
2492
  }
2270
2493
  function isInstalled(appDir, id) {
2271
- if (existsSync19(path23.join(appDir, "packages", id))) return true;
2494
+ if (existsSync20(path24.join(appDir, "packages", id))) return true;
2272
2495
  const manifest = readAppManifest(appDir);
2273
2496
  return manifest?.npmModules.some((m) => m.id === id && m.mode === "sdk") ?? false;
2274
2497
  }
@@ -2296,7 +2519,7 @@ async function addModuleWithDepsCore(opts, visited, asDependency, tarballs) {
2296
2519
  assertValidModuleId(id, `--mode local ${opts.pkg}`);
2297
2520
  } else {
2298
2521
  if (!opts.source) throw new Error(`--mode ${opts.mode} \u9700\u8981 --source <\u76EE\u5F55\u6216 tgz \u6216 registry \u5305>`);
2299
- const isRegSource = isReg && !existsSync19(path23.resolve(opts.source));
2522
+ const isRegSource = isReg && !existsSync20(path24.resolve(opts.source));
2300
2523
  if (!isRegSource && !opts.source.endsWith(".tgz") && !LocalDirSource.isUsable(opts.source)) {
2301
2524
  throw new Error(`\u6A21\u5757\u6E90\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${opts.source}`);
2302
2525
  }
@@ -2305,7 +2528,7 @@ async function addModuleWithDepsCore(opts, visited, asDependency, tarballs) {
2305
2528
  tarballs.push(opened.source);
2306
2529
  }
2307
2530
  const pkgOnly = opts.pkg.replace(/@[^/]+$/, "");
2308
- id = pkgOnly.replace(/^@tbox\.cn\/app-/, "").replace(/^@[^/]+\//, "");
2531
+ id = deriveModuleId(pkgOnly);
2309
2532
  assertValidModuleId(id, `--mode ${opts.mode} ${opts.pkg}`);
2310
2533
  sourceDescriptor = readSourceDescriptor(opened.sourceDir);
2311
2534
  }
@@ -2314,7 +2537,7 @@ async function addModuleWithDepsCore(opts, visited, asDependency, tarballs) {
2314
2537
  return;
2315
2538
  }
2316
2539
  if (!asDependency && isInstalled(appDir, id)) {
2317
- const where = existsSync19(path23.join(appDir, "packages", id)) ? `packages/${id}` : "manifest \u5DF2\u767B\u8BB0\uFF08sdk \u5305\uFF0C\u65E0\u672C\u5730\u76EE\u5F55\uFF09";
2540
+ const where = existsSync20(path24.join(appDir, "packages", id)) ? `packages/${id}` : "manifest \u5DF2\u767B\u8BB0\uFF08sdk \u5305\uFF0C\u65E0\u672C\u5730\u76EE\u5F55\uFF09";
2318
2541
  throw new Error(`\u6A21\u5757 ${id} \u5DF2\u5B58\u5728\uFF08${where}\uFF09\u3002\u82E5\u8981\u91CD\u65B0\u5C55\u5F00\u8BF7\u5148 remove \u6216\u624B\u52A8\u5904\u7406\uFF1B\u88C5\u914D\u552F\u4E00\u8986\u76D6\u5165\u53E3\u4E3A sync --force`);
2319
2542
  }
2320
2543
  if (visited.has(id)) return;
@@ -2345,7 +2568,7 @@ async function addModuleWithDepsCore(opts, visited, asDependency, tarballs) {
2345
2568
  if (depOpened.source instanceof TarballSource || depOpened.source instanceof RegistrySource) {
2346
2569
  tarballs.push(depOpened.source);
2347
2570
  }
2348
- const depIsBusiness = existsSync19(path23.join(depOpened.sourceDir, "tbox.component.json"));
2571
+ const depIsBusiness = existsSync20(path24.join(depOpened.sourceDir, "tbox.component.json"));
2349
2572
  await addModuleWithDeps(
2350
2573
  { pkg: depPkg, mode: depIsBusiness ? "codegen" : "sdk", source: depSource, registry: opts.registry },
2351
2574
  visited,
@@ -2558,9 +2781,9 @@ async function doctorCommand(opts = {}) {
2558
2781
  }
2559
2782
 
2560
2783
  // src/commands/update.ts
2561
- import { existsSync as existsSync20 } from "fs";
2562
- import { readdir as readdir11, readFile as readFile6, unlink, writeFile as writeFile7 } from "fs/promises";
2563
- import path24 from "path";
2784
+ import { existsSync as existsSync21 } from "fs";
2785
+ import { readdir as readdir12, readFile as readFile6, unlink, writeFile as writeFile7 } from "fs/promises";
2786
+ import path25 from "path";
2564
2787
 
2565
2788
  // src/merge3.ts
2566
2789
  import { diffArrays } from "diff";
@@ -2643,21 +2866,21 @@ function merge3(base, ours, theirs) {
2643
2866
  // src/commands/update.ts
2644
2867
  var EXCLUDED = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", ".tbox"]);
2645
2868
  function openSourceDir(source) {
2646
- if (source.endsWith(".tgz") && existsSync20(source)) {
2869
+ if (source.endsWith(".tgz") && existsSync21(source)) {
2647
2870
  const tarball = new TarballSource(source);
2648
2871
  return { dir: tarball.getDir(), tarball };
2649
2872
  }
2650
- return { dir: path24.resolve(source), tarball: null };
2873
+ return { dir: path25.resolve(source), tarball: null };
2651
2874
  }
2652
2875
  async function readPackageFiles(dir) {
2653
2876
  const map = /* @__PURE__ */ new Map();
2654
2877
  async function walk(rel) {
2655
- const full = path24.join(dir, rel);
2656
- const entries = await readdir11(full, { withFileTypes: true });
2878
+ const full = path25.join(dir, rel);
2879
+ const entries = await readdir12(full, { withFileTypes: true });
2657
2880
  for (const entry of entries) {
2658
2881
  if (EXCLUDED.has(entry.name)) continue;
2659
2882
  const child = rel ? `${rel}/${entry.name}` : entry.name;
2660
- const childFull = path24.join(full, entry.name);
2883
+ const childFull = path25.join(full, entry.name);
2661
2884
  if (entry.isDirectory()) {
2662
2885
  await walk(child);
2663
2886
  } else {
@@ -2687,8 +2910,8 @@ async function updateModule(opts) {
2687
2910
  console.log(`\u2705 \u5DF2\u5347\u7EA7 ${entry.package}\uFF08sdk \u6A21\u5F0F\uFF09`);
2688
2911
  return;
2689
2912
  }
2690
- const pkgDir = path24.join(appDir, "packages", opts.module);
2691
- if (!existsSync20(pkgDir)) throw new Error(`\u7F3A\u5C11 packages/${opts.module}`);
2913
+ const pkgDir = path25.join(appDir, "packages", opts.module);
2914
+ if (!existsSync21(pkgDir)) throw new Error(`\u7F3A\u5C11 packages/${opts.module}`);
2692
2915
  const ours = await readPackageFiles(pkgDir);
2693
2916
  const tarballs = [];
2694
2917
  try {
@@ -2735,8 +2958,8 @@ async function updateModule(opts) {
2735
2958
  return;
2736
2959
  }
2737
2960
  for (const [key, content] of mergedFiles) {
2738
- const out = path24.join(pkgDir, key);
2739
- if (content === "" && existsSync20(out)) {
2961
+ const out = path25.join(pkgDir, key);
2962
+ if (content === "" && existsSync21(out)) {
2740
2963
  await unlink(out);
2741
2964
  console.log(` \u{1F5D1} \u5DF2\u5220\u9664 ${key}\uFF08\u5347\u7EA7\u6E90\u5220\u9664\u8BE5\u6587\u4EF6\uFF09`);
2742
2965
  continue;
@@ -2765,27 +2988,27 @@ async function updateModule(opts) {
2765
2988
  }
2766
2989
 
2767
2990
  // src/commands/publish.ts
2768
- import { existsSync as existsSync22, readFileSync as readFileSync10, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
2991
+ import { existsSync as existsSync23, readFileSync as readFileSync11, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
2769
2992
  import { tmpdir as tmpdir2 } from "os";
2770
- import path26 from "path";
2993
+ import path27 from "path";
2771
2994
 
2772
2995
  // src/commands/pack.ts
2773
- import { existsSync as existsSync21, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
2996
+ import { existsSync as existsSync22, readFileSync as readFileSync10, readdirSync as readdirSync3 } from "fs";
2774
2997
  import { execFileSync as execFileSync3 } from "child_process";
2775
- import path25 from "path";
2998
+ import path26 from "path";
2776
2999
  function tarballFileName(name, version) {
2777
3000
  const plain = name.replace(/^@/, "").replace("/", "-");
2778
3001
  return `${plain}-${version}.tgz`;
2779
3002
  }
2780
3003
  function validatePublishableModule(moduleDir) {
2781
- if (!existsSync21(moduleDir) || !existsSync21(path25.join(moduleDir, "package.json"))) {
3004
+ if (!existsSync22(moduleDir) || !existsSync22(path26.join(moduleDir, "package.json"))) {
2782
3005
  throw new Error(`\u6A21\u5757\u76EE\u5F55\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${moduleDir}`);
2783
3006
  }
2784
3007
  let pkg;
2785
3008
  try {
2786
- pkg = JSON.parse(readFileSync9(path25.join(moduleDir, "package.json"), "utf8"));
3009
+ pkg = JSON.parse(readFileSync10(path26.join(moduleDir, "package.json"), "utf8"));
2787
3010
  } catch {
2788
- throw new Error(`package.json \u89E3\u6790\u5931\u8D25: ${path25.join(moduleDir, "package.json")}`);
3011
+ throw new Error(`package.json \u89E3\u6790\u5931\u8D25: ${path26.join(moduleDir, "package.json")}`);
2789
3012
  }
2790
3013
  const name = typeof pkg.name === "string" ? pkg.name : "";
2791
3014
  if (!/^@tbox\.cn\//.test(name)) {
@@ -2799,20 +3022,20 @@ function validatePublishableModule(moduleDir) {
2799
3022
  }
2800
3023
  function packModule(moduleDir, outDir) {
2801
3024
  const { name, version } = validatePublishableModule(moduleDir);
2802
- const absOut = path25.resolve(outDir);
3025
+ const absOut = path26.resolve(outDir);
2803
3026
  execFileSync3("pnpm", ["pack", "--pack-destination", absOut], { stdio: "inherit", cwd: moduleDir });
2804
- return path25.join(absOut, tarballFileName(name, version));
3027
+ return path26.join(absOut, tarballFileName(name, version));
2805
3028
  }
2806
3029
  function detectKind(pkgDir, name) {
2807
- if (existsSync21(path25.join(pkgDir, "pnpm-workspace.template.yaml"))) return "template";
2808
- if (existsSync21(path25.join(pkgDir, "tbox.component.json"))) return "module";
3030
+ if (existsSync22(path26.join(pkgDir, "pnpm-workspace.template.yaml"))) return "template";
3031
+ if (existsSync22(path26.join(pkgDir, "tbox.component.json"))) return "module";
2809
3032
  if (/^@[^/]+\/app-contracts/.test(name)) return "contract";
2810
3033
  return "generic";
2811
3034
  }
2812
3035
  function listAllFiles(dir) {
2813
3036
  const out = [];
2814
3037
  function walk(rel) {
2815
- const full = path25.join(dir, rel);
3038
+ const full = path26.join(dir, rel);
2816
3039
  for (const entry of readdirSync3(full, { withFileTypes: true })) {
2817
3040
  if (entry.name === "node_modules" || entry.name === ".git") continue;
2818
3041
  const child = rel ? `${rel}/${entry.name}` : entry.name;
@@ -2841,10 +3064,10 @@ function collectExportTargets(exports) {
2841
3064
  }
2842
3065
  function verifyPkgDir(pkgDir, kind) {
2843
3066
  const issues = [];
2844
- const pkgFile = path25.join(pkgDir, "package.json");
3067
+ const pkgFile = path26.join(pkgDir, "package.json");
2845
3068
  let pkg;
2846
3069
  try {
2847
- pkg = JSON.parse(readFileSync9(pkgFile, "utf8"));
3070
+ pkg = JSON.parse(readFileSync10(pkgFile, "utf8"));
2848
3071
  } catch {
2849
3072
  issues.push({ rule: "pack-manifest", level: "error", message: "\u4EA7\u7269\u7F3A\u5C11\u53EF\u89E3\u6790\u7684 package.json" });
2850
3073
  return issues;
@@ -2872,7 +3095,7 @@ function verifyPkgDir(pkgDir, kind) {
2872
3095
  level: "error",
2873
3096
  message: `exports \u6307\u5411\u5F00\u53D1\u6001\u6E90\u7801 "${t}"\uFF08\u53D1\u5E03\u6001\u5E94\u6307\u5411 dist/\uFF09`
2874
3097
  });
2875
- } else if (t.startsWith("./") && !existsSync21(path25.join(pkgDir, t))) {
3098
+ } else if (t.startsWith("./") && !existsSync22(path26.join(pkgDir, t))) {
2876
3099
  issues.push({
2877
3100
  rule: "pack-exports",
2878
3101
  level: "error",
@@ -2905,7 +3128,7 @@ function verifyPkgDir(pkgDir, kind) {
2905
3128
  }
2906
3129
  if (kindOf === "module") {
2907
3130
  for (const required of ["src", "dist", "tbox.component.json"]) {
2908
- if (!files.includes(required) && !existsSync21(path25.join(pkgDir, required))) {
3131
+ if (!files.includes(required) && !existsSync22(path26.join(pkgDir, required))) {
2909
3132
  issues.push({ rule: "pack-files", level: "error", message: `\u4E1A\u52A1\u6A21\u5757\u4EA7\u7269\u7F3A\u5C11 ${required}` });
2910
3133
  }
2911
3134
  }
@@ -2919,12 +3142,12 @@ function verifyPkgDir(pkgDir, kind) {
2919
3142
  "tsconfig.base.json",
2920
3143
  "scripts/verify-generated.mjs"
2921
3144
  ]) {
2922
- if (!existsSync21(path25.join(pkgDir, required))) {
3145
+ if (!existsSync22(path26.join(pkgDir, required))) {
2923
3146
  issues.push({ rule: "pack-files", level: "error", message: `\u6A21\u677F\u4EA7\u7269\u7F3A\u5C11 ${required}` });
2924
3147
  }
2925
3148
  }
2926
3149
  for (const leaked2 of ["pnpm-workspace.yaml", "pnpm-lock.yaml"]) {
2927
- if (existsSync21(path25.join(pkgDir, leaked2))) {
3150
+ if (existsSync22(path26.join(pkgDir, leaked2))) {
2928
3151
  issues.push({
2929
3152
  rule: "pack-files",
2930
3153
  level: "error",
@@ -2967,11 +3190,11 @@ async function verifyTarball(tgzPath) {
2967
3190
  const issues = verifyPkgDir(pkgDir, kind);
2968
3191
  if (pkg.name && pkg.version) {
2969
3192
  const expect = tarballFileName(pkg.name, pkg.version);
2970
- if (path25.basename(tgzPath) !== expect) {
3193
+ if (path26.basename(tgzPath) !== expect) {
2971
3194
  issues.push({
2972
3195
  rule: "pack-filename",
2973
3196
  level: "error",
2974
- message: `tgz \u6587\u4EF6\u540D ${path25.basename(tgzPath)} \u4E0E\u5305 ${pkg.name}@${pkg.version} \u671F\u671B ${expect} \u4E0D\u4E00\u81F4`
3197
+ message: `tgz \u6587\u4EF6\u540D ${path26.basename(tgzPath)} \u4E0E\u5305 ${pkg.name}@${pkg.version} \u671F\u671B ${expect} \u4E0D\u4E00\u81F4`
2975
3198
  });
2976
3199
  }
2977
3200
  }
@@ -2986,9 +3209,9 @@ function printIssues(issues) {
2986
3209
  }
2987
3210
  }
2988
3211
  async function packCommand(opts) {
2989
- const moduleDir = path25.resolve(process.cwd(), opts.module);
3212
+ const moduleDir = path26.resolve(process.cwd(), opts.module);
2990
3213
  const tgzPath = packModule(moduleDir, opts.out);
2991
- console.log(`\u2705 \u5DF2\u6253\u5305 ${path25.basename(tgzPath)} \u2192 ${path25.resolve(opts.out)}`);
3214
+ console.log(`\u2705 \u5DF2\u6253\u5305 ${path26.basename(tgzPath)} \u2192 ${path26.resolve(opts.out)}`);
2992
3215
  if (opts.check) {
2993
3216
  const issues = await verifyTarball(tgzPath);
2994
3217
  printIssues(issues);
@@ -2999,19 +3222,19 @@ async function packCommand(opts) {
2999
3222
  // src/commands/publish.ts
3000
3223
  async function publishModule(opts) {
3001
3224
  const cwd = process.cwd();
3002
- const moduleDir = path26.resolve(cwd, opts.module);
3003
- if (!existsSync22(moduleDir) || !existsSync22(path26.join(moduleDir, "package.json"))) {
3225
+ const moduleDir = path27.resolve(cwd, opts.module);
3226
+ if (!existsSync23(moduleDir) || !existsSync23(path27.join(moduleDir, "package.json"))) {
3004
3227
  throw new Error(`\u6A21\u5757\u76EE\u5F55\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${opts.module}`);
3005
3228
  }
3006
3229
  let tgzPath;
3007
3230
  let workDir = null;
3008
3231
  if (opts.tgz) {
3009
- tgzPath = path26.resolve(opts.tgz);
3010
- if (!existsSync22(tgzPath)) throw new Error(`tgz \u4EA7\u7269\u4E0D\u5B58\u5728: ${opts.tgz}`);
3232
+ tgzPath = path27.resolve(opts.tgz);
3233
+ if (!existsSync23(tgzPath)) throw new Error(`tgz \u4EA7\u7269\u4E0D\u5B58\u5728: ${opts.tgz}`);
3011
3234
  const tarball = new TarballSource(tgzPath);
3012
3235
  try {
3013
3236
  const tgzPkg = JSON.parse(await tarball.readFile("package.json"));
3014
- const modulePkg = JSON.parse(readFileSync10(path26.join(moduleDir, "package.json"), "utf8"));
3237
+ const modulePkg = JSON.parse(readFileSync11(path27.join(moduleDir, "package.json"), "utf8"));
3015
3238
  if (tgzPkg.name !== modulePkg.name) {
3016
3239
  throw new Error(
3017
3240
  `--tgz \u5305\u540D ${tgzPkg.name ?? "(\u672A\u77E5)"} \u4E0E\u6A21\u5757 ${modulePkg.name ?? "(\u672A\u77E5)"} \u4E0D\u4E00\u81F4\uFF08\u8BF7\u786E\u8BA4 --tgz \u4E0E\u6A21\u5757\u5339\u914D\uFF09`
@@ -3021,7 +3244,7 @@ async function publishModule(opts) {
3021
3244
  tarball.dispose();
3022
3245
  }
3023
3246
  } else {
3024
- workDir = mkdtempSync2(path26.join(tmpdir2(), "tbox-publish-"));
3247
+ workDir = mkdtempSync2(path27.join(tmpdir2(), "tbox-publish-"));
3025
3248
  tgzPath = packModule(moduleDir, workDir);
3026
3249
  }
3027
3250
  try {
@@ -3029,9 +3252,9 @@ async function publishModule(opts) {
3029
3252
  if (issues.length > 0) printIssues(issues);
3030
3253
  const errors = issues.filter((i) => i.level === "error");
3031
3254
  if (errors.length > 0) {
3032
- throw new Error(`\u4EA7\u7269\u95E8\u7981\u5931\u8D25\uFF08${errors.length} \u5904 error\uFF09\uFF0C\u5DF2\u4E2D\u6B62\u53D1\u5E03: ${path26.basename(tgzPath)}`);
3255
+ throw new Error(`\u4EA7\u7269\u95E8\u7981\u5931\u8D25\uFF08${errors.length} \u5904 error\uFF09\uFF0C\u5DF2\u4E2D\u6B62\u53D1\u5E03: ${path27.basename(tgzPath)}`);
3033
3256
  }
3034
- if (!workDir) workDir = mkdtempSync2(path26.join(tmpdir2(), "tbox-publish-"));
3257
+ if (!workDir) workDir = mkdtempSync2(path27.join(tmpdir2(), "tbox-publish-"));
3035
3258
  const { execFileSync: execFileSync4 } = await import("child_process");
3036
3259
  const registryArgs = opts.registry ? ["--registry", opts.registry] : [];
3037
3260
  execFileSync4(
@@ -3040,7 +3263,7 @@ async function publishModule(opts) {
3040
3263
  { stdio: "inherit", cwd: workDir }
3041
3264
  );
3042
3265
  console.log(
3043
- `\u2705 \u5DF2\u53D1\u5E03 ${path26.basename(tgzPath)}${opts.registry ? ` \u2192 ${opts.registry}` : ""}${opts.dryRun ? "\uFF08dry-run\uFF09" : ""}`
3266
+ `\u2705 \u5DF2\u53D1\u5E03 ${path27.basename(tgzPath)}${opts.registry ? ` \u2192 ${opts.registry}` : ""}${opts.dryRun ? "\uFF08dry-run\uFF09" : ""}`
3044
3267
  );
3045
3268
  } finally {
3046
3269
  if (workDir) {
@@ -3055,7 +3278,8 @@ async function publishModule(opts) {
3055
3278
  // src/index.ts
3056
3279
  function createProgram() {
3057
3280
  const program = new Command();
3058
- program.name("tbox-app").description("tbox \u7EC4\u4EF6\u5316\u5E94\u7528 CLI\uFF1Acreate / module add|remove|list|sync|diff|doctor").version("0.1.0");
3281
+ const pkgJson = createRequire3(import.meta.url)("../package.json");
3282
+ program.name("tbox-app").description("tbox \u7EC4\u4EF6\u5316\u5E94\u7528 CLI\uFF1Acreate / module add|remove|list|sync|diff|doctor").version(pkgJson.version);
3059
3283
  program.command("create <name>").description("\u4ECE\u6A21\u677F\u751F\u6210\u591A\u5305 workspace \u5E94\u7528\u9AA8\u67B6").option("--source <\u8DEF\u5F84>", "\u6A21\u677F\u6E90\uFF08\u672C\u5730\u76EE\u5F55 / \u6A21\u677F pack tgz / registry \u5305 @scope/pkg[@version]\uFF09").option("--local-deps <tgz\u76EE\u5F55>", "\u6CE8\u5165\u5E73\u53F0 overrides\uFF08file: \u672C\u5730\u53D1\u5E03\u6001 tgz\uFF0Cinstall \u79BB\u7EBF\u9A8C\u8BC1\u6001\uFF1B\u751F\u4EA7 B \u6863\u4E0D\u4F20\uFF09").option("--registry <url>", "\u62C9\u53D6 registry\uFF08registry \u6E90\u65F6\u751F\u6548\uFF1B\u7F3A\u7701\u81EA\u52A8\u89E3\u6790\u73AF\u5883\u955C\u50CF\uFF09").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u76EE\u5F55").action(async (name, opts) => {
3060
3284
  try {
3061
3285
  await createApp({ name, source: opts.source ?? "", force: opts.force, localDeps: opts.localDeps, registry: opts.registry });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tbox.cn/app-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "tbox-app CLI:create / module add|remove|list|sync|diff|doctor|update|publish(纯函数核心 + commander 薄壳)。",
6
6
  "bin": {