Skip to content

Commit e519828

Browse files
committed
fix: TypeScript files with dynamic require() paths now work correctly
- Fixed require() to use original .ts file path as base, not temp .mjs file - This ensures dynamic require() with relative paths work from original location - Fixed __dirname and __filename to use original file paths - Handles user scenarios where require() paths are constructed at runtime - Added test case for dynamic require() with environment-based paths - All tests pass (config + container TypeScript support) Fixes issue where: require(`./config/environment.${env}`) would fail because require was relative to transpiled temp file location
1 parent 981ec98 commit e519828

File tree

6 files changed

+67
-5
lines changed

6 files changed

+67
-5
lines changed

lib/utils/typescript.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,23 @@ export async function transpileTypeScript(mainFilePath, typescript) {
3737
let esmGlobals = ''
3838

3939
if (usesRequire || usesModuleExports) {
40+
// IMPORTANT: Use the original .ts file path as the base for require()
41+
// This ensures dynamic require() calls work with relative paths from the original file location
42+
const originalFileUrl = `file://${filePath.replace(/\\/g, '/')}`
4043
esmGlobals += `import { createRequire } from 'module';
41-
const require = createRequire(import.meta.url);
44+
const require = createRequire('${originalFileUrl}');
4245
const module = { exports: {} };
4346
const exports = module.exports;
4447
4548
`
4649
}
4750

4851
if (usesCommonJSGlobals) {
52+
// For __dirname and __filename, also use the original file path
53+
const originalFileUrl = `file://${filePath.replace(/\\/g, '/')}`
4954
esmGlobals += `import { fileURLToPath as __fileURLToPath } from 'url';
5055
import { dirname as __dirname_fn } from 'path';
51-
const __filename = __fileURLToPath(import.meta.url);
56+
const __filename = '${filePath.replace(/\\/g, '/')}';
5257
const __dirname = __dirname_fn(__filename);
5358
5459
`
@@ -96,8 +101,16 @@ const __dirname = __dirname_fn(__filename);
96101

97102
// Try adding .ts extension if file doesn't exist and no extension provided
98103
if (!path.extname(importedPath)) {
99-
if (fs.existsSync(importedPath + '.ts')) {
100-
importedPath = importedPath + '.ts'
104+
const tsPath = importedPath + '.ts'
105+
if (fs.existsSync(tsPath)) {
106+
importedPath = tsPath
107+
} else {
108+
// Try .js extension as well
109+
const jsPath = importedPath + '.js'
110+
if (fs.existsSync(jsPath)) {
111+
// Skip .js files, they don't need transpilation
112+
continue
113+
}
101114
}
102115
}
103116

@@ -134,7 +147,11 @@ const __dirname = __dirname_fn(__filename);
134147
const tempFile = transpiledFiles.get(tsPath)
135148
// Get relative path from main temp file to this temp file
136149
const relPath = path.relative(baseDir, tempFile).replace(/\\/g, '/')
137-
return `from './${relPath}'`
150+
// Ensure the path starts with ./
151+
if (!relPath.startsWith('.')) {
152+
return `from './${relPath}'`
153+
}
154+
return `from '${relPath}'`
138155
}
139156

140157
// Otherwise, keep the import as-is
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Simulate the user's scenario
2+
let env: string = process.env.E2E_ENV || "TEST";
3+
4+
// Use path relative to this file's location
5+
// Add .cjs extension since we're in an ESM project (package.json has "type": "module")
6+
const environmentPath: string = `../config/environment.${env}.cjs`;
7+
const environment = require(environmentPath);
8+
9+
export const Environment = environment;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
url: 'https://test.example.com',
3+
apiUrl: 'https://api.test.example.com',
4+
timeout: 5000
5+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "typescript-dynamic-require-test",
3+
"version": "1.0.0",
4+
"type": "module"
5+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Environment } from '../common/utils'
2+
3+
export const config = {
4+
tests: './*_test.js',
5+
output: './output',
6+
helpers: {
7+
REST: {
8+
endpoint: Environment.apiUrl,
9+
}
10+
},
11+
bootstrap: null,
12+
mocha: {},
13+
name: 'typescript-dynamic-require-test'
14+
}

test/unit/config_test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,16 @@ describe('Config', () => {
8383
expect(cfg.plugins.htmlReporter.enabled).to.equal(true)
8484
expect(cfg.name).to.equal('typescript-config-with-require')
8585
})
86+
87+
it('should load TypeScript config with dynamic require() paths', async () => {
88+
const configPath = './test/data/typescript-dynamic-require/tests/codecept.conf.ts'
89+
process.env.E2E_ENV = 'TEST'
90+
const cfg = await config.load(configPath)
91+
92+
expect(cfg).to.be.ok
93+
expect(cfg.helpers).to.have.property('REST')
94+
expect(cfg.helpers.REST.endpoint).to.equal('https://api.test.example.com')
95+
expect(cfg.name).to.equal('typescript-dynamic-require-test')
96+
delete process.env.E2E_ENV
97+
})
8698
})

0 commit comments

Comments
 (0)