@conformetry/generation 0.0.0-stage → 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jimmy Paolini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,434 @@
1
- # Temporary Holding Version
1
+ # 👔 Conformetry Generation
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The generation runtime for [Conformetry](../conformetry-cli/README.md): it
4
+ walks a template tree, renders every path and every file, and writes the
5
+ result.
6
+
7
+ ```bash
8
+ npm install --save-dev @conformetry/generation
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { GenerationService } from "@conformetry/generation";
15
+
16
+ const result = await generationService.runGenerator({
17
+ definition: {
18
+ name: "nestjs-service-module",
19
+ templateDirectoryPath: "templates/nestjs-service-module",
20
+ },
21
+ inputs: { name: "billing" },
22
+ instancePath: "packages/billing/src/modules",
23
+ });
24
+ // → { generatedFilePaths: [...], outputDirectoryPath: "..." }
25
+ ```
26
+
27
+ The lifecycle is `preGenerate` → render → `postGenerate` → format, and the
28
+ returned file paths are sorted.
29
+
30
+ ## Rendering
31
+
32
+ `RenderingService` is the single owner of template substitution across the
33
+ whole toolchain. Generation renders templates to create files; validation
34
+ renders the _same_ templates to compare against files that already exist. Both
35
+ must substitute identically or validation would flag the files the generator
36
+ itself produced — which is why neither reimplements this.
37
+
38
+ Contents and paths are both rendered with
39
+ [mustache](https://mustache.github.io), HTML escaping disabled so substituted
40
+ values cannot corrupt source code. `buildNameSubstitutions` derives
41
+ `nameCamelCase`, `nameKebabCase`, `namePascalCase`, and `nameSnakeCase` from a
42
+ single name; callers merge their own inputs over the result, so an explicit
43
+ input of the same key always wins.
44
+
45
+ > **An interpolated placeholder nobody supplied raises
46
+ > `MissingSubstitutionError`.** Mustache would otherwise render it as an empty
47
+ > string rather than leaving the token visible, and because generation and
48
+ > validation render identically, both halves of the loop lost the same value and
49
+ > agreed that nothing was wrong. A hole rendered into both sides of a comparison
50
+ > is not something that comparison can report, so the renderer refuses instead.
51
+ >
52
+ > Section tags are exempt, deliberately: `{{#field}}` and `{{^field}}` are
53
+ > conditionals, so an absent name is how a template asks for a block to be
54
+ > skipped or taken. A supplied value that is the empty string is an answer too —
55
+ > only an absent key is a hole. Together those are how a template says
56
+ > "optional": ask with a section, interpolate inside it.
57
+
58
+ Paths once used a `__field__` syntax of their own, on the assumption that
59
+ braces were not portable across filesystems. They are — and the separate syntax
60
+ could not tell a placeholder from a Python dunder, so a template shipping
61
+ `__init__.py` depended on `init` never being a substitution.
62
+
63
+ ## Adapters
64
+
65
+ Filesystem and formatter access go through `FileSystemAdapter` and
66
+ `FormatterAdapter`, defaulting to disk and a no-op. That is how
67
+ [`@conformetry/nx`](../conformetry-nx/README.md) reuses this runtime unchanged
68
+ against an Nx generator `Tree`. Rendering deliberately is _not_ an adapter, for
69
+ the reason above.
70
+
71
+ ## Exports
72
+
73
+ `GenerationService`, `RenderingService`, their modules, and the
74
+ `FileSystemAdapter`, `FormatterAdapter`, `GeneratorDefinition`, and
75
+ `Substitutions` types.
76
+
77
+ ## Test
78
+
79
+ ```bash
80
+ nx run conformetry-generation:vitest
81
+ ```
82
+
83
+ ## License
84
+
85
+ MIT — see [LICENSE](../../../../LICENSE).
86
+
87
+ ## 👔 Conformetry
88
+
89
+ This project was generated from the [nestjs-service-project](../../../../configuration/conformetry-templates/nestjs-service-project) conformetry template.
90
+
91
+ <!-- callidescope:start -->
92
+
93
+ ## 🔭 Callidescope
94
+
95
+ Call stacks traced through `packages/ic-suite/conformetry/conformetry-generation`, deepest first. Each frame shows what it takes, what it returns, and what its documentation says.
96
+
97
+ | Measure | Value |
98
+ | --- | --- |
99
+ | Callables | 13 |
100
+ | Files | 11 |
101
+ | Calls traced | 10 |
102
+ | Call stacks | 1 |
103
+ | Deepest stack | 2 |
104
+ | Stacks through recursion | 0 |
105
+ | Unfollowable calls | 2 |
106
+
107
+ ### Limits
108
+
109
+ What this project is judged against, as declared in its own `callidescope.config.ts`.
110
+
111
+ | Limit | Value |
112
+ | --- | --- |
113
+ | `maximumDepth` | 8 |
114
+ | `maximumBreadth` | 4 |
115
+
116
+ ### Call stacks (depth)
117
+
118
+ **1. `GenerationService.listDirectory`** — depth 2 · orphan-root
119
+
120
+ ```text
121
+ 🚀 GenerationService.listDirectory(directoryPath: string): Promise<DirectoryEntry[]> [packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:38]
122
+ └─> GenerationService.map(…)(entry: Dirent<string>): { isDirectory: boolean; name: string; } [packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:41]
123
+ ```
124
+
125
+ ### Breadth
126
+
127
+ | Callable | Breadth | Calls directly | Location |
128
+ | --- | --- | --- | --- |
129
+ | `GenerationService.runGenerator` | 4 | `GenerationService.resolveAdapters`, `GenerationService.normalizeInputs`, `GenerationService.buildSubstitutions`, `GenerationService.renderDirectory` | `packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:190` |
130
+ | `GenerationService.renderDirectory` | 2 | `ConfigurationService.renderPath`, `GenerationService.renderFile` | `packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:104` |
131
+ | `GenerationService.listDirectory` | 1 | `GenerationService.map(…)` | `packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:38` |
132
+
133
+ <details>
134
+ <summary>2 more callables</summary>
135
+
136
+ | Callable | Breadth | Calls directly | Location |
137
+ | --- | --- | --- | --- |
138
+ | `GenerationService.buildSubstitutions` | 1 | `ConfigurationService.buildNameSubstitutions` | `packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:74` |
139
+ | `GenerationService.renderFile` | 1 | `ConfigurationService.renderContent` | `packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts:153` |
140
+
141
+ </details>
142
+ <!-- callidescope:end -->
143
+
144
+ ## 🕸️ Codependix
145
+
146
+ Dependency graphs exported by [codependix](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codependix/codependix-cli), regenerated by `nx run codebase:codependix:write`.
147
+
148
+ ### Nx Neighborhood
149
+
150
+ <!-- codependix:start name="codependix-nx-projects" -->
151
+ ```mermaid
152
+ graph LR
153
+ conformetry_cli["conformetry-cli"]
154
+ conformetry_configuration["conformetry-configuration"]
155
+ conformetry_examples["conformetry-examples"]
156
+ conformetry_generation["conformetry-generation"]
157
+ conformetry_nx["conformetry-nx"]
158
+ conformetry_cli --> conformetry_generation
159
+ conformetry_examples --> conformetry_generation
160
+ conformetry_generation --> conformetry_configuration
161
+ conformetry_nx --> conformetry_generation
162
+ classDef subject fill:#7c3aed,color:#fff,stroke:#4c1d95,stroke-width:2px
163
+ class conformetry_generation subject
164
+ ```
165
+ <!-- codependix:end name="codependix-nx-projects" -->
166
+
167
+ ### NestJS Module Graph
168
+
169
+ <!-- codependix:start name="codependix-nestjs-modules" -->
170
+ ```mermaid
171
+ flowchart LR
172
+ ConfigurationModule
173
+ GenerationModule
174
+ InputModule
175
+ InstanceDiscoveryModule
176
+ InstanceGroupModule
177
+ RenderingModule
178
+ TemplateDiscoveryModule
179
+ ConfigurationModule --> InputModule
180
+ ConfigurationModule --> InstanceDiscoveryModule
181
+ ConfigurationModule --> InstanceGroupModule
182
+ ConfigurationModule --> RenderingModule
183
+ ConfigurationModule --> TemplateDiscoveryModule
184
+ GenerationModule --> ConfigurationModule
185
+ InstanceDiscoveryModule --> InstanceGroupModule
186
+ InstanceDiscoveryModule --> RenderingModule
187
+ InstanceDiscoveryModule --> TemplateDiscoveryModule
188
+ TemplateDiscoveryModule --> RenderingModule
189
+ ```
190
+ <!-- codependix:end name="codependix-nestjs-modules" -->
191
+
192
+ ### File Imports
193
+
194
+ <!-- codependix:start name="codependix-file-imports" -->
195
+ ```mermaid
196
+ graph LR
197
+ file_callidescope_config_ts["callidescope.config.ts"]
198
+ file_codependix_config_ts["codependix.config.ts"]
199
+ file_codometer_config_ts["codometer.config.ts"]
200
+ file_eslint_config_ts["eslint.config.ts"]
201
+ file_src_index_ts["src/index.ts"]
202
+ file_src_modules_generation_generation_constants_ts["src/modules/generation/generation.constants.ts"]
203
+ file_src_modules_generation_generation_module_ts["src/modules/generation/generation.module.ts"]
204
+ file_src_modules_generation_generation_module_unit_test_ts["src/modules/generation/generation.module.unit.test.ts"]
205
+ file_src_modules_generation_generation_service_ts["src/modules/generation/generation.service.ts"]
206
+ file_src_modules_generation_generation_service_unit_test_ts["src/modules/generation/generation.service.unit.test.ts"]
207
+ file_src_modules_generation_generation_types_ts["src/modules/generation/generation.types.ts"]
208
+ file_testing_mocks_ts["testing/mocks.ts"]
209
+ file_testing_setup_ts["testing/setup.ts"]
210
+ file_vite_config_ts["vite.config.ts"]
211
+ file_vitest_config_ts["vitest.config.ts"]
212
+ file_src_modules_generation_generation_module_ts --> file_src_modules_generation_generation_service_ts
213
+ file_src_modules_generation_generation_module_unit_test_ts --> file_src_modules_generation_generation_module_ts
214
+ file_src_modules_generation_generation_service_ts --> file_src_modules_generation_generation_types_ts
215
+ file_src_modules_generation_generation_service_unit_test_ts --> file_src_modules_generation_generation_service_ts
216
+ file_src_modules_generation_generation_service_unit_test_ts --> file_src_modules_generation_generation_types_ts
217
+ ```
218
+ <!-- codependix:end name="codependix-file-imports" -->
219
+
220
+ <!-- codometer:start -->
221
+
222
+ ## ⏲️ Codometer
223
+
224
+ ### Project
225
+
226
+ ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-802-22c55e?style=flat-square)
227
+ ![Repository Size](https://img.shields.io/badge/Repository_Size-35.34_kB-6b7280?style=flat-square)
228
+ ![Folders](https://img.shields.io/badge/Folders-4-4a4a4a?style=flat-square)
229
+ ![Source Files](https://img.shields.io/badge/Source_Files-15-3178c6?style=flat-square)
230
+
231
+ ### Measured Targets
232
+
233
+ ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-1.46_kB_gzip-6b7280?style=flat-square)
234
+
235
+ ### TypeScript
236
+
237
+ ![TypeScript Files](https://img.shields.io/badge/TypeScript_Files-15-3178c6?style=flat-square)
238
+ ![Interfaces](https://img.shields.io/badge/Interfaces-8-0ea5e9?style=flat-square)
239
+ ![Generic Declarations](https://img.shields.io/badge/Generic_Declarations-0-0369a1?style=flat-square)
240
+ ![Enums](https://img.shields.io/badge/Enums-0-f97316?style=flat-square)
241
+ ![Decorators](https://img.shields.io/badge/Decorators-2-db2777?style=flat-square)
242
+ ![Doc Comments](https://img.shields.io/badge/Doc_Comments-23-6366f1?style=flat-square)
243
+ ![Static Methods](https://img.shields.io/badge/Static_Methods-0-166534?style=flat-square)
244
+
245
+ ### JavaScript
246
+
247
+ ![JavaScript Files](https://img.shields.io/badge/JavaScript_Files-0-f7df1e?style=flat-square)
248
+ ![Test Files](https://img.shields.io/badge/Test_Files-2-10b981?style=flat-square)
249
+ ![External Packages](https://img.shields.io/badge/External_Packages-9-8b5cf6?style=flat-square)
250
+ ![Classes](https://img.shields.io/badge/Classes-4-7c3aed?style=flat-square)
251
+ ![Functions](https://img.shields.io/badge/Functions-22-16a34a?style=flat-square)
252
+ ![Methods](https://img.shields.io/badge/Methods-19-15803d?style=flat-square)
253
+ ![Sync Functions](https://img.shields.io/badge/Sync_Functions-22-4ade80?style=flat-square)
254
+ ![Async Functions](https://img.shields.io/badge/Async_Functions-19-059669?style=flat-square)
255
+ ![Constants](https://img.shields.io/badge/Constants-33-dc2626?style=flat-square)
256
+ ![Imports](https://img.shields.io/badge/Imports-32-0284c7?style=flat-square)
257
+ ![Exported Symbols](https://img.shields.io/badge/Exported_Symbols-13-ea580c?style=flat-square)
258
+ ![Comments](https://img.shields.io/badge/Comments-39-64748b?style=flat-square)
259
+ ![Comment Lines](https://img.shields.io/badge/Comment_Lines-109-475569?style=flat-square)
260
+ ![TODO Comments](https://img.shields.io/badge/TODO_Comments-0-ca8a04?style=flat-square)
261
+
262
+ ### Python
263
+
264
+ ![Python Files](https://img.shields.io/badge/Python_Files-0-3776ab?style=flat-square)
265
+ ![Python Lines](https://img.shields.io/badge/Python_Lines-0-4b8bbe?style=flat-square)
266
+ ![Python Classes](https://img.shields.io/badge/Python_Classes-0-7c3aed?style=flat-square)
267
+ ![Python Functions](https://img.shields.io/badge/Python_Functions-0-16a34a?style=flat-square)
268
+ ![Python Protocols](https://img.shields.io/badge/Python_Protocols-0-0ea5e9?style=flat-square)
269
+ ![Python Constants](https://img.shields.io/badge/Python_Constants-0-dc2626?style=flat-square)
270
+ ![Python Imports](https://img.shields.io/badge/Python_Imports-0-0284c7?style=flat-square)
271
+ ![Python Decorators](https://img.shields.io/badge/Python_Decorators-0-db2777?style=flat-square)
272
+ ![Docstrings](https://img.shields.io/badge/Docstrings-0-6366f1?style=flat-square)
273
+ ![Docstring Lines](https://img.shields.io/badge/Docstring_Lines-0-818cf8?style=flat-square)
274
+ ![Python Comments](https://img.shields.io/badge/Python_Comments-0-64748b?style=flat-square)
275
+ ![Python Comment Lines](https://img.shields.io/badge/Python_Comment_Lines-0-475569?style=flat-square)
276
+
277
+ ### JSON
278
+
279
+ ![JSON Files](https://img.shields.io/badge/JSON_Files-4-a16207?style=flat-square)
280
+ ![JSON Lines](https://img.shields.io/badge/JSON_Lines-162-ca8a04?style=flat-square)
281
+ ![JSON Objects](https://img.shields.io/badge/JSON_Objects-38-7c3aed?style=flat-square)
282
+ ![JSON Arrays](https://img.shields.io/badge/JSON_Arrays-13-8b5cf6?style=flat-square)
283
+ ![JSON Properties](https://img.shields.io/badge/JSON_Properties-100-0284c7?style=flat-square)
284
+ ![JSON Strings](https://img.shields.io/badge/JSON_Strings-87-16a34a?style=flat-square)
285
+ ![JSON Numbers](https://img.shields.io/badge/JSON_Numbers-1-059669?style=flat-square)
286
+ ![JSON Booleans](https://img.shields.io/badge/JSON_Booleans-7-0ea5e9?style=flat-square)
287
+ ![JSON Nulls](https://img.shields.io/badge/JSON_Nulls-0-64748b?style=flat-square)
288
+ ![JSON Items](https://img.shields.io/badge/JSON_Items-42-475569?style=flat-square)
289
+ ![JSON Nodes](https://img.shields.io/badge/JSON_Nodes-146-dc2626?style=flat-square)
290
+ ![JSON Max Depth](https://img.shields.io/badge/JSON_Max_Depth-5-ea580c?style=flat-square)
291
+
292
+ ### YAML
293
+
294
+ ![YAML Files](https://img.shields.io/badge/YAML_Files-0-cb171e?style=flat-square)
295
+ ![YAML Lines](https://img.shields.io/badge/YAML_Lines-0-e34c26?style=flat-square)
296
+ ![YAML Documents](https://img.shields.io/badge/YAML_Documents-0-f97316?style=flat-square)
297
+ ![YAML Mappings](https://img.shields.io/badge/YAML_Mappings-0-7c3aed?style=flat-square)
298
+ ![YAML Sequences](https://img.shields.io/badge/YAML_Sequences-0-8b5cf6?style=flat-square)
299
+ ![YAML Keys](https://img.shields.io/badge/YAML_Keys-0-0284c7?style=flat-square)
300
+ ![YAML Scalars](https://img.shields.io/badge/YAML_Scalars-0-16a34a?style=flat-square)
301
+ ![YAML Anchors](https://img.shields.io/badge/YAML_Anchors-0-059669?style=flat-square)
302
+ ![YAML Aliases](https://img.shields.io/badge/YAML_Aliases-0-10b981?style=flat-square)
303
+ ![YAML Comments](https://img.shields.io/badge/YAML_Comments-0-64748b?style=flat-square)
304
+ ![YAML Max Depth](https://img.shields.io/badge/YAML_Max_Depth-0-ea580c?style=flat-square)
305
+
306
+ ### TOML
307
+
308
+ ![TOML Files](https://img.shields.io/badge/TOML_Files-0-9c4221?style=flat-square)
309
+ ![TOML Lines](https://img.shields.io/badge/TOML_Lines-0-b45309?style=flat-square)
310
+ ![TOML Tables](https://img.shields.io/badge/TOML_Tables-0-7c3aed?style=flat-square)
311
+ ![TOML Array Tables](https://img.shields.io/badge/TOML_Array_Tables-0-8b5cf6?style=flat-square)
312
+ ![TOML Keys](https://img.shields.io/badge/TOML_Keys-0-0284c7?style=flat-square)
313
+ ![TOML Arrays](https://img.shields.io/badge/TOML_Arrays-0-16a34a?style=flat-square)
314
+ ![TOML Comments](https://img.shields.io/badge/TOML_Comments-0-64748b?style=flat-square)
315
+
316
+ ### Shell
317
+
318
+ ![Shell Files](https://img.shields.io/badge/Shell_Files-0-89e051?style=flat-square)
319
+ ![Shell Lines](https://img.shields.io/badge/Shell_Lines-0-4eaa25?style=flat-square)
320
+ ![Shell Functions](https://img.shields.io/badge/Shell_Functions-0-16a34a?style=flat-square)
321
+ ![Shell Variables](https://img.shields.io/badge/Shell_Variables-0-0284c7?style=flat-square)
322
+ ![Shell Exports](https://img.shields.io/badge/Shell_Exports-0-ea580c?style=flat-square)
323
+ ![Shell Conditionals](https://img.shields.io/badge/Shell_Conditionals-0-7c3aed?style=flat-square)
324
+ ![Shell Loops](https://img.shields.io/badge/Shell_Loops-0-8b5cf6?style=flat-square)
325
+ ![Shell Pipelines](https://img.shields.io/badge/Shell_Pipelines-0-059669?style=flat-square)
326
+ ![Shebangs](https://img.shields.io/badge/Shebangs-0-6b7280?style=flat-square)
327
+ ![Shell Comments](https://img.shields.io/badge/Shell_Comments-0-64748b?style=flat-square)
328
+ ![Shell Comment Lines](https://img.shields.io/badge/Shell_Comment_Lines-0-475569?style=flat-square)
329
+
330
+ ### SQL
331
+
332
+ ![SQL Files](https://img.shields.io/badge/SQL_Files-0-e38c00?style=flat-square)
333
+ ![SQL Lines](https://img.shields.io/badge/SQL_Lines-0-f29111?style=flat-square)
334
+ ![SQL Statements](https://img.shields.io/badge/SQL_Statements-0-7c3aed?style=flat-square)
335
+ ![SQL Selects](https://img.shields.io/badge/SQL_Selects-0-16a34a?style=flat-square)
336
+ ![SQL Inserts](https://img.shields.io/badge/SQL_Inserts-0-22c55e?style=flat-square)
337
+ ![SQL Updates](https://img.shields.io/badge/SQL_Updates-0-0ea5e9?style=flat-square)
338
+ ![SQL Deletes](https://img.shields.io/badge/SQL_Deletes-0-dc2626?style=flat-square)
339
+ ![SQL Creates](https://img.shields.io/badge/SQL_Creates-0-0284c7?style=flat-square)
340
+ ![SQL Joins](https://img.shields.io/badge/SQL_Joins-0-8b5cf6?style=flat-square)
341
+ ![SQL CTEs](https://img.shields.io/badge/SQL_CTEs-0-059669?style=flat-square)
342
+ ![SQL Comments](https://img.shields.io/badge/SQL_Comments-0-64748b?style=flat-square)
343
+
344
+ ### HCL
345
+
346
+ ![HCL Files](https://img.shields.io/badge/HCL_Files-0-844fba?style=flat-square)
347
+ ![HCL Lines](https://img.shields.io/badge/HCL_Lines-0-a78bfa?style=flat-square)
348
+ ![HCL Blocks](https://img.shields.io/badge/HCL_Blocks-0-7c3aed?style=flat-square)
349
+ ![HCL Resources](https://img.shields.io/badge/HCL_Resources-0-0284c7?style=flat-square)
350
+ ![HCL Variables](https://img.shields.io/badge/HCL_Variables-0-16a34a?style=flat-square)
351
+ ![HCL Outputs](https://img.shields.io/badge/HCL_Outputs-0-059669?style=flat-square)
352
+ ![HCL Attributes](https://img.shields.io/badge/HCL_Attributes-0-0ea5e9?style=flat-square)
353
+ ![HCL Interpolations](https://img.shields.io/badge/HCL_Interpolations-0-db2777?style=flat-square)
354
+ ![HCL Comments](https://img.shields.io/badge/HCL_Comments-0-64748b?style=flat-square)
355
+
356
+ ### CSS
357
+
358
+ ![CSS Files](https://img.shields.io/badge/CSS_Files-0-264de4?style=flat-square)
359
+ ![CSS Lines](https://img.shields.io/badge/CSS_Lines-0-2965f1?style=flat-square)
360
+ ![CSS Rules](https://img.shields.io/badge/CSS_Rules-0-7c3aed?style=flat-square)
361
+ ![CSS Selectors](https://img.shields.io/badge/CSS_Selectors-0-8b5cf6?style=flat-square)
362
+ ![CSS Declarations](https://img.shields.io/badge/CSS_Declarations-0-0284c7?style=flat-square)
363
+ ![CSS At Rules](https://img.shields.io/badge/CSS_At_Rules-0-f97316?style=flat-square)
364
+ ![CSS Media Queries](https://img.shields.io/badge/CSS_Media_Queries-0-ea580c?style=flat-square)
365
+ ![CSS Custom Properties](https://img.shields.io/badge/CSS_Custom_Properties-0-16a34a?style=flat-square)
366
+ ![CSS Comments](https://img.shields.io/badge/CSS_Comments-0-64748b?style=flat-square)
367
+
368
+ ### Conventions
369
+
370
+ ![Module Files](https://img.shields.io/badge/Module_Files-1-7c3aed?style=flat-square)
371
+ ![Service Files](https://img.shields.io/badge/Service_Files-1-0284c7?style=flat-square)
372
+ ![Command Files](https://img.shields.io/badge/Command_Files-0-16a34a?style=flat-square)
373
+ ![Constants Files](https://img.shields.io/badge/Constants_Files-1-ea580c?style=flat-square)
374
+ ![Types Files](https://img.shields.io/badge/Types_Files-1-db2777?style=flat-square)
375
+ ![Utilities Files](https://img.shields.io/badge/Utilities_Files-0-0ea5e9?style=flat-square)
376
+ ![TypeORM Entities](https://img.shields.io/badge/TypeORM_Entities-0-059669?style=flat-square)
377
+ ![Unit Tests](https://img.shields.io/badge/Unit_Tests-2-ca8a04?style=flat-square)
378
+ ![Integration Tests](https://img.shields.io/badge/Integration_Tests-0-7c3aed?style=flat-square)
379
+ ![End To End Tests](https://img.shields.io/badge/End_To_End_Tests-0-0284c7?style=flat-square)
380
+ ![CSS Comment Budget](https://img.shields.io/badge/CSS_Comment_Budget-0-16a34a?style=flat-square)
381
+ ![HCL Comment Budget](https://img.shields.io/badge/HCL_Comment_Budget-0-ea580c?style=flat-square)
382
+ ![Python Comment Budget](https://img.shields.io/badge/Python_Comment_Budget-0-db2777?style=flat-square)
383
+ ![SQL Comment Budget](https://img.shields.io/badge/SQL_Comment_Budget-0-0ea5e9?style=flat-square)
384
+ ![TOML Comment Budget](https://img.shields.io/badge/TOML_Comment_Budget-0-059669?style=flat-square)
385
+ ![TypeScript Comment Budget](https://img.shields.io/badge/TypeScript_Comment_Budget-0-ca8a04?style=flat-square)
386
+ ![YAML Comment Budget](https://img.shields.io/badge/YAML_Comment_Budget-0-7c3aed?style=flat-square)
387
+ ![Shell Comment Budget](https://img.shields.io/badge/Shell_Comment_Budget-0-0284c7?style=flat-square)
388
+
389
+ ### Jupyter
390
+
391
+ ![Notebooks](https://img.shields.io/badge/Notebooks-0-f37626?style=flat-square)
392
+ ![Notebook Cells](https://img.shields.io/badge/Notebook_Cells-0-e8a33d?style=flat-square)
393
+ ![Code Cells](https://img.shields.io/badge/Code_Cells-0-3776ab?style=flat-square)
394
+ ![Markdown Cells](https://img.shields.io/badge/Markdown_Cells-0-083fa1?style=flat-square)
395
+ ![Raw Cells](https://img.shields.io/badge/Raw_Cells-0-9ca3af?style=flat-square)
396
+ ![Executed Cells](https://img.shields.io/badge/Executed_Cells-0-16a34a?style=flat-square)
397
+ ![Cell Outputs](https://img.shields.io/badge/Cell_Outputs-0-059669?style=flat-square)
398
+ ![Notebook Code Lines](https://img.shields.io/badge/Notebook_Code_Lines-0-4b8bbe?style=flat-square)
399
+ ![Notebook Classes](https://img.shields.io/badge/Notebook_Classes-0-7c3aed?style=flat-square)
400
+ ![Notebook Functions](https://img.shields.io/badge/Notebook_Functions-0-22c55e?style=flat-square)
401
+ ![Notebook Imports](https://img.shields.io/badge/Notebook_Imports-0-0284c7?style=flat-square)
402
+ ![Notebook Decorators](https://img.shields.io/badge/Notebook_Decorators-0-db2777?style=flat-square)
403
+ ![Notebook Prose Lines](https://img.shields.io/badge/Notebook_Prose_Lines-0-1f6feb?style=flat-square)
404
+ ![Notebook Headings](https://img.shields.io/badge/Notebook_Headings-0-a78bfa?style=flat-square)
405
+ ![Notebook Links](https://img.shields.io/badge/Notebook_Links-0-10b981?style=flat-square)
406
+ ![Notebook Images](https://img.shields.io/badge/Notebook_Images-0-34d399?style=flat-square)
407
+ ![Notebook Code Blocks](https://img.shields.io/badge/Notebook_Code_Blocks-0-dc2626?style=flat-square)
408
+ ![Notebook Properties](https://img.shields.io/badge/Notebook_Properties-0-ca8a04?style=flat-square)
409
+ ![Notebook Nodes](https://img.shields.io/badge/Notebook_Nodes-0-a16207?style=flat-square)
410
+ ![Notebook Max Depth](https://img.shields.io/badge/Notebook_Max_Depth-0-ea580c?style=flat-square)
411
+
412
+ ### Markdown
413
+
414
+ ![Markdown Files](https://img.shields.io/badge/Markdown_Files-1-083fa1?style=flat-square)
415
+ ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-222-1f6feb?style=flat-square)
416
+ ![H1](https://img.shields.io/badge/H1-1-7c3aed?style=flat-square)
417
+ ![H2](https://img.shields.io/badge/H2-7-8b5cf6?style=flat-square)
418
+ ![H3](https://img.shields.io/badge/H3-12-a78bfa?style=flat-square)
419
+ ![H4](https://img.shields.io/badge/H4-0-c4b5fd?style=flat-square)
420
+ ![H5](https://img.shields.io/badge/H5-0-ddd6fe?style=flat-square)
421
+ ![H6](https://img.shields.io/badge/H6-0-ede9fe?style=flat-square)
422
+ ![Paragraphs](https://img.shields.io/badge/Paragraphs-45-64748b?style=flat-square)
423
+ ![Lists](https://img.shields.io/badge/Lists-6-16a34a?style=flat-square)
424
+ ![List Items](https://img.shields.io/badge/List_Items-25-22c55e?style=flat-square)
425
+ ![Task List Items](https://img.shields.io/badge/Task_List_Items-0-4ade80?style=flat-square)
426
+ ![Tables](https://img.shields.io/badge/Tables-2-0284c7?style=flat-square)
427
+ ![Table Rows](https://img.shields.io/badge/Table_Rows-10-0ea5e9?style=flat-square)
428
+ ![Links](https://img.shields.io/badge/Links-9-059669?style=flat-square)
429
+ ![Images](https://img.shields.io/badge/Images-0-10b981?style=flat-square)
430
+ ![Code Blocks](https://img.shields.io/badge/Code_Blocks-11-dc2626?style=flat-square)
431
+ ![Inline Code](https://img.shields.io/badge/Inline_Code-73-ef4444?style=flat-square)
432
+ ![Block Quotes](https://img.shields.io/badge/Block_Quotes-0-ca8a04?style=flat-square)
433
+ ![Thematic Breaks](https://img.shields.io/badge/Thematic_Breaks-0-a16207?style=flat-square)
434
+ <!-- codometer:end -->
@@ -0,0 +1,141 @@
1
+ import { ConfigurationService } from '@conformetry/configuration';
2
+
3
+ /**
4
+ * A directory entry read from the source template tree.
5
+ */
6
+ export declare interface DirectoryEntry {
7
+ isDirectory: boolean;
8
+ name: string;
9
+ }
10
+
11
+ /**
12
+ * Filesystem operations used by the runtime.
13
+ *
14
+ * Exists so a host with a virtual filesystem — an Nx generator `Tree`, for
15
+ * instance — can run generation without touching disk. The default
16
+ * implementation reads and writes directly.
17
+ */
18
+ export declare interface FileSystemAdapter {
19
+ listDirectory(directoryPath: string): Promise<DirectoryEntry[]>;
20
+ makeDirectory(directoryPath: string): Promise<void>;
21
+ readFile(filePath: string): Promise<string>;
22
+ writeFile(filePath: string, content: string): Promise<void>;
23
+ }
24
+
25
+ /**
26
+ * Formatting applied once generation completes.
27
+ *
28
+ * The default is a no-op: formatting is the host's concern, and a plain CLI
29
+ * run leaves files to the workspace formatter.
30
+ */
31
+ export declare interface FormatterAdapter {
32
+ formatFiles(filePaths: string[]): Promise<void>;
33
+ }
34
+
35
+ /**
36
+ * Provides the generator runtime that renders a template tree onto disk.
37
+ */
38
+ export declare class GenerationModule {
39
+ }
40
+
41
+ /**
42
+ * Runs conformetry generators: walks a template tree, renders every path and
43
+ * file through the configuration layer, and writes the result.
44
+ *
45
+ * Filesystem and formatter access go through adapters so a host with a virtual
46
+ * filesystem (an Nx generator `Tree`) can reuse this runtime unchanged.
47
+ * Rendering deliberately is *not* an adapter — validation must substitute
48
+ * exactly as generation does, so both render through one service.
49
+ */
50
+ export declare class GenerationService {
51
+ private readonly configurationService;
52
+ constructor(configurationService: ConfigurationService);
53
+ /** Reads and writes directly to disk. Used when no adapter is supplied. */
54
+ private readonly defaultFileSystem;
55
+ /** Leaves formatting to the workspace formatter unless a host overrides it. */
56
+ private readonly defaultFormatter;
57
+ /**
58
+ * Merges the derived name variants with the caller's inputs.
59
+ *
60
+ * Inputs are spread last so an explicit value always wins over the variant
61
+ * derived from the same name.
62
+ */
63
+ private buildSubstitutions;
64
+ /** Drops unset inputs so they never shadow a derived name substitution. */
65
+ private normalizeInputs;
66
+ /**
67
+ * Recursively renders a template tree, returning every generated file path.
68
+ */
69
+ private renderDirectory;
70
+ /** Renders one template file and writes it, returning the written path. */
71
+ private renderFile;
72
+ /** Falls back to the disk filesystem and no-op formatter when unset. */
73
+ private resolveAdapters;
74
+ /**
75
+ * Runs the generator lifecycle — `preGenerate`, render, `postGenerate`,
76
+ * format — and returns the sorted list of generated file paths.
77
+ */
78
+ runGenerator(args: RunGeneratorArguments): Promise<RunGeneratorResult>;
79
+ }
80
+
81
+ /**
82
+ * Generator metadata and runtime configuration.
83
+ */
84
+ export declare interface GeneratorDefinition {
85
+ description?: string;
86
+ hooks?: GeneratorHooks;
87
+ name: string;
88
+ templateDirectoryPath: string;
89
+ }
90
+
91
+ /**
92
+ * A generator hook function.
93
+ */
94
+ declare type GeneratorHook = (context: GeneratorHookContext) => Promise<void> | void;
95
+
96
+ /**
97
+ * Data exposed to generator hooks.
98
+ */
99
+ declare interface GeneratorHookContext {
100
+ definition: GeneratorDefinition;
101
+ generatedFilePaths: string[];
102
+ input: Record<string, string>;
103
+ outputDirectoryPath: string;
104
+ substitutions: Record<string, string>;
105
+ }
106
+
107
+ /**
108
+ * Generator lifecycle hooks.
109
+ */
110
+ declare interface GeneratorHooks {
111
+ postGenerate?: GeneratorHook;
112
+ preGenerate?: GeneratorHook;
113
+ }
114
+
115
+ /**
116
+ * Arguments accepted by the runtime runner.
117
+ */
118
+ declare interface RunGeneratorArguments {
119
+ definition: GeneratorDefinition;
120
+ filesystem?: FileSystemAdapter;
121
+ formatter?: FormatterAdapter;
122
+ inputs?: Record<string, string | undefined>;
123
+ /**
124
+ * The directory the template's tree is rendered into.
125
+ *
126
+ * A template that should produce a folder contains that folder, so this is
127
+ * the folder's parent. Nothing here derives a path — the caller decides
128
+ * where an instance goes, because that is workspace knowledge.
129
+ */
130
+ instancePath: string;
131
+ }
132
+
133
+ /**
134
+ * Result returned by the runtime runner.
135
+ */
136
+ declare interface RunGeneratorResult {
137
+ generatedFilePaths: string[];
138
+ outputDirectoryPath: string;
139
+ }
140
+
141
+ export { }
@@ -0,0 +1,124 @@
1
+ import { ConfigurationModule as e, ConfigurationService as t } from "@conformetry/configuration";
2
+ import { Injectable as n, Module as r } from "@nestjs/common";
3
+ import { mkdir as i, readFile as a, readdir as o, writeFile as s } from "node:fs/promises";
4
+ import c from "node:path";
5
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateMetadata.js
6
+ function l(e, t) {
7
+ if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
8
+ }
9
+ //#endregion
10
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
11
+ function u(e, t, n, r) {
12
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
13
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
14
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
15
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
16
+ }
17
+ //#endregion
18
+ //#region packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.service.ts
19
+ var d = class {
20
+ configurationService;
21
+ constructor(e) {
22
+ this.configurationService = e;
23
+ }
24
+ defaultFileSystem = {
25
+ listDirectory: async (e) => (await o(e, { withFileTypes: !0 })).map((e) => ({
26
+ isDirectory: e.isDirectory(),
27
+ name: e.name
28
+ })),
29
+ makeDirectory: async (e) => {
30
+ await i(e, { recursive: !0 });
31
+ },
32
+ readFile: async (e) => a(e, "utf8"),
33
+ writeFile: async (e, t) => {
34
+ await i(c.dirname(e), { recursive: !0 }), await s(e, t, "utf8");
35
+ }
36
+ };
37
+ defaultFormatter = { formatFiles: async (e) => {
38
+ await Promise.resolve();
39
+ } };
40
+ buildSubstitutions(e) {
41
+ return {
42
+ ...this.configurationService.buildNameSubstitutions(e.inputs.name ?? e.definitionName),
43
+ ...e.inputs
44
+ };
45
+ }
46
+ normalizeInputs(e) {
47
+ let t = {};
48
+ for (let [n, r] of Object.entries(e)) r !== void 0 && (t[n] = r);
49
+ return t;
50
+ }
51
+ async renderDirectory(e) {
52
+ let t = await e.filesystem.listDirectory(e.templateDirectoryPath), n = [];
53
+ for (let r of t) {
54
+ let t = c.join(e.templateDirectoryPath, r.name), i = c.join(e.instanceDirectoryPath, this.configurationService.renderPath({
55
+ subject: t,
56
+ substitutions: e.substitutions,
57
+ templatePath: r.name
58
+ }));
59
+ if (r.isDirectory) {
60
+ await e.filesystem.makeDirectory(i), n.push(...await this.renderDirectory({
61
+ filesystem: e.filesystem,
62
+ instanceDirectoryPath: i,
63
+ substitutions: e.substitutions,
64
+ templateDirectoryPath: t
65
+ }));
66
+ continue;
67
+ }
68
+ n.push(await this.renderFile({
69
+ filesystem: e.filesystem,
70
+ instancePath: i,
71
+ substitutions: e.substitutions,
72
+ templatePath: t
73
+ }));
74
+ }
75
+ return n;
76
+ }
77
+ async renderFile(e) {
78
+ let t = await e.filesystem.readFile(e.templatePath);
79
+ return await e.filesystem.writeFile(e.instancePath, this.configurationService.renderContent({
80
+ subject: e.templatePath,
81
+ substitutions: e.substitutions,
82
+ templateContent: t
83
+ })), e.instancePath;
84
+ }
85
+ resolveAdapters(e) {
86
+ return {
87
+ filesystem: e.filesystem ?? this.defaultFileSystem,
88
+ formatter: e.formatter ?? this.defaultFormatter
89
+ };
90
+ }
91
+ async runGenerator(e) {
92
+ let { filesystem: t, formatter: n } = this.resolveAdapters(e), r = this.normalizeInputs(e.inputs ?? {}), i = this.buildSubstitutions({
93
+ definitionName: e.definition.name,
94
+ inputs: r
95
+ }), a = {
96
+ definition: e.definition,
97
+ generatedFilePaths: [],
98
+ input: r,
99
+ outputDirectoryPath: e.instancePath,
100
+ substitutions: i
101
+ };
102
+ return await e.definition.hooks?.preGenerate?.(a), a.generatedFilePaths = (await this.renderDirectory({
103
+ filesystem: t,
104
+ instanceDirectoryPath: e.instancePath,
105
+ substitutions: i,
106
+ templateDirectoryPath: e.definition.templateDirectoryPath
107
+ })).toSorted(), await e.definition.hooks?.postGenerate?.(a), await n.formatFiles(a.generatedFilePaths), {
108
+ generatedFilePaths: a.generatedFilePaths,
109
+ outputDirectoryPath: e.instancePath
110
+ };
111
+ }
112
+ };
113
+ d = u([n(), l("design:paramtypes", [t === void 0 ? Object : t])], d);
114
+ //#endregion
115
+ //#region packages/ic-suite/conformetry/conformetry-generation/src/modules/generation/generation.module.ts
116
+ var f = class {};
117
+ f = u([r({
118
+ controllers: [],
119
+ exports: [d],
120
+ imports: [e],
121
+ providers: [d]
122
+ })], f);
123
+ //#endregion
124
+ export { f as GenerationModule, d as GenerationService };
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
+ "author": "Jimmy Paolini",
3
+ "bugs": {
4
+ "url": "https://github.com/Organizzolini/codebase/issues"
5
+ },
6
+ "dependencies": {
7
+ "@conformetry/configuration": "0.0.1",
8
+ "@nestjs/common": "^11.1.21 || ^12.0.1",
9
+ "reflect-metadata": "^0.2.2"
10
+ },
11
+ "description": "Template rendering and file generation runtime for Conformetry",
12
+ "devDependencies": {
13
+ "@golevelup/ts-vitest": "^4.0.0",
14
+ "@nestjs/testing": "^12.0.1",
15
+ "@nx/eslint-plugin": "^23.0.0",
16
+ "@swc/helpers": "^0.5.21",
17
+ "@types/node": "^26.5.1",
18
+ "@vitest/coverage-v8": "^5.0.0",
19
+ "typescript": "^6.0.3",
20
+ "vitest": "^5.0.0"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/src/index.d.ts",
25
+ "default": "./dist/src/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "LICENSE",
32
+ "README.md"
33
+ ],
34
+ "homepage": "https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/conformetry/conformetry-generation#readme",
35
+ "keywords": [
36
+ "conformetry",
37
+ "generation",
38
+ "rendering",
39
+ "scaffolding",
40
+ "templates"
41
+ ],
42
+ "license": "MIT",
43
+ "main": "./dist/src/index.js",
2
44
  "name": "@conformetry/generation",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "registry": "https://registry.npmjs.org"
48
+ },
49
+ "repository": {
50
+ "directory": "packages/ic-suite/conformetry/conformetry-generation",
51
+ "type": "git",
52
+ "url": "git+https://github.com/Organizzolini/codebase.git"
53
+ },
54
+ "type": "module",
55
+ "typeCoverage": {
56
+ "atLeast": 100,
57
+ "strict": true
58
+ },
59
+ "types": "./dist/src/index.d.ts",
60
+ "version": "0.0.1"
6
61
  }