diff --git a/src/esbuild/index.ts b/src/esbuild/index.ts index 27085b39..ccd773b7 100644 --- a/src/esbuild/index.ts +++ b/src/esbuild/index.ts @@ -112,6 +112,8 @@ export function getEsbuildPlugin ( if (plugin.load || plugin.transform) { onLoad({ filter: onLoadFilter }, async (args) => { + const id = args.path + args.suffix + const errors: PartialMessage[] = [] const warnings: PartialMessage[] = [] const pluginContext: UnpluginContext = { @@ -125,7 +127,7 @@ export function getEsbuildPlugin ( let code: string | undefined, map: SourceMap | null | undefined if (plugin.load) { - const result = await plugin.load.call(Object.assign(context, pluginContext), args.path) + const result = await plugin.load.call(Object.assign(context, pluginContext), id) if (typeof result === 'string') { code = result } else if (typeof result === 'object' && result !== null) { @@ -149,7 +151,7 @@ export function getEsbuildPlugin ( return { contents: code, errors, warnings, loader: guessLoader(args.path), resolveDir } } - if (!plugin.transformInclude || plugin.transformInclude(args.path)) { + if (!plugin.transformInclude || plugin.transformInclude(id)) { if (!code) { // caution: 'utf8' assumes the input file is not in binary. // if you want your plugin handle binary files, make sure to @@ -157,7 +159,7 @@ export function getEsbuildPlugin ( code = await fs.promises.readFile(args.path, 'utf8') } - const result = await plugin.transform.call(Object.assign(context, pluginContext), code, args.path) + const result = await plugin.transform.call(Object.assign(context, pluginContext), code, id) if (typeof result === 'string') { code = result } else if (typeof result === 'object' && result !== null) { diff --git a/src/webpack/index.ts b/src/webpack/index.ts index 65db6fa9..2844b2b3 100644 --- a/src/webpack/index.ts +++ b/src/webpack/index.ts @@ -2,7 +2,7 @@ import fs from 'fs' import { fileURLToPath } from 'url' import { resolve, dirname } from 'path' import VirtualModulesPlugin from 'webpack-virtual-modules' -import type { ResolvePluginInstance } from 'webpack' +import type { ResolvePluginInstance, RuleSetUseItem } from 'webpack' import type { UnpluginContextMeta, UnpluginInstance, UnpluginFactory, WebpackCompiler, ResolvedUnpluginOptions } from '../types' import { slash, backSlash } from './utils' import { createContext } from './context' @@ -48,24 +48,26 @@ export function getWebpackPlugin ( // transform hook if (plugin.transform) { + const useLoader: RuleSetUseItem[] = [{ + loader: TRANSFORM_LOADER, + ident: plugin.name, + options: { + unpluginName: plugin.name + } + }] + const useNone: RuleSetUseItem[] = [] compiler.options.module.rules.push({ - include (id: string) { - if (id == null) { - return false - } - if (plugin.transformInclude) { - return plugin.transformInclude(slash(id)) - } else { - return true - } - }, enforce: plugin.enforce, - use: [{ - loader: TRANSFORM_LOADER, - options: { - unpluginName: plugin.name + use: (data: { resource: string | null, resourceQuery: string }) => { + if (data.resource == null) { + return useNone } - }] + const id = slash(data.resource + (data.resourceQuery || '')) + if (!plugin.transformInclude || plugin.transformInclude(id)) { + return useLoader + } + return useNone + } }) } diff --git a/test/fixtures/transform/__test__/build.test.ts b/test/fixtures/transform/__test__/build.test.ts index 6064e788..c2895a4f 100644 --- a/test/fixtures/transform/__test__/build.test.ts +++ b/test/fixtures/transform/__test__/build.test.ts @@ -10,6 +10,7 @@ describe('transform build', () => { expect(content).toContain('NON-TARGET: __UNPLUGIN__') expect(content).toContain('TARGET: [Injected Vite]') + expect(content).toContain('QUERY: [Injected Vite]') }) it('rollup', async () => { @@ -24,6 +25,7 @@ describe('transform build', () => { expect(content).toContain('NON-TARGET: __UNPLUGIN__') expect(content).toContain('TARGET: [Injected Webpack]') + expect(content).toContain('QUERY: [Injected Webpack]') }) it('esbuild', async () => { @@ -31,5 +33,6 @@ describe('transform build', () => { expect(content).toContain('NON-TARGET: __UNPLUGIN__') expect(content).toContain('TARGET: [Injected Esbuild]') + expect(content).toContain('QUERY: [Injected Esbuild]') }) }) diff --git a/test/fixtures/transform/src/main.js b/test/fixtures/transform/src/main.js index cfc500fd..3d84918e 100644 --- a/test/fixtures/transform/src/main.js +++ b/test/fixtures/transform/src/main.js @@ -1,4 +1,5 @@ import { msg1 } from './nontarget' import { msg2 } from './target' +import { msg3 } from './query?query-param=query-value' -console.log(msg1, msg2) +console.log(msg1, msg2, msg3) diff --git a/test/fixtures/transform/src/query.js b/test/fixtures/transform/src/query.js new file mode 100644 index 00000000..d23b5f87 --- /dev/null +++ b/test/fixtures/transform/src/query.js @@ -0,0 +1 @@ +export const msg3 = 'QUERY: __UNPLUGIN__' diff --git a/test/fixtures/transform/unplugin.js b/test/fixtures/transform/unplugin.js index 6a054801..21e5862a 100644 --- a/test/fixtures/transform/unplugin.js +++ b/test/fixtures/transform/unplugin.js @@ -1,11 +1,20 @@ const { createUnplugin } = require('unplugin') const MagicString = require('magic-string') -module.exports = createUnplugin((options) => { +module.exports = createUnplugin((options, meta) => { return { name: 'transform-fixture', + resolveId (id) { + // Rollup doesn't know how to import module with query string so we ignore the module + if (id.includes('?query-param=query-value') && meta.framework === 'rollup') { + return { + id, + external: true + } + } + }, transformInclude (id) { - return id.match(/[/\\]target\.js$/) + return id.match(/[/\\]target\.js$/) || id.includes('?query-param=query-value') }, transform (code, id) { const s = new MagicString(code) @@ -14,7 +23,14 @@ module.exports = createUnplugin((options) => { return null } - s.overwrite(index, index + '__UNPLUGIN__'.length, `[Injected ${options.msg}]`) + const injectedCode = `[Injected ${options.msg}]` + + if (id.includes(injectedCode)) { + throw new Error('File was already transformed') + } + + s.overwrite(index, index + '__UNPLUGIN__'.length, injectedCode) + return { code: s.toString(), map: s.generateMap({