This commit is contained in:
144
src/parsers/importParser.ts
Normal file
144
src/parsers/importParser.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import type * as TSESTree from '@typescript-eslint/typescript-estree';
|
||||
import { createASTUtils } from '../utils/astUtils';
|
||||
import { TypeReference } from '../types';
|
||||
|
||||
export interface ImportParserOptions {
|
||||
resolvePaths?: boolean;
|
||||
basePath?: string;
|
||||
}
|
||||
|
||||
export interface ParsedImport {
|
||||
moduleName: string;
|
||||
filePath: string;
|
||||
defaultImport?: string;
|
||||
namespaceImport?: string;
|
||||
namedImports: NamedImport[];
|
||||
reExports: string[];
|
||||
isExternal: boolean;
|
||||
}
|
||||
|
||||
export interface NamedImport {
|
||||
localName: string;
|
||||
importedName: string;
|
||||
}
|
||||
|
||||
export class ImportParser {
|
||||
private astUtils: ReturnType<typeof createASTUtils>;
|
||||
private options: ImportParserOptions;
|
||||
|
||||
constructor(options: ImportParserOptions = {}) {
|
||||
this.astUtils = createASTUtils();
|
||||
this.options = {
|
||||
resolvePaths: options.resolvePaths ?? true,
|
||||
basePath: options.basePath ?? process.cwd()
|
||||
};
|
||||
}
|
||||
|
||||
parse(source: string, filePath: string): ParsedImport[] {
|
||||
const ast = this.astUtils.parse(source, filePath);
|
||||
const imports = this.astUtils.getImports(ast);
|
||||
|
||||
return imports.map((imp) => this.parseImportDeclaration(imp, filePath));
|
||||
}
|
||||
|
||||
private parseImportDeclaration(node: TSESTree.ImportDeclaration, filePath: string): ParsedImport {
|
||||
const moduleName = typeof node.source.value === 'string' ? node.source.value : '';
|
||||
const isExternal = this.isExternalModule(moduleName);
|
||||
|
||||
const namedImports: NamedImport[] = [];
|
||||
let defaultImport: string | undefined;
|
||||
let namespaceImport: string | undefined;
|
||||
|
||||
if (node.specifiers && node.specifiers.length > 0) {
|
||||
for (const specifier of node.specifiers) {
|
||||
if (specifier.type === 'ImportSpecifier') {
|
||||
namedImports.push({
|
||||
localName: specifier.local.name,
|
||||
importedName: specifier.imported.type === 'Identifier' ? specifier.imported.name : specifier.imported.value
|
||||
});
|
||||
} else if (specifier.type === 'ImportDefaultSpecifier') {
|
||||
defaultImport = specifier.local.name;
|
||||
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
||||
namespaceImport = specifier.local.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
moduleName,
|
||||
filePath,
|
||||
defaultImport,
|
||||
namespaceImport,
|
||||
namedImports,
|
||||
reExports: [],
|
||||
isExternal
|
||||
};
|
||||
}
|
||||
|
||||
private isExternalModule(moduleName: string): boolean {
|
||||
return (
|
||||
moduleName.startsWith('@') ||
|
||||
!moduleName.startsWith('.') &&
|
||||
!moduleName.startsWith('/') &&
|
||||
!moduleName.startsWith('http://') &&
|
||||
!moduleName.startsWith('https://')
|
||||
);
|
||||
}
|
||||
|
||||
extractImportedTypes(source: string, filePath: string): TypeReference[] {
|
||||
const ast = this.astUtils.parse(source, filePath);
|
||||
const typeRefs = this.astUtils.findNodes<TSESTree.TSTypeReference>(
|
||||
ast,
|
||||
(n): n is TSESTree.TSTypeReference => n.type === 'TSTypeReference'
|
||||
);
|
||||
|
||||
return typeRefs.map((ref) => ({
|
||||
name: ref.typeName.type === 'Identifier' ? ref.typeName.name : '',
|
||||
module: undefined,
|
||||
isExternal: false,
|
||||
location: {
|
||||
filePath,
|
||||
startLine: ref.loc?.start?.line ?? 0,
|
||||
startColumn: ref.loc?.start?.column ?? 0,
|
||||
endLine: ref.loc?.end?.line ?? 0,
|
||||
endColumn: ref.loc?.end?.column ?? 0
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
resolveImportPath(moduleName: string, fromFile: string): string {
|
||||
if (this.isExternalModule(moduleName)) {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
const baseDir = path.dirname(fromFile);
|
||||
let resolvedPath = moduleName;
|
||||
|
||||
if (!path.extname(resolvedPath)) {
|
||||
const tsPath = path.join(baseDir, `${resolvedPath}.ts`);
|
||||
const tsxPath = path.join(baseDir, `${resolvedPath}.tsx`);
|
||||
const dtsPath = path.join(baseDir, `${resolvedPath}.d.ts`);
|
||||
const indexPath = path.join(baseDir, resolvedPath, 'index.ts');
|
||||
|
||||
if (fs.existsSync(tsPath)) {
|
||||
resolvedPath = tsPath;
|
||||
} else if (fs.existsSync(tsxPath)) {
|
||||
resolvedPath = tsxPath;
|
||||
} else if (fs.existsSync(dtsPath)) {
|
||||
resolvedPath = dtsPath;
|
||||
} else if (fs.existsSync(indexPath)) {
|
||||
resolvedPath = indexPath;
|
||||
} else {
|
||||
resolvedPath = path.join(baseDir, resolvedPath);
|
||||
}
|
||||
}
|
||||
|
||||
return path.resolve(baseDir, resolvedPath);
|
||||
}
|
||||
}
|
||||
|
||||
export function createImportParser(options?: ImportParserOptions): ImportParser {
|
||||
return new ImportParser(options);
|
||||
}
|
||||
Reference in New Issue
Block a user