@rayburst/sharity
Version:
Analyze shared package usage across monorepos - calculate symbol sharing percentages, track exclusive imports, and identify unused exports
255 lines • 9.03 kB
JavaScript
;
/**
* TypeScript parser utilities using TypeScript Compiler API
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseFile = parseFile;
exports.countLinesInFile = countLinesInFile;
exports.shouldAnalyzeFile = shouldAnalyzeFile;
exports.isTestFile = isTestFile;
const ts = __importStar(require("typescript"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/**
* Parse a TypeScript/JavaScript file and extract exports and imports
*/
function parseFile(filePath) {
const sourceCode = fs.readFileSync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
const exports = extractExports(sourceFile, filePath);
const imports = extractImports(sourceFile, filePath);
return {
filePath,
exports,
imports,
};
}
/**
* Extract all exported symbols from a source file
*/
function extractExports(sourceFile, filePath) {
const exports = [];
function visit(node) {
// Named exports: export const/function/class/interface/type/enum
if (ts.isVariableStatement(node) &&
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
node.declarationList.declarations.forEach((decl) => {
if (ts.isIdentifier(decl.name)) {
exports.push(createExportedSymbol(decl.name.text, node, filePath));
}
});
}
// export function/class
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) &&
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
node.name) {
exports.push(createExportedSymbol(node.name.text, node, filePath));
}
// export interface/type/enum
if ((ts.isInterfaceDeclaration(node) ||
ts.isTypeAliasDeclaration(node) ||
ts.isEnumDeclaration(node)) &&
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
exports.push(createExportedSymbol(node.name.text, node, filePath));
}
// export { name1, name2 }
if (ts.isExportDeclaration(node) && node.exportClause) {
if (ts.isNamedExports(node.exportClause)) {
node.exportClause.elements.forEach((element) => {
const name = element.name.text;
exports.push(createExportedSymbol(name, node, filePath));
});
}
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
return exports;
}
/**
* Extract all import statements from a source file
*/
function extractImports(sourceFile, filePath) {
const imports = [];
function visit(node) {
if (ts.isImportDeclaration(node)) {
const moduleSpecifier = node.moduleSpecifier;
if (ts.isStringLiteral(moduleSpecifier)) {
const source = moduleSpecifier.text;
const specifiers = [];
if (node.importClause) {
// Default import: import Foo from 'bar'
if (node.importClause.name) {
specifiers.push(node.importClause.name.text);
}
// Named imports: import { Foo, Bar } from 'baz'
if (node.importClause.namedBindings) {
if (ts.isNamedImports(node.importClause.namedBindings)) {
node.importClause.namedBindings.elements.forEach((element) => {
specifiers.push(element.name.text);
});
}
// Namespace import: import * as Foo from 'bar'
else if (ts.isNamespaceImport(node.importClause.namedBindings)) {
specifiers.push(node.importClause.namedBindings.name.text);
}
}
}
imports.push({
source,
specifiers,
filePath,
});
}
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
return imports;
}
/**
* Create an ExportedSymbol from a node
*/
function createExportedSymbol(name, node, filePath) {
const sourceFile = node.getSourceFile();
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
const lineCount = endLine - startLine + 1;
return {
name,
exportPath: getExportPath(filePath),
filePath,
lineCount,
startLine: startLine + 1, // 1-indexed
endLine: endLine + 1, // 1-indexed
kind: inferSymbolKind(node),
};
}
/**
* Infer the kind of symbol from the AST node
*/
function inferSymbolKind(node) {
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
return 'function';
}
if (ts.isClassDeclaration(node)) {
return 'class';
}
if (ts.isInterfaceDeclaration(node)) {
return 'interface';
}
if (ts.isTypeAliasDeclaration(node)) {
return 'type';
}
if (ts.isEnumDeclaration(node)) {
return 'enum';
}
if (ts.isVariableStatement(node)) {
// Check if it looks like a component (starts with uppercase)
const decl = node.declarationList.declarations[0];
if (decl &&
ts.isIdentifier(decl.name) &&
/^[A-Z]/.test(decl.name.text)) {
return 'component';
}
return 'const';
}
return 'unknown';
}
/**
* Get a relative export path from an absolute file path
*/
function getExportPath(filePath) {
// Remove file extension
let exportPath = filePath.replace(/\.(ts|tsx|js|jsx)$/, '');
// Try to make it relative to common base paths
const commonPaths = ['/packages/', '/src/', '/apps/'];
for (const commonPath of commonPaths) {
const index = exportPath.indexOf(commonPath);
if (index !== -1) {
exportPath = exportPath.substring(index + commonPath.length);
break;
}
}
return exportPath;
}
/**
* Count total lines in a file (excluding empty lines and comments)
*/
function countLinesInFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n');
let count = 0;
let inMultiLineComment = false;
for (const line of lines) {
const trimmed = line.trim();
// Skip empty lines
if (trimmed.length === 0)
continue;
// Handle multi-line comments
if (trimmed.startsWith('/*')) {
inMultiLineComment = true;
}
if (inMultiLineComment) {
if (trimmed.endsWith('*/')) {
inMultiLineComment = false;
}
continue;
}
// Skip single-line comments
if (trimmed.startsWith('//'))
continue;
count++;
}
return count;
}
/**
* Check if a file should be analyzed (based on extension)
*/
function shouldAnalyzeFile(filePath) {
const ext = path.extname(filePath);
return ['.ts', '.tsx', '.js', '.jsx'].includes(ext);
}
/**
* Check if a file is a test file
*/
function isTestFile(filePath) {
const fileName = path.basename(filePath);
return (fileName.includes('.test.') ||
fileName.includes('.spec.') ||
fileName.includes('__tests__'));
}
//# sourceMappingURL=typescript-parser.js.map