sy-lowcode-workspace-tools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/bin/lowcode-workspace.mjs +7 -0
- package/package.json +34 -0
- package/scripts/build-forms.mjs +756 -0
- package/scripts/build-pages.mjs +691 -0
- package/scripts/build-workspace.mjs +59 -0
- package/scripts/publish-all.mjs +111 -0
- package/scripts/publish-oss.mjs +143 -0
- package/scripts/register-bundle.mjs +1 -0
- package/scripts/register.mjs +242 -0
- package/scripts/sync-schema.mjs +287 -0
- package/scripts/utils/form-api.mjs +482 -0
- package/scripts/utils/load-config.mjs +210 -0
- package/scripts/utils/mime-types.mjs +70 -0
- package/scripts/utils/oss-client.mjs +128 -0
- package/scripts/utils/pages.mjs +80 -0
- package/scripts/utils/progress.mjs +57 -0
- package/scripts/utils/register-payload.mjs +89 -0
- package/scripts/utils/register-payload.test.ts +76 -0
- package/scripts/utils/schema-transform.mjs +130 -0
- package/scripts/utils/schema-transform.test.ts +141 -0
- package/src/cli.mjs +382 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
assertFormReadyForBundle,
|
|
6
|
+
assertSchemaSyncResult,
|
|
7
|
+
transformToApiFormat,
|
|
8
|
+
} from "./schema-transform.mjs";
|
|
9
|
+
|
|
10
|
+
describe("schema-transform", () => {
|
|
11
|
+
it("builds form component nodes with stable field metadata", () => {
|
|
12
|
+
const payload = transformToApiFormat(
|
|
13
|
+
{
|
|
14
|
+
formMeta: {
|
|
15
|
+
formUuid: "FORM_1",
|
|
16
|
+
appType: "APP_1",
|
|
17
|
+
title: "客户信息登记",
|
|
18
|
+
},
|
|
19
|
+
fields: [
|
|
20
|
+
{
|
|
21
|
+
fieldId: "customer_name",
|
|
22
|
+
componentName: "TextField",
|
|
23
|
+
label: "客户名称",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
fieldId: "remark",
|
|
27
|
+
componentName: "TextAreaField",
|
|
28
|
+
label: "备注",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
"customer-info",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const schema = JSON.parse(payload.schema);
|
|
36
|
+
const nodes = schema.componentsTree[0].children;
|
|
37
|
+
|
|
38
|
+
expect(payload.fieldCount).toBe(2);
|
|
39
|
+
expect(nodes[0].props).toMatchObject({
|
|
40
|
+
fieldId: "customer_name",
|
|
41
|
+
componentName: "TextField",
|
|
42
|
+
isFormComponent: true,
|
|
43
|
+
});
|
|
44
|
+
expect(nodes[1].props).toMatchObject({
|
|
45
|
+
fieldId: "remark",
|
|
46
|
+
componentName: "TextareaField",
|
|
47
|
+
isFormComponent: true,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("rejects duplicate field ids and empty fields", () => {
|
|
52
|
+
expect(() =>
|
|
53
|
+
transformToApiFormat(
|
|
54
|
+
{
|
|
55
|
+
formMeta: { formUuid: "FORM_1", appType: "APP_1", title: "表单" },
|
|
56
|
+
fields: [],
|
|
57
|
+
},
|
|
58
|
+
"empty-form",
|
|
59
|
+
),
|
|
60
|
+
).toThrow("schema.fields 不能为空");
|
|
61
|
+
|
|
62
|
+
expect(() =>
|
|
63
|
+
transformToApiFormat(
|
|
64
|
+
{
|
|
65
|
+
formMeta: { formUuid: "FORM_1", appType: "APP_1", title: "表单" },
|
|
66
|
+
fields: [
|
|
67
|
+
{ fieldId: "name", componentName: "TextField", label: "姓名" },
|
|
68
|
+
{ fieldId: "name", componentName: "TextField", label: "姓名2" },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
"duplicate-form",
|
|
72
|
+
),
|
|
73
|
+
).toThrow("fieldId 重复: name");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("validates schema sync response before treating publish as ready", () => {
|
|
77
|
+
expect(() =>
|
|
78
|
+
assertSchemaSyncResult(
|
|
79
|
+
{
|
|
80
|
+
code: 200,
|
|
81
|
+
data: {
|
|
82
|
+
tableName: "form_app_1",
|
|
83
|
+
formFields: {
|
|
84
|
+
customer_name: {},
|
|
85
|
+
remark: {},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
2,
|
|
90
|
+
),
|
|
91
|
+
).not.toThrow();
|
|
92
|
+
|
|
93
|
+
expect(() =>
|
|
94
|
+
assertSchemaSyncResult(
|
|
95
|
+
{
|
|
96
|
+
code: 200,
|
|
97
|
+
data: {
|
|
98
|
+
tableName: "null",
|
|
99
|
+
formFields: { customer_name: {} },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
1,
|
|
103
|
+
),
|
|
104
|
+
).toThrow("有效 tableName");
|
|
105
|
+
|
|
106
|
+
expect(() =>
|
|
107
|
+
assertSchemaSyncResult(
|
|
108
|
+
{
|
|
109
|
+
code: 200,
|
|
110
|
+
data: {
|
|
111
|
+
tableName: "form_app_1",
|
|
112
|
+
formFields: { customer_name: {} },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
2,
|
|
116
|
+
),
|
|
117
|
+
).toThrow("formFields 数量不匹配");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("rejects bundle registration before backend schema is initialized", () => {
|
|
121
|
+
expect(() =>
|
|
122
|
+
assertFormReadyForBundle(
|
|
123
|
+
{
|
|
124
|
+
tableName: "",
|
|
125
|
+
formFields: { customer_name: {} },
|
|
126
|
+
},
|
|
127
|
+
"customer-info",
|
|
128
|
+
),
|
|
129
|
+
).toThrow("缺少 tableName");
|
|
130
|
+
|
|
131
|
+
expect(() =>
|
|
132
|
+
assertFormReadyForBundle(
|
|
133
|
+
{
|
|
134
|
+
tableName: "form_app_1",
|
|
135
|
+
formFields: {},
|
|
136
|
+
},
|
|
137
|
+
"customer-info",
|
|
138
|
+
),
|
|
139
|
+
).toThrow("缺少 formFields");
|
|
140
|
+
});
|
|
141
|
+
});
|
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
const managedScriptDir = join(packageRoot, "scripts");
|
|
10
|
+
const managedCommands = new Map([
|
|
11
|
+
["build", "build-workspace.mjs"],
|
|
12
|
+
["build-forms", "build-forms.mjs"],
|
|
13
|
+
["build-pages", "build-pages.mjs"],
|
|
14
|
+
["sync-schema", "sync-schema.mjs"],
|
|
15
|
+
["publish-oss", "publish-oss.mjs"],
|
|
16
|
+
["register", "register.mjs"],
|
|
17
|
+
["publish-all", "publish-all.mjs"],
|
|
18
|
+
]);
|
|
19
|
+
const wrapperScripts = new Map([
|
|
20
|
+
["build-workspace.mjs", "build"],
|
|
21
|
+
["build-forms.mjs", "build-forms"],
|
|
22
|
+
["build-pages.mjs", "build-pages"],
|
|
23
|
+
["sync-schema.mjs", "sync-schema"],
|
|
24
|
+
["publish-oss.mjs", "publish-oss"],
|
|
25
|
+
["register.mjs", "register"],
|
|
26
|
+
["publish-all.mjs", "publish-all"],
|
|
27
|
+
]);
|
|
28
|
+
const textExtensions = new Set([
|
|
29
|
+
".js",
|
|
30
|
+
".jsx",
|
|
31
|
+
".ts",
|
|
32
|
+
".tsx",
|
|
33
|
+
".mjs",
|
|
34
|
+
".cjs",
|
|
35
|
+
".json",
|
|
36
|
+
".md",
|
|
37
|
+
".css",
|
|
38
|
+
".scss",
|
|
39
|
+
".yml",
|
|
40
|
+
".yaml",
|
|
41
|
+
]);
|
|
42
|
+
const ignoredDirs = new Set([".git", "node_modules", "dist", "build", "coverage", ".vite"]);
|
|
43
|
+
|
|
44
|
+
function usage() {
|
|
45
|
+
return `
|
|
46
|
+
lowcode-workspace <command> [options]
|
|
47
|
+
|
|
48
|
+
Commands:
|
|
49
|
+
build | build-forms | build-pages | sync-schema | publish-oss | register | publish-all
|
|
50
|
+
update Update workspace runtime dependencies and managed wrappers
|
|
51
|
+
migrate Convert old local SDK workspace to npm package mode
|
|
52
|
+
|
|
53
|
+
Update options:
|
|
54
|
+
--workspace <path> Workspace root, defaults to cwd
|
|
55
|
+
--channel <tag> npm dist-tag, defaults to latest
|
|
56
|
+
--check Validate only; do not mutate
|
|
57
|
+
--commit Commit update changes
|
|
58
|
+
--push Push after commit
|
|
59
|
+
--no-commit Do not commit, even if changes exist
|
|
60
|
+
--allow-dirty Allow updates with existing worktree changes
|
|
61
|
+
--skip-install Do not run pnpm install/update
|
|
62
|
+
--skip-gate Do not run typecheck smoke gate
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseArgs(argv) {
|
|
67
|
+
const result = { _: [] };
|
|
68
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
69
|
+
const arg = argv[index];
|
|
70
|
+
if (!arg.startsWith("--")) {
|
|
71
|
+
result._.push(arg);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const key = arg.slice(2);
|
|
75
|
+
if (["workspace", "channel"].includes(key)) {
|
|
76
|
+
result[key] = argv[index + 1];
|
|
77
|
+
index += 1;
|
|
78
|
+
} else {
|
|
79
|
+
result[key] = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function run(command, args, options = {}) {
|
|
86
|
+
const result = spawnSync(command, args, {
|
|
87
|
+
cwd: options.cwd,
|
|
88
|
+
env: options.env || process.env,
|
|
89
|
+
stdio: options.capture ? "pipe" : "inherit",
|
|
90
|
+
encoding: "utf-8",
|
|
91
|
+
});
|
|
92
|
+
if (result.status !== 0) {
|
|
93
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
94
|
+
throw new Error(
|
|
95
|
+
`command failed: ${[command, ...args].join(" ")}${output ? `\n${output}` : ""}`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function runManagedScript(command, args, workspaceRoot) {
|
|
102
|
+
const scriptName = managedCommands.get(command);
|
|
103
|
+
if (!scriptName) throw new Error(`unknown managed command: ${command}`);
|
|
104
|
+
const scriptPath = join(managedScriptDir, scriptName);
|
|
105
|
+
const tsxCli = require.resolve("tsx/cli");
|
|
106
|
+
run(process.execPath, [tsxCli, scriptPath, ...args], {
|
|
107
|
+
cwd: workspaceRoot,
|
|
108
|
+
env: {
|
|
109
|
+
...process.env,
|
|
110
|
+
LOWCODE_WORKSPACE_ROOT: workspaceRoot,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function readJson(path) {
|
|
116
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function writeJson(path, value) {
|
|
120
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function gitStatus(workspaceRoot) {
|
|
124
|
+
const result = run("git", ["status", "--porcelain"], {
|
|
125
|
+
cwd: workspaceRoot,
|
|
126
|
+
capture: true,
|
|
127
|
+
});
|
|
128
|
+
return String(result.stdout || "").trim();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function hasGit(workspaceRoot) {
|
|
132
|
+
return existsSync(join(workspaceRoot, ".git")) || existsSync(join(workspaceRoot, "..", ".git"));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function updatePackageJson(workspaceRoot, channel) {
|
|
136
|
+
const packagePath = join(workspaceRoot, "package.json");
|
|
137
|
+
if (!existsSync(packagePath)) throw new Error(`package.json not found: ${packagePath}`);
|
|
138
|
+
const pkg = readJson(packagePath);
|
|
139
|
+
pkg.scripts = {
|
|
140
|
+
...(pkg.scripts || {}),
|
|
141
|
+
build: "lowcode-workspace build",
|
|
142
|
+
"build:forms": "lowcode-workspace build-forms",
|
|
143
|
+
"build:pages": "lowcode-workspace build-pages",
|
|
144
|
+
"sync-schema": "lowcode-workspace sync-schema",
|
|
145
|
+
"publish:oss": "lowcode-workspace publish-oss",
|
|
146
|
+
register: "lowcode-workspace register",
|
|
147
|
+
"register-bundle": "lowcode-workspace register",
|
|
148
|
+
"publish:all": "lowcode-workspace publish-all",
|
|
149
|
+
"ai:update": `pnpm dlx sy-lowcode-workspace-tools@${channel} update --channel ${channel}`,
|
|
150
|
+
"ai:migrate": `pnpm dlx sy-lowcode-workspace-tools@${channel} migrate`,
|
|
151
|
+
};
|
|
152
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
153
|
+
delete pkg.dependencies["@sy/page-sdk"];
|
|
154
|
+
pkg.dependencies["sy-form-components"] = channel;
|
|
155
|
+
pkg.dependencies["sy-page-sdk"] = channel;
|
|
156
|
+
pkg.dependencies["sy-lowcode-workspace-tools"] = channel;
|
|
157
|
+
writeJson(packagePath, pkg);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function wrapperContent(command) {
|
|
161
|
+
return `#!/usr/bin/env node
|
|
162
|
+
import { main } from "sy-lowcode-workspace-tools";
|
|
163
|
+
|
|
164
|
+
await main([${JSON.stringify(command)}, ...process.argv.slice(2)]);
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ensureWrapperScripts(workspaceRoot) {
|
|
169
|
+
const scriptsDir = join(workspaceRoot, "scripts");
|
|
170
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
171
|
+
for (const [fileName, command] of wrapperScripts) {
|
|
172
|
+
writeFileSync(join(scriptsDir, fileName), wrapperContent(command), "utf-8");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function replaceInFile(path, replacer) {
|
|
177
|
+
const before = readFileSync(path, "utf-8");
|
|
178
|
+
const after = replacer(before);
|
|
179
|
+
if (after !== before) writeFileSync(path, after, "utf-8");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function rewriteImports(workspaceRoot) {
|
|
183
|
+
walkFiles(workspaceRoot, (filePath) => {
|
|
184
|
+
if (!textExtensions.has(filePath.slice(filePath.lastIndexOf(".")))) return;
|
|
185
|
+
replaceInFile(filePath, (content) =>
|
|
186
|
+
content.replaceAll("@sy/page-sdk", "sy-page-sdk"),
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function rewriteLocalSdkConfig(workspaceRoot) {
|
|
192
|
+
const viteConfig = join(workspaceRoot, "vite.config.ts");
|
|
193
|
+
if (existsSync(viteConfig)) {
|
|
194
|
+
replaceInFile(viteConfig, (content) =>
|
|
195
|
+
content
|
|
196
|
+
.replace(/\s*\{[^{}]*packages\/page-sdk[^{}]*\},?/g, "")
|
|
197
|
+
.replace(/\s*\{\s*find:\s*["']sy-page-sdk\/react["'][\s\S]*?\n\s*\},\n/g, "")
|
|
198
|
+
.replace(/\s*\{\s*find:\s*["']sy-page-sdk["'][\s\S]*?\n\s*\},\n/g, ""),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const vitestConfig = join(workspaceRoot, "vitest.config.ts");
|
|
202
|
+
if (existsSync(vitestConfig)) {
|
|
203
|
+
replaceInFile(vitestConfig, (content) =>
|
|
204
|
+
content
|
|
205
|
+
.replace(/\s*\{[^{}]*packages\/page-sdk[^{}]*\},?/g, "")
|
|
206
|
+
.replace(/\s*\{\s*find:\s*["']sy-page-sdk\/react["'][\s\S]*?\n\s*\},\n/g, "")
|
|
207
|
+
.replace(/\s*\{\s*find:\s*["']sy-page-sdk["'][\s\S]*?\n\s*\},\n/g, "")
|
|
208
|
+
.replace(/,\s*["']sy-page-sdk["']/g, ""),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const tsconfig = join(workspaceRoot, "tsconfig.app.json");
|
|
212
|
+
if (existsSync(tsconfig)) {
|
|
213
|
+
try {
|
|
214
|
+
const config = readJson(tsconfig);
|
|
215
|
+
if (config.compilerOptions?.paths) {
|
|
216
|
+
delete config.compilerOptions.paths["sy-page-sdk"];
|
|
217
|
+
delete config.compilerOptions.paths["sy-page-sdk/react"];
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(config.include)) {
|
|
220
|
+
config.include = config.include.filter((item) => !String(item).includes("packages/page-sdk"));
|
|
221
|
+
}
|
|
222
|
+
writeJson(tsconfig, config);
|
|
223
|
+
} catch {
|
|
224
|
+
replaceInFile(tsconfig, (content) =>
|
|
225
|
+
content
|
|
226
|
+
.replace(/\s*["']sy-page-sdk["']:\s*\[[^\n]*\],?\n/g, "")
|
|
227
|
+
.replace(/\s*["']sy-page-sdk\/react["']:\s*\[[^\n]*\],?\n/g, "")
|
|
228
|
+
.replace(/,\s*["']packages\/page-sdk\/src["']/g, "")
|
|
229
|
+
.replace(/["']packages\/page-sdk\/src["'],?\s*/g, ""),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const tailwindConfig = join(workspaceRoot, "tailwind.config.cjs");
|
|
234
|
+
if (existsSync(tailwindConfig)) {
|
|
235
|
+
replaceInFile(tailwindConfig, (content) =>
|
|
236
|
+
content
|
|
237
|
+
.replace(/,\s*["'][^"']*packages\/page-sdk[^"']*["']/g, "")
|
|
238
|
+
.replace(/["'][^"']*packages\/page-sdk[^"']*["']\s*,?/g, ""),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function walkFiles(root, onFile) {
|
|
244
|
+
for (const entry of readdirSync(root)) {
|
|
245
|
+
if (ignoredDirs.has(entry)) continue;
|
|
246
|
+
const path = join(root, entry);
|
|
247
|
+
const stat = statSync(path);
|
|
248
|
+
if (stat.isDirectory()) {
|
|
249
|
+
walkFiles(path, onFile);
|
|
250
|
+
} else if (stat.isFile()) {
|
|
251
|
+
onFile(path);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function removeLocalSdk(workspaceRoot) {
|
|
257
|
+
const localSdkPath = join(workspaceRoot, "packages", "page-sdk");
|
|
258
|
+
if (existsSync(localSdkPath)) {
|
|
259
|
+
rmSync(localSdkPath, { recursive: true, force: true });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function validateWorkspace(workspaceRoot, channel = "latest") {
|
|
264
|
+
const errors = [];
|
|
265
|
+
const pkg = readJson(join(workspaceRoot, "package.json"));
|
|
266
|
+
const deps = pkg.dependencies || {};
|
|
267
|
+
if (deps["@sy/page-sdk"]) errors.push("package.json still depends on @sy/page-sdk");
|
|
268
|
+
for (const name of ["sy-form-components", "sy-page-sdk", "sy-lowcode-workspace-tools"]) {
|
|
269
|
+
if (deps[name] !== channel) errors.push(`package.json dependency ${name} must be ${channel}`);
|
|
270
|
+
}
|
|
271
|
+
if (existsSync(join(workspaceRoot, "packages", "page-sdk"))) {
|
|
272
|
+
errors.push("local packages/page-sdk must be removed from application workspaces");
|
|
273
|
+
}
|
|
274
|
+
walkFiles(workspaceRoot, (filePath) => {
|
|
275
|
+
if (!textExtensions.has(filePath.slice(filePath.lastIndexOf(".")))) return;
|
|
276
|
+
const content = readFileSync(filePath, "utf-8");
|
|
277
|
+
if (content.includes("@sy/page-sdk")) {
|
|
278
|
+
errors.push(`${filePath} still references @sy/page-sdk`);
|
|
279
|
+
}
|
|
280
|
+
if (content.includes("packages/page-sdk")) {
|
|
281
|
+
errors.push(`${filePath} still references packages/page-sdk`);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
if (errors.length) {
|
|
285
|
+
throw new Error(`workspace update check failed:\n- ${errors.join("\n- ")}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function runUpdateInstall(workspaceRoot, channel) {
|
|
290
|
+
run("pnpm", ["install"], { cwd: workspaceRoot });
|
|
291
|
+
run("pnpm", [
|
|
292
|
+
"update",
|
|
293
|
+
"--latest",
|
|
294
|
+
"sy-form-components",
|
|
295
|
+
"sy-page-sdk",
|
|
296
|
+
"sy-lowcode-workspace-tools",
|
|
297
|
+
], { cwd: workspaceRoot });
|
|
298
|
+
updatePackageJson(workspaceRoot, channel);
|
|
299
|
+
run("pnpm", ["install"], { cwd: workspaceRoot });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function runQuickGate(workspaceRoot) {
|
|
303
|
+
const pkg = readJson(join(workspaceRoot, "package.json"));
|
|
304
|
+
if (pkg.scripts?.typecheck) {
|
|
305
|
+
run("pnpm", ["typecheck"], { cwd: workspaceRoot });
|
|
306
|
+
}
|
|
307
|
+
runManagedScript("build-pages", ["--help"], workspaceRoot);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function commitAndMaybePush(workspaceRoot, push) {
|
|
311
|
+
if (!hasGit(workspaceRoot)) return;
|
|
312
|
+
run("git", ["add", "-A"], { cwd: workspaceRoot });
|
|
313
|
+
const status = gitStatus(workspaceRoot);
|
|
314
|
+
if (!status) return;
|
|
315
|
+
run("git", ["commit", "-m", "chore: update lowcode workspace runtime dependencies"], {
|
|
316
|
+
cwd: workspaceRoot,
|
|
317
|
+
});
|
|
318
|
+
if (push) {
|
|
319
|
+
run("git", ["push", "origin", "HEAD"], { cwd: workspaceRoot });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function updateWorkspace(argv, { migrate = false } = {}) {
|
|
324
|
+
const args = parseArgs(argv);
|
|
325
|
+
const workspaceRoot = resolve(args.workspace || process.cwd());
|
|
326
|
+
const channel = String(args.channel || process.env.APP_WORKSPACE_UPDATE_CHANNEL || "latest");
|
|
327
|
+
const checkOnly = Boolean(args.check);
|
|
328
|
+
const allowDirty = Boolean(args["allow-dirty"]);
|
|
329
|
+
const shouldCommit = Boolean(args.commit) && !args["no-commit"];
|
|
330
|
+
const shouldPush = Boolean(args.push);
|
|
331
|
+
|
|
332
|
+
if (checkOnly) {
|
|
333
|
+
validateWorkspace(workspaceRoot, channel);
|
|
334
|
+
console.log("[lowcode-workspace] update check passed");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (hasGit(workspaceRoot) && !allowDirty) {
|
|
339
|
+
const before = gitStatus(workspaceRoot);
|
|
340
|
+
if (before) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
"workspace has uncommitted changes; commit/stash them first or pass --allow-dirty",
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
updatePackageJson(workspaceRoot, channel);
|
|
348
|
+
ensureWrapperScripts(workspaceRoot);
|
|
349
|
+
rewriteImports(workspaceRoot);
|
|
350
|
+
rewriteLocalSdkConfig(workspaceRoot);
|
|
351
|
+
if (migrate || existsSync(join(workspaceRoot, "packages", "page-sdk"))) {
|
|
352
|
+
removeLocalSdk(workspaceRoot);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!args["skip-install"]) runUpdateInstall(workspaceRoot, channel);
|
|
356
|
+
validateWorkspace(workspaceRoot, channel);
|
|
357
|
+
if (!args["skip-gate"]) runQuickGate(workspaceRoot);
|
|
358
|
+
if (shouldCommit) commitAndMaybePush(workspaceRoot, shouldPush);
|
|
359
|
+
console.log("[lowcode-workspace] workspace update completed");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
363
|
+
const [command, ...rest] = argv;
|
|
364
|
+
if (!command || command === "--help" || command === "-h") {
|
|
365
|
+
console.log(usage());
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (command === "update") {
|
|
369
|
+
await updateWorkspace(rest);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (command === "migrate") {
|
|
373
|
+
await updateWorkspace(rest, { migrate: true });
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (managedCommands.has(command)) {
|
|
377
|
+
const args = parseArgs(rest);
|
|
378
|
+
runManagedScript(command, rest, resolve(args.workspace || process.cwd()));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
throw new Error(`unknown command: ${command}`);
|
|
382
|
+
}
|