From a47489b19c182d54b14a0c8d78afda5b1e23864a Mon Sep 17 00:00:00 2001 From: Aysajan Eziz Date: Thu, 16 Jul 2026 12:55:47 -0400 Subject: [PATCH 1/2] fix: use output filename in TypeScript usage imports Signed-off-by: Aysajan Eziz --- .../language/JavaScript/JavaScriptRenderer.ts | 10 +++--- .../language/TypeScriptFlow/FlowRenderer.ts | 4 +-- .../TypeScriptFlowBaseRenderer.ts | 4 +-- .../TypeScriptFlow/TypeScriptRenderer.ts | 12 +++++-- test/unit/typescript-output-filename.test.ts | 31 +++++++++++++++++++ 5 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 test/unit/typescript-output-filename.test.ts diff --git a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts index f03e41f27f..bdb064caef 100644 --- a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts +++ b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts @@ -493,15 +493,15 @@ function r(name${stringAnnotation}) { protected emitTypes(): void {} - protected emitUsageImportComment(): void { + protected emitUsageImportComment(_givenOutputFilename: string): void { this.emitLine('// const Convert = require("./file");'); } - protected emitUsageComments(): void { + protected emitUsageComments(givenOutputFilename: string): void { this.emitMultiline(`// To parse this data: //`); - this.emitUsageImportComment(); + this.emitUsageImportComment(givenOutputFilename); this.emitLine("//"); this.forEachTopLevel("none", (_t, name) => { const camelCaseName = modifySource(camelCase, name); @@ -537,11 +537,11 @@ function r(name${stringAnnotation}) { }); } - protected emitSourceStructure(): void { + protected emitSourceStructure(givenOutputFilename: string): void { if (this.leadingComments !== undefined) { this.emitComments(this.leadingComments); } else { - this.emitUsageComments(); + this.emitUsageComments(givenOutputFilename); } this.emitTypes(); diff --git a/packages/quicktype-core/src/language/TypeScriptFlow/FlowRenderer.ts b/packages/quicktype-core/src/language/TypeScriptFlow/FlowRenderer.ts index 76446d9b34..a0a3bfaee2 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow/FlowRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow/FlowRenderer.ts @@ -39,9 +39,9 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { }); } - protected emitSourceStructure(): void { + protected emitSourceStructure(givenOutputFilename: string): void { this.emitLine("// @flow"); this.ensureBlankLine(); - super.emitSourceStructure(); + super.emitSourceStructure(givenOutputFilename); } } diff --git a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptFlowBaseRenderer.ts b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptFlowBaseRenderer.ts index d4c2393d38..d20f56c19c 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptFlowBaseRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptFlowBaseRenderer.ts @@ -191,9 +191,9 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { ); } - protected emitUsageComments(): void { + protected emitUsageComments(givenOutputFilename: string): void { if (this._tsFlowOptions.justTypes) return; - super.emitUsageComments(); + super.emitUsageComments(givenOutputFilename); } protected deserializerFunctionLine(t: Type, name: Name): Sourcelike { diff --git a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts index 43537b1464..f413b05106 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts @@ -50,7 +50,7 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { protected emitModuleExports(): void {} - protected emitUsageImportComment(): void { + protected emitUsageImportComment(givenOutputFilename: string): void { const topLevelNames: Sourcelike[] = []; this.forEachTopLevel( "none", @@ -59,10 +59,18 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { }, isNamedType, ); + const moduleName = + givenOutputFilename === "stdout" + ? "file" + : givenOutputFilename + .replace(/^.*[/\\]/, "") + .replace(/\.[^.]+$/, ""); this.emitLine( "// import { Convert", topLevelNames, - ' } from "./file";', + ' } from "./', + moduleName, + '";', ); } diff --git a/test/unit/typescript-output-filename.test.ts b/test/unit/typescript-output-filename.test.ts new file mode 100644 index 0000000000..1abd877109 --- /dev/null +++ b/test/unit/typescript-output-filename.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from "vitest"; + +import { + InputData, + jsonInputForTargetLanguage, + quicktype, +} from "../../packages/quicktype-core/src/index.js"; + +describe("TypeScript output filename", () => { + test("uses output filename in TypeScript usage imports", async () => { + const jsonInput = jsonInputForTargetLanguage("typescript"); + await jsonInput.addSource({ + name: "ExperimentCounts", + samples: ['{"experiments": 3}'], + }); + + const inputData = new InputData(); + inputData.addInput(jsonInput); + const result = await quicktype({ + inputData, + lang: "typescript", + outputFilename: "src/cli/ExperimentCounts.ts", + }); + const output = result.lines.join("\n"); + + expect(output).toContain( + '// import { Convert, ExperimentCounts } from "./ExperimentCounts";', + ); + expect(output).not.toContain('from "./file"'); + }); +}); From 486bca786277fc37db65978c98f74188cff11493 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Sun, 19 Jul 2026 09:57:36 -0400 Subject: [PATCH 2/2] fix: use output filename in JavaScript and Flow usage comments The previous commit only applied the output-filename-derived module name to the TypeScript usage import comment; JavaScript (and Flow, which inherits it) still emitted require("./file"). Hoist the module-name derivation into a shared usageModuleName helper on JavaScriptRenderer, use it in both the JavaScript require comment and the TypeScript import comment, and extend the unit test to cover JavaScript, Flow, and the stdout fallback. Co-Authored-By: Claude Fable 5 --- .../language/JavaScript/JavaScriptRenderer.ts | 16 ++++- .../TypeScriptFlow/TypeScriptRenderer.ts | 8 +-- test/unit/typescript-output-filename.test.ts | 69 +++++++++++++++---- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts index bdb064caef..0adcaefd30 100644 --- a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts +++ b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts @@ -493,8 +493,20 @@ function r(name${stringAnnotation}) { protected emitTypes(): void {} - protected emitUsageImportComment(_givenOutputFilename: string): void { - this.emitLine('// const Convert = require("./file");'); + protected usageModuleName(givenOutputFilename: string): string { + return givenOutputFilename === "stdout" + ? "file" + : givenOutputFilename + .replace(/^.*[/\\]/, "") + .replace(/\.[^.]+$/, ""); + } + + protected emitUsageImportComment(givenOutputFilename: string): void { + this.emitLine( + '// const Convert = require("./', + this.usageModuleName(givenOutputFilename), + '");', + ); } protected emitUsageComments(givenOutputFilename: string): void { diff --git a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts index f413b05106..376036727d 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts @@ -59,17 +59,11 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { }, isNamedType, ); - const moduleName = - givenOutputFilename === "stdout" - ? "file" - : givenOutputFilename - .replace(/^.*[/\\]/, "") - .replace(/\.[^.]+$/, ""); this.emitLine( "// import { Convert", topLevelNames, ' } from "./', - moduleName, + this.usageModuleName(givenOutputFilename), '";', ); } diff --git a/test/unit/typescript-output-filename.test.ts b/test/unit/typescript-output-filename.test.ts index 1abd877109..dfa789ea51 100644 --- a/test/unit/typescript-output-filename.test.ts +++ b/test/unit/typescript-output-filename.test.ts @@ -6,26 +6,65 @@ import { quicktype, } from "../../packages/quicktype-core/src/index.js"; -describe("TypeScript output filename", () => { +async function generate( + lang: string, + outputFilename?: string, +): Promise { + const jsonInput = jsonInputForTargetLanguage(lang); + await jsonInput.addSource({ + name: "ExperimentCounts", + samples: ['{"experiments": 3}'], + }); + + const inputData = new InputData(); + inputData.addInput(jsonInput); + const result = await quicktype({ + inputData, + lang, + outputFilename, + }); + return result.lines.join("\n"); +} + +describe("output filename in usage comments", () => { test("uses output filename in TypeScript usage imports", async () => { - const jsonInput = jsonInputForTargetLanguage("typescript"); - await jsonInput.addSource({ - name: "ExperimentCounts", - samples: ['{"experiments": 3}'], - }); - - const inputData = new InputData(); - inputData.addInput(jsonInput); - const result = await quicktype({ - inputData, - lang: "typescript", - outputFilename: "src/cli/ExperimentCounts.ts", - }); - const output = result.lines.join("\n"); + const output = await generate( + "typescript", + "src/cli/ExperimentCounts.ts", + ); expect(output).toContain( '// import { Convert, ExperimentCounts } from "./ExperimentCounts";', ); expect(output).not.toContain('from "./file"'); }); + + test("uses output filename in JavaScript usage require", async () => { + const output = await generate( + "javascript", + "src/cli/ExperimentCounts.js", + ); + + expect(output).toContain( + '// const Convert = require("./ExperimentCounts");', + ); + expect(output).not.toContain('require("./file")'); + }); + + test("uses output filename in Flow usage require", async () => { + const output = await generate("flow", "ExperimentCounts.js"); + + expect(output).toContain( + '// const Convert = require("./ExperimentCounts");', + ); + expect(output).not.toContain('require("./file")'); + }); + + test('falls back to "file" when writing to stdout', async () => { + const tsOutput = await generate("typescript"); + expect(tsOutput).toContain(' } from "./file";'); + + const jsOutput = await generate("javascript"); + expect(jsOutput).toContain('// const Convert = require("./file");'); + }); });