@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.
- package/dist/index.js +292 -68
- 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
|
|
387
|
-
import
|
|
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
|
-
|
|
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
|
|
432
|
-
|
|
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
|
|
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
|
|
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 =
|
|
2248
|
-
if (!
|
|
2470
|
+
const file = path24.join(sourceDir, "tbox.component.json");
|
|
2471
|
+
if (!existsSync20(file)) return null;
|
|
2249
2472
|
try {
|
|
2250
|
-
const parsed = parseComponentDescriptor(JSON.parse(
|
|
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 =
|
|
2259
|
-
const siblingDir =
|
|
2260
|
-
if (
|
|
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
|
|
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 (
|
|
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 && !
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
2562
|
-
import { readdir as
|
|
2563
|
-
import
|
|
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") &&
|
|
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:
|
|
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 =
|
|
2656
|
-
const entries = await
|
|
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 =
|
|
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 =
|
|
2691
|
-
if (!
|
|
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 =
|
|
2739
|
-
if (content === "" &&
|
|
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
|
|
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
|
|
2993
|
+
import path27 from "path";
|
|
2771
2994
|
|
|
2772
2995
|
// src/commands/pack.ts
|
|
2773
|
-
import { existsSync as
|
|
2996
|
+
import { existsSync as existsSync22, readFileSync as readFileSync10, readdirSync as readdirSync3 } from "fs";
|
|
2774
2997
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2775
|
-
import
|
|
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 (!
|
|
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(
|
|
3009
|
+
pkg = JSON.parse(readFileSync10(path26.join(moduleDir, "package.json"), "utf8"));
|
|
2787
3010
|
} catch {
|
|
2788
|
-
throw new Error(`package.json \u89E3\u6790\u5931\u8D25: ${
|
|
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 =
|
|
3025
|
+
const absOut = path26.resolve(outDir);
|
|
2803
3026
|
execFileSync3("pnpm", ["pack", "--pack-destination", absOut], { stdio: "inherit", cwd: moduleDir });
|
|
2804
|
-
return
|
|
3027
|
+
return path26.join(absOut, tarballFileName(name, version));
|
|
2805
3028
|
}
|
|
2806
3029
|
function detectKind(pkgDir, name) {
|
|
2807
|
-
if (
|
|
2808
|
-
if (
|
|
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 =
|
|
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 =
|
|
3067
|
+
const pkgFile = path26.join(pkgDir, "package.json");
|
|
2845
3068
|
let pkg;
|
|
2846
3069
|
try {
|
|
2847
|
-
pkg = JSON.parse(
|
|
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("./") && !
|
|
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) && !
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 ${
|
|
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 =
|
|
3212
|
+
const moduleDir = path26.resolve(process.cwd(), opts.module);
|
|
2990
3213
|
const tgzPath = packModule(moduleDir, opts.out);
|
|
2991
|
-
console.log(`\u2705 \u5DF2\u6253\u5305 ${
|
|
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 =
|
|
3003
|
-
if (!
|
|
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 =
|
|
3010
|
-
if (!
|
|
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(
|
|
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(
|
|
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: ${
|
|
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(
|
|
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 ${
|
|
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
|
-
|
|
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 });
|