Skip to content

Commit 37878c1

Browse files
Copilotkobenguyent
andcommitted
Fix TypeScript import resolution for extensionless imports
Automatically add .js extension to relative imports that don't have a standard module extension. This fixes the ERR_MODULE_NOT_FOUND error when TypeScript files import other files without specifying an extension (e.g., import from "./abstract.helper" instead of "./abstract.helper.js"). The transpiler now: - Detects imports without standard extensions (.js, .mjs, .cjs, .json, .node) - Adds .js extension automatically for ESM compatibility - Handles both transpiled TypeScript files (rewrites to .temp.mjs) and regular JS files Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
1 parent 36d6087 commit 37878c1

File tree

4 files changed

+64
-0
lines changed

4 files changed

+64
-0
lines changed

lib/utils/typescript.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ const __dirname = __dirname_fn(__filename);
168168
/from\s+['"](\..+?)(?:\.ts)?['"]/g,
169169
(match, importPath) => {
170170
let resolvedPath = path.resolve(fileBaseDir, importPath)
171+
const originalExt = path.extname(importPath)
171172

172173
// Handle .js extension that might be .ts
173174
if (resolvedPath.endsWith('.js')) {
@@ -181,6 +182,8 @@ const __dirname = __dirname_fn(__filename);
181182
}
182183
return `from '${relPath}'`
183184
}
185+
// Keep .js extension as-is (might be a real .js file)
186+
return match
184187
}
185188

186189
// Try with .ts extension
@@ -197,6 +200,18 @@ const __dirname = __dirname_fn(__filename);
197200
return `from '${relPath}'`
198201
}
199202

203+
// If the import doesn't have a standard module extension (.js, .mjs, .cjs, .json)
204+
// add .js for ESM compatibility
205+
// This handles cases where:
206+
// 1. Import has no real extension (e.g., "./utils" or "./helper")
207+
// 2. Import has a non-standard extension that's part of the name (e.g., "./abstract.helper")
208+
const standardExtensions = ['.js', '.mjs', '.cjs', '.json', '.node']
209+
const hasStandardExtension = standardExtensions.includes(originalExt.toLowerCase())
210+
211+
if (!hasStandardExtension) {
212+
return match.replace(importPath, importPath + '.js')
213+
}
214+
200215
// Otherwise, keep the import as-is
201216
return match
202217
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// TypeScript custom helper that imports another TypeScript file
2+
// Testing import without extension (should work after fix)
3+
import { AbstractHelper, HelperUtils } from "./abstract-helper";
4+
5+
class MaterialComponentHelper extends AbstractHelper {
6+
customMethod(): string {
7+
return HelperUtils.formatMessage('Material component helper loaded');
8+
}
9+
10+
async clickButton(selector: string): Promise<void> {
11+
console.log(`Clicking button: ${selector}`);
12+
}
13+
}
14+
15+
export default MaterialComponentHelper;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Abstract helper base class for testing imports
2+
export abstract class AbstractHelper {
3+
protected config: any;
4+
5+
constructor(config?: any) {
6+
this.config = config;
7+
}
8+
9+
abstract customMethod(): string;
10+
}
11+
12+
export class HelperUtils {
13+
static formatMessage(msg: string): string {
14+
return `[Helper] ${msg}`;
15+
}
16+
}

test/unit/container_test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,5 +368,23 @@ describe('Container', () => {
368368
expect(helper.customMethod()).to.eql('TypeScript helper loaded successfully')
369369
expect(helper.asyncCustomMethod).to.be.a('function')
370370
})
371+
372+
it('should load TypeScript helper that imports another TypeScript file without extension', async () => {
373+
const tsHelperPath = path.join(__dirname, '../data/typescript-support/MaterialComponentHelper.ts')
374+
await container.create({
375+
helpers: {
376+
MaterialComponentHelper: {
377+
require: tsHelperPath,
378+
},
379+
},
380+
})
381+
await container.started()
382+
383+
const helper = container.helpers('MaterialComponentHelper')
384+
expect(helper).to.be.ok
385+
expect(helper.customMethod).to.be.a('function')
386+
expect(helper.customMethod()).to.eql('[Helper] Material component helper loaded')
387+
expect(helper.clickButton).to.be.a('function')
388+
})
371389
})
372390
})

0 commit comments

Comments
 (0)