From 006c26f516ea9e49fd1c34301078adfb3f387e08 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 18:10:44 -0400 Subject: [PATCH] fix(cli): honor --src-lang for directory JSON/URL inputs (#1179) Directory-mode input classification decided JSON vs. JSON Schema purely from file extension (.json/.url -> JSON data, .schema -> JSON Schema), completely ignoring an explicit --src-lang schema. This made `quicktype --src-lang schema ./schemas -l swift` parse schema files named *.json as JSON data samples, generating bogus structs/enums from the schema's own keys (`$schema`, `type`, `pattern`) instead of typing the samples the schema describes. Route .json/.url files found in a directory through the same typeSourcesForURIs() used for single-file/URL sources, so --src-lang is honored consistently. .schema/.gqlschema/.graphql handling is unchanged. Co-Authored-By: gpt-5.6-sol via pi --- src/index.ts | 23 ++++++-------- test/unit/directory-input.test.ts | 52 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 test/unit/directory-input.test.ts diff --git a/src/index.ts b/src/index.ts index 6ae510e25b..aaa4c96183 100644 --- a/src/index.ts +++ b/src/index.ts @@ -126,7 +126,7 @@ function typeNameFromFilename(filename: string): string { async function samplesFromDirectory( dataDir: string, - httpHeaders?: string[], + options: CLIOptions, ): Promise { async function readFilesOrURLsInDirectory( d: string, @@ -152,13 +152,9 @@ async function samplesFromDirectory( } if (file.endsWith(".url") || file.endsWith(".json")) { - sourcesInDir.push({ - kind: "json", - name, - samples: [ - await readableFromFileOrURL(fileOrUrl, httpHeaders), - ], - }); + sourcesInDir.push( + ...(await typeSourcesForURIs(name, [fileOrUrl], options)), + ); } else if (file.endsWith(".schema")) { sourcesInDir.push({ kind: "schema", @@ -175,7 +171,7 @@ async function samplesFromDirectory( ); graphQLSchema = await readableFromFileOrURL( fileOrUrl, - httpHeaders, + options.httpHeader, ); graphQLSchemaFileName = fileOrUrl; } else if (file.endsWith(".graphql")) { @@ -184,7 +180,10 @@ async function samplesFromDirectory( name, schema: undefined, query: await getStream( - await readableFromFileOrURL(fileOrUrl, httpHeaders), + await readableFromFileOrURL( + fileOrUrl, + options.httpHeader, + ), ), }); } @@ -875,9 +874,7 @@ async function getSources(options: CLIOptions): Promise { const directories = exists.filter((x) => fs.lstatSync(x).isDirectory()); for (const dataDir of directories) { - sources = sources.concat( - await samplesFromDirectory(dataDir, options.httpHeader), - ); + sources = sources.concat(await samplesFromDirectory(dataDir, options)); } // Every src that's not a directory is assumed to be a file or URL diff --git a/test/unit/directory-input.test.ts b/test/unit/directory-input.test.ts new file mode 100644 index 0000000000..32770e600b --- /dev/null +++ b/test/unit/directory-input.test.ts @@ -0,0 +1,52 @@ +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; + +import { expect, test } from "vitest"; + +import { main as quicktype } from "../../src"; + +test("honors schema source language for JSON files in a directory", async () => { + const temporaryDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), "quicktype-directory-"), + ); + const schemaDirectory = path.join(temporaryDirectory, "schemas"); + const outputPath = path.join(temporaryDirectory, "out.ts"); + fs.mkdirSync(schemaDirectory); + + fs.writeFileSync( + path.join(schemaDirectory, "Name.json"), + JSON.stringify({ + $schema: "http://json-schema.org/draft-07/schema#", + type: "string", + pattern: "^[a-zA-Z0-9]+$", + }), + ); + fs.writeFileSync( + path.join(schemaDirectory, "AnotherName.json"), + JSON.stringify({ + $schema: "http://json-schema.org/draft-07/schema#", + type: "string", + pattern: "^\\d+$", + }), + ); + + try { + await quicktype({ + srcLang: "schema", + src: [schemaDirectory], + lang: "typescript", + out: outputPath, + quiet: true, + telemetry: "disable", + }); + const output = fs.readFileSync(outputPath, "utf8"); + + expect(output).toContain("type AnotherName = string;"); + expect(output).toContain("type Name = string;"); + expect(output).not.toContain("$schema"); + expect(output).not.toContain("export interface Name"); + } finally { + fs.rmSync(temporaryDirectory, { recursive: true, force: true }); + } +});