From a6481257ec25a4b358c1f03d61fc56e8b12cf59f Mon Sep 17 00:00:00 2001 From: florianbgt Date: Thu, 7 Nov 2024 18:48:04 +0100 Subject: [PATCH] split js from ts --- packages/cli/src/api/sync.ts | 13 ++- packages/cli/src/helper/cleanup.ts | 60 ++++++++++--- packages/cli/src/helper/dependencyTree.ts | 38 +++++--- .../languages/javascript/annotations.ts | 5 +- .../helper/languages/javascript/cleanup.ts | 88 +++++++++++++------ .../helper/languages/javascript/exports.ts | 22 ++++- .../helper/languages/javascript/imports.ts | 10 ++- .../languages/typescript/annotations.ts | 9 ++ .../helper/languages/typescript/cleanup.ts | 46 ++++++++++ .../helper/languages/typescript/exports.ts | 6 ++ .../helper/languages/typescript/imports.ts | 17 ++++ packages/cli/src/helper/split.ts | 2 +- 12 files changed, 256 insertions(+), 60 deletions(-) create mode 100644 packages/cli/src/helper/languages/typescript/annotations.ts create mode 100644 packages/cli/src/helper/languages/typescript/cleanup.ts create mode 100644 packages/cli/src/helper/languages/typescript/exports.ts create mode 100644 packages/cli/src/helper/languages/typescript/imports.ts diff --git a/packages/cli/src/api/sync.ts b/packages/cli/src/api/sync.ts index 28f026a..66c5899 100644 --- a/packages/cli/src/api/sync.ts +++ b/packages/cli/src/api/sync.ts @@ -12,7 +12,8 @@ import { import Parser from "tree-sitter"; import { getParserLanguageFromFile } from "../helper/treeSitter"; import { replaceIndexesFromSourceCode } from "../helper/cleanup"; -import { getAnnotationNodes } from "../helper/languages/javascript/annotations"; +import { getJavascriptAnnotationNodes } from "../helper/languages/javascript/annotations"; +import { getTypescriptAnnotationNodes } from "../helper/languages/typescript/annotations"; export function sync(payload: z.infer) { const tree = getDependencyTree(payload.entrypointPath); @@ -49,7 +50,15 @@ export function sync(payload: z.infer) { text: string; }[] = []; - const annotationNodes = getAnnotationNodes(parser, tree.rootNode); + let annotationNodes: Parser.SyntaxNode[]; + if (language.name === "javascript") { + annotationNodes = getJavascriptAnnotationNodes(parser, tree.rootNode); + } else if (language.name === "typescript") { + annotationNodes = getTypescriptAnnotationNodes(parser, tree.rootNode); + } else { + throw new Error("Language not supported"); + } + annotationNodes.forEach((node) => { const annotation = parseNanoApiAnnotation(node.text); if ( diff --git a/packages/cli/src/helper/cleanup.ts b/packages/cli/src/helper/cleanup.ts index 8ea6aea..5326867 100644 --- a/packages/cli/src/helper/cleanup.ts +++ b/packages/cli/src/helper/cleanup.ts @@ -11,6 +11,13 @@ import { Group } from "./types"; import { getParserLanguageFromFile } from "./treeSitter"; import { getJavascriptExports } from "./languages/javascript/exports"; import assert from "assert"; +import { + cleanupTypescriptAnnotations, + cleanupTypescriptInvalidImports, + cleanupUnusedTypescriptImports, +} from "./languages/typescript/cleanup"; +import { getTypescriptExports } from "./languages/typescript/exports"; +import { getTypescriptImports } from "./languages/typescript/imports"; export function cleanupAnnotations( filePath: string, @@ -23,8 +30,10 @@ export function cleanupAnnotations( let updatedSourceCode: string; - if (["javascript", "typescript"].includes(language.name)) { + if (language.name === "javascript") { updatedSourceCode = cleanupJavascriptAnnotations(parser, sourceCode, group); + } else if (language.name === "typescript") { + updatedSourceCode = cleanupTypescriptAnnotations(parser, sourceCode, group); } else { throw new Error(`Unsupported language: ${language.language}`); } @@ -48,9 +57,12 @@ export function getExportMap(files: { path: string; sourceCode: string }[]) { const parser = new Parser(); parser.setLanguage(language); - if (["javascript", "typescript"].includes(language.name)) { + if (language.name === "javascript") { const exports = getJavascriptExports(parser, file.sourceCode); exportIdentifiersMap.set(file.path, exports); + } else if (language.name === "typescript") { + const exports = getTypescriptExports(parser, file.sourceCode); + exportIdentifiersMap.set(file.path, exports); } else { throw new Error(`Unsupported language: ${language.language}`); } @@ -79,13 +91,20 @@ export function cleanupInvalidImports( let updatedSourceCode: string = sourceCode; - if (["javascript", "typescript"].includes(language.name)) { + if (language.name === "javascript") { updatedSourceCode = cleanupJavascriptInvalidImports( parser, filePath, sourceCode, exportIdentifiersMap, ); + } else if (language.name === "typescript") { + updatedSourceCode = cleanupTypescriptInvalidImports( + parser, + filePath, + sourceCode, + exportIdentifiersMap, + ); } else { throw new Error(`Unsupported language: ${language.language}`); } @@ -99,8 +118,10 @@ export function cleanupUnusedImports(filePath: string, sourceCode: string) { parser.setLanguage(language); let updatedSourceCode: string; - if (["javascript", "typescript"].includes(language.name)) { + if (language.name === "javascript") { updatedSourceCode = cleanupUnusedJavascriptImports(parser, sourceCode); + } else if (language.name === "typescript") { + updatedSourceCode = cleanupUnusedTypescriptImports(parser, sourceCode); } else { throw new Error(`Unsupported language: ${language.language}`); } @@ -126,23 +147,36 @@ export function cleanupUnusedFiles( const parser = new Parser(); parser.setLanguage(language); - if (["javascript", "typescript"].includes(language.name)) { - let dependencies = getJavascriptImports( + let dependencies: { + node: Parser.SyntaxNode; + source: string; + importSpecifierIdentifiers: Parser.SyntaxNode[]; + importIdentifier?: Parser.SyntaxNode; + }[]; + if (language.name === "javascript") { + dependencies = getJavascriptImports( + parser, + parser.parse(file.sourceCode).rootNode, + ); + // Only keep files dependencies + dependencies = dependencies.filter((dep) => dep.source.startsWith(".")); + } else if (language.name === "typescript") { + dependencies = getTypescriptImports( parser, parser.parse(file.sourceCode).rootNode, ); // Only keep files dependencies dependencies = dependencies.filter((dep) => dep.source.startsWith(".")); - - dependencies.forEach((dep) => { - const resolvedPath = resolveFilePath(dep.source, file.path); - if (resolvedPath) { - filesToKeep.add(resolvedPath); - } - }); } else { throw new Error(`Unsupported language: ${language.language}`); } + + dependencies.forEach((dep) => { + const resolvedPath = resolveFilePath(dep.source, file.path); + if (resolvedPath) { + filesToKeep.add(resolvedPath); + } + }); }); const previousFilesLength = files.length; diff --git a/packages/cli/src/helper/dependencyTree.ts b/packages/cli/src/helper/dependencyTree.ts index e58c24c..1c8b33a 100644 --- a/packages/cli/src/helper/dependencyTree.ts +++ b/packages/cli/src/helper/dependencyTree.ts @@ -6,8 +6,9 @@ import { DependencyTree, Group } from "./types"; import { Endpoint } from "./types"; import { getParserLanguageFromFile } from "./treeSitter"; import { parseNanoApiAnnotation } from "./annotations"; -import { getAnnotationNodes } from "./languages/javascript/annotations"; +import { getJavascriptAnnotationNodes } from "./languages/javascript/annotations"; import { getJavascriptImports } from "./languages/javascript/imports"; +import { getTypescriptImports } from "./languages/typescript/imports"; export function getDependencyTree(filePath: string): DependencyTree { const sourceCode = fs.readFileSync(filePath, "utf8"); @@ -23,23 +24,29 @@ export function getDependencyTree(filePath: string): DependencyTree { const parser = new Parser(); parser.setLanguage(language); - if (["javascript", "typescript"].includes(language.name)) { - let imports = getJavascriptImports( - parser, - parser.parse(sourceCode).rootNode, - ); + let imports: { + node: Parser.SyntaxNode; + source: string; + importSpecifierIdentifiers: Parser.SyntaxNode[]; + importIdentifier?: Parser.SyntaxNode; + }[]; + if (language.name === "javascript") { + imports = getJavascriptImports(parser, parser.parse(sourceCode).rootNode); + imports = imports.filter((importPath) => importPath.source.startsWith(".")); + } else if (language.name === "typescript") { + imports = getTypescriptImports(parser, parser.parse(sourceCode).rootNode); imports = imports.filter((importPath) => importPath.source.startsWith(".")); - - imports.forEach((importPath) => { - const resolvedPath = resolveFilePath(importPath.source, filePath); - if (resolvedPath && fs.existsSync(resolvedPath)) { - dependencyTree.children.push(getDependencyTree(resolvedPath)); - } - }); } else { throw new Error(`Unsupported language: ${language.name}`); } + imports.forEach((importPath) => { + const resolvedPath = resolveFilePath(importPath.source, filePath); + if (resolvedPath && fs.existsSync(resolvedPath)) { + dependencyTree.children.push(getDependencyTree(resolvedPath)); + } + }); + return dependencyTree; } @@ -74,7 +81,10 @@ export function getEndpontsFromTree( return uniqueFilePaths; } - const annotationNodes = getAnnotationNodes(parser, parsedTree.rootNode); + const annotationNodes = getJavascriptAnnotationNodes( + parser, + parsedTree.rootNode, + ); annotationNodes.forEach((node) => { const annotation = parseNanoApiAnnotation(node.text); diff --git a/packages/cli/src/helper/languages/javascript/annotations.ts b/packages/cli/src/helper/languages/javascript/annotations.ts index 79d106f..a6c0034 100644 --- a/packages/cli/src/helper/languages/javascript/annotations.ts +++ b/packages/cli/src/helper/languages/javascript/annotations.ts @@ -1,6 +1,9 @@ import Parser from "tree-sitter"; -export function getAnnotationNodes(parser: Parser, node: Parser.SyntaxNode) { +export function getJavascriptAnnotationNodes( + parser: Parser, + node: Parser.SyntaxNode, +) { const commentQuery = new Parser.Query( parser.getLanguage(), ` diff --git a/packages/cli/src/helper/languages/javascript/cleanup.ts b/packages/cli/src/helper/languages/javascript/cleanup.ts index 590e130..086c2a8 100644 --- a/packages/cli/src/helper/languages/javascript/cleanup.ts +++ b/packages/cli/src/helper/languages/javascript/cleanup.ts @@ -6,19 +6,27 @@ import { getJavascriptImportIdentifierUsage, } from "./imports"; import { removeIndexesFromSourceCode } from "../../cleanup"; -import { getAnnotationNodes } from "./annotations"; +import { getJavascriptAnnotationNodes } from "./annotations"; import { resolveFilePath } from "../../file"; +import { getTypescriptAnnotationNodes } from "../typescript/annotations"; +import { + getTypescriptImportIdentifierUsage, + getTypescriptImports, +} from "../typescript/imports"; export function cleanupJavascriptAnnotations( parser: Parser, sourceCode: string, groupToKeep: Group, + isTypescript = false, ): string { const tree = parser.parse(sourceCode); const indexesToRemove: { startIndex: number; endIndex: number }[] = []; - const annotationNodes = getAnnotationNodes(parser, tree.rootNode); + const annotationNodes = isTypescript + ? getTypescriptAnnotationNodes(parser, tree.rootNode) + : getJavascriptAnnotationNodes(parser, tree.rootNode); annotationNodes.forEach((node) => { const annotation = parseNanoApiAnnotation(node.text); @@ -65,12 +73,15 @@ export function cleanupJavascriptInvalidImports( defaultExport?: Parser.SyntaxNode; } >, + isTypescript = false, ) { const indexesToRemove: { startIndex: number; endIndex: number }[] = []; const tree = parser.parse(sourceCode); - const depImports = getJavascriptImports(parser, tree.rootNode); + const depImports = isTypescript + ? getTypescriptImports(parser, tree.rootNode) + : getJavascriptImports(parser, tree.rootNode); // check if identifier exists in the imported file (as an export) depImports.forEach((depImport) => { // check if the import is a file, do not process external dependencies @@ -86,11 +97,17 @@ export function cleanupJavascriptInvalidImports( } if (depImport.importIdentifier && !exportsForFile.defaultExport) { - let usages = getJavascriptImportIdentifierUsage( - parser, - tree.rootNode, - depImport.importIdentifier, - ); + let usages = isTypescript + ? getTypescriptImportIdentifierUsage( + parser, + tree.rootNode, + depImport.importIdentifier, + ) + : getJavascriptImportIdentifierUsage( + parser, + tree.rootNode, + depImport.importIdentifier, + ); usages = usages.filter((usage) => { return usage.id !== depImport.importIdentifier?.id; }); @@ -114,11 +131,17 @@ export function cleanupJavascriptInvalidImports( namedExport.identifierNode.text === importSpecifier.text, ) ) { - let usages = getJavascriptImportIdentifierUsage( - parser, - tree.rootNode, - importSpecifier, - ); + let usages = isTypescript + ? getTypescriptImportIdentifierUsage( + parser, + tree.rootNode, + importSpecifier, + ) + : getJavascriptImportIdentifierUsage( + parser, + tree.rootNode, + importSpecifier, + ); usages = usages.filter((usage) => { return usage.id !== depImport.importIdentifier?.id; }); @@ -149,21 +172,30 @@ export function cleanupJavascriptInvalidImports( export function cleanupUnusedJavascriptImports( parser: Parser, sourceCode: string, + isTypescript = false, ) { const tree = parser.parse(sourceCode); - const imports = getJavascriptImports(parser, tree.rootNode); + const imports = isTypescript + ? getTypescriptImports(parser, tree.rootNode) + : getJavascriptImports(parser, tree.rootNode); const indexesToRemove: { startIndex: number; endIndex: number }[] = []; imports.forEach((depImport) => { const importSpecifierToRemove: Parser.SyntaxNode[] = []; depImport.importSpecifierIdentifiers.forEach((importSpecifier) => { - let usages = getJavascriptImportIdentifierUsage( - parser, - tree.rootNode, - importSpecifier, - ); + let usages = isTypescript + ? getTypescriptImportIdentifierUsage( + parser, + tree.rootNode, + importSpecifier, + ) + : getJavascriptImportIdentifierUsage( + parser, + tree.rootNode, + importSpecifier, + ); usages = usages.filter((usage) => { return usage.id !== importSpecifier.id; }); @@ -175,11 +207,17 @@ export function cleanupUnusedJavascriptImports( let removeDefaultImport = false; if (depImport.importIdentifier) { - let usages = getJavascriptImportIdentifierUsage( - parser, - tree.rootNode, - depImport.importIdentifier, - ); + let usages = isTypescript + ? getTypescriptImportIdentifierUsage( + parser, + tree.rootNode, + depImport.importIdentifier, + ) + : getJavascriptImportIdentifierUsage( + parser, + tree.rootNode, + depImport.importIdentifier, + ); usages = usages.filter((usage) => { return usage.id !== depImport.importIdentifier?.id; }); @@ -192,7 +230,7 @@ export function cleanupUnusedJavascriptImports( if ( importSpecifierToRemove.length === depImport.importSpecifierIdentifiers.length && - removeDefaultImport + (removeDefaultImport || !depImport.importIdentifier) ) { indexesToRemove.push({ startIndex: depImport.node.startIndex, diff --git a/packages/cli/src/helper/languages/javascript/exports.ts b/packages/cli/src/helper/languages/javascript/exports.ts index 24bb78f..176904e 100644 --- a/packages/cli/src/helper/languages/javascript/exports.ts +++ b/packages/cli/src/helper/languages/javascript/exports.ts @@ -1,6 +1,10 @@ import Parser from "tree-sitter"; -export function getJavascriptExports(parser: Parser, sourceCode: string) { +export function getJavascriptExports( + parser: Parser, + sourceCode: string, + isTypescript = false, +) { const tree = parser.parse(sourceCode); const exportQuery = new Parser.Query( @@ -22,8 +26,8 @@ export function getJavascriptExports(parser: Parser, sourceCode: string) { exportCaptures.forEach((capture) => { const identifierQuery = new Parser.Query( parser.getLanguage(), - // TODO type_identifier only valid for typescript, not javascript - ` + isTypescript + ? ` declaration: ([ (_ name: ([(identifier) (type_identifier)]) @identifier @@ -34,6 +38,18 @@ export function getJavascriptExports(parser: Parser, sourceCode: string) { ) ) ]) + ` + : ` + declaration: ([ + (_ + name: (identifier) @identifier + ) + (_ + (_ + name: (identifier) @identifier + ) + ) + ]) `, ); diff --git a/packages/cli/src/helper/languages/javascript/imports.ts b/packages/cli/src/helper/languages/javascript/imports.ts index 6aa393b..0f60085 100644 --- a/packages/cli/src/helper/languages/javascript/imports.ts +++ b/packages/cli/src/helper/languages/javascript/imports.ts @@ -83,15 +83,23 @@ export function getJavascriptImportIdentifierUsage( parser: Parser, node: Parser.SyntaxNode, identifier: Parser.SyntaxNode, + isTypescript = false, ) { const usageNodes: Parser.SyntaxNode[] = []; const identifierQuery = new Parser.Query( parser.getLanguage(), - ` + isTypescript + ? ` ( ([(identifier) (type_identifier)]) @identifier (#match? @identifier "${identifier.text}") ) + ` + : ` + ( + (identifier) @identifier + (#match? @identifier "${identifier.text}") + ) `, ); const identifierCaptures = identifierQuery.captures(node); diff --git a/packages/cli/src/helper/languages/typescript/annotations.ts b/packages/cli/src/helper/languages/typescript/annotations.ts new file mode 100644 index 0000000..4cccbf9 --- /dev/null +++ b/packages/cli/src/helper/languages/typescript/annotations.ts @@ -0,0 +1,9 @@ +import Parser from "tree-sitter"; +import { getJavascriptAnnotationNodes } from "../javascript/annotations"; + +export function getTypescriptAnnotationNodes( + parser: Parser, + node: Parser.SyntaxNode, +) { + return getJavascriptAnnotationNodes(parser, node); +} diff --git a/packages/cli/src/helper/languages/typescript/cleanup.ts b/packages/cli/src/helper/languages/typescript/cleanup.ts new file mode 100644 index 0000000..6618987 --- /dev/null +++ b/packages/cli/src/helper/languages/typescript/cleanup.ts @@ -0,0 +1,46 @@ +import Parser from "tree-sitter"; +import { Group } from "../../types"; +import { + cleanupJavascriptAnnotations, + cleanupJavascriptInvalidImports, + cleanupUnusedJavascriptImports, +} from "../javascript/cleanup"; + +export function cleanupTypescriptAnnotations( + parser: Parser, + sourceCode: string, + groupToKeep: Group, +): string { + return cleanupJavascriptAnnotations(parser, sourceCode, groupToKeep, true); +} + +export function cleanupTypescriptInvalidImports( + parser: Parser, + filePath: string, + sourceCode: string, + exportIdentifiersMap: Map< + string, + { + namedExports: { + exportNode: Parser.SyntaxNode; + identifierNode: Parser.SyntaxNode; + }[]; + defaultExport?: Parser.SyntaxNode; + } + >, +) { + return cleanupJavascriptInvalidImports( + parser, + filePath, + sourceCode, + exportIdentifiersMap, + true, + ); +} + +export function cleanupUnusedTypescriptImports( + parser: Parser, + sourceCode: string, +) { + return cleanupUnusedJavascriptImports(parser, sourceCode, true); +} diff --git a/packages/cli/src/helper/languages/typescript/exports.ts b/packages/cli/src/helper/languages/typescript/exports.ts new file mode 100644 index 0000000..ec0ab9f --- /dev/null +++ b/packages/cli/src/helper/languages/typescript/exports.ts @@ -0,0 +1,6 @@ +import Parser from "tree-sitter"; +import { getJavascriptExports } from "../javascript/exports"; + +export function getTypescriptExports(parser: Parser, sourceCode: string) { + return getJavascriptExports(parser, sourceCode, true); +} diff --git a/packages/cli/src/helper/languages/typescript/imports.ts b/packages/cli/src/helper/languages/typescript/imports.ts new file mode 100644 index 0000000..33fd1e1 --- /dev/null +++ b/packages/cli/src/helper/languages/typescript/imports.ts @@ -0,0 +1,17 @@ +import Parser from "tree-sitter"; +import { + getJavascriptImportIdentifierUsage, + getJavascriptImports, +} from "../javascript/imports"; + +export function getTypescriptImports(parser: Parser, node: Parser.SyntaxNode) { + return getJavascriptImports(parser, node); +} + +export function getTypescriptImportIdentifierUsage( + parser: Parser, + node: Parser.SyntaxNode, + identifier: Parser.SyntaxNode, +) { + return getJavascriptImportIdentifierUsage(parser, node, identifier, true); +} diff --git a/packages/cli/src/helper/split.ts b/packages/cli/src/helper/split.ts index 07cd969..5e7138c 100644 --- a/packages/cli/src/helper/split.ts +++ b/packages/cli/src/helper/split.ts @@ -95,5 +95,5 @@ export async function createSplit( console.timeEnd("Write to disk each file"); console.timeEnd(`split ${groupMapIndex}`); - console.log("\n"); + console.info("\n"); }