@conformetry/generation 0.0.39 → 0.0.41

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/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## 0.0.41 (2026-10-18)
2
+
3
+ ### 🧱 Updated Dependencies
4
+
5
+ - Updated conformetry-configuration to 0.0.41
6
+ - Updated conformetry-core to 0.0.40
7
+
8
+ ## 0.0.40 (2026-10-18)
9
+
10
+ ### 🚀 Features
11
+
12
+ - ⚠️ **conformetry:** ✨ expose the filesystem and configuration directory to hooks ([#1695](https://github.com/organizzolini/codebase/pull/1695), [#1679](https://github.com/organizzolini/codebase/issues/1679))
13
+
14
+ ### ⚠️ Breaking Changes
15
+
16
+ - **conformetry:** ✨ expose the filesystem and configuration directory to hooks ([#1695](https://github.com/organizzolini/codebase/pull/1695), [#1679](https://github.com/organizzolini/codebase/issues/1679))
17
+ `GenerationService.runGenerator` in the published
18
+ `@conformetry/generation` now requires `configurationDirectoryPath`, so
19
+ an embedder calling it must pass the absolute directory of its
20
+ configuration file; the title carries `!` so the squash releases it as
21
+ such.
22
+ - This is stacked on #1693.
23
+
24
+ ### 🧱 Updated Dependencies
25
+
26
+ - Updated conformetry-configuration to 0.0.40
27
+ - Updated conformetry-core to 0.0.39
28
+
1
29
  ## 0.0.39 (2026-10-18)
2
30
 
3
31
  ### 💅 Refactors
package/README.md CHANGED
@@ -16,6 +16,7 @@ npm install --save-dev @conformetry/generation
16
16
  import { GenerationService } from "@conformetry/generation";
17
17
 
18
18
  const result = await generationService.runGenerator({
19
+ configurationDirectoryPath: "/workspace/configuration",
19
20
  definition: {
20
21
  name: "nestjs-service-module",
21
22
  templateDirectoryPath: "templates/nestjs-service-module",
@@ -27,7 +28,131 @@ const result = await generationService.runGenerator({
27
28
  ```
28
29
 
29
30
  The lifecycle is `preGenerate` → render → `postGenerate` → format, and the
30
- returned file paths are sorted.
31
+ rendered file paths are sorted. A path a hook pushes onto the list is appended
32
+ after the sort.
33
+
34
+ ## Hooks
35
+
36
+ A generator may declare two functions that run around generation. The
37
+ [hooks example](../conformetry-examples/examples/hooks/README.md) runs both.
38
+
39
+ | Hook | Runs | `generatedFilePaths` holds |
40
+ | -------------- | ------------------------------------------------- | ----------------------------- |
41
+ | `preGenerate` | Before anything is written | Nothing: it is an empty array |
42
+ | `postGenerate` | After the template is rendered, before formatting | Every rendered path, sorted |
43
+
44
+ Each hook receives one `GeneratorHookContext`, may be sync or async, and is
45
+ awaited. Hooks are plain functions, so a configuration cannot declare them in
46
+ JSON, which cannot carry a function; any JavaScript or TypeScript configuration
47
+ can. Import the types from
48
+ `@conformetry/configuration`, which re-exports `GeneratorHook`,
49
+ `GeneratorHookContext`, and `GeneratorHooks` from `@conformetry/core`; a
50
+ configuration needs no other dependency to type its hooks.
51
+
52
+ ```ts
53
+ import path from "node:path";
54
+
55
+ import type {
56
+ ConformetryConfiguration,
57
+ GeneratorHookContext,
58
+ } from "@conformetry/configuration";
59
+
60
+ const configuration: ConformetryConfiguration = [
61
+ {
62
+ hooks: {
63
+ postGenerate: async (context: GeneratorHookContext): Promise<void> => {
64
+ const receiptPath = path.join(
65
+ context.configurationDirectoryPath,
66
+ "receipts",
67
+ `${context.substitutions["nameKebabCase"] ?? ""}.log`,
68
+ );
69
+
70
+ await context.filesystem.writeFile(receiptPath, "generated\n");
71
+ context.generatedFilePaths.push(receiptPath);
72
+ },
73
+ },
74
+ name: "receipt",
75
+ templatePath: "templates/receipt",
76
+ },
77
+ ];
78
+
79
+ export default configuration;
80
+ ```
81
+
82
+ ### The context
83
+
84
+ | Field | What it holds |
85
+ | ---------------------------- | ---------------------------------------------------------------------------- |
86
+ | `definition` | The generator's runtime definition: `name`, `templateDirectoryPath`, `hooks` |
87
+ | `input` | The input values the run was given, by input name |
88
+ | `substitutions` | What the template renders with: the name variants, then inputs over them |
89
+ | `outputDirectoryPath` | Where the run writes, exactly as the host passed it |
90
+ | `generatedFilePaths` | The list described above. It is mutable, and what the formatter receives |
91
+ | `filesystem` | The adapter the run itself reads and writes through |
92
+ | `configurationDirectoryPath` | Absolute directory of the configuration file that declared the generator |
93
+
94
+ Paths differ by host:
95
+
96
+ - **`outputDirectoryPath` and `generatedFilePaths`** are whatever the host
97
+ passed. Under the command line that is the form `--directory` was given in,
98
+ so possibly relative. Under Nx they are absolute.
99
+ - **`filesystem` takes absolute paths only.** Build them from
100
+ `configurationDirectoryPath`, never from the working directory, so a hook
101
+ behaves the same however the command was started.
102
+ - **`configurationDirectoryPath` is absolute under both hosts**, taken from
103
+ the file that was actually loaded.
104
+ - **`definition.templateDirectoryPath`** is the configuration's `templatePath`
105
+ as written, so workspace-relative, under the command line. Under Nx it is
106
+ resolved against the workspace root, so absolute.
107
+
108
+ There is no `replaceInFile` helper. A hook that edits a rendered file reads it
109
+ with `filesystem.readFile`, changes the text, and writes it back.
110
+
111
+ ### Writing from a hook
112
+
113
+ Write through `context.filesystem`, not `node:fs`. Under the command line the
114
+ adapter writes to disk. Under Nx it writes to the generator's virtual tree, so
115
+ the write appears in the change list, `--dry-run` previews it, and nothing
116
+ reaches disk until Nx flushes. A write made with `node:fs` skips the tree: it
117
+ lands immediately, even under `--dry-run`, and stays when the run later fails.
118
+
119
+ **Write only inside the workspace.** The Nx adapter maps an absolute path
120
+ inside the workspace to a tree path. It cannot do that for a path outside, so
121
+ the write is silently re-rooted inside the workspace instead, while a read of
122
+ the same path falls back to disk. Build every path from
123
+ `configurationDirectoryPath`.
124
+
125
+ `makeDirectory` does nothing under Nx; writing a file creates its directories.
126
+ Reads through the Nx adapter look in the tree first, so a hook sees files
127
+ rendered earlier in the same run.
128
+
129
+ ### The format list
130
+
131
+ The formatter receives `context.generatedFilePaths` after `postGenerate`. A
132
+ file the hook wrote is not on that list, so push its path to have it formatted:
133
+
134
+ ```ts
135
+ context.generatedFilePaths.push(receiptPath);
136
+ ```
137
+
138
+ The command-line host formats nothing by default, so the list there only
139
+ decides what the run reports. The Nx host formats every file changed in the
140
+ tree, hook writes included, whether or not it was pushed; push it anyway so
141
+ both hosts report the same files.
142
+
143
+ ### When a hook throws
144
+
145
+ The run stops at the throw and the error propagates.
146
+
147
+ | Host | `preGenerate` throws | `postGenerate` throws |
148
+ | ------------ | --------------------- | --------------------------------------------------------------------------- |
149
+ | Command line | Nothing was written | The rendered files **stay on disk**; the formatter does not run |
150
+ | Nx | The tree is untouched | Rendered files sit in the tree, and `nx g` flushes nothing for a failed run |
151
+
152
+ The command line prints a hook's error with its stack trace and exits non-zero.
153
+ It does not say which hook threw, so put the generator and hook name in the
154
+ message. Under Nx the run is atomic on disk for both hooks, but only for writes
155
+ made through `context.filesystem`.
31
156
 
32
157
  ## Rendering
33
158
 
@@ -74,9 +199,9 @@ the reason above.
74
199
 
75
200
  ## Exports
76
201
 
77
- `GenerationService`, `RenderingService`, their modules, and the
78
- `FileSystemAdapter`, `FormatterAdapter`, `GeneratorDefinition`, and
79
- `Substitutions` types.
202
+ `GenerationService`, `GenerationModule`, and the `DirectoryEntry`,
203
+ `FileSystemAdapter`, `FormatterAdapter`, and `GeneratorDefinition` types. The
204
+ hook types are re-exported for configurations from `@conformetry/configuration`.
80
205
 
81
206
  ## Test
82
207
 
@@ -130,7 +255,7 @@ What this project is judged against, as declared in its own `callidescope.config
130
255
 
131
256
  | Callable | Breadth | Calls directly | Location |
132
257
  | --- | --- | --- | --- |
133
- | `GenerationService.runGenerator` | 4 | `GenerationService.resolveAdapters`, `GenerationService.normalizeInputs`, `GenerationService.buildSubstitutions`, `GenerationService.renderDirectory` | `projects/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:192` |
258
+ | `GenerationService.runGenerator` | 4 | `GenerationService.resolveAdapters`, `GenerationService.normalizeInputs`, `GenerationService.buildSubstitutions`, `GenerationService.renderDirectory` | `projects/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:193` |
134
259
  | `GenerationService.renderDirectory` | 2 | `ConfigurationService.renderPath`, `GenerationService.renderFile` | `projects/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:106` |
135
260
  | `GenerationService.listDirectory` | 1 | `GenerationService.map(…)` | `projects/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:40` |
136
261
 
@@ -229,14 +354,14 @@ graph LR
229
354
 
230
355
  ### Project
231
356
 
232
- ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-748-22c55e?style=flat-square)
233
- ![Repository Size](https://img.shields.io/badge/Repository_Size-38.83_kB-6b7280?style=flat-square)
357
+ ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-932-22c55e?style=flat-square)
358
+ ![Repository Size](https://img.shields.io/badge/Repository_Size-46.22_kB-6b7280?style=flat-square)
234
359
  ![Folders](https://img.shields.io/badge/Folders-4-4a4a4a?style=flat-square)
235
360
  ![Source Files](https://img.shields.io/badge/Source_Files-15-3178c6?style=flat-square)
236
361
 
237
362
  ### Measured Targets
238
363
 
239
- ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-1.45_kB_gzip-6b7280?style=flat-square)
364
+ ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-1.47_kB_gzip-6b7280?style=flat-square)
240
365
 
241
366
  ### TypeScript
242
367
 
@@ -245,7 +370,7 @@ graph LR
245
370
  ![Generic Declarations](https://img.shields.io/badge/Generic_Declarations-0-0369a1?style=flat-square)
246
371
  ![Enums](https://img.shields.io/badge/Enums-0-f97316?style=flat-square)
247
372
  ![Decorators](https://img.shields.io/badge/Decorators-2-db2777?style=flat-square)
248
- ![Doc Comments](https://img.shields.io/badge/Doc_Comments-17-6366f1?style=flat-square)
373
+ ![Doc Comments](https://img.shields.io/badge/Doc_Comments-18-6366f1?style=flat-square)
249
374
  ![Static Methods](https://img.shields.io/badge/Static_Methods-0-166534?style=flat-square)
250
375
 
251
376
  ### JavaScript
@@ -254,15 +379,15 @@ graph LR
254
379
  ![Test Files](https://img.shields.io/badge/Test_Files-2-10b981?style=flat-square)
255
380
  ![External Packages](https://img.shields.io/badge/External_Packages-10-8b5cf6?style=flat-square)
256
381
  ![Classes](https://img.shields.io/badge/Classes-4-7c3aed?style=flat-square)
257
- ![Functions](https://img.shields.io/badge/Functions-22-16a34a?style=flat-square)
382
+ ![Functions](https://img.shields.io/badge/Functions-34-16a34a?style=flat-square)
258
383
  ![Methods](https://img.shields.io/badge/Methods-19-15803d?style=flat-square)
259
- ![Sync Functions](https://img.shields.io/badge/Sync_Functions-22-4ade80?style=flat-square)
260
- ![Async Functions](https://img.shields.io/badge/Async_Functions-19-059669?style=flat-square)
261
- ![Constants](https://img.shields.io/badge/Constants-33-dc2626?style=flat-square)
384
+ ![Sync Functions](https://img.shields.io/badge/Sync_Functions-28-4ade80?style=flat-square)
385
+ ![Async Functions](https://img.shields.io/badge/Async_Functions-25-059669?style=flat-square)
386
+ ![Constants](https://img.shields.io/badge/Constants-46-dc2626?style=flat-square)
262
387
  ![Imports](https://img.shields.io/badge/Imports-35-0284c7?style=flat-square)
263
388
  ![Exported Symbols](https://img.shields.io/badge/Exported_Symbols-7-ea580c?style=flat-square)
264
- ![Comments](https://img.shields.io/badge/Comments-33-64748b?style=flat-square)
265
- ![Comment Lines](https://img.shields.io/badge/Comment_Lines-93-475569?style=flat-square)
389
+ ![Comments](https://img.shields.io/badge/Comments-34-64748b?style=flat-square)
390
+ ![Comment Lines](https://img.shields.io/badge/Comment_Lines-99-475569?style=flat-square)
266
391
  ![TODO Comments](https://img.shields.io/badge/TODO_Comments-0-ca8a04?style=flat-square)
267
392
 
268
393
  ### Python
@@ -418,23 +543,23 @@ graph LR
418
543
  ### Markdown
419
544
 
420
545
  ![Markdown Files](https://img.shields.io/badge/Markdown_Files-2-083fa1?style=flat-square)
421
- ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-452-1f6feb?style=flat-square)
546
+ ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-480-1f6feb?style=flat-square)
422
547
  ![H1](https://img.shields.io/badge/H1-1-7c3aed?style=flat-square)
423
- ![H2](https://img.shields.io/badge/H2-42-8b5cf6?style=flat-square)
424
- ![H3](https://img.shields.io/badge/H3-52-a78bfa?style=flat-square)
548
+ ![H2](https://img.shields.io/badge/H2-44-8b5cf6?style=flat-square)
549
+ ![H3](https://img.shields.io/badge/H3-56-a78bfa?style=flat-square)
425
550
  ![H4](https://img.shields.io/badge/H4-0-c4b5fd?style=flat-square)
426
551
  ![H5](https://img.shields.io/badge/H5-0-ddd6fe?style=flat-square)
427
552
  ![H6](https://img.shields.io/badge/H6-0-ede9fe?style=flat-square)
428
- ![Paragraphs](https://img.shields.io/badge/Paragraphs-86-64748b?style=flat-square)
429
- ![Lists](https://img.shields.io/badge/Lists-46-16a34a?style=flat-square)
430
- ![List Items](https://img.shields.io/badge/List_Items-66-22c55e?style=flat-square)
553
+ ![Paragraphs](https://img.shields.io/badge/Paragraphs-93-64748b?style=flat-square)
554
+ ![Lists](https://img.shields.io/badge/Lists-51-16a34a?style=flat-square)
555
+ ![List Items](https://img.shields.io/badge/List_Items-73-22c55e?style=flat-square)
431
556
  ![Task List Items](https://img.shields.io/badge/Task_List_Items-0-4ade80?style=flat-square)
432
557
  ![Tables](https://img.shields.io/badge/Tables-2-0284c7?style=flat-square)
433
558
  ![Table Rows](https://img.shields.io/badge/Table_Rows-10-0ea5e9?style=flat-square)
434
- ![Links](https://img.shields.io/badge/Links-15-059669?style=flat-square)
559
+ ![Links](https://img.shields.io/badge/Links-19-059669?style=flat-square)
435
560
  ![Images](https://img.shields.io/badge/Images-0-10b981?style=flat-square)
436
561
  ![Code Blocks](https://img.shields.io/badge/Code_Blocks-11-dc2626?style=flat-square)
437
- ![Inline Code](https://img.shields.io/badge/Inline_Code-72-ef4444?style=flat-square)
562
+ ![Inline Code](https://img.shields.io/badge/Inline_Code-76-ef4444?style=flat-square)
438
563
  ![Block Quotes](https://img.shields.io/badge/Block_Quotes-0-ca8a04?style=flat-square)
439
564
  ![Thematic Breaks](https://img.shields.io/badge/Thematic_Breaks-0-a16207?style=flat-square)
440
565
  <!-- codometer:end -->
@@ -58,7 +58,8 @@ export declare class GenerationService {
58
58
  private resolveAdapters;
59
59
  /**
60
60
  * Runs the generator lifecycle — `preGenerate`, render, `postGenerate`,
61
- * format — and returns the sorted list of generated file paths.
61
+ * format — and returns the generated file paths: the rendered paths sorted,
62
+ * then any path a `postGenerate` hook pushed, appended after the sort.
62
63
  */
63
64
  runGenerator(args: RunGeneratorArguments): Promise<RunGeneratorResult>;
64
65
  }
@@ -69,6 +70,13 @@ export { GeneratorDefinition }
69
70
  * Arguments accepted by the runtime runner.
70
71
  */
71
72
  declare interface RunGeneratorArguments {
73
+ /**
74
+ * Absolute directory of the configuration file that declared the generator.
75
+ *
76
+ * Handed to the hooks unchanged: the runtime derives nothing from it, since
77
+ * which file declared the generator is the host's knowledge.
78
+ */
79
+ configurationDirectoryPath: string;
72
80
  definition: GeneratorDefinition;
73
81
  filesystem?: FileSystemAdapter;
74
82
  formatter?: FormatterAdapter;
package/dist/src/index.js CHANGED
@@ -90,7 +90,9 @@ var l = class GenerationService {
90
90
  definitionName: e.definition.name,
91
91
  inputs: r
92
92
  }), a = {
93
+ configurationDirectoryPath: e.configurationDirectoryPath,
93
94
  definition: e.definition,
95
+ filesystem: t,
94
96
  generatedFilePaths: [],
95
97
  input: r,
96
98
  outputDirectoryPath: e.instancePath,
package/package.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "url": "https://github.com/organizzolini/codebase/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@conformetry/configuration": "0.0.39",
8
- "@conformetry/core": "0.0.38",
7
+ "@conformetry/configuration": "0.0.41",
8
+ "@conformetry/core": "0.0.40",
9
9
  "@nestjs/common": "^11.1.21 || ^12.0.1",
10
10
  "reflect-metadata": "^0.2.2"
11
11
  },
@@ -13,7 +13,7 @@
13
13
  "devDependencies": {
14
14
  "@golevelup/ts-vitest": "^4.0.0",
15
15
  "@nestjs/testing": "^12.0.1",
16
- "@nx/eslint-plugin": "^23.0.0",
16
+ "@nx/eslint-plugin": "^23.3.0",
17
17
  "@swc/helpers": "^0.5.21",
18
18
  "@types/node": "^26.5.1",
19
19
  "@vitest/coverage-v8": "^5.0.0",
@@ -59,5 +59,5 @@
59
59
  "strict": true
60
60
  },
61
61
  "types": "./dist/src/index.d.ts",
62
- "version": "0.0.39"
62
+ "version": "0.0.41"
63
63
  }