Skip to content

Adds the option of generating real TS enums instead of string unions #2854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 9, 2023
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
5 changes: 3 additions & 2 deletions packages/rtk-query-codegen-openapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"prepare": "npm run build && chmod +x ./lib/bin/cli.js",
"format": "prettier --write \"src/**/*.ts\"",
"test:update": "lib/bin/cli.js test/fixtures/petstore.json --file test/fixtures/generated.ts -h",
"test:update:enum": "lib/bin/cli.js test/config.example.enum.ts",
"test": "jest --runInBand",
"cli": "esr src/bin/cli.ts"
},
Expand Down Expand Up @@ -56,12 +57,12 @@
},
"dependencies": {
"@apidevtools/swagger-parser": "^10.0.2",
"@rtk-query/oazapfts-patched": "^3.6.0-2",
"commander": "^6.2.0",
"oazapfts": "^4.2.0",
"prettier": "^2.2.1",
"semver": "^7.3.5",
"swagger2openapi": "^7.0.4",
"typescript": ">=4.1 <=4.5"
"typescript": "^4.9.3"
},
"husky": {
"hooks": {
Expand Down
9 changes: 1 addition & 8 deletions packages/rtk-query-codegen-openapi/src/bin/cli.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
#!/usr/bin/env node

import program from 'commander';
import { dirname, resolve } from 'path';
import { generateEndpoints, parseConfig } from '../';
import semver from 'semver';
import { version as tsVersion } from 'typescript';

if (!semver.satisfies(tsVersion, '>=4.1 <=4.5')) {
console.warn(
'Please note that `@rtk-query/codegen-openapi` only has been tested with TS versions 4.1 to 4.5 - other versions might cause problems.'
);
}
import program from 'commander';

let ts = false;
try {
Expand Down
66 changes: 26 additions & 40 deletions packages/rtk-query-codegen-openapi/src/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ts from 'typescript';
import { factory } from './utils/factory';
import ts from 'typescript';

const defaultEndpointBuilder = factory.createIdentifier('build');

Expand All @@ -12,7 +12,6 @@ export function generateObjectProperties(obj: ObjectPropertyDefinitions) {

export function generateImportNode(pkg: string, namedImports: Record<string, string>, defaultImportName?: string) {
return factory.createImportDeclaration(
undefined,
undefined,
factory.createImportClause(
false,
Expand Down Expand Up @@ -44,17 +43,7 @@ export function generateCreateApiCall({
endpoints: factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
endpointBuilder,
undefined,
undefined,
undefined
),
],
[factory.createParameterDeclaration(undefined, undefined, endpointBuilder, undefined, undefined, undefined)],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createParenthesizedExpression(endpointDefinitions)
Expand All @@ -67,30 +56,32 @@ export function generateCreateApiCall({
const enhanceEndpointsObjectLiteralExpression = factory.createObjectLiteralExpression(
[factory.createShorthandPropertyAssignment(factory.createIdentifier('addTagTypes'), undefined)],
true
)
);
return factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[factory.createVariableDeclaration(
factory.createIdentifier("injectedRtkApi"),
undefined,
undefined,
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("api"),
factory.createIdentifier("enhanceEndpoints")
[
factory.createVariableDeclaration(
factory.createIdentifier('injectedRtkApi'),
undefined,
undefined,
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('api'),
factory.createIdentifier('enhanceEndpoints')
),
undefined,
[enhanceEndpointsObjectLiteralExpression]
),
undefined,
[enhanceEndpointsObjectLiteralExpression]
factory.createIdentifier('injectEndpoints')
),
factory.createIdentifier("injectEndpoints")
),
undefined,
[injectEndpointsObjectLiteralExpression]
)
)],
undefined,
[injectEndpointsObjectLiteralExpression]
)
),
],
ts.NodeFlags.Const
)
);
Expand Down Expand Up @@ -145,21 +136,16 @@ export function generateEndpointDefinition({
factory.createIdentifier(type === 'query' ? 'providesTags' : 'invalidatesTags'),
factory.createArrayLiteralExpression(tags.map((tag) => factory.createStringLiteral(tag), false))
)
)
);
}
return factory.createPropertyAssignment(
factory.createIdentifier(operationName),

factory.createCallExpression(
factory.createPropertyAccessExpression(endpointBuilder, factory.createIdentifier(type)),
[Response, QueryArg],
[
factory.createObjectLiteralExpression(
objectProperties,
true
),
]
),
[factory.createObjectLiteralExpression(objectProperties, true)]
)
);
}

