@amritk/mjst 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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
  }
@@ -90552,12 +90552,12 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90552
90552
  if (isSchemaObject2(schema) && Array.isArray(schema.allOf)) {
90553
90553
  for (const entry of schema.allOf) {
90554
90554
  if (isSchemaObject2(entry) && entry.$ref) {
90555
- allOfIntersections.push(refToName2(entry.$ref));
90555
+ allOfIntersections.push(refToName2(entry.$ref, options.typeSuffix));
90556
90556
  }
90557
90557
  }
90558
90558
  }
90559
90559
  if (isSchemaObject2(schema) && typeof schema.$ref === "string" && schema.$ref.startsWith("#")) {
90560
- allOfIntersections.push(refToName2(schema.$ref));
90560
+ allOfIntersections.push(refToName2(schema.$ref, options.typeSuffix));
90561
90561
  }
90562
90562
  let result = "";
90563
90563
  if (jsDocTitle && jsDocDescription) {
@@ -90567,7 +90567,7 @@ var generateTypeDefinition = (schema, typeName, options = {}) => {
90567
90567
  ` + properties + `
90568
90568
  }`;
90569
90569
  if (conditionalThenRef) {
90570
- typeBody += " & " + refToName2(conditionalThenRef);
90570
+ typeBody += " & " + refToName2(conditionalThenRef, options.typeSuffix);
90571
90571
  }
90572
90572
  for (const intersectionType of allOfIntersections) {
90573
90573
  typeBody += " & " + intersectionType;
@@ -90702,6 +90702,7 @@ var collectImports = (schema, options) => {
90702
90702
  const typesOnly = options?.typesOnly === true;
90703
90703
  const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined;
90704
90704
  const rootSchema = options?.rootSchema;
90705
+ const typeSuffix = options?.typeSuffix;
90705
90706
  const refs = new Set;
90706
90707
  const collectRefsFromValue = (value10) => {
90707
90708
  if (typeof value10 !== "object" || value10 === null) {
@@ -90829,7 +90830,7 @@ var collectImports = (schema, options) => {
90829
90830
  if (!resolveRef2(ref, rootSchema))
90830
90831
  continue;
90831
90832
  }
90832
- const typeName = refToName(ref);
90833
+ const typeName = refToName(ref, typeSuffix);
90833
90834
  const filename = refToFilename(ref);
90834
90835
  if (selfFilename !== undefined && filename === selfFilename) {
90835
90836
  continue;
@@ -91495,33 +91496,33 @@ var isPropertyRequired = (key, schema) => {
91495
91496
  }
91496
91497
  return schema.required.includes(key);
91497
91498
  };
91498
- var generateRequiredRefCall = (key, ref) => {
91499
+ var generateRequiredRefCall = (key, ref, suffix) => {
91499
91500
  const acc = safeAccessor("input", key);
91500
- const parserName = generateParserName(refToName(ref));
91501
+ const parserName = generateParserName(refToName(ref, suffix));
91501
91502
  return `${parserName}(${acc})`;
91502
91503
  };
91503
- var generateOptionalRefCall = (key, ref) => {
91504
+ var generateOptionalRefCall = (key, ref, suffix) => {
91504
91505
  const acc = safeAccessor("input", key);
91505
- const parserName = generateParserName(refToName(ref));
91506
+ const parserName = generateParserName(refToName(ref, suffix));
91506
91507
  return `...(${acc} && { ${safeKey2(key)}: ${parserName}(${acc}) })`;
91507
91508
  };
91508
- var generateRequiredArrayRefCall = (key, ref) => {
91509
- const parserName = generateParserName(refToName(ref));
91509
+ var generateRequiredArrayRefCall = (key, ref, suffix) => {
91510
+ const parserName = generateParserName(refToName(ref, suffix));
91510
91511
  return `validateArray(${safeAccessor("input", key)}, ${parserName})`;
91511
91512
  };
91512
- var generateOptionalArrayRefCall = (key, ref) => {
91513
- const parserName = generateParserName(refToName(ref));
91513
+ var generateOptionalArrayRefCall = (key, ref, suffix) => {
91514
+ const parserName = generateParserName(refToName(ref, suffix));
91514
91515
  const acc = safeAccessor("input", key);
91515
91516
  return `...(${acc} && { ${safeKey2(key)}: validateArray(${acc}, ${parserName}) })`;
91516
91517
  };
91517
- var generateRequiredRecordRefCall = (key, ref) => {
91518
+ var generateRequiredRecordRefCall = (key, ref, suffix) => {
91518
91519
  const acc = safeAccessor("input", key);
91519
- const parserName = generateParserName(refToName(ref));
91520
+ const parserName = generateParserName(refToName(ref, suffix));
91520
91521
  return `validateRecord(${acc}, ${parserName})`;
91521
91522
  };
91522
- var generateOptionalRecordRefCall = (key, ref) => {
91523
+ var generateOptionalRecordRefCall = (key, ref, suffix) => {
91523
91524
  const acc = safeAccessor("input", key);
91524
- const parserName = generateParserName(refToName(ref));
91525
+ const parserName = generateParserName(refToName(ref, suffix));
91525
91526
  return `...(${acc} && { ${safeKey2(key)}: validateRecord(${acc}, ${parserName}) })`;
91526
91527
  };
91527
91528
  var generateOptionalInlineProperty = (key, valueExpression) => {
@@ -91566,20 +91567,20 @@ var shouldUseRecordRefImport = (propSchema, useRefImports) => {
91566
91567
  const additionalProps = propSchema.additionalProperties;
91567
91568
  return isSchemaObject3(additionalProps) && hasRef(additionalProps);
91568
91569
  };
91569
- var generatePropertyValue = (key, propSchema, isRequired, useRefImports) => {
91570
+ var generatePropertyValue = (key, propSchema, isRequired, useRefImports, suffix) => {
91570
91571
  if (shouldUseRefImport(propSchema, useRefImports)) {
91571
91572
  const ref = propSchema.$ref;
91572
- return isRequired ? generateRequiredRefCall(key, ref) : generateOptionalRefCall(key, ref);
91573
+ return isRequired ? generateRequiredRefCall(key, ref, suffix) : generateOptionalRefCall(key, ref, suffix);
91573
91574
  }
91574
91575
  if (shouldUseArrayRefImport(propSchema, useRefImports)) {
91575
91576
  const items = propSchema.items;
91576
91577
  const ref = items.$ref;
91577
- return isRequired ? generateRequiredArrayRefCall(key, ref) : generateOptionalArrayRefCall(key, ref);
91578
+ return isRequired ? generateRequiredArrayRefCall(key, ref, suffix) : generateOptionalArrayRefCall(key, ref, suffix);
91578
91579
  }
91579
91580
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91580
91581
  const additionalProps = propSchema.additionalProperties;
91581
91582
  const ref = additionalProps.$ref;
91582
- return isRequired ? generateRequiredRecordRefCall(key, ref) : generateOptionalRecordRefCall(key, ref);
91583
+ return isRequired ? generateRequiredRecordRefCall(key, ref, suffix) : generateOptionalRecordRefCall(key, ref, suffix);
91583
91584
  }
91584
91585
  if (!isSchemaObject3(propSchema)) {
91585
91586
  return isRequired ? "undefined" : generateOptionalInlineProperty(key, "undefined");
@@ -91588,23 +91589,23 @@ var generatePropertyValue = (key, propSchema, isRequired, useRefImports) => {
91588
91589
  const valueExpression = generateValidationExpression(key, propSchema, defaultValue, isRequired);
91589
91590
  return isRequired ? valueExpression : generateOptionalInlineProperty(key, valueExpression);
91590
91591
  };
91591
- var generatePropertyEntries = (schema, useRefImports) => {
91592
+ var generatePropertyEntries = (schema, useRefImports, suffix) => {
91592
91593
  if (!hasProperties(schema)) {
91593
91594
  return [];
91594
91595
  }
91595
91596
  const entries8 = [];
91596
91597
  for (const [key, propSchema] of Object.entries(schema.properties)) {
91597
91598
  const isRequired = isPropertyRequired(key, schema);
91598
- const value10 = generatePropertyValue(key, propSchema, isRequired, useRefImports);
91599
+ const value10 = generatePropertyValue(key, propSchema, isRequired, useRefImports, suffix);
91599
91600
  const isOptional = value10.startsWith("...");
91600
91601
  entries8.push({ key, value: value10, isOptional });
91601
91602
  }
91602
91603
  return entries8;
91603
91604
  };
91604
- var generateFallbackValue = (_, propSchema, useRefImports) => {
91605
+ var generateFallbackValue = (_, propSchema, useRefImports, suffix) => {
91605
91606
  if (useRefImports && hasRef(propSchema)) {
91606
91607
  const ref = propSchema.$ref;
91607
- const parserName = generateParserName(refToName(ref));
91608
+ const parserName = generateParserName(refToName(ref, suffix));
91608
91609
  return `${parserName}(undefined)`;
91609
91610
  }
91610
91611
  if (useRefImports && isSchemaObject3(propSchema) && propSchema.type === "array" && hasItems(propSchema) && hasRef(propSchema.items)) {
@@ -91647,7 +91648,7 @@ var generateFallbackValue = (_, propSchema, useRefImports) => {
91647
91648
  return "undefined";
91648
91649
  }
91649
91650
  };
91650
- var generateFallbackObject = (schema, useRefImports, typeName) => {
91651
+ var generateFallbackObject = (schema, useRefImports, typeName, suffix) => {
91651
91652
  if (!hasProperties(schema)) {
91652
91653
  return `{} as ${typeName}`;
91653
91654
  }
@@ -91658,7 +91659,7 @@ var generateFallbackObject = (schema, useRefImports, typeName) => {
91658
91659
  for (const [key, propSchema] of Object.entries(schema.properties)) {
91659
91660
  const isRequired = isPropertyRequired(key, schema);
91660
91661
  if (isRequired) {
91661
- const fallbackValue = generateFallbackValue(key, propSchema, useRefImports);
91662
+ const fallbackValue = generateFallbackValue(key, propSchema, useRefImports, suffix);
91662
91663
  if (fallbackValue === "undefined") {
91663
91664
  return `{} as ${typeName}`;
91664
91665
  }
@@ -91726,11 +91727,11 @@ var toVarName = (key) => {
91726
91727
  return `_${safe}`;
91727
91728
  };
91728
91729
  var shapeValidatorName = (typeName) => `validate${typeName}Shape`;
91729
- var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91730
+ var generatePropertyTypeCheck = (varName, schema, useRefImports, suffix) => {
91730
91731
  if (!isSchemaObject3(schema))
91731
91732
  return null;
91732
91733
  if (useRefImports && hasRef(schema)) {
91733
- const refName = refToName(schema.$ref);
91734
+ const refName = refToName(schema.$ref, suffix);
91734
91735
  return `${shapeValidatorName(refName)}(${varName})`;
91735
91736
  }
91736
91737
  if (hasEnum(schema) || hasOneOf(schema) || hasAnyOf(schema) || hasAllOf(schema) || hasRef(schema) || "not" in schema) {
@@ -91739,7 +91740,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91739
91740
  if (!hasType(schema))
91740
91741
  return null;
91741
91742
  if (useRefImports && schema.type === "array" && hasItems(schema) && isSchemaObject3(schema.items) && hasRef(schema.items)) {
91742
- const refName = refToName(schema.items.$ref);
91743
+ const refName = refToName(schema.items.$ref, suffix);
91743
91744
  return `Array.isArray(${varName}) && ${varName}.every(${shapeValidatorName(refName)})`;
91744
91745
  }
91745
91746
  const checks = [];
@@ -91800,7 +91801,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
91800
91801
  var shouldCacheVariable = (propSchema, _canFastPath, _useRefImports) => {
91801
91802
  return isSchemaObject3(propSchema);
91802
91803
  };
91803
- var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict2) => {
91804
+ var generateObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict2) => {
91804
91805
  const functionName = generateParserName(typeName);
91805
91806
  if (!hasProperties(schema)) {
91806
91807
  if (strict2) {
@@ -91811,7 +91812,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91811
91812
  }
91812
91813
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
91813
91814
  }
91814
- const fallbackObject = generateFallbackObject(schema, useRefImports, typeName);
91815
+ const fallbackObject = generateFallbackObject(schema, useRefImports, typeName, suffix);
91815
91816
  const properties = Object.entries(schema.properties);
91816
91817
  const lines = [];
91817
91818
  lines.push(`export const ${functionName} = (input: unknown): ${typeName} => {`);
@@ -91834,7 +91835,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91834
91835
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91835
91836
  canFastPath = false;
91836
91837
  } else {
91837
- const check7 = generatePropertyTypeCheck(varName, propSchema, useRefImports);
91838
+ const check7 = generatePropertyTypeCheck(varName, propSchema, useRefImports, suffix);
91838
91839
  if (check7 === null) {
91839
91840
  canFastPath = false;
91840
91841
  } else {
@@ -91873,7 +91874,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91873
91874
  if (useRefImports && isSchemaObject3(schema) && hasAllOf(schema)) {
91874
91875
  for (const entry of schema.allOf) {
91875
91876
  if (isSchemaObject3(entry) && hasRef(entry)) {
91876
- const parserName = generateParserName(refToName(entry.$ref));
91877
+ const parserName = generateParserName(refToName(entry.$ref, suffix));
91877
91878
  objectLines.push(` ...(${parserName}(input) as Record<string, unknown>),`);
91878
91879
  }
91879
91880
  }
@@ -91883,7 +91884,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91883
91884
  const accessor = shouldCache ? varName : safeAccessor("input", key);
91884
91885
  if (shouldUseRefImport(propSchema, useRefImports)) {
91885
91886
  const ref = propSchema.$ref;
91886
- const parserName = generateParserName(refToName(ref));
91887
+ const parserName = generateParserName(refToName(ref, suffix));
91887
91888
  if (isRequired) {
91888
91889
  objectLines.push(` ${safeKey2(key)}: ${parserName}(${accessor}),`);
91889
91890
  } else {
@@ -91894,7 +91895,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91894
91895
  if (shouldUseArrayRefImport(propSchema, useRefImports)) {
91895
91896
  const items = propSchema.items;
91896
91897
  const ref = items.$ref;
91897
- const parserName = generateParserName(refToName(ref));
91898
+ const parserName = generateParserName(refToName(ref, suffix));
91898
91899
  if (isRequired) {
91899
91900
  objectLines.push(` ${safeKey2(key)}: validateArray(${accessor}, ${parserName}),`);
91900
91901
  } else {
@@ -91905,7 +91906,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91905
91906
  if (shouldUseRecordRefImport(propSchema, useRefImports)) {
91906
91907
  const additionalProps = propSchema.additionalProperties;
91907
91908
  const ref = additionalProps.$ref;
91908
- const parserName = generateParserName(refToName(ref));
91909
+ const parserName = generateParserName(refToName(ref, suffix));
91909
91910
  if (isRequired) {
91910
91911
  objectLines.push(` ${safeKey2(key)}: validateRecord(${accessor}, ${parserName}),`);
91911
91912
  } else {
@@ -91944,9 +91945,9 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict
91944
91945
  }
91945
91946
  return result;
91946
91947
  };
91947
- var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings, strict2) => {
91948
+ var generateCombinedObjectParser = (schema, typeName, useRefImports, suffix, logWarnings, strict2) => {
91948
91949
  const functionName = generateParserName(typeName);
91949
- const entries8 = generatePropertyEntries(schema, useRefImports);
91950
+ const entries8 = generatePropertyEntries(schema, useRefImports, suffix);
91950
91951
  const propertyLines = entries8.map((entry) => {
91951
91952
  if (entry.isOptional) {
91952
91953
  return ` ${entry.value},`;
@@ -91954,17 +91955,17 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
91954
91955
  return ` ${safeKey2(entry.key)}: ${entry.value},`;
91955
91956
  });
91956
91957
  if (!isSchemaObject3(schema) || !("patternProperties" in schema)) {
91957
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
91958
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
91958
91959
  }
91959
91960
  const patternProps = schema.patternProperties;
91960
91961
  const patterns = Object.entries(patternProps);
91961
91962
  const refPattern = patterns.find(([, ps]) => isSchemaObject3(ps) && hasRef(ps));
91962
91963
  if (!refPattern || !useRefImports) {
91963
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
91964
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
91964
91965
  }
91965
91966
  const [pattern2, patternSchema] = refPattern;
91966
91967
  const ref = patternSchema.$ref;
91967
- const parserName = generateParserName(refToName(ref));
91968
+ const parserName = generateParserName(refToName(ref, suffix));
91968
91969
  const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
91969
91970
  const escapedPattern = escapeRegexPattern2(pattern2);
91970
91971
  const inputSpread = " ...input,";
@@ -91992,7 +91993,7 @@ ${objectProperties}
91992
91993
  return result;
91993
91994
  };`;
91994
91995
  };
91995
- var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, strict2) => {
91996
+ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, suffix, strict2) => {
91996
91997
  const functionName = generateParserName(typeName);
91997
91998
  const additionalProps = schema.additionalProperties;
91998
91999
  if (!additionalProps) {
@@ -92000,7 +92001,7 @@ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, stric
92000
92001
  }
92001
92002
  if (useRefImports && isSchemaObject3(additionalProps) && hasRef(additionalProps)) {
92002
92003
  const ref = additionalProps.$ref;
92003
- const parserName = generateParserName(refToName(ref));
92004
+ const parserName = generateParserName(refToName(ref, suffix));
92004
92005
  if (strict2) {
92005
92006
  return `export const ${functionName} = (input: unknown): ${typeName} => {
92006
92007
  if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
@@ -92050,7 +92051,7 @@ var generateInlineValueParser = (schema) => {
92050
92051
  var escapeRegexPattern2 = (pattern2) => {
92051
92052
  return pattern2.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
92052
92053
  };
92053
- var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict2) => {
92054
+ var generatePatternPropertiesParser = (schema, typeName, useRefImports, suffix, strict2) => {
92054
92055
  const functionName = generateParserName(typeName);
92055
92056
  if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
92056
92057
  return generateEmptyObjectParser(typeName, strict2);
@@ -92064,7 +92065,7 @@ var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict2)
92064
92065
  let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
92065
92066
  if (useRefImports && isSchemaObject3(patternSchema) && hasRef(patternSchema)) {
92066
92067
  const ref = patternSchema.$ref;
92067
- const parserName = generateParserName(refToName(ref));
92068
+ const parserName = generateParserName(refToName(ref, suffix));
92068
92069
  patternAssignment = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
92069
92070
  } else if (patternSchema === false) {
92070
92071
  patternAssignment = "";
@@ -92087,7 +92088,7 @@ ${notObjectBranch}
92087
92088
  return result;
92088
92089
  };`;
92089
92090
  };
92090
- var generateConditionalParser = (schema, typeName) => {
92091
+ var generateConditionalParser = (schema, typeName, suffix) => {
92091
92092
  const functionName = generateParserName(typeName);
92092
92093
  const ifSchema = schema.if;
92093
92094
  const thenSchema = schema.then;
@@ -92097,7 +92098,7 @@ var generateConditionalParser = (schema, typeName) => {
92097
92098
  if (!thenHasRef || !elseHasRef) {
92098
92099
  const mergedSchema = getConditionalObjectSchema2(schema);
92099
92100
  if (mergedSchema) {
92100
- return generateObjectParser(mergedSchema, typeName, false);
92101
+ return generateObjectParser(mergedSchema, typeName, false, suffix);
92101
92102
  }
92102
92103
  return generateEmptyObjectParser(typeName);
92103
92104
  }
@@ -92105,15 +92106,15 @@ var generateConditionalParser = (schema, typeName) => {
92105
92106
  if (!isRefCondition) {
92106
92107
  const mergedSchema = getConditionalObjectSchema2(schema);
92107
92108
  if (mergedSchema) {
92108
- return generateObjectParser(mergedSchema, typeName, false);
92109
+ return generateObjectParser(mergedSchema, typeName, false, suffix);
92109
92110
  }
92110
92111
  return generateEmptyObjectParser(typeName);
92111
92112
  }
92112
92113
  const thenRef = thenSchema.$ref;
92113
92114
  const elseRef = elseSchema.$ref;
92114
- const thenParserName = generateParserName(refToName(thenRef));
92115
- const elseParserName = generateParserName(refToName(elseRef));
92116
- const thenTypeName = refToName(thenRef);
92115
+ const thenParserName = generateParserName(refToName(thenRef, suffix));
92116
+ const elseParserName = generateParserName(refToName(elseRef, suffix));
92117
+ const thenTypeName = refToName(thenRef, suffix);
92117
92118
  return `export const ${functionName} = (input: unknown): ${typeName} | ${thenTypeName} =>
92118
92119
  hasRef(input) ? ${thenParserName}(input) : ${elseParserName}(input)
92119
92120
  `;
@@ -92186,7 +92187,8 @@ var selectParserStrategy = (schema, typeName, options) => {
92186
92187
  const useRefImports = options?.useRefImports ?? false;
92187
92188
  const logWarnings = options?.logWarnings ?? false;
92188
92189
  const strict2 = options?.strict ?? false;
92189
- if (typeName === "SchemaObject") {
92190
+ const suffix = options?.typeSuffix ?? "";
92191
+ if (typeName === `Schema${suffix}`) {
92190
92192
  return generateSchemaObjectParser(typeName);
92191
92193
  }
92192
92194
  const isObjectLikeSchema2 = isObjectSchema2(schema) || isSchemaObject3(schema) && (("patternProperties" in schema) || ("additionalProperties" in schema));
@@ -92194,30 +92196,30 @@ var selectParserStrategy = (schema, typeName, options) => {
92194
92196
  return generateNonObjectParser(typeName, schema, strict2);
92195
92197
  }
92196
92198
  if (hasProperties(schema) && isSchemaObject3(schema) && "patternProperties" in schema) {
92197
- return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
92199
+ return generateCombinedObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
92198
92200
  }
92199
92201
  if (hasProperties(schema)) {
92200
- return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict2);
92202
+ return generateObjectParser(schema, typeName, useRefImports, suffix, logWarnings, strict2);
92201
92203
  }
92202
92204
  if (isSchemaObject3(schema) && "if" in schema && "then" in schema && "else" in schema) {
92203
- return generateConditionalParser(schema, typeName);
92205
+ return generateConditionalParser(schema, typeName, suffix);
92204
92206
  }
92205
92207
  const conditionalObjectSchema = getConditionalObjectSchema2(schema);
92206
92208
  if (conditionalObjectSchema) {
92207
- return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings, strict2);
92209
+ return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, suffix, logWarnings, strict2);
92208
92210
  }
92209
92211
  if (!isObjectLikeSchema2) {
92210
92212
  return generateNonObjectParser(typeName, schema, strict2);
92211
92213
  }
92212
92214
  if ("patternProperties" in schema) {
92213
- return generatePatternPropertiesParser(schema, typeName, useRefImports, strict2);
92215
+ return generatePatternPropertiesParser(schema, typeName, useRefImports, suffix, strict2);
92214
92216
  }
92215
92217
  if ("additionalProperties" in schema && !hasProperties(schema)) {
92216
92218
  const additionalProps = schema.additionalProperties;
92217
92219
  if (additionalProps === true || additionalProps === false) {
92218
92220
  return generateEmptyObjectParser(typeName, strict2);
92219
92221
  }
92220
- return generateAdditionalPropertiesParser(schema, typeName, useRefImports, strict2);
92222
+ return generateAdditionalPropertiesParser(schema, typeName, useRefImports, suffix, strict2);
92221
92223
  }
92222
92224
  if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
92223
92225
  return generateEmptyObjectParser(typeName, strict2);
@@ -92227,7 +92229,7 @@ var selectParserStrategy = (schema, typeName, options) => {
92227
92229
  var generateParserFunction = (schema, typeName, options) => {
92228
92230
  return selectParserStrategy(schema, typeName, options);
92229
92231
  };
92230
- var generateShapeValidator = (schema, typeName, useRefImports) => {
92232
+ var generateShapeValidator = (schema, typeName, useRefImports, suffix = "") => {
92231
92233
  const fnName = shapeValidatorName(typeName);
92232
92234
  const stub = `export const ${fnName} = (_input: unknown): boolean => false;`;
92233
92235
  if (!isSchemaObject3(schema))
@@ -92245,7 +92247,7 @@ var generateShapeValidator = (schema, typeName, useRefImports) => {
92245
92247
  }
92246
92248
  const accessor = safeAccessor("input", key);
92247
92249
  const isRequired = isPropertyRequired(key, schema);
92248
- const check7 = generatePropertyTypeCheck(accessor, propSchema, useRefImports);
92250
+ const check7 = generatePropertyTypeCheck(accessor, propSchema, useRefImports, suffix);
92249
92251
  if (check7 === null) {
92250
92252
  return stub;
92251
92253
  }
@@ -92273,9 +92275,10 @@ var generateFile = (schema, typeName, options) => {
92273
92275
  const selfRef = options?.selfRef;
92274
92276
  const rootSchema = options?.rootSchema;
92275
92277
  const helpersMode = options?.helpersMode ?? "package";
92276
- const typeDefinition = generateTypeDefinition(schema, typeName, { readonly: options?.readonly ?? false });
92278
+ const typeSuffix = options?.typeSuffix ?? "";
92279
+ const typeDefinition = generateTypeDefinition(schema, typeName, { readonly: options?.readonly ?? false, typeSuffix });
92277
92280
  if (typesOnly) {
92278
- const imports2 = collectImports(schema, { typesOnly: true, selfRef, rootSchema });
92281
+ const imports2 = collectImports(schema, { typesOnly: true, selfRef, rootSchema, typeSuffix });
92279
92282
  let result2 = "";
92280
92283
  if (imports2.length > 0) {
92281
92284
  result2 += imports2[0];
@@ -92291,15 +92294,16 @@ var generateFile = (schema, typeName, options) => {
92291
92294
  }
92292
92295
  const parserFunction = generateParserFunction(schema, typeName, {
92293
92296
  useRefImports: true,
92297
+ typeSuffix,
92294
92298
  ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {},
92295
92299
  ...options?.strict !== undefined ? { strict: options.strict } : {}
92296
92300
  });
92297
- const shapeValidator = generateShapeValidator(schema, typeName, true);
92301
+ const shapeValidator = generateShapeValidator(schema, typeName, true, typeSuffix);
92298
92302
  const combinedFunctions = `${shapeValidator}
92299
92303
 
92300
92304
  ${parserFunction}`;
92301
92305
  const helpers = collectHelpers(combinedFunctions, helpersMode, options?.helpersImportPrefix);
92302
- const imports = [...collectImports(schema, { selfRef, rootSchema }), ...helpers.imports];
92306
+ const imports = [...collectImports(schema, { selfRef, rootSchema, typeSuffix }), ...helpers.imports];
92303
92307
  let result = "";
92304
92308
  if (imports.length > 0) {
92305
92309
  result += imports[0];
@@ -92340,7 +92344,7 @@ var extractDynamicAnchorDefs = (schema) => {
92340
92344
  }
92341
92345
  return refs;
92342
92346
  };
92343
- var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict2, helpersMode = "package", helpersImportPrefix = "./", readonly2 = false) => {
92347
+ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict2, helpersMode = "package", helpersImportPrefix = "./", readonly2 = false, typeSuffix = "") => {
92344
92348
  rootSchema = upgradeDraft07Schema(rootSchema);
92345
92349
  const files = [];
92346
92350
  const processedRefs = new Set;
@@ -92356,6 +92360,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92356
92360
  helpersMode,
92357
92361
  helpersImportPrefix,
92358
92362
  readonly: readonly2,
92363
+ typeSuffix,
92359
92364
  ...logWarnings !== undefined ? { logWarnings } : {},
92360
92365
  ...strict2 !== undefined ? { strict: strict2 } : {}
92361
92366
  });
@@ -92382,7 +92387,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92382
92387
  console.warn(`Warning: Could not resolve ref: ${ref}`);
92383
92388
  continue;
92384
92389
  }
92385
- const typeName = refToName(ref);
92390
+ const typeName = refToName(ref, typeSuffix);
92386
92391
  const filename = refToFilename(ref);
92387
92392
  const processedSchema = resolveDynamicRefs(resolvedSchema, dynamicRefMap);
92388
92393
  const extendedSchema = extensions ? applySchemaExtensions(processedSchema, filename, extensions) : processedSchema;
@@ -92393,6 +92398,7 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
92393
92398
  helpersMode,
92394
92399
  helpersImportPrefix,
92395
92400
  readonly: readonly2,
92401
+ typeSuffix,
92396
92402
  ...logWarnings !== undefined ? { logWarnings } : {},
92397
92403
  ...strict2 !== undefined ? { strict: strict2 } : {}
92398
92404
  });
@@ -92581,7 +92587,8 @@ var VALUE_KEYS = new Set([
92581
92587
  "outFile",
92582
92588
  "input",
92583
92589
  "export",
92584
- "helpers"
92590
+ "helpers",
92591
+ "typeSuffix"
92585
92592
  ]);
92586
92593
  var toCamelCase = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
92587
92594
  var parseHelpersValue = (value10) => {
@@ -92607,6 +92614,9 @@ var assignValue = (config3, key, value10) => {
92607
92614
  case "export":
92608
92615
  config3.export = value10;
92609
92616
  return true;
92617
+ case "typeSuffix":
92618
+ config3.typeSuffix = value10;
92619
+ return true;
92610
92620
  case "input": {
92611
92621
  const parsed = parseInputValue(value10);
92612
92622
  if (parsed)
@@ -92678,8 +92688,17 @@ var parseCliArgs = (args2) => {
92678
92688
  return config3;
92679
92689
  };
92680
92690
 
92691
+ // src/read-version.ts
92692
+ import { readFile as readFile3 } from "node:fs/promises";
92693
+ var readVersion = async () => {
92694
+ const pkgUrl = new URL("../package.json", import.meta.url);
92695
+ const pkg = JSON.parse(await readFile3(pkgUrl, "utf-8"));
92696
+ return pkg.version ?? "0.0.0";
92697
+ };
92698
+
92681
92699
  // src/cli.ts
92682
92700
  var execFileAsync = promisify(execFile);
92701
+ var isVersionRequest = (args2) => args2[0] === "version" || args2.includes("--version") || args2.includes("-v");
92683
92702
  var extractConfigPath = (args2) => {
92684
92703
  for (let i = 0;i < args2.length; i++) {
92685
92704
  const arg = args2[i];
@@ -92695,7 +92714,7 @@ var extractConfigPath = (args2) => {
92695
92714
  var loadSchema = async (config3, schemaPath) => {
92696
92715
  const inputFormat = config3.input ?? "json";
92697
92716
  if (inputFormat === "json") {
92698
- return JSON.parse(await readFile3(schemaPath, "utf-8"));
92717
+ return JSON.parse(await readFile4(schemaPath, "utf-8"));
92699
92718
  }
92700
92719
  console.log(`Input format: ${inputFormat}`);
92701
92720
  const source = await loadSchemaModule(schemaPath, config3.export);
@@ -92755,7 +92774,7 @@ var runSingle = async (config3, schemaPath, outputDir) => {
92755
92774
  console.log(`Helpers mode: ${helpersMode}${config3.helpers ? " (explicit)" : " (auto-detected)"}`);
92756
92775
  const rootTypeName = deriveRootTypeName(schema);
92757
92776
  console.log(`Root type: ${rootTypeName}`);
92758
- const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, undefined, config3.readonly);
92777
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, undefined, config3.readonly, config3.typeSuffix);
92759
92778
  for (const file2 of files) {
92760
92779
  await writeGeneratedFile(outputDir, file2.filename, file2.content);
92761
92780
  console.log(`Generated: ${file2.filename}`);
@@ -92773,7 +92792,7 @@ var runSingleFile = async (config3, schemaPath, outFilePath) => {
92773
92792
  await mkdir(outputDir, { recursive: true });
92774
92793
  const rootTypeName = deriveRootTypeName(schema);
92775
92794
  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);
92795
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, config3.helpers ?? "package", undefined, config3.readonly, config3.typeSuffix);
92777
92796
  await writeFile(outFilePath, combineGeneratedFiles(files), "utf-8");
92778
92797
  console.log(`Generated: ${relative(process.cwd(), outFilePath)}`);
92779
92798
  if (config3.build) {
@@ -92808,7 +92827,7 @@ var runRecursive = async (config3, schemaDir, outputDir) => {
92808
92827
  const rootTypeName = deriveRootTypeName(schema);
92809
92828
  console.log(`
92810
92829
  ${relPath} → ${relNoExt}/ (root type: ${rootTypeName})`);
92811
- const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, helpersImportPrefix, config3.readonly);
92830
+ const files = await buildSchema(schema, rootTypeName, undefined, config3.typesOnly, config3.logWarnings, config3.strict, helpersMode, helpersImportPrefix, config3.readonly, config3.typeSuffix);
92812
92831
  for (const file2 of files) {
92813
92832
  if (file2.filename.startsWith("_helpers/")) {
92814
92833
  sharedHelpers.set(file2.filename, file2.content);
@@ -92834,6 +92853,10 @@ Total files generated: ${writtenTsFiles.length}`);
92834
92853
  };
92835
92854
  var run10 = async () => {
92836
92855
  const args2 = process.argv.slice(2);
92856
+ if (isVersionRequest(args2)) {
92857
+ console.log(await readVersion());
92858
+ return;
92859
+ }
92837
92860
  const configPath = extractConfigPath(args2);
92838
92861
  const fileConfig = configPath ? await loadConfig(configPath) : {};
92839
92862
  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.0",
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.3",
46
+ "@amritk/generate-parsers": "0.6.0",
47
47
  "@amritk/generate-markdown": "0.2.1",
48
- "@amritk/helpers": "0.5.0"
48
+ "@amritk/helpers": "0.6.0"
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
+ }