Skip to content

Commit

Permalink
Merge pull request #286 from hey-api/chore/more-compiler-api-improvem…
Browse files Browse the repository at this point in the history
…ents
  • Loading branch information
jordanshatford authored Apr 8, 2024
2 parents ae5c3f3 + b03f57a commit b9f4847
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 233 deletions.
4 changes: 0 additions & 4 deletions packages/openapi-ts/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export function handlebarsPlugin(): Plugin {
camelCase: true,
dataDestructure: true,
dataParameters: true,
enumKey: true,
enumName: true,
enumUnionType: true,
enumValue: true,
equals: true,
escapeComment: true,
escapeDescription: true,
Expand Down
3 changes: 3 additions & 0 deletions packages/openapi-ts/src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export const compiler = {
import: {
named: module.createNamedImportDeclarations,
},
typedef: {
alias: types.createTypeAliasDeclaration,
},
types: {
array: types.createArrayType,
object: types.createObjectType,
Expand Down
20 changes: 19 additions & 1 deletion packages/openapi-ts/src/compiler/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ts from 'typescript';

import { isType, ots } from './utils';
import { addLeadingJSDocComment, isType, ots } from './utils';

/**
* Convert an unknown value to an expression.
Expand Down Expand Up @@ -62,3 +62,21 @@ export const createObjectType = <T extends object>(obj: T, multiLine: boolean =
.filter(isType<ts.PropertyAssignment>),
multiLine
);

export const createTypeAliasDeclaration = (
name: string,
type: string,
typeParameters: string[] = [],
comments?: Parameters<typeof addLeadingJSDocComment>[1]
) => {
const node = ts.factory.createTypeAliasDeclaration(
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
ts.factory.createIdentifier(name),
typeParameters.map(p => ts.factory.createTypeParameterDeclaration(undefined, p, undefined, undefined)),
ts.factory.createTypeReferenceNode(type)
);
if (comments?.length) {
addLeadingJSDocComment(node, comments);
}
return node;
};
20 changes: 20 additions & 0 deletions packages/openapi-ts/src/compiler/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,23 @@ export const ots = {
};

export const isType = <T>(value: T | undefined): value is T => value !== undefined;

export const addLeadingJSDocComment = (
node: ts.Node | undefined,
text: Array<string | null | false | undefined>,
hasTrailingNewLine: boolean = true
): string => {
// if node is falsy, assume string mode
if (node) {
ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
['*', ...text, ' '].filter(Boolean).join('\n'),
hasTrailingNewLine
);
return '';
}

const result = ['/**', ...text, ' */'].filter(Boolean).join('\n');
return hasTrailingNewLine ? `${result}\n` : result;
};
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export async function createClient(userConfig: UserConfig): Promise<Client> {
: (config.input as unknown as Awaited<ReturnType<typeof getOpenApiSpec>>);

const client = postProcessClient(parse(openApi, config));
const templates = registerHandlebarTemplates(config, client);
const templates = registerHandlebarTemplates(config);

if (config.write) {
logClientMessage(config.client);
Expand Down
1 change: 0 additions & 1 deletion packages/openapi-ts/src/templates/partials/isNullable.hbs

This file was deleted.

1 change: 0 additions & 1 deletion packages/openapi-ts/src/templates/partials/isReadOnly.hbs

This file was deleted.

102 changes: 40 additions & 62 deletions packages/openapi-ts/src/utils/__tests__/handlebars.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,30 @@ import { registerHandlebarHelpers, registerHandlebarTemplates } from '../handleb

describe('registerHandlebarHelpers', () => {
it('should register the helpers', () => {
registerHandlebarHelpers(
{
client: 'fetch',
debug: false,
enums: 'javascript',
experimental: false,
exportCore: true,
exportModels: true,
exportSchemas: true,
exportServices: true,
format: true,
input: '',
lint: false,
operationId: true,
output: '',
postfixServices: '',
serviceResponse: 'body',
useDateType: false,
useOptions: false,
write: true,
},
{
enumNames: [],
models: [],
server: '',
services: [],
version: '',
}
);
registerHandlebarHelpers({
client: 'fetch',
debug: false,
enums: 'javascript',
experimental: false,
exportCore: true,
exportModels: true,
exportSchemas: true,
exportServices: true,
format: true,
input: '',
lint: false,
operationId: true,
output: '',
postfixServices: '',
serviceResponse: 'body',
useDateType: false,
useOptions: false,
write: true,
});
const helpers = Object.keys(Handlebars.helpers);
expect(helpers).toContain('camelCase');
expect(helpers).toContain('dataDestructure');
expect(helpers).toContain('dataParameters');
expect(helpers).toContain('enumKey');
expect(helpers).toContain('enumName');
expect(helpers).toContain('enumUnionType');
expect(helpers).toContain('enumValue');
expect(helpers).toContain('equals');
expect(helpers).toContain('escapeComment');
expect(helpers).toContain('escapeDescription');
Expand All @@ -55,35 +42,26 @@ describe('registerHandlebarHelpers', () => {

describe('registerHandlebarTemplates', () => {
it('should return correct templates', () => {
const templates = registerHandlebarTemplates(
{
client: 'fetch',
debug: false,
enums: 'javascript',
experimental: false,
exportCore: true,
exportModels: true,
exportSchemas: true,
exportServices: true,
format: true,
input: '',
lint: false,
operationId: true,
output: '',
postfixServices: '',
serviceResponse: 'body',
useDateType: false,
useOptions: false,
write: true,
},
{
enumNames: [],
models: [],
server: '',
services: [],
version: '',
}
);
const templates = registerHandlebarTemplates({
client: 'fetch',
debug: false,
enums: 'javascript',
experimental: false,
exportCore: true,
exportModels: true,
exportSchemas: true,
exportServices: true,
format: true,
input: '',
lint: false,
operationId: true,
output: '',
postfixServices: '',
serviceResponse: 'body',
useDateType: false,
useOptions: false,
write: true,
});
expect(templates.exports.service).toBeDefined();
expect(templates.core.settings).toBeDefined();
expect(templates.core.apiError).toBeDefined();
Expand Down
77 changes: 3 additions & 74 deletions packages/openapi-ts/src/utils/handlebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,13 @@ import xhrGetResponseHeader from '../templates/core/xhr/getResponseHeader.hbs';
import xhrRequest from '../templates/core/xhr/request.hbs';
import xhrSendRequest from '../templates/core/xhr/sendRequest.hbs';
import templateExportService from '../templates/exportService.hbs';
import partialIsNullable from '../templates/partials/isNullable.hbs';
import partialIsReadOnly from '../templates/partials/isReadOnly.hbs';
import partialOperationParameters from '../templates/partials/operationParameters.hbs';
import partialOperationResult from '../templates/partials/operationResult.hbs';
import partialOperationTypes from '../templates/partials/operationTypes.hbs';
import partialRequestConfig from '../templates/partials/requestConfig.hbs';
import type { Client } from '../types/client';
import type { Config } from '../types/config';
import { enumKey, enumName, enumUnionType, enumValue } from './enum';
import { escapeComment, escapeDescription, escapeName } from './escape';
import { getDefaultPrintable, modelIsRequired } from './required';
import { sortByName } from './sort';
import { toType } from './write/type';

const dataDestructure = (config: Config, operation: Operation) => {
Expand Down Expand Up @@ -140,62 +135,7 @@ const nameOperationDataType = (service: Service, operation: Service['operations'
return `${namespace}['${key}']`;
};

export const operationDataType = (config: Config, service: Service) => {
const operationsWithParameters = service.operations.filter(operation => operation.parameters.length);
if (!config.useOptions || !operationsWithParameters.length) {
return '';
}
const namespace = `${camelCase(service.name, { pascalCase: true })}Data`;
const output = `export type ${namespace} = {
${operationsWithParameters
.map(
operation => `${camelCase(operation.name, { pascalCase: true })}: {
${sortByName(operation.parameters)
.filter(parameter => {
if (!config.experimental) {
return true;
}
return parameter.in !== 'query';
})
.map(parameter => {
let comment: string[] = [];
if (parameter.description) {
comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */'];
}
return [
...comment,
`${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`,
].join('\n');
})
.join('\n')}
${
config.experimental
? `
query${operation.parametersQuery.every(parameter => !parameter.isRequired) ? '?' : ''}: {
${sortByName(operation.parametersQuery)
.map(parameter => {
let comment: string[] = [];
if (parameter.description) {
comment = ['/**', ` * ${escapeComment(parameter.description)}`, ' */'];
}
return [
...comment,
`${parameter.name + modelIsRequired(config, parameter)}: ${toType(parameter, config)}`,
].join('\n');
})
.join('\n')}
}
`
: ''
}
};`
)
.join('\n')}
}`;
return output;
};

export const registerHandlebarHelpers = (config: Config, client: Client): void => {
export const registerHandlebarHelpers = (config: Config): void => {
Handlebars.registerHelper('camelCase', camelCase);

Handlebars.registerHelper('dataDestructure', function (operation: Operation) {
Expand All @@ -206,15 +146,6 @@ export const registerHandlebarHelpers = (config: Config, client: Client): void =
return dataParameters(config, parameters);
});

Handlebars.registerHelper('enumKey', enumKey);

Handlebars.registerHelper('enumName', function (name: string | undefined) {
return enumName(config, client, name);
});

Handlebars.registerHelper('enumUnionType', enumUnionType);
Handlebars.registerHelper('enumValue', enumValue);

Handlebars.registerHelper(
'equals',
function (this: unknown, a: string, b: string, options: Handlebars.HelperOptions) {
Expand Down Expand Up @@ -295,8 +226,8 @@ export interface Templates {
* Read all the Handlebar templates that we need and return a wrapper object
* so we can easily access the templates in our generator/write functions.
*/
export const registerHandlebarTemplates = (config: Config, client: Client): Templates => {
registerHandlebarHelpers(config, client);
export const registerHandlebarTemplates = (config: Config): Templates => {
registerHandlebarHelpers(config);

// Main templates (entry points for the files we write to disk)
const templates: Templates = {
Expand All @@ -318,8 +249,6 @@ export const registerHandlebarTemplates = (config: Config, client: Client): Temp
};

// Partials for the generations of the models, services, etc.
Handlebars.registerPartial('isNullable', Handlebars.template(partialIsNullable));
Handlebars.registerPartial('isReadOnly', Handlebars.template(partialIsReadOnly));
Handlebars.registerPartial('operationParameters', Handlebars.template(partialOperationParameters));
Handlebars.registerPartial('operationResult', Handlebars.template(partialOperationResult));
Handlebars.registerPartial('operationTypes', Handlebars.template(partialOperationTypes));
Expand Down
Loading

0 comments on commit b9f4847

Please sign in to comment.