@babylonjsmarket/cli 1.0.2

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.
@@ -0,0 +1,655 @@
1
+ // src/lib/inject/engine.ts
2
+ import fs2 from "fs";
3
+ import path2 from "path";
4
+ import mri from "mri";
5
+ import colors2 from "picocolors";
6
+
7
+ // src/lib/inject/inject-options.ts
8
+ import colors from "picocolors";
9
+ var { cyan, bold } = colors;
10
+ var INJECT_HELP = `Usage: bjs inject [OPTION]... NAME...
11
+
12
+ Submit a component or system from your project's ${cyan("src/components/<Name>/")} to
13
+ BabylonJS Market for curator review (reverse of ${cyan("arcade eject")}).
14
+
15
+ With no ${bold("--target")}, the tool auto-detects per seed:
16
+ * any file extending ${cyan("PanelDebuggerSystem")} / ${cyan("StateStepperSystem")} or
17
+ importing from ${cyan("@babylonjsmarket/viz-pro")} \u2192 ${cyan("viz-pro")},
18
+ * otherwise \u2192 ${cyan("arcade-pro")}.
19
+
20
+ Multi-seed inputs must all detect to the same target.
21
+
22
+ Options:
23
+ --target <arcade-pro|viz-pro> skip auto-detection
24
+ --source <path> override <cwd>/src/components/<Name>/
25
+ --layer <core|solid|ecs> viz-pro only; skip per-file layer detection
26
+ --force allow overwriting an existing target
27
+ --dry-run print the plan, write nothing, do not submit
28
+ -h, --help print this help message
29
+
30
+ Examples:
31
+ bjs inject Foo # auto-detect target
32
+ bjs inject Foo Bar --dry-run # multi-name, preview
33
+ bjs inject MyPanelDebugger --target viz-pro
34
+ bjs inject Foo --source ./packages/demo/src/components/Foo
35
+ `;
36
+
37
+ // src/lib/inject/detect-target.ts
38
+ function detectTarget(seedFiles) {
39
+ for (const { source } of seedFiles) {
40
+ if (/from\s+['"]@babylonjsmarket\/viz-pro['"]/.test(source)) return "viz-pro";
41
+ if (/extends\s+(PanelDebuggerSystem|StateStepperSystem)\b/.test(source)) return "viz-pro";
42
+ }
43
+ return "arcade-pro";
44
+ }
45
+ function detectLayer(filePath, source, subjectLayer) {
46
+ const base = filePath.split(/[\\/]/).pop() ?? filePath;
47
+ if (/\.test\.tsx?$/.test(base)) {
48
+ return subjectLayer ?? "core";
49
+ }
50
+ const hasSolidImport = /from\s+['"]solid-js['"]/.test(source);
51
+ if (/\.tsx$/.test(base) && hasSolidImport) return "solid";
52
+ if (/extends\s+(PanelDebuggerSystem|StateStepperSystem|System)\b/.test(source)) return "ecs";
53
+ if (/System\.ts$/.test(base)) return "ecs";
54
+ if (/extends\s+Component\b/.test(source) && !hasSolidImport) return "ecs";
55
+ if (/\bdefinePanel\s*\(/.test(source) && !hasSolidImport) return "core";
56
+ return "core";
57
+ }
58
+
59
+ // src/lib/inject/monorepo-root.ts
60
+ import fs from "fs";
61
+ import path from "path";
62
+ import { fileURLToPath } from "url";
63
+ function findMonorepoRoot() {
64
+ const here = path.dirname(fileURLToPath(import.meta.url));
65
+ let dir = here;
66
+ while (true) {
67
+ const pkgPath = path.join(dir, "package.json");
68
+ if (fs.existsSync(pkgPath)) {
69
+ try {
70
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
71
+ if (pkg.name === "babylonjs-market-monorepo") {
72
+ return {
73
+ root: dir,
74
+ arcadeProRoot: path.join(dir, "packages", "arcade-pro"),
75
+ vizProRoot: path.join(dir, "packages", "viz-pro")
76
+ };
77
+ }
78
+ } catch {
79
+ }
80
+ }
81
+ const parent = path.dirname(dir);
82
+ if (parent === dir) {
83
+ throw new Error(
84
+ `Could not locate umbrella repo (no ancestor package.json with name "babylonjs-market-monorepo" above ${here}).`
85
+ );
86
+ }
87
+ dir = parent;
88
+ }
89
+ }
90
+
91
+ // src/lib/inject/engine.ts
92
+ var { cyan: cyan2, green, yellow, dim, bold: bold2 } = colors2;
93
+ function componentFiles(dir) {
94
+ const out = [];
95
+ const walk = (d) => {
96
+ for (const e of fs2.readdirSync(d, { withFileTypes: true })) {
97
+ const p = path2.join(d, e.name);
98
+ if (e.isDirectory()) walk(p);
99
+ else if (/\.(ts|tsx)$/.test(e.name)) out.push(p);
100
+ }
101
+ };
102
+ if (fs2.existsSync(dir)) walk(dir);
103
+ return out;
104
+ }
105
+ function allComponentFiles(dir) {
106
+ const out = [];
107
+ const walk = (d) => {
108
+ for (const e of fs2.readdirSync(d, { withFileTypes: true })) {
109
+ const p = path2.join(d, e.name);
110
+ if (e.isDirectory()) walk(p);
111
+ else out.push(p);
112
+ }
113
+ };
114
+ if (fs2.existsSync(dir)) walk(dir);
115
+ return out;
116
+ }
117
+ function projectComponentNames(projectDir) {
118
+ const dir = path2.join(projectDir, "src", "components");
119
+ if (!fs2.existsSync(dir)) return [];
120
+ return fs2.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "_shared").map((e) => e.name);
121
+ }
122
+ function directDeps(name, componentsDir, allNames) {
123
+ const deps = /* @__PURE__ */ new Set();
124
+ const metaPath = path2.join(componentsDir, name, "meta.json");
125
+ if (fs2.existsSync(metaPath)) {
126
+ try {
127
+ const meta = JSON.parse(fs2.readFileSync(metaPath, "utf8"));
128
+ for (const d of meta.dependencies ?? []) if (allNames.has(d)) deps.add(d);
129
+ } catch {
130
+ }
131
+ }
132
+ const relRe = /['"]\.\.\/([A-Z][A-Za-z0-9_]*)\//g;
133
+ const aliasRe = /['"]~\/components\/([A-Z][A-Za-z0-9_]*)\//g;
134
+ for (const file of componentFiles(path2.join(componentsDir, name))) {
135
+ const src = fs2.readFileSync(file, "utf8");
136
+ let m;
137
+ while (m = relRe.exec(src)) if (allNames.has(m[1])) deps.add(m[1]);
138
+ while (m = aliasRe.exec(src)) if (allNames.has(m[1])) deps.add(m[1]);
139
+ }
140
+ return deps;
141
+ }
142
+ function resolveClosure(seed, componentsDir, allNames) {
143
+ const all = new Set(allNames);
144
+ const out = new Set(seed);
145
+ const queue = [...seed];
146
+ while (queue.length) {
147
+ const next = queue.pop();
148
+ for (const dep of directDeps(next, componentsDir, all)) {
149
+ if (!out.has(dep)) {
150
+ out.add(dep);
151
+ queue.push(dep);
152
+ }
153
+ }
154
+ }
155
+ return out;
156
+ }
157
+ function rewriteArcadeImports(src, injected) {
158
+ return src.replace(
159
+ /(['"])@babylonjsmarket\/(?:arcade|arcade-pro)\/([A-Z][A-Za-z0-9_]*)\1/g,
160
+ (whole, quote, sibling) => {
161
+ if (injected.has(sibling)) {
162
+ return `${quote}../${sibling}/${sibling}${quote}`;
163
+ }
164
+ return whole;
165
+ }
166
+ );
167
+ }
168
+ function rewriteVizImports(src, injected, layerOf, fromLayer) {
169
+ return src.replace(
170
+ /(['"])@babylonjsmarket\/(?:viz-pro|arcade|arcade-pro)\/([A-Z][A-Za-z0-9_]*)\1/g,
171
+ (whole, quote, sibling) => {
172
+ if (!injected.has(sibling)) return whole;
173
+ const dest = layerOf(sibling);
174
+ if (!dest) return whole;
175
+ const rel = dest === fromLayer ? `./${sibling}` : `../${dest}/${sibling}`;
176
+ return `${quote}${rel}${quote}`;
177
+ }
178
+ );
179
+ }
180
+ function planArcadeProRegistry(existingRegistry, names, registryRelPath = "src/registry.ts") {
181
+ const warnings = [];
182
+ if (existingRegistry === void 0) {
183
+ warnings.push(
184
+ `! packages/arcade-pro/${registryRelPath} not found \u2014 add resolvers manually for: ${names.join(", ")}`
185
+ );
186
+ return { lines: [], added: [], warnings, markerFound: false };
187
+ }
188
+ const marker = /ARCADE_PRO_COMPONENT_REGISTRY:\s*Record<string,\s*LazyComponentResolver>\s*=\s*\{/;
189
+ if (!marker.test(existingRegistry)) {
190
+ warnings.push(
191
+ `! Could not find 'ARCADE_PRO_COMPONENT_REGISTRY' marker \u2014 add resolvers manually for: ${names.join(", ")}`
192
+ );
193
+ return { lines: [], added: [], warnings, markerFound: false };
194
+ }
195
+ const added = [];
196
+ const lines = [];
197
+ for (const name of names) {
198
+ if (existingRegistry.includes(`./Components/${name}/${name}`)) continue;
199
+ lines.push(` ${name}: () => import('./Components/${name}/${name}'),`);
200
+ added.push(name);
201
+ }
202
+ return { lines, added, warnings, markerFound: true };
203
+ }
204
+ function patchArcadeProRegistry(arcadeProRoot, names, dryRun) {
205
+ const registryPath = path2.join(arcadeProRoot, "src", "registry.ts");
206
+ const existing = fs2.existsSync(registryPath) ? fs2.readFileSync(registryPath, "utf8") : void 0;
207
+ const plan = planArcadeProRegistry(existing, names);
208
+ if (plan.warnings.length) return plan.warnings;
209
+ if (plan.added.length && !dryRun) {
210
+ const marker = /ARCADE_PRO_COMPONENT_REGISTRY:\s*Record<string,\s*LazyComponentResolver>\s*=\s*\{/;
211
+ const next = existing.replace(marker, (m) => `${m}
212
+ ${plan.lines.join("\n")}`);
213
+ fs2.writeFileSync(registryPath, next);
214
+ }
215
+ return plan.added.map((n) => ` patched registry: ${n}`);
216
+ }
217
+ function planVizProBarrel(existingBarrel, layer, fileName) {
218
+ if (existingBarrel === void 0) {
219
+ return {
220
+ line: null,
221
+ warnings: [
222
+ `! packages/viz-pro/src/${layer}/index.ts not found \u2014 add the barrel re-export manually for ${fileName}`
223
+ ]
224
+ };
225
+ }
226
+ const needle = `from './${fileName}'`;
227
+ if (existingBarrel.includes(needle)) return { line: null, warnings: [] };
228
+ return { line: `export * from './${fileName}';`, warnings: [] };
229
+ }
230
+ function patchVizProBarrel(vizProRoot, layer, fileName, dryRun) {
231
+ const barrelPath = path2.join(vizProRoot, "src", layer, "index.ts");
232
+ const existing = fs2.existsSync(barrelPath) ? fs2.readFileSync(barrelPath, "utf8") : void 0;
233
+ const plan = planVizProBarrel(existing, layer, fileName);
234
+ if (plan.warnings.length) return plan.warnings;
235
+ if (plan.line === null) return [];
236
+ if (!dryRun) {
237
+ const next = existing.endsWith("\n") ? existing + plan.line + "\n" : existing + "\n" + plan.line + "\n";
238
+ fs2.writeFileSync(barrelPath, next);
239
+ }
240
+ return [` patched ${layer}/index.ts: ${fileName}`];
241
+ }
242
+ function stripProjectRegistry(projectDir, names, dryRun) {
243
+ const registryPath = path2.join(projectDir, "src", "registry.ts");
244
+ if (!fs2.existsSync(registryPath)) return [];
245
+ let text = fs2.readFileSync(registryPath, "utf8");
246
+ const removed = [];
247
+ for (const name of names) {
248
+ const re = new RegExp(
249
+ `^\\s*${name}\\s*:\\s*\\(\\)\\s*=>\\s*import\\(\\s*['"]\\.\\/components\\/${name}\\/${name}['"]\\s*\\)\\s*,?\\s*\\r?\\n`,
250
+ "m"
251
+ );
252
+ if (re.test(text)) {
253
+ text = text.replace(re, "");
254
+ removed.push(name);
255
+ }
256
+ }
257
+ if (removed.length && !dryRun) fs2.writeFileSync(registryPath, text);
258
+ return removed;
259
+ }
260
+ async function inject(args, opts = {}) {
261
+ if (args.includes("-h") || args.includes("--help")) {
262
+ console.log(INJECT_HELP);
263
+ return;
264
+ }
265
+ const argv = mri(args, {
266
+ boolean: ["dry-run", "move", "confirm", "force"],
267
+ string: ["target", "source", "layer"]
268
+ });
269
+ const mode = opts.mode ?? "apply";
270
+ const dryRun = Boolean(argv["dry-run"]);
271
+ const move = Boolean(argv.move);
272
+ const confirm = Boolean(argv.confirm);
273
+ const force = Boolean(argv.force);
274
+ const explicitTarget = argv.target;
275
+ const sourceOverride = argv.source;
276
+ const layerOverride = argv.layer;
277
+ const names = argv._.map(String);
278
+ const cwd = opts.cwd ?? process.cwd();
279
+ const projectDir = opts.projectDir ?? cwd;
280
+ if (names.length === 0) {
281
+ console.error(yellow("No component names given. Pass at least one, e.g. `bjs inject Foo`."));
282
+ process.exit(1);
283
+ return;
284
+ }
285
+ if (mode === "collect" && (move || confirm)) {
286
+ console.error(yellow("--move and --confirm are not available in member submission mode."));
287
+ process.exit(1);
288
+ return;
289
+ }
290
+ if (move && !confirm) {
291
+ console.error(yellow("--move requires --confirm (it deletes project files). Pass --move --confirm."));
292
+ process.exit(1);
293
+ return;
294
+ }
295
+ if (move && dryRun) {
296
+ console.error(yellow("--move and --dry-run are mutually exclusive. Drop one."));
297
+ process.exit(1);
298
+ return;
299
+ }
300
+ if (explicitTarget && explicitTarget !== "arcade-pro" && explicitTarget !== "viz-pro") {
301
+ console.error(yellow(`Unknown --target ${explicitTarget}. Use arcade-pro or viz-pro.`));
302
+ process.exit(1);
303
+ return;
304
+ }
305
+ if (layerOverride && layerOverride !== "core" && layerOverride !== "solid" && layerOverride !== "ecs") {
306
+ console.error(yellow(`Unknown --layer ${layerOverride}. Use core, solid, or ecs.`));
307
+ process.exit(1);
308
+ return;
309
+ }
310
+ if (sourceOverride && names.length > 1) {
311
+ console.error(yellow("--source applies to a single seed; pass one NAME with --source."));
312
+ process.exit(1);
313
+ return;
314
+ }
315
+ let packageRoot = opts.packageRoot;
316
+ let vizProRoot = opts.vizProRoot;
317
+ if (mode === "apply" && (!packageRoot || !vizProRoot)) {
318
+ try {
319
+ const monorepo = findMonorepoRoot();
320
+ packageRoot ??= monorepo.arcadeProRoot;
321
+ vizProRoot ??= monorepo.vizProRoot;
322
+ } catch (e) {
323
+ console.error(yellow(e.message));
324
+ process.exit(1);
325
+ return;
326
+ }
327
+ }
328
+ const componentsDir = path2.join(projectDir, "src", "components");
329
+ const allProjectNames = projectComponentNames(projectDir);
330
+ const seeds = [];
331
+ for (const name of names) {
332
+ const sourceDir = sourceOverride ? path2.resolve(cwd, sourceOverride) : path2.join(componentsDir, name);
333
+ if (!fs2.existsSync(sourceDir)) {
334
+ console.error(
335
+ yellow(
336
+ `Could not find source for "${name}". Looked at ${sourceDir}. Use --source <path> to point at the seed explicitly.`
337
+ )
338
+ );
339
+ process.exit(1);
340
+ return;
341
+ }
342
+ seeds.push({ name, sourceDir });
343
+ }
344
+ const seedNames = new Set(seeds.map((s) => s.name));
345
+ const closure = sourceOverride ? seedNames : resolveClosure(seedNames, componentsDir, allProjectNames);
346
+ const closureFiles = [];
347
+ for (const name of closure) {
348
+ const dir = sourceOverride && seedNames.has(name) ? seeds.find((s) => s.name === name).sourceDir : path2.join(componentsDir, name);
349
+ for (const f of componentFiles(dir)) {
350
+ closureFiles.push({ path: f, source: fs2.readFileSync(f, "utf8") });
351
+ }
352
+ }
353
+ const target = explicitTarget ?? detectTarget(closureFiles);
354
+ if (!explicitTarget && seeds.length > 1) {
355
+ for (const seed of seeds) {
356
+ const dir = seed.sourceDir;
357
+ const files = componentFiles(dir).map((f) => ({
358
+ path: f,
359
+ source: fs2.readFileSync(f, "utf8")
360
+ }));
361
+ const perSeed = detectTarget(files);
362
+ if (perSeed !== target) {
363
+ console.error(
364
+ yellow(
365
+ `Auto-detect disagrees across seeds: "${seed.name}" \u2192 ${perSeed}, others \u2192 ${target}. Pass --target explicitly or split into separate runs.`
366
+ )
367
+ );
368
+ process.exit(1);
369
+ return;
370
+ }
371
+ }
372
+ }
373
+ let chosenRoot;
374
+ if (mode === "apply") {
375
+ try {
376
+ if (target === "arcade-pro") {
377
+ if (!fs2.existsSync(packageRoot)) {
378
+ throw new Error(
379
+ `packages/arcade-pro is not checked out at ${packageRoot}. Run \`git submodule update --init packages/arcade-pro\` and retry.`
380
+ );
381
+ }
382
+ chosenRoot = packageRoot;
383
+ } else {
384
+ if (!fs2.existsSync(vizProRoot)) {
385
+ throw new Error(
386
+ `packages/viz-pro is not checked out at ${vizProRoot}. Run \`git submodule update --init packages/viz-pro\` and retry.`
387
+ );
388
+ }
389
+ chosenRoot = vizProRoot;
390
+ }
391
+ } catch (e) {
392
+ console.error(yellow(e.message));
393
+ process.exit(1);
394
+ return;
395
+ }
396
+ }
397
+ console.log(
398
+ bold2(dryRun ? "\nInject plan (dry run \u2014 nothing written):" : "\nInjecting:")
399
+ );
400
+ console.log(` target: ${cyan2(target)}`);
401
+ console.log(` components: ${cyan2([...closure].sort().join(", "))}`);
402
+ if (mode === "collect") {
403
+ const result = target === "arcade-pro" ? collectArcadePro({ seeds, closure, componentsDir, sourceOverride }) : collectVizPro({ seeds, closure, componentsDir, sourceOverride, layerOverride });
404
+ for (const f of result.files) console.log(green(` + packages/${target}/${f.path}`));
405
+ for (const w of result.warnings) console.log(yellow(w));
406
+ console.log(
407
+ dryRun ? dim("\nRe-run without --dry-run to submit.\n") : dim("\nCollect-mode plan ready (no submission performed by the library).\n")
408
+ );
409
+ return { ...result, target };
410
+ }
411
+ if (target === "arcade-pro") {
412
+ await injectArcadePro({
413
+ seeds,
414
+ closure,
415
+ componentsDir,
416
+ sourceOverride,
417
+ arcadeProRoot: chosenRoot,
418
+ dryRun,
419
+ force
420
+ });
421
+ } else {
422
+ await injectVizPro({
423
+ seeds,
424
+ closure,
425
+ componentsDir,
426
+ sourceOverride,
427
+ vizProRoot: chosenRoot,
428
+ dryRun,
429
+ force,
430
+ layerOverride
431
+ });
432
+ }
433
+ if (move) {
434
+ if (sourceOverride) {
435
+ console.log(
436
+ yellow(
437
+ " ! --move with --source: only the source dir is removed; cannot reliably patch a foreign project's registry."
438
+ )
439
+ );
440
+ }
441
+ for (const seed of seeds) {
442
+ if (fs2.existsSync(seed.sourceDir)) {
443
+ if (!dryRun) fs2.rmSync(seed.sourceDir, { recursive: true, force: true });
444
+ console.log(dim(` - ${path2.relative(projectDir, seed.sourceDir)}/ (project copy removed)`));
445
+ }
446
+ }
447
+ const removed = stripProjectRegistry(
448
+ projectDir,
449
+ seeds.map((s) => s.name),
450
+ dryRun
451
+ );
452
+ for (const name of removed) {
453
+ console.log(dim(` - project registry entry: ${name}`));
454
+ }
455
+ }
456
+ console.log(
457
+ dryRun ? dim("\nRe-run without --dry-run to apply.\n") : green(`
458
+ Done. Components landed in packages/${target}/.
459
+ `)
460
+ );
461
+ }
462
+ async function injectArcadePro(args) {
463
+ const { seeds, closure, componentsDir, sourceOverride, arcadeProRoot, dryRun, force } = args;
464
+ const destComponents = path2.join(arcadeProRoot, "src", "Components");
465
+ if (!dryRun) fs2.mkdirSync(destComponents, { recursive: true });
466
+ const sourceOf = (name) => {
467
+ const seed = seeds.find((s) => s.name === name);
468
+ return seed && sourceOverride ? seed.sourceDir : path2.join(componentsDir, name);
469
+ };
470
+ for (const name of [...closure].sort()) {
471
+ const src = sourceOf(name);
472
+ const dest = path2.join(destComponents, name);
473
+ if (fs2.existsSync(dest) && !force) {
474
+ console.log(
475
+ yellow(` ! packages/arcade-pro/src/Components/${name}/ already exists \u2014 pass --force to overwrite.`)
476
+ );
477
+ continue;
478
+ }
479
+ if (!dryRun) {
480
+ if (fs2.existsSync(dest)) fs2.rmSync(dest, { recursive: true, force: true });
481
+ fs2.cpSync(src, dest, { recursive: true });
482
+ }
483
+ console.log(green(` + packages/arcade-pro/src/Components/${name}/`));
484
+ }
485
+ if (!dryRun) {
486
+ for (const name of closure) {
487
+ const dest = path2.join(destComponents, name);
488
+ if (!fs2.existsSync(dest)) continue;
489
+ rewriteTree(dest, (text) => rewriteArcadeImports(text, closure));
490
+ }
491
+ }
492
+ for (const line of patchArcadeProRegistry(
493
+ arcadeProRoot,
494
+ [...closure].sort(),
495
+ dryRun
496
+ )) {
497
+ console.log(line.startsWith("!") ? yellow(line) : dim(line));
498
+ }
499
+ }
500
+ function collectArcadePro(args) {
501
+ const { seeds, closure, componentsDir, sourceOverride } = args;
502
+ const sourceOf = (name) => {
503
+ const seed = seeds.find((s) => s.name === name);
504
+ return seed && sourceOverride ? seed.sourceDir : path2.join(componentsDir, name);
505
+ };
506
+ const files = [];
507
+ const warnings = [];
508
+ for (const name of [...closure].sort()) {
509
+ const src = sourceOf(name);
510
+ if (!fs2.existsSync(src)) {
511
+ warnings.push(` ! source for ${name} missing at ${src}`);
512
+ continue;
513
+ }
514
+ for (const abs of allComponentFiles(src)) {
515
+ const rel = path2.relative(src, abs);
516
+ const baseRel = path2.posix.join("src", "Components", name, rel.split(path2.sep).join("/"));
517
+ const isText = /\.(ts|tsx)$/i.test(abs);
518
+ const raw = fs2.readFileSync(abs, "utf8");
519
+ const contents = isText ? rewriteArcadeImports(raw, closure) : raw;
520
+ files.push({ path: baseRel, contents });
521
+ }
522
+ }
523
+ const patches = [];
524
+ for (const name of [...closure].sort()) {
525
+ patches.push({
526
+ file: "src/registry.ts",
527
+ kind: "registry-append",
528
+ line: ` ${name}: () => import('./Components/${name}/${name}'),`
529
+ });
530
+ }
531
+ return { files, patches, warnings };
532
+ }
533
+ function classifyVizFiles(args) {
534
+ const { seeds, closure, componentsDir, sourceOverride, layerOverride } = args;
535
+ const sourceOf = (name) => {
536
+ const seed = seeds.find((s) => s.name === name);
537
+ return seed && sourceOverride ? seed.sourceDir : path2.join(componentsDir, name);
538
+ };
539
+ const files = [];
540
+ for (const name of closure) {
541
+ const dir = sourceOf(name);
542
+ for (const abs of componentFiles(dir)) {
543
+ const base = path2.basename(abs);
544
+ if (/\.test\.tsx?$/.test(base)) continue;
545
+ const source = fs2.readFileSync(abs, "utf8");
546
+ const layer = layerOverride ?? detectLayer(abs, source);
547
+ files.push({ src: abs, base, owner: name, source, layer, destBase: base });
548
+ }
549
+ }
550
+ for (const name of closure) {
551
+ const dir = sourceOf(name);
552
+ for (const abs of componentFiles(dir)) {
553
+ const base = path2.basename(abs);
554
+ if (!/\.test\.tsx?$/.test(base)) continue;
555
+ const source = fs2.readFileSync(abs, "utf8");
556
+ const subjectBase = base.replace(/\.test(\.tsx?)$/, "$1");
557
+ const subjectAlt = base.replace(/\.test\.tsx?$/, ".tsx");
558
+ const subject = files.find((f) => f.owner === name && (f.base === subjectBase || f.base === subjectAlt));
559
+ const layer = layerOverride ?? detectLayer(abs, source, subject?.layer);
560
+ files.push({ src: abs, base, owner: name, source, layer, destBase: base });
561
+ }
562
+ }
563
+ const primaryLayer = /* @__PURE__ */ new Map();
564
+ for (const f of files) {
565
+ if (/\.test\.tsx?$/.test(f.base)) continue;
566
+ if (!primaryLayer.has(f.owner)) primaryLayer.set(f.owner, f.layer);
567
+ }
568
+ return { files, primaryLayer };
569
+ }
570
+ async function injectVizPro(args) {
571
+ const { closure, vizProRoot, dryRun, force } = args;
572
+ const { files, primaryLayer } = classifyVizFiles(args);
573
+ const layerOf = (name) => primaryLayer.get(name);
574
+ for (const f of files) {
575
+ const dest = path2.join(vizProRoot, "src", f.layer, f.destBase);
576
+ if (fs2.existsSync(dest) && !force) {
577
+ console.log(
578
+ yellow(
579
+ ` ! packages/viz-pro/src/${f.layer}/${f.destBase} already exists \u2014 pass --force to overwrite.`
580
+ )
581
+ );
582
+ }
583
+ }
584
+ for (const f of files) {
585
+ const destDir = path2.join(vizProRoot, "src", f.layer);
586
+ const dest = path2.join(destDir, f.destBase);
587
+ if (fs2.existsSync(dest) && !force) continue;
588
+ if (!dryRun) {
589
+ fs2.mkdirSync(destDir, { recursive: true });
590
+ const rewritten = rewriteVizImports(f.source, closure, layerOf, f.layer);
591
+ fs2.writeFileSync(dest, rewritten);
592
+ }
593
+ console.log(green(` + packages/viz-pro/src/${f.layer}/${f.destBase}`));
594
+ }
595
+ const barrelNames = /* @__PURE__ */ new Set();
596
+ for (const f of files) {
597
+ if (/\.test\.tsx?$/.test(f.base)) continue;
598
+ const stem = f.destBase.replace(/\.tsx?$/, "");
599
+ const key = `${f.layer}:${stem}`;
600
+ if (barrelNames.has(key)) continue;
601
+ barrelNames.add(key);
602
+ for (const line of patchVizProBarrel(vizProRoot, f.layer, stem, dryRun)) {
603
+ console.log(line.startsWith("!") ? yellow(line) : dim(line));
604
+ }
605
+ }
606
+ console.log(
607
+ dim(
608
+ " hint: if you want named re-exports from the package root, edit packages/viz-pro/src/index.ts by hand."
609
+ )
610
+ );
611
+ }
612
+ function collectVizPro(args) {
613
+ const { closure } = args;
614
+ const { files: classified, primaryLayer } = classifyVizFiles(args);
615
+ const layerOf = (name) => primaryLayer.get(name);
616
+ const files = [];
617
+ const warnings = [];
618
+ for (const f of classified) {
619
+ const rel = path2.posix.join("src", f.layer, f.destBase);
620
+ const contents = rewriteVizImports(f.source, closure, layerOf, f.layer);
621
+ files.push({ path: rel, contents });
622
+ }
623
+ const patches = [];
624
+ const barrelKeys = /* @__PURE__ */ new Set();
625
+ for (const f of classified) {
626
+ if (/\.test\.tsx?$/.test(f.base)) continue;
627
+ const stem = f.destBase.replace(/\.tsx?$/, "");
628
+ const key = `${f.layer}:${stem}`;
629
+ if (barrelKeys.has(key)) continue;
630
+ barrelKeys.add(key);
631
+ patches.push({
632
+ file: `src/${f.layer}/index.ts`,
633
+ kind: "barrel-append",
634
+ line: `export * from './${stem}';`
635
+ });
636
+ }
637
+ return { files, patches, warnings };
638
+ }
639
+ function rewriteTree(dir, rewrite) {
640
+ for (const e of fs2.readdirSync(dir, { withFileTypes: true })) {
641
+ const p = path2.join(dir, e.name);
642
+ if (e.isDirectory()) rewriteTree(p, rewrite);
643
+ else if (/\.(ts|tsx)$/.test(e.name)) {
644
+ const before = fs2.readFileSync(p, "utf8");
645
+ const after = rewrite(before);
646
+ if (after !== before) fs2.writeFileSync(p, after);
647
+ }
648
+ }
649
+ }
650
+ export {
651
+ detectLayer,
652
+ detectTarget,
653
+ inject
654
+ };
655
+ //# sourceMappingURL=engine.js.map