jostraca 0.31.0 → 0.31.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.
Files changed (67) hide show
  1. package/README.md +355 -0
  2. package/dist/build/BuildContext.d.ts +1 -0
  3. package/dist/build/BuildContext.js +10 -0
  4. package/dist/build/BuildContext.js.map +1 -1
  5. package/dist/build/BuildMeta.js +21 -4
  6. package/dist/build/BuildMeta.js.map +1 -1
  7. package/dist/build/FileHandler.d.ts +11 -4
  8. package/dist/build/FileHandler.js +468 -92
  9. package/dist/build/FileHandler.js.map +1 -1
  10. package/dist/cmp/Content.js.map +1 -1
  11. package/dist/cmp/Copy.js +5 -1
  12. package/dist/cmp/Copy.js.map +1 -1
  13. package/dist/cmp/File.js +2 -0
  14. package/dist/cmp/File.js.map +1 -1
  15. package/dist/cmp/Folder.js.map +1 -1
  16. package/dist/cmp/Fragment.js +36 -7
  17. package/dist/cmp/Fragment.js.map +1 -1
  18. package/dist/cmp/Inject.js +30 -1
  19. package/dist/cmp/Inject.js.map +1 -1
  20. package/dist/cmp/Line.js.map +1 -1
  21. package/dist/cmp/List.js.map +1 -1
  22. package/dist/cmp/None.js.map +1 -1
  23. package/dist/cmp/Project.js.map +1 -1
  24. package/dist/cmp/Slot.js.map +1 -1
  25. package/dist/diff.d.ts +36 -0
  26. package/dist/diff.js +446 -0
  27. package/dist/diff.js.map +1 -0
  28. package/dist/jostraca.d.ts +19 -18
  29. package/dist/jostraca.js +83 -24
  30. package/dist/jostraca.js.map +1 -1
  31. package/dist/op/ContentOp.js.map +1 -1
  32. package/dist/op/CopyOp.js +158 -22
  33. package/dist/op/CopyOp.js.map +1 -1
  34. package/dist/op/FileOp.js +26 -27
  35. package/dist/op/FileOp.js.map +1 -1
  36. package/dist/op/FolderOp.js +3 -0
  37. package/dist/op/FolderOp.js.map +1 -1
  38. package/dist/op/FragmentOp.js.map +1 -1
  39. package/dist/op/InjectOp.js +29 -7
  40. package/dist/op/InjectOp.js.map +1 -1
  41. package/dist/op/NoneOp.js.map +1 -1
  42. package/dist/op/ProjectOp.js.map +1 -1
  43. package/dist/op/SlotOp.js.map +1 -1
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/dist/types.d.ts +1 -0
  46. package/dist/util/basic.d.ts +5 -1
  47. package/dist/util/basic.js +218 -9
  48. package/dist/util/basic.js.map +1 -1
  49. package/dist/util/point.d.ts +8 -7
  50. package/dist/util/point.js.map +1 -1
  51. package/package.json +19 -11
  52. package/src/build/BuildContext.ts +12 -0
  53. package/src/build/BuildMeta.ts +24 -5
  54. package/src/build/FileHandler.ts +489 -121
  55. package/src/cmp/Copy.ts +5 -1
  56. package/src/cmp/File.ts +3 -0
  57. package/src/cmp/Fragment.ts +41 -7
  58. package/src/cmp/Inject.ts +38 -1
  59. package/src/diff.ts +657 -0
  60. package/src/jostraca.ts +83 -20
  61. package/src/op/CopyOp.ts +173 -22
  62. package/src/op/FileOp.ts +29 -31
  63. package/src/op/FolderOp.ts +6 -0
  64. package/src/op/InjectOp.ts +39 -7
  65. package/src/tsconfig.json +3 -1
  66. package/src/types.ts +5 -0
  67. package/src/util/basic.ts +250 -10
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # jostraca
2
+
3
+ A code and project generator that uses React-style components to define
4
+ files, folders, and content declaratively. This is the canonical
5
+ TypeScript implementation, published to npm as
6
+ [`jostraca`](https://www.npmjs.com/package/jostraca). (A feature-parity Go
7
+ port lives at [`github.com/jostraca/jostraca/go`](https://pkg.go.dev/github.com/jostraca/jostraca/go).)
8
+
9
+ [![npm version](https://badge.fury.io/js/jostraca.svg)](https://www.npmjs.com/package/jostraca)
10
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jostraca/jostraca/blob/master/LICENSE)
11
+
12
+ ```bash
13
+ npm install jostraca
14
+ ```
15
+
16
+ Peer dependencies (install the ones your usage needs):
17
+
18
+ ```bash
19
+ npm install memfs shape
20
+ ```
21
+
22
+ `memfs` backs in-memory generation; `shape` backs option validation. Both
23
+ are loose ranges (`memfs >=4`, `shape >=10`).
24
+
25
+ ---
26
+
27
+ This README is organised along the four [Diátaxis](https://diataxis.fr)
28
+ documentation modes. Jump to the one that fits what you need right now:
29
+
30
+ - **[Tutorial](#tutorial)** — learning-oriented. Build your first
31
+ generator from scratch.
32
+ - **[How-to guides](#how-to-guides)** — task-oriented. Recipes for
33
+ specific jobs.
34
+ - **[Reference](#reference)** — information-oriented. Every component,
35
+ option, and utility.
36
+ - **[Explanation](#explanation)** — understanding-oriented. How and why
37
+ Jostraca works the way it does.
38
+
39
+ ---
40
+
41
+ ## Tutorial
42
+
43
+ *A short lesson. Follow it top to bottom and you'll have a working
44
+ generator.*
45
+
46
+ ### 1. A first file tree
47
+
48
+ Create a generator, then describe a tree of components. Nesting the
49
+ components mirrors the folders and files you want on disk.
50
+
51
+ ```typescript
52
+ import { Jostraca, Project, Folder, File, Content } from 'jostraca'
53
+
54
+ const jostraca = Jostraca()
55
+
56
+ await jostraca.generate({ folder: './out' }, () => {
57
+ Project({ folder: 'my-app' }, () => {
58
+
59
+ Folder({ name: 'src' }, () => {
60
+ File({ name: 'index.js' }, () => {
61
+ Content('console.log("hello world")\n')
62
+ })
63
+ })
64
+
65
+ File({ name: 'package.json' }, () => {
66
+ Content('{ "name": "my-app" }\n')
67
+ })
68
+ })
69
+ })
70
+ ```
71
+
72
+ Run it, and Jostraca writes:
73
+
74
+ ```
75
+ out/
76
+ my-app/
77
+ src/
78
+ index.js -> console.log("hello world")
79
+ package.json -> { "name": "my-app" }
80
+ ```
81
+
82
+ You declared a tree; Jostraca built it. That is the whole model.
83
+
84
+ ### 2. Insert values with a template
85
+
86
+ Real generators produce files that vary with input data. Pass a `model`
87
+ to `Jostraca()` and reference it in content with `$$path$$`:
88
+
89
+ ```typescript
90
+ const jostraca = Jostraca({
91
+ model: { app: { name: 'Acme', version: '1.0.0' } }
92
+ })
93
+
94
+ await jostraca.generate({ folder: './out' }, () => {
95
+ Project({}, () => {
96
+ File({ name: 'config.txt' }, () => {
97
+ Content('App: $$app.name$$ v$$app.version$$\n')
98
+ })
99
+ })
100
+ })
101
+ // out/config.txt -> App: Acme v1.0.0
102
+ ```
103
+
104
+ ### 3. Make a reusable component
105
+
106
+ When a shape repeats, capture it with `cmp()` — a plain function that
107
+ emits content. This is the "React-style" part: components compose.
108
+
109
+ ```typescript
110
+ import { cmp, Content, each } from 'jostraca'
111
+
112
+ const FunctionDef = cmp(function FunctionDef(props: any) {
113
+ Content(`function ${props.name}(`)
114
+ Content(props.params.join(', '))
115
+ Content(') {\n')
116
+ each(props.ctx$.model.body, (line) => Content(` ${line}\n`))
117
+ Content('}\n')
118
+ })
119
+
120
+ File({ name: 'utils.js' }, () => {
121
+ FunctionDef({ name: 'greet', params: ['name'] })
122
+ })
123
+ ```
124
+
125
+ From here, the **How-to guides** below cover each capability in turn, and
126
+ the **Reference** documents every option.
127
+
128
+ ## How-to guides
129
+
130
+ *Practical recipes. Each one is self-contained — read only the one you
131
+ need.*
132
+
133
+ ### Read a template file and fill its slots
134
+
135
+ Use `Fragment` to read an external template, and `Slot` to replace marked
136
+ regions inside it.
137
+
138
+ ```typescript
139
+ // template.html contains:
140
+ // <html>
141
+ // <!-- <[SLOT:head]> -->
142
+ // <body>
143
+ // <!-- <[SLOT:body]> -->
144
+ // </body>
145
+ // </html>
146
+
147
+ File({ name: 'index.html' }, () => {
148
+ Fragment({ from: '/templates/template.html' }, () => {
149
+ Slot({ name: 'head' }, () => {
150
+ Content('<title>My Page</title>')
151
+ })
152
+ Slot({ name: 'body' }, () => {
153
+ Content('<h1>Hello</h1>')
154
+ })
155
+ })
156
+ })
157
+ ```
158
+
159
+ An unnamed `<[SLOT]>` marker receives all non-`Slot` children of the
160
+ Fragment. Giving a Fragment non-`Slot` children when its source has no
161
+ unnamed `<[SLOT]>` marker is an error — there is nowhere for that content
162
+ to go, and it would otherwise be discarded silently.
163
+
164
+ ### Copy files and directories
165
+
166
+ `Copy` brings in existing files or whole directory trees, applying
167
+ template substitution to text files (binaries pass through untouched):
168
+
169
+ ```typescript
170
+ const jostraca = Jostraca({ model: { title: 'My App' } })
171
+
172
+ await jostraca.generate({ folder: './out' }, () => {
173
+ Project({ folder: 'app' }, () => {
174
+ Folder({ name: 'static' }, () => {
175
+ Copy({ from: '/templates/assets' })
176
+ Copy({ from: '/templates/readme.txt', to: 'README.txt' })
177
+ })
178
+ })
179
+ })
180
+ ```
181
+
182
+ ### Edit an existing file in place
183
+
184
+ `Inject` replaces the content between two markers in a file that already
185
+ exists, leaving the rest untouched:
186
+
187
+ ```typescript
188
+ // existing foo.txt:
189
+ // HEADER
190
+ // #--START--#
191
+ // old content
192
+ // #--END--#
193
+ // FOOTER
194
+
195
+ Project({}, () => {
196
+ Inject({ name: 'foo.txt' }, () => {
197
+ Content('new content')
198
+ })
199
+ })
200
+ // Result: HEADER\n#--START--#\nnew content\n#--END--#\nFOOTER
201
+ ```
202
+
203
+ ### Regenerate without clobbering hand edits
204
+
205
+ By default Jostraca overwrites. When users edit generated files, choose a
206
+ gentler mode per file extension:
207
+
208
+ ```typescript
209
+ await jostraca.generate({
210
+ folder: './out',
211
+ existing: {
212
+ txt: {
213
+ write: true, // overwrite existing files (default)
214
+ preserve: true, // keep a .old. backup of what was overwritten
215
+ present: false, // write to .new. instead of overwriting
216
+ diff: false, // write an annotated 2-way diff
217
+ merge: false, // 3-way merge with conflict markers
218
+ },
219
+ bin: {
220
+ write: true,
221
+ preserve: false,
222
+ present: false,
223
+ }
224
+ }
225
+ }, root)
226
+ ```
227
+
228
+ For files a user should own outright, add the line `# JOSTRACA_PROTECT`
229
+ anywhere in the generated file — Jostraca will never overwrite it on
230
+ subsequent runs.
231
+
232
+ ### Generate in memory (for tests or virtual FS)
233
+
234
+ Set `mem: true` and provide any template inputs via `vol`. Nothing touches
235
+ disk; read the result back from the returned volume:
236
+
237
+ ```typescript
238
+ const jostraca = Jostraca({
239
+ mem: true,
240
+ vol: { '/templates/header.txt': 'HEADER\n' }
241
+ })
242
+
243
+ const result = await jostraca.generate({ folder: '/' }, root)
244
+
245
+ const files = result.vol().toJSON()
246
+ // { '/output.txt': '...' }
247
+ ```
248
+
249
+ ## Reference
250
+
251
+ *Information-oriented. Look things up here; don't read it front to back.*
252
+
253
+ The complete, authoritative reference for every component, prop, option,
254
+ and utility is **[REFERENCE.md](./REFERENCE.md)**.
255
+
256
+ ### Components
257
+
258
+ `Project`, `Folder`, `File`, `Content`, `Fragment`, `Slot`, `Inject`,
259
+ `Copy`, `Line`, `List` — plus custom components via `cmp()`.
260
+
261
+ ### `generate()` result
262
+
263
+ `generate()` returns a `JostracaResult`:
264
+
265
+ ```typescript
266
+ {
267
+ when: number, // timestamp of generation
268
+ files: {
269
+ written: string[], // files written to disk
270
+ preserved: string[], // backup copies created
271
+ presented: string[], // .new. files created
272
+ diffed: string[], // diff files created
273
+ merged: string[], // merged files created
274
+ conflicted: string[], // files with merge conflicts
275
+ unchanged: string[], // files left unchanged
276
+ },
277
+ audit: () => Audit[], // audit trail of operations
278
+ vol?: () => any, // virtual volume (mem mode)
279
+ fs?: () => FST, // file system (mem mode)
280
+ }
281
+ ```
282
+
283
+ ### Utility exports
284
+
285
+ ```typescript
286
+ import {
287
+ each, // iterate arrays/objects with marking and sorting
288
+ get, // simple dot-path property access
289
+ getx, // advanced path access with operators
290
+ camelify, // 'foo_bar' -> 'FooBar'
291
+ snakify, // 'FooBar' -> 'foo_bar'
292
+ kebabify, // 'FooBar' -> 'foo-bar'
293
+ names, // generate all case variants of a name
294
+ template, // process template strings with model data
295
+ indent, // indent text content
296
+ cmp, // create custom components
297
+ deep, // deep merge objects
298
+ omap, // map over object entries (sorted key order)
299
+ } from 'jostraca'
300
+ ```
301
+
302
+ See [REFERENCE.md](./REFERENCE.md) for signatures and edge-case behaviour.
303
+
304
+ ## Explanation
305
+
306
+ *Understanding-oriented. Background and design — read this to build a
307
+ mental model, not to accomplish a specific task.*
308
+
309
+ ### Two phases: define, then build
310
+
311
+ A `generate()` call runs in two distinct phases. First, the **define
312
+ phase** executes your callback: every component call (`Project`, `File`,
313
+ `Content`, …) records a node in an in-memory tree — nothing is written
314
+ yet. Then the **build phase** walks that tree and performs the real file
315
+ operations. Separating the two means the whole intended output is known
316
+ before a single byte is written, which is what makes existing-file
317
+ handling, protection, and merging possible.
318
+
319
+ ### Why React-style components
320
+
321
+ Components nest to mirror the filesystem, and they compose: a component is
322
+ just a function that emits more components. That gives you ordinary
323
+ language tools — loops, conditionals, parameters, reuse via `cmp()` — for
324
+ describing structure, instead of a bespoke templating dialect. The nesting
325
+ is kept noise-free by an `AsyncLocalStorage` context, so child components
326
+ know their parent without you threading it through by hand.
327
+
328
+ ### The philosophy of existing files
329
+
330
+ Generators are run repeatedly against code that humans also edit. Jostraca
331
+ treats the already-on-disk file as a first-class input, not an obstacle:
332
+ the `write` / `preserve` / `present` / `diff` / `merge` modes and
333
+ `# JOSTRACA_PROTECT` exist so that regeneration is safe by policy rather
334
+ than by luck. The three-way merge in particular keeps a baseline of what
335
+ was last generated, so it can distinguish your edits from generator
336
+ changes.
337
+
338
+ ---
339
+
340
+ ## Build and test (contributors)
341
+
342
+ ```bash
343
+ cd ts
344
+ npm install # also pulls peer deps: memfs, shape
345
+ npm run build # tsc --build src test
346
+ npm test # node --test dist-test/**/*.test.js
347
+ ```
348
+
349
+ From the repo root, `make all` builds and tests both the TS and Go stacks.
350
+ See the top-level [`CLAUDE.md`](../CLAUDE.md) for the full contributor
351
+ guide, and [`../go/README.md`](../go/README.md) for the Go port.
352
+
353
+ ## License
354
+
355
+ MIT. Copyright (c) Richard Rodger. See [LICENSE](./LICENSE).
@@ -34,6 +34,7 @@ declare class BuildContext {
34
34
  version: boolean;
35
35
  }, fs: () => FST, now: () => number);
36
36
  addmeta(file: string, meta: any): void;
37
+ folderPath(): string;
37
38
  duplicateFolder(): string;
38
39
  }
39
40
  export { BuildContext };
@@ -50,6 +50,16 @@ class BuildContext {
50
50
  addmeta(file, meta) {
51
51
  this.bmeta.add(file, meta);
52
52
  }
53
+ // Folder path segments for the current build position, as a canonical
54
+ // (forward-slash) prefix. When neither a Project nor a Folder has seeded
55
+ // the path, fall back to the base output folder — otherwise a top-level
56
+ // File would join onto '' and resolve to '/<name>' at the filesystem
57
+ // root, ignoring the configured output folder entirely. FolderOp.before
58
+ // applies the same fallback when seeding the path.
59
+ folderPath() {
60
+ const { path, parent } = this.current.folder;
61
+ return 0 < path.length ? path.join('/') : parent;
62
+ }
53
63
  duplicateFolder() {
54
64
  if (null == this.dfolder) {
55
65
  this.dfolder =
@@ -1 +1 @@
1
- {"version":3,"file":"BuildContext.js","sourceRoot":"","sources":["../../src/build/BuildContext.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;AAS5B,+CAEsB;AAEtB,2CAEoB;AAapB,MAAM,EAAE,GAAG,eAAe,CAAA;AAG1B,4DAA4D;AAC5D,MAAM,YAAY;IAEhB,EAAE,CAAW;IACb,GAAG,CAAc;IAEjB,KAAK,CAAW;IAChB,EAAE,CAAa;IACf,KAAK,CAAO;IACZ,IAAI,CAAQ;IACZ,GAAG,CAAK;IACR,MAAM,CAAQ;IACd,OAAO,CASN;IACD,GAAG,CAGF;IAED,OAAO,CAAS;IAGhB,YACE,MAAc,EACd,QAAkB,EAClB,OAIC,EACD,EAAa,EACb,GAAiB;QAEjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QACZ,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QAEd,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,EAAE,GAAG,iCAAiC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;QACrE,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,EAAE,CAAA;QAEjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE;gBACP,IAAI,EAAE,SAAS,EAAE;aAClB;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS,EAAE;gBACjB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,EAAE;aACT;YACD,4BAA4B;YAC5B,IAAI,EAAE,SAAS,EAAE;YACjB,OAAO,EAAE,SAAS;SACnB,CAAA;QACD,IAAI,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAA;QAEpC,IAAI,CAAC,EAAE,GAAG,IAAI,yBAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAClD,IAAI,CAAC,KAAK,GAAG,IAAI,qBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACrC,CAAC;IAGD,OAAO,CAAC,IAAY,EAAE,IAAS;QAC7B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC5B,CAAC;IAGD,eAAe;QACb,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO;gBACV,mBAAI,CAAC,SAAS,CACZ,mBAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAA;QACtE,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF;AAUC,oCAAY;AAPd,SAAS,SAAS;IAChB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;AAC1D,CAAC"}
1
+ {"version":3,"file":"BuildContext.js","sourceRoot":"","sources":["../../src/build/BuildContext.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;AAS5B,+CAEsB;AAEtB,2CAEoB;AAapB,MAAM,EAAE,GAAG,eAAe,CAAA;AAG1B,4DAA4D;AAC5D,MAAM,YAAY;IAEhB,EAAE,CAAW;IACb,GAAG,CAAc;IAEjB,KAAK,CAAW;IAChB,EAAE,CAAa;IACf,KAAK,CAAO;IACZ,IAAI,CAAQ;IACZ,GAAG,CAAK;IACR,MAAM,CAAQ;IACd,OAAO,CASN;IACD,GAAG,CAGF;IAED,OAAO,CAAS;IAGhB,YACE,MAAc,EACd,QAAkB,EAClB,OAIC,EACD,EAAa,EACb,GAAiB;QAEjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QACZ,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QAEd,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,EAAE,GAAG,iCAAiC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;QACrE,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,EAAE,CAAA;QAEjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE;gBACP,IAAI,EAAE,SAAS,EAAE;aAClB;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS,EAAE;gBACjB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,EAAE;aACT;YACD,4BAA4B;YAC5B,IAAI,EAAE,SAAS,EAAE;YACjB,OAAO,EAAE,SAAS;SACnB,CAAA;QACD,IAAI,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAA;QAEpC,IAAI,CAAC,EAAE,GAAG,IAAI,yBAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAClD,IAAI,CAAC,KAAK,GAAG,IAAI,qBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACrC,CAAC;IAGD,OAAO,CAAC,IAAY,EAAE,IAAS;QAC7B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC5B,CAAC;IAGD,sEAAsE;IACtE,yEAAyE;IACzE,wEAAwE;IACxE,qEAAqE;IACrE,wEAAwE;IACxE,mDAAmD;IACnD,UAAU;QACR,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;QAC5C,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAClD,CAAC;IAGD,eAAe;QACb,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO;gBACV,mBAAI,CAAC,SAAS,CACZ,mBAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAA;QACtE,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF;QAUC,YAAY;AAPd,SAAS,SAAS;IAChB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;AAC1D,CAAC"}
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.BuildMeta = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const basic_1 = require("../util/basic");
9
+ // Log non-fatal weirdness.
10
+ const dlog = (0, basic_1.getdlog)('jostraca', __filename);
9
11
  // Handle loading, recording,and saving of build meta data
10
12
  class BuildMeta {
11
13
  fh;
@@ -58,10 +60,25 @@ function loadMetaData(fh, bmeta) {
58
60
  // directly and no longer re-join `this.folder`.
59
61
  const metapath = node_path_1.default.join(fh.folder, bmeta.foldername, bmeta.filename);
60
62
  if (fh.existsFile(metapath)) {
61
- const json = fh.loadJSON(metapath);
62
- bmeta.last = json.last;
63
- bmeta.hlast = json.hlast;
64
- bmeta.files = json.files;
63
+ try {
64
+ const json = fh.loadJSON(metapath);
65
+ bmeta.last = null == json?.last ? -1 : json.last;
66
+ bmeta.hlast = null == json?.hlast ? -1 : json.hlast;
67
+ bmeta.files = json?.files || {};
68
+ }
69
+ catch (err) {
70
+ // A truncated or hand-edited meta log used to throw straight out of
71
+ // the BuildMeta constructor, so a corrupt bookkeeping file blocked
72
+ // generation entirely — with a JSON parse error that says nothing
73
+ // about how to recover. The log is regenerated on every run, so
74
+ // starting from empty state is safe: the only cost is that merge
75
+ // baselines look absent for one run.
76
+ dlog('meta', 'unreadable meta log, continuing with empty state: ' +
77
+ metapath + ' err=' + err.message);
78
+ bmeta.last = -1;
79
+ bmeta.hlast = -1;
80
+ bmeta.files = {};
81
+ }
65
82
  }
66
83
  return bmeta;
67
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BuildMeta.js","sourceRoot":"","sources":["../../src/build/BuildMeta.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;AAI5B,yCAAwC;AAmBxC,0DAA0D;AAC1D,MAAM,SAAS;IAEb,EAAE,CAAa;IAEf,IAAI,CAAe;IACnB,IAAI,CAAe;IAEnB,YACE,EAAe;QAEf,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QAEZ,+DAA+D;QAC/D,IAAI,CAAC,IAAI,GAAG;YACV,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,mBAAmB;YAC7B,IAAI,EAAE,CAAC,CAAC;YACR,KAAK,EAAE,CAAC,CAAC;YACT,KAAK,EAAE,EAAE;SACV,CAAA;QAED,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAE5C,4BAA4B;QAE5B,IAAI,CAAC,IAAI,GAAG;YACV,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU;YAChC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAC5B,IAAI,EAAE,CAAC,CAAC;YACR,KAAK,EAAE,CAAC,CAAC;YACT,KAAK,EAAE,EAAE;SACV,CAAA;IAEH,CAAC;IAGD,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACvB,CAAC;IAGD,GAAG,CAAC,IAAY,EAAE,IAAS;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;QACrD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;IAC/B,CAAC;IAGD,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE1C,qBAAqB;QACrB,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,mBAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE;EACpF,IAAI,CAAC,IAAI,CAAC,QAAQ;;CAEnB,CAAC,CAAA;QACE,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;CACF;AA0BC,8BAAS;AAvBX,SAAS,YAAY,CAAC,EAAe,EAAE,KAAoB;IACzD,oEAAoE;IACpE,gDAAgD;IAChD,MAAM,QAAQ,GAAG,mBAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IACvE,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAClC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IAC1B,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAGD,SAAS,YAAY,CAAC,EAAe,EAAE,KAAoB;IACzD,oEAAoE;IACpE,gDAAgD;IAChD,MAAM,QAAQ,GAAG,mBAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IACvE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;AAC9B,CAAC"}
1
+ {"version":3,"file":"BuildMeta.js","sourceRoot":"","sources":["../../src/build/BuildMeta.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,7 @@ declare class FileHandler {
30
30
  unchanged: string[];
31
31
  };
32
32
  createdDirs: Set<string>;
33
+ savedPaths: Set<string>;
33
34
  constructor(bctx: BuildContext, existing: {
34
35
  txt: any;
35
36
  bin: any;
@@ -39,13 +40,15 @@ declare class FileHandler {
39
40
  version: boolean;
40
41
  });
41
42
  relative(path: string, whence?: string): string;
42
- save(path: string, newContentSource: string | Buffer, write?: boolean | string, whence?: string): void;
43
+ withinFolder(path: string): boolean;
44
+ saveBinary(path: string, newContentSource: string | Buffer, whence?: string): void;
45
+ save(path: string, newContentSource: string | Buffer, write?: boolean | string, whence?: string, mode?: number, isText?: boolean): void;
43
46
  copy(frompath: string, topath: string, write?: boolean | string, whence?: string): void;
44
- merge(editA: string, orig: string, editB: string, why: string[]): {
47
+ merge(generated: string, baseline: string, existing: string, why: string[]): {
45
48
  content: string;
46
49
  conflict: boolean;
47
50
  };
48
- diff(oldcontent: string, newcontent: string): string;
51
+ diff(generated: string, existing: string): string;
49
52
  existsFile(path: string, whence?: string): boolean;
50
53
  copyFile(frompath: string, topath: string, whence?: string): void;
51
54
  loadJSON(path: string, opts?: any | string, whence?: string): any;
@@ -53,8 +56,12 @@ declare class FileHandler {
53
56
  loadFile(path: string, opts?: any | string, whence?: string): string | Buffer;
54
57
  ensureFolder(path: string): void;
55
58
  private ensureDir;
59
+ private chmodUnchanged;
60
+ private writeFileAtomic;
56
61
  saveFile(path: string, content: string | Buffer, opts?: any | string, whence?: string): void;
57
62
  filelog(kind: string, path: string): void;
58
63
  }
64
+ declare function annotatedPath(target: string, kind: string): string;
65
+ declare function validName(name: any, kind: string, errmark: string): void;
59
66
  declare function validPath(path: string, maxdepth: number, errmark: string): void;
60
- export { validPath, FileHandler };
67
+ export { annotatedPath, validName, validPath, FileHandler };