Skip to content

Commit c2d623a

Browse files
committed
refactor(cem): use TypeScript's resolveModuleName() for import resolution
Replace manual path resolution logic with TypeScript's built-in module resolution API for more accurate and consistent import handling. Changes: - Update findTypePath() to use ts.resolveModuleName() - Update findPathAndTypesFromImports() to use ts.resolveModuleName() - Remove manual path construction and extension conversion logic - Remove unused ROOT_DIR constant - Pass ts instance and module file path to resolution functions This ensures the plugin follows the same module resolution rules as TypeScript itself, handling edge cases like .d.ts files and index files automatically.
1 parent 73917af commit c2d623a

File tree

2 files changed

+32
-21
lines changed

2 files changed

+32
-21
lines changed

cem/support-typedef-jsdoc-utils.js

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,27 @@ function isVarInitWithDoc(node, ts, gatherPrivate = false) {
136136
return isVarInit && hasJsDoc && hasTypeIdentifier && (!isFieldPrivate || gatherPrivate);
137137
}
138138

139-
export function findTypePath(importTag, rootDir, moduleDir) {
139+
export function findTypePath(importTag, ts, moduleFilePath) {
140140
// Remove leading and ending quotes
141141
const typeRelativePath = importTag.typeExpression?.type?.argument?.literal.getText().slice(1, -1);
142142

143143
if (typeRelativePath == null) {
144144
return null;
145145
}
146146

147-
const { dir: typeDir, name: typeFileName } = path.parse(typeRelativePath);
148-
const typeToTs = convertToTSExt(typeFileName);
147+
// Use TypeScript's module resolution
148+
const compilerOptions = {
149+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
150+
allowJs: true,
151+
};
149152

150-
return path.resolve(rootDir, moduleDir, typeDir, typeToTs);
153+
const result = ts.resolveModuleName(typeRelativePath, moduleFilePath, compilerOptions, ts.sys);
154+
155+
if (result.resolvedModule) {
156+
return result.resolvedModule.resolvedFileName;
157+
}
158+
159+
return null;
151160
}
152161

153162
export function findSubtypes(ts, node, types, parents) {
@@ -275,28 +284,31 @@ export function findPathAndTypesFromImports(ts, filePath, ancestors = null) {
275284
const imports = [];
276285
let sourceCode = '';
277286

278-
const parsedPath = path.parse(filePath);
279-
const formattedFilePath = path.resolve(parsedPath.dir, convertToTSExt(filePath));
280-
281-
const currentAncestors = ancestors == null ? [formattedFilePath] : ancestors;
287+
const currentAncestors = ancestors == null ? [filePath] : ancestors;
282288

283289
// Open file
284290
try {
285-
sourceCode = readFileSync(formattedFilePath).toString();
291+
sourceCode = readFileSync(filePath).toString();
286292
} catch (e) {
287293
console.error(e);
288294
return [];
289295
}
290296

291297
// transform to AST
292-
const sourceAst = ts.createSourceFile(formattedFilePath, sourceCode, ts.ScriptTarget.ES2015, true);
298+
const sourceAst = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.ES2015, true);
293299

294300
// Gather only imports from the AST
295301
const importsDeclaration = sourceAst.statements.filter((node) => node.kind === ts.SyntaxKind.ImportDeclaration);
296302
if (importsDeclaration == null) {
297303
return [];
298304
}
299305

306+
// Use TypeScript's module resolution
307+
const compilerOptions = {
308+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
309+
allowJs: true,
310+
};
311+
300312
importsDeclaration.forEach((importNode) => {
301313
const types = [];
302314
const importFile = importNode.moduleSpecifier.text;
@@ -305,14 +317,18 @@ export function findPathAndTypesFromImports(ts, filePath, ancestors = null) {
305317
return;
306318
}
307319

308-
const parsedImportPath = path.parse(importFile);
320+
const result = ts.resolveModuleName(importFile, filePath, compilerOptions, ts.sys);
321+
322+
if (!result.resolvedModule) {
323+
return;
324+
}
309325

310-
const formattedImportPath = path.join(parsedPath.dir, parsedImportPath.dir, convertToTSExt(parsedImportPath.base));
326+
const resolvedPath = result.resolvedModule.resolvedFileName;
311327

312-
if (!currentAncestors.includes(formattedImportPath)) {
328+
if (!currentAncestors.includes(resolvedPath)) {
313329
importNode.importClause.namedBindings.elements.forEach((nodeType) => types.push(nodeType.getText()));
314-
imports.push({ types, path: formattedImportPath });
315-
imports.push(...findPathAndTypesFromImports(ts, formattedImportPath, [...currentAncestors, formattedImportPath]));
330+
imports.push({ types, path: resolvedPath });
331+
imports.push(...findPathAndTypesFromImports(ts, resolvedPath, [...currentAncestors, resolvedPath]));
316332
}
317333
});
318334
return imports;

cem/support-typedef-jsdoc.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { readFileSync } from 'fs';
2-
import path from 'path';
32
import ts from 'typescript';
43
import {
54
convertInterface,
@@ -10,8 +9,6 @@ import {
109
getTypesFromClass,
1110
} from './support-typedef-jsdoc-utils.js';
1211

13-
const ROOT_DIR = process.cwd();
14-
1512
export default function supportTypedefJsdoc() {
1613
// Map that contains a `type-path` as a key and has its markdown interface as a value.
1714
const typesStore = new Map();
@@ -141,12 +138,10 @@ export default function supportTypedefJsdoc() {
141138
statement.tags?.find((tag) => tag.kind === ts.SyntaxKind.JSDocTypedefTag),
142139
)?.[0];
143140

144-
const moduleDir = path.parse(moduleDoc.path).dir;
145-
146141
// Check the jsDoc of the class and find the imports
147142
typeDefNode?.tags?.forEach((tag) => {
148143
// Extract the path from the @typedef import
149-
const typePath = findTypePath(tag, ROOT_DIR, moduleDir);
144+
const typePath = findTypePath(tag, ts, moduleDoc.path);
150145

151146
// If an import is not correct, warn the plugin user.
152147
if (typePath == null) {

0 commit comments

Comments
 (0)