Skip to content

Commit

Permalink
fix(vm): improve error when module is not found (#5053)
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa committed Feb 6, 2024
1 parent 5b1021d commit 79a50c3
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 1 deletion.
10 changes: 9 additions & 1 deletion packages/vitest/src/runtime/external-executor.ts
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
@@ -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
@@ -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\'',
})
})

0 comments on commit 79a50c3

Please sign in to comment.