@amritk/mjst 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli-config.d.ts +6 -0
- package/dist/cli.js +203 -34
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -68,7 +68,8 @@ npx mjst --config ./mjst.config.json
|
|
|
68
68
|
| 🏷️ | `typesOnly` | `--types-only` | `boolean` | — | `false` | Generate only TypeScript type definitions without parser functions. Useful when you only need the type shapes and do not need runtime validation. |
|
|
69
69
|
| 🔨 | `build` | `--build` | `boolean` | — | `false` | Compile the generated TypeScript files to .js and .d.ts output. A temporary tsconfig is written to the output directory, tsc is invoked, and the intermediate .ts source files are removed when compilation succeeds. |
|
|
70
70
|
| ⚠️ | `logWarnings` | `--log-warnings` | `boolean` | — | `false` | Emit a console.warn in the generated parsers for every input key that is not declared in the schema's properties. Useful for detecting schema drift or unexpected data shapes at runtime. |
|
|
71
|
-
|
|
|
71
|
+
| 🚫 | `strict` | `--strict` | `boolean` | — | `false` | Generate parsers that throw on type/shape mismatches (wrong type, missing required property, enum/pattern/min/max violations) instead of coercing invalid input to default values. Unknown extra keys are still allowed. |
|
|
72
|
+
| ⚙️ | `config` | `--config <path>` | `string` | — | — | Path to a JSON config file. Keys match the option names in this schema (schema, outDir, typesOnly, build, logWarnings, strict). CLI flags take precedence over config file values. |
|
|
72
73
|
<!-- config-table-end -->
|
|
73
74
|
|
|
74
75
|
---
|
package/dist/cli-config.d.ts
CHANGED
|
@@ -22,4 +22,10 @@ export type CliConfig = {
|
|
|
22
22
|
* that is not declared in the schema's properties.
|
|
23
23
|
*/
|
|
24
24
|
readonly logWarnings?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* When true, the generated parsers throw on type/shape mismatches
|
|
27
|
+
* (wrong type, missing required property, enum/pattern/min/max violations)
|
|
28
|
+
* instead of coercing invalid input to default values.
|
|
29
|
+
*/
|
|
30
|
+
readonly strict?: boolean;
|
|
25
31
|
};
|
package/dist/cli.js
CHANGED
|
@@ -1122,6 +1122,117 @@ var getDefaultValue = (schema) => {
|
|
|
1122
1122
|
return "undefined";
|
|
1123
1123
|
}
|
|
1124
1124
|
};
|
|
1125
|
+
var wrongTypeCondition = (accessor, type) => {
|
|
1126
|
+
switch (type) {
|
|
1127
|
+
case "string":
|
|
1128
|
+
return `typeof ${accessor} !== "string"`;
|
|
1129
|
+
case "number":
|
|
1130
|
+
case "integer":
|
|
1131
|
+
return `typeof ${accessor} !== "number"`;
|
|
1132
|
+
case "boolean":
|
|
1133
|
+
return `typeof ${accessor} !== "boolean"`;
|
|
1134
|
+
case "array":
|
|
1135
|
+
return `!Array.isArray(${accessor})`;
|
|
1136
|
+
case "object":
|
|
1137
|
+
return `!isObject(${accessor})`;
|
|
1138
|
+
default:
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
var typeLabel = (type) => type === "integer" ? "number" : type;
|
|
1143
|
+
var escapeRegexPattern = (pattern) => {
|
|
1144
|
+
return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
|
|
1145
|
+
};
|
|
1146
|
+
var generateConstraintChecks = (acc, propSchema, typeName, key) => {
|
|
1147
|
+
if (!isSchemaObject3(propSchema) || !hasType(propSchema))
|
|
1148
|
+
return [];
|
|
1149
|
+
const t = propSchema.type;
|
|
1150
|
+
const lines = [];
|
|
1151
|
+
if (t === "string") {
|
|
1152
|
+
if (hasPattern(propSchema)) {
|
|
1153
|
+
const pattern = escapeRegexPattern(propSchema.pattern);
|
|
1154
|
+
lines.push(` if (typeof ${acc} === "string" && !/${pattern}/.test(${acc})) throw new Error('[${typeName}] field "${key}" must match pattern ${propSchema.pattern}');`);
|
|
1155
|
+
}
|
|
1156
|
+
if (hasMinLength(propSchema)) {
|
|
1157
|
+
lines.push(` if (typeof ${acc} === "string" && ${acc}.length < ${propSchema.minLength}) throw new Error('[${typeName}] field "${key}" must have at least ${propSchema.minLength} characters');`);
|
|
1158
|
+
}
|
|
1159
|
+
if (hasMaxLength(propSchema)) {
|
|
1160
|
+
lines.push(` if (typeof ${acc} === "string" && ${acc}.length > ${propSchema.maxLength}) throw new Error('[${typeName}] field "${key}" must have at most ${propSchema.maxLength} characters');`);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
if (t === "number" || t === "integer") {
|
|
1164
|
+
if (hasMinimum(propSchema)) {
|
|
1165
|
+
lines.push(` if (typeof ${acc} === "number" && ${acc} < ${propSchema.minimum}) throw new Error('[${typeName}] field "${key}" must be >= ${propSchema.minimum}');`);
|
|
1166
|
+
}
|
|
1167
|
+
if (hasMaximum(propSchema)) {
|
|
1168
|
+
lines.push(` if (typeof ${acc} === "number" && ${acc} > ${propSchema.maximum}) throw new Error('[${typeName}] field "${key}" must be <= ${propSchema.maximum}');`);
|
|
1169
|
+
}
|
|
1170
|
+
if (hasExclusiveMinimum(propSchema)) {
|
|
1171
|
+
lines.push(` if (typeof ${acc} === "number" && ${acc} <= ${propSchema.exclusiveMinimum}) throw new Error('[${typeName}] field "${key}" must be > ${propSchema.exclusiveMinimum}');`);
|
|
1172
|
+
}
|
|
1173
|
+
if (hasExclusiveMaximum(propSchema)) {
|
|
1174
|
+
lines.push(` if (typeof ${acc} === "number" && ${acc} >= ${propSchema.exclusiveMaximum}) throw new Error('[${typeName}] field "${key}" must be < ${propSchema.exclusiveMaximum}');`);
|
|
1175
|
+
}
|
|
1176
|
+
if (hasMultipleOf(propSchema)) {
|
|
1177
|
+
lines.push(` if (typeof ${acc} === "number" && ${acc} % ${propSchema.multipleOf} !== 0) throw new Error('[${typeName}] field "${key}" must be a multiple of ${propSchema.multipleOf}');`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
return lines;
|
|
1181
|
+
};
|
|
1182
|
+
var generatePropertyAssertion = (key, propSchema, isRequired, typeName) => {
|
|
1183
|
+
const acc = safeAccessor("input", key);
|
|
1184
|
+
const lines = [];
|
|
1185
|
+
if (isRequired) {
|
|
1186
|
+
lines.push(` if (!(${JSON.stringify(key)} in input)) throw new Error('[${typeName}] missing required property "${key}"');`);
|
|
1187
|
+
}
|
|
1188
|
+
if (!isSchemaObject3(propSchema))
|
|
1189
|
+
return lines;
|
|
1190
|
+
if (hasRef(propSchema))
|
|
1191
|
+
return lines;
|
|
1192
|
+
if (hasEnum(propSchema)) {
|
|
1193
|
+
const allowed = JSON.stringify(propSchema.enum);
|
|
1194
|
+
const label = propSchema.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
1195
|
+
if (isRequired) {
|
|
1196
|
+
lines.push(` if (!(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
|
|
1197
|
+
} else {
|
|
1198
|
+
lines.push(` if (${acc} !== undefined && !(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
|
|
1199
|
+
}
|
|
1200
|
+
return lines;
|
|
1201
|
+
}
|
|
1202
|
+
if (hasType(propSchema)) {
|
|
1203
|
+
const t = propSchema.type;
|
|
1204
|
+
const wrongType = wrongTypeCondition(acc, t);
|
|
1205
|
+
if (wrongType) {
|
|
1206
|
+
if (isRequired) {
|
|
1207
|
+
lines.push(` if (${wrongType}) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
|
|
1208
|
+
} else {
|
|
1209
|
+
lines.push(` if (${acc} !== undefined && (${wrongType})) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
lines.push(...generateConstraintChecks(acc, propSchema, typeName, key));
|
|
1213
|
+
}
|
|
1214
|
+
return lines;
|
|
1215
|
+
};
|
|
1216
|
+
var generateObjectStrictAssertion = (schema, typeName) => {
|
|
1217
|
+
const lines = [];
|
|
1218
|
+
lines.push(` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`);
|
|
1219
|
+
if (!hasProperties(schema) || !isSchemaObject3(schema))
|
|
1220
|
+
return lines;
|
|
1221
|
+
const required = new Set(hasRequired(schema) ? schema.required : []);
|
|
1222
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
1223
|
+
lines.push(...generatePropertyAssertion(key, propSchema, required.has(key), typeName));
|
|
1224
|
+
}
|
|
1225
|
+
return lines;
|
|
1226
|
+
};
|
|
1227
|
+
var generateScalarStrictAssertion = (schema, typeName) => {
|
|
1228
|
+
if (!isSchemaObject3(schema) || !hasType(schema))
|
|
1229
|
+
return null;
|
|
1230
|
+
const t = schema.type;
|
|
1231
|
+
const wrongType = wrongTypeCondition("input", t);
|
|
1232
|
+
if (!wrongType)
|
|
1233
|
+
return null;
|
|
1234
|
+
return ` if (${wrongType}) throw new Error(\`[${typeName}] expected ${typeLabel(t)}, got \${input === null ? "null" : typeof input}\`);`;
|
|
1235
|
+
};
|
|
1125
1236
|
var findDiscriminator = (schemas) => {
|
|
1126
1237
|
const propertyValues = new Map;
|
|
1127
1238
|
for (const schema of schemas) {
|
|
@@ -1677,11 +1788,22 @@ var generateFallbackObject = (schema, useRefImports, typeName) => {
|
|
|
1677
1788
|
}`;
|
|
1678
1789
|
return result;
|
|
1679
1790
|
};
|
|
1680
|
-
var generateNonObjectParser = (typeName, schema) => {
|
|
1791
|
+
var generateNonObjectParser = (typeName, schema, strict) => {
|
|
1681
1792
|
const functionName = generateParserName(typeName);
|
|
1682
1793
|
if (!isSchemaObject3(schema) || !hasType(schema)) {
|
|
1683
1794
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
1684
1795
|
}
|
|
1796
|
+
if (strict) {
|
|
1797
|
+
const assertion = generateScalarStrictAssertion(schema, typeName);
|
|
1798
|
+
if (assertion === null) {
|
|
1799
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
1800
|
+
}
|
|
1801
|
+
const returnExpr = schema.type === "array" ? `[...(input as readonly unknown[])] as ${typeName}` : `input as ${typeName}`;
|
|
1802
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
1803
|
+
${assertion}
|
|
1804
|
+
return ${returnExpr};
|
|
1805
|
+
};`;
|
|
1806
|
+
}
|
|
1685
1807
|
switch (schema.type) {
|
|
1686
1808
|
case "string":
|
|
1687
1809
|
return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "string" ? input as ${typeName} : "" as ${typeName};`;
|
|
@@ -1696,8 +1818,14 @@ var generateNonObjectParser = (typeName, schema) => {
|
|
|
1696
1818
|
return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
|
|
1697
1819
|
}
|
|
1698
1820
|
};
|
|
1699
|
-
var generateEmptyObjectParser = (typeName) => {
|
|
1821
|
+
var generateEmptyObjectParser = (typeName, strict) => {
|
|
1700
1822
|
const functionName = generateParserName(typeName);
|
|
1823
|
+
if (strict) {
|
|
1824
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
1825
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
|
|
1826
|
+
return { ...input } as ${typeName};
|
|
1827
|
+
};`;
|
|
1828
|
+
}
|
|
1701
1829
|
return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {} as ${typeName};`;
|
|
1702
1830
|
};
|
|
1703
1831
|
var toVarName = (key) => {
|
|
@@ -1726,7 +1854,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
|
|
|
1726
1854
|
case "string": {
|
|
1727
1855
|
checks.push(`typeof ${varName} === "string"`);
|
|
1728
1856
|
if (hasPattern(schema))
|
|
1729
|
-
checks.push(`/${
|
|
1857
|
+
checks.push(`/${escapeRegexPattern2(schema.pattern)}/.test(${varName})`);
|
|
1730
1858
|
if (hasMinLength(schema))
|
|
1731
1859
|
checks.push(`${varName}.length >= ${schema.minLength}`);
|
|
1732
1860
|
if (hasMaxLength(schema))
|
|
@@ -1779,16 +1907,28 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
|
|
|
1779
1907
|
var shouldCacheVariable = (propSchema, _canFastPath, _useRefImports) => {
|
|
1780
1908
|
return isSchemaObject3(propSchema);
|
|
1781
1909
|
};
|
|
1782
|
-
var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
|
|
1910
|
+
var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
|
|
1783
1911
|
const functionName = generateParserName(typeName);
|
|
1784
1912
|
if (!hasProperties(schema)) {
|
|
1913
|
+
if (strict) {
|
|
1914
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
1915
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
|
|
1916
|
+
return input as ${typeName};
|
|
1917
|
+
};`;
|
|
1918
|
+
}
|
|
1785
1919
|
return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
|
|
1786
1920
|
}
|
|
1787
1921
|
const fallbackObject = generateFallbackObject(schema, useRefImports, typeName);
|
|
1788
1922
|
const properties = Object.entries(schema.properties);
|
|
1789
1923
|
const lines = [];
|
|
1790
1924
|
lines.push(`export const ${functionName} = (input: unknown): ${typeName} => {`);
|
|
1791
|
-
|
|
1925
|
+
if (strict) {
|
|
1926
|
+
for (const assertionLine of generateObjectStrictAssertion(schema, typeName)) {
|
|
1927
|
+
lines.push(assertionLine);
|
|
1928
|
+
}
|
|
1929
|
+
} else {
|
|
1930
|
+
lines.push(` if (!isObject(input)) return ${fallbackObject};`);
|
|
1931
|
+
}
|
|
1792
1932
|
const propInfo = [];
|
|
1793
1933
|
const hasAllOfRefParsers = useRefImports && isSchemaObject3(schema) && hasAllOf(schema) && schema.allOf.some((entry) => isSchemaObject3(entry) && hasRef(entry));
|
|
1794
1934
|
let canFastPath = !hasAllOfRefParsers;
|
|
@@ -1911,7 +2051,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
|
|
|
1911
2051
|
}
|
|
1912
2052
|
return result;
|
|
1913
2053
|
};
|
|
1914
|
-
var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings) => {
|
|
2054
|
+
var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
|
|
1915
2055
|
const functionName = generateParserName(typeName);
|
|
1916
2056
|
const entries = generatePropertyEntries(schema, useRefImports);
|
|
1917
2057
|
const propertyLines = entries.map((entry) => {
|
|
@@ -1921,19 +2061,19 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
|
|
|
1921
2061
|
return ` ${safeKey2(entry.key)}: ${entry.value},`;
|
|
1922
2062
|
});
|
|
1923
2063
|
if (!isSchemaObject3(schema) || !("patternProperties" in schema)) {
|
|
1924
|
-
return generateObjectParser(schema, typeName, useRefImports, logWarnings);
|
|
2064
|
+
return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
|
|
1925
2065
|
}
|
|
1926
2066
|
const patternProps = schema.patternProperties;
|
|
1927
2067
|
const patterns = Object.entries(patternProps);
|
|
1928
2068
|
const refPattern = patterns.find(([, ps]) => isSchemaObject3(ps) && hasRef(ps));
|
|
1929
2069
|
if (!refPattern || !useRefImports) {
|
|
1930
|
-
return generateObjectParser(schema, typeName, useRefImports, logWarnings);
|
|
2070
|
+
return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
|
|
1931
2071
|
}
|
|
1932
2072
|
const [pattern, patternSchema] = refPattern;
|
|
1933
2073
|
const ref = patternSchema.$ref;
|
|
1934
2074
|
const parserName = generateParserName(refToName(ref));
|
|
1935
2075
|
const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
|
|
1936
|
-
const escapedPattern =
|
|
2076
|
+
const escapedPattern = escapeRegexPattern2(pattern);
|
|
1937
2077
|
const inputSpread = " ...input,";
|
|
1938
2078
|
let objectProperties = inputSpread;
|
|
1939
2079
|
if (propertyLines.length > 0) {
|
|
@@ -1942,9 +2082,10 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
|
|
|
1942
2082
|
` + line;
|
|
1943
2083
|
}
|
|
1944
2084
|
}
|
|
2085
|
+
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
1945
2086
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
1946
2087
|
if (!isObject(input)) {
|
|
1947
|
-
|
|
2088
|
+
${notObjectBranch}
|
|
1948
2089
|
}
|
|
1949
2090
|
const result = {
|
|
1950
2091
|
${objectProperties}
|
|
@@ -1958,23 +2099,41 @@ ${objectProperties}
|
|
|
1958
2099
|
return result;
|
|
1959
2100
|
};`;
|
|
1960
2101
|
};
|
|
1961
|
-
var generateAdditionalPropertiesParser = (schema, typeName, useRefImports) => {
|
|
2102
|
+
var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, strict) => {
|
|
1962
2103
|
const functionName = generateParserName(typeName);
|
|
1963
2104
|
const additionalProps = schema.additionalProperties;
|
|
1964
2105
|
if (!additionalProps) {
|
|
1965
|
-
return generateEmptyObjectParser(typeName);
|
|
2106
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
1966
2107
|
}
|
|
1967
2108
|
if (useRefImports && isSchemaObject3(additionalProps) && hasRef(additionalProps)) {
|
|
1968
2109
|
const ref = additionalProps.$ref;
|
|
1969
2110
|
const parserName = generateParserName(refToName(ref));
|
|
2111
|
+
if (strict) {
|
|
2112
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
2113
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
|
|
2114
|
+
return validateRecord(input, ${parserName}) as ${typeName};
|
|
2115
|
+
};`;
|
|
2116
|
+
}
|
|
1970
2117
|
return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${parserName}) as ${typeName};`;
|
|
1971
2118
|
}
|
|
1972
2119
|
if (isSchemaObject3(additionalProps) && hasType(additionalProps)) {
|
|
1973
2120
|
const inlineParser = generateInlineValueParser(additionalProps);
|
|
1974
2121
|
if (inlineParser) {
|
|
2122
|
+
if (strict) {
|
|
2123
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
2124
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
|
|
2125
|
+
return validateRecord(input, ${inlineParser}) as ${typeName};
|
|
2126
|
+
};`;
|
|
2127
|
+
}
|
|
1975
2128
|
return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${inlineParser}) as ${typeName};`;
|
|
1976
2129
|
}
|
|
1977
2130
|
}
|
|
2131
|
+
if (strict) {
|
|
2132
|
+
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
2133
|
+
if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
|
|
2134
|
+
return { ...input } as ${typeName};
|
|
2135
|
+
};`;
|
|
2136
|
+
}
|
|
1978
2137
|
return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {};`;
|
|
1979
2138
|
};
|
|
1980
2139
|
var generateInlineValueParser = (schema) => {
|
|
@@ -1995,18 +2154,18 @@ var generateInlineValueParser = (schema) => {
|
|
|
1995
2154
|
return null;
|
|
1996
2155
|
}
|
|
1997
2156
|
};
|
|
1998
|
-
var
|
|
2157
|
+
var escapeRegexPattern2 = (pattern) => {
|
|
1999
2158
|
return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
|
|
2000
2159
|
};
|
|
2001
|
-
var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
|
|
2160
|
+
var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict) => {
|
|
2002
2161
|
const functionName = generateParserName(typeName);
|
|
2003
2162
|
if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
|
|
2004
|
-
return generateEmptyObjectParser(typeName);
|
|
2163
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
2005
2164
|
}
|
|
2006
2165
|
const patternProps = schema.patternProperties;
|
|
2007
2166
|
const patterns = Object.entries(patternProps);
|
|
2008
2167
|
if (patterns.length === 0) {
|
|
2009
|
-
return generateEmptyObjectParser(typeName);
|
|
2168
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
2010
2169
|
}
|
|
2011
2170
|
const [pattern, patternSchema] = patterns[0];
|
|
2012
2171
|
let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
|
|
@@ -2017,10 +2176,11 @@ var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
|
|
|
2017
2176
|
} else if (patternSchema === false) {
|
|
2018
2177
|
patternAssignment = "";
|
|
2019
2178
|
}
|
|
2020
|
-
const escapedPattern =
|
|
2179
|
+
const escapedPattern = escapeRegexPattern2(pattern);
|
|
2180
|
+
const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
|
|
2021
2181
|
return `export const ${functionName} = (input: unknown): ${typeName} => {
|
|
2022
2182
|
if (!isObject(input)) {
|
|
2023
|
-
|
|
2183
|
+
${notObjectBranch}
|
|
2024
2184
|
}
|
|
2025
2185
|
const result = {
|
|
2026
2186
|
...input,
|
|
@@ -2132,43 +2292,44 @@ var generateSchemaObjectParser = (typeName) => {
|
|
|
2132
2292
|
var selectParserStrategy = (schema, typeName, options) => {
|
|
2133
2293
|
const useRefImports = options?.useRefImports ?? false;
|
|
2134
2294
|
const logWarnings = options?.logWarnings ?? false;
|
|
2295
|
+
const strict = options?.strict ?? false;
|
|
2135
2296
|
if (typeName === "SchemaObject") {
|
|
2136
2297
|
return generateSchemaObjectParser(typeName);
|
|
2137
2298
|
}
|
|
2138
2299
|
const isObjectLikeSchema2 = isObjectSchema2(schema) || isSchemaObject3(schema) && (("patternProperties" in schema) || ("additionalProperties" in schema));
|
|
2139
2300
|
if (!isObjectLikeSchema2 && !isSchemaObject3(schema)) {
|
|
2140
|
-
return generateNonObjectParser(typeName, schema);
|
|
2301
|
+
return generateNonObjectParser(typeName, schema, strict);
|
|
2141
2302
|
}
|
|
2142
2303
|
if (hasProperties(schema) && isSchemaObject3(schema) && "patternProperties" in schema) {
|
|
2143
|
-
return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings);
|
|
2304
|
+
return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings, strict);
|
|
2144
2305
|
}
|
|
2145
2306
|
if (hasProperties(schema)) {
|
|
2146
|
-
return generateObjectParser(schema, typeName, useRefImports, logWarnings);
|
|
2307
|
+
return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
|
|
2147
2308
|
}
|
|
2148
2309
|
if (isSchemaObject3(schema) && "if" in schema && "then" in schema && "else" in schema) {
|
|
2149
2310
|
return generateConditionalParser(schema, typeName);
|
|
2150
2311
|
}
|
|
2151
2312
|
const conditionalObjectSchema = getConditionalObjectSchema2(schema);
|
|
2152
2313
|
if (conditionalObjectSchema) {
|
|
2153
|
-
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings);
|
|
2314
|
+
return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings, strict);
|
|
2154
2315
|
}
|
|
2155
2316
|
if (!isObjectLikeSchema2) {
|
|
2156
|
-
return generateNonObjectParser(typeName, schema);
|
|
2317
|
+
return generateNonObjectParser(typeName, schema, strict);
|
|
2157
2318
|
}
|
|
2158
2319
|
if ("patternProperties" in schema) {
|
|
2159
|
-
return generatePatternPropertiesParser(schema, typeName, useRefImports);
|
|
2320
|
+
return generatePatternPropertiesParser(schema, typeName, useRefImports, strict);
|
|
2160
2321
|
}
|
|
2161
2322
|
if ("additionalProperties" in schema && !hasProperties(schema)) {
|
|
2162
2323
|
const additionalProps = schema.additionalProperties;
|
|
2163
2324
|
if (additionalProps === true || additionalProps === false) {
|
|
2164
|
-
return generateEmptyObjectParser(typeName);
|
|
2325
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
2165
2326
|
}
|
|
2166
|
-
return generateAdditionalPropertiesParser(schema, typeName, useRefImports);
|
|
2327
|
+
return generateAdditionalPropertiesParser(schema, typeName, useRefImports, strict);
|
|
2167
2328
|
}
|
|
2168
2329
|
if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
|
|
2169
|
-
return generateEmptyObjectParser(typeName);
|
|
2330
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
2170
2331
|
}
|
|
2171
|
-
return generateEmptyObjectParser(typeName);
|
|
2332
|
+
return generateEmptyObjectParser(typeName, strict);
|
|
2172
2333
|
};
|
|
2173
2334
|
var generateParserFunction = (schema, typeName, options) => {
|
|
2174
2335
|
return selectParserStrategy(schema, typeName, options);
|
|
@@ -2236,7 +2397,8 @@ var generateFile = (schema, typeName, options) => {
|
|
|
2236
2397
|
}
|
|
2237
2398
|
const parserFunction = generateParserFunction(schema, typeName, {
|
|
2238
2399
|
useRefImports: true,
|
|
2239
|
-
...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {}
|
|
2400
|
+
...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {},
|
|
2401
|
+
...options?.strict !== undefined ? { strict: options.strict } : {}
|
|
2240
2402
|
});
|
|
2241
2403
|
const shapeValidator = generateShapeValidator(schema, typeName, true);
|
|
2242
2404
|
const combinedFunctions = `${shapeValidator}
|
|
@@ -2277,7 +2439,7 @@ var extractDynamicAnchorDefs = (schema) => {
|
|
|
2277
2439
|
}
|
|
2278
2440
|
return refs;
|
|
2279
2441
|
};
|
|
2280
|
-
var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings) => {
|
|
2442
|
+
var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict) => {
|
|
2281
2443
|
rootSchema = upgradeDraft07Schema(rootSchema);
|
|
2282
2444
|
const files = [];
|
|
2283
2445
|
const processedRefs = new Set;
|
|
@@ -2289,7 +2451,8 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
|
|
|
2289
2451
|
const rootContent = generateFile(extendedRootSchema, rootTypeName, {
|
|
2290
2452
|
typesOnly: typesOnly ?? false,
|
|
2291
2453
|
rootSchema,
|
|
2292
|
-
...logWarnings !== undefined ? { logWarnings } : {}
|
|
2454
|
+
...logWarnings !== undefined ? { logWarnings } : {},
|
|
2455
|
+
...strict !== undefined ? { strict } : {}
|
|
2293
2456
|
});
|
|
2294
2457
|
const rootFilename = rootTypeName.toLowerCase();
|
|
2295
2458
|
processedFilenames.add(rootFilename);
|
|
@@ -2320,7 +2483,8 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
|
|
|
2320
2483
|
typesOnly: typesOnly ?? false,
|
|
2321
2484
|
selfRef: ref,
|
|
2322
2485
|
rootSchema,
|
|
2323
|
-
...logWarnings !== undefined ? { logWarnings } : {}
|
|
2486
|
+
...logWarnings !== undefined ? { logWarnings } : {},
|
|
2487
|
+
...strict !== undefined ? { strict } : {}
|
|
2324
2488
|
});
|
|
2325
2489
|
if (!processedFilenames.has(filename)) {
|
|
2326
2490
|
processedFilenames.add(filename);
|
|
@@ -2385,7 +2549,8 @@ var loadConfig = async (configPath) => {
|
|
|
2385
2549
|
...typeof obj["outDir"] === "string" && { outDir: obj["outDir"] },
|
|
2386
2550
|
...typeof obj["typesOnly"] === "boolean" && { typesOnly: obj["typesOnly"] },
|
|
2387
2551
|
...typeof obj["build"] === "boolean" && { build: obj["build"] },
|
|
2388
|
-
...typeof obj["logWarnings"] === "boolean" && { logWarnings: obj["logWarnings"] }
|
|
2552
|
+
...typeof obj["logWarnings"] === "boolean" && { logWarnings: obj["logWarnings"] },
|
|
2553
|
+
...typeof obj["strict"] === "boolean" && { strict: obj["strict"] }
|
|
2389
2554
|
};
|
|
2390
2555
|
};
|
|
2391
2556
|
|
|
@@ -2411,6 +2576,8 @@ var parseCliArgs = (args) => {
|
|
|
2411
2576
|
config.build = value !== "false";
|
|
2412
2577
|
} else if (key === "log-warnings") {
|
|
2413
2578
|
config.logWarnings = value !== "false";
|
|
2579
|
+
} else if (key === "strict") {
|
|
2580
|
+
config.strict = value !== "false";
|
|
2414
2581
|
}
|
|
2415
2582
|
continue;
|
|
2416
2583
|
}
|
|
@@ -2432,6 +2599,8 @@ var parseCliArgs = (args) => {
|
|
|
2432
2599
|
config.build = true;
|
|
2433
2600
|
} else if (arg === "--log-warnings") {
|
|
2434
2601
|
config.logWarnings = true;
|
|
2602
|
+
} else if (arg === "--strict") {
|
|
2603
|
+
config.strict = true;
|
|
2435
2604
|
}
|
|
2436
2605
|
}
|
|
2437
2606
|
return config;
|
|
@@ -2468,7 +2637,7 @@ var run = async () => {
|
|
|
2468
2637
|
const schemaPath = resolve2(config.schema);
|
|
2469
2638
|
const raw = await readFile2(schemaPath, "utf-8");
|
|
2470
2639
|
const schema = JSON.parse(raw);
|
|
2471
|
-
const files = await buildSchema(schema, "Document", undefined, config.typesOnly, config.logWarnings);
|
|
2640
|
+
const files = await buildSchema(schema, "Document", undefined, config.typesOnly, config.logWarnings, config.strict);
|
|
2472
2641
|
const outputDir = resolve2(config.outDir);
|
|
2473
2642
|
await mkdir(outputDir, { recursive: true });
|
|
2474
2643
|
for (const file of files) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/mjst",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"files": [
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
32
35
|
"scripts": {
|
|
33
36
|
"dev": "bun run --conditions=development ./src/cli.ts",
|
|
34
37
|
"start": "bun run ./src/cli.ts",
|