|
1 | 1 | import type vm from 'node:vm' |
2 | 2 | import type { ExternalModulesExecutor, SyncModuleDisposition } from '../external-executor' |
3 | | -import type { VMModule, VMSourceTextModule, VMSyntheticModule } from './types' |
| 3 | +import type { SourceTextModuleOptions, VMModule, VMSourceTextModule, VMSyntheticModule } from './types' |
4 | 4 | import { dirname } from 'node:path' |
5 | 5 | import { fileURLToPath } from 'node:url' |
6 | 6 | import { VITEST_VM_CONTEXT_SYMBOL } from '../moduleRunner/startVitestModuleRunner' |
@@ -164,22 +164,38 @@ export class EsmExecutor { |
164 | 164 | code: string, |
165 | 165 | ): VMSourceTextModule { |
166 | 166 | const codeCache = this.executor.codeCache |
167 | | - const cachedData = codeCache?.get(fileURL, code) |
168 | | - const m = new SourceTextModule(code, { |
| 167 | + let cachedData = codeCache?.get(fileURL, code) |
| 168 | + const options: SourceTextModuleOptions = { |
169 | 169 | identifier: fileURL, |
170 | 170 | context: this.context, |
171 | | - cachedData, |
172 | 171 | // static callbacks: Node keeps them registered for as long as the |
173 | 172 | // module's host-defined-options symbol is alive, so a closure here would |
174 | 173 | // retain this executor (and the whole test file's world) beyond the |
175 | 174 | // file's lifetime. The executor is recovered from the module's context |
176 | 175 | // at call time instead. |
177 | 176 | importModuleDynamically: staticImportModuleDynamically, |
178 | 177 | initializeImportMeta: staticInitializeImportMeta, |
179 | | - }) |
| 178 | + } |
| 179 | + let m: VMSourceTextModule | undefined |
| 180 | + if (cachedData) { |
| 181 | + try { |
| 182 | + m = new SourceTextModule(code, { ...options, cachedData }) |
| 183 | + } |
| 184 | + catch (error: any) { |
| 185 | + // unlike vm.Script, a module throws when V8 rejects the cache (e.g. the |
| 186 | + // V8 flags changed at runtime): compile from source instead |
| 187 | + if (error?.code !== 'ERR_VM_MODULE_CACHED_DATA_REJECTED') { |
| 188 | + throw error |
| 189 | + } |
| 190 | + codeCache!.delete(fileURL) |
| 191 | + cachedData = undefined |
| 192 | + } |
| 193 | + } |
| 194 | + m ??= new SourceTextModule(code, options) |
180 | 195 | // the code cache of a SourceTextModule must be created before evaluation |
181 | 196 | if (!cachedData) { |
182 | | - codeCache?.store(fileURL, code, () => m.createCachedData()) |
| 197 | + const created = m |
| 198 | + codeCache?.store(fileURL, code, () => created.createCachedData()) |
183 | 199 | } |
184 | 200 | return m |
185 | 201 | } |
|
0 commit comments