Skip to content

Commit

Permalink
fix: correctly resolve filename, when running code (#2439)
Browse files Browse the repository at this point in the history
* fix: correctly resolve filename, when running code

* fix: update lastHMRTimestamp to enable caching

* chore: make test files unique

* test: fix flacky shard test

* chore: use consistent id, when transforming module

* fix: use filepath for c8 coverage

* fix: ensure correct caching, if fsPath and filepath are different

* chore: check fsPath as clean url

* chore: ignore error on wrong cache

* chore: ignore paths with query instead
  • Loading branch information
sheremet-va committed Dec 6, 2022
1 parent 8595c0e commit 2e7892c
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 32 deletions.
6 changes: 4 additions & 2 deletions packages/coverage-c8/src/provider.ts
Expand Up @@ -58,11 +58,13 @@ export class C8CoverageProvider implements CoverageProvider {
if (!map)
return

const url = _url.pathToFileURL(file.split('?')[0]).href
const filepath = result.file || file.split('?')[0]

const url = _url.pathToFileURL(filepath).href

let code: string | undefined
try {
code = (await fs.readFile(file)).toString()
code = (await fs.readFile(filepath)).toString()
}
catch { }

Expand Down
41 changes: 24 additions & 17 deletions packages/vite-node/src/client.ts
Expand Up @@ -191,7 +191,7 @@ export class ViteNodeRunner {
async directRequest(id: string, fsPath: string, _callstack: string[]) {
const callstack = [..._callstack, fsPath]

const mod = this.moduleCache.get(fsPath)
let mod = this.moduleCache.get(fsPath)

const request = async (dep: string) => {
const depFsPath = toFilePath(normalizeRequestId(dep, this.options.base), this.root)
Expand Down Expand Up @@ -222,11 +222,6 @@ export class ViteNodeRunner {
Object.defineProperty(request, 'callstack', { get: () => callstack })

const resolveId = async (dep: string, callstackPosition = 1) => {
// probably means it was passed as variable
// and wasn't transformed by Vite
// or some dependency name was passed
// runner.executeFile('@scope/name')
// runner.executeFile(myDynamicName)
if (this.options.resolveId && this.shouldResolveId(dep)) {
let importer = callstack[callstack.length - callstackPosition]
if (importer && importer.startsWith('mock:'))
Expand All @@ -238,14 +233,30 @@ export class ViteNodeRunner {
return dep
}

id = await resolveId(id, 2)

const requestStubs = this.options.requestStubs || DEFAULT_REQUEST_STUBS
if (id in requestStubs)
return requestStubs[id]

// eslint-disable-next-line prefer-const
let { code: transformed, externalize } = await this.options.fetchModule(id)
let { code: transformed, externalize, file } = await this.options.fetchModule(id)

// in case we resolved fsPath incorrectly, Vite will return the correct file path
// in that case we need to update cache, so we don't have the same module as different exports
// but we ignore fsPath that has custom query, because it might need to be different
if (file && !fsPath.includes('?') && fsPath !== file) {
if (this.moduleCache.has(file)) {
mod = this.moduleCache.get(file)
this.moduleCache.set(fsPath, mod)
if (mod.promise)
return mod.promise
if (mod.exports)
return mod.exports
}
else {
this.moduleCache.set(file, mod)
}
}

if (externalize) {
debugNative(externalize)
const exports = await this.interopedImport(externalize)
Expand All @@ -257,19 +268,16 @@ export class ViteNodeRunner {
throw new Error(`[vite-node] Failed to load ${id}`)

// disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
const url = pathToFileURL(fsPath).href
const url = pathToFileURL(file || fsPath).href
const meta = { url }
const exports: any = Object.create(null)
const exports = Object.create(null)
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module',
enumerable: false,
configurable: false,
})
// this prosxy is triggered only on exports.name and module.exports access
const cjsExports = new Proxy(exports, {
get(_, p, receiver) {
return Reflect.get(exports, p, receiver)
},
set(_, p, value) {
if (!Reflect.has(exports, 'default'))
exports.default = {}
Expand All @@ -289,8 +297,7 @@ export class ViteNodeRunner {
},
})

Object.assign(mod, { code: transformed, exports, evaluated: false })

Object.assign(mod, { code: transformed, exports })
const __filename = fileURLToPath(url)
const moduleProxy = {
set exports(value) {
Expand Down Expand Up @@ -345,7 +352,7 @@ export class ViteNodeRunner {
const codeDefinition = `'use strict';async (${Object.keys(context).join(',')})=>{{`
const code = `${codeDefinition}${transformed}\n}}`
const fn = vm.runInThisContext(code, {
filename: fsPath,
filename: __filename,
lineOffset: 0,
columnOffset: -codeDefinition.length,
})
Expand Down
23 changes: 15 additions & 8 deletions packages/vite-node/src/server.ts
Expand Up @@ -4,17 +4,14 @@ import type { TransformResult, ViteDevServer } from 'vite'
import createDebug from 'debug'
import type { DebuggerOptions, FetchResult, RawSourceMap, ViteNodeResolveId, ViteNodeServerOptions } from './types'
import { shouldExternalize } from './externalize'
import { toArray, toFilePath } from './utils'
import { cleanUrl, normalizeModuleId, toArray, toFilePath } from './utils'
import { Debugger } from './debug'
import { withInlineSourcemap } from './source-map'

export * from './externalize'

const debugRequest = createDebug('vite-node:server:request')

// store the original reference to avoid it been mocked
const RealDate = Date

export class ViteNodeServer {
private fetchPromiseMap = new Map<string, Promise<FetchResult>>()
private transformPromiseMap = new Map<string, Promise<TransformResult | null | undefined>>()
Expand Down Expand Up @@ -83,6 +80,7 @@ export class ViteNodeServer {
}

async fetchModule(id: string): Promise<FetchResult> {
id = normalizeModuleId(id)
// reuse transform for concurrent requests
if (!this.fetchPromiseMap.has(id)) {
this.fetchPromiseMap.set(id,
Expand Down Expand Up @@ -130,27 +128,36 @@ export class ViteNodeServer {
const filePath = toFilePath(id, this.server.config.root)

const module = this.server.moduleGraph.getModuleById(id)
const timestamp = module?.lastHMRTimestamp || RealDate.now()
const timestamp = module ? module.lastHMRTimestamp : null
const cache = this.fetchCache.get(filePath)
if (timestamp && cache && cache.timestamp >= timestamp)
if (cache?.result.id)
id = cache.result.id
if (timestamp !== null && cache && cache.timestamp >= timestamp)
return cache.result

const time = Date.now()
const externalize = await this.shouldExternalize(filePath)
let duration: number | undefined
if (externalize) {
result = { externalize }
this.debugger?.recordExternalize(id, externalize)
}
else {
let file = module?.file
if (!file) {
const [, resolvedId] = await this.server.moduleGraph.resolveUrl(id, true)
id = resolvedId
file = cleanUrl(resolvedId)
}
const start = performance.now()
const r = await this._transformRequest(id)
duration = performance.now() - start
result = { code: r?.code, map: r?.map as unknown as RawSourceMap }
result = { file, id, code: r?.code, map: r?.map as unknown as RawSourceMap }
}

this.fetchCache.set(filePath, {
duration,
timestamp,
timestamp: time,
result,
})

Expand Down
2 changes: 2 additions & 0 deletions packages/vite-node/src/types.ts
Expand Up @@ -31,6 +31,8 @@ export interface FetchResult {
code?: string
externalize?: string
map?: RawSourceMap
id?: string
file?: string
}

export type HotContext = Omit<ViteHotContext, 'acceptDeps' | 'decline'>
Expand Down
6 changes: 6 additions & 0 deletions packages/vite-node/src/utils.ts
Expand Up @@ -34,6 +34,12 @@ export function normalizeRequestId(id: string, base?: string): string {
.replace(/\?+$/, '') // remove end query mark
}

export const queryRE = /\?.*$/s
export const hashRE = /#.*$/s

export const cleanUrl = (url: string): string =>
url.replace(hashRE, '').replace(queryRE, '')

export function normalizeModuleId(id: string) {
return id
.replace(/\\/g, '/')
Expand Down
9 changes: 6 additions & 3 deletions packages/vitest/src/node/core.ts
Expand Up @@ -115,9 +115,7 @@ export class Vitest {
try {
await this.cache.results.readFromCache()
}
catch (err) {
this.logger.error(`[vitest] Error, while trying to parse cache in ${this.cache.results.getCachePath()}:`, err)
}
catch {}
}

async initCoverageProvider() {
Expand Down Expand Up @@ -311,6 +309,8 @@ export class Vitest {
}

async runFiles(paths: string[]) {
paths = Array.from(new Set(paths))

// previous run
await this.runningPromise
this.state.startCollectingPaths()
Expand Down Expand Up @@ -396,6 +396,9 @@ export class Vitest {

private _rerunTimer: any
private async scheduleRerun(triggerId: string) {
const mod = this.server.moduleGraph.getModuleById(triggerId)
if (mod)
mod.lastHMRTimestamp = Date.now()
const currentCount = this.restartsCount
clearTimeout(this._rerunTimer)
await this.runningPromise
Expand Down
4 changes: 2 additions & 2 deletions test/shard/shard-test.test.ts
Expand Up @@ -8,11 +8,11 @@ const runVitest = async (args: string[]) => {
}

const parsePaths = (stdout: string) => {
return stdout
return Array.from(new Set(stdout
.split('\n')
.filter(line => line && line.includes('.test.js'))
.map(file => basename(file.trim().split(' ')[1]))
.sort()
.sort()))
}

test('--shard=1/1', async () => {
Expand Down

0 comments on commit 2e7892c

Please sign in to comment.