@timeax/scaffold 0.0.3 → 0.0.5

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/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- # @timeax/scaffold
1
+ # Scaffold – `@timeax/scaffold`
2
2
 
3
3
  A tiny, opinionated scaffolding tool that keeps your project structure in sync with a **declarative tree** (like `structure.txt`) – Prisma‑style.
4
4
 
@@ -8,34 +8,30 @@ A tiny, opinionated scaffolding tool that keeps your project structure in sync w
8
8
  * Reverse‑engineer existing projects into `*.txt` structures.
9
9
  * Watch for changes and re‑apply automatically.
10
10
 
11
- > **Supported structure files:** `.tss`, `.stx`, `structure.txt`, and any `.txt` files inside `.scaffold/`.
11
+ > **Supported structure files:** `.tss`, `.stx`, `structure.txt` (and any `.txt` inside `.scaffold/`).
12
12
 
13
13
  ---
14
14
 
15
15
  ## Features
16
16
 
17
- * **Prisma‑style scaffold directory**: all config and structure lives under `.scaffold/` by default.
17
+ * **Prisma‑style scaffold directory**: all config and structures live under a hidden root, **`.scaffold/`**, by default.
18
18
  * **Config‑driven groups**: declare multiple roots (e.g. `app`, `frontend`) with their own structure files.
19
19
  * **Plain‑text structure files**: strict, easy‑to‑read tree syntax with indentation and annotations.
20
20
  * **Safe apply**:
21
21
 
22
- * Creates missing files/directories.
23
- * Tracks what it created in a cache.
24
- * Only auto‑deletes files it previously created.
25
- * Interactive delete for “large” files.
22
+ * Creates missing files/directories.
23
+ * Tracks what it created in a cache.
24
+ * Only auto‑deletes files it previously created.
25
+ * Interactive delete for “large” files.
26
26
  * **Hooks**:
27
27
 
28
- * Regular hooks around file create/delete.
29
- * Stub hooks around content generation.
28
+ * Regular hooks around file create/delete.
29
+ * Stub hooks around content generation.
30
30
  * **Stubs**: programmatic content generators for files (e.g. React pages, controllers, etc.).
31
31
  * **Watch mode**: watch `.scaffold/` for changes and re‑run automatically.
32
- * **Scanner**: generate `structure.txt` (or per‑group `*.txt`) from an existing codebase.
33
- * **VS Code integration** (via a companion extension):
34
-
35
- * Syntax highlighting for `.tss`, `.stx`, `structure.txt`, and `.scaffold/**/*.txt`.
36
- * Inline diagnostics using the same parser as the CLI.
37
- * “Go to file” from a structure line.
38
- * Simple formatting and sorting commands.
32
+ * **Scanner**: generate `*.tss`/`structure.txt` from an existing codebase.
33
+ * **AST + formatter**: loose/strict parser with diagnostics, plus a smart formatter that can *fix* simple indentation mistakes.
34
+ * **VS Code extension**: syntax highlighting, formatting, diagnostics, folding, hover info, “go to file”, and code actions.
39
35
 
40
36
  ---
41
37
 
@@ -73,7 +69,7 @@ This will create:
73
69
  If you want a different directory name:
74
70
 
75
71
  ```bash
76
- scaffold init --dir tools/scaffold
72
+ scaffold init --dir tools/.scaffold
77
73
  ```
78
74
 
79
75
  > Use `--force` to overwrite existing config/structure files.
@@ -106,16 +102,13 @@ README.md
106
102
 
107
103
  **Rules:**
108
104
 
109
- * Indent with **2 spaces per level** (strict by default; configurable).
105
+ * Indent with **2 spaces per level** by default (configurable via `indentStep`).
110
106
  * Directories **must** end with `/`.
111
107
  * Files **must not** end with `/`.
112
- * You **cannot indent under a file** (files cannot have children).
113
- * You can’t “skip” levels (no jumping from depth 0 to depth 2 in one go).
114
- * Lines starting with `#` or `//` (after indentation) are comments.
115
- * Inline comments are supported:
116
-
117
- * `index.ts # comment`
118
- * `index.ts // comment`
108
+ * You **cannot indent under a file** (files cannot have children) – in strict mode this is an error, in loose mode you get a diagnostic.
109
+ * You can’t “skip” levels (no jumping from depth 0 straight to depth 2 in one go).
110
+ * Lines starting with `#` or `//` are treated as comments.
111
+ * Inline comments are supported: `index.ts # comment`, `index.ts // comment`.
119
112
 
120
113
  #### Annotations
121
114
 
@@ -136,20 +129,23 @@ Supported inline annotations:
136
129
 
