ts-runtime-validation 1.0.3 → 1.1.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 +6 -5
- package/dist/SchemaGenerator.js +241 -0
- package/dist/SchemaGenerator.js.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/package.json +3 -2
- package/src/SchemaGenerator.ts +56 -61
- package/src/index.ts +2 -0
package/README.md
CHANGED
|
@@ -24,11 +24,12 @@ Ensure your project files containing the schemas you want to validate end with t
|
|
|
24
24
|
Usage: ts-runtime-validation [options]
|
|
25
25
|
|
|
26
26
|
Options:
|
|
27
|
-
--glob
|
|
28
|
-
--rootPath
|
|
29
|
-
--output
|
|
30
|
-
--no-helpers
|
|
31
|
-
|
|
27
|
+
--glob Glob file path of typescript files to generate ts-interface -> json-schema validations - default: *.jsonschema.{ts,tsx}
|
|
28
|
+
--rootPath RootPath of source - default: ./src
|
|
29
|
+
--output Validation schema + typescript interface output directory (relative to root path) - default: ./.ts-runtime-validation
|
|
30
|
+
--no-helpers Only generate JSON schema without typescript helper files
|
|
31
|
+
--additionalProperties Allow additional properties to pass validation (default: false)
|
|
32
|
+
-h, --help display help for command
|
|
32
33
|
```
|
|
33
34
|
|
|
34
35
|
## Example usage of generated ts type validation
|
|
@@ -0,0 +1,241 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.SchemaGenerator = void 0;
|
|
39
|
+
const fdir_1 = require("fdir");
|
|
40
|
+
const fs_1 = __importDefault(require("fs"));
|
|
41
|
+
const picomatch_1 = __importDefault(require("picomatch"));
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const ts_morph_1 = require("ts-morph");
|
|
44
|
+
const tsj = __importStar(require("ts-json-schema-generator"));
|
|
45
|
+
const defaultTsMorphProjectSettings = {
|
|
46
|
+
manipulationSettings: {
|
|
47
|
+
indentationText: ts_morph_1.IndentationText.FourSpaces,
|
|
48
|
+
newLineKind: ts_morph_1.NewLineKind.LineFeed,
|
|
49
|
+
quoteKind: ts_morph_1.QuoteKind.Double,
|
|
50
|
+
usePrefixAndSuffixTextForRename: false,
|
|
51
|
+
useTrailingCommas: true,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const defaultCreateFileOptions = {
|
|
55
|
+
overwrite: true,
|
|
56
|
+
};
|
|
57
|
+
const validationSchemaFileName = "validation.schema.json";
|
|
58
|
+
const schemaDefinitionFileName = "SchemaDefinition.ts";
|
|
59
|
+
class SchemaGenerator {
|
|
60
|
+
constructor(options) {
|
|
61
|
+
this.options = options;
|
|
62
|
+
this.outputPath = path_1.default.join(this.options.rootPath, this.options.output);
|
|
63
|
+
this.jsonSchemaOutputFile = path_1.default.join(this.options.rootPath, this.options.output, validationSchemaFileName);
|
|
64
|
+
this.tsSchemaDefinitionOutputFile = path_1.default.join(this.options.rootPath, this.options.output, schemaDefinitionFileName);
|
|
65
|
+
this.isValidSchemaOutputFile = path_1.default.join(this.options.rootPath, this.options.output, "isSchemaValid.ts");
|
|
66
|
+
this.Generate = () => __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
const { helpers, glob } = this.options;
|
|
68
|
+
const fileList = yield this.getMatchingFiles();
|
|
69
|
+
console.log(`Found ${fileList.length} schema file(s)`);
|
|
70
|
+
if (fileList.length === 0) {
|
|
71
|
+
console.log(`Aborting - no files found with glob: ${glob}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const fileSchemas = yield this.getJsonSchemaMap(fileList);
|
|
75
|
+
console.log(`Generating ${fileSchemas.size} validation schema(s)`);
|
|
76
|
+
if (fileSchemas.size === 0) {
|
|
77
|
+
console.log(`Aborting - no interfaces found: ${glob}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.writeSchemaMapToValidationSchema(fileSchemas);
|
|
81
|
+
if (helpers === false) {
|
|
82
|
+
console.log("Skipping helper file generation");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
yield this.writeSchemaMapToValidationTypes(fileSchemas);
|
|
86
|
+
this.writeValidatorFunction();
|
|
87
|
+
});
|
|
88
|
+
this.getMatchingFiles = () => __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const { glob, rootPath } = this.options;
|
|
90
|
+
const api = new fdir_1.fdir().crawlWithOptions(rootPath, {
|
|
91
|
+
includeBasePath: true,
|
|
92
|
+
includeDirs: false,
|
|
93
|
+
filters: [
|
|
94
|
+
(path) => {
|
|
95
|
+
return picomatch_1.default.isMatch(path, glob, { contains: true });
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
return api.withPromise();
|
|
100
|
+
});
|
|
101
|
+
this.getJsonSchemaMap = (filesList) => __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
const { additionalProperties } = this.options;
|
|
103
|
+
const schemaMap = new Map();
|
|
104
|
+
filesList.forEach((file) => {
|
|
105
|
+
const config = {
|
|
106
|
+
path: file,
|
|
107
|
+
type: "*",
|
|
108
|
+
additionalProperties,
|
|
109
|
+
};
|
|
110
|
+
const schemaGenerator = tsj.createGenerator(config);
|
|
111
|
+
const fileSchemas = schemaGenerator.createSchema(config.type);
|
|
112
|
+
schemaMap.set(file, fileSchemas);
|
|
113
|
+
});
|
|
114
|
+
return schemaMap;
|
|
115
|
+
});
|
|
116
|
+
this.getSchemaVersion = (schemaMap) => {
|
|
117
|
+
var _a;
|
|
118
|
+
const firstEntry = schemaMap.values().next().value;
|
|
119
|
+
return (_a = firstEntry["$schema"]) !== null && _a !== void 0 ? _a : "";
|
|
120
|
+
};
|
|
121
|
+
this.ensureOutputPathExists = () => {
|
|
122
|
+
if (!fs_1.default.existsSync(this.outputPath)) {
|
|
123
|
+
fs_1.default.mkdirSync(this.outputPath, { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
this.writeSchemaMapToValidationSchema = (schemaMap) => {
|
|
127
|
+
const definitions = {};
|
|
128
|
+
schemaMap.forEach((fileSchema) => {
|
|
129
|
+
var _a;
|
|
130
|
+
const defs = (_a = fileSchema.definitions) !== null && _a !== void 0 ? _a : {};
|
|
131
|
+
Object.keys(defs).forEach((key) => {
|
|
132
|
+
if (definitions[key] !== undefined) {
|
|
133
|
+
throw new Error(`Duplicate symbol: ${key} found`);
|
|
134
|
+
}
|
|
135
|
+
const schema = defs[key];
|
|
136
|
+
definitions[key] = schema;
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
const outputBuffer = {
|
|
140
|
+
$schema: this.getSchemaVersion(schemaMap),
|
|
141
|
+
definitions,
|
|
142
|
+
};
|
|
143
|
+
this.ensureOutputPathExists();
|
|
144
|
+
fs_1.default.writeFileSync(this.jsonSchemaOutputFile, JSON.stringify(outputBuffer, null, 4));
|
|
145
|
+
};
|
|
146
|
+
this.writeSchemaMapToValidationTypes = (schemaMap) => __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const project = new ts_morph_1.Project(defaultTsMorphProjectSettings);
|
|
148
|
+
const symbols = [];
|
|
149
|
+
const importMap = new Map();
|
|
150
|
+
schemaMap.forEach((schema, filePath) => {
|
|
151
|
+
var _a;
|
|
152
|
+
const dir = path_1.default.dirname(filePath);
|
|
153
|
+
const fileWithoutExtension = path_1.default.parse(filePath).name;
|
|
154
|
+
const relativeFilePath = path_1.default.relative(this.outputPath, dir);
|
|
155
|
+
const importPath = `${relativeFilePath}/${fileWithoutExtension}`;
|
|
156
|
+
const defs = (_a = schema.definitions) !== null && _a !== void 0 ? _a : {};
|
|
157
|
+
Object.keys(defs).forEach((symbol) => {
|
|
158
|
+
var _a;
|
|
159
|
+
const namedImports = (_a = importMap.get(importPath)) !== null && _a !== void 0 ? _a : [];
|
|
160
|
+
namedImports.push(symbol);
|
|
161
|
+
importMap.set(importPath, namedImports);
|
|
162
|
+
symbols.push(symbol);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
const sourceFile = project.createSourceFile(this.tsSchemaDefinitionOutputFile, {}, defaultCreateFileOptions);
|
|
166
|
+
importMap.forEach((namedImports, importPath) => {
|
|
167
|
+
sourceFile.addImportDeclaration({ namedImports, moduleSpecifier: importPath });
|
|
168
|
+
});
|
|
169
|
+
sourceFile.addVariableStatement({
|
|
170
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
171
|
+
declarations: [
|
|
172
|
+
{
|
|
173
|
+
name: "schemas",
|
|
174
|
+
type: "Record<keyof ISchema, string>",
|
|
175
|
+
initializer: (writer) => {
|
|
176
|
+
writer.writeLine(`{`);
|
|
177
|
+
symbols.forEach((symbol) => {
|
|
178
|
+
writer.writeLine(`["#/definitions/${symbol}"] : "${symbol}",`);
|
|
179
|
+
}),
|
|
180
|
+
writer.writeLine(`}`);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
sourceFile.addInterface({
|
|
186
|
+
kind: ts_morph_1.StructureKind.Interface,
|
|
187
|
+
name: "ISchema",
|
|
188
|
+
isExported: false,
|
|
189
|
+
properties: symbols.map((symbol) => {
|
|
190
|
+
return { name: `readonly ["#/definitions/${symbol}"]`, type: symbol };
|
|
191
|
+
}),
|
|
192
|
+
});
|
|
193
|
+
sourceFile.addExportDeclaration({
|
|
194
|
+
namedExports: ["schemas", "ISchema"],
|
|
195
|
+
});
|
|
196
|
+
yield project.save();
|
|
197
|
+
});
|
|
198
|
+
this.writeValidatorFunction = () => __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const project = new ts_morph_1.Project(defaultTsMorphProjectSettings);
|
|
200
|
+
const sourceFile = project.createSourceFile(this.isValidSchemaOutputFile, {}, defaultCreateFileOptions);
|
|
201
|
+
sourceFile.addImportDeclaration({ namespaceImport: "schema", moduleSpecifier: `./${validationSchemaFileName}` });
|
|
202
|
+
sourceFile.addImportDeclaration({ defaultImport: "Ajv", moduleSpecifier: "ajv" });
|
|
203
|
+
sourceFile.addImportDeclaration({
|
|
204
|
+
namedImports: ["ISchema", "schemas"],
|
|
205
|
+
moduleSpecifier: `./${path_1.default.parse(schemaDefinitionFileName).name}`,
|
|
206
|
+
});
|
|
207
|
+
sourceFile.addVariableStatement({
|
|
208
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
209
|
+
declarations: [
|
|
210
|
+
{
|
|
211
|
+
name: "validator",
|
|
212
|
+
initializer: (writer) => {
|
|
213
|
+
writer.writeLine(`new Ajv({ allErrors: true });`);
|
|
214
|
+
writer.writeLine(`validator.compile(schema)`);
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
});
|
|
219
|
+
sourceFile.addVariableStatement({
|
|
220
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
221
|
+
declarations: [
|
|
222
|
+
{
|
|
223
|
+
name: "isValidSchema",
|
|
224
|
+
initializer: (writer) => {
|
|
225
|
+
writer.writeLine(`<T extends keyof typeof schemas>(data: unknown, schemaKeyRef: T): data is ISchema[T] => {`);
|
|
226
|
+
writer.writeLine(`validator.validate(schemaKeyRef as string, data);`);
|
|
227
|
+
writer.writeLine(`return Boolean(validator.errors) === false;`);
|
|
228
|
+
writer.writeLine(`}`);
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
sourceFile.addExportDeclaration({
|
|
234
|
+
namedExports: ["validator", "isValidSchema"],
|
|
235
|
+
});
|
|
236
|
+
yield project.save();
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
exports.SchemaGenerator = SchemaGenerator;
|
|
241
|
+
//# sourceMappingURL=SchemaGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemaGenerator.js","sourceRoot":"","sources":["../src/SchemaGenerator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const SchemaGenerator_1 = require("./SchemaGenerator");
|
|
5
|
+
const commander_1 = require("commander");
|
|
6
|
+
const defaultGlobPattern = "*.jsonschema.{ts,tsx}";
|
|
7
|
+
const defaultRootPath = "./src";
|
|
8
|
+
const defaultOutputFolder = "./.ts-runtime-validation";
|
|
9
|
+
commander_1.program.option("--glob", `Glob file path of typescript files to generate ts-interface -> json-schema validations - default: ${defaultGlobPattern}`, defaultGlobPattern);
|
|
10
|
+
commander_1.program.option("--rootPath", `RootPath of source - default: ${defaultRootPath}`, defaultRootPath);
|
|
11
|
+
commander_1.program.option("--output", `Validation schema + typescript interface output directory (relative to root path) - default: ${defaultOutputFolder}`, defaultOutputFolder);
|
|
12
|
+
commander_1.program.option("--no-helpers", "Only generate JSON schema without typescript helper files", true);
|
|
13
|
+
commander_1.program.option("--additionalProperties", "Allow additional properties to pass validation", false);
|
|
14
|
+
commander_1.program.parse();
|
|
15
|
+
const options = commander_1.program.opts();
|
|
16
|
+
const generator = new SchemaGenerator_1.SchemaGenerator(options);
|
|
17
|
+
generator.Generate();
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,uDAAoD;AACpD,yCAAoC;AAEpC,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AAEnD,MAAM,eAAe,GAAG,OAAO,CAAC;AAChC,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;AAUvD,mBAAO,CAAC,MAAM,CACV,QAAQ,EACR,qGAAqG,kBAAkB,EAAE,EACzH,kBAAkB,CACrB,CAAC;AACF,mBAAO,CAAC,MAAM,CAAC,YAAY,EAAE,iCAAiC,eAAe,EAAE,EAAE,eAAe,CAAC,CAAC;AAClG,mBAAO,CAAC,MAAM,CACV,UAAU,EACV,gGAAgG,mBAAmB,EAAE,EACrH,mBAAmB,CACtB,CAAC;AACF,mBAAO,CAAC,MAAM,CAAC,cAAc,EAAE,2DAA2D,EAAE,IAAI,CAAC,CAAC;AAClG,mBAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,gDAAgD,EAAE,KAAK,CAAC,CAAC;AAElG,mBAAO,CAAC,KAAK,EAAE,CAAC;AAEhB,MAAM,OAAO,GAAG,mBAAO,CAAC,IAAI,EAAmB,CAAC;AAEhD,MAAM,SAAS,GAAG,IAAI,iCAAe,CAAC,OAAO,CAAC,CAAC;AAC/C,SAAS,CAAC,QAAQ,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-runtime-validation",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"author": "Matthew Duong <thegalah@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"commander": "^9.1.0",
|
|
8
8
|
"fdir": "^5.2.0",
|
|
9
9
|
"picomatch": "^2.3.1",
|
|
10
|
+
"ts-json-schema-generator": "^1.0.0",
|
|
10
11
|
"ts-morph": "^14.0.0",
|
|
11
|
-
"
|
|
12
|
+
"ts-runtime-validation": "../ts-runtime-validation"
|
|
12
13
|
},
|
|
13
14
|
"bin": {
|
|
14
15
|
"ts-runtime-validation": "dist/index.js"
|
package/src/SchemaGenerator.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { fdir } from "fdir";
|
|
2
2
|
import { ICommandOptions } from "./index";
|
|
3
3
|
import { resolve } from "path";
|
|
4
|
-
import * as TJS from "typescript-json-schema";
|
|
5
4
|
import fs from "fs";
|
|
6
5
|
import picomatch from "picomatch";
|
|
7
6
|
import path from "path";
|
|
@@ -16,8 +15,10 @@ import {
|
|
|
16
15
|
ProjectOptions,
|
|
17
16
|
SourceFileCreateOptions,
|
|
18
17
|
} from "ts-morph";
|
|
18
|
+
import * as tsj from "ts-json-schema-generator";
|
|
19
|
+
import { Config, Schema } from "ts-json-schema-generator";
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const defaultTsMorphProjectSettings: ProjectOptions = {
|
|
21
22
|
manipulationSettings: {
|
|
22
23
|
indentationText: IndentationText.FourSpaces,
|
|
23
24
|
newLineKind: NewLineKind.LineFeed,
|
|
@@ -43,17 +44,26 @@ export class SchemaGenerator {
|
|
|
43
44
|
public constructor(private options: ICommandOptions) {}
|
|
44
45
|
|
|
45
46
|
public Generate = async () => {
|
|
46
|
-
const { helpers } = this.options;
|
|
47
|
+
const { helpers, glob } = this.options;
|
|
47
48
|
const fileList = await this.getMatchingFiles();
|
|
49
|
+
|
|
48
50
|
console.log(`Found ${fileList.length} schema file(s)`);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
if (fileList.length === 0) {
|
|
52
|
+
console.log(`Aborting - no files found with glob: ${glob}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const fileSchemas = await this.getJsonSchemaMap(fileList);
|
|
56
|
+
console.log(`Generating ${fileSchemas.size} validation schema(s)`);
|
|
57
|
+
if (fileSchemas.size === 0) {
|
|
58
|
+
console.log(`Aborting - no interfaces found: ${glob}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.writeSchemaMapToValidationSchema(fileSchemas);
|
|
52
62
|
if (helpers === false) {
|
|
53
63
|
console.log("Skipping helper file generation");
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
56
|
-
await this.writeSchemaMapToValidationTypes(
|
|
66
|
+
await this.writeSchemaMapToValidationTypes(fileSchemas);
|
|
57
67
|
this.writeValidatorFunction();
|
|
58
68
|
};
|
|
59
69
|
|
|
@@ -72,41 +82,23 @@ export class SchemaGenerator {
|
|
|
72
82
|
};
|
|
73
83
|
|
|
74
84
|
private getJsonSchemaMap = async (filesList: Array<string>) => {
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const compilerOptions: TJS.CompilerOptions = {
|
|
89
|
-
strictNullChecks: true,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const program = TJS.getProgramFromFiles(files, compilerOptions);
|
|
93
|
-
|
|
94
|
-
const generator = TJS.buildGenerator(program, settings);
|
|
95
|
-
const userDefinedSymbols = generator?.getMainFileSymbols(program) ?? [];
|
|
96
|
-
userDefinedSymbols.forEach((symbol) => {
|
|
97
|
-
if (schemaMap.has(symbol)) {
|
|
98
|
-
throw new Error(`Duplicate symbol "${symbol}" found.`);
|
|
99
|
-
}
|
|
100
|
-
const schema = generator?.getSchemaForSymbol(symbol);
|
|
101
|
-
if (schema) {
|
|
102
|
-
schemaMap.set(symbol, schema);
|
|
103
|
-
}
|
|
85
|
+
const { additionalProperties } = this.options;
|
|
86
|
+
const schemaMap = new Map<string, Schema>();
|
|
87
|
+
filesList.forEach((file) => {
|
|
88
|
+
const config: Config = {
|
|
89
|
+
path: file,
|
|
90
|
+
type: "*",
|
|
91
|
+
additionalProperties,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const schemaGenerator = tsj.createGenerator(config);
|
|
95
|
+
const fileSchemas = schemaGenerator.createSchema(config.type);
|
|
96
|
+
schemaMap.set(file, fileSchemas);
|
|
104
97
|
});
|
|
105
|
-
|
|
106
98
|
return schemaMap;
|
|
107
99
|
};
|
|
108
100
|
|
|
109
|
-
private getSchemaVersion = (schemaMap: Map<string,
|
|
101
|
+
private getSchemaVersion = (schemaMap: Map<string, Schema>) => {
|
|
110
102
|
const firstEntry = schemaMap.values().next().value;
|
|
111
103
|
return firstEntry["$schema"] ?? "";
|
|
112
104
|
};
|
|
@@ -117,12 +109,20 @@ export class SchemaGenerator {
|
|
|
117
109
|
}
|
|
118
110
|
};
|
|
119
111
|
|
|
120
|
-
private writeSchemaMapToValidationSchema = (schemaMap: Map<string,
|
|
121
|
-
const definitions: { [id: string]:
|
|
122
|
-
schemaMap.forEach((
|
|
123
|
-
|
|
112
|
+
private writeSchemaMapToValidationSchema = (schemaMap: Map<string, Schema>) => {
|
|
113
|
+
const definitions: { [id: string]: Schema } = {};
|
|
114
|
+
schemaMap.forEach((fileSchema) => {
|
|
115
|
+
const defs = fileSchema.definitions ?? {};
|
|
116
|
+
|
|
117
|
+
Object.keys(defs).forEach((key) => {
|
|
118
|
+
if (definitions[key] !== undefined) {
|
|
119
|
+
throw new Error(`Duplicate symbol: ${key} found`);
|
|
120
|
+
}
|
|
121
|
+
const schema = defs[key] as Schema;
|
|
122
|
+
definitions[key] = schema;
|
|
123
|
+
});
|
|
124
124
|
});
|
|
125
|
-
const outputBuffer:
|
|
125
|
+
const outputBuffer: Schema = {
|
|
126
126
|
$schema: this.getSchemaVersion(schemaMap),
|
|
127
127
|
definitions,
|
|
128
128
|
};
|
|
@@ -131,32 +131,29 @@ export class SchemaGenerator {
|
|
|
131
131
|
fs.writeFileSync(this.jsonSchemaOutputFile, JSON.stringify(outputBuffer, null, 4));
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
-
private writeSchemaMapToValidationTypes = async (schemaMap: Map<string,
|
|
135
|
-
const project = new Project(
|
|
136
|
-
|
|
137
|
-
const symbols = Array.from(schemaMap.keys()).filter((symbol) => {
|
|
138
|
-
return symbol !== "ISchema" && symbol !== "Schemas";
|
|
139
|
-
});
|
|
134
|
+
private writeSchemaMapToValidationTypes = async (schemaMap: Map<string, Schema>) => {
|
|
135
|
+
const project = new Project(defaultTsMorphProjectSettings);
|
|
140
136
|
|
|
141
|
-
const
|
|
142
|
-
readerProject.addSourceFilesAtPaths(fileList);
|
|
137
|
+
const symbols: Array<string> = [];
|
|
143
138
|
|
|
144
139
|
const importMap = new Map<string, Array<string>>();
|
|
145
|
-
|
|
146
|
-
const dir = path.dirname(
|
|
147
|
-
const fileWithoutExtension = path.parse(
|
|
140
|
+
schemaMap.forEach((schema, filePath) => {
|
|
141
|
+
const dir = path.dirname(filePath);
|
|
142
|
+
const fileWithoutExtension = path.parse(filePath).name;
|
|
148
143
|
const relativeFilePath = path.relative(this.outputPath, dir);
|
|
149
144
|
const importPath = `${relativeFilePath}/${fileWithoutExtension}`;
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
const defs = schema.definitions ?? {};
|
|
146
|
+
|
|
147
|
+
Object.keys(defs).forEach((symbol) => {
|
|
153
148
|
const namedImports = importMap.get(importPath) ?? [];
|
|
154
|
-
namedImports.push(
|
|
149
|
+
namedImports.push(symbol);
|
|
155
150
|
importMap.set(importPath, namedImports);
|
|
151
|
+
symbols.push(symbol);
|
|
156
152
|
});
|
|
157
153
|
});
|
|
158
154
|
|
|
159
155
|
const sourceFile = project.createSourceFile(this.tsSchemaDefinitionOutputFile, {}, defaultCreateFileOptions);
|
|
156
|
+
|
|
160
157
|
importMap.forEach((namedImports, importPath) => {
|
|
161
158
|
sourceFile.addImportDeclaration({ namedImports, moduleSpecifier: importPath });
|
|
162
159
|
});
|
|
@@ -190,12 +187,11 @@ export class SchemaGenerator {
|
|
|
190
187
|
sourceFile.addExportDeclaration({
|
|
191
188
|
namedExports: ["schemas", "ISchema"],
|
|
192
189
|
});
|
|
193
|
-
|
|
194
190
|
await project.save();
|
|
195
191
|
};
|
|
196
192
|
|
|
197
193
|
private writeValidatorFunction = async () => {
|
|
198
|
-
const project = new Project(
|
|
194
|
+
const project = new Project(defaultTsMorphProjectSettings);
|
|
199
195
|
const sourceFile = project.createSourceFile(this.isValidSchemaOutputFile, {}, defaultCreateFileOptions);
|
|
200
196
|
sourceFile.addImportDeclaration({ namespaceImport: "schema", moduleSpecifier: `./${validationSchemaFileName}` });
|
|
201
197
|
sourceFile.addImportDeclaration({ defaultImport: "Ajv", moduleSpecifier: "ajv" });
|
|
@@ -203,7 +199,6 @@ export class SchemaGenerator {
|
|
|
203
199
|
namedImports: ["ISchema", "schemas"],
|
|
204
200
|
moduleSpecifier: `./${path.parse(schemaDefinitionFileName).name}`,
|
|
205
201
|
});
|
|
206
|
-
|
|
207
202
|
sourceFile.addVariableStatement({
|
|
208
203
|
declarationKind: VariableDeclarationKind.Const,
|
|
209
204
|
declarations: [
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface ICommandOptions {
|
|
|
13
13
|
readonly rootPath: string;
|
|
14
14
|
readonly output: string;
|
|
15
15
|
readonly helpers: boolean;
|
|
16
|
+
readonly additionalProperties: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
program.option(
|
|
@@ -27,6 +28,7 @@ program.option(
|
|
|
27
28
|
defaultOutputFolder
|
|
28
29
|
);
|
|
29
30
|
program.option("--no-helpers", "Only generate JSON schema without typescript helper files", true);
|
|
31
|
+
program.option("--additionalProperties", "Allow additional properties to pass validation", false);
|
|
30
32
|
|
|
31
33
|
program.parse();
|
|
32
34
|
|