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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public
gatsby-types.d.ts

# Generated
!generated
generated

# Turbo
.turbo
8 changes: 4 additions & 4 deletions packages/apicraft/apicraft.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { apicraft } from './dist/esm/index.mjs';

const apicraftConfig = apicraft([
{
input: 'example-apiV1.yaml',
output: 'generated/apiV1',
instance: 'fetches',
input: 'example-apiV2.yaml',
output: 'generated/apiV2-class',
instance: 'axios',
nameBy: 'path',
groupBy: 'tag',
groupBy: 'paths',
plugins: ['tanstack']
}
]);
Expand Down
19 changes: 8 additions & 11 deletions packages/apicraft/bin/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,28 @@ export const generate = {
option.instance === name ||
(typeof option.instance === 'object' && option.instance.name === name);

const generateOutput =
typeof option.output === 'string' ? option.output : option.output.path;
const runtimeInstancePath =
typeof option.instance === 'object' ? option.instance.runtimeInstancePath : undefined;

if (matchInstance('axios')) {
plugins.push(
defineAxiosPlugin({
generateOutput:
typeof option.output === 'string' ? option.output : option.output.path,
...(typeof option.instance === 'object' && {
runtimeInstancePath: option.instance.runtimeInstancePath
}),
generateOutput,
runtimeInstancePath,
exportFromIndex: true,
nameBy: option.nameBy,
groupBy: option.groupBy
})
);
}

const generateOutput =
typeof option.output === 'string' ? option.output : option.output.path;

if (matchInstance('fetches')) {
plugins.push(
defineFetchesPlugin({
generateOutput,
...(typeof option.instance === 'object' && {
runtimeInstancePath: option.instance.runtimeInstancePath
}),
runtimeInstancePath,
exportFromIndex: true,
nameBy: option.nameBy,
groupBy: option.groupBy
Expand Down
240 changes: 240 additions & 0 deletions packages/apicraft/bin/plugins/axios/class/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import * as nodePath from 'node:path';
import ts from 'typescript';

import {
capitalize,
generateRequestName,
getImportRuntimeInstance,
getRequestInfo
} from '@/bin/plugins/helpers';

import type { AxiosPlugin } from '../types';

import {
getAxiosRequestCallExpression,
getAxiosRequestParameterDeclaration,
getAxiosRequestParamsType,
getImportAxios,
getImportAxiosRequestParams
} from '../helpers';

const CLASS_NAME = 'ApiInstance';

export const classHandler: AxiosPlugin['Handler'] = ({ plugin }) => {
const classFilePath = nodePath.normalize(`${plugin.output}/instance`);
const classFolderPath = nodePath.dirname(`${plugin.config.generateOutput}/${classFilePath}`);
const classFile = plugin.createFile({
id: 'axiosInstance',
path: classFilePath
});

const typeImportNames = new Set<string>();
const typeStatements: ts.Statement[] = [];
const classElements: ts.ClassElement[] = [];

plugin.forEach('operation', (event) => {
if (event.type !== 'operation') return;

const request = event.operation;
const requestName = generateRequestName(request, plugin.config.nameBy);
const requestInfo = getRequestInfo({ request });

const requestDataTypeName = `${capitalize(request.id)}Data`;
typeImportNames.add(requestDataTypeName);

const requestResponseTypeName = `${capitalize(request.id)}Response`;
if (requestInfo.hasResponse) typeImportNames.add(requestResponseTypeName);

const requestParamsTypeName = `${capitalize(requestName)}RequestParams`;
typeStatements.push(
getAxiosRequestParamsType({
requestDataTypeName,
requestParamsTypeName
})
);

// ({ path, body, query, config }: RequestParams)
const requestParameter = getAxiosRequestParameterDeclaration({
request,
requestInfo,
requestParamsTypeName
});

const requestBody = ts.factory.createBlock(
[
ts.factory.createReturnStatement(
// instance.request({ method, url, data, params })
getAxiosRequestCallExpression({
request,
requestInfo,
requestResponseTypeName,
instanceVariant: 'class'
})
)
],
true
);

classElements.push(
ts.factory.createMethodDeclaration(
undefined,
undefined,
ts.factory.createIdentifier(requestName),
undefined,
undefined,
[requestParameter],
undefined,
requestBody
)
);
});

// import type { AxiosRequestParams } from '@siberiacancode/apicraft';
const importAxiosRequestParams = getImportAxiosRequestParams();

// import type { RequestData, RequestResponse, ... } from './types.gen';
const importTypes = ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
true,
undefined,
ts.factory.createNamedImports(
Array.from(typeImportNames).map((typeImportName) =>
ts.factory.createImportSpecifier(
false,
undefined,
ts.factory.createIdentifier(typeImportName)
)
)
)
),
ts.factory.createStringLiteral('./types.gen')
);

// import type { AxiosInstance, CreateAxiosDefaults } from 'axios';
const importAxiosTypes = ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
true,
undefined,
ts.factory.createNamedImports([
ts.factory.createImportSpecifier(
false,
undefined,
ts.factory.createIdentifier('AxiosInstance')
),
...(!plugin.config.runtimeInstancePath
? [
ts.factory.createImportSpecifier(
false,
undefined,
ts.factory.createIdentifier('CreateAxiosDefaults')
)
]
: [])
])
),
ts.factory.createStringLiteral('axios'),
undefined
);

// private instance: AxiosInstance;
const classInstanceProperty = ts.factory.createPropertyDeclaration(
[ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword)],
ts.factory.createIdentifier('instance'),
undefined,
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('AxiosInstance'), undefined),
undefined
);