137
130
  These map onto the `StructureEntry` fields in TypeScript.
138
131
 
139
- > `:` is reserved for annotations (e.g. `@stub:page`). Paths themselves must **not** contain `:`.
140
-
141
132
  ---
142
133
 
143
- ### 3. Configure groups (optional but recommended)
134
+ ## Config: groups, base, and indent
144
135
 
145
- In `.scaffold/config.ts` you can enable grouped mode:
136
+ In `.scaffold/config.ts` you can enable grouped mode and control the base root + indent step:
146
137
 
147
138
  ```ts
148
139
  import type { ScaffoldConfig } from '@timeax/scaffold';
149
140
 
150
141
  const config: ScaffoldConfig = {
151
- base: '.', // project root (optional, defaults to cwd)
142
+ // Project root (defaults to cwd if omitted)
143
+ base: '.',
144
+
145
+ // Indent step in spaces (must match your `.tss`/`structure.txt`)
146
+ indentStep: 2,
152
147
 
148
+ // Optional: grouped mode
153
149
  groups: [
154
150
  { name: 'app', root: 'app', structureFile: 'app.txt' },
155
151
  { name: 'frontend', root: 'resources/js', structureFile: 'frontend.txt' },
@@ -176,11 +172,11 @@ src/
176
172
  home.tsx
177
173
  ```
178
174
 
179
- > When `groups` is defined and non‑empty, single‑root `structure` / `structureFile` is ignored.
175
+ > When `groups` is defined and non‑empty, single‑root `structure`/`structureFile` is ignored.
180
176
 
181
177
  ---
182
178
 
183
- ### 4. Run scaffold
179
+ ## Running scaffold
184
180
 
