create-flow-os 0.0.47-dev.1772051797 → 0.0.47-dev.1772054229
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 -3
- package/src/init/merge.ts +14 -40
- package/src/init/scaffold.ts +3 -35
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-flow-os",
|
|
3
|
-
"version": "0.0.47-dev.
|
|
3
|
+
"version": "0.0.47-dev.1772054229",
|
|
4
4
|
"license": "PolyForm-Shield-1.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@flow-os/client": ">=0.0.1-dev.0"
|
|
8
|
-
"node-diff3": "^3.2.0"
|
|
7
|
+
"@flow-os/client": ">=0.0.1-dev.0"
|
|
9
8
|
},
|
|
10
9
|
"bin": {
|
|
11
10
|
"create-flow-os": "./src/index.ts"
|
package/src/init/merge.ts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Merge
|
|
3
|
-
*
|
|
2
|
+
* Merge: strategia "never overwrite" (create-react-app, ecc.).
|
|
3
|
+
* File vuoto → template. File con contenuto → mantieni utente. Mai corrompere.
|
|
4
|
+
* JSON: merge additivo strutturato.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import * as readline from "readline";
|
|
7
|
-
import { merge as diff3Merge } from "node-diff3";
|
|
8
8
|
|
|
9
9
|
export type Conflict = { key: string; userVal: unknown; templateVal: unknown; path: string };
|
|
10
10
|
|
|
11
|
-
/** Opzioni
|
|
11
|
+
/** Opzioni (compatibilità): non usate con never-overwrite */
|
|
12
12
|
export type Merge3WayOptions = {
|
|
13
13
|
baseContent?: string;
|
|
14
14
|
saveBase?: (content: string) => void;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const LINE_SEP = /\n/;
|
|
18
|
-
|
|
19
17
|
/** Deep merge di oggetti: aggiunge chiavi nuove, per esistenti chiede se diverso */
|
|
20
18
|
function deepMergeAdditive(
|
|
21
19
|
user: Record<string, unknown>,
|
|
@@ -73,21 +71,6 @@ export function mergeCodeTemplates(a: string, b: string): string {
|
|
|
73
71
|
return b || a;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
/** Merge 3-way per file di codice (tipo Git). Base = template precedente (da cache). */
|
|
77
|
-
function merge3Way(
|
|
78
|
-
userContent: string,
|
|
79
|
-
templateContent: string,
|
|
80
|
-
baseContent: string | undefined
|
|
81
|
-
): { merged: string; hasConflicts: boolean } {
|
|
82
|
-
if (!userContent.trim()) return { merged: templateContent, hasConflicts: false };
|
|
83
|
-
const base = baseContent ?? templateContent;
|
|
84
|
-
const { conflict, result } = diff3Merge(userContent, base, templateContent, {
|
|
85
|
-
stringSeparator: LINE_SEP,
|
|
86
|
-
excludeFalseConflicts: true,
|
|
87
|
-
});
|
|
88
|
-
return { merged: result.join("\n"), hasConflicts: conflict };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
74
|
/** Chiede all'utente se applicare i valori del template in conflitto */
|
|
92
75
|
export async function resolveConflicts(conflicts: Conflict[], path: string): Promise<Record<string, unknown>> {
|
|
93
76
|
if (conflicts.length === 0) return {};
|
|
@@ -104,16 +87,15 @@ export async function resolveConflicts(conflicts: Conflict[], path: string): Pro
|
|
|
104
87
|
return overrides;
|
|
105
88
|
}
|
|
106
89
|
|
|
107
|
-
/**
|
|
90
|
+
/** Never overwrite: file vuoto → template, file con contenuto → mantieni. Mai corrompere. */
|
|
108
91
|
export async function mergeFile(
|
|
109
92
|
userContent: string | null,
|
|
110
93
|
templateContent: string,
|
|
111
94
|
relPath: string,
|
|
112
95
|
promptConflict: (path: string, conflicts: Conflict[]) => Promise<Record<string, unknown>>,
|
|
113
|
-
|
|
96
|
+
_options?: Merge3WayOptions
|
|
114
97
|
): Promise<string> {
|
|
115
98
|
const isJson = relPath.toLowerCase().endsWith(".json");
|
|
116
|
-
const use3Way = /\.(ts|tsx|js|jsx|mjs|cjs|html|css|md|vue|svelte)$/.test(relPath);
|
|
117
99
|
|
|
118
100
|
if (isJson) {
|
|
119
101
|
const { merged, conflicts } = mergeJson(userContent ?? "{}", templateContent, relPath);
|
|
@@ -126,21 +108,13 @@ export async function mergeFile(
|
|
|
126
108
|
return merged;
|
|
127
109
|
}
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.warn(`\n[flow-os] Conflitti in ${relPath} - risolvi i marker <<<<<<< / >>>>>>>`);
|
|
137
|
-
}
|
|
138
|
-
options?.saveBase?.(templateContent);
|
|
139
|
-
return merged;
|
|
111
|
+
// Never overwrite: vuoto → template. Con contenuto → mantieni. Eccezione: file incompleto (manca export che template ha)
|
|
112
|
+
const content = (userContent ?? "").trim();
|
|
113
|
+
if (!content) return templateContent;
|
|
114
|
+
const tHasExport = /\bexport\s+default\b/.test(templateContent);
|
|
115
|
+
const uHasExport = /\bexport\s+default\b/.test(userContent ?? "");
|
|
116
|
+
if (tHasExport && !uHasExport) {
|
|
117
|
+
return templateContent; // File troncato
|
|
140
118
|
}
|
|
141
|
-
|
|
142
|
-
// Altri file: merge 3-way se possibile
|
|
143
|
-
const { merged } = merge3Way(userContent ?? "", templateContent, options?.baseContent);
|
|
144
|
-
options?.saveBase?.(templateContent);
|
|
145
|
-
return merged;
|
|
119
|
+
return userContent!;
|
|
146
120
|
}
|
package/src/init/scaffold.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, cpSync, readFileSync, writeFileSync, readdirSync, mkdirSync
|
|
|
2
2
|
import { join, basename, dirname } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { pkgRoot, flowDeps, toPkgName, toShortName } from "./lib";
|
|
5
|
-
import { mergeFile, resolveConflicts, mergeJsonTemplates, mergeCodeTemplates
|
|
5
|
+
import { mergeFile, resolveConflicts, mergeJsonTemplates, mergeCodeTemplates } from "./merge";
|
|
6
6
|
|
|
7
7
|
const SKIP = new Set(["node_modules", ".git", ".vite", "package.json"]);
|
|
8
8
|
const NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -48,54 +48,22 @@ function mergeTemplateInto(combined: Map<string, string>, pkgFiles: Map<string,
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function loadInitBase(cwd: string): Record<string, string> {
|
|
54
|
-
const p = join(cwd, INIT_BASE_FILE);
|
|
55
|
-
if (!existsSync(p)) return {};
|
|
56
|
-
try {
|
|
57
|
-
return JSON.parse(readFileSync(p, "utf-8")) as Record<string, string>;
|
|
58
|
-
} catch {
|
|
59
|
-
return {};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function saveInitBase(cwd: string, base: Record<string, string>): void {
|
|
64
|
-
const dir = join(cwd, ".flow-os");
|
|
65
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
66
|
-
writeFileSync(join(cwd, INIT_BASE_FILE), JSON.stringify(base, null, 2));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Scrive combined template in cwd facendo merge con file utente (merge 3-way tipo Git) */
|
|
51
|
+
/** Scrive combined template in cwd. Never overwrite: file con contenuto → mantieni. */
|
|
70
52
|
async function writeMergedWithUser(
|
|
71
53
|
combined: Map<string, string>,
|
|
72
54
|
cwd: string,
|
|
73
55
|
promptConflict: (path: string, conflicts: import("./merge").Conflict[]) => Promise<Record<string, unknown>>
|
|
74
56
|
): Promise<void> {
|
|
75
|
-
const baseCache = loadInitBase(cwd);
|
|
76
|
-
const newBase: Record<string, string> = {};
|
|
77
|
-
|
|
78
57
|
for (const [relPath, templateContent] of combined) {
|
|
79
58
|
const dest = join(cwd, relPath);
|
|
80
59
|
const destDir = dirname(dest);
|
|
81
60
|
if (!existsSync(destDir)) {
|
|
82
|
-
const { mkdirSync } = await import("fs");
|
|
83
61
|
mkdirSync(destDir, { recursive: true });
|
|
84
62
|
}
|
|
85
63
|
const userContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
86
|
-
const
|
|
87
|
-
baseContent: baseCache[relPath],
|
|
88
|
-
saveBase: (content) => {
|
|
89
|
-
newBase[relPath] = content;
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
const merged = await mergeFile(userContent, templateContent, relPath, promptConflict, mergeOpts);
|
|
64
|
+
const merged = await mergeFile(userContent, templateContent, relPath, promptConflict);
|
|
93
65
|
writeFileSync(dest, merged);
|
|
94
66
|
}
|
|
95
|
-
|
|
96
|
-
if (Object.keys(newBase).length > 0) {
|
|
97
|
-
saveInitBase(cwd, { ...baseCache, ...newBase });
|
|
98
|
-
}
|
|
99
67
|
}
|
|
100
68
|
|
|
101
69
|
/** Indica se create-flow-os è in modalità dev (flow-os@dev) */
|