Skip to content

fix(vite-node): provide import.meta.filename and dirname #5011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 19, 2024
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/node": "^18.18.9",
"@types/node": "^20.11.5",
"@types/ws": "^8.5.9",
"@vitest/browser": "workspace:*",
"@vitest/coverage-istanbul": "workspace:*",
Expand Down
12 changes: 9 additions & 3 deletions packages/vite-node/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,14 @@ export class ViteNodeRunner {
const modulePath = cleanUrl(moduleId)
// disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
const href = pathToFileURL(modulePath).href
const meta = { url: href, env }
const __filename = fileURLToPath(href)
const __dirname = dirname(__filename)
const meta = {
url: href,
env,
filename: __filename,
dirname: __dirname,
}
const exports = Object.create(null)
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module',
Expand Down Expand Up @@ -344,7 +351,6 @@ export class ViteNodeRunner {
})

Object.assign(mod, { code: transformed, exports })
const __filename = fileURLToPath(href)
const moduleProxy = {
set exports(value) {
exportAll(cjsExports, value)
Expand Down Expand Up @@ -388,7 +394,7 @@ export class ViteNodeRunner {
exports: cjsExports,
module: moduleProxy,
__filename,
__dirname: dirname(__filename),
__dirname,
})

debugExecute(__filename)
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"@types/istanbul-reports": "^3.0.4",
"@types/jsdom": "^21.1.6",
"@types/micromatch": "^4.0.6",
"@types/node": "^20.11.5",
"@types/prompts": "^2.4.9",
"@types/sinonjs__fake-timers": "^8.1.5",
"birpc": "0.2.14",
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/runtime/external-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ export class ExternalModulesExecutor {
}

public resolveModule = async (specifier: string, referencer: string) => {
const identifier = await this.resolve(specifier, referencer)
const identifier = this.resolve(specifier, referencer)
return await this.createModule(identifier)
}

public async resolve(specifier: string, parent: string) {
public resolve(specifier: string, parent: string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's behind this change? I'm now running into error where resolveModule is passing Promise to createModule. Then this part:

if (identifier.startsWith('data:'))

... crashes with error

TypeError: identifier.startsWith is not a function
 ❯ ExternalModulesExecutor.getModuleInformation ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:575:20
 ❯ ExternalModulesExecutor.createModule ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:597:38
 ❯ ExternalModulesExecutor.resolveModule ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:505:23
 ❯ ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:258:58
 ❯ ModuleWrap.<anonymous> node:internal/vm/module:306:30
 ❯ SourceTextModule.<computed> node:internal/vm/module:305:36
 ❯ SourceTextModule.link node:internal/vm/module:199:22
 ❯ EsmExecutor.evaluateModule ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:258:11
 ❯ ExternalModulesExecutor.import ../../packages/vitest/dist/vendor/vm.UXtVveu9.js:621:20
 ❯ VitestExecutor.interopedImport ../../packages/vite-node/dist/client.mjs:383:28

And by debugging the identifier there, it's Promise and not string.

This can be reproduced with test/run tests. The exec-argv one is crashing with vmThreads.

Copy link
Member

@AriPerkkio AriPerkkio Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import.meta.resolve is async. This is on Node v18.17.1:

> console.log(nativeResolve.toString())

async function resolve(specifier, parentUrl = defaultParentUrl) {
    return PromisePrototypeThen(
      asyncESM.esmLoader.resolve(specifier, parentUrl),
      ({ url }) => url,
      (error) => (
        error.code === 'ERR_UNSUPPORTED_DIR_IMPORT' ?
          error.url : PromiseReject(error)),
    );
  }

Though it's not in sources: https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/internal/modules/esm/initialize_import_meta.js#L22

Oh well

for (const resolver of this.resolvers) {
const id = resolver(specifier, parent)
if (id)
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/runtime/vm/commonjs-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export class CommonjsExecutor {
return _require
}

static register = () => {
throw new Error(`[vitest] "register" is not available when running in Vitest.`)
}

_compile(code: string, filename: string) {
const cjsModule = Module.wrap(code)
const script = new vm.Script(cjsModule, {
Expand Down
11 changes: 9 additions & 2 deletions packages/vitest/src/runtime/vm/esm-executor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable antfu/no-cjs-exports */

import type vm from 'node:vm'
import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
import type { ExternalModulesExecutor } from '../external-executor'
import type { VMModule } from './types'
import { SourceTextModule, SyntheticModule } from './utils'
Expand Down Expand Up @@ -62,8 +64,13 @@ export class EsmExecutor {
importModuleDynamically: this.executor.importModuleDynamically,
initializeImportMeta: (meta, mod) => {
meta.url = mod.identifier
meta.resolve = (specifier: string, importer?: string) => {
return this.executor.resolve(specifier, importer ?? mod.identifier)
if (mod.identifier.startsWith('file:')) {
const filename = fileURLToPath(mod.identifier)
meta.filename = filename
meta.dirname = dirname(filename)
}
meta.resolve = (specifier: string, importer?: string | URL) => {
return this.executor.resolve(specifier, importer != null ? importer.toString() : mod.identifier)
}
},
},
Expand Down
Loading