@ng-forge/openapi-generator 0.8.0-next.2 → 0.8.0-next.3
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 +37 -15
- package/bin/ng-forge-generator.js +19 -5
- package/package.json +1 -1
- package/src/cli/commands/generate.command.d.ts +1 -0
- package/src/cli/commands/generate.command.d.ts.map +1 -1
- package/src/config/generator-config.d.ts +1 -0
- package/src/config/generator-config.d.ts.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +19 -5
package/README.md
CHANGED
|
@@ -129,19 +129,20 @@ ng-forge-generator --spec openapi.yaml --output src/generated
|
|
|
129
129
|
|
|
130
130
|
### Options
|
|
131
131
|
|
|
132
|
-
| Option
|
|
133
|
-
|
|
|
134
|
-
| `--spec <path>`
|
|
135
|
-
| `--output <path>`
|
|
136
|
-
| `--interactive <mode>`
|
|
137
|
-
| `--endpoints <list>`
|
|
138
|
-
| `--read-only`
|
|
139
|
-
| `--
|
|
140
|
-
| `--
|
|
141
|
-
| `--
|
|
142
|
-
| `--
|
|
143
|
-
| `--
|
|
144
|
-
| `--
|
|
132
|
+
| Option | Description |
|
|
133
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `--spec <path>` | Path to OpenAPI spec file (required) |
|
|
135
|
+
| `--output <path>` | Output directory for generated files (required) |
|
|
136
|
+
| `--interactive <mode>` | `full` (prompt for endpoints + ambiguous types) or `none` (auto-select). Default: `full` |
|
|
137
|
+
| `--endpoints <list>` | Comma-separated endpoints, e.g. `"POST:/users,PUT:/users/{id}"` |
|
|
138
|
+
| `--read-only` | Generate GET endpoint forms with all fields disabled |
|
|
139
|
+
| `--barrel-extension <ext>` | Extension in barrel re-exports. Accepts `""` (default — no extension) or a dot-prefixed value like `".js"` (for Node ESM). Pass `""` to reset |
|
|
140
|
+
| `--watch` | Watch spec file for changes and regenerate |
|
|
141
|
+
| `--config <path>` | Directory for `.ng-forge-generator.json` config (defaults to `--output`) |
|
|
142
|
+
| `--dry-run` | List files that would be generated without writing them |
|
|
143
|
+
| `--skip-existing` | Skip files that already exist on disk |
|
|
144
|
+
| `--verbose` | Show detailed output including field mapping decisions |
|
|
145
|
+
| `--quiet` | Suppress info output; still shows success summary, warnings, and errors |
|
|
145
146
|
|
|
146
147
|
### Interactive Modes
|
|
147
148
|
|
|
@@ -164,6 +165,9 @@ ng-forge-generator --spec openapi.yaml --output src/generated \
|
|
|
164
165
|
# Generate GET endpoints with disabled (read-only) fields
|
|
165
166
|
ng-forge-generator --spec openapi.yaml --output src/generated --interactive none --read-only
|
|
166
167
|
|
|
168
|
+
# Node ESM / moduleResolution nodenext — emit .js extensions in barrel files
|
|
169
|
+
ng-forge-generator --spec openapi.yaml --output src/generated --barrel-extension .js
|
|
170
|
+
|
|
167
171
|
# Preview without writing
|
|
168
172
|
ng-forge-generator --spec openapi.yaml --output src/generated --dry-run
|
|
169
173
|
|
|
@@ -288,6 +292,7 @@ A `.ng-forge-generator.json` config file is saved in the output directory (or th
|
|
|
288
292
|
- **Selected endpoints** — which `METHOD:/path` pairs to generate
|
|
289
293
|
- **Field type decisions** — ambiguous field type choices (e.g., `"registerUser.acceptTerms": "checkbox"`)
|
|
290
294
|
- **Read-only flag** — whether GET endpoint fields are disabled
|
|
295
|
+
- **Barrel extension** — extension used in generated `index.ts` re-exports (empty by default)
|
|
291
296
|
|
|
292
297
|
This enables reproducible non-interactive re-runs. On subsequent runs, the generator reuses saved decisions without prompting.
|
|
293
298
|
|
|
@@ -309,6 +314,22 @@ This enables reproducible non-interactive re-runs. On subsequent runs, the gener
|
|
|
309
314
|
- **With `operationId`**: kebab-cased operationId (e.g., `createPet` -> `create-pet.form.ts`)
|
|
310
315
|
- **Without `operationId`**: method + path (e.g., `POST /users/register` -> `post-users-register.form.ts`)
|
|
311
316
|
|
|
317
|
+
### Barrel Exports
|
|
318
|
+
|
|
319
|
+
By default, generated `index.ts` barrel files re-export modules without a file extension:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// forms/index.ts
|
|
323
|
+
export * from './create-pet.form';
|
|
324
|
+
export * from './update-pet.form';
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
This matches the typical Angular app setup (`moduleResolution: bundler`). For Node ESM consumers running with `moduleResolution: node16` or `nodenext`, pass `--barrel-extension .js` to emit fully-qualified ESM imports.
|
|
328
|
+
|
|
329
|
+
The selected extension persists in `.ng-forge-generator.json` (only when non-empty) so future runs keep your choice. To clear a previously-saved `.js` and return to the default, pass `--barrel-extension ""` or delete the `barrelExtension` key from the config file.
|
|
330
|
+
|
|
331
|
+
> **Migrating from pre-1.x:** Earlier versions emitted `.js` suffixes by default. Upgrading will change generated barrel output for users who had not explicitly configured this — pass `--barrel-extension .js` on your next run to preserve the old behavior.
|
|
332
|
+
|
|
312
333
|
## Programmatic API
|
|
313
334
|
|
|
314
335
|
The package exports all core functions for programmatic use:
|
|
@@ -348,8 +369,9 @@ generateFormConfig(fields: FieldConfig[], options: FormConfigGeneratorOptions):
|
|
|
348
369
|
// Generate a TypeScript interface source string from a schema
|
|
349
370
|
generateInterface(schema: SchemaObject, options: InterfaceGeneratorOptions): string
|
|
350
371
|
|
|
351
|
-
// Generate an index.ts barrel file
|
|
352
|
-
|
|
372
|
+
// Generate an index.ts barrel file. `options.extension` controls the re-export
|
|
373
|
+
// suffix (default: '' — no extension; use '.js' for Node ESM/nodenext).
|
|
374
|
+
generateBarrel(fileNames: string[], options?: BarrelOptions): string
|
|
353
375
|
```
|
|
354
376
|
|
|
355
377
|
### I/O
|
|
@@ -1003,7 +1003,7 @@ function generateBarrel(fileNames, options) {
|
|
|
1003
1003
|
if (fileNames.length === 0) {
|
|
1004
1004
|
return "";
|
|
1005
1005
|
}
|
|
1006
|
-
const ext = options?.extension ?? "
|
|
1006
|
+
const ext = options?.extension ?? "";
|
|
1007
1007
|
const lines = [
|
|
1008
1008
|
"// @generated by @ng-forge/openapi-generator ",
|
|
1009
1009
|
...fileNames.map((name) => {
|
|
@@ -1176,7 +1176,16 @@ function registerGenerateOptions(cmd) {
|
|
|
1176
1176
|
return value;
|
|
1177
1177
|
},
|
|
1178
1178
|
"full"
|
|
1179
|
-
).option("--endpoints <list>", 'Comma-separated endpoints, e.g. "POST:/users,PUT:/users/{id}"').option("--read-only", "Generate GET endpoint forms with all fields disabled").option("--watch", "Watch spec file for changes and regenerate").option("--config <path>", "Directory for .ng-forge-generator.json config (defaults to --output)").option("--dry-run", "List files that would be generated without writing them").option("--skip-existing", "Skip files that already exist on disk").option(
|
|
1179
|
+
).option("--endpoints <list>", 'Comma-separated endpoints, e.g. "POST:/users,PUT:/users/{id}"').option("--read-only", "Generate GET endpoint forms with all fields disabled").option("--watch", "Watch spec file for changes and regenerate").option("--config <path>", "Directory for .ng-forge-generator.json config (defaults to --output)").option("--dry-run", "List files that would be generated without writing them").option("--skip-existing", "Skip files that already exist on disk").option(
|
|
1180
|
+
"--barrel-extension <ext>",
|
|
1181
|
+
'Extension used in barrel file exports. Accepts "" (default \u2014 no extension, for bundler/Angular setups) or a dot-prefixed extension like ".js" (for Node ESM / moduleResolution node16|nodenext). Pass "" to clear a previously-saved value',
|
|
1182
|
+
(value) => {
|
|
1183
|
+
if (value !== "" && !/^\.[a-z0-9]+$/i.test(value)) {
|
|
1184
|
+
throw new Error(`Invalid --barrel-extension '${value}'. Expected "" or a dot-prefixed extension like ".js".`);
|
|
1185
|
+
}
|
|
1186
|
+
return value;
|
|
1187
|
+
}
|
|
1188
|
+
).option("--verbose", "Show detailed output including field mapping decisions").option("--quiet", "Suppress info output; still shows success summary, warnings, and errors").addHelpText("after", "\nNote: Generated files are not formatted. Run your project formatter (e.g. prettier) after generation.");
|
|
1180
1189
|
}
|
|
1181
1190
|
async function runGenerateAction(options) {
|
|
1182
1191
|
if (options.verbose && options.quiet) {
|
|
@@ -1195,6 +1204,7 @@ async function runGenerate(options) {
|
|
|
1195
1204
|
const configDir = options.config ?? options.output;
|
|
1196
1205
|
const existingConfig = await loadConfig(configDir);
|
|
1197
1206
|
const decisions = existingConfig?.decisions ?? {};
|
|
1207
|
+
const barrelExtension = options.barrelExtension ?? existingConfig?.barrelExtension ?? "";
|
|
1198
1208
|
if (existingConfig) {
|
|
1199
1209
|
logger.info(`Loaded config from ${configDir}/.ng-forge-generator.json (${existingConfig.endpoints.length} saved endpoint(s))`);
|
|
1200
1210
|
}
|
|
@@ -1333,12 +1343,12 @@ Ambiguous fields in ${endpoint.method} ${endpoint.path}:`);
|
|
|
1333
1343
|
}
|
|
1334
1344
|
allFiles.push({
|
|
1335
1345
|
fileName: "index.ts",
|
|
1336
|
-
content: generateBarrel(allFormFileNames),
|
|
1346
|
+
content: generateBarrel(allFormFileNames, { extension: barrelExtension }),
|
|
1337
1347
|
subdirectory: "forms"
|
|
1338
1348
|
});
|
|
1339
1349
|
allFiles.push({
|
|
1340
1350
|
fileName: "index.ts",
|
|
1341
|
-
content: generateBarrel(allInterfaceFileNames),
|
|
1351
|
+
content: generateBarrel(allInterfaceFileNames, { extension: barrelExtension }),
|
|
1342
1352
|
subdirectory: "types"
|
|
1343
1353
|
});
|
|
1344
1354
|
if (allFormFileNames.length === 0) {
|
|
@@ -1375,7 +1385,11 @@ Ambiguous fields in ${endpoint.method} ${endpoint.path}:`);
|
|
|
1375
1385
|
output: options.output,
|
|
1376
1386
|
endpoints: configEndpoints,
|
|
1377
1387
|
decisions: updatedDecisions,
|
|
1378
|
-
readOnly: options.readOnly
|
|
1388
|
+
readOnly: options.readOnly,
|
|
1389
|
+
// Only persist when non-default so the empty (default) state is representable
|
|
1390
|
+
// as "field absent" — letting users clear a previously-saved .js setting by
|
|
1391
|
+
// passing --barrel-extension "" (or deleting the key manually).
|
|
1392
|
+
...barrelExtension !== "" && { barrelExtension }
|
|
1379
1393
|
};
|
|
1380
1394
|
await saveConfig(configDir, config);
|
|
1381
1395
|
if (options.watch) {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.command.d.ts","sourceRoot":"","sources":["../../../../../../packages/openapi-generator/src/cli/commands/generate.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAkBvE,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.command.d.ts","sourceRoot":"","sources":["../../../../../../packages/openapi-generator/src/cli/commands/generate.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAkBvE,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAkC1D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAe/E;AAkSD,wBAAgB,eAAe,CAAC,YAAY,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAQ7F"}
|
|
@@ -4,6 +4,7 @@ export interface GeneratorConfig {
|
|
|
4
4
|
endpoints: string[];
|
|
5
5
|
decisions: Record<string, string>;
|
|
6
6
|
readOnly?: boolean;
|
|
7
|
+
barrelExtension?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare function loadConfig(dir: string): Promise<GeneratorConfig | null>;
|
|
9
10
|
export declare function saveConfig(dir: string, config: GeneratorConfig): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator-config.d.ts","sourceRoot":"","sources":["../../../../../packages/openapi-generator/src/config/generator-config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"generator-config.d.ts","sourceRoot":"","sources":["../../../../../packages/openapi-generator/src/config/generator-config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAiB7E;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpF"}
|
package/src/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type { FormConfigGeneratorOptions } from './generator/form-config-generat
|
|
|
15
15
|
export { generateInterface } from './generator/interface-generator.js';
|
|
16
16
|
export type { InterfaceGeneratorOptions } from './generator/interface-generator.js';
|
|
17
17
|
export { generateBarrel } from './generator/barrel-generator.js';
|
|
18
|
+
export type { BarrelOptions } from './generator/barrel-generator.js';
|
|
18
19
|
export { writeGeneratedFiles } from './generator/file-writer.js';
|
|
19
20
|
export type { GeneratedFile, WriteOptions, WriteResult } from './generator/file-writer.js';
|
|
20
21
|
export { loadConfig, saveConfig } from './config/generator-config.js';
|
package/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/openapi-generator/src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACvF,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAEpF,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/openapi-generator/src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACvF,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAEpF,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE3F,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC"}
|
package/src/index.js
CHANGED
|
@@ -995,7 +995,7 @@ function generateBarrel(fileNames, options) {
|
|
|
995
995
|
if (fileNames.length === 0) {
|
|
996
996
|
return "";
|
|
997
997
|
}
|
|
998
|
-
const ext = options?.extension ?? "
|
|
998
|
+
const ext = options?.extension ?? "";
|
|
999
999
|
const lines = [
|
|
1000
1000
|
"// @generated by @ng-forge/openapi-generator ",
|
|
1001
1001
|
...fileNames.map((name) => {
|
|
@@ -1174,7 +1174,16 @@ function registerGenerateOptions(cmd) {
|
|
|
1174
1174
|
return value;
|
|
1175
1175
|
},
|
|
1176
1176
|
"full"
|
|
1177
|
-
).option("--endpoints <list>", 'Comma-separated endpoints, e.g. "POST:/users,PUT:/users/{id}"').option("--read-only", "Generate GET endpoint forms with all fields disabled").option("--watch", "Watch spec file for changes and regenerate").option("--config <path>", "Directory for .ng-forge-generator.json config (defaults to --output)").option("--dry-run", "List files that would be generated without writing them").option("--skip-existing", "Skip files that already exist on disk").option(
|
|
1177
|
+
).option("--endpoints <list>", 'Comma-separated endpoints, e.g. "POST:/users,PUT:/users/{id}"').option("--read-only", "Generate GET endpoint forms with all fields disabled").option("--watch", "Watch spec file for changes and regenerate").option("--config <path>", "Directory for .ng-forge-generator.json config (defaults to --output)").option("--dry-run", "List files that would be generated without writing them").option("--skip-existing", "Skip files that already exist on disk").option(
|
|
1178
|
+
"--barrel-extension <ext>",
|
|
1179
|
+
'Extension used in barrel file exports. Accepts "" (default \u2014 no extension, for bundler/Angular setups) or a dot-prefixed extension like ".js" (for Node ESM / moduleResolution node16|nodenext). Pass "" to clear a previously-saved value',
|
|
1180
|
+
(value) => {
|
|
1181
|
+
if (value !== "" && !/^\.[a-z0-9]+$/i.test(value)) {
|
|
1182
|
+
throw new Error(`Invalid --barrel-extension '${value}'. Expected "" or a dot-prefixed extension like ".js".`);
|
|
1183
|
+
}
|
|
1184
|
+
return value;
|
|
1185
|
+
}
|
|
1186
|
+
).option("--verbose", "Show detailed output including field mapping decisions").option("--quiet", "Suppress info output; still shows success summary, warnings, and errors").addHelpText("after", "\nNote: Generated files are not formatted. Run your project formatter (e.g. prettier) after generation.");
|
|
1178
1187
|
}
|
|
1179
1188
|
async function runGenerateAction(options) {
|
|
1180
1189
|
if (options.verbose && options.quiet) {
|
|
@@ -1193,6 +1202,7 @@ async function runGenerate(options) {
|
|
|
1193
1202
|
const configDir = options.config ?? options.output;
|
|
1194
1203
|
const existingConfig = await loadConfig(configDir);
|
|
1195
1204
|
const decisions = existingConfig?.decisions ?? {};
|
|
1205
|
+
const barrelExtension = options.barrelExtension ?? existingConfig?.barrelExtension ?? "";
|
|
1196
1206
|
if (existingConfig) {
|
|
1197
1207
|
logger.info(`Loaded config from ${configDir}/.ng-forge-generator.json (${existingConfig.endpoints.length} saved endpoint(s))`);
|
|
1198
1208
|
}
|
|
@@ -1331,12 +1341,12 @@ Ambiguous fields in ${endpoint.method} ${endpoint.path}:`);
|
|
|
1331
1341
|
}
|
|
1332
1342
|
allFiles.push({
|
|
1333
1343
|
fileName: "index.ts",
|
|
1334
|
-
content: generateBarrel(allFormFileNames),
|
|
1344
|
+
content: generateBarrel(allFormFileNames, { extension: barrelExtension }),
|
|
1335
1345
|
subdirectory: "forms"
|
|
1336
1346
|
});
|
|
1337
1347
|
allFiles.push({
|
|
1338
1348
|
fileName: "index.ts",
|
|
1339
|
-
content: generateBarrel(allInterfaceFileNames),
|
|
1349
|
+
content: generateBarrel(allInterfaceFileNames, { extension: barrelExtension }),
|
|
1340
1350
|
subdirectory: "types"
|
|
1341
1351
|
});
|
|
1342
1352
|
if (allFormFileNames.length === 0) {
|
|
@@ -1373,7 +1383,11 @@ Ambiguous fields in ${endpoint.method} ${endpoint.path}:`);
|
|
|
1373
1383
|
output: options.output,
|
|
1374
1384
|
endpoints: configEndpoints,
|
|
1375
1385
|
decisions: updatedDecisions,
|
|
1376
|
-
readOnly: options.readOnly
|
|
1386
|
+
readOnly: options.readOnly,
|
|
1387
|
+
// Only persist when non-default so the empty (default) state is representable
|
|
1388
|
+
// as "field absent" — letting users clear a previously-saved .js setting by
|
|
1389
|
+
// passing --barrel-extension "" (or deleting the key manually).
|
|
1390
|
+
...barrelExtension !== "" && { barrelExtension }
|
|
1377
1391
|
};
|
|
1378
1392
|
await saveConfig(configDir, config);
|
|
1379
1393
|
if (options.watch) {
|