fix: handle addWatchFile in load hooks (#14967)

This commit is contained in:
Bjorn Lu 2023-11-14 02:06:58 +08:00 committed by GitHub
parent 4033a320d6
commit a0ab85bdf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 6 deletions

View File

@ -82,7 +82,7 @@ import { FS_PREFIX } from '../constants'
import type { ResolvedConfig } from '../config'
import { createPluginHookUtils, getHookHandler } from '../plugins'
import { buildErrorMessage } from './middlewares/error'
import type { ModuleGraph } from './moduleGraph'
import type { ModuleGraph, ModuleNode } from './moduleGraph'
const noop = () => {}
@ -182,6 +182,11 @@ export async function createPluginContainer(
// ---------------------------------------------------------------------------
const watchFiles = new Set<string>()
// _addedFiles from the `load()` hook gets saved here so it can be reused in the `transform()` hook
const moduleNodeToLoadAddedImports = new WeakMap<
ModuleNode,
Set<string> | null
>()
const minimalContext: MinimalPluginContext = {
meta: {
@ -273,6 +278,13 @@ export async function createPluginContainer(
}
}
function updateModuleLoadAddedImports(id: string, ctx: Context) {
const module = moduleGraph?.getModuleById(id)
if (module) {
moduleNodeToLoadAddedImports.set(module, ctx._addedImports)
}
}
// we should create a new context for each async hook pipeline so that the
// active plugin in that pipeline can be tracked in a concurrency-safe manner.
// using a class to make creating new contexts more efficient
@ -526,9 +538,9 @@ export async function createPluginContainer(
sourcemapChain: NonNullable<SourceDescription['map']>[] = []
combinedMap: SourceMap | { mappings: '' } | null = null
constructor(filename: string, code: string, inMap?: SourceMap | string) {
constructor(id: string, code: string, inMap?: SourceMap | string) {
super()
this.filename = filename
this.filename = id
this.originalCode = code
if (inMap) {
if (debugSourcemapCombine) {
@ -537,6 +549,11 @@ export async function createPluginContainer(
}
this.sourcemapChain.push(inMap)
}
// Inherit `_addedImports` from the `load()` hook
const node = moduleGraph?.getModuleById(id)
if (node) {
this._addedImports = moduleNodeToLoadAddedImports.get(node) ?? null
}
}
_getCombinedSourcemap() {
@ -725,9 +742,11 @@ export async function createPluginContainer(
if (isObject(result)) {
updateModuleInfo(id, result)
}
updateModuleLoadAddedImports(id, ctx)
return result
}
}
updateModuleLoadAddedImports(id, ctx)
return null
},

View File

@ -1,9 +1,12 @@
import { expect, test } from 'vitest'
import { editFile, page, untilUpdated } from '~utils'
test('should re-run transform when plugin-dep file is edited', async () => {
test('should re-run transform when dependencies are edited', async () => {
expect(await page.textContent('#transform-count')).toBe('1')
await editFile('plugin-dep.js', (str) => str)
editFile('plugin-dep.js', (str) => str)
await untilUpdated(() => page.textContent('#transform-count'), '2')
editFile('plugin-dep-load.js', (str) => str)
await untilUpdated(() => page.textContent('#transform-count'), '3')
})

View File

@ -0,0 +1 @@
// Empty file for detecting changes in tests

View File

@ -1,12 +1,19 @@
import { resolve } from 'node:path'
import { defineConfig, normalizePath } from 'vite'
const file = normalizePath(resolve(__dirname, 'index.js'))
let transformCount = 1
const transformPlugin = {
name: 'transform',
load(id) {
if (id === file) {
// Ensure `index.js` is reloaded if 'plugin-dep-load.js' is changed
this.addWatchFile('./plugin-dep-load.js')
}
},
transform(code, id) {
if (id === normalizePath(resolve(__dirname, 'index.js'))) {
if (id === file) {
// Ensure `index.js` is reevaluated if 'plugin-dep.js' is changed
this.addWatchFile('./plugin-dep.js')