@amritk/generate-parsers 0.1.0 → 0.3.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.
Files changed (41) hide show
  1. package/README.md +3 -1
  2. package/dist/generators/build-schema.d.ts +8 -1
  3. package/dist/generators/generate-files.d.ts +20 -2
  4. package/dist/generators/generate-parser-function.d.ts +6 -0
  5. package/dist/generators/generate-strict-assertion.d.ts +18 -0
  6. package/dist/helpers/collect-helpers.d.ts +16 -2
  7. package/dist/index.js +261 -50
  8. package/package.json +6 -2
  9. package/src/generators/build-schema.test.ts +830 -0
  10. package/src/generators/build-schema.ts +280 -0
  11. package/src/generators/generate-default-from-pattern.test.ts +81 -0
  12. package/src/generators/generate-default-from-pattern.ts +39 -0
  13. package/src/generators/generate-discriminated-union-validation.test.ts +84 -0
  14. package/src/generators/generate-discriminated-union-validation.ts +54 -0
  15. package/src/generators/generate-enum-check.test.ts +45 -0
  16. package/src/generators/generate-enum-check.ts +8 -0
  17. package/src/generators/generate-files.test.ts +435 -0
  18. package/src/generators/generate-files.ts +136 -0
  19. package/src/generators/generate-non-discriminated-union-validation.test.ts +74 -0
  20. package/src/generators/generate-non-discriminated-union-validation.ts +40 -0
  21. package/src/generators/generate-parser-function.test.ts +1798 -0
  22. package/src/generators/generate-parser-function.ts +1319 -0
  23. package/src/generators/generate-schema-checks.test.ts +322 -0
  24. package/src/generators/generate-schema-checks.ts +114 -0
  25. package/src/generators/generate-strict-assertion.ts +210 -0
  26. package/src/generators/generate-validation-expression.test.ts +794 -0
  27. package/src/generators/generate-validation-expression.ts +286 -0
  28. package/src/helpers/apply-schema-extensions.test.ts +194 -0
  29. package/src/helpers/apply-schema-extensions.ts +63 -0
  30. package/src/helpers/collect-helpers.test.ts +103 -0
  31. package/src/helpers/collect-helpers.ts +64 -0
  32. package/src/helpers/collect-imports.test.ts +587 -0
  33. package/src/helpers/collect-imports.ts +268 -0
  34. package/src/helpers/find-discriminator.test.ts +88 -0
  35. package/src/helpers/find-discriminator.ts +39 -0
  36. package/src/helpers/get-default-value.test.ts +135 -0
  37. package/src/helpers/get-default-value.ts +89 -0
  38. package/src/helpers/get-discriminator-value.test.ts +83 -0
  39. package/src/helpers/get-discriminator-value.ts +21 -0
  40. package/src/index.ts +1 -0
  41. package/src/types/schema-extensions.ts +35 -0
package/dist/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ // src/generators/build-schema.ts
2
+ import { readFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, resolve as resolvePath } from "node:path";
5
+
1
6
  // ../helpers/dist/build-dynamic-ref-map.js
