@amritk/mjst 0.6.0 → 0.7.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 CHANGED
@@ -109,7 +109,8 @@ npx mjst --config ./mjst.config.json
109
109
  | 🚫 | `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. |
110
110
  | 🔒 | `readonly` | `--readonly` | `boolean` | — | `false` | Emit every property, array, and record in the generated type definitions as readonly, producing deeply immutable types. Affects type definitions only; the generated parsers still build and return plain objects. |
111
111
  | 🧰 | `helpers` | `--helpers <mode>` | `string` | — | — | Controls how generated parsers reference their runtime helpers. 'package' emits imports from @amritk/helpers (requires it to be installed in the consumer project). 'embedded' ships the helper source under outDir/_helpers/ so the output is self-contained. When omitted, the CLI auto-detects: it picks 'package' if @amritk/helpers resolves from outDir, otherwise 'embedded'. |
112
- | ⚙️ | `config` | `--config <path>` | `string` | — | — | Path to a JSON config file. Keys match the option names in this schema (schema, schemaDir, outDir, outFile, typesOnly, build, logWarnings, strict, readonly, helpers). CLI flags take precedence over config file values. |
112
+ | 🏷️ | `typeSuffix` | `--type-suffix <suffix>` | `string` | — | `""` | Suffix appended to every generated type name derived from a $ref (e.g. 'Object' turns Contact into ContactObject). Defaults to no suffix. The root type name is used verbatim and is unaffected. |
113
+ | ⚙️ | `config` | `--config <path>` | `string` | — | — | Path to a JSON config file. Keys match the option names in this schema (schema, schemaDir, outDir, outFile, typesOnly, build, logWarnings, strict, readonly, helpers, typeSuffix). CLI flags take precedence over config file values. |
113
114
  <!-- config-table-end -->
114
115
 
115
116
  ---
@@ -67,4 +67,10 @@ export type CliConfig = {
67
67
  * definitions is emitted as `readonly`, producing deeply immutable types.
68
68
  */
69
69
  readonly readonly?: boolean;
70
+ /**
71
+ * Suffix appended to every generated type name derived from a `$ref`
72
+ * (e.g. `'Object'` turns `Contact` into `ContactObject`). Defaults to `''`
73
+ * (no suffix). The root type name is used verbatim and is unaffected.
74
+ */
75
+ readonly typeSuffix?: string;
70
76
  };
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { execFile } from "node:child_process";
5
- import { mkdir, readdir, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
5
+ import { mkdir, readdir, readFile as readFile4, unlink, writeFile } from "node:fs/promises";
6
6
  import { basename, dirname as dirname2, join as join8, relative, resolve as resolve3, sep } from "node:path";
7
7
  import { promisify } from "node:util";
8
8
 
@@ -89888,15 +89888,15 @@ var refToFilename2 = (ref) => {
89888
89888
  }
89889
89889
  return filename;
89890
89890
  };
89891
- var kebabToPascal = (kebab) => {
89891
+ var kebabToPascal = (kebab, suffix) => {
89892
89892
  const words = kebab.split("-");
89893
89893
  let pascalCase = "";
89894
89894
  for (const word of words) {
89895
89895
  pascalCase += word.charAt(0).toUpperCase() + word.slice(1);
89896
89896
  }
89897
- return pascalCase + "Object";
89897
+ return pascalCase + suffix;
89898
89898
  };
89899
- var refToName = (ref) => kebabToPascal(refToFilename2(ref));
89899
+ var refToName = (ref, suffix = "") => kebabToPascal(refToFilename2(ref), suffix);
89900
89900
  var resolveDynamicRefs = (schema, dynamicRefMap) => {
89901
89901
  if (typeof schema !== "object" || schema === null) {
89902
89902
  return schema;
@@ -90180,15 +90180,15 @@ var refToFilename4 = (ref) => {
90180
90180
  }
90181
90181
  return filename;
90182
90182
  };
90183
- var kebabToPascal2 = (kebab) => {
90183
+ var kebabToPascal2 = (kebab, suffix) => {
90184
90184
  const words = kebab.split("-");
90185
90185
  let pascalCase = "";
90186
90186
  for (const word of words) {
90187
90187
  pascalCase += word.charAt(0).toUpperCase() + word.slice(1);
90188
90188
  }
90189
- return pascalCase + "Object";
90189
+ return pascalCase + suffix;
90190
90190
  };
90191
- var refToName2 = (ref) => kebabToPascal2(refToFilename4(ref));
90191
+ var refToName2 = (ref, suffix = "") => kebabToPascal2(refToFilename4(ref), suffix);
90192
90192
  var JS_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
90193
90193
  var safeKey = (key) => {
90194
90194
  if (JS_IDENTIFIER.test(key)) {
@@ -90306,13 +90306,13 @@ var getUnbrandedType = (schema, options = {}) => {
90306
90306
  if (!schema.$ref.startsWith("#")) {
90307
90307
  return "unknown";
90308
90308
  }
90309
- return refToName2(schema.$ref);
90309
+ return refToName2(schema.$ref, options.typeSuffix);
90310
90310
  }
90311
90311
  if (schema.$dynamicRef) {
90312
90312
  if (schema.$dynamicRef === "#meta") {
90313
- return "Schema";
90313
+ return `Schema${options.typeSuffix ?? ""}`;
90314
90314
  }
90315
- return refToName2(schema.$dynamicRef);
90315
+ return refToName2(schema.$dynamicRef, options.typeSuffix);
90316
90316
  }
90317
90317
  if (schema.const !== undefined) {
90318
90318
  return JSON.stringify(schema.const);
@@ -90359,7 +90359,7 @@ var getUnbrandedType = (schema, options = {}) => {
90359
90359
  if (conditionalResult) {
90360
90360
  const baseType = getTypeScriptType(conditionalResult.schema, options);
90361
90361
  if (conditionalResult.thenRef) {
90362
- return `(${baseType}) & ${refToName2(conditionalResult.thenRef)}`;
90362
+ return `(${baseType}) & ${refToName2(conditionalResult.thenRef, options.typeSuffix)}`;
90363
90363
  }
90364
90364
  return baseType;
90365
90365
  }
@@ -90440,6 +90440,31 @@ var getUnbrandedType = (schema, options = {}) => {
90440
90440
  case "object":
90441
90441
  if (schema.properties) {
90442
90442
  const readonlyPrefix = options.readonly ? "readonly " : "";
90443
+ const hasDescriptions = Object.values(schema.properties).some((p) => isSchemaObject2(p) && (typeof p.description === "string" || typeof p.$comment === "string"));
90444
+ if (hasDescriptions) {
90445
+ let properties2 = "";
90446
+ let first22 = true;
90447
+ for (const key in schema.properties) {
90448
+ const propSchema = schema.properties[key];
90449
+ const isRequired = schema.required?.includes(key) ?? false;
90450
+ const optional3 = isRequired ? "" : "?";
90451
+ const propType = getTypeScriptType(propSchema, options);
90452
+ const inlineDescription = isSchemaObject2(propSchema) && typeof propSchema.description === "string" ? propSchema.description : isSchemaObject2(propSchema) && typeof propSchema.$comment === "string" ? propSchema.$comment : undefined;
90453
+ if (!first22)
90454
+ properties2 += `
90455
+ `;
90456
+ first22 = false;
90457
+ if (inlineDescription) {
90458
+ properties2 += " /** " + inlineDescription + ` */
90459
+ ` + readonlyPrefix + safeKey(key) + optional3 + ": " + propType + ";";
90460
+ } else {
90461
+ properties2 += " " + readonlyPrefix + safeKey(key) + optional3 + ": " + propType + ";";
90462
+ }
90463
+ }
90464
+ return `{
90465
+ ` + properties2 + `
90466
+ }`;
90467
+ }
90443
90468
  let properties = "";
90444
90469
  let first5 = true;
90445
90470
  for (const key in schema.properties) {
@@ -90481,8 +90506,9 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90481
90506
  if (!isObjectLikeSchema(schema)) {
90482
90507
  const tsType = getTypeScriptType(schema, options);
90483
90508
  let result = "";
90484
- if (isSchemaObject2(schema) && schema.$comment && typeof schema.$comment === "string") {
90485
- result += buildJsDocBlock(typeName, schema.$comment);
90509
+ const topLevelComment = isSchemaObject2(schema) && typeof schema.description === "string" && schema.description || isSchemaObject2(schema) && typeof schema.$comment === "string" && schema.$comment || undefined;
90510
+ if (topLevelComment) {
90511
+ result += buildJsDocBlock(typeName, topLevelComment);
90486
90512
  }
90487
90513
  result += `export type ${typeName} = ${tsType};`;
90488
90514
  return result;
@@ -90493,9 +90519,10 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90493
90519
  const conditionalThenRef = conditionalResult?.thenRef ?? null;
90494
90520
  let jsDocTitle;
90495
90521
  let jsDocDescription;
90496
- if (isSchemaObject2(schema) && schema.$comment && typeof schema.$comment === "string") {
90522
+ const topLevelComment = isSchemaObject2(schema) && typeof schema.description === "string" && schema.description || isSchemaObject2(schema) && typeof schema.$comment === "string" && schema.$comment || undefined;
90523
+ if (topLevelComment) {
90497
90524
  jsDocTitle = typeName;
90498
- jsDocDescription = schema.$comment;
90525
+ jsDocDescription = topLevelComment;
90499
90526
  }
90500
90527
  const hasProperties2 = normalizedSchema.properties && Object.keys(normalizedSchema.properties).length > 0;
90501
90528
  const hasAdditionalProperties2 = normalizedSchema.additionalProperties && typeof normalizedSchema.additionalProperties === "object";
@@ -90552,12 +90579,12 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90552
90579
  if (isSchemaObject2(schema) && Array.isArray(schema.allOf)) {
90553
90580
  for (const entry of schema.allOf) {
90554
90581
  if (isSchemaObject2(entry) && entry.$ref) {
90555
- allOfIntersections.push(refToName2(entry.$ref));
90582
+ allOfIntersections.push(refToName2(entry.$ref, options.typeSuffix));
90556
90583
  }
90557
90584
  }
90558
90585
  }
90559
90586
  if (isSchemaObject2(schema) && typeof schema.$ref === "string" && schema.$ref.startsWith("#")) {
90560
- allOfIntersections.push(refToName2(schema.$ref));
90587
+ allOfIntersections.push(refToName2(schema.$ref, options.typeSuffix));
90561
90588
  }
90562
90589
  let result = "";
90563
90590
  if (jsDocTitle && jsDocDescription) {
@@ -90567,7 +90594,7 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90567
90594
  ` + properties + `
90568
90595
  }`;
