diff --git a/README.md b/README.md index 47800697..5c2f8cc4 100644 --- a/README.md +++ b/README.md @@ -192,12 +192,12 @@ export const unplugin = createUnplugin((options: UserOptions, meta) => { console.log(meta.framework) // 'vite' | 'rollup' | 'webpack' | 'rspack' | 'esbuild' return { - // common unplugin hooks + // Common unplugin hooks name: 'unplugin-prefixed-name', transformInclude(id) { /* ... */ }, transform(code) { /* ... */ }, - // framework specific hooks + // Framework specific hooks vite: { // Vite plugin configureServer(server) { @@ -209,16 +209,21 @@ export const unplugin = createUnplugin((options: UserOptions, meta) => { // Rollup plugin }, webpack(compiler) { - // configure Webpack compiler + // Configure Webpack compiler }, rspack(compiler) { - // configure Rspack compiler + // Configure Rspack compiler }, esbuild: { - // change the filter of onResolve and onLoad + // Change the filter of onResolve and onLoad // onResolveFilter?: RegExp, // onLoadFilter?: RegExp, - // or you can completely replace the setup logic + + // Tell esbuild how to interpret the contents. By default unplugin tries to guess the loader + // from file extension (eg: .js -> "js", .jsx -> 'jsx') + // loader?: (Loader | (code: string, id: string) => Loader) + + // Or you can completely replace the setup logic // setup?: EsbuildPlugin.setup, }, } diff --git a/src/esbuild/index.ts b/src/esbuild/index.ts index 099a1ca3..b1605159 100644 --- a/src/esbuild/index.ts +++ b/src/esbuild/index.ts @@ -4,7 +4,7 @@ import type { PartialMessage } from 'esbuild' import type { SourceMap } from 'rollup' import type { RawSourceMap } from '@ampproject/remapping' import type { EsbuildPlugin, UnpluginBuildContext, UnpluginContext, UnpluginContextMeta, UnpluginFactory, UnpluginInstance, UnpluginOptions } from '../types' -import { combineSourcemaps, createEsbuildContext, guessLoader, processCodeWithSourceMap, toArray } from './utils' +import { combineSourcemaps, createEsbuildContext, guessLoader, processCodeWithSourceMap, toArray, unwrapLoader } from './utils' let i = 0 @@ -25,6 +25,7 @@ export function getEsbuildPlugin>( const onResolveFilter = plugin.esbuild?.onResolveFilter ?? /.*/ const onLoadFilter = plugin.esbuild?.onLoadFilter ?? /.*/ + const loader = plugin.esbuild?.loader ?? guessLoader const context: UnpluginBuildContext = createEsbuildContext(initialOptions) @@ -98,7 +99,7 @@ export function getEsbuildPlugin>( if (map) code = processCodeWithSourceMap(map, code) - return { contents: code, errors, warnings, loader: guessLoader(args.path), resolveDir } + return { contents: code, errors, warnings, loader: unwrapLoader(loader, code, args.path), resolveDir } } if (!plugin.transformInclude || plugin.transformInclude(id)) { @@ -133,7 +134,7 @@ export function getEsbuildPlugin>( if (code) { if (map) code = processCodeWithSourceMap(map, code) - return { contents: code, errors, warnings, loader: guessLoader(args.path), resolveDir } + return { contents: code, errors, warnings, loader: unwrapLoader(loader, code, args.path), resolveDir } } }) } diff --git a/src/esbuild/utils.ts b/src/esbuild/utils.ts index 740af501..6d93cfd5 100644 --- a/src/esbuild/utils.ts +++ b/src/esbuild/utils.ts @@ -28,10 +28,21 @@ const ExtToLoader: Record = { '.txt': 'text', } -export function guessLoader(id: string): Loader { +export function guessLoader(code: string, id: string): Loader { return ExtToLoader[path.extname(id).toLowerCase()] || 'js' } +export function unwrapLoader( + loader: Loader | ((code: string, id: string) => Loader), + code: string, + id: string, +): Loader { + if (typeof loader === 'function') + return loader(code, id) + + return loader +} + // `load` and `transform` may return a sourcemap without toString and toUrl, // but esbuild needs them, we fix the two methods export function fixSourceMap(map: EncodedSourceMap): SourceMap { diff --git a/src/types.ts b/src/types.ts index 8f7e5d5b..b3a3bea6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import type { AcornNode, EmittedAsset, PluginContextMeta as RollupContextMeta, Plugin as RollupPlugin, SourceMapInput } from 'rollup' import type { Compiler as WebpackCompiler, WebpackPluginInstance } from 'webpack' import type { Plugin as VitePlugin } from 'vite' -import type { Plugin as EsbuildPlugin, PluginBuild } from 'esbuild' +import type { Plugin as EsbuildPlugin, Loader, PluginBuild } from 'esbuild' import type { Compiler as RspackCompiler, RspackPluginInstance } from '@rspack/core' import type VirtualModulesPlugin from 'webpack-virtual-modules' @@ -75,6 +75,7 @@ export interface UnpluginOptions { onResolveFilter?: RegExp onLoadFilter?: RegExp setup?: EsbuildPlugin['setup'] + loader?: Loader | ((code: string, id: string) => Loader) } }