2
7
  var isSchemaObject = (schema) => {
3
8
  return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
@@ -792,24 +797,46 @@ var generateTypeDefinition = (schema, typeName) => {
792
797
  };
793
798
 
794
799
  // src/helpers/collect-helpers.ts
795
- var collectHelpers = (parserFunction) => {
800
+ var PACKAGE_IMPORTS = {
801
+ "is-object": "import { isObject } from '@amritk/helpers/is-object';",
802
+ "validate-array": "import { validateArray } from '@amritk/helpers/validate-array';",
803
+ "validate-record": "import { validateRecord } from '@amritk/helpers/validate-record';",
804
+ "has-ref": "import { hasRef } from '@amritk/helpers/schema-guards';"
805
+ };
806
+ var EMBEDDED_IMPORTS = {
807
+ "is-object": "import { isObject } from './_helpers/is-object';",
808
+ "validate-array": "import { validateArray } from './_helpers/validate-array';",
809
+ "validate-record": "import { validateRecord } from './_helpers/validate-record';",
810
+ "has-ref": "import { hasRef } from './_helpers/has-ref';"
811
+ };
812
+ var collectHelpers = (parserFunction, mode) => {
796
813
  const imports = [];
814
+ const used = new Set;
815
+ const table = mode === "embedded" ? EMBEDDED_IMPORTS : PACKAGE_IMPORTS;
797
816
  if (parserFunction.includes("validateArray")) {
798
- imports.push("import { validateArray } from '@amritk/helpers/validate-array';");
817
+ imports.push(table["validate-array"]);
818
+ used.add("validate-array");
799
819
  }
800
820
  if (parserFunction.includes("validateRecord")) {
801
- imports.push("import { validateRecord } from '@amritk/helpers/validate-record';");
821
+ imports.push(table["validate-record"]);
822
+ used.add("validate-record");
823
+ used.add("is-object");
802
824
  }
803
825
  if (parserFunction.includes("isObject")) {
804
- imports.push("import { isObject } from '@amritk/helpers/is-object';");
826
+ imports.push(table["is-object"]);
827
+ used.add("is-object");
805
828
  }
806
829
  if (parserFunction.includes("hasRef(")) {
807
- imports.push("import { hasRef } from '@amritk/helpers/schema-guards';");
830
+ imports.push(table["has-ref"]);
831
+ used.add("has-ref");
808
832
  }
809
- return imports;
833
+ return { imports, used };
810
834
  };
811
835
 
812
836
  // ../helpers/dist/schema-guards.js
837
+ var hasRef = (value) => {
838
+ return typeof value === "object" && value !== null && !Array.isArray(value) && "$ref" in value && typeof value.$ref === "string";
839
+ };
813
840
  var isSchemaObject3 = (schema) => {
814
841
  return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
815
842
  };
@@ -891,9 +918,6 @@ var hasMinProperties = (schema) => {
891
918
  var hasMaxProperties = (schema) => {
892
919
  return isSchemaObject3(schema) && "maxProperties" in schema && typeof schema.maxProperties === "number";
893
920
  };
894
- var hasRef = (schema) => {
895
- return isSchemaObject3(schema) && "$ref" in schema && typeof schema.$ref === "string";
896
- };
897
921
 
898
922
  // src/helpers/collect-imports.ts
899
923
  var getImportPathForFilename = (filename) => `./${filename}`;
@@ -1142,6 +1166,119 @@ var getDefaultValue = (schema) => {
1142
1166
  }
1143
1167
  };
1144
1168
 
1169
+ // src/generators/generate-strict-assertion.ts
1170
+ var wrongTypeCondition = (accessor, type) => {
1171
+ switch (type) {
1172
+ case "string":
1173
+ return `typeof ${accessor} !== "string"`;
1174
+ case "number":
1175
+ case "integer":
1176
+ return `typeof ${accessor} !== "number"`;
1177
+ case "boolean":
1178
+ return `typeof ${accessor} !== "boolean"`;
1179
+ case "array":
1180
+ return `!Array.isArray(${accessor})`;
1181
+ case "object":
1182
+ return `!isObject(${accessor})`;
1183
+ default:
1184
+ return null;
1185
+ }
1186
+ };
1187
+ var typeLabel = (type) => type === "integer" ? "number" : type;
1188
+ var escapeRegexPattern = (pattern) => {
1189
+ return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
1190
+ };
1191
+ var generateConstraintChecks = (acc, propSchema, typeName, key) => {
1192
+ if (!isSchemaObject3(propSchema) || !hasType(propSchema))
1193
+ return [];
1194
+ const t = propSchema.type;
1195
+ const lines = [];
1196
+ if (t === "string") {
1197
+ if (hasPattern(propSchema)) {
1198
+ const pattern = escapeRegexPattern(propSchema.pattern);
1199
+ lines.push(` if (typeof ${acc} === "string" && !/${pattern}/.test(${acc})) throw new Error('[${typeName}] field "${key}" must match pattern ${propSchema.pattern}');`);
1200
+ }
1201
+ if (hasMinLength(propSchema)) {
1202
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length < ${propSchema.minLength}) throw new Error('[${typeName}] field "${key}" must have at least ${propSchema.minLength} characters');`);
1203
+ }
1204
+ if (hasMaxLength(propSchema)) {
1205
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length > ${propSchema.maxLength}) throw new Error('[${typeName}] field "${key}" must have at most ${propSchema.maxLength} characters');`);
1206
+ }
1207
+ }
1208
+ if (t === "number" || t === "integer") {
1209
+ if (hasMinimum(propSchema)) {
1210
+ lines.push(` if (typeof ${acc} === "number" && ${acc} < ${propSchema.minimum}) throw new Error('[${typeName}] field "${key}" must be >= ${propSchema.minimum}');`);
1211
+ }
1212
+ if (hasMaximum(propSchema)) {
1213
+ lines.push(` if (typeof ${acc} === "number" && ${acc} > ${propSchema.maximum}) throw new Error('[${typeName}] field "${key}" must be <= ${propSchema.maximum}');`);
1214
+ }
1215
+ if (hasExclusiveMinimum(propSchema)) {
1216
+ lines.push(` if (typeof ${acc} === "number" && ${acc} <= ${propSchema.exclusiveMinimum}) throw new Error('[${typeName}] field "${key}" must be > ${propSchema.exclusiveMinimum}');`);
1217
+ }
1218
+ if (hasExclusiveMaximum(propSchema)) {
1219
+ lines.push(` if (typeof ${acc} === "number" && ${acc} >= ${propSchema.exclusiveMaximum}) throw new Error('[${typeName}] field "${key}" must be < ${propSchema.exclusiveMaximum}');`);
1220
+ }
1221
+ if (hasMultipleOf(propSchema)) {
1222
+ lines.push(` if (typeof ${acc} === "number" && ${acc} % ${propSchema.multipleOf} !== 0) throw new Error('[${typeName}] field "${key}" must be a multiple of ${propSchema.multipleOf}');`);
1223
+ }
1224
+ }
1225
+ return lines;
1226
+ };
1227
+ var generatePropertyAssertion = (key, propSchema, isRequired, typeName) => {
1228
+ const acc = safeAccessor("input", key);
1229
+ const lines = [];
1230
+ if (isRequired) {
1231
+ lines.push(` if (!(${JSON.stringify(key)} in input)) throw new Error('[${typeName}] missing required property "${key}"');`);
1232
+ }
1233
+ if (!isSchemaObject3(propSchema))
1234
+ return lines;
1235
+ if (hasRef(propSchema))
1236
+ return lines;
1237
+ if (hasEnum(propSchema)) {
1238
+ const allowed = JSON.stringify(propSchema.enum);
1239
+ const label = propSchema.enum.map((v) => JSON.stringify(v)).join(", ");
1240
+ if (isRequired) {
1241
+ lines.push(` if (!(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
1242
+ } else {
1243
+ lines.push(` if (${acc} !== undefined && !(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
1244
+ }
1245
+ return lines;
1246
+ }
1247
+ if (hasType(propSchema)) {
1248
+ const t = propSchema.type;
1249
+ const wrongType = wrongTypeCondition(acc, t);
1250
+ if (wrongType) {
1251
+ if (isRequired) {
1252
+ lines.push(` if (${wrongType}) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
1253
+ } else {
1254
+ lines.push(` if (${acc} !== undefined && (${wrongType})) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
1255
+ }
1256
+ }
1257
+ lines.push(...generateConstraintChecks(acc, propSchema, typeName, key));
1258
+ }
1259
+ return lines;
1260
+ };
1261
+ var generateObjectStrictAssertion = (schema, typeName) => {
1262
+ const lines = [];
1263
+ lines.push(` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`);
1264
+ if (!hasProperties(schema) || !isSchemaObject3(schema))
1265
+ return lines;
1266
+ const required = new Set(hasRequired(schema) ? schema.required : []);
1267
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
1268
+ lines.push(...generatePropertyAssertion(key, propSchema, required.has(key), typeName));
1269
+ }
1270
+ return lines;
1271
+ };
1272
+ var generateScalarStrictAssertion = (schema, typeName) => {
1273
+ if (!isSchemaObject3(schema) || !hasType(schema))
1274
+ return null;
1275
+ const t = schema.type;
1276
+ const wrongType = wrongTypeCondition("input", t);
1277
+ if (!wrongType)
1278
+ return null;
1279
+ return ` if (${wrongType}) throw new Error(\`[${typeName}] expected ${typeLabel(t)}, got \${input === null ? "null" : typeof input}\`);`;
1280
+ };
1281
+
1145
1282
  // src/helpers/find-discriminator.ts
1146
1283
  var findDiscriminator = (schemas) => {
1147
1284
  const propertyValues = new Map;
@@ -1712,11 +1849,22 @@ var generateFallbackObject = (schema, useRefImports, typeName) => {
1712
1849
  }`;
1713
1850
  return result;
1714
1851
  };
1715
- var generateNonObjectParser = (typeName, schema) => {
1852
+ var generateNonObjectParser = (typeName, schema, strict) => {
1716
1853
  const functionName = generateParserName(typeName);
1717
1854
  if (!isSchemaObject3(schema) || !hasType(schema)) {
1718
1855
  return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1719
1856
  }
1857
+ if (strict) {
1858
+ const assertion = generateScalarStrictAssertion(schema, typeName);
1859
+ if (assertion === null) {
1860
+ return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1861
+ }
1862
+ const returnExpr = schema.type === "array" ? `[...(input as readonly unknown[])] as ${typeName}` : `input as ${typeName}`;
1863
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1864
+ ${assertion}
1865
+ return ${returnExpr};
1866
+ };`;
1867
+ }
1720
1868
  switch (schema.type) {
1721
1869
  case "string":
1722
1870
  return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "string" ? input as ${typeName} : "" as ${typeName};`;
@@ -1731,8 +1879,14 @@ var generateNonObjectParser = (typeName, schema) => {
1731
1879
  return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1732
1880
  }
1733
1881
  };
1734
- var generateEmptyObjectParser = (typeName) => {
1882
+ var generateEmptyObjectParser = (typeName, strict) => {
1735
1883
  const functionName = generateParserName(typeName);
1884
+ if (strict) {
1885
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1886
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
1887
+ return { ...input } as ${typeName};
1888
+ };`;
1889
+ }
1736
1890
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {} as ${typeName};`;
1737
1891
  };
1738
1892
  var toVarName = (key) => {
@@ -1761,7 +1915,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
1761
1915
  case "string": {
1762
1916
  checks.push(`typeof ${varName} === "string"`);
1763
1917
  if (hasPattern(schema))
1764
- checks.push(`/${escapeRegexPattern(schema.pattern)}/.test(${varName})`);
1918
+ checks.push(`/${escapeRegexPattern2(schema.pattern)}/.test(${varName})`);
1765
1919
  if (hasMinLength(schema))
1766
1920
  checks.push(`${varName}.length >= ${schema.minLength}`);
1767
1921
  if (hasMaxLength(schema))
@@ -1814,16 +1968,28 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
1814
1968
  var shouldCacheVariable = (propSchema, _canFastPath, _useRefImports) => {
1815
1969
  return isSchemaObject3(propSchema);
1816
1970
  };
1817
- var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
1971
+ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
1818
1972
  const functionName = generateParserName(typeName);
1819
1973
  if (!hasProperties(schema)) {
1974
+ if (strict) {
1975
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1976
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
1977
+ return input as ${typeName};
1978
+ };`;
1979
+ }
1820
1980
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
1821
1981
  }
1822
1982
  const fallbackObject = generateFallbackObject(schema, useRefImports, typeName);
1823
1983
  const properties = Object.entries(schema.properties);
1824
1984
  const lines = [];
1825
1985
  lines.push(`export const ${functionName} = (input: unknown): ${typeName} => {`);
1826
- lines.push(` if (!isObject(input)) return ${fallbackObject};`);
1986
+ if (strict) {
1987
+ for (const assertionLine of generateObjectStrictAssertion(schema, typeName)) {
1988
+ lines.push(assertionLine);
1989
+ }
1990
+ } else {
1991
+ lines.push(` if (!isObject(input)) return ${fallbackObject};`);
1992
+ }
1827
1993
  const propInfo = [];
1828
1994
  const hasAllOfRefParsers = useRefImports && isSchemaObject3(schema) && hasAllOf(schema) && schema.allOf.some((entry) => isSchemaObject3(entry) && hasRef(entry));
1829
1995
  let canFastPath = !hasAllOfRefParsers;
@@ -1946,7 +2112,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
1946
2112
  }