// constructor(config?: CreateAxiosDefaults) { this.instance = axios.create(config); }
const constructorDeclaration = ts.factory.createConstructorDeclaration(
undefined,
!plugin.config.runtimeInstancePath
? [
ts.factory.createParameterDeclaration(
undefined,
undefined,
ts.factory.createIdentifier('config'),
ts.factory.createToken(ts.SyntaxKind.QuestionToken),
ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier('CreateAxiosDefaults'),
undefined
),
undefined
)
]
: [],
ts.factory.createBlock(
[
ts.factory.createExpressionStatement(
ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createThis(),
ts.factory.createIdentifier('instance')
),
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
plugin.config.runtimeInstancePath
? ts.factory.createIdentifier('runtimeInstance')
: ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier('axios'),
ts.factory.createIdentifier('create')
),
undefined,
!plugin.config.runtimeInstancePath ? [ts.factory.createIdentifier('config')] : []
)
)
)
],
true
)
);

// export class ApiInstance {...}
const classDeclaration = ts.factory.createClassDeclaration(
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
ts.factory.createIdentifier(CLASS_NAME),
undefined,
undefined,
[classInstanceProperty, constructorDeclaration, ...classElements]
);

// export const instance = new ApiInstance();
const classInstance = ts.factory.createVariableStatement(
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
ts.factory.createVariableDeclarationList(
[
ts.factory.createVariableDeclaration(
ts.factory.createIdentifier('instance'),
undefined,
undefined,
ts.factory.createNewExpression(ts.factory.createIdentifier(CLASS_NAME), undefined, [])
)
],
ts.NodeFlags.Const
)
);

classFile.add(importAxiosRequestParams);
classFile.add(importTypes);
classFile.add(importAxiosTypes);

if (plugin.config.runtimeInstancePath) {
// import { instance as runtimeInstance } from runtimeInstancePath;
classFile.add(
getImportRuntimeInstance({
folderPath: classFolderPath,
Copy link
Member

Choose a reason for hiding this comment

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

по факту это generateOutput для функции, опять у кирпичей хотелось один нейминг сохранить, постараться если это возможно

Copy link
Contributor Author

Choose a reason for hiding this comment

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

тоже не понял про нейминг, там где мы тупо возвразаем сразу ts factory назвал get
Там где какие то расчеты есть назвал generate как у хукво, могу везде get назвать

runtimeInstancePath: plugin.config.runtimeInstancePath
})
);
}
if (!plugin.config.runtimeInstancePath) {
// import axios from 'axios';
classFile.add(getImportAxios());
}

typeStatements.forEach((alias) => classFile.add(alias));
classFile.add(classDeclaration);
classFile.add(classInstance);
};
Loading