@amritk/generate-parsers 0.12.3 → 0.14.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.
@@ -2,11 +2,12 @@ import { refToFilename } from '@amritk/helpers/ref-to-filename';
2
2
  import { refToName } from '@amritk/helpers/ref-to-name';
3
3
  import { resolveRef } from '@amritk/helpers/resolve-ref';
4
4
  import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasItems, hasOneOf, hasRef } from '@amritk/helpers/schema-guards';
5
- // Emit an explicit `.js` extension on the relative specifier. Node's ESM resolver
6
- // requires it (extensionless relative imports only work under a bundler or Bun),
7
- // and `./x.js` pointing at a sibling `x.ts` is the standard TS NodeNext form —
8
- // accepted by Bun, esbuild, webpack, and tsc alike.
9
- const getImportPathForFilename = (filename) => `./${filename}.js`;
5
+ // Emit an explicit extension on the relative specifier. Node's ESM resolver
6
+ // requires one (extensionless relative imports only work under a bundler or
7
+ // Bun). `./x.js` pointing at a sibling `x.ts` is the standard TS NodeNext form
8
+ // — accepted by Bun, esbuild, webpack, and tsc alike — while `./x.ts` is the
9
+ // literal on-disk path Node's type stripping needs to run the sources directly.
10
+ const getImportPathForFilename = (filename, ext) => `./${filename}.${ext}`;
10
11
  /**
11
12
  * Collects all import statements needed for a schema by finding $ref references
12
13
  * that are directly used in the type definition (from properties, items, etc.).
@@ -48,6 +49,32 @@ const getImportPathForFilename = (filename) => `./${filename}.js`;
48
49
  */
49
50
  export const collectImports = (schema, options) => {
50
51
  const typesOnly = options?.typesOnly === true;
52
+ const importExt = options?.importExt ?? 'js';
53
+ const importMap = new Map();
54
+ for (const [filename, typeName] of collectImportTargets(schema, options)) {
55
+ const importPath = getImportPathForFilename(filename, importExt);
56
+ // In types-only mode, omit the parser function import since there is no parser to call
57
+ const importStatement = typesOnly
58
+ ? `import type { ${typeName} } from '${importPath}';`
59
+ : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
60
+ importMap.set(filename, importStatement);
61
+ }
62
+ return Array.from(importMap.values()).sort();
63
+ };
64
+ /**
65
+ * The type names a generated file will import for its `$ref`s — the same
66
+ * dedup/skip rules as {@link collectImports}. Generators seed their private
67
+ * sub-type naming with this set so a synthesized name (e.g. a root array's
68
+ * `FooItem`) can never shadow an imported identifier.
69
+ */
70
+ export const collectImportTypeNames = (schema, options) => {
71
+ const names = new Set();
72
+ for (const [, typeName] of collectImportTargets(schema, options))
73
+ names.add(typeName);
74
+ return names;
75
+ };
76
+ /** Shared `$ref` walk: filename → derived type name for every import target. */
77
+ const collectImportTargets = (schema, options) => {
51
78
  const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined;
52
79
  const rootSchema = options?.rootSchema;
53
80
  const typeSuffix = options?.typeSuffix;
@@ -194,8 +221,8 @@ export const collectImports = (schema, options) => {
194
221
  if (typeof schema === 'object' && schema !== null && 'else' in schema) {
195
222
  collectRefsFromValue(schema.else);
196
223
  }
197
- // Convert refs to combined import statements, deduplicating by filename
198
- const importMap = new Map();
224
+ // Convert refs to import targets, deduplicating by filename.
225
+ const targets = new Map();
199
226
  for (const ref of refs) {
200
227
  // For URI refs, skip if the base URI is not resolvable in the root schema.
201
228
  // This prevents generating imports for external schemas that were never generated as files.
@@ -209,12 +236,7 @@ export const collectImports = (schema, options) => {
209
236
  if (selfFilename !== undefined && filename === selfFilename) {
210
237
  continue;
211
238
  }
212
- const importPath = getImportPathForFilename(filename);
213
- // In types-only mode, omit the parser function import since there is no parser to call
214
- const importStatement = typesOnly
215
- ? `import type { ${typeName} } from '${importPath}';`
216
- : `import { type ${typeName}, parse${typeName}, validate${typeName}Shape } from '${importPath}';`;
217
- importMap.set(filename, importStatement);
239
+ targets.set(filename, typeName);
218
240
  }
219
- return Array.from(importMap.values()).sort();
241
+ return targets;
220
242
  };
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { buildSchema, type GeneratedFile } from './generators/build-schema.js';
2
+ export type { ImportExtension } from './helpers/collect-imports.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/generate-parsers",
3
- "version": "0.12.3",
3
+ "version": "0.14.0",
4
4
  "description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "json-schema-typed": "^8.0.1",
51
- "@amritk/helpers": "0.10.2",
51
+ "@amritk/helpers": "0.11.0",
52
52
  "@amritk/generate-markdown": "0.4.1"
53
53
  },
54
54
  "devDependencies": {