1947
2113
  return result;
1948
2114
  };
1949
- var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings) => {
2115
+ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
1950
2116
  const functionName = generateParserName(typeName);
1951
2117
  const entries = generatePropertyEntries(schema, useRefImports);
1952
2118
  const propertyLines = entries.map((entry) => {
@@ -1956,19 +2122,19 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
1956
2122
  return ` ${safeKey2(entry.key)}: ${entry.value},`;
1957
2123
  });
1958
2124
  if (!isSchemaObject3(schema) || !("patternProperties" in schema)) {
1959
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2125
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
1960
2126
  }
1961
2127
  const patternProps = schema.patternProperties;
1962
2128
  const patterns = Object.entries(patternProps);
1963
2129
  const refPattern = patterns.find(([, ps]) => isSchemaObject3(ps) && hasRef(ps));
1964
2130
  if (!refPattern || !useRefImports) {
1965
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2131
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
1966
2132
  }
1967
2133
  const [pattern, patternSchema] = refPattern;
1968
2134
  const ref = patternSchema.$ref;
1969
2135
  const parserName = generateParserName(refToName(ref));
1970
2136
  const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
1971
- const escapedPattern = escapeRegexPattern(pattern);
2137
+ const escapedPattern = escapeRegexPattern2(pattern);
1972
2138
  const inputSpread = " ...input,";
1973
2139
  let objectProperties = inputSpread;
1974
2140
  if (propertyLines.length > 0) {
@@ -1977,9 +2143,10 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
1977
2143
  ` + line;
1978
2144
  }
1979
2145
  }
2146
+ const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
1980
2147
  return `export const ${functionName} = (input: unknown): ${typeName} => {
1981
2148
  if (!isObject(input)) {
1982
- return {} as unknown as ${typeName};
2149
+ ${notObjectBranch}
1983
2150
  }
1984
2151
  const result = {
1985
2152
  ${objectProperties}
@@ -1993,23 +2160,41 @@ ${objectProperties}
1993
2160
  return result;
1994
2161
  };`;
1995
2162
  };
1996
- var generateAdditionalPropertiesParser = (schema, typeName, useRefImports) => {
2163
+ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, strict) => {
1997
2164
  const functionName = generateParserName(typeName);
1998
2165
  const additionalProps = schema.additionalProperties;
1999
2166
  if (!additionalProps) {
2000
- return generateEmptyObjectParser(typeName);
2167
+ return generateEmptyObjectParser(typeName, strict);
2001
2168
  }
2002
2169
  if (useRefImports && isSchemaObject3(additionalProps) && hasRef(additionalProps)) {
2003
2170
  const ref = additionalProps.$ref;
2004
2171
  const parserName = generateParserName(refToName(ref));
2172
+ if (strict) {
2173
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2174
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2175
+ return validateRecord(input, ${parserName}) as ${typeName};
2176
+ };`;
2177
+ }
2005
2178
  return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${parserName}) as ${typeName};`;
2006
2179
  }
2007
2180
  if (isSchemaObject3(additionalProps) && hasType(additionalProps)) {
2008
2181
  const inlineParser = generateInlineValueParser(additionalProps);
2009
2182
  if (inlineParser) {
2183
+ if (strict) {
2184
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2185
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2186
+ return validateRecord(input, ${inlineParser}) as ${typeName};
2187
+ };`;
2188
+ }
2010
2189
  return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${inlineParser}) as ${typeName};`;
2011
2190
  }
2012
2191
  }
2192
+ if (strict) {
2193
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2194
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2195
+ return { ...input } as ${typeName};
2196
+ };`;
2197
+ }
2013
2198
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {};`;
2014
2199
  };
2015
2200
  var generateInlineValueParser = (schema) => {
@@ -2030,18 +2215,18 @@ var generateInlineValueParser = (schema) => {
2030
2215
  return null;
2031
2216
  }
2032
2217
  };
2033
- var escapeRegexPattern = (pattern) => {
2218
+ var escapeRegexPattern2 = (pattern) => {
2034
2219
  return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
2035
2220
  };
2036
- var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
2221
+ var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict) => {
2037
2222
  const functionName = generateParserName(typeName);
2038
2223
  if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
2039
- return generateEmptyObjectParser(typeName);
2224
+ return generateEmptyObjectParser(typeName, strict);
2040
2225
  }
2041
2226
  const patternProps = schema.patternProperties;
2042
2227
  const patterns = Object.entries(patternProps);
2043
2228
  if (patterns.length === 0) {
2044
- return generateEmptyObjectParser(typeName);
2229
+ return generateEmptyObjectParser(typeName, strict);
2045
2230
  }
2046
2231
  const [pattern, patternSchema] = patterns[0];
2047
2232
  let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
@@ -2052,10 +2237,11 @@ var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
2052
2237
  } else if (patternSchema === false) {
2053
2238
  patternAssignment = "";
2054
2239
  }
2055
- const escapedPattern = escapeRegexPattern(pattern);
2240
+ const escapedPattern = escapeRegexPattern2(pattern);
2241
+ const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
2056
2242
  return `export const ${functionName} = (input: unknown): ${typeName} => {
2057
2243
  if (!isObject(input)) {
2058
- return {} as unknown as ${typeName};
2244
+ ${notObjectBranch}
2059
2245
  }
2060
2246
  const result = {
2061
2247
  ...input,
@@ -2167,43 +2353,44 @@ var generateSchemaObjectParser = (typeName) => {
2167
2353
  var selectParserStrategy = (schema, typeName, options) => {
2168
2354
  const useRefImports = options?.useRefImports ?? false;
2169
2355
  const logWarnings = options?.logWarnings ?? false;
2356
+ const strict = options?.strict ?? false;
2170
2357
  if (typeName === "SchemaObject") {
2171
2358
  return generateSchemaObjectParser(typeName);
2172
2359
  }
2173
2360
  const isObjectLikeSchema2 = isObjectSchema2(schema) || isSchemaObject3(schema) && (("patternProperties" in schema) || ("additionalProperties" in schema));
2174
2361
  if (!isObjectLikeSchema2 && !isSchemaObject3(schema)) {
2175
- return generateNonObjectParser(typeName, schema);
2362
+ return generateNonObjectParser(typeName, schema, strict);
2176
2363
  }
2177
2364
  if (hasProperties(schema) && isSchemaObject3(schema) && "patternProperties" in schema) {
2178
- return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings);
2365
+ return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings, strict);
2179
2366
  }
2180
2367
  if (hasProperties(schema)) {
2181
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2368
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
2182
2369
  }
2183
2370
  if (isSchemaObject3(schema) && "if" in schema && "then" in schema && "else" in schema) {
2184
2371
  return generateConditionalParser(schema, typeName);
2185
2372
  }
2186
2373
  const conditionalObjectSchema = getConditionalObjectSchema2(schema);
2187
2374
  if (conditionalObjectSchema) {
2188
- return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings);
2375
+ return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings, strict);
2189
2376
  }
