@amritk/generate-validators 0.4.2 → 0.5.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/dist/generators/build-schema.d.ts +4 -2
- package/dist/generators/build-schema.d.ts.map +1 -0
- package/dist/generators/build-schema.js +61 -0
- package/dist/generators/collect-validator-imports.d.ts +1 -0
- package/dist/generators/collect-validator-imports.d.ts.map +1 -0
- package/dist/generators/collect-validator-imports.js +99 -0
- package/dist/generators/generate-files.d.ts +1 -0
- package/dist/generators/generate-files.d.ts.map +1 -0
- package/dist/generators/generate-files.js +47 -0
- package/dist/generators/generate-validator-function.d.ts +1 -0
- package/dist/generators/generate-validator-function.d.ts.map +1 -0
- package/dist/generators/generate-validator-function.js +405 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1468
- package/package.json +6 -6
- package/src/generators/build-schema.ts +15 -80
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/generate-validators",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Generate TypeScript validation functions from JSON Schemas.",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,23 +31,23 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "
|
|
35
|
-
"
|
|
36
|
-
"
|
|
34
|
+
"build": "tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f",
|
|
35
|
+
"types:check": "tsgo -p . --noEmit",
|
|
36
|
+
"test": "NODE_ENV=production vitest run --root ../.. generate-validators"
|
|
37
37
|
},
|
|
38
38
|
"imports": {
|
|
39
39
|
"#generators/*": "./src/generators/*.ts"
|
|
40
40
|
},
|
|
41
41
|
"exports": {
|
|
42
42
|
".": {
|
|
43
|
-
"
|
|
43
|
+
"development": "./src/index.ts",
|
|
44
44
|
"default": "./dist/index.js",
|
|
45
45
|
"types": "./dist/index.d.ts"
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"json-schema-typed": "^8.0.1",
|
|
50
|
-
"@amritk/helpers": "0.
|
|
50
|
+
"@amritk/helpers": "0.7.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@scalar/openapi-parser": "^0.26.1"
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { refToFilename } from '@amritk/helpers/ref-to-filename'
|
|
4
|
-
import { refToName } from '@amritk/helpers/ref-to-name'
|
|
5
|
-
import { resolveDynamicRefs } from '@amritk/helpers/resolve-dynamic-refs'
|
|
6
|
-
import { resolveRef } from '@amritk/helpers/resolve-ref'
|
|
7
|
-
import { upgradeDraft07Schema } from '@amritk/helpers/upgrade-draft07-schema'
|
|
1
|
+
import { generateIndexBarrel } from '@amritk/helpers/generate-index-barrel'
|
|
2
|
+
import { walkRefGraph } from '@amritk/helpers/walk-ref-graph'
|
|
8
3
|
import type { JSONSchema } from 'json-schema-typed/draft-2020-12'
|
|
9
4
|
|
|
10
5
|
import { generateValidatorFile } from './generate-files'
|
|
@@ -35,8 +30,9 @@ export type ValidationResult = true | { valid: false; errors: ValidationError[]
|
|
|
35
30
|
`
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
|
-
* Builds all TypeScript validator files from a JSON Schema by traversing
|
|
39
|
-
*
|
|
33
|
+
* Builds all TypeScript validator files from a JSON Schema by traversing all
|
|
34
|
+
* `$ref` / `$dynamicRef` references recursively (via the shared
|
|
35
|
+
* `@amritk/helpers/walk-ref-graph` walker).
|
|
40
36
|
*
|
|
41
37
|
* Each generated file exports:
|
|
42
38
|
* - A TypeScript type definition
|
|
@@ -60,87 +56,26 @@ export const buildValidatorSchema = async (
|
|
|
60
56
|
rootTypeName: string,
|
|
61
57
|
typeSuffix = '',
|
|
62
58
|
): Promise<GeneratedFile[]> => {
|
|
63
|
-
rootSchema = upgradeDraft07Schema(rootSchema as Record<string, unknown>) as JSONSchema
|
|
64
|
-
|
|
65
59
|
const files: GeneratedFile[] = []
|
|
66
|
-
const processedRefs = new Set<string>()
|
|
67
|
-
const processedFilenames = new Set<string>()
|
|
68
|
-
const refsToProcess: string[] = []
|
|
69
|
-
|
|
70
|
-
const dynamicRefMap = buildDynamicRefMap(rootSchema)
|
|
71
|
-
|
|
72
|
-
// Root schema
|
|
73
|
-
const processedRootSchema = resolveDynamicRefs(rootSchema, dynamicRefMap)
|
|
74
|
-
const rootContent = generateValidatorFile(processedRootSchema, rootTypeName, {
|
|
75
|
-
rootSchema: rootSchema as Record<string, unknown>,
|
|
76
|
-
typeSuffix,
|
|
77
|
-
})
|
|
78
|
-
const rootFilename = rootTypeName.toLowerCase()
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
61
|
+
walkRefGraph(rootSchema, rootTypeName, { typeSuffix }, (node) => {
|
|
62
|
+
// `validation-result` and `index` are reserved output filenames, so never
|
|
63
|
+
// let a definition of either name overwrite them.
|
|
64
|
+
if (node.filename === 'validation-result' || node.filename === 'index') return
|
|
84
65
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
while (refsToProcess.length > 0) {
|
|
89
|
-
const ref = refsToProcess.shift()
|
|
90
|
-
if (!ref || processedRefs.has(ref)) continue
|
|
91
|
-
processedRefs.add(ref)
|
|
92
|
-
|
|
93
|
-
const resolvedSchema = resolveRef(ref, rootSchema as Record<string, unknown>)
|
|
94
|
-
if (!resolvedSchema) {
|
|
95
|
-
console.warn(`Warning: Could not resolve ref: ${ref}`)
|
|
96
|
-
continue
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const typeName = refToName(ref, typeSuffix)
|
|
100
|
-
const filename = refToFilename(ref)
|
|
101
|
-
const processedSchema = resolveDynamicRefs(resolvedSchema as JSONSchema, dynamicRefMap)
|
|
102
|
-
const content = generateValidatorFile(processedSchema, typeName, {
|
|
103
|
-
selfRef: ref,
|
|
104
|
-
rootSchema: rootSchema as Record<string, unknown>,
|
|
66
|
+
const content = generateValidatorFile(node.schema, node.typeName, {
|
|
67
|
+
rootSchema: node.rootSchema,
|
|
105
68
|
typeSuffix,
|
|
69
|
+
...(node.ref !== undefined ? { selfRef: node.ref } : {}),
|
|
106
70
|
})
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
processedFilenames.add(filename)
|
|
110
|
-
files.push({ filename: `${filename}.ts`, content })
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
for (const nestedRef of extractRefs(resolvedSchema as JSONSchema)) {
|
|
114
|
-
if (!processedRefs.has(nestedRef)) refsToProcess.push(nestedRef)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
71
|
+
files.push({ filename: `${node.filename}.ts`, content })
|
|
72
|
+
})
|
|
117
73
|
|
|
118
74
|
// Emit the runtime contract for validators. ValidationResult is mjst-defined
|
|
119
75
|
// (not derived from the input schema), so its content is fixed.
|
|
120
76
|
files.push({ filename: 'validation-result.ts', content: VALIDATION_RESULT_CONTENT })
|
|
121
77
|
|
|
122
|
-
|
|
123
|
-
const TYPE_EXPORT_RE = /^export type (\w+)/gm
|
|
124
|
-
const CONST_EXPORT_RE = /^export const (\w+)/gm
|
|
125
|
-
|
|
126
|
-
const sortedFiles = [...files].sort((a, b) => a.filename.localeCompare(b.filename))
|
|
127
|
-
let indexContent = ''
|
|
128
|
-
|
|
129
|
-
for (const file of sortedFiles) {
|
|
130
|
-
const moduleName = file.filename.replace(/\.ts$/, '')
|
|
131
|
-
const typeNames: string[] = []
|
|
132
|
-
const constNames: string[] = []
|
|
133
|
-
|
|
134
|
-
for (const match of file.content.matchAll(TYPE_EXPORT_RE)) typeNames.push(match[1] as string)
|
|
135
|
-
for (const match of file.content.matchAll(CONST_EXPORT_RE)) constNames.push(match[1] as string)
|
|
136
|
-
|
|
137
|
-
if (typeNames.length === 0 && constNames.length === 0) continue
|
|
138
|
-
|
|
139
|
-
const typeExports = typeNames.map((n) => `type ${n}`)
|
|
140
|
-
indexContent += `export { ${[...typeExports, ...constNames].join(', ')} } from './${moduleName}';\n`
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
files.push({ filename: 'index.ts', content: indexContent })
|
|
78
|
+
files.push({ filename: 'index.ts', content: generateIndexBarrel(files) })
|
|
144
79
|
|
|
145
80
|
return files
|
|
146
81
|
}
|