sy-lowcode-workspace-tools 0.1.0 → 0.1.1
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/package.json +2 -2
- package/scripts/register.mjs +42 -0
- package/src/cli.mjs +161 -10
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sy-lowcode-workspace-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CLI tools for sy lowcode app workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"lowcode-workspace": "
|
|
7
|
+
"lowcode-workspace": "bin/lowcode-workspace.mjs"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/cli.mjs"
|
package/scripts/register.mjs
CHANGED
|
@@ -198,6 +198,7 @@ async function registerPages() {
|
|
|
198
198
|
if (dryRun) {
|
|
199
199
|
console.log(" [DRY] 代码页发布 payload:");
|
|
200
200
|
console.log(JSON.stringify(payload, null, 2));
|
|
201
|
+
writePagePublishResult(payload, null, true);
|
|
201
202
|
return { succeeded: pages.length, failed: 0 };
|
|
202
203
|
}
|
|
203
204
|
|
|
@@ -220,9 +221,50 @@ async function registerPages() {
|
|
|
220
221
|
body.data?.items?.forEach((item) => {
|
|
221
222
|
console.log(` ✓ 代码页 ${item.code} (${item.pageId})`);
|
|
222
223
|
});
|
|
224
|
+
writePagePublishResult(payload, body, false);
|
|
223
225
|
return { succeeded: pages.length, failed: 0 };
|
|
224
226
|
}
|
|
225
227
|
|
|
228
|
+
function writePagePublishResult(payload, responseBody, isDryRun) {
|
|
229
|
+
const publishedItems = new Map(
|
|
230
|
+
(responseBody?.data?.items || []).map((item) => [item.code, item]),
|
|
231
|
+
);
|
|
232
|
+
const result = {
|
|
233
|
+
appId: config.appId || config.appType,
|
|
234
|
+
appType: payload.appType,
|
|
235
|
+
workspacePath: rootDir,
|
|
236
|
+
version: payload.version,
|
|
237
|
+
buildId: payload.buildId,
|
|
238
|
+
pages: payload.pages.map((page) => {
|
|
239
|
+
const published = publishedItems.get(page.code) || {};
|
|
240
|
+
return {
|
|
241
|
+
code: page.code,
|
|
242
|
+
name: page.name,
|
|
243
|
+
pageId: published.pageId || (isDryRun ? "<dry-run-pageId>" : ""),
|
|
244
|
+
routeKey:
|
|
245
|
+
published.routeKey ||
|
|
246
|
+
page.route?.pathKey ||
|
|
247
|
+
page.route?.path ||
|
|
248
|
+
page.code,
|
|
249
|
+
legacyFormUuid:
|
|
250
|
+
published.legacyFormUuid || published.formUuid || null,
|
|
251
|
+
entryUrl: page.runtime?.entryUrl || "",
|
|
252
|
+
cssUrls: page.runtime?.cssUrls || [],
|
|
253
|
+
menuId:
|
|
254
|
+
published.menuId === undefined
|
|
255
|
+
? isDryRun
|
|
256
|
+
? "<dry-run-menuId>"
|
|
257
|
+
: null
|
|
258
|
+
: published.menuId,
|
|
259
|
+
};
|
|
260
|
+
}),
|
|
261
|
+
publishedAt: new Date().toISOString(),
|
|
262
|
+
};
|
|
263
|
+
const outputPath = path.resolve(rootDir, "dist", "publish-result.json");
|
|
264
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
265
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(result, null, 2)}\n`, "utf-8");
|
|
266
|
+
}
|
|
267
|
+
|
|
226
268
|
console.log(`🔗 注册应用工作区产物 (${dryRun ? "DRY RUN" : "LIVE"})`);
|
|
227
269
|
console.log(` API: ${apiBase}`);
|
|
228
270
|
console.log("");
|
package/src/cli.mjs
CHANGED
|
@@ -39,7 +39,20 @@ const textExtensions = new Set([
|
|
|
39
39
|
".yml",
|
|
40
40
|
".yaml",
|
|
41
41
|
]);
|
|
42
|
-
const ignoredDirs = new Set([
|
|
42
|
+
const ignoredDirs = new Set([
|
|
43
|
+
".git",
|
|
44
|
+
"node_modules",
|
|
45
|
+
"dist",
|
|
46
|
+
"build",
|
|
47
|
+
"coverage",
|
|
48
|
+
".vite",
|
|
49
|
+
"packages",
|
|
50
|
+
]);
|
|
51
|
+
const runtimePackages = [
|
|
52
|
+
"sy-form-components",
|
|
53
|
+
"sy-page-sdk",
|
|
54
|
+
"sy-lowcode-workspace-tools",
|
|
55
|
+
];
|
|
43
56
|
|
|
44
57
|
function usage() {
|
|
45
58
|
return `
|
|
@@ -49,17 +62,24 @@ Commands:
|
|
|
49
62
|
build | build-forms | build-pages | sync-schema | publish-oss | register | publish-all
|
|
50
63
|
update Update workspace runtime dependencies and managed wrappers
|
|
51
64
|
migrate Convert old local SDK workspace to npm package mode
|
|
65
|
+
smoke Run workspace runtime smoke checks
|
|
52
66
|
|
|
53
67
|
Update options:
|
|
54
68
|
--workspace <path> Workspace root, defaults to cwd
|
|
55
69
|
--channel <tag> npm dist-tag, defaults to latest
|
|
56
70
|
--check Validate only; do not mutate
|
|
71
|
+
--strict-lock Compare lockfile and installed versions with npm latest
|
|
57
72
|
--commit Commit update changes
|
|
58
73
|
--push Push after commit
|
|
59
74
|
--no-commit Do not commit, even if changes exist
|
|
60
75
|
--allow-dirty Allow updates with existing worktree changes
|
|
61
76
|
--skip-install Do not run pnpm install/update
|
|
62
77
|
--skip-gate Do not run typecheck smoke gate
|
|
78
|
+
--gate <quick|full> Gate depth after update, defaults to quick
|
|
79
|
+
|
|
80
|
+
Smoke options:
|
|
81
|
+
--workspace <path> Workspace root, defaults to cwd
|
|
82
|
+
--mode <quick|full> quick validates commands/manifests; full builds shared runtimes
|
|
63
83
|
`;
|
|
64
84
|
}
|
|
65
85
|
|
|
@@ -72,7 +92,7 @@ function parseArgs(argv) {
|
|
|
72
92
|
continue;
|
|
73
93
|
}
|
|
74
94
|
const key = arg.slice(2);
|
|
75
|
-
if (["workspace", "channel"].includes(key)) {
|
|
95
|
+
if (["workspace", "channel", "mode", "gate"].includes(key)) {
|
|
76
96
|
result[key] = argv[index + 1];
|
|
77
97
|
index += 1;
|
|
78
98
|
} else {
|
|
@@ -120,6 +140,68 @@ function writeJson(path, value) {
|
|
|
120
140
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
121
141
|
}
|
|
122
142
|
|
|
143
|
+
function escapeRegExp(value) {
|
|
144
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readOptionalJson(path) {
|
|
148
|
+
if (!existsSync(path)) return null;
|
|
149
|
+
try {
|
|
150
|
+
return readJson(path);
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function npmLatestVersion(packageName, channel) {
|
|
157
|
+
const result = run("npm", ["view", `${packageName}@${channel}`, "version"], {
|
|
158
|
+
capture: true,
|
|
159
|
+
});
|
|
160
|
+
return String(result.stdout || "").trim();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function readInstalledVersion(workspaceRoot, packageName) {
|
|
164
|
+
const packagePath = join(workspaceRoot, "node_modules", packageName, "package.json");
|
|
165
|
+
const pkg = readOptionalJson(packagePath);
|
|
166
|
+
return typeof pkg?.version === "string" ? pkg.version : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function readLockVersion(workspaceRoot, packageName) {
|
|
170
|
+
const lockPath = join(workspaceRoot, "pnpm-lock.yaml");
|
|
171
|
+
if (!existsSync(lockPath)) return null;
|
|
172
|
+
const content = readFileSync(lockPath, "utf-8");
|
|
173
|
+
const packagePattern = new RegExp(
|
|
174
|
+
`(?:^|\\n)\\s{2,}/?${escapeRegExp(packageName)}@([^:\\n(]+)(?:\\([^\\n]*\\))?:`,
|
|
175
|
+
);
|
|
176
|
+
const packageMatch = content.match(packagePattern);
|
|
177
|
+
if (packageMatch?.[1]) return packageMatch[1].trim();
|
|
178
|
+
|
|
179
|
+
const dependencyPattern = new RegExp(
|
|
180
|
+
`(?:^|\\n)\\s{6}${escapeRegExp(packageName)}:\\n\\s{8}specifier:\\s*[^\\n]+\\n\\s{8}version:\\s*([^\\s(]+)`,
|
|
181
|
+
);
|
|
182
|
+
const dependencyMatch = content.match(dependencyPattern);
|
|
183
|
+
return dependencyMatch?.[1]?.trim() || null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function validateRuntimeManifest(workspaceRoot, manifestPath, expectedProtocol, expectedMajorVersion) {
|
|
187
|
+
if (!existsSync(manifestPath)) return;
|
|
188
|
+
const manifest = readJson(manifestPath);
|
|
189
|
+
const errors = [];
|
|
190
|
+
if (manifest.protocol !== expectedProtocol) {
|
|
191
|
+
errors.push(`protocol must be ${expectedProtocol}`);
|
|
192
|
+
}
|
|
193
|
+
if (manifest.majorVersion !== expectedMajorVersion) {
|
|
194
|
+
errors.push(`majorVersion must be ${expectedMajorVersion}`);
|
|
195
|
+
}
|
|
196
|
+
if (!manifest.version) errors.push("version is required");
|
|
197
|
+
if (!manifest.files?.entry) errors.push("files.entry is required");
|
|
198
|
+
if (errors.length) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`${manifestPath.replace(`${workspaceRoot}/`, "")} is invalid: ${errors.join("; ")}`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
123
205
|
function gitStatus(workspaceRoot) {
|
|
124
206
|
const result = run("git", ["status", "--porcelain"], {
|
|
125
207
|
cwd: workspaceRoot,
|
|
@@ -260,15 +342,19 @@ function removeLocalSdk(workspaceRoot) {
|
|
|
260
342
|
}
|
|
261
343
|
}
|
|
262
344
|
|
|
263
|
-
function validateWorkspace(workspaceRoot, channel = "latest") {
|
|
345
|
+
function validateWorkspace(workspaceRoot, channel = "latest", options = {}) {
|
|
264
346
|
const errors = [];
|
|
347
|
+
const warnings = [];
|
|
265
348
|
const pkg = readJson(join(workspaceRoot, "package.json"));
|
|
266
349
|
const deps = pkg.dependencies || {};
|
|
267
350
|
if (deps["@sy/page-sdk"]) errors.push("package.json still depends on @sy/page-sdk");
|
|
268
|
-
for (const name of
|
|
351
|
+
for (const name of runtimePackages) {
|
|
269
352
|
if (deps[name] !== channel) errors.push(`package.json dependency ${name} must be ${channel}`);
|
|
270
353
|
}
|
|
271
|
-
|
|
354
|
+
const isWorkspaceToolsSourceRepo = existsSync(
|
|
355
|
+
join(workspaceRoot, "packages", "workspace-tools", "package.json"),
|
|
356
|
+
);
|
|
357
|
+
if (existsSync(join(workspaceRoot, "packages", "page-sdk")) && !isWorkspaceToolsSourceRepo) {
|
|
272
358
|
errors.push("local packages/page-sdk must be removed from application workspaces");
|
|
273
359
|
}
|
|
274
360
|
walkFiles(workspaceRoot, (filePath) => {
|
|
@@ -284,6 +370,41 @@ function validateWorkspace(workspaceRoot, channel = "latest") {
|
|
|
284
370
|
if (errors.length) {
|
|
285
371
|
throw new Error(`workspace update check failed:\n- ${errors.join("\n- ")}`);
|
|
286
372
|
}
|
|
373
|
+
if (options.strictLock) {
|
|
374
|
+
const versionRows = [];
|
|
375
|
+
for (const packageName of runtimePackages) {
|
|
376
|
+
const latest = npmLatestVersion(packageName, channel);
|
|
377
|
+
const installed = readInstalledVersion(workspaceRoot, packageName);
|
|
378
|
+
const locked = readLockVersion(workspaceRoot, packageName);
|
|
379
|
+
versionRows.push({ packageName, latest, installed, locked });
|
|
380
|
+
if (latest && locked && locked !== latest) {
|
|
381
|
+
errors.push(`pnpm-lock.yaml locks ${packageName}@${locked}, latest is ${latest}`);
|
|
382
|
+
}
|
|
383
|
+
if (latest && installed && installed !== latest) {
|
|
384
|
+
errors.push(`node_modules has ${packageName}@${installed}, latest is ${latest}`);
|
|
385
|
+
}
|
|
386
|
+
if (!locked) {
|
|
387
|
+
warnings.push(`pnpm-lock.yaml does not contain ${packageName}; run pnpm install after update`);
|
|
388
|
+
}
|
|
389
|
+
if (!installed) {
|
|
390
|
+
warnings.push(`node_modules does not contain ${packageName}; run pnpm install`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (versionRows.length) {
|
|
394
|
+
console.log("[lowcode-workspace] runtime dependency versions:");
|
|
395
|
+
for (const row of versionRows) {
|
|
396
|
+
console.log(
|
|
397
|
+
` - ${row.packageName}: latest=${row.latest || "unknown"} locked=${row.locked || "missing"} installed=${row.installed || "missing"}`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (errors.length) {
|
|
403
|
+
throw new Error(`workspace update check failed:\n- ${errors.join("\n- ")}`);
|
|
404
|
+
}
|
|
405
|
+
for (const warning of warnings) {
|
|
406
|
+
console.warn(`[lowcode-workspace] warning: ${warning}`);
|
|
407
|
+
}
|
|
287
408
|
}
|
|
288
409
|
|
|
289
410
|
function runUpdateInstall(workspaceRoot, channel) {
|
|
@@ -299,12 +420,34 @@ function runUpdateInstall(workspaceRoot, channel) {
|
|
|
299
420
|
run("pnpm", ["install"], { cwd: workspaceRoot });
|
|
300
421
|
}
|
|
301
422
|
|
|
302
|
-
function
|
|
423
|
+
function runSmoke(workspaceRoot, mode = "quick") {
|
|
424
|
+
const normalizedMode = String(mode || "quick").trim();
|
|
425
|
+
if (!["quick", "full"].includes(normalizedMode)) {
|
|
426
|
+
throw new Error(`unsupported smoke mode: ${mode}`);
|
|
427
|
+
}
|
|
303
428
|
const pkg = readJson(join(workspaceRoot, "package.json"));
|
|
304
429
|
if (pkg.scripts?.typecheck) {
|
|
305
430
|
run("pnpm", ["typecheck"], { cwd: workspaceRoot });
|
|
306
431
|
}
|
|
307
|
-
|
|
432
|
+
if (normalizedMode === "full") {
|
|
433
|
+
runManagedScript("build-pages", ["--bundle-mode", "shared"], workspaceRoot);
|
|
434
|
+
runManagedScript("build-forms", ["--bundle-mode", "shared"], workspaceRoot);
|
|
435
|
+
} else {
|
|
436
|
+
runManagedScript("build-pages", ["--bundle-mode", "shared", "--help"], workspaceRoot);
|
|
437
|
+
runManagedScript("build-forms", ["--bundle-mode", "shared", "--help"], workspaceRoot);
|
|
438
|
+
}
|
|
439
|
+
validateRuntimeManifest(
|
|
440
|
+
workspaceRoot,
|
|
441
|
+
join(workspaceRoot, "dist", "page-runtime", "manifest.json"),
|
|
442
|
+
"sy-page-runtime",
|
|
443
|
+
1,
|
|
444
|
+
);
|
|
445
|
+
validateRuntimeManifest(
|
|
446
|
+
workspaceRoot,
|
|
447
|
+
join(workspaceRoot, "dist", "form-runtime", "manifest.json"),
|
|
448
|
+
"sy-form-runtime",
|
|
449
|
+
2,
|
|
450
|
+
);
|
|
308
451
|
}
|
|
309
452
|
|
|
310
453
|
function commitAndMaybePush(workspaceRoot, push) {
|
|
@@ -325,12 +468,14 @@ async function updateWorkspace(argv, { migrate = false } = {}) {
|
|
|
325
468
|
const workspaceRoot = resolve(args.workspace || process.cwd());
|
|
326
469
|
const channel = String(args.channel || process.env.APP_WORKSPACE_UPDATE_CHANNEL || "latest");
|
|
327
470
|
const checkOnly = Boolean(args.check);
|
|
471
|
+
const strictLock = Boolean(args["strict-lock"]);
|
|
328
472
|
const allowDirty = Boolean(args["allow-dirty"]);
|
|
329
473
|
const shouldCommit = Boolean(args.commit) && !args["no-commit"];
|
|
330
474
|
const shouldPush = Boolean(args.push);
|
|
475
|
+
const gateMode = String(args.gate || "quick");
|
|
331
476
|
|
|
332
477
|
if (checkOnly) {
|
|
333
|
-
validateWorkspace(workspaceRoot, channel);
|
|
478
|
+
validateWorkspace(workspaceRoot, channel, { strictLock });
|
|
334
479
|
console.log("[lowcode-workspace] update check passed");
|
|
335
480
|
return;
|
|
336
481
|
}
|
|
@@ -353,8 +498,8 @@ async function updateWorkspace(argv, { migrate = false } = {}) {
|
|
|
353
498
|
}
|
|
354
499
|
|
|
355
500
|
if (!args["skip-install"]) runUpdateInstall(workspaceRoot, channel);
|
|
356
|
-
validateWorkspace(workspaceRoot, channel);
|
|
357
|
-
if (!args["skip-gate"])
|
|
501
|
+
validateWorkspace(workspaceRoot, channel, { strictLock: true });
|
|
502
|
+
if (!args["skip-gate"]) runSmoke(workspaceRoot, gateMode);
|
|
358
503
|
if (shouldCommit) commitAndMaybePush(workspaceRoot, shouldPush);
|
|
359
504
|
console.log("[lowcode-workspace] workspace update completed");
|
|
360
505
|
}
|
|
@@ -373,6 +518,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
373
518
|
await updateWorkspace(rest, { migrate: true });
|
|
374
519
|
return;
|
|
375
520
|
}
|
|
521
|
+
if (command === "smoke") {
|
|
522
|
+
const args = parseArgs(rest);
|
|
523
|
+
runSmoke(resolve(args.workspace || process.cwd()), args.mode || "quick");
|
|
524
|
+
console.log("[lowcode-workspace] smoke check passed");
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
376
527
|
if (managedCommands.has(command)) {
|
|
377
528
|
const args = parseArgs(rest);
|
|
378
529
|
runManagedScript(command, rest, resolve(args.workspace || process.cwd()));
|