2190
2377
  if (!isObjectLikeSchema2) {
2191
- return generateNonObjectParser(typeName, schema);
2378
+ return generateNonObjectParser(typeName, schema, strict);
2192
2379
  }
2193
2380
  if ("patternProperties" in schema) {
2194
- return generatePatternPropertiesParser(schema, typeName, useRefImports);
2381
+ return generatePatternPropertiesParser(schema, typeName, useRefImports, strict);
2195
2382
  }
2196
2383
  if ("additionalProperties" in schema && !hasProperties(schema)) {
2197
2384
  const additionalProps = schema.additionalProperties;
2198
2385
  if (additionalProps === true || additionalProps === false) {
2199
- return generateEmptyObjectParser(typeName);
2386
+ return generateEmptyObjectParser(typeName, strict);
2200
2387
  }
2201
- return generateAdditionalPropertiesParser(schema, typeName, useRefImports);
2388
+ return generateAdditionalPropertiesParser(schema, typeName, useRefImports, strict);
2202
2389
  }
2203
2390
  if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
2204
- return generateEmptyObjectParser(typeName);
2391
+ return generateEmptyObjectParser(typeName, strict);
2205
2392
  }
2206
- return generateEmptyObjectParser(typeName);
2393
+ return generateEmptyObjectParser(typeName, strict);
2207
2394
  };
