Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
51bf050
refactor(CodeGenerator): remove unused typescript import and simplify…
Himenon May 1, 2026
7bd98ab
refactor(TsGenerator): simplify code generation to use string arrays …
Himenon May 1, 2026
3656f01
refactor(factory): delete all individual TypeScript AST node factory …
Himenon May 1, 2026
d78e7f5
refactor: replace typescript AST types with string return types acros…
Himenon May 1, 2026
04f3144
refactor: replace TypeScript AST types with string return types acros…
Himenon May 1, 2026
845c902
test: update snapshots to include generator version v1.1.0
Himenon May 1, 2026
4e66438
test(argo-rollout-test): update snapshot with formatting and version …
Himenon May 1, 2026
504d3d6
chore(package.json): remove typescript peer dependency
Himenon May 1, 2026
cdbd6d5
test(PathParameter): add explicit vitest imports
Himenon May 1, 2026
f298063
chore(.dependency-cruiser.cjs): expand test file exclusion patterns t…
Himenon May 1, 2026
2f56deb
fix: escape double quotes in generated string literals and enum values
Himenon May 1, 2026
1f5212b
fix: snapshot差分をzeroにする修正 1
Himenon May 1, 2026
f309629
feat: change tsc scope
Himenon May 2, 2026
43f03a8
feat: update snapshot
Himenon May 2, 2026
95fe90a
feat: update readme
Himenon May 2, 2026
8cd378a
refactor: index.ts to factory.ts
Himenon May 2, 2026
1e2705b
refactor: remove ast api I/F
Himenon May 2, 2026
ccdeaac
test: add test code
Himenon May 2, 2026
38e9e8b
Merge branch 'main' of https://github.com/Himenon/openapi-typescript-…
Himenon May 4, 2026
8621632
fix: confilict
Himenon May 4, 2026
c83989c
feat: remove factory directory
Himenon May 5, 2026
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
2 changes: 1 addition & 1 deletion .dependency-cruiser.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ module.exports = {
"from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration",
from: {
path: "^(src|app|lib)",
pathNot: "\\.spec\\.(js|ts|ls|coffee|litcoffee|coffee\\.md)$",
pathNot: "(\\.spec\\.(js|ts|ls|coffee|litcoffee|coffee\\.md)$|__tests__|[-.]test\\.ts$)",
},
to: {
dependencyTypes: ["npm-dev"],
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
- name: Test & Build
run: |
pnpm build
pnpm type-check
pnpm test:code:gen
pnpm test
env:
Expand Down
2 changes: 1 addition & 1 deletion examples/readme-sample/ast-code-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const generator: Types.CodeGenerator.GenerateFunction<Option> = (
return payload.map(params => {
return factory.InterfaceDeclaration.create({
export: true,
name: params.functionName,
name: params.convertedParams.functionName,
members: [],
});
});
Expand Down
1 change: 1 addition & 0 deletions examples/readme-sample/generator-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";
import type * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

/** ここにCode Templateの定義を記述してください */
// @ts-ignore -- placeholder example, generator is intentionally omitted

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove

const customGenerator: Types.CodeGenerator.CustomGenerator<{}> = {
/** .... */
};
Expand Down
2 changes: 1 addition & 1 deletion examples/readme-sample/use-extract-schema-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type Option = {};

const generator: Types.CodeGenerator.GenerateFunction<Option> = (payload: Types.CodeGenerator.Params[]): string[] => {
return payload.map(params => {
return `function ${params.operationId}() { console.log("${params.comment}") }`;
return `function ${params.operationId}() { console.log("${params.operationParams.comment}") }`;
});
};

Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"test:snapshot": "vitest run --config ./vitest.snapshot.config.ts",
"test:vitest": "vitest run --config ./vitest.config.ts --coverage",
"ts": "node --no-warnings=ExperimentalWarning --experimental-specifier-resolution=node --loader ts-node/esm",
"type-check": "tsc --noEmit",
"update:snapshot": "vitest run --config ./vitest.snapshot.config.ts --update",
"validate": "pnpm ts ./scripts/validate.ts",
"watch": "pnpm ts ./scripts/watch.ts"
Expand Down Expand Up @@ -118,9 +119,6 @@
"typescript": "6.0.3",
"vitest": "^4.1.5"
},
"peerDependencies": {
"typescript": ">=5"
},
"packageManager": "pnpm@10.33.2",
"engines": {
"node": ">=22.0.0"
Expand Down
4 changes: 2 additions & 2 deletions scripts/tools/shell.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ExecaChildProcess, execa } from "execa";
import { type ResultPromise, execa } from "execa";

export const shell = (command: string, cwd: string = process.cwd()): ExecaChildProcess<string> => {
export const shell = (command: string, cwd: string = process.cwd()): ResultPromise => {
return execa(command, {
stdio: ["pipe", "pipe", "inherit"],
shell: true,
Expand Down
10 changes: 4 additions & 6 deletions src/code-templates/_shared/ApiClientArgument.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../api";
import type { CodeGenerator } from "../../types";

Expand Down Expand Up @@ -52,7 +50,7 @@ export const createResponseContentTypeReference = (factory: TsGenerator.Factory.
};

const createHeaders = (factory: TsGenerator.Factory.Type, { convertedParams }: CodeGenerator.Params) => {
const members = [];
const members: string[] = [];

if (convertedParams.has2OrMoreRequestContentTypes) {
members.push(
Expand Down Expand Up @@ -90,10 +88,10 @@ const createHeaders = (factory: TsGenerator.Factory.Type, { convertedParams }: C
* requestBody: {requestBodyName}[T];
* }
*/
export const create = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params): ts.InterfaceDeclaration | undefined => {
export const create = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params): string | undefined => {
const { convertedParams } = params;
const typeParameters: ts.TypeParameterDeclaration[] = [];
const members: ts.TypeElement[] = [];
const typeParameters: string[] = [];
const members: string[] = [];
if (convertedParams.hasRequestBody && convertedParams.has2OrMoreRequestContentTypes) {
typeParameters.push(
factory.TypeParameterDeclaration.create({
Expand Down
13 changes: 3 additions & 10 deletions src/code-templates/_shared/ApiClientInterface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../api";
import type { CodeGenerator } from "../../types";
import type { MethodType } from "./MethodBody/types";
Expand All @@ -12,7 +10,7 @@ const createErrorResponsesTypeAlias = (typeName: string, factory: TsGenerator.Fa
return factory.TypeAliasDeclaration.create({
export: true,
name: typeName,
type: ts.factory.createToken(ts.SyntaxKind.VoidKeyword),
type: "void",
});
}
return factory.TypeAliasDeclaration.create({
Expand All @@ -33,7 +31,7 @@ const createSuccessResponseTypeAlias = (typeName: string, factory: TsGenerator.F
return factory.TypeAliasDeclaration.create({
export: true,
name: typeName,
type: ts.factory.createToken(ts.SyntaxKind.VoidKeyword),
type: "void",
});
}
return factory.TypeAliasDeclaration.create({
Expand Down Expand Up @@ -156,12 +154,7 @@ const createEncodingInterface = (factory: TsGenerator.Factory.Type) => {
});
};

export const create = (
factory: TsGenerator.Factory.Type,
list: CodeGenerator.Params[],
methodType: MethodType,
option: Option,
): ts.Statement[] => {
export const create = (factory: TsGenerator.Factory.Type, list: CodeGenerator.Params[], methodType: MethodType, option: Option): string[] => {
const objectLikeOrAnyType = factory.UnionTypeNode.create({
typeNodes: [
factory.TypeReferenceNode.create({
Expand Down
6 changes: 2 additions & 4 deletions src/code-templates/_shared/MethodBody/CallRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../../api";
import type { CodeGenerator } from "../../../types";
import * as Utils from "../utils";
Expand All @@ -18,7 +16,7 @@ export interface Params {
* "application/x-www-form-urlencoded": {},
* }
*/
const createEncodingParams = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params): ts.Expression | undefined => {
const createEncodingParams = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params): string | undefined => {
const content = params.operationParams.requestBody?.content;
if (!content) {
return;
Expand All @@ -36,7 +34,7 @@ const createEncodingParams = (factory: TsGenerator.Factory.Type, params: CodeGen
/**
* this.apiClient.request("GET", url, requestBody, headers, queryParameters);
*/
export const create = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params, methodType: MethodType): ts.CallExpression => {
export const create = (factory: TsGenerator.Factory.Type, params: CodeGenerator.Params, methodType: MethodType): string => {
const { convertedParams } = params;
const apiClientVariableIdentifier: Record<MethodType, string> = {
class: "this.apiClient.request",
Expand Down
4 changes: 1 addition & 3 deletions src/code-templates/_shared/MethodBody/HeaderParameter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../../api";
import * as Utils from "../utils";

Expand All @@ -8,7 +6,7 @@ export interface Params {
object: Utils.LiteralExpressionObject;
}

export const create = (factory: TsGenerator.Factory.Type, params: Params): ts.VariableStatement => {
export const create = (factory: TsGenerator.Factory.Type, params: Params): string => {
return factory.VariableStatement.create({
declarationList: factory.VariableDeclarationList.create({
flag: "const",
Expand Down
13 changes: 4 additions & 9 deletions src/code-templates/_shared/MethodBody/PathParameter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../../api";
import type { CodeGenerator } from "../../../types";
import { escapeText2 as escapeText } from "../../../utils";
Expand All @@ -16,8 +14,8 @@ export const isPathParameter = (params: any): params is CodeGenerator.PickedPara
const generateUrlVariableStatement = (
factory: TsGenerator.Factory.Type,
urlTemplate: Utils.Params$TemplateExpression,
variableExpression: ts.Expression,
): ts.VariableStatement => {
variableExpression: string,
): string => {
return factory.VariableStatement.create({
declarationList: factory.VariableDeclarationList.create({
declarations: [
Expand All @@ -38,10 +36,7 @@ const generateUrlVariableStatement = (
/**
* const uri = `[head]${params.parameter.[parameterName]}`;
*/
const generateUriVariableStatement = (
factory: TsGenerator.Factory.Type,
urlTemplate: Utils.Params$TemplateExpression,
): ts.VariableStatement => {
const generateUriVariableStatement = (factory: TsGenerator.Factory.Type, urlTemplate: Utils.Params$TemplateExpression): string => {
return factory.VariableStatement.create({
declarationList: factory.VariableDeclarationList.create({
declarations: [
Expand Down Expand Up @@ -134,7 +129,7 @@ export const create = (
requestUri: string,
pathParameters: CodeGenerator.PickedParameter[],
methodType: MethodType,
): ts.VariableStatement => {
): string => {
const urlTemplate: Utils.Params$TemplateExpression =
pathParameters.length > 0 ? generateUrlTemplateExpression(factory, requestUri, pathParameters) : [{ type: "string", value: requestUri }];
if (methodType === "currying-function") {
Expand Down
10 changes: 4 additions & 6 deletions src/code-templates/_shared/MethodBody/QueryParameter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import ts from "typescript";

import type { TsGenerator } from "../../../api";
import * as Utils from "../../../utils";
import * as UtilsExtra from "../utils";
Expand All @@ -18,9 +16,9 @@ export interface Params {
};
}

export const create = (factory: TsGenerator.Factory.Type, params: Params): ts.VariableStatement => {
const properties: ts.PropertyAssignment[] = Object.entries(params.object).reduce<ts.PropertyAssignment[]>((previous, [key, item]) => {
const childProperties: ts.PropertyAssignment[] = [
export const create = (factory: TsGenerator.Factory.Type, params: Params): string => {
const properties: string[] = Object.entries(params.object).reduce<string[]>((previous, [key, item]) => {
const childProperties: string[] = [
factory.PropertyAssignment.create({
name: "value",
initializer:
Expand All @@ -40,7 +38,7 @@ export const create = (factory: TsGenerator.Factory.Type, params: Params): ts.Va
childProperties.push(
factory.PropertyAssignment.create({
name: "explode",
initializer: item.explode ? ts.factory.createTrue() : ts.factory.createFalse(),
initializer: item.explode ? "true" : "false",
}),
);
const childObjectInitializer = factory.ObjectLiteralExpression.create({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,134 +1,89 @@
import ts from "typescript";
import { describe, expect, test } from "vitest";

import { TsGenerator } from "../../../../api";
import type { CodeGenerator } from "../../../../types";
import * as Utils from "../../utils";
import * as PathParameter from "../PathParameter";

const EOL = "\n";

const traverse =
(expression: ts.Expression) =>
<T extends ts.Node>(context: Pick<ts.TransformationContext, "factory">) =>
(rootNode: T) => {
const visit = (node: ts.Node): ts.Node => {
if (!ts.isSourceFile(node)) {
return node;
}
return context.factory.updateSourceFile(
node,
[ts.factory.createExpressionStatement(expression)],
node.isDeclarationFile,
node.referencedFiles,
node.typeReferenceDirectives,
node.hasNoDefaultLib,
node.libReferenceDirectives,
);
};
return ts.visitNode(rootNode, visit);
};

const getText = (expression: ts.Expression) => {
const source = ts.createSourceFile("", "", ts.ScriptTarget.ESNext);
const result = ts.transform(source, [traverse(expression)]);
result.dispose();

const printer = ts.createPrinter(); // AST -> TypeScriptに変換
return printer.printFile(result.transformed[0] as any);
};

describe("PathParameter Test", () => {
const factory = TsGenerator.Factory.create();
const generate = (url: string, pathParameter: CodeGenerator.PickedParameter[]): string => {
const urlTemplates = PathParameter.generateUrlTemplateExpression(factory, url, pathParameter);
const expression = Utils.generateTemplateExpression(factory, urlTemplates);
return getText(expression);
return Utils.generateTemplateExpression(factory, urlTemplates);
};
test("generateUrlTemplateExpression", () => {
expect(generate("/{a}", [{ in: "path", name: "a", required: true }])).toEqual(`\`/\${encodeURIComponent(params.parameter.a)}\`;${EOL}`);
expect(generate("/{a}/", [{ in: "path", name: "a", required: true }])).toEqual(`\`/\${encodeURIComponent(params.parameter.a)}/\`;${EOL}`);
expect(generate("/a/{b}", [{ in: "path", name: "b", required: true }])).toEqual(`\`/a/\${encodeURIComponent(params.parameter.b)}\`;${EOL}`);
expect(generate("/a/{b}/", [{ in: "path", name: "b", required: true }])).toEqual(
`\`/a/\${encodeURIComponent(params.parameter.b)}/\`;${EOL}`,
);
expect(generate("/a/{b}/c", [{ in: "path", name: "b", required: true }])).toEqual(
`\`/a/\${encodeURIComponent(params.parameter.b)}/c\`;${EOL}`,
);
expect(generate("/a/{b}/c/", [{ in: "path", name: "b", required: true }])).toEqual(
`\`/a/\${encodeURIComponent(params.parameter.b)}/c/\`;${EOL}`,
);
expect(generate("/a/b/{c}", [{ in: "path", name: "c", required: true }])).toEqual(
`\`/a/b/\${encodeURIComponent(params.parameter.c)}\`;${EOL}`,
);
expect(generate("/a/b/{c}", [{ in: "path", name: "c", required: true }])).toEqual(
`\`/a/b/\${encodeURIComponent(params.parameter.c)}\`;${EOL}`,
);
expect(generate("/a/b/{c}/", [{ in: "path", name: "c", required: true }])).toEqual(
`\`/a/b/\${encodeURIComponent(params.parameter.c)}/\`;${EOL}`,
);
expect(generate("/{a}", [{ in: "path", name: "a", required: true }])).toEqual(`\`/\${encodeURIComponent(params.parameter.a)}\``);
expect(generate("/{a}/", [{ in: "path", name: "a", required: true }])).toEqual(`\`/\${encodeURIComponent(params.parameter.a)}/\``);
expect(generate("/a/{b}", [{ in: "path", name: "b", required: true }])).toEqual(`\`/a/\${encodeURIComponent(params.parameter.b)}\``);
expect(generate("/a/{b}/", [{ in: "path", name: "b", required: true }])).toEqual(`\`/a/\${encodeURIComponent(params.parameter.b)}/\``);
expect(generate("/a/{b}/c", [{ in: "path", name: "b", required: true }])).toEqual(`\`/a/\${encodeURIComponent(params.parameter.b)}/c\``);
expect(generate("/a/{b}/c/", [{ in: "path", name: "b", required: true }])).toEqual(`\`/a/\${encodeURIComponent(params.parameter.b)}/c/\``);
expect(generate("/a/b/{c}", [{ in: "path", name: "c", required: true }])).toEqual(`\`/a/b/\${encodeURIComponent(params.parameter.c)}\``);
expect(generate("/a/b/{c}", [{ in: "path", name: "c", required: true }])).toEqual(`\`/a/b/\${encodeURIComponent(params.parameter.c)}\``);
expect(generate("/a/b/{c}/", [{ in: "path", name: "c", required: true }])).toEqual(`\`/a/b/\${encodeURIComponent(params.parameter.c)}/\``);
expect(generate("/a/b/{c}.json", [{ in: "path", name: "c", required: true }])).toEqual(
`\`/a/b/\${encodeURIComponent(params.parameter.c)}.json\`;${EOL}`,
`\`/a/b/\${encodeURIComponent(params.parameter.c)}.json\``,
);
expect(generate("/{a}.json/{a}.json/{a}.json", [{ in: "path", name: "a", required: true }])).toEqual(
`\`/\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json\`;${EOL}`,
`\`/\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json\``,
);
expect(generate("/.json.{a}.json/{a}.json.{a}", [{ in: "path", name: "a", required: true }])).toEqual(
`\`/.json.\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json.\${encodeURIComponent(params.parameter.a)}\`;${EOL}`,
`\`/.json.\${encodeURIComponent(params.parameter.a)}.json/\${encodeURIComponent(params.parameter.a)}.json.\${encodeURIComponent(params.parameter.a)}\``,
);

expect(
generate("/{a}/{b}", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "b", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}\``);
expect(
generate("/{a}/{b}/", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "b", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/\``);
expect(
generate("/{a}/{b}/c", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "b", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/c\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/c\``);
expect(
generate("/{a}/{b}/c/", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "b", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/c/\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/\${encodeURIComponent(params.parameter.b)}/c/\``);
expect(
generate("/{a}/b/{c}", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "c", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/b/\${encodeURIComponent(params.parameter.c)}\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/b/\${encodeURIComponent(params.parameter.c)}\``);
expect(
generate("/{a}/b/{c}/", [
{ in: "path", name: "a", required: true },
{ in: "path", name: "c", required: true },
]),
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/b/\${encodeURIComponent(params.parameter.c)}/\`;${EOL}`);
).toBe(`\`/\${encodeURIComponent(params.parameter.a)}/b/\${encodeURIComponent(params.parameter.c)}/\``);
expect(
generate("/a/{b}/{c}", [
{ in: "path", name: "b", required: true },
{ in: "path", name: "c", required: true },
]),
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}/\${encodeURIComponent(params.parameter.c)}\`;${EOL}`);
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}/\${encodeURIComponent(params.parameter.c)}\``);
expect(
generate("/a/{b}/{c}/", [
{ in: "path", name: "b", required: true },
{ in: "path", name: "c", required: true },
]),
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}/\${encodeURIComponent(params.parameter.c)}/\`;${EOL}`);
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}/\${encodeURIComponent(params.parameter.c)}/\``);
expect(
generate("/a/{b}...{c}/", [
{ in: "path", name: "b", required: true },
{ in: "path", name: "c", required: true },
]),
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}...\${encodeURIComponent(params.parameter.c)}/\`;${EOL}`);
).toBe(`\`/a/\${encodeURIComponent(params.parameter.b)}...\${encodeURIComponent(params.parameter.c)}/\``);
});
});
Loading
Loading