@elaraai/create-east 1.0.30 → 1.0.32
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 +273 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../scaffold-core/dist/scaffold.js
|
|
4
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { join, relative } from "node:path";
|
|
4
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5
|
+
import { join as join2, relative as relative2 } from "node:path";
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
7
|
|
|
8
8
|
// ../scaffold-core/dist/names.js
|
|
@@ -18,6 +18,203 @@ function deriveNames(rawName, cwd) {
|
|
|
18
18
|
return { projectName, displayName, workspaceName };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// ../scaffold-core/dist/packages.js
|
|
22
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { join, relative } from "node:path";
|
|
24
|
+
var RUNTIMES = {
|
|
25
|
+
// Python package/dir names must be import-safe (letters, digits, underscore).
|
|
26
|
+
python: {
|
|
27
|
+
runtime: "python",
|
|
28
|
+
destParent: "packages/python",
|
|
29
|
+
templateName: "python",
|
|
30
|
+
label: "Python",
|
|
31
|
+
valid: /^[a-z][a-z0-9_]*$/,
|
|
32
|
+
rule: "lowercase letters, digits and underscores, starting with a letter"
|
|
33
|
+
},
|
|
34
|
+
// Node member names become the last segment of an npm dir; keep them tame.
|
|
35
|
+
node: {
|
|
36
|
+
runtime: "node",
|
|
37
|
+
destParent: "packages/node",
|
|
38
|
+
templateName: "node",
|
|
39
|
+
label: "Node",
|
|
40
|
+
valid: /^[a-z][a-z0-9-]*$/,
|
|
41
|
+
rule: "lowercase letters, digits and hyphens, starting with a letter"
|
|
42
|
+
},
|
|
43
|
+
// C build dir names — letters, digits, underscore, hyphen.
|
|
44
|
+
c: {
|
|
45
|
+
runtime: "c",
|
|
46
|
+
destParent: "packages/native",
|
|
47
|
+
templateName: "c",
|
|
48
|
+
label: "C",
|
|
49
|
+
valid: /^[a-z][a-z0-9_-]*$/,
|
|
50
|
+
rule: "lowercase letters, digits, underscores and hyphens, starting with a letter"
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var RUNTIME_ORDER = ["python", "node", "c"];
|
|
54
|
+
function toIdent(name) {
|
|
55
|
+
return name.replace(/[^a-zA-Z0-9]/g, "_");
|
|
56
|
+
}
|
|
57
|
+
function substitute(text, names, version, pkg) {
|
|
58
|
+
let out = text.replaceAll("__PROJECT_NAME__", names.projectName).replaceAll("__DISPLAY_NAME__", names.displayName).replaceAll("__WORKSPACE_NAME__", names.workspaceName).replaceAll("__VERSION__", version);
|
|
59
|
+
if (pkg)
|
|
60
|
+
out = out.replaceAll("__PACKAGE_NAME__", pkg.name).replaceAll("__PACKAGE_IDENT__", pkg.ident);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
function walkFiles(dir) {
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const entry of readdirSync(dir)) {
|
|
66
|
+
const full = join(dir, entry);
|
|
67
|
+
if (statSync(full).isDirectory())
|
|
68
|
+
out.push(...walkFiles(full));
|
|
69
|
+
else
|
|
70
|
+
out.push(full);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function stampMember(projectDir, templateRoot, cfg, names, version, pkg) {
|
|
75
|
+
const memberTemplate = join(templateRoot, cfg.templateName);
|
|
76
|
+
if (!existsSync(memberTemplate)) {
|
|
77
|
+
throw new Error(`internal: missing package template for ${cfg.label} at ${memberTemplate}`);
|
|
78
|
+
}
|
|
79
|
+
const memberDir = join(projectDir, cfg.destParent, pkg.name);
|
|
80
|
+
for (const srcPath of walkFiles(memberTemplate)) {
|
|
81
|
+
const rel = relative(memberTemplate, srcPath).replaceAll("\\", "/");
|
|
82
|
+
if (!rel.includes("/") && rel.startsWith("_")) {
|
|
83
|
+
if (rel === "_app.ts") {
|
|
84
|
+
const dest2 = join(projectDir, "src", "packages", `${pkg.name}.ts`);
|
|
85
|
+
mkdirSync(join(dest2, ".."), { recursive: true });
|
|
86
|
+
writeFileSync(dest2, substitute(readFileSync(srcPath, "utf8"), names, version, pkg));
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const dest = join(memberDir, substitute(rel, names, version, pkg));
|
|
91
|
+
mkdirSync(join(dest, ".."), { recursive: true });
|
|
92
|
+
writeFileSync(dest, substitute(readFileSync(srcPath, "utf8"), names, version, pkg));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function writePythonWorkspaceRoot(projectDir, names, members) {
|
|
96
|
+
const sources = members.map((m) => `${m} = { workspace = true }`).join("\n");
|
|
97
|
+
const body = `[project]
|
|
98
|
+
name = "${names.projectName}"
|
|
99
|
+
version = "0.1.0"
|
|
100
|
+
description = "${names.displayName}"
|
|
101
|
+
requires-python = ">=3.11"
|
|
102
|
+
dependencies = [
|
|
103
|
+
"elaraai-east-py",
|
|
104
|
+
"elaraai-east-py-std",
|
|
105
|
+
"elaraai-east-py-io",
|
|
106
|
+
"elaraai-east-py-datascience",
|
|
107
|
+
"elaraai-east-py-cli",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
# This project is a uv workspace root: each package under packages/python/*
|
|
111
|
+
# is its own member with its own dependency closure (and its own captured
|
|
112
|
+
# execution environment). No [build-system] \u2014 the root is not itself a
|
|
113
|
+
# Python package, just the workspace + the shared runtime dependencies.
|
|
114
|
+
[tool.uv.workspace]
|
|
115
|
+
members = ["packages/python/*"]
|
|
116
|
+
|
|
117
|
+
[tool.uv.sources]
|
|
118
|
+
${sources}
|
|
119
|
+
`;
|
|
120
|
+
writeFileSync(join(projectDir, "pyproject.toml"), body);
|
|
121
|
+
}
|
|
122
|
+
function patchNodeWorkspaceRoot(projectDir) {
|
|
123
|
+
const path = join(projectDir, "package.json");
|
|
124
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
125
|
+
const globs = new Set(Array.isArray(pkg.workspaces) ? pkg.workspaces : []);
|
|
126
|
+
globs.add("packages/node/*");
|
|
127
|
+
pkg.workspaces = [...globs];
|
|
128
|
+
pkg.private = true;
|
|
129
|
+
const scripts = pkg.scripts ?? {};
|
|
130
|
+
if (typeof scripts.build === "string" && !scripts.build.includes("--workspaces")) {
|
|
131
|
+
scripts.build = `npm run build --workspaces --if-present && ${scripts.build}`;
|
|
132
|
+
pkg.scripts = scripts;
|
|
133
|
+
}
|
|
134
|
+
writeFileSync(path, `${JSON.stringify(pkg, null, 2)}
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
137
|
+
function excludePackagesFromRootTsconfig(projectDir) {
|
|
138
|
+
const path = join(projectDir, "tsconfig.json");
|
|
139
|
+
if (!existsSync(path))
|
|
140
|
+
return;
|
|
141
|
+
const tsconfig = JSON.parse(readFileSync(path, "utf8"));
|
|
142
|
+
const exclude = new Set(tsconfig.exclude ?? []);
|
|
143
|
+
exclude.add("packages");
|
|
144
|
+
tsconfig.exclude = [...exclude];
|
|
145
|
+
writeFileSync(path, `${JSON.stringify(tsconfig, null, 2)}
|
|
146
|
+
`);
|
|
147
|
+
}
|
|
148
|
+
function writeAppWiring(projectDir, names, members) {
|
|
149
|
+
const imports = members.map((m) => `import { ${m.ident}_task } from "./${m.name}.js";`).join("\n");
|
|
150
|
+
const list = members.map((m) => ` ${m.ident}_task,`).join("\n");
|
|
151
|
+
const barrel = `// Generated by create-e3 \u2014 collects one example task per workspace package.
|
|
152
|
+
// Each task's \`environment\` points at its package, so editing one package
|
|
153
|
+
// re-runs only its task. Add packages with \`create-e3 --python-packages=\u2026\`.
|
|
154
|
+
${imports}
|
|
155
|
+
|
|
156
|
+
export const packageTasks = [
|
|
157
|
+
${list}
|
|
158
|
+
];
|
|
159
|
+
`;
|
|
160
|
+
mkdirSync(join(projectDir, "src", "packages"), { recursive: true });
|
|
161
|
+
writeFileSync(join(projectDir, "src", "packages", "index.ts"), barrel);
|
|
162
|
+
const index = `import e3 from "@elaraai/e3";
|
|
163
|
+
|
|
164
|
+
import { packageTasks } from "./packages/index.js";
|
|
165
|
+
|
|
166
|
+
// This app wires one example task per generated workspace package. Every
|
|
167
|
+
// task's \`environment\` is captured independently, so editing one package
|
|
168
|
+
// re-runs only its task \u2014 the other packages stay cached. Add your own
|
|
169
|
+
// inputs, tasks and dataflows here.
|
|
170
|
+
export default e3.package("${names.projectName}", "1.0.0", ...packageTasks);
|
|
171
|
+
`;
|
|
172
|
+
writeFileSync(join(projectDir, "src", "index.ts"), index);
|
|
173
|
+
}
|
|
174
|
+
function generatePackages(options) {
|
|
175
|
+
const { projectDir, templateDir, names, version, spec, log } = options;
|
|
176
|
+
const templateRoot = join(templateDir, "_packages");
|
|
177
|
+
const members = [];
|
|
178
|
+
const seen = /* @__PURE__ */ new Map();
|
|
179
|
+
for (const runtime of RUNTIME_ORDER) {
|
|
180
|
+
const cfg = RUNTIMES[runtime];
|
|
181
|
+
for (const raw of spec[runtime] ?? []) {
|
|
182
|
+
const name = raw.trim();
|
|
183
|
+
if (!name)
|
|
184
|
+
throw new Error(`--${runtime}-packages contains an empty package name`);
|
|
185
|
+
if (!cfg.valid.test(name)) {
|
|
186
|
+
throw new Error(`${cfg.label} package name '${name}' is invalid \u2014 use ${cfg.rule}`);
|
|
187
|
+
}
|
|
188
|
+
const prior = seen.get(name);
|
|
189
|
+
if (prior) {
|
|
190
|
+
throw new Error(`package name '${name}' is declared twice (${prior} and ${runtime}) \u2014 names must be unique`);
|
|
191
|
+
}
|
|
192
|
+
seen.set(name, runtime);
|
|
193
|
+
members.push({ name, ident: toIdent(name), runtime });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (members.length === 0)
|
|
197
|
+
return;
|
|
198
|
+
for (const runtime of RUNTIME_ORDER) {
|
|
199
|
+
if ((spec[runtime]?.length ?? 0) > 0 && !existsSync(join(templateRoot, RUNTIMES[runtime].templateName))) {
|
|
200
|
+
throw new Error(`${RUNTIMES[runtime].label} packages are not available in this scaffold yet \u2014 remove --${runtime}-packages for now`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const pkg of members) {
|
|
204
|
+
stampMember(projectDir, templateRoot, RUNTIMES[pkg.runtime], names, version, pkg);
|
|
205
|
+
}
|
|
206
|
+
const py = members.filter((m) => m.runtime === "python").map((m) => m.name);
|
|
207
|
+
const node = members.filter((m) => m.runtime === "node").map((m) => m.name);
|
|
208
|
+
if (py.length > 0)
|
|
209
|
+
writePythonWorkspaceRoot(projectDir, names, py);
|
|
210
|
+
if (node.length > 0)
|
|
211
|
+
patchNodeWorkspaceRoot(projectDir);
|
|
212
|
+
excludePackagesFromRootTsconfig(projectDir);
|
|
213
|
+
writeAppWiring(projectDir, names, members);
|
|
214
|
+
const summary = RUNTIME_ORDER.map((r) => ({ r, n: members.filter((m) => m.runtime === r).length })).filter((x) => x.n > 0).map((x) => `${x.n} ${RUNTIMES[x.r].label}`).join(", ");
|
|
215
|
+
log(`Generated ${members.length} workspace package${members.length === 1 ? "" : "s"} (${summary}).`);
|
|
216
|
+
}
|
|
217
|
+
|
|
21
218
|
// ../scaffold-core/dist/scaffold.js
|
|
22
219
|
var TEXT_REPLACE_EXT = /* @__PURE__ */ new Set([
|
|
23
220
|
".ts",
|
|
@@ -102,9 +299,9 @@ function transformPackageJson(raw, names, version, manifest, enabled) {
|
|
|
102
299
|
}
|
|
103
300
|
function walk(dir) {
|
|
104
301
|
const out = [];
|
|
105
|
-
for (const entry of
|
|
106
|
-
const full =
|
|
107
|
-
if (
|
|
302
|
+
for (const entry of readdirSync2(dir)) {
|
|
303
|
+
const full = join2(dir, entry);
|
|
304
|
+
if (statSync2(full).isDirectory())
|
|
108
305
|
out.push(...walk(full));
|
|
109
306
|
else
|
|
110
307
|
out.push(full);
|
|
@@ -117,16 +314,16 @@ function extOf(path) {
|
|
|
117
314
|
return dot <= 0 ? "" : base.slice(dot);
|
|
118
315
|
}
|
|
119
316
|
function loadManifest(templateDir) {
|
|
120
|
-
const path =
|
|
121
|
-
if (!
|
|
317
|
+
const path = join2(templateDir, MANIFEST_FILE);
|
|
318
|
+
if (!existsSync2(path))
|
|
122
319
|
return null;
|
|
123
|
-
return JSON.parse(
|
|
320
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
124
321
|
}
|
|
125
322
|
function scaffold(options) {
|
|
126
323
|
const { kind, name, templateDir, version } = options;
|
|
127
324
|
const cwd = options.cwd ?? process.cwd();
|
|
128
325
|
const log = options.log ?? ((m) => console.log(m));
|
|
129
|
-
if (!
|
|
326
|
+
if (!existsSync2(templateDir)) {
|
|
130
327
|
throw new Error(`Template directory not found: ${templateDir}`);
|
|
131
328
|
}
|
|
132
329
|
const manifest = loadManifest(templateDir);
|
|
@@ -164,34 +361,39 @@ function scaffold(options) {
|
|
|
164
361
|
}
|
|
165
362
|
const names = deriveNames(name, cwd);
|
|
166
363
|
const inPlace = name === ".";
|
|
167
|
-
const projectDir = inPlace ? cwd :
|
|
168
|
-
if (!inPlace &&
|
|
364
|
+
const projectDir = inPlace ? cwd : join2(cwd, names.projectName);
|
|
365
|
+
if (!inPlace && existsSync2(projectDir)) {
|
|
169
366
|
throw new Error(`Directory already exists: ${projectDir}`);
|
|
170
367
|
}
|
|
171
|
-
|
|
368
|
+
mkdirSync2(projectDir, { recursive: true });
|
|
172
369
|
for (const srcPath of walk(templateDir)) {
|
|
173
|
-
const rel =
|
|
370
|
+
const rel = relative2(templateDir, srcPath).replaceAll("\\", "/");
|
|
174
371
|
if (rel === MANIFEST_FILE)
|
|
175
372
|
continue;
|
|
373
|
+
if (rel.split("/")[0].startsWith("_"))
|
|
374
|
+
continue;
|
|
176
375
|
if (skip.has(rel))
|
|
177
376
|
continue;
|
|
178
377
|
const destRel = renames[rel] ?? rel;
|
|
179
378
|
const segments = destRel.split("/");
|
|
180
379
|
const last = segments[segments.length - 1];
|
|
181
380
|
segments[segments.length - 1] = DOTFILE_RENAMES[last] ?? last;
|
|
182
|
-
const destPath =
|
|
183
|
-
|
|
381
|
+
const destPath = join2(projectDir, segments.join("/"));
|
|
382
|
+
mkdirSync2(join2(destPath, ".."), { recursive: true });
|
|
184
383
|
const baseName = segments[segments.length - 1];
|
|
185
384
|
if (baseName === "package.json") {
|
|
186
|
-
const transformed = transformPackageJson(
|
|
187
|
-
|
|
385
|
+
const transformed = transformPackageJson(readFileSync2(srcPath, "utf8"), names, version, manifest, enabled);
|
|
386
|
+
writeFileSync2(destPath, substituteTokens(transformed, names));
|
|
188
387
|
} else if (TEXT_REPLACE_EXT.has(extOf(srcPath)) || baseName.startsWith(".")) {
|
|
189
|
-
|
|
388
|
+
writeFileSync2(destPath, substituteTokens(readFileSync2(srcPath, "utf8"), names));
|
|
190
389
|
} else {
|
|
191
|
-
|
|
390
|
+
writeFileSync2(destPath, readFileSync2(srcPath));
|
|
192
391
|
}
|
|
193
392
|
}
|
|
194
393
|
log(`Created ${names.projectName} (${kind}) at ${projectDir}`);
|
|
394
|
+
if (options.packages) {
|
|
395
|
+
generatePackages({ projectDir, templateDir, names, version, spec: options.packages, log });
|
|
396
|
+
}
|
|
195
397
|
if (options.install) {
|
|
196
398
|
runInstall(kind, projectDir, enabled, log);
|
|
197
399
|
}
|
|
@@ -221,35 +423,36 @@ function runInstall(kind, projectDir, enabled, log) {
|
|
|
221
423
|
}
|
|
222
424
|
|
|
223
425
|
// ../scaffold-core/dist/cli.js
|
|
224
|
-
import { existsSync as
|
|
225
|
-
import { dirname, join as
|
|
426
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
427
|
+
import { dirname, join as join3 } from "node:path";
|
|
226
428
|
import { createInterface } from "node:readline/promises";
|
|
227
429
|
import { fileURLToPath } from "node:url";
|
|
228
430
|
async function runCreateCli(kind, moduleUrl) {
|
|
229
|
-
const pkgRoot =
|
|
431
|
+
const pkgRoot = join3(dirname(fileURLToPath(moduleUrl)), "..");
|
|
230
432
|
const args = process.argv.slice(2);
|
|
231
433
|
if (args.includes("--help") || args.includes("-h")) {
|
|
232
434
|
printHelp(kind);
|
|
233
435
|
return;
|
|
234
436
|
}
|
|
235
|
-
const version = JSON.parse(
|
|
236
|
-
const templateDir =
|
|
437
|
+
const version = JSON.parse(readFileSync3(join3(pkgRoot, "package.json"), "utf8")).version;
|
|
438
|
+
const templateDir = join3(pkgRoot, "templates", kind);
|
|
237
439
|
const name = args.find((a) => !a.startsWith("-")) ?? ".";
|
|
238
440
|
const install = args.includes("--install") ? true : args.includes("--no-install") ? false : Boolean(process.stdout.isTTY);
|
|
239
441
|
try {
|
|
240
|
-
const
|
|
241
|
-
const
|
|
442
|
+
const packages = kind === "e3" ? parsePackages(args) : void 0;
|
|
443
|
+
const features = await resolveFeatures(templateDir, args, packages);
|
|
444
|
+
const result = scaffold({ kind, name, templateDir, version, install, features, packages });
|
|
242
445
|
printNextSteps(kind, result.projectName, result.inPlace, install, features);
|
|
243
446
|
} catch (err) {
|
|
244
447
|
console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
245
448
|
process.exit(1);
|
|
246
449
|
}
|
|
247
450
|
}
|
|
248
|
-
async function resolveFeatures(templateDir, args) {
|
|
249
|
-
const manifestPath =
|
|
250
|
-
if (!
|
|
451
|
+
async function resolveFeatures(templateDir, args, packages) {
|
|
452
|
+
const manifestPath = join3(templateDir, "template.json");
|
|
453
|
+
if (!existsSync3(manifestPath))
|
|
251
454
|
return {};
|
|
252
|
-
const manifest = JSON.parse(
|
|
455
|
+
const manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
|
|
253
456
|
const features = {};
|
|
254
457
|
for (const [key, spec] of Object.entries(manifest.features)) {
|
|
255
458
|
if (spec.allOf)
|
|
@@ -259,7 +462,9 @@ async function resolveFeatures(templateDir, args) {
|
|
|
259
462
|
const runnerKeys = Object.keys(manifest.features).filter((k) => k.startsWith("runner:"));
|
|
260
463
|
const runnerName = (key) => key.slice("runner:".length);
|
|
261
464
|
const runnersFlag = args.find((a) => a.startsWith("--runners="));
|
|
262
|
-
const selectionFlags = ["--tests", "--no-tests", "--ui", "--no-ui", "--platform", "--no-platform", "--eslint", "--no-eslint"].some((f) => args.includes(f)) || Boolean(runnersFlag)
|
|
465
|
+
const selectionFlags = ["--tests", "--no-tests", "--ui", "--no-ui", "--platform", "--no-platform", "--eslint", "--no-eslint"].some((f) => args.includes(f)) || Boolean(runnersFlag) || // `--python-packages=…` / `--node-packages=…` / `--c-packages=…` are an
|
|
466
|
+
// explicit non-interactive selection: skip the prompt.
|
|
467
|
+
Boolean(packages);
|
|
263
468
|
if (args.includes("--tests"))
|
|
264
469
|
features["tests"] = true;
|
|
265
470
|
if (args.includes("--no-tests"))
|
|
@@ -285,6 +490,15 @@ async function resolveFeatures(templateDir, args) {
|
|
|
285
490
|
for (const key of runnerKeys)
|
|
286
491
|
features[key] = chosen.has(runnerName(key));
|
|
287
492
|
}
|
|
493
|
+
if (packages) {
|
|
494
|
+
if (packages.python?.length && "runner:east-py" in manifest.features)
|
|
495
|
+
features["runner:east-py"] = true;
|
|
496
|
+
if (packages.node?.length && "runner:east-node" in manifest.features)
|
|
497
|
+
features["runner:east-node"] = true;
|
|
498
|
+
if (packages.c?.length && "runner:east-c" in manifest.features)
|
|
499
|
+
features["runner:east-c"] = true;
|
|
500
|
+
features["platform"] = false;
|
|
501
|
+
}
|
|
288
502
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !selectionFlags;
|
|
289
503
|
if (!interactive)
|
|
290
504
|
return features;
|
|
@@ -317,6 +531,28 @@ async function resolveFeatures(templateDir, args) {
|
|
|
317
531
|
}
|
|
318
532
|
return features;
|
|
319
533
|
}
|
|
534
|
+
function parsePackages(args) {
|
|
535
|
+
const grab = (flag) => {
|
|
536
|
+
const arg = args.find((a) => a === flag || a.startsWith(`${flag}=`));
|
|
537
|
+
if (arg === void 0)
|
|
538
|
+
return void 0;
|
|
539
|
+
const value = arg.startsWith(`${flag}=`) ? arg.slice(flag.length + 1) : "";
|
|
540
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
541
|
+
};
|
|
542
|
+
const python = grab("--python-packages");
|
|
543
|
+
const node = grab("--node-packages");
|
|
544
|
+
const c = grab("--c-packages");
|
|
545
|
+
if (!python && !node && !c)
|
|
546
|
+
return void 0;
|
|
547
|
+
const spec = {};
|
|
548
|
+
if (python)
|
|
549
|
+
spec.python = python;
|
|
550
|
+
if (node)
|
|
551
|
+
spec.node = node;
|
|
552
|
+
if (c)
|
|
553
|
+
spec.c = c;
|
|
554
|
+
return spec;
|
|
555
|
+
}
|
|
320
556
|
async function askYesNo(rl, question, def) {
|
|
321
557
|
const answer = (await rl.question(`${question} [${def ? "Y/n" : "y/N"}] `)).trim().toLowerCase();
|
|
322
558
|
if (answer === "")
|
|
@@ -336,6 +572,12 @@ function printHelp(kind) {
|
|
|
336
572
|
console.log(" --ui | --no-ui include east-ui + e3-ui UI components (default: no)");
|
|
337
573
|
console.log(" --platform | --no-platform include a project-owned platform module (TS-East; +Python when east-py is on) (default: no)");
|
|
338
574
|
console.log(" --runners=east-node,east-c,east-py East runtimes to include (default: all)");
|
|
575
|
+
console.log("");
|
|
576
|
+
console.log(" Split platform code into separate workspace packages, each with its own");
|
|
577
|
+
console.log(" execution environment (editing one re-runs only its tasks):");
|
|
578
|
+
console.log(" --python-packages=pricing,common uv workspace members (packages/python/*)");
|
|
579
|
+
console.log(" --node-packages=api npm workspace members (packages/node/*)");
|
|
580
|
+
console.log(" --c-packages=solver native binaries wired via a tools env (packages/native/*)");
|
|
339
581
|
}
|
|
340
582
|
console.log("");
|
|
341
583
|
console.log("Run interactively (a TTY with no feature flags) to be prompted for these.");
|