codeceptjs 4.0.1-beta.9 → 4.0.2-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codecept.js +2 -2
- package/lib/command/definitions.js +8 -3
- package/lib/command/run-workers.js +12 -2
- package/lib/command/workers/runTests.js +84 -4
- package/lib/config.js +3 -2
- package/lib/container.js +78 -6
- package/lib/helper/Playwright.js +80 -120
- package/lib/helper/Puppeteer.js +8 -5
- package/lib/listener/helpers.js +2 -14
- package/lib/mocha/factory.js +2 -27
- package/lib/mocha/test.js +4 -2
- package/lib/output.js +2 -2
- package/lib/step/base.js +14 -1
- package/lib/step/record.js +8 -0
- package/lib/utils/loaderCheck.js +13 -3
- package/lib/utils/typescript.js +82 -35
- package/lib/workers.js +86 -5
- package/package.json +22 -22
- package/typings/index.d.ts +1 -1
- package/typings/promiseBasedTypes.d.ts +136 -43
- package/typings/types.d.ts +150 -74
package/bin/codecept.js
CHANGED
|
@@ -174,7 +174,7 @@ program
|
|
|
174
174
|
.option('-R, --reporter <name>', 'specify the reporter to use')
|
|
175
175
|
.option('-S, --sort', 'sort test files')
|
|
176
176
|
.option('-b, --bail', 'bail after first test failure')
|
|
177
|
-
.option('
|
|
177
|
+
.option('--inspec', "enable node's debugger, synonym for node --debug")
|
|
178
178
|
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
|
|
179
179
|
.option('-f, --fgrep <string>', 'only run tests containing <string>')
|
|
180
180
|
.option('-i, --invert', 'inverts --grep and --fgrep matches')
|
|
@@ -276,7 +276,7 @@ program
|
|
|
276
276
|
.option('-R, --reporter <name>', 'specify the reporter to use')
|
|
277
277
|
.option('-S, --sort', 'sort test files')
|
|
278
278
|
.option('-b, --bail', 'bail after first test failure')
|
|
279
|
-
.option('
|
|
279
|
+
.option('--inspect', "enable node's debugger, synonym for node --debug")
|
|
280
280
|
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
|
|
281
281
|
.option('-f, --fgrep <string>', 'only run tests containing <string>')
|
|
282
282
|
.option('-i, --invert', 'inverts --grep and --fgrep matches')
|
|
@@ -41,7 +41,7 @@ const getDefinitionsFileContent = ({ hasCustomHelper, hasCustomStepsFile, helper
|
|
|
41
41
|
|
|
42
42
|
const importPathsFragment = importPaths.join('\n')
|
|
43
43
|
const supportObjectsTypeFragment = convertMapToType(supportObject)
|
|
44
|
-
const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : ''
|
|
44
|
+
const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : 'interface Methods {}'
|
|
45
45
|
const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2)
|
|
46
46
|
|
|
47
47
|
return generateDefinitionsContent({
|
|
@@ -239,8 +239,13 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue)
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
for (const name in pathsToValue) {
|
|
242
|
-
const
|
|
243
|
-
|
|
242
|
+
const originalPath = pathsToValue[name]
|
|
243
|
+
const relativePath = getPath(originalPath, targetFolderPath, testsPath)
|
|
244
|
+
if (originalPath.endsWith('.js') || originalPath.endsWith('.ts')) {
|
|
245
|
+
importStrings.push(`type ${name} = InstanceType<typeof import('${relativePath}').default>;`)
|
|
246
|
+
} else {
|
|
247
|
+
importStrings.push(`type ${name} = import('${relativePath}');`)
|
|
248
|
+
}
|
|
244
249
|
}
|
|
245
250
|
|
|
246
251
|
return importStrings
|
|
@@ -40,11 +40,22 @@ export default async function (workerCount, selectedRuns, options) {
|
|
|
40
40
|
|
|
41
41
|
output.print(`CodeceptJS v${Codecept.version()} ${output.standWithUkraine()}`)
|
|
42
42
|
output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`)
|
|
43
|
-
output.print()
|
|
44
43
|
store.hasWorkers = true
|
|
45
44
|
|
|
46
45
|
const workers = new Workers(numberOfWorkers, config)
|
|
47
46
|
workers.overrideConfig(overrideConfigs)
|
|
47
|
+
|
|
48
|
+
// Show test distribution after workers are initialized
|
|
49
|
+
await workers.bootstrapAll()
|
|
50
|
+
|
|
51
|
+
const workerObjects = workers.getWorkers()
|
|
52
|
+
output.print()
|
|
53
|
+
output.print('Test distribution:')
|
|
54
|
+
workerObjects.forEach((worker, index) => {
|
|
55
|
+
const testCount = worker.tests.length
|
|
56
|
+
output.print(` Worker ${index + 1}: ${testCount} test${testCount !== 1 ? 's' : ''}`)
|
|
57
|
+
})
|
|
58
|
+
output.print()
|
|
48
59
|
|
|
49
60
|
workers.on(event.test.failed, test => {
|
|
50
61
|
output.test.failed(test)
|
|
@@ -68,7 +79,6 @@ export default async function (workerCount, selectedRuns, options) {
|
|
|
68
79
|
if (options.verbose) {
|
|
69
80
|
await getMachineInfo()
|
|
70
81
|
}
|
|
71
|
-
await workers.bootstrapAll()
|
|
72
82
|
await workers.run()
|
|
73
83
|
} catch (err) {
|
|
74
84
|
output.error(err)
|
|
@@ -19,6 +19,29 @@ const stderr = ''
|
|
|
19
19
|
|
|
20
20
|
const { options, tests, testRoot, workerIndex, poolMode } = workerData
|
|
21
21
|
|
|
22
|
+
// Global error handlers to catch critical errors but not test failures
|
|
23
|
+
process.on('uncaughtException', (err) => {
|
|
24
|
+
// Don't exit on test assertion errors - those are handled by mocha
|
|
25
|
+
if (err.name === 'AssertionError' || err.message?.includes('expected')) {
|
|
26
|
+
console.error(`[Worker ${workerIndex}] Test assertion error (handled by mocha):`, err.message)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
console.error(`[Worker ${workerIndex}] Uncaught exception:`, err.message)
|
|
30
|
+
console.error(err.stack)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
35
|
+
// Don't exit on test-related rejections
|
|
36
|
+
const msg = reason?.message || String(reason)
|
|
37
|
+
if (msg.includes('expected') || msg.includes('AssertionError')) {
|
|
38
|
+
console.error(`[Worker ${workerIndex}] Test rejection (handled by mocha):`, msg)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
console.error(`[Worker ${workerIndex}] Unhandled rejection:`, reason)
|
|
42
|
+
process.exit(1)
|
|
43
|
+
})
|
|
44
|
+
|
|
22
45
|
// hide worker output
|
|
23
46
|
// In pool mode, only suppress output if debug is NOT enabled
|
|
24
47
|
// In regular mode, hide result output but allow step output in verbose/debug
|
|
@@ -82,6 +105,8 @@ let config
|
|
|
82
105
|
// Load test and run
|
|
83
106
|
initPromise = (async function () {
|
|
84
107
|
try {
|
|
108
|
+
console.log(`[Worker ${workerIndex}] Starting initialization...`)
|
|
109
|
+
|
|
85
110
|
// Import modules dynamically to avoid ES Module loader race conditions in Node 22.x
|
|
86
111
|
const eventModule = await import('../../event.js')
|
|
87
112
|
const containerModule = await import('../../container.js')
|
|
@@ -89,6 +114,8 @@ initPromise = (async function () {
|
|
|
89
114
|
const coreUtilsModule = await import('../../utils.js')
|
|
90
115
|
const CodeceptModule = await import('../../codecept.js')
|
|
91
116
|
|
|
117
|
+
console.log(`[Worker ${workerIndex}] Modules imported`)
|
|
118
|
+
|
|
92
119
|
event = eventModule.default
|
|
93
120
|
container = containerModule.default
|
|
94
121
|
getConfig = utilsModule.getConfig
|
|
@@ -98,14 +125,24 @@ initPromise = (async function () {
|
|
|
98
125
|
|
|
99
126
|
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
|
|
100
127
|
|
|
128
|
+
console.log(`[Worker ${workerIndex}] Loading config...`)
|
|
129
|
+
|
|
101
130
|
// IMPORTANT: await is required here since getConfig is async
|
|
102
131
|
const baseConfig = await getConfig(options.config || testRoot)
|
|
103
132
|
|
|
133
|
+
console.log(`[Worker ${workerIndex}] Config loaded, creating Codecept...`)
|
|
134
|
+
|
|
104
135
|
// important deep merge so dynamic things e.g. functions on config are not overridden
|
|
105
136
|
config = deepMerge(baseConfig, overrideConfigs)
|
|
106
137
|
|
|
107
|
-
|
|
138
|
+
// Pass workerIndex as child option for output.process() to display worker prefix
|
|
139
|
+
const optsWithChild = { ...options, child: workerIndex }
|
|
140
|
+
codecept = new Codecept(config, optsWithChild)
|
|
141
|
+
|
|
142
|
+
console.log(`[Worker ${workerIndex}] Initializing Codecept...`)
|
|
108
143
|
await codecept.init(testRoot)
|
|
144
|
+
|
|
145
|
+
console.log(`[Worker ${workerIndex}] Loading tests...`)
|
|
109
146
|
codecept.loadTests()
|
|
110
147
|
mocha = container.mocha()
|
|
111
148
|
|
|
@@ -114,9 +151,14 @@ initPromise = (async function () {
|
|
|
114
151
|
// We'll reload test files fresh for each test request
|
|
115
152
|
} else {
|
|
116
153
|
// Legacy mode - filter tests upfront
|
|
154
|
+
console.log(`[Worker ${workerIndex}] Starting test filtering. Assigned ${tests.length} test UIDs`)
|
|
117
155
|
filterTests()
|
|
156
|
+
const finalCount = mocha.suite.total()
|
|
157
|
+
console.log(`[Worker ${workerIndex}] After filtering: ${finalCount} tests to run`)
|
|
118
158
|
}
|
|
119
159
|
|
|
160
|
+
console.log(`[Worker ${workerIndex}] Initialization complete, starting tests...`)
|
|
161
|
+
|
|
120
162
|
// run tests
|
|
121
163
|
if (poolMode) {
|
|
122
164
|
await runPoolTests()
|
|
@@ -124,10 +166,12 @@ initPromise = (async function () {
|
|
|
124
166
|
await runTests()
|
|
125
167
|
} else {
|
|
126
168
|
// No tests to run, close the worker
|
|
169
|
+
console.error(`[Worker ${workerIndex}] ERROR: No tests found after filtering! Assigned ${tests.length} UIDs but none matched.`)
|
|
127
170
|
parentPort?.close()
|
|
128
171
|
}
|
|
129
172
|
} catch (err) {
|
|
130
|
-
console.error(
|
|
173
|
+
console.error(`[Worker ${workerIndex}] FATAL ERROR:`, err.message)
|
|
174
|
+
console.error(err.stack)
|
|
131
175
|
process.exit(1)
|
|
132
176
|
}
|
|
133
177
|
})()
|
|
@@ -138,6 +182,7 @@ async function runTests() {
|
|
|
138
182
|
try {
|
|
139
183
|
await codecept.bootstrap()
|
|
140
184
|
} catch (err) {
|
|
185
|
+
console.error(`[Worker ${workerIndex}] Bootstrap error:`, err.message)
|
|
141
186
|
throw new Error(`Error while running bootstrap file :${err}`)
|
|
142
187
|
}
|
|
143
188
|
listenToParentThread()
|
|
@@ -145,8 +190,15 @@ async function runTests() {
|
|
|
145
190
|
disablePause()
|
|
146
191
|
try {
|
|
147
192
|
await codecept.run()
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error(`[Worker ${workerIndex}] Runtime error:`, err.message)
|
|
195
|
+
throw err
|
|
148
196
|
} finally {
|
|
149
|
-
|
|
197
|
+
try {
|
|
198
|
+
await codecept.teardown()
|
|
199
|
+
} catch (err) {
|
|
200
|
+
console.error(`[Worker ${workerIndex}] Teardown error:`, err.message)
|
|
201
|
+
}
|
|
150
202
|
}
|
|
151
203
|
}
|
|
152
204
|
|
|
@@ -334,8 +386,36 @@ function filterTests() {
|
|
|
334
386
|
mocha.files = files
|
|
335
387
|
mocha.loadFiles()
|
|
336
388
|
|
|
337
|
-
|
|
389
|
+
// Collect all loaded tests for debugging
|
|
390
|
+
const allLoadedTests = [];
|
|
391
|
+
mocha.suite.eachTest(test => {
|
|
392
|
+
if (test) {
|
|
393
|
+
allLoadedTests.push({ uid: test.uid, title: test.fullTitle() });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
console.log(`[Worker ${workerIndex}] Loaded ${allLoadedTests.length} tests from ${files.length} files`);
|
|
398
|
+
console.log(`[Worker ${workerIndex}] Expecting ${tests.length} test UIDs`);
|
|
399
|
+
|
|
400
|
+
const loadedUids = new Set(allLoadedTests.map(t => t.uid));
|
|
401
|
+
const missingTests = tests.filter(uid => !loadedUids.has(uid));
|
|
402
|
+
|
|
403
|
+
if (missingTests.length > 0) {
|
|
404
|
+
console.error(`[Worker ${workerIndex}] ERROR: ${missingTests.length} assigned tests NOT FOUND in loaded files!`);
|
|
405
|
+
console.error(`[Worker ${workerIndex}] Missing UIDs:`, missingTests);
|
|
406
|
+
console.error(`[Worker ${workerIndex}] Available UIDs:`, Array.from(loadedUids).slice(0, 5), '...');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Recursively filter tests in all suites (including nested ones)
|
|
410
|
+
const filterSuiteTests = (suite) => {
|
|
338
411
|
suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0)
|
|
412
|
+
for (const childSuite of suite.suites) {
|
|
413
|
+
filterSuiteTests(childSuite)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (const suite of mocha.suite.suites) {
|
|
418
|
+
filterSuiteTests(suite)
|
|
339
419
|
}
|
|
340
420
|
}
|
|
341
421
|
|
package/lib/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'fs'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { createRequire } from 'module'
|
|
4
4
|
import { fileExists, isFile, deepMerge, deepClone } from './utils.js'
|
|
5
|
-
import { transpileTypeScript, cleanupTempFiles } from './utils/typescript.js'
|
|
5
|
+
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
|
|
6
6
|
|
|
7
7
|
const defaultConfig = {
|
|
8
8
|
output: './_output',
|
|
@@ -159,12 +159,13 @@ async function loadConfigFile(configFile) {
|
|
|
159
159
|
try {
|
|
160
160
|
// Use the TypeScript transpilation utility
|
|
161
161
|
const typescript = require('typescript')
|
|
162
|
-
const { tempFile, allTempFiles } = await transpileTypeScript(configFile, typescript)
|
|
162
|
+
const { tempFile, allTempFiles, fileMapping } = await transpileTypeScript(configFile, typescript)
|
|
163
163
|
|
|
164
164
|
try {
|
|
165
165
|
configModule = await import(tempFile)
|
|
166
166
|
cleanupTempFiles(allTempFiles)
|
|
167
167
|
} catch (err) {
|
|
168
|
+
fixErrorStack(err, fileMapping)
|
|
168
169
|
cleanupTempFiles(allTempFiles)
|
|
169
170
|
throw err
|
|
170
171
|
}
|
package/lib/container.js
CHANGED
|
@@ -5,7 +5,7 @@ import debugModule from 'debug'
|
|
|
5
5
|
const debug = debugModule('codeceptjs:container')
|
|
6
6
|
import { MetaStep } from './step.js'
|
|
7
7
|
import { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally, deepMerge } from './utils.js'
|
|
8
|
-
import { transpileTypeScript, cleanupTempFiles } from './utils/typescript.js'
|
|
8
|
+
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
|
|
9
9
|
import Translation from './translation.js'
|
|
10
10
|
import MochaFactory from './mocha/factory.js'
|
|
11
11
|
import recorder from './recorder.js'
|
|
@@ -34,6 +34,7 @@ let container = {
|
|
|
34
34
|
/** @type {Result | null} */
|
|
35
35
|
result: null,
|
|
36
36
|
sharedKeys: new Set(), // Track keys shared via share() function
|
|
37
|
+
tsFileMapping: null, // TypeScript file mapping for error stack fixing
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
@@ -88,7 +89,7 @@ class Container {
|
|
|
88
89
|
container.support.I = mod
|
|
89
90
|
}
|
|
90
91
|
} catch (e) {
|
|
91
|
-
throw
|
|
92
|
+
throw e
|
|
92
93
|
}
|
|
93
94
|
} else {
|
|
94
95
|
// Create default actor - this sets up the callback in asyncHelperPromise
|
|
@@ -176,6 +177,15 @@ class Container {
|
|
|
176
177
|
return container.translation
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Get TypeScript file mapping for error stack fixing
|
|
182
|
+
*
|
|
183
|
+
* @api
|
|
184
|
+
*/
|
|
185
|
+
static tsFileMapping() {
|
|
186
|
+
return container.tsFileMapping
|
|
187
|
+
}
|
|
188
|
+
|
|
179
189
|
/**
|
|
180
190
|
* Get Mocha instance
|
|
181
191
|
*
|
|
@@ -398,20 +408,66 @@ async function requireHelperFromModule(helperName, config, HelperClass) {
|
|
|
398
408
|
throw err
|
|
399
409
|
}
|
|
400
410
|
} else {
|
|
411
|
+
// Handle TypeScript files
|
|
412
|
+
let importPath = moduleName
|
|
413
|
+
let tempJsFile = null
|
|
414
|
+
let fileMapping = null
|
|
415
|
+
const ext = path.extname(moduleName)
|
|
416
|
+
|
|
417
|
+
if (ext === '.ts') {
|
|
418
|
+
try {
|
|
419
|
+
// Use the TypeScript transpilation utility
|
|
420
|
+
const typescript = await import('typescript')
|
|
421
|
+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
|
|
422
|
+
|
|
423
|
+
debug(`Transpiled TypeScript helper: ${importPath} -> ${tempFile}`)
|
|
424
|
+
|
|
425
|
+
importPath = tempFile
|
|
426
|
+
tempJsFile = allTempFiles
|
|
427
|
+
fileMapping = mapping
|
|
428
|
+
// Store file mapping in container for runtime error fixing (merge with existing)
|
|
429
|
+
if (!container.tsFileMapping) {
|
|
430
|
+
container.tsFileMapping = new Map()
|
|
431
|
+
}
|
|
432
|
+
for (const [key, value] of mapping.entries()) {
|
|
433
|
+
container.tsFileMapping.set(key, value)
|
|
434
|
+
}
|
|
435
|
+
} catch (tsError) {
|
|
436
|
+
throw new Error(`Failed to load TypeScript helper ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
401
440
|
// check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
|
|
402
441
|
try {
|
|
403
442
|
// Try dynamic import for both CommonJS and ESM modules
|
|
404
|
-
const mod = await import(
|
|
443
|
+
const mod = await import(importPath)
|
|
405
444
|
if (!mod && !mod.default) {
|
|
406
445
|
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
407
446
|
}
|
|
408
447
|
HelperClass = mod.default || mod
|
|
448
|
+
|
|
449
|
+
// Clean up temp files if created
|
|
450
|
+
if (tempJsFile) {
|
|
451
|
+
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
452
|
+
cleanupTempFiles(filesToClean)
|
|
453
|
+
}
|
|
409
454
|
} catch (err) {
|
|
455
|
+
// Fix error stack to point to original .ts files
|
|
456
|
+
if (fileMapping) {
|
|
457
|
+
fixErrorStack(err, fileMapping)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Clean up temp files before rethrowing
|
|
461
|
+
if (tempJsFile) {
|
|
462
|
+
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
463
|
+
cleanupTempFiles(filesToClean)
|
|
464
|
+
}
|
|
465
|
+
|
|
410
466
|
if (err.code === 'ERR_REQUIRE_ESM' || (err.message && err.message.includes('ES module'))) {
|
|
411
467
|
// This is an ESM module, use dynamic import
|
|
412
468
|
try {
|
|
413
469
|
const pathModule = await import('path')
|
|
414
|
-
const absolutePath = pathModule.default.resolve(
|
|
470
|
+
const absolutePath = pathModule.default.resolve(importPath)
|
|
415
471
|
const mod = await import(absolutePath)
|
|
416
472
|
HelperClass = mod.default || mod
|
|
417
473
|
debug(`helper ${helperName} loaded via ESM import`)
|
|
@@ -699,6 +755,7 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
699
755
|
// Use dynamic import for both ESM and CJS modules
|
|
700
756
|
let importPath = modulePath
|
|
701
757
|
let tempJsFile = null
|
|
758
|
+
let fileMapping = null
|
|
702
759
|
|
|
703
760
|
if (typeof importPath === 'string') {
|
|
704
761
|
const ext = path.extname(importPath)
|
|
@@ -708,7 +765,7 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
708
765
|
try {
|
|
709
766
|
// Use the TypeScript transpilation utility
|
|
710
767
|
const typescript = await import('typescript')
|
|
711
|
-
const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
|
|
768
|
+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
|
|
712
769
|
|
|
713
770
|
debug(`Transpiled TypeScript file: ${importPath} -> ${tempFile}`)
|
|
714
771
|
|
|
@@ -716,6 +773,14 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
716
773
|
importPath = tempFile
|
|
717
774
|
// Store temp files list in a way that cleanup can access them
|
|
718
775
|
tempJsFile = allTempFiles
|
|
776
|
+
fileMapping = mapping
|
|
777
|
+
// Store file mapping in container for runtime error fixing (merge with existing)
|
|
778
|
+
if (!container.tsFileMapping) {
|
|
779
|
+
container.tsFileMapping = new Map()
|
|
780
|
+
}
|
|
781
|
+
for (const [key, value] of mapping.entries()) {
|
|
782
|
+
container.tsFileMapping.set(key, value)
|
|
783
|
+
}
|
|
719
784
|
} catch (tsError) {
|
|
720
785
|
throw new Error(`Failed to load TypeScript file ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
|
|
721
786
|
}
|
|
@@ -729,6 +794,11 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
729
794
|
try {
|
|
730
795
|
obj = await import(importPath)
|
|
731
796
|
} catch (importError) {
|
|
797
|
+
// Fix error stack to point to original .ts files
|
|
798
|
+
if (fileMapping) {
|
|
799
|
+
fixErrorStack(importError, fileMapping)
|
|
800
|
+
}
|
|
801
|
+
|
|
732
802
|
// Clean up temp files if created before rethrowing
|
|
733
803
|
if (tempJsFile) {
|
|
734
804
|
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
@@ -777,7 +847,9 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
777
847
|
|
|
778
848
|
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof actualObj}`)
|
|
779
849
|
} catch (err) {
|
|
780
|
-
|
|
850
|
+
const newErr = new Error(`Could not include object ${supportObjectName} from module '${modulePath}': ${err.message}`)
|
|
851
|
+
newErr.stack = err.stack
|
|
852
|
+
throw newErr
|
|
781
853
|
}
|
|
782
854
|
}
|
|
783
855
|
|