Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/vitest/src/runtime/external-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import vm from 'node:vm'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { dirname } from 'node:path'
import { statSync } from 'node:fs'
import { existsSync, statSync } from 'node:fs'
import { extname, join, normalize } from 'pathe'
import { getCachedData, isNodeBuiltin, setCacheData } from 'vite-node/utils'
import type { RuntimeRPC } from '../types/rpc'
Expand Down Expand Up @@ -188,6 +188,14 @@ export class ExternalModulesExecutor {
private async createModule(identifier: string): Promise<VMModule> {
const { type, url, path } = this.getModuleInformation(identifier)

// create ERR_MODULE_NOT_FOUND on our own since latest NodeJS's import.meta.resolve doesn't throw on non-existing namespace or path
// https://github.com/nodejs/node/pull/49038
if ((type === 'module' || type === 'commonjs') && !existsSync(path)) {
const error = new Error(`Cannot find module '${path}'`)
;(error as any).code = 'ERR_MODULE_NOT_FOUND'
throw error
}

switch (type) {
case 'data':
return this.esm.createDataModule(identifier)
Expand Down
15 changes: 15 additions & 0 deletions test/vm-threads/src/external/not-found.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export async function importPackage() {
await import('@vitest/non-existing-package')
}

export async function importPath() {
await import('./non-existing-path')
}

export async function importBuiltin() {
await import('node:non-existing-builtin')
}

export async function importNamespace() {
await import('non-existing-namespace:xyz')
}
36 changes: 36 additions & 0 deletions test/vm-threads/test/not-found.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect, it } from 'vitest'

// @ts-expect-error untyped
import * as notFound from '../src/external/not-found.js'

it('path', async () => {
await expect(() => notFound.importPath()).rejects.toMatchObject({
code: 'ERR_MODULE_NOT_FOUND',
message: expect.stringMatching(/Cannot find module '.*?non-existing-path'/),
})
})

// NodeJs's import.meta.resolve throws ERR_MODULE_NOT_FOUND error only this case.
// For other cases, similar errors are fabricated by Vitest to mimic NodeJs's behavior.
it('package', async () => {
await expect(() => notFound.importPackage()).rejects.toMatchObject({
code: 'ERR_MODULE_NOT_FOUND',
message: expect.stringContaining('Cannot find package \'@vitest/non-existing-package\''),
})
})

it('builtin', async () => {
await expect(() => notFound.importBuiltin()).rejects.toMatchObject({
code: 'ERR_MODULE_NOT_FOUND',
message: 'Cannot find module \'node:non-existing-builtin\'',
})
})

// this test fails before node 20.3.0 since it throws a different error (cf. https://github.com/nodejs/node/pull/47824)
// > Only URLs with a scheme in: file and data are supported by the default ESM loader. Received protocol 'non-existing-namespace:'
it('namespace', async () => {
await expect(() => notFound.importNamespace()).rejects.toMatchObject({
code: 'ERR_MODULE_NOT_FOUND',
message: 'Cannot find module \'non-existing-namespace:xyz\'',
})
})