185
181
  ```bash
186
182
  # single run
@@ -195,11 +191,11 @@ What happens:
195
191
  * Config is loaded from `.scaffold/config.*` (Prisma‑style resolution).
196
192
  * Structure(s) are resolved (grouped or single‑root).
197
193
  * Files/directories missing on disk are created.
198
- * New files are registered in `.scaffold-cache.json` (under project root by default).
194
+ * New files are registered in a cache file (default: `.scaffold-cache.json` under project root).
199
195
  * Any previously created files that are no longer in the structure are candidates for deletion:
200
196
 
201
- * Small files are deleted automatically.
202
- * Large files (configurable threshold) trigger an interactive prompt.
197
+ * Small files are deleted automatically.
198
+ * Large files (configurable threshold) trigger an interactive prompt.
203
199
 
204
200
  ### Watch mode
205
201
 
@@ -209,8 +205,8 @@ scaffold --watch
209
205
 
210
206
  * Watches:
211
207
 
212
- * `.scaffold/config.*`
213
- * `.scaffold/*.txt`
208
+ * `.scaffold/config.*`
209
+ * `.scaffold/*.txt` / `.scaffold/*.tss` / `.scaffold/*.stx`
214
210
  * Debounces rapid edits.
215
211
  * Prevents overlapping runs.
216
212
 
@@ -251,11 +247,11 @@ Options:
251
247
 
252
248
  ### `scaffold scan`
253
249
 
254
- Generate `structure.txt`‑style definitions from an existing project.
250
+ Generate `*.tss`/`structure.txt`‑style definitions from an existing project.
255
251
 
256
252
  Two modes:
257
253
 
258
- #### 1. Config‑aware mode (default if no `--root` / `--out` given)
254
+ #### 1. Config‑aware mode (default if no `--root` / `--out`)
259
255
 
260
256
  ```bash
261
257
  scaffold scan
@@ -266,8 +262,8 @@ scaffold scan --from-config --groups app frontend
266
262
  * Loads `.scaffold/config.ts`.
267
263
  * For each `group` in config:
268
264
 
269
- * Scans `group.root` on disk.
270
- * Writes to `.scaffold/<group.structureFile || group.name + '.txt'>`.
265
+ * Scans `group.root` on disk.
266
+ * Writes to `.scaffold/<group.structureFile || group.name + '.txt'>`.
271
267
  * `--groups` filters which groups to scan.
272
268
 
273
269
  #### 2. Manual mode (single root)
@@ -298,12 +294,12 @@ What it does:
298
294
  * Loads `.scaffold/config.*`.
299
295
  * Determines which structure files are expected:
300
296
 
301
- * **Grouped mode** (`config.groups` defined): each group gets `group.structureFile || `${group.name}.txt``.
302
- * **Single-root mode** (no groups): uses `config.structureFile || 'structure.txt'`.
297
+ * **Grouped mode** (`config.groups` defined): each group gets `group.structureFile || \`${group.name}.txt``.
298
+ * **Single-root mode** (no groups): uses `config.structureFile || 'structure.txt'`.
303
299
  * For each expected structure file:
304
300
 
305
- * If it **already exists** → it is left untouched.
306
- * If it is **missing** → it is created with a small header comment.
301
+ * If it **already exists** → it is left untouched.
302
+ * If it is **missing** → it is created with a small header comment.
307
303
 
308
304
  Examples:
309
305
 
@@ -365,7 +361,100 @@ await writeScannedStructuresFromConfig(process.cwd(), {
365
361
 
366
362
  ---
367
363
 
368
- ## Hooks & stubs (high‑level overview)
364
+ ## AST & Formatter
365
+
366
+ `@timeax/scaffold` exposes an AST parser and formatter from a dedicated subpath:
367
+
368
+ ```ts
369
+ import { parseStructureAst, formatStructureText } from '@timeax/scaffold/ast';
370
+ ```
371
+
372
+ ### `parseStructureAst(text, options)`
373
+
374
+ Parses a structure file into a loose or strict AST with diagnostics:
375
+
376
+ ```ts
377
+ const ast = parseStructureAst(text, {
378
+ indentStep: 2, // must match your structure files
379
+ mode: 'loose', // 'loose' | 'strict'
380
+ });
381
+ ```
382
+
383
+ Return shape (simplified):
384
+
385
+ ```ts
386
+ interface StructureAstNode {
387
+ type: 'dir' | 'file';
388
+ name: string;
389
+ path?: string; // normalized POSIX path (no trailing slash for files)
390
+ line?: number; // 1-based source line
391
+ indentLevel?: number; // 0,1,2,...
392
+ stub?: string;
393
+ include?: string[];
394
+ exclude?: string[];
395
+ children?: StructureAstNode[];
396
+ }
397
+
398
+ interface StructureDiagnostic {
399
+ code:
400
+ | 'indent-misaligned'
401
+ | 'indent-skip-level'
402
+ | 'child-of-file-loose'
403
+ | 'path-colon'
404
+ | 'unknown';
405
+ message: string;
406
+ line: number; // 1-based
407
+ severity: 'info' | 'warning' | 'error';
408
+ }
409
+
410
+ interface StructureAst {
411
+ rootNodes: StructureAstNode[];
412
+ indentStep: number;
413
+ mode: 'loose' | 'strict';
414
+ diagnostics: StructureDiagnostic[];
415
+ }
416
+ ```
417
+
418
+ **Loose mode** tries to recover from small mistakes (over‑indent, under‑indent) and reports them as diagnostics instead of throwing.
419
+
420
+ **Strict mode** is closer to the CLI parser and may reject invalid indentation entirely.
421
+
422
+ Typical diagnostics:
423
+
424
+ * `indent-misaligned` – indent is not a multiple of `indentStep`.
425
+ * `indent-skip-level` – you jumped more than one level at once.
426
+ * `child-of-file-loose` – a line is indented under a file.
427
+ * `path-colon` – path token contains a colon (`:`), which is reserved for annotations.
428
+
429
+ ### `formatStructureText(text, options)`
430
+
431
+ Smart formatter that:
432
+
433
+ * Normalizes line endings and trailing whitespace.
434
+ * Re‑indents entries to canonical multiples of `indentStep`.
435
+ * Fixes common over‑indent/under‑indent issues in **loose mode**.
436
+ * Preserves:
437
+
438
+ * Blank lines.
439
+ * Full‑line comments (`#`, `//`).
440
+ * Inline comments and annotations (keeps them attached to their entries).
441
+
442
+ ```ts
443
+ const { text: formatted, ast, diagnostics } = formatStructureText(input, {
444
+ indentStep: 2,
445
+ mode: 'loose', // 'loose' is recommended for editor integrations
446
+ });
447
+ ```
448
+
449
+ `formatStructureText` reuses the same AST model and diagnostics, so you can:
450
+
451
+ * Run it in an editor (e.g. VS Code) for formatting.
452
+ * Show the diagnostics in a side panel or gutter.
453
+ * Still feed the formatted text back into the strict CLI parser later.
454
+
455
+ ---
456
+
457
+ ## Hooks & Stubs (high‑level overview)
369
458
 
370
459
  ### Regular hooks
371
460
 
@@ -406,6 +495,7 @@ Each receives a `HookContext` with fields like:
406
495
  * `absolutePath`
407
496
  * `isDirectory`
408
497
  * `stubName?`
498
+ * `group?`
409
499
 
410
500
  ### Stubs
411
501
 
@@ -434,6 +524,7 @@ const config: ScaffoldConfig = {
434
524
  },
435
525
  },
436
526
  ],
527
+ postStub: [],
437
528
  },
438
529
  },
439
530
  },
@@ -453,110 +544,124 @@ Any file in `pages/` without an explicit stub inherits `@stub:page` from the par
453
544
 
454
545
  ---
455
546
 
456
- ## Cache & safety
547
+ ## Cache & Safety
457
548
 
458
549
  * Cache file (default): `.scaffold-cache.json` under project root (configurable via `cacheFile`).
459
550
  * Every file created by scaffold is recorded with:
460
551
 
461
- * project‑relative path
462
- * created time
463
- * size at creation
464
- * stub name (if any)
465
- * group metadata
552
+ * project‑relative path
553
+ * created time
554
+ * size at creation
555
+ * stub name (if any)
556
+ * group metadata
466
557
  * On each run, scaffold compares the **desired structure** vs. **cached entries**:
467
558
 
468
- * If a cached file is no longer in the structure and still exists → deletion candidate.
469
- * If its size exceeds `sizePromptThreshold` (configurable) and the CLI is interactive → prompt the user.
470
- * If the user chooses “keep”, the file is left on disk and removed from the cache (user now owns it).
559
+ * If a cached file is no longer in the structure and still exists → deletion candidate.
560
+ * If its size exceeds `sizePromptThreshold` (configurable) and the CLI is interactive → prompt the user.
561
+ * If the user chooses “keep”, the file is left on disk and removed from the cache (user now owns it).
471
562
 
472
563
  This keeps scaffolding **idempotent** and avoids reckless deletes.
473
564
 
474
565
  ---
475
566
 
476
- ## Roadmap / Ideas
567
+ ## VS Code Extension
477
568
 
478
- Some things this package is intentionally designed to grow into:
569
+ There is a companion VS Code extension that makes editing scaffold files much nicer.
479
570
 
480
- * Richer annotations in `*.txt` (e.g. per‑entry hooks, metadata aliases).
481
- * Stub groups (one logical stub creating multiple files).
482
- * Built‑in templates for common stacks (Laravel + Inertia, Next.js, etc.).
483
- * Better diff/dry‑run UX (show what will change without touching disk).
484
- * Deeper VS Code integration:
571
+ ### Language & files
485
572
 
486
- * Tree-aware sorting.
487
- * Visual tree editor.
488
- * Code actions / quick fixes for common mistakes.
573
+ * Registers a `scaffold-structure` language.
574
+ * Treats as scaffold structure files:
489
575
 
490
- PRs and ideas are welcome ✨
576
+ * `.tss`
577
+ * `.stx`
578
+ * `structure.txt`
579
+ * Any `.txt` inside `.scaffold/` (configurable in the extension’s `package.json`).
491
580
 
492
- ---
581
+ ### Syntax highlighting
582
+
583
+ * Tree‑like syntax with clear highlighting for:
584
+
585
+ * Directories vs files.
586
+ * Annotations (`@stub:`, `@include:`, `@exclude:`).
587
+ * Comments and inline comments.
588
+
589
+ ### Formatting
493
590
 
494
- ## VS Code extension
591
+ * Command: **“Scaffold: Format structure”** (`timeax-scaffold.formatStructure`).
592
+ * Uses `formatStructureText` from `@timeax/scaffold/ast`:
495
593
 
496
- There is an official VS Code companion extension for `@timeax/scaffold` that makes working with your structure files much nicer.
594
+ * Normalizes indentation to `indentStep`.
595
+ * Fixes simple over/under‑indents in loose mode.
596
+ * Preserves blank lines and comments.
497
597
 
498
- ### Language support
598
+ You can wire this as the default formatter for `scaffold-structure` files via your VS Code settings.
499
599
 
500
- The extension adds a custom language **Scaffold Structure** and:
600
+ ### Sorting
501
601
 
502
- * Highlights:
602
+ * Command: **“Scaffold: Sort entries”** (`timeax-scaffold.sortEntries`).
603
+ * Sorts non‑comment lines lexicographically while preserving comment/blank line positions.
503
604
 
504
- * Directories (lines ending with `/`)
505
- * Files
506
- * Inline annotations like `@stub:name`, `@include:pattern`, `@exclude:pattern`
507
- * Comments using `#` or `//` (full-line and inline)
508
- * Treats the following files as scaffold structures:
605
+ ### Diagnostics
509
606
 
510
- * `*.tss`
511
- * `*.stx`
512
- * `structure.txt`
513
- * Any `.txt` file inside your `.scaffold/` directory
607
+ * Live diagnostics (squiggles) using `parseStructureAst`:
514
608
 
515
- The syntax rules match the CLI parser:
609
+ * `indent-misaligned`, `indent-skip-level`, `child-of-file-loose`, `path-colon`, etc.
610
+ * Diagnostics update on open and on change.
516
611
 
517
- * Indent is in fixed steps (configurable via `indentStep`).
518
- * Only directories can have children.
519
- * `:` is reserved for annotations and not allowed inside path names.
612
+ ### Folding
520
613
 
521
- ### Editor commands
614
+ * Folding regions for directories based on AST:
522
615
 
523
- The extension contributes several commands (available via the Command Palette and context menus when editing a scaffold structure file):
616
+ * Collapse an entire subtree under a dir.
524
617
 
525
- * **Scaffold: Go to file**
618
+ ### Hover
526
619
 
527
- * Reads the path on the current line and opens the corresponding file in your project.
528
- * Respects `.scaffold/config.*`:
620
+ * Hovering an entry shows:
529
621
 
530
- * Uses `base`/`root` to resolve paths.
531
- * If the current structure file belongs to a `group`, it resolves relative to that group’s `root`.
532
- * If the file doesn’t exist, it can prompt to create it and open it immediately.
622
+ * Kind (dir/file).
623
+ * Effective `path`.
624
+ * Stub / include / exclude.
625
+ * Resolved absolute path based on `base` + group root.
533
626
 
534
- * **Scaffold: Format structure file**
627
+ ### “Go to file
535
628
 
536
- * Normalizes line endings and trims trailing whitespace.
537
- * Designed to be safe even on partially-invalid files.
538
- * Future versions may use the full AST from `@timeax/scaffold` to enforce indentation and ordering.
629
+ * Command: **“Scaffold: Go to file”** (`timeax-scaffold.openTargetFile`).
630
+ * On a file line:
539
631
 
540
- * **Scaffold: Sort entries**
632
+ * Resolves the project base (from `.scaffold/config.*`, `base`, and group `root`).
633
+ * Opens the target file if it exists.
634
+ * If it doesn’t exist, you can create it on the spot.
541
635
 
542
- * Naive helper that sorts non-comment lines lexicographically while keeping comment/blank lines in place.
543
- * Useful for quick cleanups of small structure files.
636
+ ### Code actions
544
637
 
545
- * **Scaffold: Open config**
638
+ Source actions exposed in the light‑bulb menu for structure files:
546
639
 
547
- * Opens `.scaffold/config.*` for the current workspace (searching common extensions like `config.ts`, `config.mts`, etc.).
640
+ * **“Scaffold: Ensure structure files exist (scaffold structures)”**
548
641
 
549
- * **Scaffold: Open .scaffold folder**
642
+ * Runs `npx scaffold structures` in a workspace terminal.
643
+ * **“Scaffold: Apply structure to project (scaffold)”**
550
644
 
551
- * Reveals the `.scaffold/` directory in the VS Code Explorer.
645
+ * Runs `npx scaffold` in a workspace terminal.
552
646
 
553
- ### Live validation (diagnostics)
647
+ ### Status bar integration
554
648
 
555
- Whenever you open or edit a scaffold structure file:
649
+ * Status bar item (left side) shows current scaffold context:
556
650
 
557
- * The extension calls `parseStructureText` from `@timeax/scaffold` under the hood.
558
- * If parsing fails, the error message (e.g. invalid indentation, children under a file, bad path, etc.) is shown as an editor diagnostic (squiggly underline) on the relevant line.
651
+ * `Scaffold: frontend (resources/js)` when editing `.scaffold/frontend.txt`.
652
+ * `Scaffold: single root` when in single‑root mode.
653
+ * Tooltip shows the resolved base root path.
559
654
 
560
- This means your editor and the CLI always agree on what is valid, since they share the same parser and rules.
655
+ ---
656
+
657
+ ## Roadmap / Ideas
561
658
 
562
- > The extension is optional, but highly recommended if you edit `*.tss` / `*.stx` or `structure.txt` files frequently.
659
+ Some things this package is intentionally designed to grow into:
660
+
661
+ * Richer annotations in `*.tss` (e.g. per‑entry hooks, metadata aliases).
662
+ * Stub groups (one logical stub creating multiple files at once).
663
+ * Built‑in templates for common stacks (Laravel + Inertia, Next.js, etc.).
664
+ * Better diff/dry‑run UX (show what will change without touching disk).
665
+ * Deeper editor integrations (per‑group commands, quick‑fixes for diagnostics, etc.).
666
+
667
+ PRs and ideas are welcome ✨