create-flow-os 0.0.51-dev.1772054686 → 0.0.51-dev.1772055703

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flow-os",
3
- "version": "0.0.51-dev.1772054686",
3
+ "version": "0.0.51-dev.1772055703",
4
4
  "license": "PolyForm-Shield-1.0.0",
5
5
  "type": "module",
6
6
  "dependencies": {
package/src/init/merge.ts CHANGED
@@ -87,13 +87,20 @@ export async function resolveConflicts(conflicts: Conflict[], path: string): Pro
87
87
  return overrides;
88
88
  }
89
89
 
90
- /** Never overwrite: file vuoto → template, file con contenuto → mantieni. Mai corrompere. */
90
+ /** Normalizza contenuto per confronto (trim, line endings) */
91
+ function normalizeForCompare(s: string): string {
92
+ return s.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
93
+ }
94
+
95
+ /** Never overwrite: file vuoto → template, file con contenuto → mantieni. Mai corrompere.
96
+ * Se supersededContent è fornito e userContent coincide (normalizzato), usa templateContent:
97
+ * un pacchetto "figlio" (es. router) può sostituire il template di un pacchetto "genitore" (es. client). */
91
98
  export async function mergeFile(
92
99
  userContent: string | null,
93
100
  templateContent: string,
94
101
  relPath: string,
95
102
  promptConflict: (path: string, conflicts: Conflict[]) => Promise<Record<string, unknown>>,
96
- _options?: Merge3WayOptions
103
+ _options?: Merge3WayOptions & { supersededContent?: string | null }
97
104
  ): Promise<string> {
98
105
  const isJson = relPath.toLowerCase().endsWith(".json");
99
106
 
@@ -108,9 +115,18 @@ export async function mergeFile(
108
115
  return merged;
109
116
  }
110
117
 
111
- // Never overwrite: vuoto → template. Con contenuto → mantieni. Eccezione: file incompleto (manca export che template ha)
112
118
  const content = (userContent ?? "").trim();
113
119
  if (!content) return templateContent;
120
+
121
+ // Template supersede template: se il file utente è identico al template precedente (sostituito da un pkg figlio), usa il nuovo
122
+ const superseded = _options?.supersededContent;
123
+ if (superseded != null && superseded.trim()) {
124
+ if (normalizeForCompare(userContent!) === normalizeForCompare(superseded)) {
125
+ return templateContent;
126
+ }
127
+ }
128
+
129
+ // Never overwrite: eccezione file incompleto (manca export che template ha)
114
130
  const tHasExport = /\bexport\s+default\b/.test(templateContent);
115
131
  const uHasExport = /\bexport\s+default\b/.test(userContent ?? "");
116
132
  if (tHasExport && !uHasExport) {
@@ -30,38 +30,42 @@ function collectConfigFiles(configDir: string, relPath = ""): Map<string, string
30
30
  return out;
31
31
  }
32
32
 
33
- /** Merge template A con template B (stesso relPath) */
34
- function mergeTemplateInto(combined: Map<string, string>, pkgFiles: Map<string, string>): void {
33
+ type TemplateEntry = { current: string; superseded?: string };
34
+
35
+ /** Merge template A con template B (stesso relPath). Quando B sovrascrive A, A va in superseded. */
36
+ function mergeTemplateInto(combined: Map<string, TemplateEntry>, pkgFiles: Map<string, string>): void {
35
37
  for (const [relPath, content] of pkgFiles) {
36
38
  const existing = combined.get(relPath);
37
39
  const isJson = relPath.toLowerCase().endsWith(".json");
38
40
  const isCode = /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(relPath);
39
41
  if (existing === undefined) {
40
- combined.set(relPath, content);
42
+ combined.set(relPath, { current: content });
41
43
  } else if (isJson) {
42
- combined.set(relPath, mergeJsonTemplates(existing, content));
44
+ combined.set(relPath, { current: mergeJsonTemplates(existing.current, content) });
43
45
  } else if (isCode) {
44
- combined.set(relPath, mergeCodeTemplates(existing, content));
46
+ combined.set(relPath, { current: mergeCodeTemplates(existing.current, content), superseded: existing.current });
45
47
  } else {
46
- combined.set(relPath, content);
48
+ combined.set(relPath, { current: content, superseded: existing.current });
47
49
  }
48
50
  }
49
51
  }
50
52
 
51
- /** Scrive combined template in cwd. Never overwrite: file con contenuto → mantieni. */
53
+ /** Scrive combined template in cwd. Never overwrite: file con contenuto → mantieni. Se coincide con superseded → usa nuovo template. */
52
54
  async function writeMergedWithUser(
53
- combined: Map<string, string>,
55
+ combined: Map<string, TemplateEntry>,
54
56
  cwd: string,
55
57
  promptConflict: (path: string, conflicts: import("./merge").Conflict[]) => Promise<Record<string, unknown>>
56
58
  ): Promise<void> {
57
- for (const [relPath, templateContent] of combined) {
59
+ for (const [relPath, entry] of combined) {
58
60
  const dest = join(cwd, relPath);
59
61
  const destDir = dirname(dest);
60
62
  if (!existsSync(destDir)) {
61
63
  mkdirSync(destDir, { recursive: true });
62
64
  }
63
65
  const userContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
64
- const merged = await mergeFile(userContent, templateContent, relPath, promptConflict);
66
+ const merged = await mergeFile(userContent, entry.current, relPath, promptConflict, {
67
+ supersededContent: entry.superseded ?? null,
68
+ });
65
69
  writeFileSync(dest, merged);
66
70
  }
67
71
  }
@@ -455,7 +459,7 @@ export async function initLib(
455
459
  }
456
460
 
457
461
  onProgress?.("templates");
458
- const combined = new Map<string, string>();
462
+ const combined = new Map<string, { current: string; superseded?: string }>();
459
463
  const done = new Set<string>();
460
464
  const order: string[] = [];
461
465
  const tmpDirs: string[] = [];