Skip to content

Commit

Permalink
fix: use webpack loader context to implement addWatchFile (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Feb 1, 2024
1 parent 3b05d44 commit a3bb13f
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 17 deletions.
30 changes: 19 additions & 11 deletions src/webpack/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,23 @@ import type { Compilation } from 'webpack'
import { Parser } from 'acorn'
import type { UnpluginBuildContext } from '../types'

export function createContext(compilation?: Compilation): UnpluginBuildContext {
interface ContextOptions {
addWatchFile(file: string): void
getWatchFiles(): string[]
}

export function contextOptionsFromCompilation(compilation: Compilation): ContextOptions {
return {
addWatchFile(file) {
(compilation.fileDependencies ?? compilation.compilationDependencies).add(file)
},
getWatchFiles() {
return Array.from(compilation.fileDependencies ?? compilation.compilationDependencies)
},
}
}

export function createContext(options: ContextOptions, compilation?: Compilation): UnpluginBuildContext {
return {
parse(code: string, opts: any = {}) {
return Parser.parse(code, {
Expand All @@ -17,11 +33,7 @@ export function createContext(compilation?: Compilation): UnpluginBuildContext {
})
},
addWatchFile(id) {
if (!compilation)
throw new Error('unplugin/webpack: addWatchFile outside supported hooks (buildStart, buildEnd, load, transform, watchChange)');
(compilation.fileDependencies ?? compilation.compilationDependencies).add(
resolve(process.cwd(), id),
)
options.addWatchFile(resolve(process.cwd(), id))
},
emitFile(emittedFile) {
const outFileName = emittedFile.fileName || emittedFile.name
Expand All @@ -45,11 +57,7 @@ export function createContext(compilation?: Compilation): UnpluginBuildContext {
}
},
getWatchFiles() {
if (!compilation)
throw new Error('unplugin/webpack: getWatchFiles outside supported hooks (buildStart, buildEnd, load, transform, watchChange)')
return Array.from(
compilation.fileDependencies ?? compilation.compilationDependencies,
)
return options.getWatchFiles()
},
}
}
19 changes: 15 additions & 4 deletions src/webpack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import VirtualModulesPlugin from 'webpack-virtual-modules'
import type { ResolvePluginInstance, Resolver } from 'webpack'
import type { ResolvedUnpluginOptions, UnpluginContext, UnpluginContextMeta, UnpluginFactory, UnpluginInstance, WebpackCompiler } from '../types'
import { normalizeAbsolutePath, shouldLoad, toArray, transformUse } from '../utils'
import { createContext } from './context'
import { contextOptionsFromCompilation, createContext } from './context'

const TRANSFORM_LOADER = resolve(
__dirname,
Expand Down Expand Up @@ -89,7 +89,18 @@ export function getWebpackPlugin<UserOptions = Record<string, never>>(
const isEntry = requestContext.issuer === ''

// call hook
const context = createContext()
// resolveContext.fileDependencies is typed as a WriteOnlySet, so make our own copy here
// so we can return it from getWatchFiles.
const fileDependencies = new Set<string>()
const context = createContext({
addWatchFile(file) {
fileDependencies.add(file)
resolveContext.fileDependencies?.add(file)
},
getWatchFiles() {
return Array.from(fileDependencies)
},
})
let error: Error | undefined
const pluginContext: UnpluginContext = {
error(msg: string | Error) {
Expand Down Expand Up @@ -178,7 +189,7 @@ export function getWebpackPlugin<UserOptions = Record<string, never>>(

if (plugin.watchChange || plugin.buildStart) {
compiler.hooks.make.tapPromise(plugin.name, async (compilation) => {
const context = createContext(compilation)
const context = createContext(contextOptionsFromCompilation(compilation), compilation)
if (plugin.watchChange && (compiler.modifiedFiles || compiler.removedFiles)) {
const promises: Promise<void>[] = []
if (compiler.modifiedFiles) {
Expand All @@ -201,7 +212,7 @@ export function getWebpackPlugin<UserOptions = Record<string, never>>(

if (plugin.buildEnd) {
compiler.hooks.emit.tapPromise(plugin.name, async (compilation) => {
await plugin.buildEnd!.call(createContext(compilation))
await plugin.buildEnd!.call(createContext(contextOptionsFromCompilation(compilation), compilation))
})
}

Expand Down
9 changes: 8 additions & 1 deletion src/webpack/loaders/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ export default async function load(this: LoaderContext<any>, source: string, map
id = decodeURIComponent(id.slice(plugin.__virtualModulePrefix.length))

const res = await plugin.load.call(
{ ...this._compilation && createContext(this._compilation) as any, ...context },
{ ...createContext({
addWatchFile: (file) => {
this.addDependency(file)
},
getWatchFiles: () => {
return this.getDependencies()
},
}, this._compilation), ...context },
normalizeAbsolutePath(id),
)

Expand Down
9 changes: 8 additions & 1 deletion src/webpack/loaders/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ export default async function transform(this: LoaderContext<{ unpluginName: stri
warn: error => this.emitWarning(typeof error === 'string' ? new Error(error) : error),
}
const res = await plugin.transform.call(
{ ...this._compilation && createContext(this._compilation) as any, ...context },
{ ...createContext({
addWatchFile: (file) => {
this.addDependency(file)
},
getWatchFiles: () => {
return this.getDependencies()
},
}, this._compilation), ...context },
source,
this.resource,
)
Expand Down

0 comments on commit a3bb13f

Please sign in to comment.