90569
90596
  if (conditionalThenRef) {
90570
- typeBody += " & " + refToName2(conditionalThenRef);
90597
+ typeBody += " & " + refToName2(conditionalThenRef, options.typeSuffix);
90571
90598
  }
90572
90599
  for (const intersectionType of allOfIntersections) {
90573
90600
  typeBody += " & " + intersectionType;
@@ -90702,6 +90729,7 @@ var collectImports = (schema, options) => {
90702
90729
  const typesOnly = options?.typesOnly === true;
90703
90730
  const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined;
90704
90731
  const rootSchema = options?.rootSchema;
90732
+ const typeSuffix = options?.typeSuffix;
90705
90733
  const refs = new Set;
90706
90734
  const collectRefsFromValue = (value10) => {
90707
90735
  if (typeof value10 !== "object" || value10 === null) {
@@ -90829,7 +90857,7 @@ var collectImports = (schema, options) => {
90829
90857
  if (!resolveRef2(ref, rootSchema))
90830
90858
  continue;
90831
90859
  }
90832
- const typeName = refToName(ref);
90860
+ const typeName = refToName(ref, typeSuffix);
90833
90861
  const filename = refToFilename(ref);
90834
90862
  if (selfFilename !== undefined && filename === selfFilename) {
90835
90863
  continue;
@@ -91495,33 +91523,33 @@ var isPropertyRequired = (key, schema) => {
91495
91523
  }
91496
91524
  return schema.required.includes(key);
91497
91525
  };
91498
- var generateRequiredRefCall = (key, ref) => {
91526
+ var generateRequiredRefCall = (key, ref, suffix) => {
91499
91527
  const acc = safeAccessor("input", key);
91500
- const parserName = generateParserName(refToName(ref));
91528
+ const parserName = generateParserName(refToName(ref, suffix));
91501
91529
  return `${parserName}(${acc})`;
91502
91530
  };
91503
- var generateOptionalRefCall = (key, ref) => {
91531
+ var generateOptionalRefCall = (key, ref, suffix) => {
91504
91532
  const acc = safeAccessor("input", key);
91505
- const parserName = generateParserName(refToName(ref));
91533
+ const parserName = generateParserName(refToName(ref, suffix));
91506
91534
  return `...(${acc} && { ${safeKey2(key)}: ${parserName}(${acc}) })`;
91507
91535
  };
91508
- var generateRequiredArrayRefCall = (key, ref) => {
91509
- const parserName = generateParserName(refToName(ref));
91536
+ var generateRequiredArrayRefCall = (key, ref, suffix) => {
91537
+ const parserName = generateParserName(refToName(ref, suffix));
91510
91538
  return `validateArray(${safeAccessor("input", key)}, ${parserName})`;
91511
91539
  };
91512
- var generateOptionalArrayRefCall = (key, ref) => {
91513
- const parserName = generateParserName(refToName(ref));
91540
+ var generateOptionalArrayRefCall = (key, ref, suffix) => {
91541
+ const parserName = generateParserName(refToName(ref, suffix));
91514
91542
  const acc = safeAccessor("input", key);
91515
91543
  return `...(${acc} && { ${safeKey2(key)}: validateArray(${acc}, ${parserName}) })`;
91516
91544
  };
91517
- var generateRequiredRecordRefCall = (key, ref) => {
91545
+ var generateRequiredRecordRefCall = (key, ref, suffix) => {
91518
91546
  const acc = safeAccessor("input", key);
91519
- const parserName = generateParserName(refToName(ref));
91547
+ const parserName = generateParserName(refToName(ref, suffix));
91520
91548
  return `validateRecord(${acc}, ${parserName})`;
91521
91549
  };
91522
- var generateOptionalRecordRefCall = (key, ref) => {
91550
+ var generateOptionalRecordRefCall = (key, ref, suffix) => {
91523
91551
  const acc = safeAccessor("input", key);
91524
- const parserName = generateParserName(refToName(ref));
91552
+ const parserName = generateParserName(refToName(ref, suffix));
91525
91553
  return `...(${acc} && { ${safeKey2(key)}: validateRecord(${acc}, ${parserName}) })`;
91526
91554
  };
91527
91555
  var generateOptionalInlineProperty = (key, valueExpression) => {
@@ -91566,20 +91594,20 @@ var shouldUseRecordRefImport = (propSchema, useRefImports) => {
91566
91594
  const additionalProps = propSchema.additionalProperties;
91567
91595
  return isSchemaObject3(additionalProps) && hasRef(additionalProps);
91568
91596
  };
91569
- var generatePropertyValue = (key, propSchema, isRequired, useRefImports) => {
91597
+ var generatePropertyValue = (key, propSchema, isRequired, useRefImports, suffix) => {
91570
91598
  if (shouldUseRefImport(propSchema, useRefImports)) {
91571
91599
  const ref = propSchema.$ref;
91572
- return isRequired ? generateRequiredRefCall(key, ref) : generateOptionalRefCall(key, ref);
91600
+ return isRequired ? generateRequiredRefCall(key, ref, suffix) : generateOptionalRefCall(key, ref, suffix);
91573
91601
  }
91574
91602
  if (shouldUseArrayRefImport(propSchema, useRefImports)) {
91575
91603
  const items = propSchema.items;
91576
91604
  const ref = items.$ref;
91577
- return isRequired ? generateRequiredArrayRefCall(key, ref) : generateOptionalArrayRefCall(key, ref);
91605
+ return isRequired ? generateRequiredArrayRefCall(key, ref, suffix) : generateOptionalArrayRefCall(key, ref, suffix);
91578
91606
  }
91579
91607
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91580
91608
  const additionalProps = propSchema.additionalProperties;
91581
91609
  const ref = additionalProps.$ref;
91582
- return isRequired ? generateRequiredRecordRefCall(key, ref) : generateOptionalRecordRefCall(key, ref);
91610
+ return isRequired ? generateRequiredRecordRefCall(key, ref, suffix) : generateOptionalRecordRefCall(key, ref, suffix);
91583
91611
  }
91584
91612
  if (!isSchemaObject3(propSchema)) {
91585
91613
  return isRequired ? "undefined" : generateOptionalInlineProperty(key, "undefined");
@@ -91588,23 +91616,23 @@ var generatePropertyValue = (key, propSchema, isRequired, useRefImports) => {
91588
91616
  const valueExpression = generateValidationExpression(key, propSchema, defaultValue, isRequired);
91589
91617
  return isRequired ? valueExpression : generateOptionalInlineProperty(key, valueExpression);
91590
91618
  };
91591
- var generatePropertyEntries = (schema, useRefImports) => {
91619
+ var generatePropertyEntries = (schema, useRefImports, suffix) => {
91592
91620
  if (!hasProperties(schema)) {
91593
91621
  return [];
91594
91622
  }
91595
91623
  const entries8 = [];
91596
91624
  for (const [key, propSchema] of Object.entries(schema.properties)) {
91597
91625
  const isRequired = isPropertyRequired(key, schema);
91598
- const value10 = generatePropertyValue(key, propSchema, isRequired, useRefImports);
91626
+ const value10 = generatePropertyValue(key, propSchema, isRequired, useRefImports, suffix);
91599
91627
  const isOptional = value10.startsWith("...");
91600
91628
  entries8.push({ key, value: value10, isOptional });
91601
91629
  }
91602
91630
  return entries8;
91603
91631
  };
91604
- var generateFallbackValue = (_, propSchema, useRefImports) => {
91632
+ var generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
91605
91633
  if (useRefImports && hasRef(propSchema)) {
91606
91634
  const ref = propSchema.$ref;
91607
- const parserName = generateParserName(refToName(ref));
91635
+ const parserName = generateParserName(refToName(ref, suffix));
91608
91636
  return `${parserName}(undefined)`;
91609
91637
  }
91610
91638
  if (useRefImports && isSchemaObject3(propSchema) && propSchema.type === "array" && hasItems(propSchema) && hasRef(propSchema.items)) {
@@ -91647,7 +91675,7 @@ var generateFallbackValue = (_, propSchema, useRefImports) => {
91647
91675
  return "undefined";
91648
91676
  }
91649
91677
  };
91650
- var generateFallbackObject = (schema, useRefImports, typeName) => {
91678
+ var generateFallbackObject = (schema, useRefImports, typeName, suffix) => {
91651
91679
  if (!hasProperties(schema)) {
91652
91680
  return `{} as ${typeName}`;
91653
91681
  }
@@ -91658,7 +91686,7 @@ var generateFallbackObject = (schema, useRefImports, typeName) => {
91658
91686
  for (const [key, propSchema] of Object.entries(schema.properties)) {
91659
91687
  const isRequired = isPropertyRequired(key, schema);
91660
91688
  if (isRequired) {
91661
- const fallbackValue = generateFallbackValue(key, propSchema, useRefImports);
91689
+ const fallbackValue = generateFallbackValue(key, propSchema, useRefImports, suffix);
91662
91690
  if (fallbackValue === "undefined") {
91663
91691
  return `{} as ${typeName}`;
91664
91692
  }
@@ -91726,11 +91754,11 @@ var toVarName = (key) => {
91726
91754
  return `_${safe}`;
91727
91755
  };
91728
91756
  var shapeValidatorName = (typeName) => `validate${typeName}Shape`;
91729
- var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91757
+ var generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
91730
91758
  if (!isSchemaObject3(schema))
91731
91759
  return null;
91732
91760
  if (useRefImports && hasRef(schema)) {
91733
- const refName = refToName(schema.$ref);
91761
+ const refName = refToName(schema.$ref, suffix);
91734
91762
  return `${shapeValidatorName(refName)}(${varName})`;
91735
91763
  }
91736
91764
  if (hasEnum(schema) || hasOneOf(schema) || hasAnyOf(schema) || hasAllOf(schema) || hasRef(schema) || "not" in schema) {
@@ -91739,7 +91767,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91739
91767
  if (!hasType(schema))
91740
91768
  return null;
91741
91769
  if (useRefImports && schema.type === "array" && hasItems(schema) && isSchemaObject3(schema.items) && hasRef(schema.items)) {
91742
- const refName = refToName(schema.items.$ref);
91770
+ const refName = refToName(schema.items.$ref, suffix);
91743
91771
  return `Array.isArray(${varName}) && ${varName}.every(${shapeValidatorName(refName)})`;
91744
91772
  }
91745
91773
  const checks = [];
@@ -91800,7 +91828,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91800
91828
  var shouldCacheVariable = (propSchema, _canFastPath, _useRefImports) => {
91801
91829
  return isSchemaObject3(propSchema);
91802
91830
  };
91803
- var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict2) => {
91831
+ var generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict2) => {
91804
91832
  const functionName = generateParserName(typeName);
91805
91833
  if (!hasProperties(schema)) {
91806
91834
  if (strict2) {
@@ -91811,7 +91839,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91811
91839
  }
91812
91840
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
91813
91841
  }
91814
- const fallbackObject = generateFallbackObject(schema, useRefImports, typeName);
91842
+ const fallbackObject = generateFallbackObject(schema, useRefImports, typeName, suffix);
91815
91843
  const properties = Object.entries(schema.properties);
91816
91844
  const lines = [];
91817
91845
  lines.push(`export const ${functionName} = (input: unknown): ${typeName} => {`);
@@ -91834,7 +91862,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91834
91862
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91835
91863
  canFastPath = false;
91836
91864
  } else {
91837
- const check7 = generatePropertyTypeCheck(varName, propSchema, useRefImports);
91865
+ const check7 = generatePropertyTypeCheck(varName, propSchema, useRefImports, suffix);
91838
91866
  if (check7 === null) {
91839
91867
  canFastPath = false;
91840
91868
  } else {
@@ -91873,7 +91901,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91873
91901
  if (useRefImports && isSchemaObject3(schema) && hasAllOf(schema)) {
91874
91902
  for (const entry of schema.allOf) {
91875
91903
  if (isSchemaObject3(entry) && hasRef(entry)) {
91876
- const parserName = generateParserName(refToName(entry.$ref));
91904
+ const parserName = generateParserName(refToName(entry.$ref, suffix));
91877
91905
  objectLines.push(` ...(${parserName}(input) as Record<string, unknown>),`);
91878
91906
  }
91879
91907
  }
@@ -91883,7 +91911,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91883
91911
  const accessor = shouldCache ? varName : safeAccessor("input", key);
91884
91912
  if (shouldUseRefImport(propSchema, useRefImports)) {
91885
91913
  const ref = propSchema.$ref;
91886
- const parserName = generateParserName(refToName(ref));
91914
+ const parserName = generateParserName(refToName(ref, suffix));
91887
91915
  if (isRequired) {
91888
91916
  objectLines.push(` ${safeKey2(key)}: ${parserName}(${accessor}),`);
91889
91917
  } else {
@@ -91894,7 +91922,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91894
91922
  if (shouldUseArrayRefImport(propSchema, useRefImports)) {
91895
91923
  const items = propSchema.items;
91896
91924
  const ref = items.$ref;
91897
- const parserName = generateParserName(refToName(ref));
91925
+ const parserName = generateParserName(refToName(ref, suffix));
91898
91926
  if (isRequired) {
91899
91927
  objectLines.push(` ${safeKey2(key)}: validateArray(${accessor}, ${parserName}),`);
91900
91928
  } else {
@@ -91905,7 +91933,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91905
91933
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91906
91934
  const additionalProps = propSchema.additionalProperties;
91907
91935
  const ref = additionalProps.$ref;
91908
- const parserName = generateParserName(refToName(ref));
91936
+ const parserName = generateParserName(refToName(ref, suffix));
91909
91937
  if (isRequired) {
91910
91938
  objectLines.push(` ${safeKey2(key)}: validateRecord(${accessor}, ${parserName}),`);
91911
91939
  } else {
@@ -91944,9 +91972,9 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91944
91972
  }
91945
91973
  return result;
91946
91974
  };
91947
- var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings, strict2) => {
91975
+ var generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict2) => {
91948
91976
  const functionName = generateParserName(typeName);
91949
- const entries8 = generatePropertyEntries(schema, useRefImports);
91977
+ const entries8 = generatePropertyEntries(schema, useRefImports, suffix);
91950
91978
  const propertyLines = entries8.map((entry) => {
91951
91979
  if (entry.isOptional) {
91952
91980
  return ` ${entry.value},`;
@@ -91954,17 +91982,17 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
91954
91982
  return ` ${safeKey2(entry.key)}: ${entry.value},`;
91955
91983
  });
91956
91984
  if (!isSchemaObject3(schema) || !("patternProperties" in schema)) {
91957
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
91985
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
91958
91986
  }
91959
91987
  const patternProps = schema.patternProperties;
91960
91988
  const patterns = Object.entries(patternProps);
91961
91989
  const refPattern = patterns.find(([, ps]) => isSchemaObject3(ps) && hasRef(ps));
91962
91990
  if (!refPattern || !useRefImports) {
91963
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
91991
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
91964
91992
  }
91965
91993
  const [pattern2, patternSchema] = refPattern;
91966
91994
  const ref = patternSchema.$ref;
91967
- const parserName = generateParserName(refToName(ref));
91995
+ const parserName = generateParserName(refToName(ref, suffix));
91968
91996
  const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
91969
91997
  const escapedPattern = escapeRegexPattern2(pattern2);
91970
91998
  const inputSpread = " ...input,";
@@ -91992,7 +92020,7 @@ ${objectProperties}
91992
92020
  return result;
91993
92021
  };`;
91994
92022
  };
91995
- var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, strict2) => {
92023
+ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict2) => {
91996
92024
  const functionName = generateParserName(typeName);
91997
92025
  const additionalProps = schema.additionalProperties;
91998
92026
  if (!additionalProps) {
@@ -92000,7 +92028,7 @@ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, stric
92000
92028
  }
92001
92029
  if (useRefImports && isSchemaObject3(additionalProps) && hasRef(additionalProps)) {
92002
92030
  const ref = additionalProps.$ref;
92003
- const parserName = generateParserName(refToName(ref));
92031
+ const parserName = generateParserName(refToName(ref, suffix));
92004
92032
  if (strict2) {
92005
92033
  return `export const ${functionName} = (input: unknown): ${typeName} => {
92006
92034
  if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
@@ -92050,7 +92078,7 @@ var generateInlineValueParser = (schema) => {
92050
92078
  var escapeRegexPattern2 = (pattern2) => {
92051
92079
  return pattern2.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
92052
92080
  };
92053
- var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict2) => {
92081
+ var generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict2) => {
92054
92082
  const functionName = generateParserName(typeName);
92055
92083
  if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
92056
92084
  return generateEmptyObjectParser(typeName, strict2);
@@ -92064,7 +92092,7 @@ var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict2)
92064
92092
  let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
92065
92093
  if (useRefImports && isSchemaObject3(patternSchema) && hasRef(patternSchema)) {
92066
92094
  const ref = patternSchema.$ref;
92067
- const parserName = generateParserName(refToName(ref));
92095
+ const parserName = generateParserName(refToName(ref, suffix));
92068
92096
  patternAssignment = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
92069
92097
  } else if (patternSchema === false) {
92070
92098
  patternAssignment = "";
@@ -92087,7 +92115,7 @@ ${notObjectBranch}
92087
92115
  return result;
92088
92116
  };`;
92089
92117
  };
92090
- var generateConditionalParser = (schema, typeName) => {
92118
+ var generateConditionalParser = (schema, typeName, suffix) => {
92091
92119
  const functionName = generateParserName(typeName);
92092
92120
  const ifSchema = schema.if;
92093
92121
  const thenSchema = schema.then;
@@ -92097,7 +92125,7 @@ var generateConditionalParser = (schema, typeName) => {
92097
92125
  if (!thenHasRef || !elseHasRef) {
92098
92126
  const mergedSchema = getConditionalObjectSchema2(schema);
92099
92127
  if (mergedSchema) {
92100
- return generateObjectParser(mergedSchema, typeName, false);
92128
+ return generateObjectParser(mergedSchema, typeName, false, suffix);
92101
92129
  }
92102
92130
  return generateEmptyObjectParser(typeName);
92103
92131
  }
@@ -92105,15 +92133,15 @@ var generateConditionalParser = (schema, typeName) => {
92105
92133
  if (!isRefCondition) {
92106
92134
  const mergedSchema = getConditionalObjectSchema2(schema);
92107
92135
  if (mergedSchema) {
92108
- return generateObjectParser(mergedSchema, typeName, false);
92136
+ return generateObjectParser(mergedSchema, typeName, false, suffix);
92109
92137
  }
92110
92138
  return generateEmptyObjectParser(typeName);
92111
92139
  }
92112
92140
  const thenRef = thenSchema.$ref;
92113
92141
  const elseRef = elseSchema.$ref;
92114
- const thenParserName = generateParserName(refToName(thenRef));
92115
- const elseParserName = generateParserName(refToName(elseRef));
92116
- const thenTypeName = refToName(thenRef);
92142
+ const thenParserName = generateParserName(refToName(thenRef, suffix));
92143
+ const elseParserName = generateParserName(refToName(elseRef, suffix));
92144
+ const thenTypeName = refToName(thenRef, suffix);
92117
92145
  return `export const ${functionName} = (input: unknown): ${typeName} | ${thenTypeName} =>
92118
92146
  hasRef(input) ? ${thenParserName}(input) : ${elseParserName}(input)
92119
92147
  `;
@@ -92186,7 +92214,8 @@ var selectParserStrategy = (schema, typeName, options) => {
92186
92214
  const useRefImports = options?.useRefImports ?? false;
92187
92215
  const logWarnings = options?.logWarnings ?? false;
92188
92216
  const strict2 = options?.strict ?? false;
92189
- if (typeName === "SchemaObject") {
92217
+ const suffix = options?.typeSuffix ?? "";
92218
+ if (typeName === `Schema${suffix}`) {
92190
92219
  return generateSchemaObjectParser(typeName);
92191
92220
  }
92192
92221
  const isObjectLikeSchema2 = isObjectSchema2(schema) || isSchemaObject3(schema) && (("patternProperties" in schema) || ("additionalProperties" in schema));
@@ -92194,30 +92223,30 @@ var selectParserStrategy = (schema, typeName, options) => {
92194
92223
  return generateNonObjectParser(typeName, schema, strict2);
92195
92224
  }
92196
92225
  if (hasProperties(schema) && isSchemaObject3(schema) && "patternProperties" in schema) {
92197
- return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
92226
+ return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
92198
92227
  }
92199
92228
  if (hasProperties(schema)) {
92200
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
92229
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
92201
92230
  }
92202
92231
  if (isSchemaObject3(schema) && "if" in schema && "then" in schema && "else" in schema) {
92203
- return generateConditionalParser(schema, typeName);
92232
+ return generateConditionalParser(schema, typeName, suffix);
92204
92233
  }
92205
92234
  const conditionalObjectSchema = getConditionalObjectSchema2(schema);
92206
92235
  if (conditionalObjectSchema) {
92207
- return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings, strict2);
92236
+ return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict2);
92208
92237
  }
92209
92238
  if (!isObjectLikeSchema2) {
92210
92239
  return generateNonObjectParser(typeName, schema, strict2);
92211
92240
  }
92212
92241
  if ("patternProperties" in schema) {
92213
- return generatePatternPropertiesParser(schema, typeName, useRefImports, strict2);
92242
+ return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict2);
92214
92243
  }
92215
92244
  if ("additionalProperties" in schema && !hasProperties(schema)) {
92216
92245
  const additionalProps = schema.additionalProperties;
92217
92246
  if (additionalProps === true || additionalProps === false) {
92218
92247
  return generateEmptyObjectParser(typeName, strict2);
92219
92248
  }
92220
- return generateAdditionalPropertiesParser(schema, typeName, useRefImports, strict2);
92249
+ return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict2);
92221
92250
  }
92222
92251
  if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
92223
92252
  return generateEmptyObjectParser(typeName, strict2);
@@ -92227,7 +92256,7 @@ var selectParserStrategy = (schema, typeName, options) => {
92227
92256
  var generateParserFunction = (schema, typeName, options) => {
92228
92257
  return selectParserStrategy(schema, typeName, options);
92229
92258
  };
92230
- var generateShapeValidator = (schema, typeName, useRefImports) => {
92259
+ var generateShapeValidator = (schema, typeName, useRefImports, suffix = "") => {
92231
92260
  const fnName = shapeValidatorName(typeName);
92232
92261
  const stub = `export const ${fnName} = (_input: unknown): boolean => false;`;
92233
92262
  if (!isSchemaObject3(schema))
@@ -92245,7 +92274,7 @@ var generateShapeValidator = (schema, typeName, useRefImports) => {
92245
92274
  }
92246
92275
  const accessor = safeAccessor("input", key);
92247
92276
  const isRequired = isPropertyRequired(key, schema);
92248
- const check7 = generatePropertyTypeCheck(accessor, propSchema, useRefImports);
92277
+ const check7 = generatePropertyTypeCheck(accessor, propSchema, useRefImports, suffix);
92249
92278
  if (check7 === null) {
92250
92279
  return stub;
92251
92280
  }
@@ -92273,9 +92302,10 @@ var generateFile = (schema, typeName, options) => {
92273
92302
  const selfRef = options?.selfRef;
92274
92303
  const rootSchema = options?.rootSchema;
92275
92304
  const helpersMode = options?.helpersMode ?? "package";
92276
- const typeDefinition = generateTypeDefinition(schema, typeName, { readonly: options?.readonly ?? false });
92305
+ const typeSuffix = options?.typeSuffix ?? "";
92306
+ const typeDefinition = generateTypeDefinition(schema, typeName, { readonly: options?.readonly ?? false, typeSuffix });
92277
92307
  if (typesOnly) {
92278
- const imports2 = collectImports(schema, { typesOnly: true, selfRef, rootSchema });
92308
+ const imports2 = collectImports(schema, { typesOnly: true, selfRef, rootSchema, typeSuffix });
92279
92309
  let result2 = "";
92280
92310
  if (imports2.length > 0) {
92281
92311
  result2 += imports2[0];
@@ -92291,15 +92321,16 @@ var generateFile = (schema, typeName, options) => {
92291
92321
  }
92292
92322
  const parserFunction = generateParserFunction(schema, typeName, {
92293
92323
  useRefImports: true,
92324
+ typeSuffix,
92294
92325
  ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {},
92295
92326
  ...options?.strict !== undefined ? { strict: options.strict } : {}
92296
92327
  });
92297
- const shapeValidator = generateShapeValidator(schema, typeName, true);
92328
+ const shapeValidator = generateShapeValidator(schema, typeName, true, typeSuffix);
92298
92329
  const combinedFunctions = `${shapeValidator}
92299
92330
 
92300
92331
  ${parserFunction}`;
92301
92332
  const helpers = collectHelpers(combinedFunctions, helpersMode, options?.helpersImportPrefix);
92302
- const imports = [...collectImports(schema, { selfRef, rootSchema }), ...helpers.imports];
92333
+ const imports = [...collectImports(schema, { selfRef, rootSchema, typeSuffix }), ...helpers.imports];
92303
92334
  let result = "";
92304
92335
  if (imports.length > 0) {
92305
92336
  result += imports[0];
@@ -92340,7 +92371,7 @@ var extractDynamicAnchorDefs = (schema) => {
92340
92371
  }
92341
92372
  return refs;
92342
92373
  };
92343
- var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict2, helpersMode = "package", helpersImportPrefix = "./", readonly2 = false) => {
92374
+ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict2, helpersMode = "package", helpersImportPrefix = "./", readonly2 = false, typeSuffix = "") => {
92344
92375
  rootSchema = upgradeDraft07Schema(rootSchema);
92345
92376
  const files = [];
92346
92377
  const processedRefs = new Set;
@@ -92356,6 +92387,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92356
92387
  helpersMode,
92357
92388
  helpersImportPrefix,
92358
92389
  readonly: readonly2,
92390
+ typeSuffix,
92359
92391
  ...logWarnings !== undefined ? { logWarnings } : {},
92360
92392
  ...strict2 !== undefined ? { strict: strict2 } : {}
92361
92393
  });
@@ -92382,7 +92414,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92382
92414
  console.warn(`Warning: Could not resolve ref: ${ref}`);
92383
92415
  continue;
92384
92416
  }
92385
- const typeName = refToName(ref);
92417
+ const typeName = refToName(ref, typeSuffix);
92386
92418
  const filename = refToFilename(ref);
92387
92419
  const processedSchema = resolveDynamicRefs(resolvedSchema, dynamicRefMap);
92388
92420
  const extendedSchema = extensions ? applySchemaExtensions(processedSchema, filename, extensions) : processedSchema;
@@ -92393,6 +92425,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92393
92425
  helpersMode,
92394
92426
  helpersImportPrefix,
92395
92427
  readonly: readonly2,
92428
+ typeSuffix,
92396
92429
  ...logWarnings !== undefined ? { logWarnings } : {},
92397
92430
  ...strict2 !== undefined ? { strict: strict2 } : {}
92398
92431
  });
@@ -92581,7 +92614,8 @@ var VALUE_KEYS = new Set([
92581
92614
  "outFile",
92582
92615
  "input",
92583
92616
  "export",
92584
- "helpers"
92617
+ "helpers",
92618
+ "typeSuffix"
92585
92619
  ]);
92586
92620
  var toCamelCase = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
92587
92621
  var parseHelpersValue = (value10) => {
@@ -92607,6 +92641,9 @@ var assignValue = (config3, key, value10) => {
92607
92641
  case "export":
92608
92642
  config3.export = value10;
92609
92643
  return true;
92644
+ case "typeSuffix":
92645
+ config3.typeSuffix = value10;
92646
+ return true;
92610
92647
  case "input": {
92611
92648
  const parsed = parseInputValue(value10);
92612
92649
  if (parsed)
@@ -92678,8 +92715,17 @@ var parseCliArgs = (args2) => {
92678
92715
  return config3;
92679
92716
  };
92680
92717
 
92718
+ // src/read-version.ts
92719
+ import { readFile as readFile3 } from "node:fs/promises";
92720
+ var readVersion = async () => {
92721
+ const pkgUrl = new URL("../package.json", import.meta.url);
92722
+ const pkg = JSON.parse(await readFile3(pkgUrl, "utf-8"));
92723
+ return pkg.version ?? "0.0.0";
92724
+ };
92725
+
92681
92726
  // src/cli.ts
92682
92727
  var execFileAsync = promisify(execFile);
92728
+ var isVersionRequest = (args2) => args2[0] === "version" || args2.includes("--version") || args2.includes("-v");
92683
92729
  var extractConfigPath = (args2) => {
92684
92730
  for (let i = 0;i < args2.length; i++) {
92685
92731
  const arg = args2[i];
@@ -92695,7 +92741,7 @@ var extractConfigPath = (args2) => {
92695
92741
  var loadSchema = async (config3, schemaPath) => {
92696
92742
  const inputFormat = config3.input ?? "json";
92697
92743
  if (inputFormat === "json") {
92698
- return JSON.parse(await readFile3(schemaPath, "utf-8"));
92744
+ return JSON.parse(await readFile4(schemaPath, "utf-8"));
92699
92745
  }
92700
92746
  console.log(`Input format: ${inputFormat}`);
92701
92747
  const source = await loadSchemaModule(schemaPath, config3.export);
@@ -92755,7 +92801,7 @@ var runSingle = async (config3, schemaPath, outputDir) => {
92755
92801
  console.log(`Helpers mode: ${helpersMode}${config3.helpers ? " (explicit)" : " (auto-detected)"}`);
92756
92802
  const rootTypeName = deriveRootTypeName(schema);
92757
92803
  console.log(`Root type: ${rootTypeName}`);
92758
- const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, undefined, config3.readonly);
92804
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, undefined, config3.readonly, config3.typeSuffix);
92759
92805
  for (const file2 of files) {
92760
92806
  await writeGeneratedFile(outputDir, file2.filename, file2.content);
92761
92807
  console.log(`Generated: ${file2.filename}`);
@@ -92773,7 +92819,7 @@ var runSingleFile = async (config3, schemaPath, outFilePath) => {
92773
92819
  await mkdir(outputDir, { recursive: true });
92774
92820
  const rootTypeName = deriveRootTypeName(schema);
92775
92821
  console.log(`Root type: ${rootTypeName}`);
92776
- const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, config3.helpers ?? "package", undefined, config3.readonly);
92822
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, config3.helpers ?? "package", undefined, config3.readonly, config3.typeSuffix);
92777
92823
  await writeFile(outFilePath, combineGeneratedFiles(files), "utf-8");
92778
92824
  console.log(`Generated: ${relative(process.cwd(), outFilePath)}`);
92779
92825
  if (config3.build) {
@@ -92808,7 +92854,7 @@ var runRecursive = async (config3, schemaDir, outputDir) => {
92808
92854
  const rootTypeName = deriveRootTypeName(schema);
92809
92855
  console.log(`
92810
92856
  ${relPath} → ${relNoExt}/ (root type: ${rootTypeName})`);
92811
- const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, helpersImportPrefix, config3.readonly);
92857
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, helpersImportPrefix, config3.readonly, config3.typeSuffix);
92812
92858
  for (const file2 of files) {
92813
92859
  if (file2.filename.startsWith("_helpers/")) {
92814
92860
  sharedHelpers.set(file2.filename, file2.content);
@@ -92834,6 +92880,10 @@ Total files generated: ${writtenTsFiles.length}`);
92834
92880
  };
92835
92881
  var run10 = async () => {
92836
92882
  const args2 = process.argv.slice(2);
92883
+ if (isVersionRequest(args2)) {
92884
+ console.log(await readVersion());
92885
+ return;
92886
+ }
92837
92887
  const configPath = extractConfigPath(args2);
92838
92888
  const fileConfig = configPath ? await loadConfig(configPath) : {};
92839
92889
  const cliConfig = parseCliArgs(args2);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads the CLI's own version from its `package.json`.
3
+ *
4
+ * Resolved relative to this module so it works whether the code runs from
5
+ * `src/` (dev) or the bundled `dist/cli.js` (published) — `package.json`
6
+ * sits one directory above both.
7
+ */
8
+ export declare const readVersion: () => Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/mjst",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,10 +42,10 @@
42
42
  "generate-readme": "bun run ./scripts/generate-readme.ts"
43
43
  },
44
44
  "dependencies": {
45
- "@amritk/adapters": "0.2.2",
46
- "@amritk/generate-parsers": "0.5.0",
45
+ "@amritk/adapters": "0.2.4",
46
+ "@amritk/generate-parsers": "0.6.1",
47
47
  "@amritk/generate-markdown": "0.2.1",
48
- "@amritk/helpers": "0.5.0"
48
+ "@amritk/helpers": "0.6.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "json-schema-typed": "^8.0.1"
package/src/cli-config.ts CHANGED
@@ -68,4 +68,10 @@ export type CliConfig = {
68
68
  * definitions is emitted as `readonly`, producing deeply immutable types.
69
69
  */
70
70
  readonly readonly?: boolean
71
+ /**
72
+ * Suffix appended to every generated type name derived from a `$ref`
73
+ * (e.g. `'Object'` turns `Contact` into `ContactObject`). Defaults to `''`
74
+ * (no suffix). The root type name is used verbatim and is unaffected.
75
+ */
76
+ readonly typeSuffix?: string
71
77
  }
package/src/cli.ts CHANGED
@@ -16,6 +16,11 @@ import { detectHelpersMode } from './detect-helpers-mode'
16
16
  import { loadConfig } from './load-config'
17
17
  import { loadSchemaModule } from './load-schema-module'
18
18
  import { parseCliArgs } from './parse-cli-args'
19
+ import { readVersion } from './read-version'
20
+
21
+ /** True when the args request the CLI version (`version`, `--version`, or `-v`). */
22
+ const isVersionRequest = (args: readonly string[]): boolean =>
23
+ args[0] === 'version' || args.includes('--version') || args.includes('-v')
19
24
 
20
25
  /**
21
26
  * Extracts the --config flag value from process args before full parsing.
@@ -142,6 +147,7 @@ const runSingle = async (config: Partial<CliConfig>, schemaPath: string, outputD
142
147
  helpersMode,
143
148
  undefined,
144
149
  config.readonly,
150
+ config.typeSuffix,
145
151
  )
146
152
 
147
153
  for (const file of files) {
@@ -183,6 +189,7 @@ const runSingleFile = async (config: Partial<CliConfig>, schemaPath: string, out
183
189
  config.helpers ?? 'package',
184
190
  undefined,
185
191
  config.readonly,
192
+ config.typeSuffix,
186
193
  )
187
194
 
188
195
  await writeFile(outFilePath, combineGeneratedFiles(files), 'utf-8')
@@ -247,6 +254,7 @@ const runRecursive = async (config: Partial<CliConfig>, schemaDir: string, outpu
247
254
  helpersMode,
248
255
  helpersImportPrefix,
249
256
  config.readonly,
257
+ config.typeSuffix,
250
258
  )
251
259
 
252
260
  for (const file of files) {
@@ -279,6 +287,11 @@ const run = async (): Promise<void> => {
279
287
  // Skip the first two args (node executable and script path)
280
288
  const args = process.argv.slice(2)
281
289
 
290
+ if (isVersionRequest(args)) {
291
+ console.log(await readVersion())
292
+ return
293
+ }
294
+
282
295
  const configPath = extractConfigPath(args)
283
296
 
284
297
  // Start with config file values if provided, then overlay CLI flags on top
@@ -18,6 +18,7 @@ type MutableConfig = {
18
18
  strict?: boolean
19
19
  readonly?: boolean
20
20
  helpers?: 'package' | 'embedded'
21
+ typeSuffix?: string
21
22
  }
22
23
 
23
24
  // Boolean flags toggle on by presence and accept `--flag=false` to opt out.
@@ -31,6 +32,7 @@ const VALUE_KEYS = new Set<keyof MutableConfig>([
31
32
  'input',
32
33
  'export',
33
34
  'helpers',
35
+ 'typeSuffix',
34
36
  ])
35
37
 
36
38
  /** Normalizes a CLI flag name to its camelCase config key so both `--out-dir` and `--outDir` map to `outDir`. */
@@ -62,6 +64,9 @@ const assignValue = (config: MutableConfig, key: string, value: string): boolean
62
64
  case 'export':
63
65
  config.export = value
64
66
  return true
67
+ case 'typeSuffix':
68
+ config.typeSuffix = value
69
+ return true
65
70
  case 'input': {
66
71
  const parsed = parseInputValue(value)
67
72
  if (parsed) config.input = parsed
@@ -0,0 +1,18 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { readVersion } from './read-version'
5
+
6
+ describe('read-version', () => {
7
+ it('returns the version declared in the package.json', async () => {
8
+ const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf-8')) as {
9
+ version: string
10
+ }
11
+
12
+ expect(await readVersion()).toBe(pkg.version)
13
+ })
14
+
15
+ it('returns a semver-shaped string', async () => {
16
+ expect(await readVersion()).toMatch(/^\d+\.\d+\.\d+/)
17
+ })
18
+ })
@@ -0,0 +1,14 @@
1
+ import { readFile } from 'node:fs/promises'
2
+
3
+ /**
4
+ * Reads the CLI's own version from its `package.json`.
5
+ *
6
+ * Resolved relative to this module so it works whether the code runs from
7
+ * `src/` (dev) or the bundled `dist/cli.js` (published) — `package.json`
8
+ * sits one directory above both.
9
+ */
10
+ export const readVersion = async (): Promise<string> => {
11
+ const pkgUrl = new URL('../package.json', import.meta.url)
12
+ const pkg = JSON.parse(await readFile(pkgUrl, 'utf-8')) as { version?: string }
13
+ return pkg.version ?? '0.0.0'
14
+ }