UNPKG

@vendure/ngx-translate-extract

Version:
177 lines (176 loc) 7.71 kB
import { extname } from 'node:path'; import { ScriptKind, tsquery } from '@phenomnomnominal/tsquery'; import pkg from 'typescript'; const { isArrayLiteralExpression, isBinaryExpression, isCallExpression, isConditionalExpression, isPropertyAccessExpression, isStringLiteralLike, SyntaxKind } = pkg; export function getAST(source, fileName = '') { const supportedScriptTypes = { '.js': ScriptKind.JS, '.jsx': ScriptKind.JSX, '.ts': ScriptKind.TS, '.tsx': ScriptKind.TSX }; const scriptKind = supportedScriptTypes[extname(fileName)] ?? ScriptKind.TS; return tsquery.ast(source, fileName, scriptKind); } export function getNamedImportIdentifiers(node, moduleName, importPath) { const importStringLiteralValue = importPath instanceof RegExp ? `value=${importPath.toString()}` : `value="${importPath}"`; const query = `ImportDeclaration:has(StringLiteral[${importStringLiteralValue}]) ImportSpecifier:has(Identifier[name="${moduleName}"]) > Identifier`; return tsquery(node, query); } export function getNamedImport(node, importName, importPath) { const identifiers = getNamedImportIdentifiers(node, importName, importPath); return identifiers.at(0)?.text ?? null; } export function getNamedImportAlias(node, importName, importPath) { const identifiers = getNamedImportIdentifiers(node, importName, importPath); return identifiers.at(-1)?.text ?? null; } export function findClassDeclarations(node, name = null) { let query = 'ClassDeclaration'; if (name) { query += `:has(Identifier[name="${name}"])`; } return tsquery(node, query); } export function findFunctionExpressions(node) { return tsquery(node, 'VariableDeclaration ArrowFunction, VariableDeclaration FunctionExpression'); } export function getSuperClassName(node) { const query = 'ClassDeclaration > HeritageClause Identifier'; const [result] = tsquery(node, query); return result?.text; } export function getImportPath(node, className) { const query = `ImportDeclaration:has(Identifier[name="${className}"]) StringLiteral`; const [result] = tsquery(node, query); return result?.text; } export function findClassPropertiesByType(node, type) { return [ ...findClassPropertiesConstructorParameterByType(node, type), ...findClassPropertiesDeclarationByType(node, type), ...findClassPropertiesDeclarationByInject(node, type), ...findClassPropertiesGetterByType(node, type) ]; } export function findConstructorDeclaration(node) { const query = 'Constructor'; const [result] = tsquery(node, query); return result; } export function findMethodParameterByType(node, type) { const query = `Parameter:has(TypeReference > Identifier[name="${type}"]) > Identifier`; const [result] = tsquery(node, query); if (result) { return result.text; } return null; } export function findVariableNameByInjectType(node, type) { const query = `VariableDeclaration:has(Identifier[name="inject"]):has(CallExpression > Identifier[name="${type}"]) > Identifier`; const [result] = tsquery(node, query); return result?.text ?? null; } export function findMethodCallExpressions(node, propName, fnName) { const functionNames = typeof fnName === 'string' ? [fnName] : fnName; const fnNameRegex = functionNames.join('|'); const query = `CallExpression > PropertyAccessExpression:has(Identifier[name=/^(${fnNameRegex})$/]):has(PropertyAccessExpression:has(Identifier[name="${propName}"]):not(:has(ThisKeyword)), CallExpression:has(Identifier[name="inject"]))`; return tsquery(node, query) .filter((n) => functionNames.includes(n.getLastToken().getText())) .map((n) => n.parent); } export function findClassPropertiesConstructorParameterByType(node, type) { const query = `Constructor Parameter:has(TypeReference > Identifier[name="${type}"]):has(PublicKeyword,ProtectedKeyword,PrivateKeyword,ReadonlyKeyword) > Identifier`; const result = tsquery(node, query); return result.map((n) => n.text); } export function findClassPropertiesDeclarationByType(node, type) { const query = `PropertyDeclaration:has(TypeReference > Identifier[name="${type}"])`; const result = tsquery(node, query); return result.map((n) => n.name.getText()); } export function findClassPropertiesDeclarationByInject(node, type) { const query = `PropertyDeclaration:has(CallExpression > Identifier[name="inject"]):has(CallExpression > Identifier[name="${type}"])`; const result = tsquery(node, query); return result.map((n) => n.name.getText()); } export function findClassPropertiesGetterByType(node, type) { const query = `GetAccessor:has(TypeReference > Identifier[name="${type}"]) > Identifier`; const result = tsquery(node, query); return result.map((n) => n.text); } export function findFunctionCallExpressions(node, fnName) { if (Array.isArray(fnName)) { fnName = fnName.join('|'); } const query = `CallExpression:has(Identifier[name="${fnName}"]):not(:has(PropertyAccessExpression))`; return tsquery(node, query); } export function findSimpleCallExpressions(node, fnName) { if (Array.isArray(fnName)) { fnName = fnName.join('|'); } const query = `CallExpression:has(Identifier[name="${fnName}"])`; return tsquery(node, query); } export function findPropertyCallExpressions(node, prop, fnName) { if (Array.isArray(fnName)) { fnName = fnName.join('|'); } const query = `CallExpression > PropertyAccessExpression:has(Identifier[name=/^(${fnName})$/]):has(PropertyAccessExpression:has(ThisKeyword))`; const result = tsquery(node, query); const nodes = []; result.forEach((n) => { const identifier = isPropertyAccessExpression(n.expression) ? n.expression.name : null; const property = identifier?.parent; const method = property?.parent; const callExpression = method?.parent; if (identifier?.getText() === prop && isCallExpression(callExpression)) { nodes.push(callExpression); } }); return nodes; } export function getStringsFromExpression(expression) { if (isStringLiteralLike(expression) && expression.text !== '') { return [expression.text]; } if (isArrayLiteralExpression(expression)) { return expression.elements.reduce((result, element) => { const strings = getStringsFromExpression(element); return [...result, ...strings]; }, []); } if (isBinaryExpression(expression)) { const [left] = getStringsFromExpression(expression.left); const [right] = getStringsFromExpression(expression.right); if (expression.operatorToken.kind === SyntaxKind.PlusToken) { if (typeof left === 'string' && typeof right === 'string') { return [left + right]; } } if (expression.operatorToken.kind === SyntaxKind.BarBarToken) { const result = []; if (typeof left === 'string') { result.push(left); } if (typeof right === 'string') { result.push(right); } return result; } } if (isConditionalExpression(expression)) { const [whenTrue] = getStringsFromExpression(expression.whenTrue); const [whenFalse] = getStringsFromExpression(expression.whenFalse); const result = []; if (typeof whenTrue === 'string') { result.push(whenTrue); } if (typeof whenFalse === 'string') { result.push(whenFalse); } return result; } return []; }