Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
private readonly _options: OptionValues<typeof newtonsoftCSharpOptions>,
) {
super(targetLanguage, renderContext, _options);
this._needHelpers = _options.features.helpers;
this._needAttributes = _options.features.attributes;
// `--just-types` wins over whatever `--features` says.
this._needHelpers = _options.features.helpers && !_options.justTypes;
this._needAttributes =
_options.features.attributes && !_options.justTypes;
this._needNamespaces = _options.features.namespaces;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
>,
) {
super(targetLanguage, renderContext, _options);
this._needHelpers = _options.features.helpers;
this._needAttributes = _options.features.attributes;
// `--just-types` wins over whatever `--features` says.
this._needHelpers = _options.features.helpers && !_options.justTypes;
this._needAttributes =
_options.features.attributes && !_options.justTypes;
this._needNamespaces = _options.features.namespaces;
}

Expand Down
11 changes: 1 addition & 10 deletions packages/quicktype-core/src/language/CSharp/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,10 @@ export const cSharpOptions = {
helpers: false,
attributes: true,
},
"just-types-and-namespace": {
namespaces: true,
helpers: false,
attributes: false,
},
"just-types": {
namespaces: true,
helpers: false,
attributes: false,
},
} as const,
"complete",
),
justTypes: new BooleanOption("just-types", "Plain types only", false),
baseclass: new EnumOption(
"base-class",
"Base class",
Expand Down
10 changes: 7 additions & 3 deletions packages/quicktype-core/src/language/Kotlin/language.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ConvenienceRenderer } from "../../ConvenienceRenderer.js";
import type { RenderContext } from "../../Renderer.js";
import {
BooleanOption,
EnumOption,
StringOption,
getOptionValues,
Expand All @@ -16,11 +17,11 @@ import { KotlinRenderer } from "./KotlinRenderer.js";
import { KotlinXRenderer } from "./KotlinXRenderer.js";

export const kotlinOptions = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
framework: new EnumOption(
"framework",
"Serialization framework",
{
"just-types": "None",
jackson: "Jackson",
klaxon: "Klaxon",
kotlinx: "KotlinX",
Expand Down Expand Up @@ -62,9 +63,12 @@ export class KotlinTargetLanguage extends TargetLanguage<
): ConvenienceRenderer {
const options = getOptionValues(kotlinOptions, untypedOptionValues);

// `--just-types` wins over whatever `--framework` says.
if (options.justTypes) {
return new KotlinRenderer(this, renderContext, options);
}

switch (options.framework) {
case "None":
return new KotlinRenderer(this, renderContext, options);
case "Jackson":
return new KotlinJacksonRenderer(this, renderContext, options);
case "Klaxon":
Expand Down
12 changes: 8 additions & 4 deletions packages/quicktype-core/src/language/Scala3/language.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ConvenienceRenderer } from "../../ConvenienceRenderer.js";
import type { RenderContext } from "../../Renderer.js";
import {
BooleanOption,
EnumOption,
StringOption,
getOptionValues,
Expand All @@ -14,15 +15,15 @@ import { Scala3Renderer } from "./Scala3Renderer.js";
import { UpickleRenderer } from "./UpickleRenderer.js";

export const scala3Options = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
framework: new EnumOption(
"framework",
"Serialization framework",
{
"just-types": "None",
circe: "Circe",
upickle: "Upickle",
} as const,
"just-types",
"circe",
),
packageName: new StringOption("package", "Package", "PACKAGE", "quicktype"),
};
Expand Down Expand Up @@ -58,9 +59,12 @@ export class Scala3TargetLanguage extends TargetLanguage<
): ConvenienceRenderer {
const options = getOptionValues(scala3Options, untypedOptionValues);

// `--just-types` wins over whatever `--framework` says.
if (options.justTypes) {
return new Scala3Renderer(this, renderContext, options);
}

switch (options.framework) {
case "None":
return new Scala3Renderer(this, renderContext, options);
case "Upickle":
return new UpickleRenderer(this, renderContext, options);
case "Circe":
Expand Down
30 changes: 9 additions & 21 deletions packages/quicktype-core/src/language/Smithy4s/language.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import type { ConvenienceRenderer } from "../../ConvenienceRenderer.js";
import type { RenderContext } from "../../Renderer.js";
import {
EnumOption,
BooleanOption,
StringOption,
getOptionValues,
} from "../../RendererOptions/index.js";
import { assertNever } from "../../support/Support.js";
import { TargetLanguage } from "../../TargetLanguage.js";
import type { LanguageName, RendererOptions } from "../../types.js";

import { Smithy4sRenderer } from "./Smithy4sRenderer.js";

export enum Framework {
None = "None",
}

export const smithyOptions = {
// FIXME: why does this exist
framework: new EnumOption(
"framework",
"Serialization framework",
{ "just-types": Framework.None } as const,
"just-types",
),
// Plain types is the only mode Smithy supports; the flag is accepted
// for consistency with the other languages.
justTypes: new BooleanOption("just-types", "Plain types only", false),
packageName: new StringOption("package", "Package", "PACKAGE", "quicktype"),
};

Expand Down Expand Up @@ -55,13 +46,10 @@ export class SmithyTargetLanguage extends TargetLanguage<
renderContext: RenderContext,
untypedOptionValues: RendererOptions<Lang>,
): ConvenienceRenderer {
const options = getOptionValues(smithyOptions, untypedOptionValues);

switch (options.framework) {
case Framework.None:
return new Smithy4sRenderer(this, renderContext, options);
default:
return assertNever(options.framework);
}
return new Smithy4sRenderer(
this,
renderContext,
getOptionValues(smithyOptions, untypedOptionValues),
);
}
}
19 changes: 6 additions & 13 deletions packages/quicktype-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,12 @@ async function runQuicktype(
vscode.workspace.getConfiguration(configurationSection);
const justTypes = forceJustTypes || configuration.justTypes;

const rendererOptions: RendererOptions = {};
if (justTypes) {
// FIXME: The target language should have a property to return these options.
if (lang.name === "csharp") {
(rendererOptions as RendererOptions<"csharp">).features =
"just-types";
} else if (lang.name === "kotlin") {
(rendererOptions as RendererOptions<"kotlin">).framework =
"just-types";
} else if ("just-types" in rendererOptions) {
rendererOptions["just-types"] = "true";
}
}
// Not every language has a `just-types` option — JSON Schema, for
// example, has no serialization helpers to leave out.
const rendererOptions: RendererOptions =
justTypes && lang.optionDefinitions.some((o) => o.name === "just-types")
? ({ "just-types": "true" } as RendererOptions)
: {};

const inputData = new InputData();
switch (kind) {
Expand Down
2 changes: 1 addition & 1 deletion test/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ I havea no idea how to encode these tests correctly.
],
skipSchema: [],
skipMiscJSON: false,
rendererOptions: { framework: "just-types" },
rendererOptions: { "just-types": "true" },
quickTestRendererOptions: [],
sourceFiles: ["src/language/Smithy4s/index.ts"],
};
Expand Down
138 changes: 138 additions & 0 deletions test/unit/just-types-option.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Every quicktype language spells "generate plain types without
// (de)serialization helpers" as the boolean `just-types` option. C#'s
// `features=just-types` / `features=just-types-and-namespace` and Kotlin's,
// Scala 3's, and Smithy4s's `framework=just-types` are gone; the boolean
// wins over a conflicting explicit enum value.
import { describe, expect, test } from "vitest";

import {
InputData,
type LanguageName,
type RendererOptions,
jsonInputForTargetLanguage,
quicktype,
} from "quicktype-core";

async function linesFor(
lang: LanguageName,
rendererOptions: RendererOptions = {},
): Promise<string> {
const jsonInput = jsonInputForTargetLanguage(lang);
await jsonInput.addSource({
name: "Person",
samples: ['{"name": "Alice", "age": 37}'],
});
const inputData = new InputData();
inputData.addInput(jsonInput);
const result = await quicktype({ inputData, lang, rendererOptions });
return result.lines.join("\n");
}

describe("just-types generates plain types in every language", () => {
test("C#: no attributes, no helpers, but a namespace", async () => {
const output = await linesFor("csharp", { "just-types": true });
expect(output).toContain("namespace QuickType");
expect(output).toContain("public partial class Person");
expect(output).not.toContain("JsonConverter");
expect(output).not.toContain("JsonProperty");
});

test("Kotlin: plain data classes, no Klaxon", async () => {
const output = await linesFor("kotlin", { "just-types": true });
expect(output).toContain("data class Person");
expect(output).not.toContain("Klaxon");
});

test("Scala 3: plain case classes, no circe", async () => {
const output = await linesFor("scala3", { "just-types": true });
expect(output).toContain("case class Person");
expect(output).not.toContain("circe");
});

test("Smithy: accepted (plain types is the only mode)", async () => {
const viaBoolean = await linesFor("smithy4a", { "just-types": true });
expect(viaBoolean).toContain("structure Person");
expect(viaBoolean).toEqual(await linesFor("smithy4a"));
});
});

describe("the removed enum spellings are errors", () => {
// The old spellings aren't valid `RendererOptions` anymore, which is
// the point: they must also fail for API callers who evade the types.
test("C#: features=just-types", async () => {
const options = { features: "just-types" } as RendererOptions;
await expect(linesFor("csharp", options)).rejects.toThrow(
"Unknown value just-types for option features",
);
});

test("C#: features=just-types-and-namespace", async () => {
const options = {
features: "just-types-and-namespace",
} as RendererOptions;
await expect(linesFor("csharp", options)).rejects.toThrow(
"Unknown value just-types-and-namespace for option features",
);
});

test("Kotlin: framework=just-types", async () => {
const options = { framework: "just-types" } as RendererOptions;
await expect(linesFor("kotlin", options)).rejects.toThrow(
"Unknown value just-types for option framework",
);
});

test("Scala 3: framework=just-types", async () => {
const options = { framework: "just-types" } as RendererOptions;
await expect(linesFor("scala3", options)).rejects.toThrow(
"Unknown value just-types for option framework",
);
});
});

describe("just-types wins over a conflicting enum option", () => {
test("C#: just-types beats features=attributes-only", async () => {
const output = await linesFor("csharp", {
"just-types": true,
features: "attributes-only",
});
expect(output).toEqual(
await linesFor("csharp", { "just-types": true }),
);
expect(output).not.toContain("JsonProperty");
});

test("Kotlin: just-types beats framework=jackson", async () => {
const output = await linesFor("kotlin", {
"just-types": true,
framework: "jackson",
});
expect(output).toEqual(
await linesFor("kotlin", { "just-types": true }),
);
expect(output).not.toContain("jackson");
});

test("Scala 3: just-types beats framework=upickle", async () => {
const output = await linesFor("scala3", {
"just-types": true,
framework: "upickle",
});
expect(output).toEqual(
await linesFor("scala3", { "just-types": true }),
);
expect(output).not.toContain("upickle");
});
});

describe("framework defaults", () => {
test("Scala 3 defaults to circe", async () => {
const output = await linesFor("scala3");
expect(output).toContain("io.circe");
});

test("Kotlin defaults to Klaxon", async () => {
const output = await linesFor("kotlin");
expect(output).toContain("com.beust.klaxon");
});
});
4 changes: 2 additions & 2 deletions test/unit/renderer-options.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ describe("rendererOptions typing", () => {
test("rejects another language's option name", () => {
void quicktype({
inputData,
lang: "kotlin",
// @ts-expect-error `just-types` is a C#/TypeScript spelling; Kotlin has no such option
lang: "rust",
// @ts-expect-error Rust has no `just-types` option
rendererOptions: { "just-types": "true" },
});
});
Expand Down
Loading