synthesisui 0.16.318 → 0.16.320
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/commands/add.js +2 -0
- package/dist/commands/ci.js +1 -1
- package/dist/commands/component.js +22 -8
- package/dist/commands/connect.js +1 -1
- package/dist/commands/generate.js +3 -0
- package/dist/commands/import.js +8 -8
- package/dist/commands/refit.js +3 -0
- package/dist/commands/status.js +2 -2
- package/dist/commands/upgrade.js +38 -2
- package/dist/install-marks.js +8 -1
- package/dist/written.js +70 -0
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -308,6 +308,8 @@ export async function add(slug, opts) {
|
|
|
308
308
|
const reference = lockReference(payload, prev);
|
|
309
309
|
return reference ? { reference } : {};
|
|
310
310
|
})(),
|
|
311
|
+
/** A memória do que NÓS escrevemos atravessa a re-materialização - ver `RootLock.written`. */
|
|
312
|
+
...(prev?.written ? { written: prev.written } : {}),
|
|
311
313
|
};
|
|
312
314
|
const landed = (() => {
|
|
313
315
|
if (!prev?.fetchedAt)
|
package/dist/commands/ci.js
CHANGED
|
@@ -46,7 +46,7 @@ export async function ci(opts) {
|
|
|
46
46
|
console.log(section("The contract"));
|
|
47
47
|
console.log(body("--format=github annotations on the exact line, always warnings"));
|
|
48
48
|
console.log(body("--format=sarif for code-scanning or SonarQube, same findings"));
|
|
49
|
-
console.log(body("--baseline exit 1 only when
|
|
49
|
+
console.log(body("--baseline exit 1 only when the count went UP, in total or in any file"));
|
|
50
50
|
console.log(body("--write-baseline freeze where you are today, and commit it - yesterday's"));
|
|
51
51
|
console.log(body(" debt stops hurting without going uncounted"));
|
|
52
52
|
console.log(body("--strict exit 1 on ANY finding. Honest, and red on day one in a"));
|
|
@@ -11,6 +11,7 @@ import { fetchComponent, RegistryError } from "../registry.js";
|
|
|
11
11
|
import { flavourResolver } from "../styles-flavour.js";
|
|
12
12
|
import { inTheirTongue, projectTongue } from "../their-tongue.js";
|
|
13
13
|
import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
|
|
14
|
+
import { recordWritten } from "../written.js";
|
|
14
15
|
/**
|
|
15
16
|
* Writes the shared `cn.ts` next to the components, built from THIS project's
|
|
16
17
|
* installed theme.
|
|
@@ -219,10 +220,16 @@ export async function component(slug, name, opts) {
|
|
|
219
220
|
// it wears (.css) + the barrel. Ignores the css|tailwind flavor - the
|
|
220
221
|
// template drives itself off the .ds-* classes.
|
|
221
222
|
const tsx = interactiveTemplate(res.name);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
const written = [
|
|
224
|
+
{ filename: `${local}.tsx`, content: tsx },
|
|
225
|
+
{ filename: `${local}.css`, content: `${css}\n` },
|
|
226
|
+
{ filename: "index.ts", content: `export * from "./${local}";\n` },
|
|
227
|
+
];
|
|
228
|
+
for (const f of written)
|
|
229
|
+
await writeFile(join(compDir, f.filename), f.content, "utf8");
|
|
230
|
+
/** O fingerprint do que escrevemos - é o que protege a edição dele no upgrade (T7). */
|
|
231
|
+
await recordWritten(join(root, "_synthesisui", "ds", slug), local, written);
|
|
232
|
+
filenames = written.map((f) => f.filename);
|
|
226
233
|
}
|
|
227
234
|
else {
|
|
228
235
|
const files = generateComponentFiles(slug, res.name, res.recipe, css, res.version, flavour, await reactMajorOf(root),
|
|
@@ -258,12 +265,19 @@ export async function component(slug, name, opts) {
|
|
|
258
265
|
* sistema não tem censo nenhum, e ele recebe o que sempre recebeu.
|
|
259
266
|
*/
|
|
260
267
|
const nota = unreadComment(unreadForComponent(await readCensus(root), res.name));
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
268
|
+
/** Os bytes finais, para o fingerprint lembrar EXATAMENTE o que foi a disco (T7). */
|
|
269
|
+
const written = files.map((file) => ({
|
|
270
|
+
filename: file.filename,
|
|
271
|
+
content: file.filename.endsWith(".tsx")
|
|
272
|
+
? `${nota}${file.code}`
|
|
273
|
+
: file.code,
|
|
274
|
+
}));
|
|
275
|
+
for (const f of written)
|
|
276
|
+
await writeFile(join(compDir, f.filename), f.content, "utf8");
|
|
277
|
+
await recordWritten(join(root, "_synthesisui", "ds", slug), local, written);
|
|
264
278
|
if (flavour === "tailwind")
|
|
265
279
|
await writeCn(root, compDir, slug);
|
|
266
|
-
filenames =
|
|
280
|
+
filenames = written.map((f) => f.filename);
|
|
267
281
|
}
|
|
268
282
|
const flavor = wantInteractive ? "interactive" : `styles: ${flavour}`;
|
|
269
283
|
console.log(`✓ ${config.componentsDir}/${local}/ → ${filenames.join(", ")} (${flavor})${local === res.name ? "" : ` - the "${res.name}" blueprint, under your name`}`);
|
package/dist/commands/connect.js
CHANGED
|
@@ -388,7 +388,7 @@ export async function connect(opts) {
|
|
|
388
388
|
}
|
|
389
389
|
else {
|
|
390
390
|
console.log("");
|
|
391
|
-
console.log(body(paint.dim("Your PRs can carry this too: annotations on the exact line, and a
|
|
391
|
+
console.log(body(paint.dim("Your PRs can carry this too: annotations on the exact line, and a check that fails only when the count goes UP - never on the debt you already have.")));
|
|
392
392
|
console.log(paint.blue(snippet(["npx synthesisui@latest ci"])));
|
|
393
393
|
}
|
|
394
394
|
/** A mesma versão que a fiação do editor recebe - os dois pinam no mesmo número. */
|
|
@@ -6,6 +6,7 @@ import { installedSlugs } from "../installed.js";
|
|
|
6
6
|
import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
7
7
|
import { postGenerate, RegistryError } from "../registry.js";
|
|
8
8
|
import { flavourResolver } from "../styles-flavour.js";
|
|
9
|
+
import { recordWritten } from "../written.js";
|
|
9
10
|
/** PascalCase para o hint de import (course-card → CourseCard). */
|
|
10
11
|
function pascalName(name) {
|
|
11
12
|
return name.replace(/(^|[-_])([a-z0-9])/g, (_, __, c) => c.toUpperCase());
|
|
@@ -75,6 +76,8 @@ export async function generate(description, opts) {
|
|
|
75
76
|
for (const file of files) {
|
|
76
77
|
await writeFile(join(compDir, file.filename), file.code, "utf8");
|
|
77
78
|
}
|
|
79
|
+
/** O fingerprint do que escrevemos - protege a edição dele no upgrade (T7). */
|
|
80
|
+
await recordWritten(join(root, "_synthesisui", "ds", slug), res.name, files.map((x) => ({ filename: x.filename, content: x.code })));
|
|
78
81
|
materialized = true;
|
|
79
82
|
console.log(`✓ ${config.componentsDir}/${res.name}/ → ${files.map((f) => f.filename).join(", ")} (styles: ${config.styles})`);
|
|
80
83
|
}
|
package/dist/commands/import.js
CHANGED
|
@@ -507,7 +507,7 @@ export async function takeCensus(root, opts) {
|
|
|
507
507
|
* tipo de surpresa que ninguém pede.
|
|
508
508
|
*/
|
|
509
509
|
if (globalClasses.size > MAX_GLOBAL_CLASSES) {
|
|
510
|
-
console.log(body(paint.faint(`${globalClasses.size} classes in your global sheet, and ${globalClasses.size - MAX_GLOBAL_CLASSES} of them are not travelling with this
|
|
510
|
+
console.log(body(paint.faint(`${globalClasses.size} classes in your global sheet, and ${globalClasses.size - MAX_GLOBAL_CLASSES} of them are not travelling with this measurement - the first ${MAX_GLOBAL_CLASSES} are.`)));
|
|
511
511
|
}
|
|
512
512
|
/**
|
|
513
513
|
* OS KEYFRAMES DA FOLHA DELE, colhidos onde as folhas globais já estão na mão.
|
|
@@ -1831,7 +1831,7 @@ export async function takeCensus(root, opts) {
|
|
|
1831
1831
|
say(body(paint.faint(`${reachability.unreached
|
|
1832
1832
|
.slice(0, 4)
|
|
1833
1833
|
.map((f) => relative(root, f))
|
|
1834
|
-
.join(", ")}${reachability.unreached.length > 4 ? `, and ${reachability.unreached.length - 4} more` : ""} - the full list travels in
|
|
1834
|
+
.join(", ")}${reachability.unreached.length > 4 ? `, and ${reachability.unreached.length - 4} more` : ""} - the full list travels in \`_synthesisui/census.json\`, and nothing was left out because of it`)));
|
|
1835
1835
|
}
|
|
1836
1836
|
/**
|
|
1837
1837
|
* WHAT THE GATE TURNED AWAY - said out loud, grouped, with the reason.
|
|
@@ -2916,7 +2916,7 @@ function printCrosswalk(mine) {
|
|
|
2916
2916
|
function printAgentContract() {
|
|
2917
2917
|
console.log("");
|
|
2918
2918
|
console.log(section("If you are an agent reading this"));
|
|
2919
|
-
console.log(body("The
|
|
2919
|
+
console.log(body("The measurement is deterministic and complete. What it cannot do is judge, so"));
|
|
2920
2920
|
console.log(body("add a `reading` object to the file and send that back to us:"));
|
|
2921
2921
|
console.log("");
|
|
2922
2922
|
console.log(body(paint.faint(' "reading": {')));
|
|
@@ -2926,7 +2926,7 @@ function printAgentContract() {
|
|
|
2926
2926
|
console.log(body(paint.faint(' "themes": { "default": "dark", "has": ["dark"] }')));
|
|
2927
2927
|
console.log(body(paint.faint(" }")));
|
|
2928
2928
|
console.log("");
|
|
2929
|
-
console.log(body("Every hex in `roles` must be one the
|
|
2929
|
+
console.log(body("Every hex in `roles` must be one the measurement already observed - a value"));
|
|
2930
2930
|
console.log(body("we did not find in their code is dropped. Then send the file:"));
|
|
2931
2931
|
console.log("");
|
|
2932
2932
|
console.log(body(` ${paint.strong("synthesisui import --census _synthesisui/census.json")}`));
|
|
@@ -3020,7 +3020,7 @@ async function sayAboutPlace(root, scope) {
|
|
|
3020
3020
|
console.log(body(` ${paint.strong(`synthesisui import --dir ${up} --scope ${vocab.rel} ${usage}`)}`));
|
|
3021
3021
|
console.log(body(paint.faint(` --dir points at the workspace root, so --scope and --usage are read from there.`)));
|
|
3022
3022
|
console.log("");
|
|
3023
|
-
console.log(body(paint.dim("Nothing you ran is wasted - this
|
|
3023
|
+
console.log(body(paint.dim("Nothing you ran is wasted - this measurement is a fine diagnosis of this app. It is a system for one consumer, which is the part worth knowing before you publish it.")));
|
|
3024
3024
|
}
|
|
3025
3025
|
async function sayIfWorkspace(root) {
|
|
3026
3026
|
const { apps, shared } = await siblingProjects(root);
|
|
@@ -3028,7 +3028,7 @@ async function sayIfWorkspace(root) {
|
|
|
3028
3028
|
return;
|
|
3029
3029
|
console.log("");
|
|
3030
3030
|
console.log(section("This root holds several projects"));
|
|
3031
|
-
console.log(body(`${apps.length} apps (${apps.slice(0, 3).join(", ")}${apps.length > 3 ? ", …" : ""}) - and this
|
|
3031
|
+
console.log(body(`${apps.length} apps (${apps.slice(0, 3).join(", ")}${apps.length > 3 ? ", …" : ""}) - and this measurement is the average of all of them.`));
|
|
3032
3032
|
console.log(body("That is a fine DIAGNOSIS and a poor system: a light app and a dark one"));
|
|
3033
3033
|
console.log(body("average into a palette that is neither."));
|
|
3034
3034
|
console.log("");
|
|
@@ -3836,7 +3836,7 @@ export async function runImport(opts) {
|
|
|
3836
3836
|
}
|
|
3837
3837
|
if (census.census !== 1) {
|
|
3838
3838
|
console.log(section("Import"));
|
|
3839
|
-
console.log(body("That file is not a
|
|
3839
|
+
console.log(body("That file is not a measurement this version can send."));
|
|
3840
3840
|
return;
|
|
3841
3841
|
}
|
|
3842
3842
|
/**
|
|
@@ -3850,7 +3850,7 @@ export async function runImport(opts) {
|
|
|
3850
3850
|
* Resolved here rather than at measure time because the reading arrives
|
|
3851
3851
|
* AFTER the census was taken - this is the first moment both halves exist.
|
|
3852
3852
|
*/
|
|
3853
|
-
phase(2, 3, "Resolving
|
|
3853
|
+
phase(2, 3, "Resolving your reading against the measurement");
|
|
3854
3854
|
await resolveReadParts(census, root);
|
|
3855
3855
|
}
|
|
3856
3856
|
else {
|
package/dist/commands/refit.js
CHANGED
|
@@ -7,6 +7,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
7
7
|
import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
8
8
|
import { fetchComponent, postRefit, postSaveComponent, RegistryError, } from "../registry.js";
|
|
9
9
|
import { flavourResolver } from "../styles-flavour.js";
|
|
10
|
+
import { recordWritten } from "../written.js";
|
|
10
11
|
/** Slugs INSTALLED under `_synthesisui/ds/` (a `.lock` marks a real install -
|
|
11
12
|
* a folder holding only refit artifacts doesn't count). */
|
|
12
13
|
/** True when the system is actually installed (tokens.css present). */
|
|
@@ -121,6 +122,8 @@ export async function refit(file, opts) {
|
|
|
121
122
|
for (const f of files) {
|
|
122
123
|
await writeFile(join(compDir, f.filename), f.code, "utf8");
|
|
123
124
|
}
|
|
125
|
+
/** O fingerprint do que escrevemos - protege a edição dele no upgrade (T7). */
|
|
126
|
+
await recordWritten(join(root, "_synthesisui", "ds", slug), res.name, files.map((x) => ({ filename: x.filename, content: x.code })));
|
|
124
127
|
materialized = true;
|
|
125
128
|
console.log(`✓ ${config.componentsDir}/${res.name}/ → ${files.map((f) => f.filename).join(", ")} (styles: ${config.styles})`);
|
|
126
129
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -105,11 +105,11 @@ export async function status(opts) {
|
|
|
105
105
|
console.log(body(`Ratchet at ${parsed.totals?.drift ?? "?"} findings, frozen ${parsed.at ? ageOf(parsed.at) : "at an unrecorded time"} - CI fails only above it.`));
|
|
106
106
|
}
|
|
107
107
|
catch {
|
|
108
|
-
console.log(body("
|
|
108
|
+
console.log(body("`_synthesisui/drift-baseline.json` is here but is not readable JSON."));
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
|
-
console.log(body(paint.dim("
|
|
112
|
+
console.log(body(paint.dim("Nothing in your pipeline can fail on a hard-coded value yet. `synthesisui ci` prints the two steps.")));
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
115
|
* TODO REQUEST LIDO ESTÁ ABERTO: `closeRequest` REMOVE a linha do arquivo, então o arquivo é a fila
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -11,6 +11,7 @@ import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../
|
|
|
11
11
|
import { fetchChangelog, fetchComponent, fetchDesignSystem, RegistryError, } from "../registry.js";
|
|
12
12
|
import { flavourResolver } from "../styles-flavour.js";
|
|
13
13
|
import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
|
|
14
|
+
import { editedSinceWritten, keptLine, readWritten, recordWritten, } from "../written.js";
|
|
14
15
|
import { add } from "./add.js";
|
|
15
16
|
import { reportWhatIsLeft } from "./align.js";
|
|
16
17
|
import { doctor } from "./doctor.js";
|
|
@@ -283,6 +284,9 @@ export async function upgrade(asked, opts) {
|
|
|
283
284
|
const config = await readProjectConfig(root);
|
|
284
285
|
const regenerated = [];
|
|
285
286
|
const failed = [];
|
|
287
|
+
/** Componentes que ELE editou desde que os escrevemos - mantidos intactos (T7). */
|
|
288
|
+
const kept = [];
|
|
289
|
+
const writtenMap = await readWritten(slugDir);
|
|
286
290
|
if (config.target === "next") {
|
|
287
291
|
const componentsRoot = join(root, config.componentsDir);
|
|
288
292
|
const marker = `from the "${slug}" design system`;
|
|
@@ -308,6 +312,17 @@ export async function upgrade(asked, opts) {
|
|
|
308
312
|
}
|
|
309
313
|
if (!head.includes("Generated by SynthesisUI") || !head.includes(marker))
|
|
310
314
|
continue;
|
|
315
|
+
/**
|
|
316
|
+
* A EDIÇÃO DELE VENCE A REGENERAÇÃO (T7) - ver `written.ts`. O fingerprint do que NÓS
|
|
317
|
+
* escrevemos foi gravado no `.lock` na hora da escrita; se o disco divergiu, ele editou, e o
|
|
318
|
+
* arquivo é DELE - fica intacto e o terminal diz qual. `null` é install de antes do
|
|
319
|
+
* fingerprint: sem memória não há veredito, e o comportamento de sempre continua.
|
|
320
|
+
*/
|
|
321
|
+
const edited = await editedSinceWritten(writtenMap[entry], join(componentsRoot, entry));
|
|
322
|
+
if (edited && edited.length > 0) {
|
|
323
|
+
kept.push({ entry, edited });
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
311
326
|
try {
|
|
312
327
|
const res = await fetchComponent(base, slug, entry);
|
|
313
328
|
const files = generateComponentFiles(slug, res.name, res.recipe, res.css, res.version, flavourOf(res.name),
|
|
@@ -325,9 +340,17 @@ export async function upgrade(asked, opts) {
|
|
|
325
340
|
* Ver `unread-for-component.ts`: censo ausente responde vazio, e vazio não vira comentário.
|
|
326
341
|
*/
|
|
327
342
|
const nota = unreadComment(unreadForComponent(await readCensus(root), res.name));
|
|
328
|
-
|
|
329
|
-
|
|
343
|
+
/** Os bytes finais, para o fingerprint lembrar EXATAMENTE o que foi a disco. */
|
|
344
|
+
const finalFiles = files.map((file) => ({
|
|
345
|
+
filename: file.filename,
|
|
346
|
+
content: file.filename.endsWith(".tsx")
|
|
347
|
+
? `${nota}${file.code}`
|
|
348
|
+
: file.code,
|
|
349
|
+
}));
|
|
350
|
+
for (const file of finalFiles) {
|
|
351
|
+
await writeFile(join(componentsRoot, entry, file.filename), file.content, "utf8");
|
|
330
352
|
}
|
|
353
|
+
await recordWritten(slugDir, entry, finalFiles);
|
|
331
354
|
regenerated.push(entry);
|
|
332
355
|
}
|
|
333
356
|
catch {
|
|
@@ -372,6 +395,13 @@ export async function upgrade(asked, opts) {
|
|
|
372
395
|
.map((n) => `\`${n}\``)
|
|
373
396
|
.join(", ")}.`
|
|
374
397
|
: "- No materialized components needed regeneration.",
|
|
398
|
+
...(kept.length > 0
|
|
399
|
+
? [
|
|
400
|
+
`- These components were edited by hand after we wrote them, so the upgrade KEPT your files: ${kept
|
|
401
|
+
.map((k) => `\`${k.entry}\``)
|
|
402
|
+
.join(", ")}. To take the new version of one, rerun \`npx synthesisui component ${slug} <name>\`.`,
|
|
403
|
+
]
|
|
404
|
+
: []),
|
|
375
405
|
"- The full new contract lives in `design-system.json` / `GUIDE.md` next to this file.",
|
|
376
406
|
"",
|
|
377
407
|
].join("\n");
|
|
@@ -394,6 +424,12 @@ export async function upgrade(asked, opts) {
|
|
|
394
424
|
console.log("");
|
|
395
425
|
console.log(body(`⚠ Could not regenerate: ${failed.join(", ")} (removed/renamed in v${latest.version}?)`));
|
|
396
426
|
}
|
|
427
|
+
/** A prova VISÍVEL do T7: o dono fica sabendo AGORA, com o arquivo nomeado e a saída dita. */
|
|
428
|
+
if (kept.length > 0) {
|
|
429
|
+
console.log("");
|
|
430
|
+
for (const k of kept)
|
|
431
|
+
console.log(body(keptLine(slug, k.entry, k.edited)));
|
|
432
|
+
}
|
|
397
433
|
await checkAfterWriting(root, `v${latest.version}`);
|
|
398
434
|
console.log(section("Migrate the app"));
|
|
399
435
|
console.log(body(`The migration brief is at _synthesisui/ds/${slug}/UPGRADE.md`));
|
package/dist/install-marks.js
CHANGED
|
@@ -137,7 +137,14 @@
|
|
|
137
137
|
* ali, que é exatamente o que esta marca existe para dizer. Quem avisa versão nova passa a ser o
|
|
138
138
|
* próprio align, com o `cli` que o servidor manda no `?meta=1`.
|
|
139
139
|
*/
|
|
140
|
-
|
|
140
|
+
/**
|
|
141
|
+
* 0.16.306 -> 0.16.319 em 26/08 (T7), e o SIM é sobre DUAS coisas que caem na pasta: o `.lock`
|
|
142
|
+
* ganha `written` (o fingerprint do que NÓS escrevemos, por componente e arquivo) e o `upgrade`
|
|
143
|
+
* muda QUAIS bytes reescreve - um componente que o dono editou à mão fica INTACTO, com a frase no
|
|
144
|
+
* terminal dizendo qual arquivo e como tomar a versão nova por escolha. Antes desta versão a
|
|
145
|
+
* reescrita era incondicional e o único aviso era prosa depois do fato.
|
|
146
|
+
*/
|
|
147
|
+
export const MATERIALISER_SINCE = "0.16.319";
|
|
141
148
|
/**
|
|
142
149
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
143
150
|
*
|
package/dist/written.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export function fingerprintOf(content) {
|
|
5
|
+
return createHash("sha256")
|
|
6
|
+
.update(content, "utf8")
|
|
7
|
+
.digest("hex")
|
|
8
|
+
.slice(0, 12);
|
|
9
|
+
}
|
|
10
|
+
/** O mapa gravado, ou vazio - um `.lock` ausente/ilegível responde como install legado. */
|
|
11
|
+
export async function readWritten(slugDir) {
|
|
12
|
+
try {
|
|
13
|
+
const lock = JSON.parse(await readFile(join(slugDir, ".lock"), "utf8"));
|
|
14
|
+
return lock.written ?? {};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Grava o fingerprint do que acabou de ser escrito. Lê-modifica-escreve o `.lock` inteiro para
|
|
22
|
+
* não perder nenhum campo irmão; sem `.lock` (componente materializado antes do install), não há
|
|
23
|
+
* onde lembrar - silêncio de propósito, o próximo `add` cria o arquivo e o próximo write grava.
|
|
24
|
+
*/
|
|
25
|
+
export async function recordWritten(slugDir, entry, files) {
|
|
26
|
+
const lockPath = join(slugDir, ".lock");
|
|
27
|
+
let lock;
|
|
28
|
+
try {
|
|
29
|
+
lock = JSON.parse(await readFile(lockPath, "utf8"));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const written = (lock.written ?? {});
|
|
35
|
+
written[entry] = Object.fromEntries(files.map((f) => [f.filename, fingerprintOf(f.content)]));
|
|
36
|
+
lock.written = written;
|
|
37
|
+
await writeFile(lockPath, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* QUAIS ARQUIVOS ELE EDITOU desde que nós os escrevemos.
|
|
41
|
+
*
|
|
42
|
+
* Um arquivo que sumiu também conta: apagar é uma edição - regenerá-lo desfaria a decisão dele
|
|
43
|
+
* do mesmo jeito. Devolve vazio quando nada divergiu (nosso para regenerar) e `null` quando não
|
|
44
|
+
* há fingerprint gravado (install legado - o chamador mantém o comportamento de sempre).
|
|
45
|
+
*/
|
|
46
|
+
export async function editedSinceWritten(recorded, dir) {
|
|
47
|
+
if (!recorded)
|
|
48
|
+
return null;
|
|
49
|
+
const edited = [];
|
|
50
|
+
for (const [filename, fp] of Object.entries(recorded)) {
|
|
51
|
+
try {
|
|
52
|
+
const current = await readFile(join(dir, filename), "utf8");
|
|
53
|
+
if (fingerprintOf(current) !== fp)
|
|
54
|
+
edited.push(filename);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
edited.push(filename);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return edited;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A FRASE DO TERMINAL, uma por componente mantido - o critério do T7 é a prova VISÍVEL: o dono
|
|
64
|
+
* fica sabendo na hora, com o arquivo nomeado e a saída dita, não num diff depois do fato. A
|
|
65
|
+
* saída oferecida regenera por ESCOLHA (o `component` reescreve e regrava o fingerprint).
|
|
66
|
+
*/
|
|
67
|
+
export function keptLine(slug, entry, edited) {
|
|
68
|
+
return (`! ${entry}: you edited ${edited.join(", ")} after we wrote it - kept YOUR file(s).\n` +
|
|
69
|
+
` To take the new version anyway: npx synthesisui component ${slug} ${entry}`);
|
|
70
|
+
}
|
package/package.json
CHANGED