Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat #3666 add optional exclude option for plugin onLoad and onResolve hooks #3667

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@
})(Foo || {});
```

* Add `exclude?: RegExp` option to `onLoad` on `onResolve` hooks

Allows plugins to stamp out files and folders to not execute the hook.

```js
const myPlugin = {
name: 'my-plugin',
setup(build) {
build.onResolve({ filter: /.js$/, exclude: /^(static|my-lib)\// }, args => {
})
build.onLoad({ filter: /.js$/, exclude: /\/node_modules\// }, args => {
})
}
}
```

## 0.20.1

* Fix a bug with the CSS nesting transform ([#3648](https://github.com/evanw/esbuild/issues/3648))
Expand Down
11 changes: 9 additions & 2 deletions cmd/esbuild/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ func stringToResolveKind(kind string) (api.ResolveKind, bool) {
func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activeBuild *activeBuild) ([]api.Plugin, bool, error) {
type filteredCallback struct {
filter *regexp.Regexp
exclude *regexp.Regexp
pluginName string
namespace string
id int
Expand All @@ -846,10 +847,16 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
if err != nil {
return nil, err
}
exclude, err := config.CompileExcludeForPlugin(pluginName, kind, item["exclude"].(string))
if err != nil {
return nil, err
}

result = append(result, filteredCallback{
pluginName: pluginName,
id: item["id"].(int),
filter: filter,
exclude: exclude,
namespace: item["namespace"].(string),
})
}
Expand Down Expand Up @@ -960,7 +967,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
var ids []interface{}
applyPath := logger.Path{Text: args.Path, Namespace: args.Namespace}
for _, item := range onResolveCallbacks {
if config.PluginAppliesToPath(applyPath, item.filter, item.namespace) {
if config.PluginAppliesToPath(applyPath, item.filter, item.exclude, item.namespace) {
ids = append(ids, item.id)
}
}
Expand Down Expand Up @@ -1045,7 +1052,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
var ids []interface{}
applyPath := logger.Path{Text: args.Path, Namespace: args.Namespace}
for _, item := range onLoadCallbacks {
if config.PluginAppliesToPath(applyPath, item.filter, item.namespace) {
if config.PluginAppliesToPath(applyPath, item.filter, item.exclude, item.namespace) {
ids = append(ids, item.id)
}
}
Expand Down
4 changes: 2 additions & 2 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ func RunOnResolvePlugins(
// Apply resolver plugins in order until one succeeds
for _, plugin := range plugins {
for _, onResolve := range plugin.OnResolve {
if !config.PluginAppliesToPath(applyPath, onResolve.Filter, onResolve.Namespace) {
if !config.PluginAppliesToPath(applyPath, onResolve.Filter, onResolve.Exclude, onResolve.Namespace) {
continue
}

Expand Down Expand Up @@ -1002,7 +1002,7 @@ func runOnLoadPlugins(
// Apply loader plugins in order until one succeeds
for _, plugin := range plugins {
for _, onLoad := range plugin.OnLoad {
if !config.PluginAppliesToPath(source.KeyPath, onLoad.Filter, onLoad.Namespace) {
if !config.PluginAppliesToPath(source.KeyPath, onLoad.Filter, onLoad.Exclude, onLoad.Namespace) {
continue
}

Expand Down
54 changes: 36 additions & 18 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,41 +683,41 @@ type InjectableExport struct {
Loc logger.Loc
}

var filterMutex sync.Mutex
var filterCache map[string]*regexp.Regexp
var regexpMutex sync.Mutex
var regexpCache map[string]*regexp.Regexp

func compileFilter(filter string) (result *regexp.Regexp) {
if filter == "" {
// Must provide a filter
func compileRegexp(value string) (result *regexp.Regexp) {
if value == "" {
// Must not be empty
return nil
}
ok := false

// Cache hit?
(func() {
filterMutex.Lock()
defer filterMutex.Unlock()
if filterCache != nil {
result, ok = filterCache[filter]
regexpMutex.Lock()
defer regexpMutex.Unlock()
if regexpCache != nil {
result, ok = regexpCache[value]
}
})()
if ok {
return
}

// Cache miss
result, err := regexp.Compile(filter)
result, err := regexp.Compile(value)
if err != nil {
return nil
}

// Cache for next time
filterMutex.Lock()
defer filterMutex.Unlock()
if filterCache == nil {
filterCache = make(map[string]*regexp.Regexp)
regexpMutex.Lock()
defer regexpMutex.Unlock()
if regexpCache == nil {
regexpCache = make(map[string]*regexp.Regexp)
}
filterCache[filter] = result
regexpCache[value] = result
return
}

Expand All @@ -726,16 +726,32 @@ func CompileFilterForPlugin(pluginName string, kind string, filter string) (*reg
return nil, fmt.Errorf("[%s] %q is missing a filter", pluginName, kind)
}

result := compileFilter(filter)
result := compileRegexp(filter)
if result == nil {
return nil, fmt.Errorf("[%s] %q filter is not a valid Go regular expression: %q", pluginName, kind, filter)
}

return result, nil
}

func PluginAppliesToPath(path logger.Path, filter *regexp.Regexp, namespace string) bool {
return (namespace == "" || path.Namespace == namespace) && filter.MatchString(path.Text)
func CompileExcludeForPlugin(pluginName string, kind string, exclude string) (*regexp.Regexp, error) {
if exclude == "" {
// exclude regex is optional
return nil, nil
}

result := compileRegexp(exclude)
if result == nil {
return nil, fmt.Errorf("[%s] %q exclude is not a valid Go regular expression: %q", pluginName, kind, exclude)
}

return result, nil
}

func PluginAppliesToPath(path logger.Path, filter *regexp.Regexp, exclude *regexp.Regexp, namespace string) bool {
return (namespace == "" || path.Namespace == namespace) &&
filter.MatchString(path.Text) &&
(exclude == nil || !exclude.MatchString(path.Text))
}

////////////////////////////////////////////////////////////////////////////////
Expand All @@ -760,6 +776,7 @@ type OnStartResult struct {

type OnResolve struct {
Filter *regexp.Regexp
Exclude *regexp.Regexp
Callback func(OnResolveArgs) OnResolveResult
Name string
Namespace string
Expand Down Expand Up @@ -790,6 +807,7 @@ type OnResolveResult struct {

type OnLoad struct {
Filter *regexp.Regexp
Exclude *regexp.Regexp
Callback func(OnLoadArgs) OnLoadResult
Name string
Namespace string
Expand Down
6 changes: 4 additions & 2 deletions lib/shared/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1306,25 +1306,27 @@ let handlePlugins = async (
let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onResolve')
let keys: OptionKeys = {}
let filter = getFlag(options, keys, 'filter', mustBeRegExp)
let exclude = getFlag(options, keys, 'exclude', mustBeRegExp)
let namespace = getFlag(options, keys, 'namespace', mustBeString)
checkForInvalidFlags(options, keys, `in onResolve() call for plugin ${quote(name)}`)
if (filter == null) throw new Error(`onResolve() call is missing a filter`)
let id = nextCallbackID++
onResolveCallbacks[id] = { name: name!, callback, note: registeredNote }
plugin.onResolve.push({ id, filter: filter.source, namespace: namespace || '' })
plugin.onResolve.push({ id, filter: filter.source, exclude: exclude?.source || '', namespace: namespace || '' })
},

onLoad(options, callback) {
let registeredText = `This error came from the "onLoad" callback registered here:`
let registeredNote = extractCallerV8(new Error(registeredText), streamIn, 'onLoad')
let keys: OptionKeys = {}
let filter = getFlag(options, keys, 'filter', mustBeRegExp)
let exclude = getFlag(options, keys, 'exclude', mustBeRegExp)
let namespace = getFlag(options, keys, 'namespace', mustBeString)
checkForInvalidFlags(options, keys, `in onLoad() call for plugin ${quote(name)}`)
if (filter == null) throw new Error(`onLoad() call is missing a filter`)
let id = nextCallbackID++
onLoadCallbacks[id] = { name: name!, callback, note: registeredNote }
plugin.onLoad.push({ id, filter: filter.source, namespace: namespace || '' })
plugin.onLoad.push({ id, filter: filter.source, exclude: exclude?.source || '', namespace: namespace || '' })
},

onDispose(callback) {
Expand Down
4 changes: 2 additions & 2 deletions lib/shared/stdio_protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export interface BuildPlugin {
name: string
onStart: boolean
onEnd: boolean
onResolve: { id: number, filter: string, namespace: string }[]
onLoad: { id: number, filter: string, namespace: string }[]
onResolve: { id: number, filter: string, exclude: string, namespace: string }[]
onLoad: { id: number, filter: string, exclude: string, namespace: string }[]
}

export interface BuildResponse {
Expand Down
2 changes: 2 additions & 0 deletions lib/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ export interface OnEndResult {
/** Documentation: https://esbuild.github.io/plugins/#on-resolve-options */
export interface OnResolveOptions {
filter: RegExp
exclude?: RegExp
namespace?: string
}

Expand Down Expand Up @@ -416,6 +417,7 @@ export interface OnResolveResult {
/** Documentation: https://esbuild.github.io/plugins/#on-load-options */
export interface OnLoadOptions {
filter: RegExp
exclude?: RegExp
namespace?: string
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ type OnEndResult struct {
// Documentation: https://esbuild.github.io/plugins/#on-resolve-options
type OnResolveOptions struct {
Filter string
Exclude string
Namespace string
}

Expand Down Expand Up @@ -637,6 +638,7 @@ type OnResolveResult struct {
// Documentation: https://esbuild.github.io/plugins/#on-load-options
type OnLoadOptions struct {
Filter string
Exclude string
Namespace string
}

Expand Down
16 changes: 14 additions & 2 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1929,14 +1929,20 @@ func resolveKindToImportKind(kind ResolveKind) ast.ImportKind {

func (impl *pluginImpl) onResolve(options OnResolveOptions, callback func(OnResolveArgs) (OnResolveResult, error)) {
filter, err := config.CompileFilterForPlugin(impl.plugin.Name, "OnResolve", options.Filter)
if filter == nil {
if filter == nil || err != nil {
impl.log.AddError(nil, logger.Range{}, err.Error())
return
}
exclude, err := config.CompileExcludeForPlugin(impl.plugin.Name, "OnResolve", options.Exclude)
if err != nil {
impl.log.AddError(nil, logger.Range{}, err.Error())
return
}

impl.plugin.OnResolve = append(impl.plugin.OnResolve, config.OnResolve{
Name: impl.plugin.Name,
Filter: filter,
Exclude: exclude,
Namespace: options.Namespace,
Callback: func(args config.OnResolveArgs) (result config.OnResolveResult) {
response, err := callback(OnResolveArgs{
Expand Down Expand Up @@ -1979,13 +1985,19 @@ func (impl *pluginImpl) onResolve(options OnResolveOptions, callback func(OnReso

func (impl *pluginImpl) onLoad(options OnLoadOptions, callback func(OnLoadArgs) (OnLoadResult, error)) {
filter, err := config.CompileFilterForPlugin(impl.plugin.Name, "OnLoad", options.Filter)
if filter == nil {
if filter == nil || err != nil {
impl.log.AddError(nil, logger.Range{}, err.Error())
return
}
exclude, err := config.CompileExcludeForPlugin(impl.plugin.Name, "OnLoad", options.Exclude)
if err != nil {
impl.log.AddError(nil, logger.Range{}, err.Error())
return
}

impl.plugin.OnLoad = append(impl.plugin.OnLoad, config.OnLoad{
Filter: filter,
Exclude: exclude,
Namespace: options.Namespace,
Callback: func(args config.OnLoadArgs) (result config.OnLoadResult) {
with := make(map[string]string)
Expand Down