create-flow-os 0.0.47-dev.1772043993 → 0.0.47-dev.1772047467
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/src/init/index.ts +3 -4
- package/src/init/lib.ts +22 -2
- package/src/init/merge.ts +21 -2
- package/src/init/scaffold.ts +123 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-flow-os",
|
|
3
|
-
"version": "0.0.47-dev.
|
|
3
|
+
"version": "0.0.47-dev.1772047467",
|
|
4
4
|
"license": "PolyForm-Shield-1.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"create": "bun run src/create/index.ts",
|
|
14
14
|
"dev": "flow-os dev",
|
|
15
15
|
"build": "flow-os build",
|
|
16
|
-
"start": "bun
|
|
16
|
+
"start": "bun ./node_modules/@flow-os/server/src/bin.ts"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^25.3.0",
|
package/src/init/index.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
// Clear subito per nascondere resolving/installing di Bun
|
|
3
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
4
2
|
|
|
5
3
|
import * as readline from "readline";
|
|
6
4
|
import { join, dirname } from "path";
|
|
7
5
|
import { fileURLToPath } from "url";
|
|
8
6
|
import { libsWithConfig, toShortName, toPkgName } from "./lib";
|
|
9
|
-
import { initLib, fetchFlowPackageVersions, shouldUseWorkspace } from "./scaffold";
|
|
7
|
+
import { initLib, fetchFlowPackageVersions, shouldUseWorkspace, findFlowOsRepoRoot } from "./scaffold";
|
|
10
8
|
import { bannerBox, withLoading, colors } from "./ui";
|
|
11
9
|
|
|
12
10
|
const { V, V_LIGHT, Y, E, R, B } = colors;
|
|
@@ -16,7 +14,8 @@ const { V, V_LIGHT, Y, E, R, B } = colors;
|
|
|
16
14
|
// ───────────────────────────────────────────────────────────────────────────────
|
|
17
15
|
const cwd = process.cwd();
|
|
18
16
|
const cliRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
19
|
-
const
|
|
17
|
+
const flowOsRepoRoot = findFlowOsRepoRoot(cwd);
|
|
18
|
+
const available = libsWithConfig(cliRoot, flowOsRepoRoot).map(toShortName);
|
|
20
19
|
|
|
21
20
|
let libs = [...new Set(process.argv.slice(3))];
|
|
22
21
|
|
package/src/init/lib.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
2
2
|
import { join, dirname } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
|
|
@@ -21,7 +21,27 @@ export function flowDeps(root: string): string[] {
|
|
|
21
21
|
return Object.keys(deps).filter((k) => k.startsWith(FLOW_PREFIX));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/** Scansiona packages/ per tutti gli @flow-os/* (con o senza config) */
|
|
25
|
+
function scanPackagesDir(packagesDir: string): string[] {
|
|
26
|
+
const found: string[] = [];
|
|
27
|
+
try {
|
|
28
|
+
for (const name of readdirSync(packagesDir, { withFileTypes: true })) {
|
|
29
|
+
if (!name.isDirectory()) continue;
|
|
30
|
+
const pkgPath = join(packagesDir, name.name, "package.json");
|
|
31
|
+
if (!existsSync(pkgPath)) continue;
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { name?: string };
|
|
34
|
+
if (pkg?.name?.startsWith(FLOW_PREFIX)) found.push(pkg.name);
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
} catch {}
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function libsWithConfig(cliRoot: string, flowOsRepoRoot?: string | null): string[] {
|
|
42
|
+
const packagesDir = flowOsRepoRoot ? join(flowOsRepoRoot, "packages") : join(cliRoot, "..");
|
|
43
|
+
const fromScan = scanPackagesDir(packagesDir);
|
|
44
|
+
if (fromScan.length > 0) return fromScan;
|
|
25
45
|
const pkg = JSON.parse(readFileSync(join(cliRoot, "package.json"), "utf-8"));
|
|
26
46
|
const deps = Object.keys(pkg.dependencies ?? {}).filter((k) => k.startsWith(FLOW_PREFIX));
|
|
27
47
|
return deps.filter((name) => existsSync(join(pkgRoot(name), "config")));
|
package/src/init/merge.ts
CHANGED
|
@@ -136,7 +136,19 @@ export function mergeCodeTemplates(a: string, b: string): string {
|
|
|
136
136
|
return replaceObjectArg(base, extB, merged);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
/**
|
|
139
|
+
/** Estrae la riga "export default fn(...)" dal template */
|
|
140
|
+
function extractExportDefault(content: string): string | null {
|
|
141
|
+
const m = content.match(/export\s+default\s+\w+\s*\([^)]*\)\s*;?/);
|
|
142
|
+
return m ? m[0].trim() : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** True se user ha già export default (anche su righe diverse, formattazione diversa) */
|
|
146
|
+
function userHasExportDefault(content: string): boolean {
|
|
147
|
+
const normalized = content.replace(/\s+/g, " ");
|
|
148
|
+
return /\bexport\s+default\b/.test(normalized);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Merge per file con pattern fn({...}) - config(), defineConfig(), ecc. Additivo: non sovrascrive. */
|
|
140
152
|
export function mergeCodeObject(userContent: string, templateContent: string, _path: string): { merged: string; conflicts: Conflict[]; ext: Extracted | null } {
|
|
141
153
|
const conflicts: Conflict[] = [];
|
|
142
154
|
const userExt = extractObjectArg(userContent);
|
|
@@ -146,7 +158,14 @@ export function mergeCodeObject(userContent: string, templateContent: string, _p
|
|
|
146
158
|
const templateObj = templateExt.obj;
|
|
147
159
|
const mergedObj = deepMergeAdditive(userObj, templateObj, "", conflicts);
|
|
148
160
|
const base = userContent || templateContent;
|
|
149
|
-
const
|
|
161
|
+
const extToReplace = userExt ?? templateExt;
|
|
162
|
+
let merged = replaceObjectArg(base, extToReplace, mergedObj);
|
|
163
|
+
const templateExport = extractExportDefault(templateContent);
|
|
164
|
+
if (templateExport && !userHasExportDefault(userContent)) {
|
|
165
|
+
const userTrimmed = merged.trimEnd();
|
|
166
|
+
const sep = userTrimmed.endsWith("\n") || !userTrimmed ? "" : "\n\n";
|
|
167
|
+
merged = userTrimmed + sep + templateExport;
|
|
168
|
+
}
|
|
150
169
|
return { merged, conflicts, ext: templateExt };
|
|
151
170
|
}
|
|
152
171
|
|
package/src/init/scaffold.ts
CHANGED
|
@@ -192,7 +192,7 @@ async function fetchConfigFromNpm(
|
|
|
192
192
|
/** Sostituisce workspace:* e 0.0.1 con versione concreta (workspace va bene solo dentro flow-os) */
|
|
193
193
|
function resolveFlowDeps(
|
|
194
194
|
deps: Record<string, string> | undefined,
|
|
195
|
-
|
|
195
|
+
pkgRootDir: string,
|
|
196
196
|
versionsFromNpm: Map<string, string>,
|
|
197
197
|
useWorkspace: boolean
|
|
198
198
|
): Record<string, string> {
|
|
@@ -204,7 +204,7 @@ function resolveFlowDeps(
|
|
|
204
204
|
}
|
|
205
205
|
return resolved;
|
|
206
206
|
}
|
|
207
|
-
const ownerPkgPath = join(
|
|
207
|
+
const ownerPkgPath = join(pkgRootDir, "package.json");
|
|
208
208
|
let ownerVersion: string | undefined;
|
|
209
209
|
if (existsSync(ownerPkgPath)) {
|
|
210
210
|
try {
|
|
@@ -235,6 +235,24 @@ function resolveFlowDeps(
|
|
|
235
235
|
return resolved;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
const PKG_KEY_ORDER = ["name", "version", "private", "type", "scripts", "dependencies", "devDependencies"];
|
|
239
|
+
|
|
240
|
+
function sortPkgKeys(pkg: Record<string, unknown>): Record<string, unknown> {
|
|
241
|
+
const ordered: Record<string, unknown> = {};
|
|
242
|
+
for (const k of PKG_KEY_ORDER) {
|
|
243
|
+
if (k in pkg) {
|
|
244
|
+
const v = pkg[k];
|
|
245
|
+
ordered[k] = k === "scripts" && v && typeof v === "object" && !Array.isArray(v)
|
|
246
|
+
? Object.fromEntries(Object.entries(v as Record<string, string>).sort(([a], [b]) => a.localeCompare(b)))
|
|
247
|
+
: v;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (const k of Object.keys(pkg).sort()) {
|
|
251
|
+
if (!(k in ordered)) ordered[k] = pkg[k];
|
|
252
|
+
}
|
|
253
|
+
return ordered;
|
|
254
|
+
}
|
|
255
|
+
|
|
238
256
|
function mergePkg(configDir: string, cwd: string, versionsFromNpm: Map<string, string>, useWorkspace: boolean): void {
|
|
239
257
|
const configPkg = join(configDir, "package.json");
|
|
240
258
|
if (!existsSync(configPkg)) return;
|
|
@@ -245,12 +263,75 @@ function mergePkg(configDir: string, cwd: string, versionsFromNpm: Map<string, s
|
|
|
245
263
|
: { ...config, name: basename(cwd) || "flow-app" };
|
|
246
264
|
target.dependencies = { ...target.dependencies, ...config.dependencies };
|
|
247
265
|
target.devDependencies = { ...target.devDependencies, ...config.devDependencies };
|
|
248
|
-
|
|
266
|
+
const pkgRootDir = join(configDir, "..");
|
|
267
|
+
for (const [k, v] of Object.entries(resolveFlowDeps(config.dependencies, pkgRootDir, versionsFromNpm, useWorkspace)))
|
|
249
268
|
target.dependencies[k] = v;
|
|
250
|
-
for (const [k, v] of Object.entries(resolveFlowDeps(config.devDependencies,
|
|
269
|
+
for (const [k, v] of Object.entries(resolveFlowDeps(config.devDependencies, pkgRootDir, versionsFromNpm, useWorkspace)))
|
|
251
270
|
target.devDependencies[k] = v;
|
|
252
271
|
target.scripts = { ...target.scripts, ...config.scripts };
|
|
253
|
-
writeFileSync(targetPath, JSON.stringify(target, null, 2));
|
|
272
|
+
writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
type FlowOsInit = { dependencies?: Record<string, string>; devDependencies?: Record<string, string>; scripts?: Record<string, string>; name?: string; version?: string; type?: string };
|
|
276
|
+
|
|
277
|
+
/** Merge da flow-os-init: root package.json (se in repo) oppure package (npm) */
|
|
278
|
+
function mergeFlowOsInit(
|
|
279
|
+
packageRoot: string,
|
|
280
|
+
pkgName: string,
|
|
281
|
+
cwd: string,
|
|
282
|
+
versionsFromNpm: Map<string, string>,
|
|
283
|
+
useWorkspace: boolean,
|
|
284
|
+
flowOsRepoRoot: string | null
|
|
285
|
+
): void {
|
|
286
|
+
let init: FlowOsInit | undefined;
|
|
287
|
+
if (flowOsRepoRoot) {
|
|
288
|
+
const rootPkgPath = join(flowOsRepoRoot, "package.json");
|
|
289
|
+
if (existsSync(rootPkgPath)) {
|
|
290
|
+
try {
|
|
291
|
+
const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8")) as { "flow-os-init"?: Record<string, FlowOsInit> };
|
|
292
|
+
init = rootPkg["flow-os-init"]?.[pkgName];
|
|
293
|
+
} catch {}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!init) {
|
|
297
|
+
const pkgPath = join(packageRoot, "package.json");
|
|
298
|
+
if (!existsSync(pkgPath)) return;
|
|
299
|
+
try {
|
|
300
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { "flow-os-init"?: FlowOsInit };
|
|
301
|
+
init = pkg["flow-os-init"];
|
|
302
|
+
} catch {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!init) return;
|
|
307
|
+
const targetPath = join(cwd, "package.json");
|
|
308
|
+
const target = existsSync(targetPath)
|
|
309
|
+
? JSON.parse(readFileSync(targetPath, "utf-8"))
|
|
310
|
+
: { name: init.name ?? (basename(cwd) || "flow-app"), version: init.version ?? "0.0.1", type: init.type ?? "module" };
|
|
311
|
+
target.dependencies = { ...target.dependencies, ...init.dependencies };
|
|
312
|
+
target.devDependencies = { ...target.devDependencies, ...init.devDependencies };
|
|
313
|
+
for (const [k, v] of Object.entries(resolveFlowDeps(init.dependencies, packageRoot, versionsFromNpm, useWorkspace)))
|
|
314
|
+
target.dependencies[k] = v;
|
|
315
|
+
for (const [k, v] of Object.entries(resolveFlowDeps(init.devDependencies, packageRoot, versionsFromNpm, useWorkspace)))
|
|
316
|
+
target.devDependencies[k] = v;
|
|
317
|
+
target.scripts = { ...target.scripts, ...init.scripts };
|
|
318
|
+
writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Aggiunge dipendenza per pacchetti senza config (solo install) */
|
|
322
|
+
function addDependencyForPackage(
|
|
323
|
+
pkgName: string,
|
|
324
|
+
cwd: string,
|
|
325
|
+
versionsFromNpm: Map<string, string>,
|
|
326
|
+
useWorkspace: boolean
|
|
327
|
+
): void {
|
|
328
|
+
const targetPath = join(cwd, "package.json");
|
|
329
|
+
if (!existsSync(targetPath)) return;
|
|
330
|
+
const target = JSON.parse(readFileSync(targetPath, "utf-8"));
|
|
331
|
+
target.dependencies = target.dependencies ?? {};
|
|
332
|
+
const spec = useWorkspace ? "workspace:*" : (versionsFromNpm.get(pkgName) ? `^${versionsFromNpm.get(pkgName)}` : undefined);
|
|
333
|
+
if (spec) target.dependencies[pkgName] = spec;
|
|
334
|
+
writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
|
|
254
335
|
}
|
|
255
336
|
|
|
256
337
|
/** Assicura che versions abbia le versioni per pkgNames (fetch lazy) */
|
|
@@ -382,7 +463,42 @@ export async function initLib(
|
|
|
382
463
|
await collectAllTemplates(libs, combined, done, order, versions, tmpDirs, flowOsRepoRoot);
|
|
383
464
|
|
|
384
465
|
for (const configDir of order) {
|
|
385
|
-
|
|
466
|
+
const packageRoot = join(configDir, "..");
|
|
467
|
+
const pkgName = (() => {
|
|
468
|
+
try {
|
|
469
|
+
const p = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf-8")) as { name?: string };
|
|
470
|
+
return p?.name ?? "";
|
|
471
|
+
} catch {
|
|
472
|
+
return "";
|
|
473
|
+
}
|
|
474
|
+
})();
|
|
475
|
+
if (existsSync(join(configDir, "package.json"))) {
|
|
476
|
+
mergePkg(configDir, cwd, versions, useWorkspace);
|
|
477
|
+
} else {
|
|
478
|
+
mergeFlowOsInit(packageRoot, pkgName, cwd, versions, useWorkspace, flowOsRepoRoot);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const targetPath = join(cwd, "package.json");
|
|
483
|
+
if (!existsSync(targetPath)) {
|
|
484
|
+
writeFileSync(
|
|
485
|
+
targetPath,
|
|
486
|
+
JSON.stringify(sortPkgKeys({ name: basename(cwd) || "flow-app", version: "0.0.1", private: true, type: "module" }), null, 2)
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const toAdd = new Set<string>(pkgNames);
|
|
491
|
+
for (const configDir of order) {
|
|
492
|
+
const packageRoot = join(configDir, "..");
|
|
493
|
+
for (const peer of flowDepsFromPkg(packageRoot)) toAdd.add(peer);
|
|
494
|
+
}
|
|
495
|
+
for (const pkgName of pkgNames) {
|
|
496
|
+
const packageRoot = flowOsRepoRoot ? join(flowOsRepoRoot, "packages", toShortName(pkgName)) : (() => { try { return pkgRoot(pkgName); } catch { return ""; } })();
|
|
497
|
+
if (packageRoot) for (const peer of flowDepsFromPkg(packageRoot)) toAdd.add(peer);
|
|
498
|
+
}
|
|
499
|
+
await ensureVersions(versions, [...toAdd]);
|
|
500
|
+
for (const pkgName of toAdd) {
|
|
501
|
+
addDependencyForPackage(pkgName, cwd, versions, useWorkspace);
|
|
386
502
|
}
|
|
387
503
|
|
|
388
504
|
onProgress?.("write");
|
|
@@ -390,7 +506,7 @@ export async function initLib(
|
|
|
390
506
|
|
|
391
507
|
onProgress?.("install");
|
|
392
508
|
const installCwd = useWorkspace && flowOsRepoRoot ? flowOsRepoRoot : cwd;
|
|
393
|
-
const proc = Bun.spawn(["bun", "install"], { cwd: installCwd, stdout: "
|
|
509
|
+
const proc = Bun.spawn(["bun", "install"], { cwd: installCwd, stdout: "pipe", stderr: "pipe" });
|
|
394
510
|
const exitCode = await proc.exited;
|
|
395
511
|
if (exitCode !== 0) {
|
|
396
512
|
throw new Error(`bun install exited with code ${exitCode}`);
|