From 277e9b552301b17db928a9fb2c028765a502dd8b Mon Sep 17 00:00:00 2001 From: debabin Date: Sun, 1 Mar 2026 21:40:43 +0700 Subject: [PATCH 1/3] =?UTF-8?q?feature/reatom-init=20=F0=9F=A7=8A=20add=20?= =?UTF-8?q?reatom=20proto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/apicraft/apicraft.config.ts | 2 +- packages/apicraft/bin/generate.ts | 15 ++ .../getAxiosRequestParameterDeclaration.ts | 2 +- .../helpers/imports/getImportRequest.ts | 2 +- .../bin/plugins/reatom/class/plugin.ts | 72 +++++++ .../composed/helpers/generateReatomFile.ts | 73 +++++++ .../plugins/reatom/composed/helpers/index.ts | 1 + .../bin/plugins/reatom/composed/plugin.ts | 24 +++ .../apicraft/bin/plugins/reatom/config.ts | 18 ++ .../helpers/getImportRequestParamsType.ts | 102 +++++++++ .../plugins/reatom/helpers/getReatomAsync.ts | 149 +++++++++++++ .../reatom/helpers/getReatomAsyncData.ts | 197 ++++++++++++++++++ .../plugins/reatom/helpers/getReatomImport.ts | 48 +++++ .../bin/plugins/reatom/helpers/index.ts | 9 + packages/apicraft/bin/plugins/reatom/index.ts | 2 + .../apicraft/bin/plugins/reatom/plugin.ts | 13 ++ .../apicraft/bin/plugins/reatom/types.d.ts | 14 ++ packages/apicraft/bin/schemas/index.ts | 1 + packages/apicraft/package.json | 5 +- packages/apicraft/src/types.ts | 17 ++ yarn.lock | 14 ++ 21 files changed, 775 insertions(+), 5 deletions(-) create mode 100644 packages/apicraft/bin/plugins/reatom/class/plugin.ts create mode 100644 packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts create mode 100644 packages/apicraft/bin/plugins/reatom/composed/helpers/index.ts create mode 100644 packages/apicraft/bin/plugins/reatom/composed/plugin.ts create mode 100644 packages/apicraft/bin/plugins/reatom/config.ts create mode 100644 packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts create mode 100644 packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts create mode 100644 packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts create mode 100644 packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts create mode 100644 packages/apicraft/bin/plugins/reatom/helpers/index.ts create mode 100644 packages/apicraft/bin/plugins/reatom/index.ts create mode 100644 packages/apicraft/bin/plugins/reatom/plugin.ts create mode 100644 packages/apicraft/bin/plugins/reatom/types.d.ts diff --git a/packages/apicraft/apicraft.config.ts b/packages/apicraft/apicraft.config.ts index 1d89ce0..0f53dc9 100644 --- a/packages/apicraft/apicraft.config.ts +++ b/packages/apicraft/apicraft.config.ts @@ -11,7 +11,7 @@ const apicraftConfig = apicraft([ instance: 'axios', nameBy: 'path', groupBy: 'paths', - plugins: ['tanstack'] + plugins: ['reatom', 'tanstack'] } ]); diff --git a/packages/apicraft/bin/generate.ts b/packages/apicraft/bin/generate.ts index 8e86ff0..8a7a6e4 100644 --- a/packages/apicraft/bin/generate.ts +++ b/packages/apicraft/bin/generate.ts @@ -10,6 +10,7 @@ import type { ApicraftOption, GenerateApicraftOption, InstanceName } from './sch import { defineAxiosPlugin } from './plugins/axios'; import { defineFetchesPlugin } from './plugins/fetches'; +import { defineReatomPlugin } from './plugins/reatom'; import { defineTanstackPlugin } from './plugins/tanstack'; import { apicraftConfigSchema, apicraftOptionSchema } from './schemas'; @@ -94,6 +95,20 @@ export const generate = { ); } + const reatomPlugin = plugins.find( + (plugin) => plugin === 'reatom' || plugin.name === 'reatom' + ); + if (reatomPlugin) { + plugins.push( + defineReatomPlugin({ + generateOutput, + exportFromIndex: true, + nameBy: option.nameBy, + groupBy: option.groupBy + }) + ); + } + await createClient({ parser: { filters: option.filters }, input: option.input, diff --git a/packages/apicraft/bin/plugins/axios/helpers/getAxiosRequestParameterDeclaration.ts b/packages/apicraft/bin/plugins/axios/helpers/getAxiosRequestParameterDeclaration.ts index f7550d8..d2e6386 100644 --- a/packages/apicraft/bin/plugins/axios/helpers/getAxiosRequestParameterDeclaration.ts +++ b/packages/apicraft/bin/plugins/axios/helpers/getAxiosRequestParameterDeclaration.ts @@ -2,7 +2,7 @@ import type { IR } from '@hey-api/openapi-ts'; import ts from 'typescript'; -import type { GetRequestInfoResult } from '../getRequestInfo'; +import type { GetRequestInfoResult } from '@/bin/plugins/helpers'; interface GetAxiosRequestParameterDeclarationParams { request: IR.OperationObject; diff --git a/packages/apicraft/bin/plugins/helpers/imports/getImportRequest.ts b/packages/apicraft/bin/plugins/helpers/imports/getImportRequest.ts index 24b8962..fb2aec0 100644 --- a/packages/apicraft/bin/plugins/helpers/imports/getImportRequest.ts +++ b/packages/apicraft/bin/plugins/helpers/imports/getImportRequest.ts @@ -25,6 +25,6 @@ export const getImportRequest = ({ ]) ), ts.factory.createStringLiteral( - nodePath.relative(folderPath, `${generateOutput}/${requestFilePath}.gen`) + nodePath.relative(folderPath, `${generateOutput}/${requestFilePath}.gen`).replace(/\\/g, '/') ) ); diff --git a/packages/apicraft/bin/plugins/reatom/class/plugin.ts b/packages/apicraft/bin/plugins/reatom/class/plugin.ts new file mode 100644 index 0000000..68c42a7 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/class/plugin.ts @@ -0,0 +1,72 @@ +import type ts from 'typescript'; + +import { capitalize, generateRequestName, getImportInstance } from '@/bin/plugins/helpers'; + +import type { ReatomPlugin } from '../types'; + +import { + getImportRequestParamsTypes, + getReatomAsync, + getReatomAsyncData, + getReatomCoreImport, + getReatomSettingsTypeImport +} from '../helpers'; + +export const classHandler: ReatomPlugin['Handler'] = ({ plugin }) => { + const reatomFile = plugin.createFile({ + id: 'reatom', + path: `${plugin.output}/reatom` + }); + + const requestParamsTypeNames: string[] = []; + + plugin.forEach('operation', (event) => { + if (event.type !== 'operation') return; + + const request = event.operation; + const requestName = generateRequestName(request, plugin.config.nameBy); + requestParamsTypeNames.push(`${capitalize(requestName)}RequestParams`); + }); + + const imports: ts.ImportDeclaration[] = [ + getReatomCoreImport(), + getReatomSettingsTypeImport(), + getImportInstance({ + output: plugin.output, + folderPath: plugin.output, + generateOutput: plugin.config.generateOutput + }), + getImportRequestParamsTypes({ + folderPath: plugin.output, + generateOutput: plugin.config.generateOutput, + output: plugin.output, + requestParamsTypeNames + }) + ]; + + reatomFile.add(...imports); + + plugin.forEach('operation', (event) => { + if (event.type !== 'operation') return; + + const request = event.operation; + const requestName = generateRequestName(request, plugin.config.nameBy); + const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; + + reatomFile.add( + getReatomAsyncData({ + request, + requestName, + requestParamsTypeName, + requestRef: 'instance' + }) + ); + reatomFile.add( + getReatomAsync({ + requestName, + requestParamsTypeName, + requestRef: 'instance' + }) + ); + }); +}; diff --git a/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts b/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts new file mode 100644 index 0000000..97aa84c --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts @@ -0,0 +1,73 @@ +import type { IR } from '@hey-api/openapi-ts'; + +import * as nodePath from 'node:path'; + +import { capitalize, getImportRequest } from '@/bin/plugins/helpers'; + +import type { ReatomPlugin } from '../../types'; + +import { + getImportRequestParamsTypeFromRequestFile, + getReatomAsync, + getReatomAsyncData, + getReatomCoreImport, + getReatomSettingsTypeImport +} from '../../helpers'; + +interface GenerateReatomFileParams { + plugin: ReatomPlugin['Instance']; + request: IR.OperationObject; + requestFilePath: string; + requestName: string; +} + +export const generateReatomFile = ({ + plugin, + request, + requestName, + requestFilePath +}: GenerateReatomFileParams) => { + const reatomFilePath = `${nodePath.dirname(requestFilePath).replace('requests', 'reatom')}/${requestName}`; + const reatomFolderPath = nodePath.dirname(`${plugin.config.generateOutput}/${reatomFilePath}`); + + const reatomFile = plugin.createFile({ + id: requestName, + path: reatomFilePath + }); + + const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; + + reatomFile.add(getReatomCoreImport()); + reatomFile.add(getReatomSettingsTypeImport()); + reatomFile.add( + getImportRequest({ + folderPath: reatomFolderPath, + requestFilePath, + requestName, + generateOutput: plugin.config.generateOutput + }) + ); + reatomFile.add( + getImportRequestParamsTypeFromRequestFile({ + folderPath: reatomFolderPath, + generateOutput: plugin.config.generateOutput, + requestFilePath, + requestParamsTypeName + }) + ); + reatomFile.add( + getReatomAsyncData({ + request, + requestName, + requestParamsTypeName, + requestRef: 'function' + }) + ); + reatomFile.add( + getReatomAsync({ + requestName, + requestParamsTypeName, + requestRef: 'function' + }) + ); +}; diff --git a/packages/apicraft/bin/plugins/reatom/composed/helpers/index.ts b/packages/apicraft/bin/plugins/reatom/composed/helpers/index.ts new file mode 100644 index 0000000..29003fb --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/composed/helpers/index.ts @@ -0,0 +1 @@ +export { generateReatomFile } from './generateReatomFile'; diff --git a/packages/apicraft/bin/plugins/reatom/composed/plugin.ts b/packages/apicraft/bin/plugins/reatom/composed/plugin.ts new file mode 100644 index 0000000..ea251e8 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/composed/plugin.ts @@ -0,0 +1,24 @@ +import { generateRequestName, getRequestFilePaths } from '@/bin/plugins/helpers'; + +import type { ReatomPlugin } from '../types'; + +import { generateReatomFile } from './helpers'; + +export const composedHandler: ReatomPlugin['Handler'] = ({ plugin }) => + plugin.forEach('operation', (event) => { + if (event.type !== 'operation') return; + + const request = event.operation; + const requestName = generateRequestName(request, plugin.config.nameBy); + + const requestFilePaths = getRequestFilePaths({ + groupBy: plugin.config.groupBy, + output: plugin.output, + requestName, + request + }); + + requestFilePaths.forEach((requestFilePath) => { + generateReatomFile({ plugin, requestFilePath, request, requestName }); + }); + }); diff --git a/packages/apicraft/bin/plugins/reatom/config.ts b/packages/apicraft/bin/plugins/reatom/config.ts new file mode 100644 index 0000000..81494e4 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/config.ts @@ -0,0 +1,18 @@ +import { definePluginConfig } from '@hey-api/openapi-ts'; + +import type { ReatomPlugin } from './types'; + +import { handler } from './plugin'; + +export const defaultConfig: ReatomPlugin['Config'] = { + config: { + generateOutput: '', + exportFromIndex: true + }, + dependencies: ['@hey-api/typescript'], + handler, + name: 'reatom', + output: '.' +}; + +export const defineReatomPlugin = definePluginConfig(defaultConfig); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts b/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts new file mode 100644 index 0000000..9cec630 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts @@ -0,0 +1,102 @@ +import nodePath from 'node:path'; +import ts from 'typescript'; + +interface GetImportRequestParamsTypeParams { + folderPath: string; + generateOutput: string; + output: string; + requestParamsTypeName: string; + runtimeInstancePath?: string; +} + +/** Import type from instance.gen (class mode) */ +export const getImportRequestParamsType = ({ + folderPath, + generateOutput, + output, + requestParamsTypeName, + runtimeInstancePath +}: GetImportRequestParamsTypeParams) => + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier(requestParamsTypeName) + ) + ]) + ), + ts.factory.createStringLiteral( + nodePath + .relative( + folderPath, + runtimeInstancePath ?? nodePath.normalize(`${generateOutput}/${output}/instance.gen`) + ) + .replace(/\\/g, '/') + ) + ); + +export const getImportRequestParamsTypes = (params: { + folderPath: string; + generateOutput: string; + output: string; + requestParamsTypeNames: string[]; + runtimeInstancePath?: string; +}) => + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports( + params.requestParamsTypeNames.map((name) => + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)) + ) + ) + ), + ts.factory.createStringLiteral( + nodePath + .relative( + params.folderPath, + params.runtimeInstancePath ?? + nodePath.normalize(`${params.generateOutput}/${params.output}/instance.gen`) + ) + .replace(/\\/g, '/') + ) + ); + +interface GetImportRequestParamsTypeFromRequestFileParams { + folderPath: string; + generateOutput: string; + requestFilePath: string; + requestParamsTypeName: string; +} + +/** Import type from request file (composed mode) */ +export const getImportRequestParamsTypeFromRequestFile = ({ + folderPath, + generateOutput, + requestFilePath, + requestParamsTypeName +}: GetImportRequestParamsTypeFromRequestFileParams) => + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier(requestParamsTypeName) + ) + ]) + ), + ts.factory.createStringLiteral( + nodePath.relative(folderPath, `${generateOutput}/${requestFilePath}.gen`).replace(/\\/g, '/') + ) + ); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts new file mode 100644 index 0000000..0f6b43b --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts @@ -0,0 +1,149 @@ +import ts from 'typescript'; + +import type { RequestRef } from './getReatomAsyncData'; + +interface GetReatomAsyncParams { + requestName: string; + requestParamsTypeName: string; + requestRef: RequestRef; +} + +export const getReatomAsync = ({ + requestName, + requestParamsTypeName, + requestRef +}: GetReatomAsyncParams) => { + const asyncName = `${requestName}Async`; + + const requestCall = + requestRef === 'instance' + ? ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('instance'), + ts.factory.createIdentifier(requestName) + ), + undefined, + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createSpreadAssignment( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('request') + ) + ), + ts.factory.createSpreadAssignment(ts.factory.createIdentifier('payload')) + ], + false + ) + ] + ) + : ts.factory.createCallExpression(ts.factory.createIdentifier(requestName), undefined, [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createSpreadAssignment( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('request') + ) + ), + ts.factory.createSpreadAssignment(ts.factory.createIdentifier('payload')) + ], + false + ) + ]); + + const settingsTypeNode = + requestRef === 'instance' + ? ts.factory.createTypeQueryNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier('instance'), + ts.factory.createIdentifier(requestName) + ) + ) + : ts.factory.createTypeQueryNode(ts.factory.createIdentifier(requestName)); + + const actionCall = ts.factory.createCallExpression( + ts.factory.createIdentifier('action'), + undefined, + [ + ts.factory.createArrowFunction( + [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)], + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('payload'), + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier(requestParamsTypeName), + undefined + ) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createAwaitExpression( + ts.factory.createCallExpression(ts.factory.createIdentifier('wrap'), undefined, [ + requestCall + ]) + ) + ), + ts.factory.createStringLiteral(asyncName) + ] + ); + + const extendCall = ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(actionCall, ts.factory.createIdentifier('extend')), + undefined, + [ + ts.factory.createCallExpression(ts.factory.createIdentifier('withAsync'), undefined, [ + ts.factory.createBinaryExpression( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('params') + ), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false) + ) + ]) + ] + ); + + return ts.factory.createVariableStatement( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier(asyncName), + undefined, + undefined, + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionToken), + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ReatomAsyncSettings'), + [settingsTypeNode] + ) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + extendCall + ) + ) + ], + ts.NodeFlags.Const + ) + ); +}; diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts new file mode 100644 index 0000000..aa0f224 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts @@ -0,0 +1,197 @@ +import type { IR } from '@hey-api/openapi-ts'; + +import ts from 'typescript'; + +import { capitalize, getRequestInfo } from '@/bin/plugins/helpers'; + +export type RequestRef = 'function' | 'instance'; + +interface GetReatomAsyncDataParams { + request: IR.OperationObject; + requestName: string; + requestParamsTypeName: string; + requestRef: RequestRef; +} + +function getRequestFieldsForOperation(request: IR.OperationObject): string[] { + const hasPathParam = !!Object.keys(request.parameters?.path ?? {}).length; + const hasQueryParam = !!Object.keys(request.parameters?.query ?? {}).length; + const hasBody = !!request.body; + const fields: string[] = []; + if (hasPathParam) fields.push('path'); + if (hasQueryParam) fields.push('query'); + if (hasBody) fields.push('body'); + fields.push('config'); + return fields; +} + +function createUnwrapAtom(field: string): ts.Statement { + return ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier(field), + undefined, + undefined, + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('request'), + ts.factory.createIdentifier(field) + ), + undefined, + [] + ) + ) + ], + ts.NodeFlags.Const + ) + ); +} + +export const getReatomAsyncData = ({ + request, + requestName, + requestRef +}: GetReatomAsyncDataParams) => { + const requestInfo = getRequestInfo({ request }); + const requestFields = getRequestFieldsForOperation(request); + const asyncDataName = `create${capitalize(requestName)}AsyncData`; + + const settingsTypeNode = + requestRef === 'instance' + ? ts.factory.createTypeQueryNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier('instance'), + ts.factory.createIdentifier(requestName) + ) + ) + : ts.factory.createTypeQueryNode(ts.factory.createIdentifier(requestName)); + + const requestCallArg = ts.factory.createObjectLiteralExpression( + requestFields.map((field) => + ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(field)) + ), + false + ); + + const requestCall = + requestRef === 'instance' + ? ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('instance'), + ts.factory.createIdentifier(requestName) + ), + undefined, + [requestCallArg] + ) + : ts.factory.createCallExpression(ts.factory.createIdentifier(requestName), undefined, [ + requestCallArg + ]); + + const computedBody: ts.Statement[] = [ + ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('request'), + undefined, + undefined, + ts.factory.createBinaryExpression( + requestInfo.hasRequiredParam + ? ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('settings'), + ts.factory.createIdentifier('request') + ) + : ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('request') + ), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false) + ) + ) + ], + ts.NodeFlags.Const + ) + ), + ...requestFields.map((field) => createUnwrapAtom(field)), + ts.factory.createReturnStatement( + ts.factory.createAwaitExpression( + ts.factory.createCallExpression(ts.factory.createIdentifier('wrap'), undefined, [ + requestCall + ]) + ) + ) + ]; + + const computedCall = ts.factory.createCallExpression( + ts.factory.createIdentifier('computed'), + undefined, + [ + ts.factory.createArrowFunction( + [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)], + undefined, + [], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createBlock(computedBody, true) + ) + ] + ); + + const extendCall = ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(computedCall, ts.factory.createIdentifier('extend')), + undefined, + [ + ts.factory.createCallExpression(ts.factory.createIdentifier('withAsyncData'), undefined, [ + ts.factory.createBinaryExpression( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('params') + ), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false) + ) + ]) + ] + ); + + return ts.factory.createVariableStatement( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier(asyncDataName), + undefined, + undefined, + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('settings'), + !requestInfo.hasRequiredParam + ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) + : undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ReatomAsyncDataSettings'), + [settingsTypeNode] + ) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + extendCall + ) + ) + ], + ts.NodeFlags.Const + ) + ); +}; diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts new file mode 100644 index 0000000..f1a662e --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts @@ -0,0 +1,48 @@ +import ts from 'typescript'; + +export const getReatomCoreImport = () => + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + false, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('action')), + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('computed')), + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('wrap')), + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('withAsync') + ), + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('withAsyncData') + ) + ]) + ), + ts.factory.createStringLiteral('@reatom/core') + ); + +export const getReatomSettingsTypeImport = () => + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('ReatomAsyncDataSettings') + ), + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('ReatomAsyncSettings') + ) + ]) + ), + ts.factory.createStringLiteral('@siberiacancode/apicraft') + ); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/index.ts b/packages/apicraft/bin/plugins/reatom/helpers/index.ts new file mode 100644 index 0000000..ced37f4 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/helpers/index.ts @@ -0,0 +1,9 @@ +export { + getImportRequestParamsType, + getImportRequestParamsTypeFromRequestFile, + getImportRequestParamsTypes +} from './getImportRequestParamsType'; +export { getReatomAsync } from './getReatomAsync'; +export type { RequestRef } from './getReatomAsyncData'; +export { getReatomAsyncData } from './getReatomAsyncData'; +export { getReatomCoreImport, getReatomSettingsTypeImport } from './getReatomImport'; diff --git a/packages/apicraft/bin/plugins/reatom/index.ts b/packages/apicraft/bin/plugins/reatom/index.ts new file mode 100644 index 0000000..35c0807 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/index.ts @@ -0,0 +1,2 @@ +export { defineReatomPlugin } from './config'; +export type { ReatomPlugin } from './types'; diff --git a/packages/apicraft/bin/plugins/reatom/plugin.ts b/packages/apicraft/bin/plugins/reatom/plugin.ts new file mode 100644 index 0000000..47c9c77 --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/plugin.ts @@ -0,0 +1,13 @@ +import type { ReatomPlugin } from './types'; + +import { classHandler } from './class/plugin'; +import { composedHandler } from './composed/plugin'; + +export const handler: ReatomPlugin['Handler'] = ({ plugin }) => { + if (plugin.config.groupBy === 'class') { + classHandler({ plugin }); + } + if (plugin.config.groupBy === 'paths' || plugin.config.groupBy === 'tags') { + composedHandler({ plugin }); + } +}; diff --git a/packages/apicraft/bin/plugins/reatom/types.d.ts b/packages/apicraft/bin/plugins/reatom/types.d.ts new file mode 100644 index 0000000..42a401e --- /dev/null +++ b/packages/apicraft/bin/plugins/reatom/types.d.ts @@ -0,0 +1,14 @@ +import type { DefinePlugin } from '@hey-api/openapi-ts'; + +import type { ApicraftOption } from '@/bin/schemas'; + +export interface ReatomPluginConfig { + exportFromIndex: boolean; + generateOutput: string; + groupBy?: ApicraftOption['groupBy']; + name: 'reatom'; + nameBy?: ApicraftOption['nameBy']; + output?: string; +} + +export type ReatomPlugin = DefinePlugin; diff --git a/packages/apicraft/bin/schemas/index.ts b/packages/apicraft/bin/schemas/index.ts index 93b073e..3a7d2ea 100644 --- a/packages/apicraft/bin/schemas/index.ts +++ b/packages/apicraft/bin/schemas/index.ts @@ -33,6 +33,7 @@ const pluginNameSchema = z.enum([ 'fastify', 'valibot', 'tanstack', + 'reatom', 'zod' ]); diff --git a/packages/apicraft/package.json b/packages/apicraft/package.json index 536b419..f6560b1 100644 --- a/packages/apicraft/package.json +++ b/packages/apicraft/package.json @@ -58,14 +58,15 @@ }, "dependencies": { "@hey-api/openapi-ts": "0.82.5", - "@siberiacancode/fetches": "^1.13.2", - "@tanstack/react-query": "^5.90.5", + "@siberiacancode/fetches": "^1.14.0", + "@tanstack/react-query": "^5.90.21", "cosmiconfig": "^9.0.0", "typescript": "^5.9.3", "yargs": "^18.0.0", "zod": "^4.3.6" }, "devDependencies": { + "@reatom/core": "^1000.15.1", "@siberiacancode/eslint": "*", "@siberiacancode/prettier": "*", "@types/node": "^25.2.3", diff --git a/packages/apicraft/src/types.ts b/packages/apicraft/src/types.ts index 912d8b3..ea13ab8 100644 --- a/packages/apicraft/src/types.ts +++ b/packages/apicraft/src/types.ts @@ -1,3 +1,4 @@ +import type { AsyncDataOptions, AsyncOptions } from '@reatom/core'; import type { RequestConfig } from '@siberiacancode/fetches'; import type { UseMutationOptions, @@ -32,3 +33,19 @@ export interface TanstackMutationSettings Prom params?: UseMutationOptions>, never, Parameters[0], unknown>; request?: NonNullable[0]>; } + +export interface ReatomAsyncDataSettings Promise> { + params?: AsyncDataOptions< + Awaited>, + Parameters, + Awaited>, + Error, + undefined + >; + request?: NonNullable[0]>; +} + +export interface ReatomAsyncSettings Promise> { + params?: AsyncOptions; + request?: NonNullable[0]>; +} diff --git a/yarn.lock b/yarn.lock index 621be1f..c25b570 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,6 +907,15 @@ dependencies: quansync "^1.0.0" +"@reatom/core@^1000.10.0": + version "1000.15.1" + resolved "https://registry.yarnpkg.com/@reatom/core/-/core-1000.15.1.tgz#4bf1151c99dc7b0713df245bc37810c7ec36aa76" + integrity sha512-5KKwjD7teOfHNXUj9aJxFYR+DLWiaE0xZwy1tpWlSE+cx3VI5VwJZJuhvZ887pdrMbgwyOypcz/V9p1v3YSWPg== + dependencies: + "@standard-schema/spec" "^1.0.0" + optionalDependencies: + idb-keyval "^6.2.2" + "@rolldown/binding-android-arm64@1.0.0-rc.3": version "1.0.0-rc.3" resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.3.tgz#223d0624c748ab2bcf20159e4baa17c6f03fe8a6" @@ -3617,6 +3626,11 @@ iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +idb-keyval@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.2.tgz#b0171b5f73944854a3291a5cdba8e12768c4854a" + integrity sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg== + ignore@^5.2.0, ignore@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" From bae4df5b6ef0d7e06d1f4f650e702e1cba575c39 Mon Sep 17 00:00:00 2001 From: debabin Date: Thu, 9 Apr 2026 20:53:36 +0700 Subject: [PATCH 2/3] =?UTF-8?q?feature/reatom-init=20=F0=9F=A7=8A=20add=20?= =?UTF-8?q?prototype=20for=20reatom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reatom/helpers/getReatomAsyncData.ts | 292 +++++++++++++++--- packages/apicraft/src/types.ts | 14 +- yarn.lock | 31 +- 3 files changed, 280 insertions(+), 57 deletions(-) diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts index aa0f224..540496b 100644 --- a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts @@ -25,33 +25,10 @@ function getRequestFieldsForOperation(request: IR.OperationObject): string[] { return fields; } -function createUnwrapAtom(field: string): ts.Statement { - return ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createIdentifier(field), - undefined, - undefined, - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('request'), - ts.factory.createIdentifier(field) - ), - undefined, - [] - ) - ) - ], - ts.NodeFlags.Const - ) - ); -} - export const getReatomAsyncData = ({ request, requestName, + requestParamsTypeName, requestRef }: GetReatomAsyncDataParams) => { const requestInfo = getRequestInfo({ request }); @@ -89,35 +66,264 @@ export const getReatomAsyncData = ({ requestCallArg ]); - const computedBody: ts.Statement[] = [ - ts.factory.createVariableStatement( + const requestAccess = requestInfo.hasRequiredParam + ? ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('settings'), + ts.factory.createIdentifier('request') + ) + : ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('request') + ); + + const requestVariable = ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('request'), + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + ts.factory.createAsExpression( + ts.factory.createBinaryExpression( + requestAccess, + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false) + ), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ) + ], + ts.NodeFlags.Const + ) + ); + + const unwrapReactiveObjectHelper = ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('unwrapReactiveObject'), + undefined, + undefined, + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('value'), + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ], + ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createBlock( + [ + ts.factory.createIfStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('Array'), + ts.factory.createIdentifier('isArray') + ), + undefined, + [ts.factory.createIdentifier('value')] + ), + ts.factory.createBlock( + [ + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('value'), + ts.factory.createIdentifier('map') + ), + undefined, + [ + ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, 'item')], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createCallExpression( + ts.factory.createIdentifier('unwrapReactiveObject'), + undefined, + [ts.factory.createIdentifier('item')] + ) + ) + ] + ) + ) + ], + true + ) + ), + ts.factory.createIfStatement( + ts.factory.createBinaryExpression( + ts.factory.createIdentifier('value'), + ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), + ts.factory.createBinaryExpression( + ts.factory.createTypeOfExpression(ts.factory.createIdentifier('value')), + ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), + ts.factory.createStringLiteral('object') + ) + ), + ts.factory.createBlock( + [ + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('Object'), + ts.factory.createIdentifier('fromEntries') + ), + undefined, + [ + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('Object'), + ts.factory.createIdentifier('entries') + ), + undefined, + [ts.factory.createIdentifier('value')] + ), + ts.factory.createIdentifier('map') + ), + undefined, + [ + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createArrayBindingPattern([ + ts.factory.createBindingElement(undefined, undefined, 'key'), + ts.factory.createBindingElement( + undefined, + undefined, + 'entry' + ) + ]) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createArrayLiteralExpression( + [ + ts.factory.createIdentifier('key'), + ts.factory.createConditionalExpression( + ts.factory.createBinaryExpression( + ts.factory.createTypeOfExpression( + ts.factory.createIdentifier('entry') + ), + ts.factory.createToken( + ts.SyntaxKind.EqualsEqualsEqualsToken + ), + ts.factory.createStringLiteral('function') + ), + undefined, + ts.factory.createConditionalExpression( + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('entry'), + ts.factory.createToken( + ts.SyntaxKind.QuestionDotToken + ), + ts.factory.createIdentifier('__reatom') + ) + ) + ), + undefined, + ts.factory.createCallExpression( + ts.factory.createIdentifier('entry'), + undefined, + [] + ), + undefined, + ts.factory.createIdentifier('entry') + ), + undefined, + ts.factory.createCallExpression( + ts.factory.createIdentifier('unwrapReactiveObject'), + undefined, + [ts.factory.createIdentifier('entry')] + ) + ) + ], + false + ) + ) + ] + ) + ] + ) + ) + ], + true + ) + ), + ts.factory.createReturnStatement(ts.factory.createIdentifier('value')) + ], + true + ) + ) + ) + ], + ts.NodeFlags.Const + ) + ); + + const createRequestFieldStatement = (field: string): ts.Statement => { + const requestFieldAccess = ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('request'), + ts.factory.createIdentifier(field) + ); + + const valueExpression = + field === 'config' + ? requestFieldAccess + : ts.factory.createCallExpression(ts.factory.createIdentifier('unwrapReactiveObject'), undefined, [ + requestFieldAccess + ]); + + return ts.factory.createVariableStatement( undefined, ts.factory.createVariableDeclarationList( [ ts.factory.createVariableDeclaration( - ts.factory.createIdentifier('request'), + ts.factory.createIdentifier(field), undefined, - undefined, - ts.factory.createBinaryExpression( - requestInfo.hasRequiredParam - ? ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('settings'), - ts.factory.createIdentifier('request') - ) - : ts.factory.createPropertyAccessChain( - ts.factory.createIdentifier('settings'), - ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), - ts.factory.createIdentifier('request') - ), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createObjectLiteralExpression([], false) + ts.factory.createIndexedAccessTypeNode( + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(requestParamsTypeName)), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field)) + ), + ts.factory.createAsExpression( + valueExpression, + ts.factory.createIndexedAccessTypeNode( + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(requestParamsTypeName)), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field)) + ) ) ) ], ts.NodeFlags.Const ) - ), - ...requestFields.map((field) => createUnwrapAtom(field)), + ); + }; + + const computedBody: ts.Statement[] = [ + requestVariable, + unwrapReactiveObjectHelper, + ...requestFields.map((field) => createRequestFieldStatement(field)), ts.factory.createReturnStatement( ts.factory.createAwaitExpression( ts.factory.createCallExpression(ts.factory.createIdentifier('wrap'), undefined, [ diff --git a/packages/apicraft/src/types.ts b/packages/apicraft/src/types.ts index ea13ab8..0faeeea 100644 --- a/packages/apicraft/src/types.ts +++ b/packages/apicraft/src/types.ts @@ -34,6 +34,14 @@ export interface TanstackMutationSettings Prom request?: NonNullable[0]>; } +export type ReatomAtom = (() => TValue) & { __reatom: unknown }; +export type ReatomAtomized = TValue | ReatomAtom; +export type ReatomDeepAtomized = TValue extends readonly (infer Item)[] + ? ReatomDeepAtomized[] + : TValue extends object + ? { [Key in keyof TValue]: ReatomDeepAtomized | ReatomAtom } + : ReatomAtomized; + export interface ReatomAsyncDataSettings Promise> { params?: AsyncDataOptions< Awaited>, @@ -42,7 +50,11 @@ export interface ReatomAsyncDataSettings Promi Error, undefined >; - request?: NonNullable[0]>; + request?: Partial<{ + [Key in keyof NonNullable[0]>]: Key extends 'config' + ? NonNullable[0]>[Key] + : ReatomDeepAtomized[0]>[Key]>; + }>; } export interface ReatomAsyncSettings Promise> { diff --git a/yarn.lock b/yarn.lock index c25b570..5f6826f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,10 +907,10 @@ dependencies: quansync "^1.0.0" -"@reatom/core@^1000.10.0": - version "1000.15.1" - resolved "https://registry.yarnpkg.com/@reatom/core/-/core-1000.15.1.tgz#4bf1151c99dc7b0713df245bc37810c7ec36aa76" - integrity sha512-5KKwjD7teOfHNXUj9aJxFYR+DLWiaE0xZwy1tpWlSE+cx3VI5VwJZJuhvZ887pdrMbgwyOypcz/V9p1v3YSWPg== +"@reatom/core@^1000.15.1": + version "1000.15.2" + resolved "https://registry.yarnpkg.com/@reatom/core/-/core-1000.15.2.tgz#cf8300744551d9a3b6a5b87751b3be4e7861af35" + integrity sha512-ychH11wZaVHHYsfPuPQvgGr8YyonURggwQwiaTxy+164nRfDnXXgbvuaWePjim7K3FR3LfI6ydYNoqJnDh88Bg== dependencies: "@standard-schema/spec" "^1.0.0" optionalDependencies: @@ -1098,6 +1098,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz#e0d19dffcf25f0fd86f50402a3413004003939e9" integrity sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ== +"@siberiacancode/fetches@^1.14.0": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@siberiacancode/fetches/-/fetches-1.14.1.tgz#30b60b35a7e9cea3d0755a3f258a9deb2e382549" + integrity sha512-JXoBRg3/MfgzUr5tMOpM83uL91lfxLtsuWyYDlRfUjHTCU7DsxNBe7net6CSJR6gi8Qgovu923zIC13KK0Xc+A== + "@sindresorhus/base62@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@sindresorhus/base62/-/base62-1.0.0.tgz#c47c42410e5212e4fa4657670e118ddfba39acd6" @@ -1120,17 +1125,17 @@ estraverse "^5.3.0" picomatch "^4.0.3" -"@tanstack/query-core@5.90.12": - version "5.90.12" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.90.12.tgz#e1f5f47e72ef7d0fc794325936921c700352515e" - integrity sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg== +"@tanstack/query-core@5.97.0": + version "5.97.0" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.97.0.tgz#538c66b9290cd57771c2e198c9d8f89d89299943" + integrity sha512-QdpLP5VzVMgo4VtaPppRA2W04UFjIqX+bxke/ZJhE5cfd5UPkRzqIAJQt9uXkQJjqE8LBOMbKv7f8HCsZltXlg== -"@tanstack/react-query@^5.90.5": - version "5.90.12" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.90.12.tgz#49536842eff6487a9e645a453fea2642d8f4f209" - integrity sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg== +"@tanstack/react-query@^5.90.21": + version "5.97.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.97.0.tgz#0df5f6320a4b62ae8ed5042bb59166b6426624ad" + integrity sha512-y4So4eGcQoK2WVMAcDNZE9ofB/p5v1OlKvtc1F3uqHwrtifobT7q+ZnXk2mRkc8E84HKYSlAE9z6HXl2V0+ySQ== dependencies: - "@tanstack/query-core" "5.90.12" + "@tanstack/query-core" "5.97.0" "@tybys/wasm-util@^0.10.1": version "0.10.1" From ed1a9bc82584b74fdf27004a1c1b12b88139f5b6 Mon Sep 17 00:00:00 2001 From: debabin Date: Sun, 12 Apr 2026 18:15:49 +0700 Subject: [PATCH 3/3] =?UTF-8?q?feature/reatom-init=20=F0=9F=A7=8A=20rework?= =?UTF-8?q?=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bin/plugins/reatom/class/plugin.ts | 82 +++--- .../composed/helpers/generateReatomFile.ts | 40 +-- .../bin/plugins/reatom/composed/plugin.ts | 4 +- .../helpers/getImportRequestParamsType.ts | 130 ---------- .../plugins/reatom/helpers/getReatomAsync.ts | 16 +- .../reatom/helpers/getReatomAsyncData.ts | 235 ++++++------------ .../plugins/reatom/helpers/getReatomImport.ts | 43 +--- .../bin/plugins/reatom/helpers/index.ts | 13 +- .../bin/plugins/reatom/standalone/plugin.ts | 72 +++--- packages/apicraft/src/types.ts | 25 +- 10 files changed, 202 insertions(+), 458 deletions(-) delete mode 100644 packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts diff --git a/packages/apicraft/bin/plugins/reatom/class/plugin.ts b/packages/apicraft/bin/plugins/reatom/class/plugin.ts index 68c42a7..12c2a7b 100644 --- a/packages/apicraft/bin/plugins/reatom/class/plugin.ts +++ b/packages/apicraft/bin/plugins/reatom/class/plugin.ts @@ -1,16 +1,16 @@ import type ts from 'typescript'; -import { capitalize, generateRequestName, getImportInstance } from '@/bin/plugins/helpers'; +import { + capitalize, + generateRequestName, + getApicraftTypeImport, + getImportInstance, + getImportRequest +} from '@/bin/plugins/helpers'; import type { ReatomPlugin } from '../types'; -import { - getImportRequestParamsTypes, - getReatomAsync, - getReatomAsyncData, - getReatomCoreImport, - getReatomSettingsTypeImport -} from '../helpers'; +import { getReatomAsync, getReatomAsyncData, getReatomImport } from '../helpers'; export const classHandler: ReatomPlugin['Handler'] = ({ plugin }) => { const reatomFile = plugin.createFile({ @@ -19,54 +19,50 @@ export const classHandler: ReatomPlugin['Handler'] = ({ plugin }) => { }); const requestParamsTypeNames: string[] = []; + const functions: ts.VariableStatement[] = []; plugin.forEach('operation', (event) => { - if (event.type !== 'operation') return; - const request = event.operation; const requestName = generateRequestName(request, plugin.config.nameBy); - requestParamsTypeNames.push(`${capitalize(requestName)}RequestParams`); + const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; + requestParamsTypeNames.push(requestParamsTypeName); + + functions.push( + getReatomAsyncData({ + plugin, + request, + requestName, + requestParamsTypeName + }), + getReatomAsync({ + plugin, + requestName, + requestParamsTypeName + }) + ); }); const imports: ts.ImportDeclaration[] = [ - getReatomCoreImport(), - getReatomSettingsTypeImport(), - getImportInstance({ - output: plugin.output, + // import type { ReatomAsyncDataSettings, ReatomAsyncSettings } from '@siberiacancode/apicraft'; + getApicraftTypeImport(['ReatomAsyncDataSettings', 'ReatomAsyncSettings']), + // import { action, computed, wrap, withAsync, withAsyncData } from '@reatom/core'; + getReatomImport(['action', 'computed', 'wrap', 'withAsync', 'withAsyncData']), + // import { RequestNameRequestParams } from './instance.gen'; + getImportRequest({ folderPath: plugin.output, + requestFilePath: `${plugin.output}/instance`, + requestName: requestParamsTypeNames, generateOutput: plugin.config.generateOutput }), - getImportRequestParamsTypes({ - folderPath: plugin.output, - generateOutput: plugin.config.generateOutput, + // import { instance } from './instance.gen'; + getImportInstance({ output: plugin.output, - requestParamsTypeNames + folderPath: plugin.output, + generateOutput: plugin.config.generateOutput }) ]; reatomFile.add(...imports); - - plugin.forEach('operation', (event) => { - if (event.type !== 'operation') return; - - const request = event.operation; - const requestName = generateRequestName(request, plugin.config.nameBy); - const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; - - reatomFile.add( - getReatomAsyncData({ - request, - requestName, - requestParamsTypeName, - requestRef: 'instance' - }) - ); - reatomFile.add( - getReatomAsync({ - requestName, - requestParamsTypeName, - requestRef: 'instance' - }) - ); - }); + // export const requestNameAsyncData / requestNameAsync + reatomFile.add(...functions); }; diff --git a/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts b/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts index 97aa84c..8cbd3d1 100644 --- a/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts +++ b/packages/apicraft/bin/plugins/reatom/composed/helpers/generateReatomFile.ts @@ -2,17 +2,11 @@ import type { IR } from '@hey-api/openapi-ts'; import * as nodePath from 'node:path'; -import { capitalize, getImportRequest } from '@/bin/plugins/helpers'; +import { capitalize, getApicraftTypeImport, getImportRequest } from '@/bin/plugins/helpers'; import type { ReatomPlugin } from '../../types'; -import { - getImportRequestParamsTypeFromRequestFile, - getReatomAsync, - getReatomAsyncData, - getReatomCoreImport, - getReatomSettingsTypeImport -} from '../../helpers'; +import { getReatomAsync, getReatomAsyncData, getReatomImport } from '../../helpers'; interface GenerateReatomFileParams { plugin: ReatomPlugin['Instance']; @@ -29,16 +23,20 @@ export const generateReatomFile = ({ }: GenerateReatomFileParams) => { const reatomFilePath = `${nodePath.dirname(requestFilePath).replace('requests', 'reatom')}/${requestName}`; const reatomFolderPath = nodePath.dirname(`${plugin.config.generateOutput}/${reatomFilePath}`); + const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; const reatomFile = plugin.createFile({ id: requestName, path: reatomFilePath }); - const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; + // import type { ReatomAsyncDataSettings, ReatomAsyncSettings } from '@siberiacancode/apicraft'; + reatomFile.add(getApicraftTypeImport(['ReatomAsyncDataSettings', 'ReatomAsyncSettings'])); + + // import { action, computed, wrap, withAsync, withAsyncData } from '@reatom/core'; + reatomFile.add(getReatomImport(['action', 'computed', 'wrap', 'withAsync', 'withAsyncData'])); - reatomFile.add(getReatomCoreImport()); - reatomFile.add(getReatomSettingsTypeImport()); + // import { requestName } from './requestName.gen'; reatomFile.add( getImportRequest({ folderPath: reatomFolderPath, @@ -47,27 +45,33 @@ export const generateReatomFile = ({ generateOutput: plugin.config.generateOutput }) ); + + // import { RequestNameRequestParams } from './requestName.gen'; reatomFile.add( - getImportRequestParamsTypeFromRequestFile({ + getImportRequest({ folderPath: reatomFolderPath, - generateOutput: plugin.config.generateOutput, requestFilePath, - requestParamsTypeName + requestName: requestParamsTypeName, + generateOutput: plugin.config.generateOutput }) ); + + // export const requestNameAsyncData = (...) => computed(...).extend(withAsyncData(...)); reatomFile.add( getReatomAsyncData({ + plugin, request, requestName, - requestParamsTypeName, - requestRef: 'function' + requestParamsTypeName }) ); + + // export const requestNameAsync = (...) => action(...).extend(withAsync(...)); reatomFile.add( getReatomAsync({ + plugin, requestName, - requestParamsTypeName, - requestRef: 'function' + requestParamsTypeName }) ); }; diff --git a/packages/apicraft/bin/plugins/reatom/composed/plugin.ts b/packages/apicraft/bin/plugins/reatom/composed/plugin.ts index baabb26..ebf6067 100644 --- a/packages/apicraft/bin/plugins/reatom/composed/plugin.ts +++ b/packages/apicraft/bin/plugins/reatom/composed/plugin.ts @@ -6,8 +6,6 @@ import { generateReatomFile } from './helpers'; export const composedHandler: ReatomPlugin['Handler'] = ({ plugin }) => plugin.forEach('operation', (event) => { - if (event.type !== 'operation') return; - const request = event.operation; const requestName = generateRequestName(request, plugin.config.nameBy); @@ -18,5 +16,5 @@ export const composedHandler: ReatomPlugin['Handler'] = ({ plugin }) => request }); - generateReatomFile({ plugin, requestFilePath, request, requestName }); + generateReatomFile({ plugin, request, requestFilePath, requestName }); }); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts b/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts deleted file mode 100644 index 1774223..0000000 --- a/packages/apicraft/bin/plugins/reatom/helpers/getImportRequestParamsType.ts +++ /dev/null @@ -1,130 +0,0 @@ -import nodePath from 'node:path'; -import ts from 'typescript'; - -import { getRelativePath } from '@/bin/plugins/helpers'; - -interface GetImportRequestParamsTypeParams { - folderPath: string; - generateOutput: string; - output: string; - requestParamsTypeName: string; - runtimeInstancePath?: string; -} - -/** Import type from instance.gen (class mode) */ -export const getImportRequestParamsType = ({ - folderPath, - generateOutput, - output, - requestParamsTypeName, - runtimeInstancePath -}: GetImportRequestParamsTypeParams) => - ts.factory.createImportDeclaration( - undefined, - ts.factory.createImportClause( - true, - undefined, - ts.factory.createNamedImports([ - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier(requestParamsTypeName) - ) - ]) - ), - ts.factory.createStringLiteral( - getRelativePath( - folderPath, - runtimeInstancePath ?? nodePath.normalize(`${generateOutput}/${output}/instance.gen`) - ) - ) - ); - -export const getImportRequestParamsTypes = (params: { - folderPath: string; - generateOutput: string; - output: string; - requestParamsTypeNames: string[]; - runtimeInstancePath?: string; -}) => - ts.factory.createImportDeclaration( - undefined, - ts.factory.createImportClause( - true, - undefined, - ts.factory.createNamedImports( - params.requestParamsTypeNames.map((name) => - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)) - ) - ) - ), - ts.factory.createStringLiteral( - getRelativePath( - params.folderPath, - params.runtimeInstancePath ?? - nodePath.normalize(`${params.generateOutput}/${params.output}/instance.gen`) - ) - ) - ); - -interface GetImportRequestParamsTypeFromRequestFileParams { - folderPath: string; - generateOutput: string; - requestFilePath: string; - requestParamsTypeName: string; -} - -/** Import type from request file (composed mode) */ -export const getImportRequestParamsTypeFromRequestFile = ({ - folderPath, - generateOutput, - requestFilePath, - requestParamsTypeName -}: GetImportRequestParamsTypeFromRequestFileParams) => - ts.factory.createImportDeclaration( - undefined, - ts.factory.createImportClause( - true, - undefined, - ts.factory.createNamedImports([ - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier(requestParamsTypeName) - ) - ]) - ), - ts.factory.createStringLiteral( - getRelativePath(folderPath, `${generateOutput}/${requestFilePath}.gen`) - ) - ); - -interface GetImportRequestParamsTypesFromRequestFileParams { - folderPath: string; - generateOutput: string; - requestFilePath: string; - requestParamsTypeNames: string[]; -} - -/** Import types from request file (standalone mode) */ -export const getImportRequestParamsTypesFromRequestFile = ({ - folderPath, - generateOutput, - requestFilePath, - requestParamsTypeNames -}: GetImportRequestParamsTypesFromRequestFileParams) => - ts.factory.createImportDeclaration( - undefined, - ts.factory.createImportClause( - true, - undefined, - ts.factory.createNamedImports( - requestParamsTypeNames.map((name) => - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)) - ) - ) - ), - ts.factory.createStringLiteral( - getRelativePath(folderPath, `${generateOutput}/${requestFilePath}.gen`) - ) - ); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts index 0f6b43b..f1ee98e 100644 --- a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsync.ts @@ -1,22 +1,22 @@ import ts from 'typescript'; -import type { RequestRef } from './getReatomAsyncData'; +import type { ReatomPlugin } from '../types'; interface GetReatomAsyncParams { + plugin: ReatomPlugin['Instance']; requestName: string; requestParamsTypeName: string; - requestRef: RequestRef; } export const getReatomAsync = ({ + plugin, requestName, - requestParamsTypeName, - requestRef + requestParamsTypeName }: GetReatomAsyncParams) => { const asyncName = `${requestName}Async`; const requestCall = - requestRef === 'instance' + plugin.config.groupBy === 'class' ? ts.factory.createCallExpression( ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('instance'), @@ -55,8 +55,8 @@ export const getReatomAsync = ({ ) ]); - const settingsTypeNode = - requestRef === 'instance' + const requestTypeNode = + plugin.config.groupBy === 'class' ? ts.factory.createTypeQueryNode( ts.factory.createQualifiedName( ts.factory.createIdentifier('instance'), @@ -133,7 +133,7 @@ export const getReatomAsync = ({ ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode( ts.factory.createIdentifier('ReatomAsyncSettings'), - [settingsTypeNode] + [requestTypeNode] ) ) ], diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts index 2279dff..4451066 100644 --- a/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomAsyncData.ts @@ -2,43 +2,28 @@ import type { IR } from '@hey-api/openapi-ts'; import ts from 'typescript'; -import { capitalize, getRequestInfo } from '@/bin/plugins/helpers'; +import { getRequestInfo } from '@/bin/plugins/helpers'; -export type RequestRef = 'function' | 'instance'; +import type { ReatomPlugin } from '../types'; interface GetReatomAsyncDataParams { + plugin: ReatomPlugin['Instance']; request: IR.OperationObject; requestName: string; requestParamsTypeName: string; - requestRef: RequestRef; -} - -function getRequestFieldsForOperation(request: IR.OperationObject): string[] { - const hasPathParam = !!Object.keys(request.parameters?.path ?? {}).length; - const hasQueryParam = !!Object.keys(request.parameters?.query ?? {}).length; - const hasHeaderParam = !!Object.keys(request.parameters?.header ?? {}).length; - const hasBody = !!request.body; - const fields: string[] = []; - if (hasPathParam) fields.push('path'); - if (hasQueryParam) fields.push('query'); - if (hasHeaderParam) fields.push('headers'); - if (hasBody) fields.push('body'); - fields.push('config'); - return fields; } export const getReatomAsyncData = ({ + plugin, request, requestName, - requestParamsTypeName, - requestRef + requestParamsTypeName }: GetReatomAsyncDataParams) => { const requestInfo = getRequestInfo(request); - const requestFields = getRequestFieldsForOperation(request); - const asyncDataName = `create${capitalize(requestName)}AsyncData`; + const asyncDataName = `${requestName}AsyncData`; - const settingsTypeNode = - requestRef === 'instance' + const requestTypeNode = + plugin.config.groupBy === 'class' ? ts.factory.createTypeQueryNode( ts.factory.createQualifiedName( ts.factory.createIdentifier('instance'), @@ -47,66 +32,27 @@ export const getReatomAsyncData = ({ ) : ts.factory.createTypeQueryNode(ts.factory.createIdentifier(requestName)); - const requestCallArg = ts.factory.createObjectLiteralExpression( - requestFields.map((field) => - ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(field)) - ), - false - ); - const requestCall = - requestRef === 'instance' + plugin.config.groupBy === 'class' ? ts.factory.createCallExpression( ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier('instance'), ts.factory.createIdentifier(requestName) ), undefined, - [requestCallArg] + [ts.factory.createIdentifier('normalizedRequest')] ) : ts.factory.createCallExpression(ts.factory.createIdentifier(requestName), undefined, [ - requestCallArg + ts.factory.createIdentifier('normalizedRequest') ]); - const requestAccess = requestInfo.hasRequiredParam - ? ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('settings'), - ts.factory.createIdentifier('request') - ) - : ts.factory.createPropertyAccessChain( - ts.factory.createIdentifier('settings'), - ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), - ts.factory.createIdentifier('request') - ); - - const requestVariable = ts.factory.createVariableStatement( + // const unwrap = (value: unknown): unknown => { ... } + const unwrapHelper = ts.factory.createVariableStatement( undefined, ts.factory.createVariableDeclarationList( [ ts.factory.createVariableDeclaration( - ts.factory.createIdentifier('request'), - undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), - ts.factory.createAsExpression( - ts.factory.createBinaryExpression( - requestAccess, - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createObjectLiteralExpression([], false) - ), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) - ) - ) - ], - ts.NodeFlags.Const - ) - ); - - const unwrapReactiveObjectHelper = ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createIdentifier('unwrapReactiveObject'), + ts.factory.createIdentifier('unwrap'), undefined, undefined, ts.factory.createArrowFunction( @@ -118,13 +64,32 @@ export const getReatomAsyncData = ({ undefined, ts.factory.createIdentifier('value'), undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) ) ], - ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword), ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock( [ + ts.factory.createIfStatement( + ts.factory.createBinaryExpression( + ts.factory.createTypeOfExpression(ts.factory.createIdentifier('value')), + ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), + ts.factory.createStringLiteral('function') + ), + ts.factory.createBlock( + [ + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + ts.factory.createIdentifier('value'), + undefined, + [] + ) + ) + ], + true + ) + ), ts.factory.createIfStatement( ts.factory.createCallExpression( ts.factory.createPropertyAccessExpression( @@ -151,7 +116,7 @@ export const getReatomAsyncData = ({ undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression( - ts.factory.createIdentifier('unwrapReactiveObject'), + ts.factory.createIdentifier('unwrap'), undefined, [ts.factory.createIdentifier('item')] ) @@ -223,46 +188,10 @@ export const getReatomAsyncData = ({ ts.factory.createArrayLiteralExpression( [ ts.factory.createIdentifier('key'), - ts.factory.createConditionalExpression( - ts.factory.createBinaryExpression( - ts.factory.createTypeOfExpression( - ts.factory.createIdentifier('entry') - ), - ts.factory.createToken( - ts.SyntaxKind.EqualsEqualsEqualsToken - ), - ts.factory.createStringLiteral('function') - ), - undefined, - ts.factory.createConditionalExpression( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, - ts.factory.createPropertyAccessChain( - ts.factory.createIdentifier('entry'), - ts.factory.createToken( - ts.SyntaxKind.QuestionDotToken - ), - ts.factory.createIdentifier('__reatom') - ) - ) - ), - undefined, - ts.factory.createCallExpression( - ts.factory.createIdentifier('entry'), - undefined, - [] - ), - undefined, - ts.factory.createIdentifier('entry') - ), + ts.factory.createCallExpression( + ts.factory.createIdentifier('unwrap'), undefined, - ts.factory.createCallExpression( - ts.factory.createIdentifier('unwrapReactiveObject'), - undefined, - [ts.factory.createIdentifier('entry')] - ) + [ts.factory.createIdentifier('entry')] ) ], false @@ -288,60 +217,48 @@ export const getReatomAsyncData = ({ ) ); - const createRequestFieldStatement = (field: string): ts.Statement => { - const requestFieldAccess = ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier('request'), - ts.factory.createIdentifier(field) - ); - - const valueExpression = - field === 'config' - ? requestFieldAccess - : ts.factory.createCallExpression( - ts.factory.createIdentifier('unwrapReactiveObject'), - undefined, - [requestFieldAccess] - ); + const requestAccess = requestInfo.hasRequiredParam + ? ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('settings'), + ts.factory.createIdentifier('request') + ) + : ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('settings'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('request') + ); - return ts.factory.createVariableStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createIdentifier(field), - undefined, - ts.factory.createIndexedAccessTypeNode( - ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier(requestParamsTypeName) - ), - ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field)) - ), - ts.factory.createAsExpression( - valueExpression, - ts.factory.createIndexedAccessTypeNode( - ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier(requestParamsTypeName) - ), - ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(field)) + // const normalizedRequest = unwrap(settings.request ?? {}) as RequestParams; + const normalizedRequestVariable = ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('normalizedRequest'), + undefined, + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(requestParamsTypeName)), + ts.factory.createAsExpression( + ts.factory.createCallExpression(ts.factory.createIdentifier('unwrap'), undefined, [ + ts.factory.createBinaryExpression( + requestAccess, + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false) ) - ) + ]), + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(requestParamsTypeName)) ) - ], - ts.NodeFlags.Const - ) - ); - }; + ) + ], + ts.NodeFlags.Const + ) + ); const computedBody: ts.Statement[] = [ - requestVariable, - unwrapReactiveObjectHelper, - ...requestFields.map((field) => createRequestFieldStatement(field)), + unwrapHelper, + normalizedRequestVariable, + // return wrap(requestName(normalizedRequest)); ts.factory.createReturnStatement( - ts.factory.createAwaitExpression( - ts.factory.createCallExpression(ts.factory.createIdentifier('wrap'), undefined, [ - requestCall - ]) - ) + ts.factory.createCallExpression(ts.factory.createIdentifier('wrap'), undefined, [requestCall]) ) ]; @@ -399,7 +316,7 @@ export const getReatomAsyncData = ({ : undefined, ts.factory.createTypeReferenceNode( ts.factory.createIdentifier('ReatomAsyncDataSettings'), - [settingsTypeNode] + [requestTypeNode] ) ) ], diff --git a/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts b/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts index f1a662e..42a91a4 100644 --- a/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts +++ b/packages/apicraft/bin/plugins/reatom/helpers/getReatomImport.ts @@ -1,48 +1,17 @@ import ts from 'typescript'; -export const getReatomCoreImport = () => +// import { name } from '@reatom/core'; +export const getReatomImport = (name: string | string[]) => ts.factory.createImportDeclaration( undefined, ts.factory.createImportClause( false, undefined, - ts.factory.createNamedImports([ - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('action')), - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('computed')), - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier('wrap')), - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier('withAsync') - ), - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier('withAsyncData') + ts.factory.createNamedImports( + (Array.isArray(name) ? name : [name]).map((name) => + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name)) ) - ]) + ) ), ts.factory.createStringLiteral('@reatom/core') ); - -export const getReatomSettingsTypeImport = () => - ts.factory.createImportDeclaration( - undefined, - ts.factory.createImportClause( - true, - undefined, - ts.factory.createNamedImports([ - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier('ReatomAsyncDataSettings') - ), - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier('ReatomAsyncSettings') - ) - ]) - ), - ts.factory.createStringLiteral('@siberiacancode/apicraft') - ); diff --git a/packages/apicraft/bin/plugins/reatom/helpers/index.ts b/packages/apicraft/bin/plugins/reatom/helpers/index.ts index 23a8bfc..df2e7ca 100644 --- a/packages/apicraft/bin/plugins/reatom/helpers/index.ts +++ b/packages/apicraft/bin/plugins/reatom/helpers/index.ts @@ -1,10 +1,3 @@ -export { - getImportRequestParamsType, - getImportRequestParamsTypeFromRequestFile, - getImportRequestParamsTypes, - getImportRequestParamsTypesFromRequestFile -} from './getImportRequestParamsType'; -export { getReatomAsync } from './getReatomAsync'; -export type { RequestRef } from './getReatomAsyncData'; -export { getReatomAsyncData } from './getReatomAsyncData'; -export { getReatomCoreImport, getReatomSettingsTypeImport } from './getReatomImport'; +export * from './getReatomAsync'; +export * from './getReatomAsyncData'; +export * from './getReatomImport'; diff --git a/packages/apicraft/bin/plugins/reatom/standalone/plugin.ts b/packages/apicraft/bin/plugins/reatom/standalone/plugin.ts index ae55f3b..d1107cf 100644 --- a/packages/apicraft/bin/plugins/reatom/standalone/plugin.ts +++ b/packages/apicraft/bin/plugins/reatom/standalone/plugin.ts @@ -1,16 +1,15 @@ import type ts from 'typescript'; -import { capitalize, generateRequestName, getImportRequest } from '@/bin/plugins/helpers'; +import { + capitalize, + generateRequestName, + getApicraftTypeImport, + getImportRequest +} from '@/bin/plugins/helpers'; import type { ReatomPlugin } from '../types'; -import { - getImportRequestParamsTypesFromRequestFile, - getReatomAsync, - getReatomAsyncData, - getReatomCoreImport, - getReatomSettingsTypeImport -} from '../helpers'; +import { getReatomAsync, getReatomAsyncData, getReatomImport } from '../helpers'; export const standaloneHandler: ReatomPlugin['Handler'] = ({ plugin }) => { const reatomFile = plugin.createFile({ @@ -20,59 +19,52 @@ export const standaloneHandler: ReatomPlugin['Handler'] = ({ plugin }) => { const requestImportNames: string[] = []; const requestParamsTypeNames: string[] = []; + const functions: ts.VariableStatement[] = []; plugin.forEach('operation', (event) => { - if (event.type !== 'operation') return; - const request = event.operation; const requestName = generateRequestName(request, plugin.config.nameBy); const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; requestImportNames.push(requestName); requestParamsTypeNames.push(requestParamsTypeName); + functions.push( + getReatomAsyncData({ + plugin, + request, + requestName, + requestParamsTypeName + }), + getReatomAsync({ + plugin, + requestName, + requestParamsTypeName + }) + ); }); const imports: ts.ImportDeclaration[] = [ - getReatomCoreImport(), - getReatomSettingsTypeImport(), + // import type { ReatomAsyncDataSettings, ReatomAsyncSettings } from '@siberiacancode/apicraft'; + getApicraftTypeImport(['ReatomAsyncDataSettings', 'ReatomAsyncSettings']), + // import { action, computed, wrap, withAsync, withAsyncData } from '@reatom/core'; + getReatomImport(['action', 'computed', 'wrap', 'withAsync', 'withAsyncData']), + // import { requestName1, requestName2 } from './requests.gen'; getImportRequest({ folderPath: plugin.config.generateOutput, requestFilePath: `${plugin.output}/requests`, requestName: requestImportNames, generateOutput: plugin.config.generateOutput }), - getImportRequestParamsTypesFromRequestFile({ + // import { RequestNameRequestParams } from './requests.gen'; + getImportRequest({ folderPath: plugin.config.generateOutput, - generateOutput: plugin.config.generateOutput, requestFilePath: `${plugin.output}/requests`, - requestParamsTypeNames + requestName: requestParamsTypeNames, + generateOutput: plugin.config.generateOutput }) ]; reatomFile.add(...imports); - - plugin.forEach('operation', (event) => { - if (event.type !== 'operation') return; - - const request = event.operation; - const requestName = generateRequestName(request, plugin.config.nameBy); - const requestParamsTypeName = `${capitalize(requestName)}RequestParams`; - - reatomFile.add( - getReatomAsyncData({ - request, - requestName, - requestParamsTypeName, - requestRef: 'function' - }) - ); - - reatomFile.add( - getReatomAsync({ - requestName, - requestParamsTypeName, - requestRef: 'function' - }) - ); - }); + // export const requestNameAsyncData / requestNameAsync + reatomFile.add(...functions); }; diff --git a/packages/apicraft/src/types.ts b/packages/apicraft/src/types.ts index 350222f..b74b156 100644 --- a/packages/apicraft/src/types.ts +++ b/packages/apicraft/src/types.ts @@ -40,13 +40,18 @@ export interface TanstackMutationSettings Prom request?: NonNullable[0]>; } -export type ReatomAtom = (() => TValue) & { __reatom: unknown }; -export type ReatomAtomized = ReatomAtom | TValue; -export type ReatomDeepAtomized = TValue extends readonly (infer Item)[] - ? ReatomDeepAtomized[] - : TValue extends object - ? { [Key in keyof TValue]: ReatomAtom | ReatomDeepAtomized } - : ReatomAtomized; +export type ReatomAtom = (() => TValue) | TValue; +export type ReatomDeepAtomized = + | ([NonNullable] extends [readonly (infer Item)[]] + ? ReatomAtom> | ReatomDeepAtomized[] + : [NonNullable] extends [object] + ? + | ReatomAtom> + | { + [Key in keyof NonNullable]: ReatomDeepAtomized[Key]>; + } + : ReatomAtom>) + | Extract; export interface ReatomAsyncDataSettings Promise> { params?: AsyncDataOptions< @@ -57,9 +62,9 @@ export interface ReatomAsyncDataSettings Promi undefined >; request?: Partial<{ - [Key in keyof NonNullable[0]>]: Key extends 'config' - ? NonNullable[0]>[Key] - : ReatomDeepAtomized[0]>[Key]>; + [Key in keyof NonNullable[0]>]: ReatomDeepAtomized< + NonNullable[0]>[Key] + >; }>; }