@jmm-devkit/ngx-form-generator 1.3.1 → 1.4.0

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
@@ -14,8 +14,6 @@ Generates an Angular ReactiveForm from a Swagger or OpenAPI definition.
14
14
 
15
15
  Validation rules should have a single source of truth. These rules should be exposed to consumers to apply them. By doing this we can be sure that the same rules for UI validation are enforced at the API layer.
16
16
 
17
- [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
18
-
19
17
  ## Install
20
18
 
21
19
  ```bash
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Saves the generated file
3
+ * @param content - The content of the file to save.
4
+ * @param fileName - The name of the file to save.
5
+ */
6
+ export declare function saveFile(content: string, fileName: string): void;
@@ -0,0 +1,9 @@
1
+ import * as fs from 'node:fs';
2
+ /**
3
+ * Saves the generated file
4
+ * @param content - The content of the file to save.
5
+ * @param fileName - The name of the file to save.
6
+ */
7
+ export function saveFile(content, fileName) {
8
+ fs.writeFileSync(fileName, content);
9
+ }
@@ -0,0 +1,9 @@
1
+ #! /usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Licensed under the MIT License, (“the License”); you may not use this
5
+ * file except in compliance with the License.
6
+ *
7
+ * Copyright (c) 2020 Verizon
8
+ */
9
+ export {};
@@ -1,57 +1,56 @@
1
- #! /usr/bin/env node
2
- "use strict";
3
- /**
4
- * @license
5
- * Licensed under the MIT License, (“the License”); you may not use this
6
- * file except in compliance with the License.
7
- *
8
- * Copyright (c) 2020 Verizon
9
- */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- const generator_lib_1 = require("./generator-lib");
12
- const path_1 = require("path");
13
- const yargs = require('yargs');
14
- async function main() {
15
- const argv = yargs
16
- .option('input-spec', {
17
- alias: ['i', 'swaggerUrl'],
18
- description: 'Location of the OpenAPI spec as a URL or file path',
19
- type: 'string',
20
- require: true
21
- })
22
- .option('output', {
23
- alias: ['o', 'outDir'],
24
- description: 'Where to write the generated files',
25
- type: 'string'
26
- })
27
- .option('file-name', {
28
- alias: ['f', 'outFile'],
29
- description: 'Generated file name',
30
- type: 'string'
31
- })
32
- .option('max-depth', {
33
- alias: ['d', 'maxDepth'],
34
- description: 'Maximum depth of the generated forms',
35
- type: 'number'
36
- })
37
- .help()
38
- .wrap(null)
39
- .usage('Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.\n\n Usage: $0 -i <spec> -o <path>')
40
- .example('ngx-form-generator -i https://petstore.swagger.io/v2/swagger.json -o petstore-forms')
41
- .example('ngx-form-generator -i https://petstore.swagger.io/v2/swagger.yaml -o petstore-forms')
42
- .example('npx ngx-form-generator -i swagger.json -o project/form/src/lib')
43
- .alias('help', 'h').argv;
44
- const spec = await (0, generator_lib_1.loadSpec)(argv['input-spec']);
45
- const maxDepth = argv['max-depth'];
46
- if (maxDepth !== undefined && (isNaN(maxDepth) || maxDepth < 1)) {
47
- console.error('Error: max-depth must be a number greater than 0');
48
- process.exit(1);
49
- }
50
- const file = (0, generator_lib_1.makeForm)(spec, maxDepth);
51
- let fileName = argv['file-name'] || (0, generator_lib_1.makeFileName)(spec) || 'forms.ts';
52
- if (argv.output) {
53
- fileName = (0, path_1.join)(argv.output, fileName);
54
- }
55
- await (0, generator_lib_1.saveFile)(file, fileName);
56
- }
57
- main();
1
+ #! /usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Licensed under the MIT License, (“the License”); you may not use this
5
+ * file except in compliance with the License.
6
+ *
7
+ * Copyright (c) 2020 Verizon
8
+ */
9
+ import { saveFile } from './file-utils';
10
+ import { makeForm, makeFileName, loadSpec } from './generator-lib';
11
+ import { join } from 'node:path';
12
+ const yargs = require('yargs');
13
+ async function main() {
14
+ const argv = yargs
15
+ .option('input-spec', {
16
+ alias: ['i', 'swaggerUrl'],
17
+ description: 'Location of the OpenAPI spec as a URL or file path',
18
+ type: 'string',
19
+ require: true,
20
+ })
21
+ .option('output', {
22
+ alias: ['o', 'outDir'],
23
+ description: 'Where to write the generated files',
24
+ type: 'string',
25
+ })
26
+ .option('file-name', {
27
+ alias: ['f', 'outFile'],
28
+ description: 'Generated file name',
29
+ type: 'string',
30
+ })
31
+ .option('max-depth', {
32
+ alias: ['d', 'maxDepth'],
33
+ description: 'Maximum depth of the generated forms',
34
+ type: 'number',
35
+ })
36
+ .help()
37
+ .wrap(null)
38
+ .usage('Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.\n\n Usage: $0 -i <spec> -o <path>')
39
+ .example('ngx-form-generator -i https://petstore.swagger.io/v2/swagger.json -o petstore-forms')
40
+ .example('ngx-form-generator -i https://petstore.swagger.io/v2/swagger.yaml -o petstore-forms')
41
+ .example('npx ngx-form-generator -i swagger.json -o project/form/src/lib')
42
+ .alias('help', 'h').argv;
43
+ const spec = await loadSpec(argv['input-spec']);
44
+ const maxDepth = argv['max-depth'];
45
+ if (maxDepth !== undefined && (Number.isNaN(maxDepth) || maxDepth < 1)) {
46
+ console.error('Error: max-depth must be a number greater than 0');
47
+ process.exit(1);
48
+ }
49
+ const file = await makeForm(spec, maxDepth);
50
+ let fileName = argv['file-name'] || makeFileName(spec) || 'forms.ts';
51
+ if (argv.output) {
52
+ fileName = join(argv.output, fileName);
53
+ }
54
+ saveFile(file, fileName);
55
+ }
56
+ await main();
@@ -1,45 +1,38 @@
1
- /**
2
- * @license
3
- * Licensed under the MIT License, (“the License”); you may not use this
4
- * file except in compliance with the License.
5
- *
6
- * Copyright (c) 2020 Verizon
7
- */
8
- import { Rule } from './rules';
9
- import { OpenAPI } from 'openapi-types';
10
- /**
11
- * Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.
12
- * @param spec - The OpenAPI document.
13
- * @param maxDepth - The maximum depth of the generated forms.
14
- * @returns A string representing the generated forms.
15
- */
16
- export declare function makeForm(spec: OpenAPI.Document, maxDepth?: number): string;
17
- /**
18
- * Adds a validation rule for a form field.
19
- * @param rule - The validation rule to add.
20
- */
21
- export declare function addRule(rule: Rule): void;
22
- /**
23
- * Removes a validation rule from the form field rules.
24
- * @param rule - The validation rule to remove.
25
- */
26
- export declare function resetRules(): void;
27
- /**
28
- * Generates a file name based on the OpenAPI spec title.
29
- * @param swagger - The OpenAPI document.
30
- * @returns A string representing the file name or undefined if no title is available.
31
- */
32
- export declare function makeFileName(swagger: OpenAPI.Document): string | undefined;
33
- /**
34
- * Saves the generated file using ts-morph.
35
- * @param file - The content of the file to save.
36
- * @param fileName - The name of the file to save.
37
- * @returns A promise that resolves when the file is saved.
38
- */
39
- export declare function saveFile(file: string, fileName: string): Promise<void>;
40
- /**
41
- * Loads an OpenAPI specification from a file or URL.
42
- * @param fileOrUrlPath - The path to the OpenAPI spec file or URL.
43
- * @returns A promise that resolves to the dereferenced OpenAPI document.
44
- */
45
- export declare function loadSpec(fileOrUrlPath: string): Promise<OpenAPI.Document>;
1
+ /**
2
+ * @license
3
+ * Licensed under the MIT License, (“the License”); you may not use this
4
+ * file except in compliance with the License.
5
+ *
6
+ * Copyright (c) 2020 Verizon
7
+ */
8
+ import { type Rule } from './rules';
9
+ import { type OpenAPI } from 'openapi-types';
10
+ /**
11
+ * Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.
12
+ * @param spec - The OpenAPI document.
13
+ * @param maxDepth - The maximum depth of the generated forms.
14
+ * @returns A string representing the generated forms.
15
+ */
16
+ export declare function makeForm(spec: OpenAPI.Document, maxDepth?: number): Promise<string>;
17
+ /**
18
+ * Adds a validation rule for a form field.
19
+ * @param rule - The validation rule to add.
20
+ */
21
+ export declare function addRule(rule: Rule): void;
22
+ /**
23
+ * Removes a validation rule from the form field rules.
24
+ * @param rule - The validation rule to remove.
25
+ */
26
+ export declare function resetRules(): void;
27
+ /**
28
+ * Generates a file name based on the OpenAPI spec title.
29
+ * @param swagger - The OpenAPI document.
30
+ * @returns A string representing the file name or undefined if no title is available.
31
+ */
32
+ export declare function makeFileName(swagger: OpenAPI.Document): string | undefined;
33
+ /**
34
+ * Loads an OpenAPI specification from a file or URL.
35
+ * @param fileOrUrlPath - The path to the OpenAPI spec file or URL.
36
+ * @returns A promise that resolves to the dereferenced OpenAPI document.
37
+ */
38
+ export declare function loadSpec(fileOrUrlPath: string): Promise<OpenAPI.Document>;
@@ -1,265 +1,239 @@
1
- "use strict";
2
- /**
3
- * @license
4
- * Licensed under the MIT License, (“the License”); you may not use this
5
- * file except in compliance with the License.
6
- *
7
- * Copyright (c) 2020 Verizon
8
- */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.loadSpec = exports.saveFile = exports.makeFileName = exports.resetRules = exports.addRule = exports.makeForm = void 0;
14
- const ts_morph_1 = require("ts-morph");
15
- const prettier_1 = __importDefault(require("prettier"));
16
- const camelcase_1 = __importDefault(require("camelcase"));
17
- const rules_1 = require("./rules");
18
- const swagger_parser_1 = __importDefault(require("@apidevtools/swagger-parser"));
19
- const DEFAULT_RULES = [rules_1.requiredRule, rules_1.patternRule, rules_1.minLengthRule, rules_1.maxLengthRule, rules_1.emailRule, rules_1.minimumRule, rules_1.maximumRule];
20
- const NEEDED_IMPORTS = `import { FormGroup, FormControl, Validators, FormArray } from '@angular/forms'; \n`;
21
- let rules = [...DEFAULT_RULES];
22
- let MAX_DEPTH = 2;
23
- /**
24
- * Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.
25
- * @param spec - The OpenAPI document.
26
- * @param maxDepth - The maximum depth of the generated forms.
27
- * @returns A string representing the generated forms.
28
- */
29
- function makeForm(spec, maxDepth) {
30
- var _a;
31
- let definitions;
32
- MAX_DEPTH = maxDepth !== null && maxDepth !== void 0 ? maxDepth : MAX_DEPTH;
33
- if ('definitions' in spec) {
34
- definitions = spec.definitions;
35
- }
36
- else if ('components' in spec) {
37
- definitions = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.schemas;
38
- }
39
- else {
40
- throw new Error('Cannot find schemas/definitions');
41
- }
42
- if (!definitions) {
43
- throw new Error('Cannot find schemas/definitions');
44
- }
45
- let body = NEEDED_IMPORTS + '\n\n';
46
- for (const [key, value] of Object.entries(definitions)) {
47
- if (value) {
48
- body += makeDefinition(key, value);
49
- }
50
- }
51
- return prettier_1.default.format(body, { parser: 'typescript', singleQuote: true });
52
- }
53
- exports.makeForm = makeForm;
54
- /**
55
- * Creates a form definition for a given OpenAPI definition.
56
- * @param definitionName - The name of the definition.
57
- * @param definition - The OpenAPI definition object.
58
- * @returns A string representing the form definition.
59
- */
60
- function makeDefinition(definitionName, definition) {
61
- return `export const ${(0, camelcase_1.default)(definitionName)}Form = () => new FormGroup({
62
- ${makeFieldsBody(definition, 0)}
63
- });\n`;
64
- }
65
- /**
66
- * Generates the body of fields for a form based on the provided definition.
67
- * @param definition - The OpenAPI definition object containing properties.
68
- * @param depth - The current depth of recursion.
69
- * @returns An array of strings representing the form fields.
70
- */
71
- function makeFieldsBody(definition, depth) {
72
- if (depth >= MAX_DEPTH)
73
- return [];
74
- depth++;
75
- return [...extractPropertiesFields(definition, depth), ...extractAllOfFields(definition, depth)];
76
- }
77
- /**
78
- * Extracts fields from the properties of a given OpenAPI definition.
79
- * @param definition - The OpenAPI definition object.
80
- * @param depth - The current depth of recursion.
81
- * @returns An array of strings representing the form fields.
82
- */
83
- function extractPropertiesFields(definition, depth) {
84
- var _a;
85
- if (!('properties' in definition) || !definition.properties)
86
- return [];
87
- const fields = [];
88
- for (const [fieldName, fieldValue] of Object.entries(definition.properties)) {
89
- const field = makeField(fieldName, fieldValue, !!((_a = definition.required) === null || _a === void 0 ? void 0 : _a.includes(fieldName)), depth);
90
- if (field !== '') {
91
- fields.push(field);
92
- }
93
- }
94
- return fields;
95
- }
96
- /**
97
- * Extracts fields from the allOf properties of a given OpenAPI definition.
98
- * @param definition - The OpenAPI definition object.
99
- * @param depth - The current depth of recursion.
100
- * @returns An array of strings representing the form fields.
101
- */
102
- function extractAllOfFields(definition, depth) {
103
- var _a, _b, _c;
104
- if (!('allOf' in definition) || !Array.isArray(definition.allOf))
105
- return [];
106
- const fields = [];
107
- for (const subSchema of definition.allOf) {
108
- if ('$ref' in subSchema) {
109
- const refName = subSchema.$ref.split('/').pop();
110
- const refSchema = ((_a = definition.definitions) === null || _a === void 0 ? void 0 : _a[refName]) || ((_c = (_b = definition.components) === null || _b === void 0 ? void 0 : _b.schemas) === null || _c === void 0 ? void 0 : _c[refName]);
111
- if (refSchema) {
112
- fields.push(...makeFieldsBody(refSchema, depth));
113
- }
114
- }
115
- else if ('type' in subSchema && subSchema.type === 'object') {
116
- subSchema.required = definition.required;
117
- fields.push(...makeFieldsBody(subSchema, depth));
118
- }
119
- else {
120
- fields.push(...makeFieldsBody(subSchema, depth));
121
- }
122
- }
123
- return fields;
124
- }
125
- /**
126
- * Creates a form field for a given OpenAPI property.
127
- * @param fieldName - The name of the field.
128
- * @param property - The OpenAPI property object.
129
- * @param isRequired - Indicates if the field is required.
130
- * @param depth - The current depth of recursion.
131
- * @returns A string representing the form field.
132
- */
133
- function makeField(fieldName, property, isRequired, depth) {
134
- let fieldRepesentation;
135
- if (property.allOf) {
136
- fieldRepesentation = makeField(fieldName, property.allOf, isRequired, depth);
137
- }
138
- else if (property.type === 'array') {
139
- fieldRepesentation = makeArrayField(fieldName, property, isRequired, depth);
140
- }
141
- else if (property.type === 'object') {
142
- fieldRepesentation = makeObjectField(fieldName, property, isRequired, depth);
143
- }
144
- else {
145
- fieldRepesentation = makePrimitiveField(fieldName, property, isRequired);
146
- }
147
- return fieldRepesentation;
148
- }
149
- /**
150
- * Creates a form array field for a given OpenAPI array schema.
151
- * @param fieldName - The name of the field.
152
- * @param property - The OpenAPI array schema object.
153
- * @param isRequired - Indicates if the field is required.
154
- * @param depth - The current depth of recursion.
155
- * @returns A string representing the form array field.
156
- */
157
- function makeArrayField(fieldName, property, isRequired, depth) {
158
- var _a;
159
- const itemDefinition = property.items;
160
- const minItems = (_a = property['minItems']) !== null && _a !== void 0 ? _a : 1;
161
- const items = [];
162
- if (itemDefinition['type'] === 'object') {
163
- for (let i = 0; i <= minItems; i++) {
164
- items.push(`new FormGroup({${makeFieldsBody(itemDefinition, depth)}})`);
165
- }
166
- }
167
- else {
168
- const _dummyProps = {
169
- properties: {
170
- dummy: itemDefinition
171
- }
172
- };
173
- const value = 'default' in _dummyProps.properties.dummy ? `'${_dummyProps.properties.dummy.default}'` : null;
174
- for (let i = 1; i <= minItems; i++) {
175
- items.push(`new FormControl(${value}, [${makeFieldRules('dummy', _dummyProps, isRequired)}])`);
176
- }
177
- }
178
- return `"${fieldName}": new FormArray([${items.join(',')}])`;
179
- }
180
- /**
181
- * Creates a form object field for a given OpenAPI object schema.
182
- * @param fieldName - The name of the field.
183
- * @param property - The OpenAPI object schema object.
184
- * @param isRequired - Indicates if the field is required.
185
- * @param depth - The current depth of recursion.
186
- * @returns A string representing the form object field.
187
- */
188
- function makeObjectField(fieldName, property, isRequired, depth) {
189
- const groupBody = makeFieldsBody(property, depth);
190
- return `"${fieldName}": new FormGroup({${groupBody}}, [${makeFieldRules(fieldName, property, isRequired)}])`;
191
- }
192
- /**
193
- * Creates a form control for a primitive field based on its OpenAPI definition.
194
- * @param fieldName - The name of the field.
195
- * @param property - The OpenAPI property object.
196
- * @param isRequired - Indicates if the field is required.
197
- * @returns A string representing the form control.
198
- */
199
- function makePrimitiveField(fieldName, property, isRequired) {
200
- const value = 'default' in property ? `'${property.default}'` : null;
201
- return `"${fieldName}": new FormControl(${value}, [${makeFieldRules(fieldName, property, isRequired)}])`;
202
- }
203
- /**
204
- * Creates validation rules for a form field based on its OpenAPI definition.
205
- * @param fieldName - The name of the field.
206
- * @param property - The OpenAPI property object.
207
- * @param isRequired - Indicates if the field is required.
208
- * @returns A string representing the validation rules.
209
- */
210
- function makeFieldRules(fieldName, property, isRequired) {
211
- return rules
212
- .map(rule => rule(fieldName, property, isRequired))
213
- .filter(item => item != '')
214
- .join();
215
- }
216
- /**
217
- * Adds a validation rule for a form field.
218
- * @param rule - The validation rule to add.
219
- */
220
- function addRule(rule) {
221
- rules.push(rule);
222
- }
223
- exports.addRule = addRule;
224
- /**
225
- * Removes a validation rule from the form field rules.
226
- * @param rule - The validation rule to remove.
227
- */
228
- function resetRules() {
229
- rules = [...DEFAULT_RULES];
230
- }
231
- exports.resetRules = resetRules;
232
- /**
233
- * Generates a file name based on the OpenAPI spec title.
234
- * @param swagger - The OpenAPI document.
235
- * @returns A string representing the file name or undefined if no title is available.
236
- */
237
- function makeFileName(swagger) {
238
- var _a;
239
- if ((_a = swagger.info) === null || _a === void 0 ? void 0 : _a.title) {
240
- return `${(0, camelcase_1.default)(swagger.info.title)}.ts`;
241
- }
242
- }
243
- exports.makeFileName = makeFileName;
244
- /**
245
- * Saves the generated file using ts-morph.
246
- * @param file - The content of the file to save.
247
- * @param fileName - The name of the file to save.
248
- * @returns A promise that resolves when the file is saved.
249
- */
250
- async function saveFile(file, fileName) {
251
- const project = new ts_morph_1.Project();
252
- project.createSourceFile(fileName, file, { overwrite: true });
253
- return project.save();
254
- }
255
- exports.saveFile = saveFile;
256
- /**
257
- * Loads an OpenAPI specification from a file or URL.
258
- * @param fileOrUrlPath - The path to the OpenAPI spec file or URL.
259
- * @returns A promise that resolves to the dereferenced OpenAPI document.
260
- */
261
- async function loadSpec(fileOrUrlPath) {
262
- const parser = new swagger_parser_1.default();
263
- return parser.dereference(fileOrUrlPath);
264
- }
265
- exports.loadSpec = loadSpec;
1
+ /**
2
+ * @license
3
+ * Licensed under the MIT License, (“the License”); you may not use this
4
+ * file except in compliance with the License.
5
+ *
6
+ * Copyright (c) 2020 Verizon
7
+ */
8
+ import * as prettier from 'prettier';
9
+ import camelcase from 'camelcase';
10
+ import { requiredRule, patternRule, minLengthRule, maxLengthRule, emailRule, minimumRule, maximumRule, } from './rules';
11
+ import * as SwaggerParser from '@apidevtools/swagger-parser';
12
+ import { OpenAPIV3 } from 'openapi-types';
13
+ const DEFAULT_RULES = [requiredRule, patternRule, minLengthRule, maxLengthRule, emailRule, minimumRule, maximumRule];
14
+ const NEEDED_IMPORTS = `import { FormGroup, FormControl, Validators, FormArray } from '@angular/forms'; \n`;
15
+ let rules = [...DEFAULT_RULES];
16
+ let MAX_DEPTH = 2;
17
+ /**
18
+ * Generates Angular ReactiveForms from an OpenAPI v2 or v3 spec.
19
+ * @param spec - The OpenAPI document.
20
+ * @param maxDepth - The maximum depth of the generated forms.
21
+ * @returns A string representing the generated forms.
22
+ */
23
+ export async function makeForm(spec, maxDepth) {
24
+ let definitions;
25
+ MAX_DEPTH = maxDepth ?? MAX_DEPTH;
26
+ if ('definitions' in spec) {
27
+ definitions = spec.definitions;
28
+ }
29
+ else if ('components' in spec) {
30
+ definitions = spec.components?.schemas;
31
+ }
32
+ else {
33
+ throw new Error('Cannot find schemas/definitions');
34
+ }
35
+ if (!definitions) {
36
+ throw new Error('Cannot find schemas/definitions');
37
+ }
38
+ let body = NEEDED_IMPORTS + '\n\n';
39
+ for (const [key, value] of Object.entries(definitions)) {
40
+ if (value) {
41
+ body += makeDefinition(key, value);
42
+ }
43
+ }
44
+ return await prettier.format(body, { parser: 'typescript', singleQuote: true });
45
+ }
46
+ /**
47
+ * Creates a form definition for a given OpenAPI definition.
48
+ * @param definitionName - The name of the definition.
49
+ * @param definition - The OpenAPI definition object.
50
+ * @returns A string representing the form definition.
51
+ */
52
+ function makeDefinition(definitionName, definition) {
53
+ return `export const ${camelcase(definitionName)}Form = () => new FormGroup({
54
+ ${makeFieldsBody({ ...definition }, 0)}
55
+ });\n`;
56
+ }
57
+ /**
58
+ * Generates the body of fields for a form based on the provided definition.
59
+ * @param definition - The OpenAPI definition object containing properties.
60
+ * @param depth - The current depth of recursion.
61
+ * @returns An array of strings representing the form fields.
62
+ */
63
+ function makeFieldsBody(definition, depth) {
64
+ if (depth >= MAX_DEPTH)
65
+ return [];
66
+ depth++;
67
+ return [...extractPropertiesFields(definition, depth), ...extractAllOfFields(definition, depth)];
68
+ }
69
+ /**
70
+ * Extracts fields from the properties of a given OpenAPI definition.
71
+ * @param definition - The OpenAPI definition object.
72
+ * @param depth - The current depth of recursion.
73
+ * @returns An array of strings representing the form fields.
74
+ */
75
+ function extractPropertiesFields(definition, depth) {
76
+ if (!('properties' in definition) || !definition.properties)
77
+ return [];
78
+ const fields = [];
79
+ for (const [fieldName, fieldValue] of Object.entries(definition.properties)) {
80
+ const field = makeField(fieldName, fieldValue, !!definition.required?.includes(fieldName), depth);
81
+ if (field !== '') {
82
+ fields.push(field);
83
+ }
84
+ }
85
+ return fields;
86
+ }
87
+ /**
88
+ * Extracts fields from the allOf properties of a given OpenAPI definition.
89
+ * @param definition - The OpenAPI definition object.
90
+ * @param depth - The current depth of recursion.
91
+ * @returns An array of strings representing the form fields.
92
+ */
93
+ function extractAllOfFields(definition, depth) {
94
+ if (!('allOf' in definition) || !Array.isArray(definition.allOf))
95
+ return [];
96
+ const fields = [];
97
+ const definitionRequired = definition.required ?? [];
98
+ for (const subSchema of definition.allOf) {
99
+ if ('$ref' in subSchema) {
100
+ const refName = subSchema.$ref.split('/').pop();
101
+ const refSchema = definition.definitions?.[refName] || definition.components?.schemas?.[refName];
102
+ if (refSchema) {
103
+ refSchema.required = [...refSchema.required, ...definitionRequired];
104
+ fields.push(...makeFieldsBody(refSchema, depth));
105
+ }
106
+ }
107
+ else if ('type' in subSchema && subSchema.type === 'object') {
108
+ subSchema.required = [...(subSchema.required ?? []), ...definitionRequired];
109
+ fields.push(...makeFieldsBody(subSchema, depth));
110
+ }
111
+ else {
112
+ fields.push(...makeFieldsBody(subSchema, depth));
113
+ }
114
+ }
115
+ return fields;
116
+ }
117
+ /**
118
+ * Creates a form field for a given OpenAPI property.
119
+ * @param fieldName - The name of the field.
120
+ * @param property - The OpenAPI property object.
121
+ * @param isRequired - Indicates if the field is required.
122
+ * @param depth - The current depth of recursion.
123
+ * @returns A string representing the form field.
124
+ */
125
+ function makeField(fieldName, property, isRequired, depth) {
126
+ let fieldRepesentation;
127
+ if (property.allOf) {
128
+ fieldRepesentation = makeField(fieldName, property.allOf, isRequired, depth);
129
+ }
130
+ else if (property.type === 'array') {
131
+ fieldRepesentation = makeArrayField(fieldName, property, isRequired, depth);
132
+ }
133
+ else if (property.type === 'object') {
134
+ fieldRepesentation = makeObjectField(fieldName, property, isRequired, depth);
135
+ }
136
+ else {
137
+ fieldRepesentation = makePrimitiveField(fieldName, property, isRequired);
138
+ }
139
+ return fieldRepesentation;
140
+ }
141
+ /**
142
+ * Creates a form array field for a given OpenAPI array schema.
143
+ * @param fieldName - The name of the field.
144
+ * @param property - The OpenAPI array schema object.
145
+ * @param isRequired - Indicates if the field is required.
146
+ * @param depth - The current depth of recursion.
147
+ * @returns A string representing the form array field.
148
+ */
149
+ function makeArrayField(fieldName, property, isRequired, depth) {
150
+ const itemDefinition = property.items;
151
+ const minItems = property['minItems'] ?? 1;
152
+ const items = [];
153
+ if (itemDefinition['type'] === 'object') {
154
+ for (let i = 0; i <= minItems; i++) {
155
+ items.push(`new FormGroup({${makeFieldsBody(itemDefinition, depth)}})`);
156
+ }
157
+ }
158
+ else {
159
+ const _dummyProps = {
160
+ properties: {
161
+ dummy: itemDefinition,
162
+ },
163
+ };
164
+ const value = 'default' in _dummyProps.properties.dummy ? `'${_dummyProps.properties.dummy.default}'` : null;
165
+ for (let i = 1; i <= minItems; i++) {
166
+ items.push(`new FormControl(${value}, [${makeFieldRules('dummy', _dummyProps, isRequired)}])`);
167
+ }
168
+ }
169
+ return `"${fieldName}": new FormArray([${items.join(',')}])`;
170
+ }
171
+ /**
172
+ * Creates a form object field for a given OpenAPI object schema.
173
+ * @param fieldName - The name of the field.
174
+ * @param property - The OpenAPI object schema object.
175
+ * @param isRequired - Indicates if the field is required.
176
+ * @param depth - The current depth of recursion.
177
+ * @returns A string representing the form object field.
178
+ */
179
+ function makeObjectField(fieldName, property, isRequired, depth) {
180
+ const groupBody = makeFieldsBody(property, depth);
181
+ return `"${fieldName}": new FormGroup({${groupBody}}, [${makeFieldRules(fieldName, property, isRequired)}])`;
182
+ }
183
+ /**
184
+ * Creates a form control for a primitive field based on its OpenAPI definition.
185
+ * @param fieldName - The name of the field.
186
+ * @param property - The OpenAPI property object.
187
+ * @param isRequired - Indicates if the field is required.
188
+ * @returns A string representing the form control.
189
+ */
190
+ function makePrimitiveField(fieldName, property, isRequired) {
191
+ const value = 'default' in property ? `'${property.default}'` : null;
192
+ return `"${fieldName}": new FormControl(${value}, [${makeFieldRules(fieldName, property, isRequired)}])`;
193
+ }
194
+ /**
195
+ * Creates validation rules for a form field based on its OpenAPI definition.
196
+ * @param fieldName - The name of the field.
197
+ * @param property - The OpenAPI property object.
198
+ * @param isRequired - Indicates if the field is required.
199
+ * @returns A string representing the validation rules.
200
+ */
201
+ function makeFieldRules(fieldName, property, isRequired) {
202
+ return rules
203
+ .map((rule) => rule(fieldName, property, isRequired))
204
+ .filter((item) => item != '')
205
+ .join();
206
+ }
207
+ /**
208
+ * Adds a validation rule for a form field.
209
+ * @param rule - The validation rule to add.
210
+ */
211
+ export function addRule(rule) {
212
+ rules.push(rule);
213
+ }
214
+ /**
215
+ * Removes a validation rule from the form field rules.
216
+ * @param rule - The validation rule to remove.
217
+ */
218
+ export function resetRules() {
219
+ rules = [...DEFAULT_RULES];
220
+ }
221
+ /**
222
+ * Generates a file name based on the OpenAPI spec title.
223
+ * @param swagger - The OpenAPI document.
224
+ * @returns A string representing the file name or undefined if no title is available.
225
+ */
226
+ export function makeFileName(swagger) {
227
+ if (swagger.info?.title) {
228
+ return `${camelcase(swagger.info.title)}.ts`;
229
+ }
230
+ }
231
+ /**
232
+ * Loads an OpenAPI specification from a file or URL.
233
+ * @param fileOrUrlPath - The path to the OpenAPI spec file or URL.
234
+ * @returns A promise that resolves to the dereferenced OpenAPI document.
235
+ */
236
+ export async function loadSpec(fileOrUrlPath) {
237
+ const parser = new SwaggerParser();
238
+ return parser.dereference(fileOrUrlPath);
239
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export * from './generator-lib';
2
- export * from './rules';
1
+ export * from './generator-lib';
2
+ export * from './rules';
3
+ export * from './file-utils';
package/dist/index.js CHANGED
@@ -1,18 +1,3 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./generator-lib"), exports);
18
- __exportStar(require("./rules"), exports);
1
+ export * from './generator-lib';
2
+ export * from './rules';
3
+ export * from './file-utils';
package/dist/rules.d.ts CHANGED
@@ -1,28 +1,28 @@
1
- /**
2
- * @license
3
- * Licensed under the MIT License, (“the License”); you may not use this
4
- * file except in compliance with the License.
5
- *
6
- * Copyright (c) 2020 Verizon
7
- */
8
- import { OpenAPIV2, OpenAPIV3 } from 'openapi-types';
9
- export declare type Property = {
10
- format?: string;
11
- pattern?: string;
12
- maxLength?: number;
13
- minLength?: number;
14
- minimum?: number;
15
- maximum?: number;
16
- };
17
- export declare type Properties = Record<string, Property>;
18
- export declare type Rule = (fieldName: string, properties: SchemaProperty, isRequired: boolean) => string;
19
- export declare type Definitions = OpenAPIV2.DefinitionsObject | Record<string, OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject> | undefined;
20
- export declare type Definition = OpenAPIV2.DefinitionsObject | OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
21
- export declare type SchemaProperty = OpenAPIV2.SchemaObject | OpenAPIV3.SchemaObject;
22
- export declare function requiredRule(fieldName: string, property: SchemaProperty, isRequired: boolean): string;
23
- export declare function patternRule(fieldName: string, property: SchemaProperty): string;
24
- export declare function minLengthRule(fieldName: string, property: SchemaProperty): string;
25
- export declare function maxLengthRule(fieldName: string, property: SchemaProperty): string;
26
- export declare function emailRule(fieldName: string, property: SchemaProperty): string;
27
- export declare function minimumRule(fieldName: string, property: SchemaProperty): string;
28
- export declare function maximumRule(fieldName: string, property: SchemaProperty): string;
1
+ /**
2
+ * @license
3
+ * Licensed under the MIT License, (“the License”); you may not use this
4
+ * file except in compliance with the License.
5
+ *
6
+ * Copyright (c) 2020 Verizon
7
+ */
8
+ import { OpenAPIV2, OpenAPIV3 } from 'openapi-types';
9
+ export type Property = {
10
+ format?: string;
11
+ pattern?: string;
12
+ maxLength?: number;
13
+ minLength?: number;
14
+ minimum?: number;
15
+ maximum?: number;
16
+ };
17
+ export type Properties = Record<string, Property>;
18
+ export type Rule = (fieldName: string, properties: SchemaProperty, isRequired: boolean) => string;
19
+ export type Definitions = OpenAPIV2.DefinitionsObject | Record<string, OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject> | undefined;
20
+ export type Definition = OpenAPIV2.DefinitionsObject | OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
21
+ export type SchemaProperty = OpenAPIV2.SchemaObject | OpenAPIV3.SchemaObject;
22
+ export declare function requiredRule(fieldName: string, property: SchemaProperty, isRequired: boolean): string;
23
+ export declare function patternRule(fieldName: string, property: SchemaProperty): string;
24
+ export declare function minLengthRule(fieldName: string, property: SchemaProperty): string;
25
+ export declare function maxLengthRule(fieldName: string, property: SchemaProperty): string;
26
+ export declare function emailRule(fieldName: string, property: SchemaProperty): string;
27
+ export declare function minimumRule(fieldName: string, property: SchemaProperty): string;
28
+ export declare function maximumRule(fieldName: string, property: SchemaProperty): string;
package/dist/rules.js CHANGED
@@ -1,44 +1,35 @@
1
- "use strict";
2
- /**
3
- * @license
4
- * Licensed under the MIT License, (“the License”); you may not use this
5
- * file except in compliance with the License.
6
- *
7
- * Copyright (c) 2020 Verizon
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.maximumRule = exports.minimumRule = exports.emailRule = exports.maxLengthRule = exports.minLengthRule = exports.patternRule = exports.requiredRule = void 0;
11
- function hasMetadata(fieldName, property, metadataName) {
12
- return property.hasOwnProperty(metadataName);
13
- }
14
- function abstractRule(fieldName, property, ruleName) {
15
- return hasMetadata(fieldName, property, ruleName) ? `Validators.${ruleName}(${property[ruleName]})` : '';
16
- }
17
- function requiredRule(fieldName, property, isRequired) {
18
- return isRequired ? `Validators.required` : '';
19
- }
20
- exports.requiredRule = requiredRule;
21
- function patternRule(fieldName, property) {
22
- return hasMetadata(fieldName, property, 'pattern') ? `Validators.pattern(/${property['pattern']}/)` : '';
23
- }
24
- exports.patternRule = patternRule;
25
- function minLengthRule(fieldName, property) {
26
- return abstractRule(fieldName, property, 'minLength');
27
- }
28
- exports.minLengthRule = minLengthRule;
29
- function maxLengthRule(fieldName, property) {
30
- return abstractRule(fieldName, property, 'maxLength');
31
- }
32
- exports.maxLengthRule = maxLengthRule;
33
- function emailRule(fieldName, property) {
34
- return property.format === 'email' ? `Validators.email` : '';
35
- }
36
- exports.emailRule = emailRule;
37
- function minimumRule(fieldName, property) {
38
- return hasMetadata(fieldName, property, 'minimum') ? `Validators.min(${property['minimum']})` : '';
39
- }
40
- exports.minimumRule = minimumRule;
41
- function maximumRule(fieldName, property) {
42
- return hasMetadata(fieldName, property, 'maximum') ? `Validators.max(${property['maximum']})` : '';
43
- }
44
- exports.maximumRule = maximumRule;
1
+ /**
2
+ * @license
3
+ * Licensed under the MIT License, (“the License”); you may not use this
4
+ * file except in compliance with the License.
5
+ *
6
+ * Copyright (c) 2020 Verizon
7
+ */
8
+ import { OpenAPIV2, OpenAPIV3 } from 'openapi-types';
9
+ function hasMetadata(fieldName, property, metadataName) {
10
+ return property.hasOwnProperty(metadataName);
11
+ }
12
+ function abstractRule(fieldName, property, ruleName) {
13
+ return hasMetadata(fieldName, property, ruleName) ? `Validators.${ruleName}(${property[ruleName]})` : '';
14
+ }
15
+ export function requiredRule(fieldName, property, isRequired) {
16
+ return isRequired ? `Validators.required` : '';
17
+ }
18
+ export function patternRule(fieldName, property) {
19
+ return hasMetadata(fieldName, property, 'pattern') ? `Validators.pattern(/${property['pattern']}/)` : '';
20
+ }
21
+ export function minLengthRule(fieldName, property) {
22
+ return abstractRule(fieldName, property, 'minLength');
23
+ }
24
+ export function maxLengthRule(fieldName, property) {
25
+ return abstractRule(fieldName, property, 'maxLength');
26
+ }
27
+ export function emailRule(fieldName, property) {
28
+ return property.format === 'email' ? `Validators.email` : '';
29
+ }
30
+ export function minimumRule(fieldName, property) {
31
+ return hasMetadata(fieldName, property, 'minimum') ? `Validators.min(${property['minimum']})` : '';
32
+ }
33
+ export function maximumRule(fieldName, property) {
34
+ return hasMetadata(fieldName, property, 'maximum') ? `Validators.max(${property['maximum']})` : '';
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmm-devkit/ngx-form-generator",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Generates an Angular ReactiveForm from a Swagger or OpenAPI definition",
5
5
  "main": "dist/generator-lib.js",
6
6
  "repository": "github:jmm-devkit/ngx-form-generator",
@@ -25,55 +25,16 @@
25
25
  "typescript"
26
26
  ],
27
27
  "devDependencies": {
28
- "@commitlint/cli": "^17.1.2",
29
- "@commitlint/config-conventional": "^17.0.3",
30
- "@types/camelcase": "^4.1.0",
31
- "@types/jasmine": "^4.3.0",
32
- "@types/node-fetch": "^2.5.5",
33
- "@types/prettier": "^1.19.1",
34
- "@types/yaml": "^1.2.0",
35
- "@types/yargs": "^17.0.13",
36
- "@typescript-eslint/eslint-plugin": "^5.0.0",
37
- "@typescript-eslint/parser": "^5.0.0",
38
- "commitiquette": "^1.2.1",
39
- "commitizen": "^4.2.4",
40
- "eslint": "^8.0.1",
41
- "eslint-config-prettier": "^6.10.1",
42
- "husky": "^7.0.4",
43
- "jasmine": "^4.4.0",
44
- "lint-staged": "^13.0.3",
45
- "openapi-types": "^7.0.1",
46
- "typescript": "^4.8.4"
28
+ "@eslint/js": "^10.0.1",
29
+ "eslint": "^10.0.0",
30
+ "ts-node": "^10.9.2",
31
+ "typescript": "^5.9.3",
32
+ "typescript-eslint": "^8.56.0"
47
33
  },
48
34
  "dependencies": {
49
- "@apidevtools/swagger-parser": "^10.0.2",
50
- "camelcase": "^5.0.0",
51
- "prettier": "^1.19.1",
52
- "ts-morph": "^16.0.0",
53
- "yaml": "^2.1.3",
54
- "yargs": "^17.6.0"
55
- },
56
- "husky": {
57
- "hooks": {
58
- "pre-commit": "lint-staged",
59
- "prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",
60
- "commit-msg": "commitLint -E HUSKY_GIT_PARAMS"
61
- }
62
- },
63
- "lint-staged": {
64
- "*.{js,css,json,md}": [
65
- "prettier --write",
66
- "git add"
67
- ],
68
- "*.ts": [
69
- "eslint --fix",
70
- "prettier --write",
71
- "git add"
72
- ]
73
- },
74
- "config": {
75
- "commitizen": {
76
- "path": "commitiquette"
77
- }
35
+ "@apidevtools/swagger-parser": "^12.1.0",
36
+ "camelcase": "^9.0.0",
37
+ "openapi-types": "^12.1.3",
38
+ "prettier": "^3.8.1"
78
39
  }
79
- }
40
+ }