Skip to content

Commit

Permalink
chore(client): use compiler api to generate service imports
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanshatford committed Apr 6, 2024
1 parent c4b345b commit 4e2ff2b
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 58 deletions.
7 changes: 5 additions & 2 deletions packages/openapi-ts/src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import * as types from './types';
import { tsNodeToString } from './utils';

export class TypeScriptFile extends Array<ts.Node> {
public override toString(seperator: string = '\n') {
return this.map(v => tsNodeToString(v)).join(seperator);
}

public write(file: PathOrFileDescriptor, seperator: string = '\n') {
const items = this.map(i => tsNodeToString(i));
writeFileSync(file, items.join(seperator));
writeFileSync(file, this.toString(seperator));
}
}

Expand Down
18 changes: 13 additions & 5 deletions packages/openapi-ts/src/compiler/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ots } from './utils';
export const createExportAllDeclaration = (module: string) =>
ts.factory.createExportDeclaration(undefined, false, undefined, ots.string(module));

type ImportItem = { name: string; isTypeOnly?: boolean } | string;
type ImportItem = { name: string; isTypeOnly?: boolean; alias?: string } | string;

/**
* Create a named export declaration. Example: `export { X } from './y'`.
Expand All @@ -29,8 +29,12 @@ export const createNamedExportDeclarations = (
isAllTypes,
ts.factory.createNamedExports(
items.map(item => {
const { name, isTypeOnly = undefined } = typeof item === 'string' ? { name: item } : item;
return ots.export(name, isAllTypes ? false : Boolean(isTypeOnly));
const {
name,
isTypeOnly = undefined,
alias = undefined,
} = typeof item === 'string' ? { name: item } : item;
return ots.export(name, isAllTypes ? false : Boolean(isTypeOnly), alias);
})
),
ots.string(module)
Expand Down Expand Up @@ -78,8 +82,12 @@ export const createNamedImportDeclarations = (
undefined,
ts.factory.createNamedImports(
items.map(item => {
const { name, isTypeOnly = undefined } = typeof item === 'string' ? { name: item } : item;
return ots.import(name, isAllTypes ? false : Boolean(isTypeOnly));
const {
name,
isTypeOnly = undefined,
alias = undefined,
} = typeof item === 'string' ? { name: item } : item;
return ots.import(name, isAllTypes ? false : Boolean(isTypeOnly), alias);
})
)
),
Expand Down
24 changes: 14 additions & 10 deletions packages/openapi-ts/src/compiler/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,22 @@ export function tsNodeToString(node: ts.Node): string {
export const ots = {
// Create a boolean expression based on value.
boolean: (value: boolean) => (value ? ts.factory.createTrue() : ts.factory.createFalse()),
export: (name: string, isTypeOnly?: boolean) =>
ts.factory.createExportSpecifier(
export: (name: string, isTypeOnly?: boolean, alias?: string) => {
const n = ts.factory.createIdentifier(encodeURIComponent(name));
return ts.factory.createExportSpecifier(
isTypeOnly ?? false,
undefined,
ts.factory.createIdentifier(encodeURIComponent(name))
),
import: (name: string, isTypeOnly?: boolean) =>
ts.factory.createImportSpecifier(
alias ? n : undefined,
alias ? ts.factory.createIdentifier(encodeURIComponent(alias)) : n
);
},
import: (name: string, isTypeOnly?: boolean, alias?: string) => {
const n = ts.factory.createIdentifier(encodeURIComponent(name));
return ts.factory.createImportSpecifier(
isTypeOnly ?? false,
undefined,
ts.factory.createIdentifier(encodeURIComponent(name))
),
alias ? n : undefined,
alias ? ts.factory.createIdentifier(encodeURIComponent(alias)) : n
);
},
// Create a numeric expression, handling negative numbers.
number: (value: number) => {
if (value < 0) {
Expand Down
63 changes: 30 additions & 33 deletions packages/openapi-ts/src/utils/write/services.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { writeFileSync } from 'node:fs';
import path from 'node:path';

import compiler from '../../compiler';
import { tsNodeToString } from '../../compiler/utils';
import compiler, { TypeScriptFile } from '../../compiler';
import type { Client } from '../../types/client';
import type { Config } from '../../types/config';
import { operationDataType, type Templates } from '../handlebars';
Expand All @@ -24,6 +23,7 @@ export const writeClientServices = async (
if (!client.services.length) {
return;
}
const file = new TypeScriptFile();

let imports: string[] = [];
let operationTypes: string[] = [];
Expand All @@ -41,58 +41,55 @@ export const writeClientServices = async (
}

// Import required packages and core files.
const coreImports: string[] = [];
if (config.client === 'angular') {
coreImports.push(tsNodeToString(compiler.import.named('Injectable', '@angular/core')));
file.push(compiler.import.named('Injectable', '@angular/core'));

Check warning on line 45 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L45

Added line #L45 was not covered by tests
if (config.name === undefined) {
coreImports.push(tsNodeToString(compiler.import.named('HttpClient', '@angular/common/http')));
file.push(compiler.import.named('HttpClient', '@angular/common/http'));

Check warning on line 47 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L47

Added line #L47 was not covered by tests
}
coreImports.push(tsNodeToString(compiler.import.named({ isTypeOnly: true, name: 'Observable' }, 'rxjs')));
file.push(compiler.import.named({ isTypeOnly: true, name: 'Observable' }, 'rxjs'));

Check warning on line 49 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L49

Added line #L49 was not covered by tests
} else {
coreImports.push(
tsNodeToString(
compiler.import.named({ isTypeOnly: true, name: 'CancelablePromise' }, './core/CancelablePromise')
)
);
file.push(compiler.import.named({ isTypeOnly: true, name: 'CancelablePromise' }, './core/CancelablePromise'));
}
if (config.serviceResponse === 'response') {
coreImports.push(
tsNodeToString(compiler.import.named({ isTypeOnly: true, name: 'ApiResult' }, './core/ApiResult'))
);
file.push(compiler.import.named({ isTypeOnly: true, name: 'ApiResult' }, './core/ApiResult'));

Check warning on line 54 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L54

Added line #L54 was not covered by tests
}
if (config.name) {
coreImports.push(
tsNodeToString(
compiler.import.named(
{ isTypeOnly: config.client !== 'angular', name: 'BaseHttpRequest' },
'./core/BaseHttpRequest'
)
file.push(
compiler.import.named(
{ isTypeOnly: config.client !== 'angular', name: 'BaseHttpRequest' },
'./core/BaseHttpRequest'

Check warning on line 60 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L57-L60

Added lines #L57 - L60 were not covered by tests
)
);
} else {
if (config.useOptions) {
if (config.serviceResponse === 'generics') {
coreImports.push(`import { mergeOpenApiConfig, OpenAPI } from './core/OpenAPI';`);
coreImports.push(`import { request as __request } from './core/request';`);
coreImports.push(`import type { TApiResponse, TConfig, TResult } from './core/types';`);
file.push(compiler.import.named(['mergeOpenApiConfig', 'OpenAPI'], './core/OpenAPI'));
file.push(compiler.import.named({ alias: '__request', name: 'request' }, './core/request'));
file.push(
compiler.import.named(
[
{ isTypeOnly: true, name: 'TApiResponse' },
{ isTypeOnly: true, name: 'TConfig' },
{ isTypeOnly: true, name: 'TResult' },
],
'./core/types'
)
);

Check warning on line 77 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L66-L77

Added lines #L66 - L77 were not covered by tests
} else {
coreImports.push(`import { OpenAPI } from './core/OpenAPI';`);
coreImports.push(`import { request as __request } from './core/request';`);
file.push(compiler.import.named('OpenAPI', './core/OpenAPI'));
file.push(compiler.import.named({ alias: '__request', name: 'request' }, './core/request'));

Check warning on line 80 in packages/openapi-ts/src/utils/write/services.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/utils/write/services.ts#L79-L80

Added lines #L79 - L80 were not covered by tests
}
} else {
coreImports.push(`import { OpenAPI } from './core/OpenAPI';`);
coreImports.push(`import { request as __request } from './core/request';`);
file.push(compiler.import.named('OpenAPI', './core/OpenAPI'));
file.push(compiler.import.named({ alias: '__request', name: 'request' }, './core/request'));
}
}

// Import all models required by the services.
let modelImportsString = '';
const uniqueImports = imports.filter(unique);
if (uniqueImports.length) {
modelImportsString = `import type { ${uniqueImports.join(',')} } from './models';`;
}
const models = imports.filter(unique).map(imp => ({ isTypeOnly: true, name: imp }));
file.push(compiler.import.named(models, './models'));

const data = [coreImports.join('\n'), modelImportsString, ...operationTypes, ...results].join('\n\n');
const data = [file.toString(), ...operationTypes, ...results].join('\n\n');

await writeFileSync(path.resolve(outputPath, 'services.ts'), data);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type {
ModelWithString,
ModelThatExtends,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type {
ModelWithArrayReadOnlyAndWriteOnly,
ModelWithReadOnlyAndWriteOnly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { HttpClient } from '@angular/common/http';
import type { Observable } from 'rxjs';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type {
ModelWithArrayReadOnlyAndWriteOnly,
ModelWithReadOnlyAndWriteOnly,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { CancelablePromise } from './core/CancelablePromise';
import type { BaseHttpRequest } from './core/BaseHttpRequest';

import type {
ModelWithArrayReadOnlyAndWriteOnly,
ModelWithReadOnlyAndWriteOnly,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type {
ModelWithArrayReadOnlyAndWriteOnly,
ModelWithReadOnlyAndWriteOnly,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type {
ModelWithArrayReadOnlyAndWriteOnly,
ModelWithReadOnlyAndWriteOnly,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type { ModelWithString } from './models';

export class DefaultsService {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';

import type { ModelWithString } from './models';

export type DefaultsData = {
Expand Down

0 comments on commit 4e2ff2b

Please sign in to comment.