Expand Down
53 changes: 21 additions & 32 deletions packages/rtk-query-codegen-openapi/src/generate.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import ts from 'typescript';
import * as path from 'path';
import { camelCase } from 'lodash';

import ApiGenerator, {
getOperationName as _getOperationName,
getReferenceName,
isReference,
supportDeepObjects,
} from '@rtk-query/oazapfts-patched/lib/codegen/generate';
} from 'oazapfts/lib/codegen/generate';
import type { EndpointMatcher, EndpointOverrides, GenerationOptions, OperationDefinition, TextMatcher } from './types';
import { capitalize, getOperationDefinitions, getV3Doc, removeUndefined, isQuery as testIsQuery } from './utils';
import {
createQuestionToken,
keywordType,
createPropertyAssignment,
createQuestionToken,
isValidIdentifier,
} from '@rtk-query/oazapfts-patched/lib/codegen/tscodegen';
import type { OpenAPIV3 } from 'openapi-types';
import { generateReactHooks } from './generators/react-hooks';
import type { EndpointMatcher, EndpointOverrides, GenerationOptions, OperationDefinition, TextMatcher } from './types';
import { capitalize, getOperationDefinitions, getV3Doc, isQuery as testIsQuery, removeUndefined } from './utils';
import { generateTagTypes } from './codegen';
keywordType,
} from 'oazapfts/lib/codegen/tscodegen';
import { generateCreateApiCall, generateEndpointDefinition, generateImportNode, generateTagTypes } from './codegen';

import type { ObjectPropertyDefinitions } from './codegen';
import { generateCreateApiCall, generateEndpointDefinition, generateImportNode } from './codegen';
import type { OpenAPIV3 } from 'openapi-types';
import { camelCase } from 'lodash';
import { factory } from './utils/factory';
import { generateReactHooks } from './generators/react-hooks';
import ts from 'typescript';

const generatedApiName = 'injectedRtkApi';