2208
2395
  var generateParserFunction = (schema, typeName, options) => {
2209
2396
  return selectParserStrategy(schema, typeName, options);
@@ -2255,6 +2442,7 @@ var generateFile = (schema, typeName, options) => {
2255
2442
  const typesOnly = options?.typesOnly === true;
2256
2443
  const selfRef = options?.selfRef;
2257
2444
  const rootSchema = options?.rootSchema;
2445
+ const helpersMode = options?.helpersMode ?? "package";
2258
2446
  const typeDefinition = generateTypeDefinition(schema, typeName);
2259
2447
  if (typesOnly) {
2260
2448
  const imports2 = collectImports(schema, { typesOnly: true, selfRef, rootSchema });
@@ -2269,17 +2457,19 @@ var generateFile = (schema, typeName, options) => {
2269
2457
 
2270
2458
  `;
2271
2459
  }
2272
- return result2 + typeDefinition;
2460
+ return { content: result2 + typeDefinition, usedHelpers: new Set };
2273
2461
  }
2274
2462
  const parserFunction = generateParserFunction(schema, typeName, {
2275
2463
  useRefImports: true,
2276
- ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {}
2464
+ ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {},
2465
+ ...options?.strict !== undefined ? { strict: options.strict } : {}
2277
2466
  });
2278
2467
  const shapeValidator = generateShapeValidator(schema, typeName, true);
2279
2468
  const combinedFunctions = `${shapeValidator}
2280
2469
 
2281
2470
  ${parserFunction}`;
2282
- const imports = [...collectImports(schema, { selfRef, rootSchema }), ...collectHelpers(combinedFunctions)];
2471
+ const helpers = collectHelpers(combinedFunctions, helpersMode);
2472
+ const imports = [...collectImports(schema, { selfRef, rootSchema }), ...helpers.imports];
2283
2473
  let result = "";
2284
2474
  if (imports.length > 0) {
2285
2475
  result += imports[0];
@@ -2291,12 +2481,18 @@ ${parserFunction}`;
2291
2481
 
2292
2482
  `;
2293
2483
  }
2294
- return result + typeDefinition + `
2484
+ return { content: result + typeDefinition + `
2295
2485
 
2296
- ` + combinedFunctions;
2486
+ ` + combinedFunctions, usedHelpers: helpers.used };
2297
2487
  };
2298
2488
 
2299
2489
  // src/generators/build-schema.ts
2490
+ var readHelperSource = async (helper) => {
2491
+ const require2 = createRequire(import.meta.url);
2492
+ const helpersPkgPath = require2.resolve("@amritk/helpers/package.json");
2493
+ const helpersRoot = dirname(helpersPkgPath);
2494
+ return readFile(resolvePath(helpersRoot, "src", `${helper}.ts`), "utf-8");
2495
+ };
2300
2496
  var extractDynamicAnchorDefs = (schema) => {
2301
2497
  const refs = [];
2302
2498
  if (typeof schema !== "object" || schema === null) {
@@ -2316,26 +2512,31 @@ var extractDynamicAnchorDefs = (schema) => {
2316
2512
  }
2317
2513
  return refs;
2318
2514
  };
2319
- var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings) => {
2515
+ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict, helpersMode = "package") => {
2320
2516
  rootSchema = upgradeDraft07Schema(rootSchema);
2321
2517
  const files = [];
2322
2518
  const processedRefs = new Set;
2323
2519
  const processedFilenames = new Set;
2324
2520
  const refsToProcess = [];
2521
+ const usedHelpers = new Set;
2325
2522
  const dynamicRefMap = buildDynamicRefMap(rootSchema);
2326
2523
  const processedRootSchema = resolveDynamicRefs(rootSchema, dynamicRefMap);
2327
2524
  const extendedRootSchema = extensions ? applySchemaExtensions(processedRootSchema, rootTypeName.toLowerCase(), extensions) : processedRootSchema;
2328
- const rootContent = generateFile(extendedRootSchema, rootTypeName, {
2525
+ const rootResult = generateFile(extendedRootSchema, rootTypeName, {
2329
2526
  typesOnly: typesOnly ?? false,
2330
2527
  rootSchema,
2331
- ...logWarnings !== undefined ? { logWarnings } : {}
2528
+ helpersMode,
2529
+ ...logWarnings !== undefined ? { logWarnings } : {},
2530
+ ...strict !== undefined ? { strict } : {}
2332
2531
  });
2333
2532
  const rootFilename = rootTypeName.toLowerCase();
2334
2533
  processedFilenames.add(rootFilename);
2335
2534
  files.push({
2336
2535
  filename: `${rootFilename}.ts`,
2337
- content: rootContent
2536
+ content: rootResult.content
2338
2537
  });
2538
+ for (const helper of rootResult.usedHelpers)
2539
+ usedHelpers.add(helper);
2339
2540
  const rootRefs = extractRefs(rootSchema);
2340
2541
  refsToProcess.push(...rootRefs);
2341
2542
  const dynamicAnchorRefs = extractDynamicAnchorDefs(rootSchema);
@@ -2355,19 +2556,23 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
2355
2556
  const filename = refToFilename(ref);
2356
2557
  const processedSchema = resolveDynamicRefs(resolvedSchema, dynamicRefMap);
2357
2558
  const extendedSchema = extensions ? applySchemaExtensions(processedSchema, filename, extensions) : processedSchema;
2358
- const content = generateFile(extendedSchema, typeName, {
2559
+ const fileResult = generateFile(extendedSchema, typeName, {
2359
2560
  typesOnly: typesOnly ?? false,
2360
2561
  selfRef: ref,
2361
2562
  rootSchema,
2362
- ...logWarnings !== undefined ? { logWarnings } : {}
2563
+ helpersMode,
2564
+ ...logWarnings !== undefined ? { logWarnings } : {},
2565
+ ...strict !== undefined ? { strict } : {}
2363
2566
  });
2364
2567
  if (!processedFilenames.has(filename)) {
2365
2568
  processedFilenames.add(filename);
2366
2569
  files.push({
2367
2570
  filename: `${filename}.ts`,
2368
- content
2571
+ content: fileResult.content
2369
2572
  });
2370
2573
  }
2574
+ for (const helper of fileResult.usedHelpers)
2575
+ usedHelpers.add(helper);
2371
2576
  const nestedRefs = extractRefs(resolvedSchema);
2372
2577
  for (const nestedRef of nestedRefs) {
2373
2578
  if (!processedRefs.has(nestedRef)) {
@@ -2375,9 +2580,15 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
2375
2580
  }
2376
2581
  }
2377
2582
  }
2583
+ if (helpersMode === "embedded" && !typesOnly) {
2584
+ for (const helper of usedHelpers) {
2585
+ const source = await readHelperSource(helper);
2586
+ files.push({ filename: `_helpers/${helper}.ts`, content: source });
2587
+ }
2588
+ }
2378
2589
  const TYPE_EXPORT_RE = /^export type (\w+)/gm;
2379
2590
  const CONST_EXPORT_RE = /^export const (\w+)/gm;
2380
- const sortedFiles = [...files].sort((a, b) => a.filename.localeCompare(b.filename));
2591
+ const sortedFiles = [...files].filter((f) => !f.filename.startsWith("_helpers/")).sort((a, b) => a.filename.localeCompare(b.filename));
2381
2592
  let indexContent = "";
2382
2593
  for (const file of sortedFiles) {
2383
2594
  const moduleName = file.filename.replace(/\.ts$/, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/generate-parsers",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -23,8 +23,12 @@
23
23
  "url": "https://github.com/amritk/mjst/issues"
24
24
  },
25
25
  "files": [
26
- "dist"
26
+ "dist",
27
+ "src"
27
28
  ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
28
32
  "scripts": {
29
33
  "build": "bun run build:code && bun run build:types",
30
34
  "build:code": "bun build ./src/index.ts --outdir=dist --target=node",