UNPKG

@query-key-gen/generator

Version:

Generated for [Vite](https://vitejs.dev)

598 lines (482 loc) 23.2 kB
import ts from 'typescript'; import { IQueryNodeType, QueryNodeKind } from '../types'; import { UseQueryFinder } from './UseQueryFinder'; import { logger } from '../utils'; namespace QueryKeyFinder { export type IFindQueryKeyReturn = ReturnType<typeof findQueryKey>; const PROPERTY_NAME = 'queryKey'; export const findQueryKey = ({ useQueries, checker }: { useQueries: UseQueryFinder.IFindUserQueryReturn; checker: ts.TypeChecker; }) => { const result = useQueries .map(value => { const arrayLiteral = visitor(value, checker); return arrayLiteral; }) .flat(); return result; }; const visitor = (value: ts.CallExpression, checker: ts.TypeChecker) => { const arrayLiteral: IQueryNodeType[][] = []; const visit = (node: ts.Node) => { if (ts.isCallExpression(node)) { const expression = node.expression; const args = node.arguments; args.forEach(arg => { // useQuery(exampleQueryService.queryOptions); // useQuery(exampleQueryService.queryOptions()); if (ts.isPropertyAccessExpression(arg)) { const symbol = checker.getSymbolAtLocation(arg); if (symbol && symbol.valueDeclaration) { const valueDeclaration = symbol.valueDeclaration; if (ts.isPropertyAssignment(valueDeclaration)) { const initializer = valueDeclaration.initializer; if (initializer && ts.isCallExpression(initializer)) { visit(initializer); } } } } // useQuery(exampleQueryService.queryOptions()); if (ts.isCallExpression(arg)) { const expression = arg.expression; visit(expression); } // useQuery(exampleQueryService); if (ts.isIdentifier(arg)) { const symbol = checker.getSymbolAtLocation(arg); if (symbol && symbol.valueDeclaration) { const valueDeclaration = symbol.valueDeclaration; if (ts.isVariableDeclaration(valueDeclaration)) { const initializer = valueDeclaration.initializer; if (initializer) { visit(initializer); } } } } if (ts.isObjectLiteralExpression(arg)) { visit(arg); // const properties = arg.properties; // properties.forEach(property => { // if (ts.isPropertyAssignment(property)) { // const initializer = property.initializer; // if (ts.isArrayLiteralExpression(initializer)) { // arrayLiteral.push( // makeArrayLiteral(initializer.elements, checker) // ); // } // } // }); } }); if (ts.isIdentifier(expression)) { const symbol = checker.getSymbolAtLocation(expression); if (symbol && symbol.valueDeclaration) { const valueDeclaration = symbol.valueDeclaration; if (ts.isVariableDeclaration(valueDeclaration)) { const initializer = valueDeclaration.initializer; if (initializer && ts.isCallExpression(initializer)) { visit(initializer); } } } } if (ts.isPropertyAccessExpression(expression)) { const symbol = checker.getSymbolAtLocation(expression); if (symbol && symbol.valueDeclaration) { const valueDeclaration = symbol.valueDeclaration; if (ts.isPropertyAssignment(valueDeclaration)) { const initializer = valueDeclaration.initializer; if (initializer && ts.isArrowFunction(initializer)) { const body = initializer.body; if (ts.isArrayLiteralExpression(body)) { arrayLiteral.push(makeArrayLiteral(body.elements, checker)); } if (ts.isBlock(body)) { const statements = body.statements; statements.forEach(statement => { if (ts.isReturnStatement(statement)) { const expression = statement.expression; if (expression && ts.isCallExpression(expression)) { const argument = expression.arguments; const callExpression = expression.expression; argument.forEach(arg => { if (ts.isObjectLiteralExpression(arg)) { arrayLiteral.push( objectExpression(arg, checker) ); } }); visit(callExpression); } } }); } if (ts.isArrowFunction(body)) { visit(body); } if (ts.isCallExpression(body)) { visit(body); } } } } } } if (ts.isIdentifier(node)) { const identifierSymbol = checker.getSymbolAtLocation(node); if (identifierSymbol) { const valueDeclaration = identifierSymbol.valueDeclaration; if (valueDeclaration && ts.isVariableDeclaration(valueDeclaration)) { const objectInitializer = valueDeclaration.initializer; if (objectInitializer && ts.isObjectLiteralExpression(objectInitializer)) { const targetProperty = findPropertyByQueryKey( objectInitializer.properties ); if (targetProperty && ts.isPropertyAssignment(targetProperty)) { const targetInitializer = targetProperty.initializer; if (ts.isArrayLiteralExpression(targetInitializer)) { arrayLiteral.push( makeArrayLiteral(targetInitializer.elements, checker) ); } if (ts.isArrowFunction(targetInitializer)) { const body = targetInitializer.body; if (ts.isArrayLiteralExpression(body)) { arrayLiteral.push(makeArrayLiteral(body.elements, checker)); } if (ts.isBlock(body)) { const statements = body.statements; statements.forEach(statement => { visit(statement); }); } if (ts.isParenthesizedExpression(body)) { visit(body.expression); } } } } if (objectInitializer && ts.isArrowFunction(objectInitializer)) { const body = objectInitializer.body; if (ts.isCallExpression(body)) { visit(body); } if (ts.isBlock(body)) { const statements = body.statements; statements.forEach(statement => { visit(statement); }); } if (ts.isParenthesizedExpression(body)) { visit(body.expression); } } if (objectInitializer && ts.isCallExpression(objectInitializer)) { visit(objectInitializer); } } if (valueDeclaration && ts.isFunctionDeclaration(valueDeclaration)) { const body = valueDeclaration.body; if (body && ts.isBlock(body)) { visit(body); } } } } /** * useQuery({ * queryKey: ~ * }) */ /** * useQuery({ * queries: [query1, query2] * }) */ if (ts.isObjectLiteralExpression(node)) { // queries 예외 처리 const properties = node.properties; const isQueries = properties.some( property => ts.isPropertyAssignment(property) && property.name.getText() === 'queries' ); if (isQueries) { properties.forEach(property => { if ( ts.isPropertyAssignment(property) && property.name.getText() === 'queries' ) { const initializer = property.initializer; visit(initializer); } }); return; } arrayLiteral.push(objectExpression(node, checker)); } ts.forEachChild(node, visit); }; visit(value); if (arrayLiteral.flat().length === 0) { logger.warn('This function is not provided. : \n' + value.getText()); } return arrayLiteral; }; const objectExpression = (node: ts.ObjectLiteralExpression, checker: ts.TypeChecker) => { let arrayLiteral: IQueryNodeType[] = []; const properties = node.properties; properties.forEach(property => { if (ts.isPropertyAssignment(property) && property.name.getText() === 'queryKey') { const initializer = property.initializer; /** * useQuery({ * queryKey: ['keys'] * }) */ if (ts.isArrayLiteralExpression(initializer)) { arrayLiteral = makeArrayLiteral(initializer.elements, checker); } /** * useQuery({ * queryKey: keys() * }) */ if (ts.isCallExpression(initializer)) { const expression = initializer.expression; if (ts.isIdentifier(expression)) { const valueDeclaration = checker.getSymbolAtLocation(expression)?.valueDeclaration; if (valueDeclaration && ts.isVariableDeclaration(valueDeclaration)) { const initializer = valueDeclaration.initializer; if (initializer && ts.isArrowFunction(initializer)) { const body = initializer.body; if (ts.isArrayLiteralExpression(body)) { const elements = body.elements; arrayLiteral = makeArrayLiteral(elements, checker); } } } if (valueDeclaration && ts.isFunctionDeclaration(valueDeclaration)) { const body = valueDeclaration.body; if (body && ts.isBlock(body)) { const statements = body.statements; statements.forEach(statement => { if (ts.isReturnStatement(statement)) { const expression = statement.expression; if (expression && ts.isArrayLiteralExpression(expression)) { arrayLiteral = makeArrayLiteral( expression.elements, checker ); } } }); } } } } /** * const test = { * queryKey: ['1111'] * } * * useQuery({ * queryKey: test.queryKey * }) */ if (ts.isPropertyAccessExpression(initializer)) { arrayLiteral = propertyAccessExpression(initializer, checker); } /** * * const test = { queryKeys: () => ['1111'] } * useQuery({ * queryKey: test.queryKeys() * }) */ if (ts.isCallExpression(initializer)) { const expression = initializer.expression; if (ts.isPropertyAccessExpression(expression)) { arrayLiteral = propertyAccessExpression(expression, checker); } } } }); return arrayLiteral; }; const makeArrayLiteral = (elements: ts.NodeArray<ts.Expression>, checker: ts.TypeChecker) => { return elements .map(el => { if (ts.isStringLiteral(el)) { return { name: el.text, type: el, kind: QueryNodeKind.StringLiteral }; } if (ts.isIdentifier(el)) { const symbol = checker.getSymbolAtLocation(el); const type = checker.getTypeAtLocation(el); const valueDeclaration = symbol?.valueDeclaration; const typeKind = type.symbol?.valueDeclaration?.kind; /** * @description typeof keyword */ if (valueDeclaration && ts.isParameter(valueDeclaration)) { if (valueDeclaration.type?.kind === ts.SyntaxKind.TypeQuery) { return { name: el.text, type: type, symbol: symbol, kind: QueryNodeKind.TypeOfKeyword }; } // tuple type // if (valueDeclaration.type?.kind === ts.SyntaxKind.TupleType) { // console.log(type); // return { // name: el.text, // type: type, // symbol: symbol, // kind: QueryNodeKind.Tuple // }; // } if (valueDeclaration.type && ts.isTupleTypeNode(valueDeclaration.type)) { const tupleNode = valueDeclaration.type; return { name: el.text, type: tupleNode, symbol: symbol, kind: QueryNodeKind.Tuple }; } } /** * @description enum keyword */ if ( typeKind === ts.SyntaxKind.EnumMember || typeKind === ts.SyntaxKind.EnumDeclaration ) { return { name: el.text, type: type, symbol: symbol, kind: QueryNodeKind.EnumMember }; } /** * @description symbol keyword */ if (symbol && symbol.getDeclarations()) { return { name: symbol.getName(), type: symbol, kind: QueryNodeKind.Symbol }; } } return undefined; }) .filter(Boolean) as IQueryNodeType[]; }; const propertyAccessExpression = ( node: ts.PropertyAccessExpression, checker: ts.TypeChecker ) => { const expression = node.expression; const propertyName = node.name.getText(); const arrayLiteral: IQueryNodeType[] = []; if (expression && ts.isIdentifier(expression)) { const valueDeclaration = checker.getSymbolAtLocation(expression)?.valueDeclaration; if (valueDeclaration && ts.isVariableDeclaration(valueDeclaration)) { const objectInitializer = valueDeclaration.initializer; if (objectInitializer && ts.isObjectLiteralExpression(objectInitializer)) { const targetProperty = objectInitializer.properties.find( prop => ts.isPropertyAssignment(prop) && prop.name.getText() === propertyName ); if (targetProperty && ts.isPropertyAssignment(targetProperty)) { const targetInitializer = targetProperty.initializer; if (ts.isArrayLiteralExpression(targetInitializer)) { arrayLiteral.push( ...makeArrayLiteral(targetInitializer.elements, checker) ); } if (ts.isArrowFunction(targetInitializer)) { const body = targetInitializer.body; if (ts.isArrayLiteralExpression(body)) { arrayLiteral.push(...makeArrayLiteral(body.elements, checker)); } } if (ts.isCallExpression(targetInitializer)) { arrayLiteral.push(...visitor(targetInitializer, checker).flat()); } } } } } return arrayLiteral; }; /** * @description find property by queryKey recursively */ const findPropertyByQueryKey = (properties: ts.NodeArray<ts.ObjectLiteralElement>) => { for (const prop of properties) { if (ts.isPropertyAssignment(prop)) { const initializer = prop.initializer; // Arrow function 처리 if (ts.isArrowFunction(initializer)) { /** * () => { * return { * queryKey: ['key'] * } * } */ if (ts.isBlock(initializer.body)) { const statements = initializer.body.statements; for (const statement of statements) { if (ts.isReturnStatement(statement)) { const expression = statement.expression; if (expression && ts.isObjectLiteralExpression(expression)) { return findPropertyByQueryKey(expression.properties); } } } } /** * () => ({ * queryKey: ['key'] * }) */ if (ts.isParenthesizedExpression(initializer.body)) { const expression = initializer.body.expression; if (expression && ts.isObjectLiteralExpression(expression)) { return findPropertyByQueryKey(expression.properties); } } } if (ts.isObjectLiteralExpression(initializer)) { return findPropertyByQueryKey(initializer.properties); } /** * @description find queryKey return */ if (ts.isIdentifier(prop.name) && prop.name.getText() === PROPERTY_NAME) { return prop; } } } return undefined; }; } export { QueryKeyFinder };