Expand Down Expand Up @@ -92,12 +93,14 @@ export async function generateApi(
endpointOverrides,
unionUndefined,
flattenArg = false,
useEnumType = false,
}: GenerationOptions
) {
const v3Doc = await getV3Doc(spec);

const apiGen = new ApiGenerator(v3Doc, {
unionUndefined,
useEnumType,
});

const operationDefinitions = getOperationDefinitions(v3Doc).filter(operationMatches(filterEndpoints));
Expand Down Expand Up @@ -131,7 +134,7 @@ export async function generateApi(
}
apiFile = apiFile.replace(/\.[jt]sx?$/, '');

const sourceCode = printer.printNode(
return printer.printNode(
ts.EmitHint.Unspecified,
factory.createSourceFile(
[
Expand All @@ -150,7 +153,6 @@ export async function generateApi(
),
}),
factory.createExportDeclaration(
undefined,
undefined,
false,
factory.createNamedExports([
Expand All @@ -162,7 +164,8 @@ export async function generateApi(
undefined
),
...Object.values(interfaces),
...apiGen['aliases'],
...apiGen.aliases,
...apiGen.enumAliases,
...(hooks
? [
generateReactHooks({
Expand All @@ -180,8 +183,6 @@ export async function generateApi(
resultFile
);

return sourceCode;

function extractAllTagTypes({ operationDefinitions }: { operationDefinitions: OperationDefinition[] }) {
let allTagTypes = new Set<string>();

Expand Down Expand Up @@ -242,7 +243,6 @@ export async function generateApi(
const ResponseTypeName = factory.createTypeReferenceNode(
registerInterface(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
capitalize(operationName + responseSuffix),
undefined,
Expand All @@ -259,7 +259,7 @@ export async function generateApi(
const allNames = parameters.map((p) => p.name);
const queryArg: QueryArgDefinitions = {};
for (const param of parameters) {
const isPureSnakeCase = /^[a-zA-Z][a-zA-Z0-9_]*$/.test(param.name);
const isPureSnakeCase = /^[a-zA-Z][\\w]*$/.test(param.name);
const camelCaseName = camelCase(param.name);

const name = isPureSnakeCase && !allNames.includes(camelCaseName) ? camelCaseName : param.name;
Expand Down Expand Up @@ -309,7 +309,6 @@ export async function generateApi(
const QueryArg = factory.createTypeReferenceNode(
registerInterface(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
capitalize(operationName + argSuffix),
undefined,
Expand Down Expand Up @@ -391,17 +390,7 @@ export async function generateApi(
undefined,
undefined,
Object.keys(queryArg).length
? [
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
rootObject,
undefined,
undefined,
undefined
),
]
? [factory.createParameterDeclaration(undefined, undefined, rootObject, undefined, undefined, undefined)]
: [],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
Expand Down Expand Up @@ -461,7 +450,7 @@ function generatePathExpression(
) {
const expressions: Array<[string, string]> = [];

const head = path.replace(/\{(.*?)\}(.*?)(?=\{|$)/g, (_, expression, literal) => {
const head = path.replace(/\{(.*?)}(.*?)(?=\{|$)/g, (_, expression, literal) => {
const param = pathParameters.find((p) => p.originalName === expression);
if (!param) {
throw new Error(`path parameter ${expression} does not seem to be defined in '${path}'!`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ts from 'typescript';
import { getOperationName } from '@rtk-query/oazapfts-patched/lib/codegen/generate';
import { getOperationName } from 'oazapfts/lib/codegen/generate';
import { capitalize, isQuery } from '../utils';
import type { OperationDefinition, EndpointOverrides, ConfigFile } from '../types';
import { getOverrides } from '../generate';
Expand Down
4 changes: 2 additions & 2 deletions packages/rtk-query-codegen-openapi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export function parseConfig(fullConfig: ConfigFile) {
}

/**
* Enforces `@rtk-query/oazapfts-patched` to use the same TypeScript version as this module itself uses.
* Enforces `oazapfts` to use the same TypeScript version as this module itself uses.
* That should prevent enums from running out of sync if both libraries use different TS versions.
*/
function enforceOazapftsTsVersion<T>(cb: () => T): T {
const ozTsPath = require.resolve('typescript', { paths: [require.resolve('@rtk-query/oazapfts-patched')] });
const ozTsPath = require.resolve('typescript', { paths: [require.resolve('oazapfts')] });
const tsPath = require.resolve('typescript');
const originalEntry = require.cache[ozTsPath];
try {
Expand Down
5 changes: 5 additions & 0 deletions packages/rtk-query-codegen-openapi/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ export interface OutputFileOptions extends Partial<CommonOptions> {
outputFile: string;
filterEndpoints?: EndpointMatcher;
endpointOverrides?: EndpointOverrides[];
/**
* defaults to false
* If passed as true it will generate TS enums instead of union of strings
*/
useEnumType?: boolean;
}

export interface EndpointOverrides {
Expand Down
10 changes: 10 additions & 0 deletions packages/rtk-query-codegen-openapi/test/config.example.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ConfigFile } from '@rtk-query/codegen-openapi';

const config: ConfigFile = {
schemaFile: './fixtures/petstore.yaml',
apiFile: './fixtures/emptyApi.ts',
outputFile: './tmp/example.ts',
useEnumType: true,
};

export default config;
Loading