codeceptjs 4.0.0-beta.12 → 4.0.0-beta.14
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/lib/config.js +7 -22
- package/lib/container.js +8 -133
- package/lib/utils/typescript.js +169 -0
- package/package.json +1 -1
package/lib/config.js
CHANGED
|
@@ -2,6 +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
6
|
|
|
6
7
|
const defaultConfig = {
|
|
7
8
|
output: './_output',
|
|
@@ -156,31 +157,15 @@ async function loadConfigFile(configFile) {
|
|
|
156
157
|
// For .ts files, try to compile and load as JavaScript
|
|
157
158
|
if (extensionName === '.ts') {
|
|
158
159
|
try {
|
|
159
|
-
//
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
// Transpile TypeScript to JavaScript with ES module output
|
|
164
|
-
const jsContent = transpile(tsContent, {
|
|
165
|
-
module: 99, // ModuleKind.ESNext
|
|
166
|
-
target: 99, // ScriptTarget.ESNext
|
|
167
|
-
esModuleInterop: true,
|
|
168
|
-
allowSyntheticDefaultImports: true,
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// Create a temporary JS file with .mjs extension to force ES module treatment
|
|
172
|
-
const tempJsFile = configFile.replace('.ts', '.temp.mjs')
|
|
173
|
-
fs.writeFileSync(tempJsFile, jsContent)
|
|
160
|
+
// Use the TypeScript transpilation utility
|
|
161
|
+
const typescript = require('typescript')
|
|
162
|
+
const { tempFile, allTempFiles } = await transpileTypeScript(configFile, typescript)
|
|
174
163
|
|
|
175
164
|
try {
|
|
176
|
-
configModule = await import(
|
|
177
|
-
|
|
178
|
-
fs.unlinkSync(tempJsFile)
|
|
165
|
+
configModule = await import(tempFile)
|
|
166
|
+
cleanupTempFiles(allTempFiles)
|
|
179
167
|
} catch (err) {
|
|
180
|
-
|
|
181
|
-
if (fs.existsSync(tempJsFile)) {
|
|
182
|
-
fs.unlinkSync(tempJsFile)
|
|
183
|
-
}
|
|
168
|
+
cleanupTempFiles(allTempFiles)
|
|
184
169
|
throw err
|
|
185
170
|
}
|
|
186
171
|
} catch (tsError) {
|
package/lib/container.js
CHANGED
|
@@ -5,6 +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
9
|
import Translation from './translation.js'
|
|
9
10
|
import MochaFactory from './mocha/factory.js'
|
|
10
11
|
import recorder from './recorder.js'
|
|
@@ -683,124 +684,14 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
683
684
|
// Handle TypeScript files
|
|
684
685
|
if (ext === '.ts') {
|
|
685
686
|
try {
|
|
686
|
-
|
|
687
|
+
// Use the TypeScript transpilation utility
|
|
688
|
+
const typescript = await import('typescript')
|
|
689
|
+
const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
|
|
687
690
|
|
|
688
|
-
|
|
689
|
-
const transpileTS = (filePath) => {
|
|
690
|
-
const tsContent = fs.readFileSync(filePath, 'utf8')
|
|
691
|
-
|
|
692
|
-
// Transpile TypeScript to JavaScript with ES module output
|
|
693
|
-
let jsContent = transpile(tsContent, {
|
|
694
|
-
module: 99, // ModuleKind.ESNext
|
|
695
|
-
target: 99, // ScriptTarget.ESNext
|
|
696
|
-
esModuleInterop: true,
|
|
697
|
-
allowSyntheticDefaultImports: true,
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
// Check if the code uses __dirname or __filename (CommonJS globals)
|
|
701
|
-
const usesCommonJSGlobals = /__dirname|__filename/.test(jsContent)
|
|
702
|
-
|
|
703
|
-
if (usesCommonJSGlobals) {
|
|
704
|
-
// Inject ESM equivalents at the top of the file
|
|
705
|
-
const esmGlobals = `import { fileURLToPath as __fileURLToPath } from 'url';
|
|
706
|
-
import { dirname as __dirname_fn } from 'path';
|
|
707
|
-
const __filename = __fileURLToPath(import.meta.url);
|
|
708
|
-
const __dirname = __dirname_fn(__filename);
|
|
709
|
-
|
|
710
|
-
`
|
|
711
|
-
jsContent = esmGlobals + jsContent
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
return jsContent
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Create a map to track transpiled files
|
|
718
|
-
const transpiledFiles = new Map()
|
|
719
|
-
const baseDir = path.dirname(importPath)
|
|
720
|
-
|
|
721
|
-
// Transpile main file
|
|
722
|
-
let jsContent = transpileTS(importPath)
|
|
723
|
-
|
|
724
|
-
// Find and transpile all relative TypeScript imports
|
|
725
|
-
// Match: import ... from './file' or '../file' or './file.ts'
|
|
726
|
-
const importRegex = /from\s+['"](\..+?)(?:\.ts)?['"]/g
|
|
727
|
-
let match
|
|
728
|
-
const imports = []
|
|
729
|
-
|
|
730
|
-
while ((match = importRegex.exec(jsContent)) !== null) {
|
|
731
|
-
imports.push(match[1])
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Transpile each imported TypeScript file
|
|
735
|
-
for (const relativeImport of imports) {
|
|
736
|
-
let importedPath = path.resolve(baseDir, relativeImport)
|
|
737
|
-
|
|
738
|
-
// Handle .js extensions that might actually be .ts files
|
|
739
|
-
if (importedPath.endsWith('.js')) {
|
|
740
|
-
const tsVersion = importedPath.replace(/\.js$/, '.ts')
|
|
741
|
-
if (fs.existsSync(tsVersion)) {
|
|
742
|
-
importedPath = tsVersion
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// Try adding .ts extension if file doesn't exist and no extension provided
|
|
747
|
-
if (!path.extname(importedPath)) {
|
|
748
|
-
if (fs.existsSync(importedPath + '.ts')) {
|
|
749
|
-
importedPath = importedPath + '.ts'
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
// If it's a TypeScript file, transpile it
|
|
754
|
-
if (importedPath.endsWith('.ts') && fs.existsSync(importedPath)) {
|
|
755
|
-
const transpiledImportContent = transpileTS(importedPath)
|
|
756
|
-
const tempImportFile = importedPath.replace(/\.ts$/, '.temp.mjs')
|
|
757
|
-
fs.writeFileSync(tempImportFile, transpiledImportContent)
|
|
758
|
-
transpiledFiles.set(importedPath, tempImportFile)
|
|
759
|
-
debug(`Transpiled dependency: ${importedPath} -> ${tempImportFile}`)
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Replace imports in the main file to point to temp .mjs files
|
|
764
|
-
jsContent = jsContent.replace(
|
|
765
|
-
/from\s+['"](\..+?)(?:\.ts)?['"]/g,
|
|
766
|
-
(match, importPath) => {
|
|
767
|
-
let resolvedPath = path.resolve(baseDir, importPath)
|
|
768
|
-
|
|
769
|
-
// Handle .js extension that might be .ts
|
|
770
|
-
if (resolvedPath.endsWith('.js')) {
|
|
771
|
-
const tsVersion = resolvedPath.replace(/\.js$/, '.ts')
|
|
772
|
-
if (transpiledFiles.has(tsVersion)) {
|
|
773
|
-
const tempFile = transpiledFiles.get(tsVersion)
|
|
774
|
-
const relPath = path.relative(baseDir, tempFile).replace(/\\/g, '/')
|
|
775
|
-
return `from './${relPath}'`
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// Try with .ts extension
|
|
780
|
-
const tsPath = resolvedPath.endsWith('.ts') ? resolvedPath : resolvedPath + '.ts'
|
|
781
|
-
|
|
782
|
-
// If we transpiled this file, use the temp file
|
|
783
|
-
if (transpiledFiles.has(tsPath)) {
|
|
784
|
-
const tempFile = transpiledFiles.get(tsPath)
|
|
785
|
-
// Get relative path from main temp file to this temp file
|
|
786
|
-
const relPath = path.relative(baseDir, tempFile).replace(/\\/g, '/')
|
|
787
|
-
return `from './${relPath}'`
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// Otherwise, keep the import as-is
|
|
791
|
-
return match
|
|
792
|
-
}
|
|
793
|
-
)
|
|
794
|
-
|
|
795
|
-
// Create a temporary JS file with .mjs extension for the main file
|
|
796
|
-
tempJsFile = importPath.replace(/\.ts$/, '.temp.mjs')
|
|
797
|
-
fs.writeFileSync(tempJsFile, jsContent)
|
|
798
|
-
|
|
799
|
-
// Store all temp files for cleanup
|
|
800
|
-
const allTempFiles = [tempJsFile, ...Array.from(transpiledFiles.values())]
|
|
691
|
+
debug(`Transpiled TypeScript file: ${importPath} -> ${tempFile}`)
|
|
801
692
|
|
|
802
693
|
// Attach cleanup handler
|
|
803
|
-
importPath =
|
|
694
|
+
importPath = tempFile
|
|
804
695
|
// Store temp files list in a way that cleanup can access them
|
|
805
696
|
tempJsFile = allTempFiles
|
|
806
697
|
|
|
@@ -820,30 +711,14 @@ const __dirname = __dirname_fn(__filename);
|
|
|
820
711
|
// Clean up temp files if created before rethrowing
|
|
821
712
|
if (tempJsFile) {
|
|
822
713
|
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
823
|
-
|
|
824
|
-
try {
|
|
825
|
-
if (fs.existsSync(file)) {
|
|
826
|
-
fs.unlinkSync(file)
|
|
827
|
-
}
|
|
828
|
-
} catch (cleanupError) {
|
|
829
|
-
// Ignore cleanup errors
|
|
830
|
-
}
|
|
831
|
-
}
|
|
714
|
+
cleanupTempFiles(filesToClean)
|
|
832
715
|
}
|
|
833
716
|
throw importError
|
|
834
717
|
} finally {
|
|
835
718
|
// Clean up temp files if created
|
|
836
719
|
if (tempJsFile) {
|
|
837
720
|
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
838
|
-
|
|
839
|
-
try {
|
|
840
|
-
if (fs.existsSync(file)) {
|
|
841
|
-
fs.unlinkSync(file)
|
|
842
|
-
}
|
|
843
|
-
} catch (cleanupError) {
|
|
844
|
-
// Ignore cleanup errors
|
|
845
|
-
}
|
|
846
|
-
}
|
|
721
|
+
cleanupTempFiles(filesToClean)
|
|
847
722
|
}
|
|
848
723
|
}
|
|
849
724
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transpile TypeScript files to ES modules with CommonJS shim support
|
|
6
|
+
* Handles recursive transpilation of imported TypeScript files
|
|
7
|
+
*
|
|
8
|
+
* @param {string} mainFilePath - Path to the main TypeScript file to transpile
|
|
9
|
+
* @param {object} typescript - TypeScript compiler instance
|
|
10
|
+
* @returns {Promise<{tempFile: string, allTempFiles: string[]}>} - Main temp file and all temp files created
|
|
11
|
+
*/
|
|
12
|
+
export async function transpileTypeScript(mainFilePath, typescript) {
|
|
13
|
+
const { transpile } = typescript
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Transpile a single TypeScript file to JavaScript
|
|
17
|
+
* Injects CommonJS shims (require, module, exports, __dirname, __filename) as needed
|
|
18
|
+
*/
|
|
19
|
+
const transpileTS = (filePath) => {
|
|
20
|
+
const tsContent = fs.readFileSync(filePath, 'utf8')
|
|
21
|
+
|
|
22
|
+
// Transpile TypeScript to JavaScript with ES module output
|
|
23
|
+
let jsContent = transpile(tsContent, {
|
|
24
|
+
module: 99, // ModuleKind.ESNext
|
|
25
|
+
target: 99, // ScriptTarget.ESNext
|
|
26
|
+
esModuleInterop: true,
|
|
27
|
+
allowSyntheticDefaultImports: true,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Check if the code uses CommonJS globals
|
|
31
|
+
const usesCommonJSGlobals = /__dirname|__filename/.test(jsContent)
|
|
32
|
+
const usesRequire = /\brequire\s*\(/.test(jsContent)
|
|
33
|
+
const usesModuleExports = /\b(module\.exports|exports\.)/.test(jsContent)
|
|
34
|
+
|
|
35
|
+
if (usesCommonJSGlobals || usesRequire || usesModuleExports) {
|
|
36
|
+
// Inject ESM equivalents at the top of the file
|
|
37
|
+
let esmGlobals = ''
|
|
38
|
+
|
|
39
|
+
if (usesRequire || usesModuleExports) {
|
|
40
|
+
esmGlobals += `import { createRequire } from 'module';
|
|
41
|
+
const require = createRequire(import.meta.url);
|
|
42
|
+
const module = { exports: {} };
|
|
43
|
+
const exports = module.exports;
|
|
44
|
+
|
|
45
|
+
`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (usesCommonJSGlobals) {
|
|
49
|
+
esmGlobals += `import { fileURLToPath as __fileURLToPath } from 'url';
|
|
50
|
+
import { dirname as __dirname_fn } from 'path';
|
|
51
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
52
|
+
const __dirname = __dirname_fn(__filename);
|
|
53
|
+
|
|
54
|
+
`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
jsContent = esmGlobals + jsContent
|
|
58
|
+
|
|
59
|
+
// If module.exports is used, we need to export it as default
|
|
60
|
+
if (usesModuleExports) {
|
|
61
|
+
jsContent += `\nexport default module.exports;\n`
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return jsContent
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create a map to track transpiled files
|
|
69
|
+
const transpiledFiles = new Map()
|
|
70
|
+
const baseDir = path.dirname(mainFilePath)
|
|
71
|
+
|
|
72
|
+
// Transpile main file
|
|
73
|
+
let jsContent = transpileTS(mainFilePath)
|
|
74
|
+
|
|
75
|
+
// Find and transpile all relative TypeScript imports
|
|
76
|
+
// Match: import ... from './file' or '../file' or './file.ts'
|
|
77
|
+
const importRegex = /from\s+['"](\..+?)(?:\.ts)?['"]/g
|
|
78
|
+
let match
|
|
79
|
+
const imports = []
|
|
80
|
+
|
|
81
|
+
while ((match = importRegex.exec(jsContent)) !== null) {
|
|
82
|
+
imports.push(match[1])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Transpile each imported TypeScript file
|
|
86
|
+
for (const relativeImport of imports) {
|
|
87
|
+
let importedPath = path.resolve(baseDir, relativeImport)
|
|
88
|
+
|
|
89
|
+
// Handle .js extensions that might actually be .ts files
|
|
90
|
+
if (importedPath.endsWith('.js')) {
|
|
91
|
+
const tsVersion = importedPath.replace(/\.js$/, '.ts')
|
|
92
|
+
if (fs.existsSync(tsVersion)) {
|
|
93
|
+
importedPath = tsVersion
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Try adding .ts extension if file doesn't exist and no extension provided
|
|
98
|
+
if (!path.extname(importedPath)) {
|
|
99
|
+
if (fs.existsSync(importedPath + '.ts')) {
|
|
100
|
+
importedPath = importedPath + '.ts'
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If it's a TypeScript file, transpile it
|
|
105
|
+
if (importedPath.endsWith('.ts') && fs.existsSync(importedPath)) {
|
|
106
|
+
const transpiledImportContent = transpileTS(importedPath)
|
|
107
|
+
const tempImportFile = importedPath.replace(/\.ts$/, '.temp.mjs')
|
|
108
|
+
fs.writeFileSync(tempImportFile, transpiledImportContent)
|
|
109
|
+
transpiledFiles.set(importedPath, tempImportFile)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Replace imports in the main file to point to temp .mjs files
|
|
114
|
+
jsContent = jsContent.replace(
|
|
115
|
+
/from\s+['"](\..+?)(?:\.ts)?['"]/g,
|
|
116
|
+
(match, importPath) => {
|
|
117
|
+
let resolvedPath = path.resolve(baseDir, importPath)
|
|
118
|
+
|
|
119
|
+
// Handle .js extension that might be .ts
|
|
120
|
+
if (resolvedPath.endsWith('.js')) {
|
|
121
|
+
const tsVersion = resolvedPath.replace(/\.js$/, '.ts')
|
|
122
|
+
if (transpiledFiles.has(tsVersion)) {
|
|
123
|
+
const tempFile = transpiledFiles.get(tsVersion)
|
|
124
|
+
const relPath = path.relative(baseDir, tempFile).replace(/\\/g, '/')
|
|
125
|
+
return `from './${relPath}'`
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Try with .ts extension
|
|
130
|
+
const tsPath = resolvedPath.endsWith('.ts') ? resolvedPath : resolvedPath + '.ts'
|
|
131
|
+
|
|
132
|
+
// If we transpiled this file, use the temp file
|
|
133
|
+
if (transpiledFiles.has(tsPath)) {
|
|
134
|
+
const tempFile = transpiledFiles.get(tsPath)
|
|
135
|
+
// Get relative path from main temp file to this temp file
|
|
136
|
+
const relPath = path.relative(baseDir, tempFile).replace(/\\/g, '/')
|
|
137
|
+
return `from './${relPath}'`
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Otherwise, keep the import as-is
|
|
141
|
+
return match
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
// Create a temporary JS file with .mjs extension for the main file
|
|
146
|
+
const tempJsFile = mainFilePath.replace(/\.ts$/, '.temp.mjs')
|
|
147
|
+
fs.writeFileSync(tempJsFile, jsContent)
|
|
148
|
+
|
|
149
|
+
// Store all temp files for cleanup
|
|
150
|
+
const allTempFiles = [tempJsFile, ...Array.from(transpiledFiles.values())]
|
|
151
|
+
|
|
152
|
+
return { tempFile: tempJsFile, allTempFiles }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Clean up temporary transpiled files
|
|
157
|
+
* @param {string[]} tempFiles - Array of temp file paths to delete
|
|
158
|
+
*/
|
|
159
|
+
export function cleanupTempFiles(tempFiles) {
|
|
160
|
+
for (const file of tempFiles) {
|
|
161
|
+
if (fs.existsSync(file)) {
|
|
162
|
+
try {
|
|
163
|
+
fs.unlinkSync(file)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// Ignore cleanup errors
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|