Skip to content

Commit

Permalink
feat evanw#3666 add optional exclude option for plugin onLoad and `…
Browse files Browse the repository at this point in the history
…onResolve` hooks
  • Loading branch information
sod committed Feb 24, 2024
1 parent cc74e60 commit 6d2d857
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 36 deletions.
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
14 changes: 13 additions & 1 deletion pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1933,10 +1933,16 @@ func (impl *pluginImpl) onResolve(options OnResolveOptions, callback func(OnReso
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 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

0 comments on commit 6d2d857

Please sign in to comment.