Skip to content

Commit

Permalink
fix #3639, fix #3646: pass with to onResolve
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 24, 2024
1 parent 516ca31 commit 4ad11c3
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 16 deletions.
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,37 @@
function n(){console.log("macOS")}export{n as logPlatform};
```

* Pass import attributes to on-resolve plugins ([#3384](https://github.com/evanw/esbuild/issues/3384), [#3639](https://github.com/evanw/esbuild/issues/3639), [#3646](https://github.com/evanw/esbuild/issues/3646))

With this release, on-resolve plugins will now have access to the import attributes on the import via the `with` property of the arguments object. This mirrors the `with` property of the arguments object that's already passed to on-load plugins. In addition, you can now pass `with` to the `resolve()` API call which will then forward that value on to all relevant plugins. Here's an example of a plugin that can now be written:

```js
const examplePlugin = {
name: 'Example plugin',
setup(build) {
build.onResolve({ filter: /.*/ }, args => {
if (args.with.type === 'external')
return { external: true }
})
}
}
require('esbuild').build({
stdin: {
contents: `
import foo from "./foo" with { type: "external" }
foo()
`,
},
bundle: true,
format: 'esm',
write: false,
plugins: [examplePlugin],
}).then(result => {
console.log(result.outputFiles[0].text)
})
```

* Formatting support for the `@position-try` rule ([#3773](https://github.com/evanw/esbuild/issues/3773))

Chrome shipped this new CSS at-rule in version 125 as part of the [CSS anchor positioning API](https://developer.chrome.com/blog/anchor-positioning-api). With this release, esbuild now knows to expect a declaration list inside of the `@position-try` body block and will format it appropriately.
Expand Down
25 changes: 19 additions & 6 deletions cmd/esbuild/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,13 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
if value, ok := request["pluginData"]; ok {
options.PluginData = value.(int)
}
if value, ok := request["with"]; ok {
value := value.(map[string]interface{})
options.With = make(map[string]string, len(value))
for k, v := range value {
options.With[k] = v.(string)
}
}

result := build.Resolve(path, options)
return encodePacket(packet{
Expand Down Expand Up @@ -970,6 +977,11 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
return result, nil
}

with := make(map[string]interface{}, len(args.With))
for k, v := range args.With {
with[k] = v
}

response, ok := service.sendRequest(map[string]interface{}{
"command": "on-resolve",
"key": key,
Expand All @@ -980,6 +992,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
"resolveDir": args.ResolveDir,
"kind": resolveKindToString(args.Kind),
"pluginData": args.PluginData,
"with": with,
}).(map[string]interface{})
if !ok {
return result, errors.New("The service was stopped")
Expand Down Expand Up @@ -1055,7 +1068,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
return result, nil
}

with := make(map[string]interface{})
with := make(map[string]interface{}, len(args.With))
for k, v := range args.With {
with[k] = v
}
Expand Down Expand Up @@ -1266,11 +1279,11 @@ func decodeStringArray(values []interface{}) []string {
func encodeOutputFiles(outputFiles []api.OutputFile) []interface{} {
values := make([]interface{}, len(outputFiles))
for i, outputFile := range outputFiles {
value := make(map[string]interface{})
values[i] = value
value["path"] = outputFile.Path
value["contents"] = outputFile.Contents
value["hash"] = outputFile.Hash
values[i] = map[string]interface{}{
"path": outputFile.Path,
"contents": outputFile.Contents,
"hash": outputFile.Hash,
}
}
return values
}
Expand Down
11 changes: 8 additions & 3 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ func parseFile(args parseArgs) {
record.Range,
source.KeyPath,
record.Path.Text,
attrs,
record.Kind,
absResolveDir,
pluginData,
Expand Down Expand Up @@ -865,6 +866,7 @@ func RunOnResolvePlugins(
importPathRange logger.Range,
importer logger.Path,
path string,
importAttributes logger.ImportAttributes,
kind ast.ImportKind,
absResolveDir string,
pluginData interface{},
Expand All @@ -875,6 +877,7 @@ func RunOnResolvePlugins(
Kind: kind,
PluginData: pluginData,
Importer: importer,
With: importAttributes,
}
applyPath := logger.Path{
Text: path,
Expand Down Expand Up @@ -1057,7 +1060,7 @@ func runOnLoadPlugins(

// Reject unsupported import attributes
loader := config.LoaderDefault
for _, attr := range source.KeyPath.ImportAttributes.Decode() {
for _, attr := range source.KeyPath.ImportAttributes.DecodeIntoArray() {
if attr.Key == "type" {
if attr.Value == "json" {
loader = config.LoaderWithTypeJSON
Expand Down Expand Up @@ -1625,6 +1628,7 @@ func (s *scanner) preprocessInjectedFiles() {
logger.Range{},
importer,
importPath,
logger.ImportAttributes{},
ast.ImportEntryPoint,
injectAbsResolveDir,
nil,
Expand Down Expand Up @@ -1804,6 +1808,7 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []graph.EntryPoint {
logger.Range{},
importer,
entryPoint.InputPath,
logger.ImportAttributes{},
ast.ImportEntryPoint,
entryPointAbsResolveDir,
nil,
Expand Down Expand Up @@ -2203,7 +2208,7 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann

for _, sourceIndex := range sourceIndices {
source := &s.results[sourceIndex].file.inputFile.Source
attrs := source.KeyPath.ImportAttributes.Decode()
attrs := source.KeyPath.ImportAttributes.DecodeIntoArray()
if len(attrs) == 0 {
continue
}
Expand Down Expand Up @@ -2491,7 +2496,7 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
} else {
sb.WriteString("]")
}
if attrs := result.file.inputFile.Source.KeyPath.ImportAttributes.Decode(); len(attrs) > 0 {
if attrs := result.file.inputFile.Source.KeyPath.ImportAttributes.DecodeIntoArray(); len(attrs) > 0 {
sb.WriteString(",\n \"with\": {")
for i, attr := range attrs {
if i > 0 {
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ type OnResolveArgs struct {
PluginData interface{}
Importer logger.Path
Kind ast.ImportKind
With logger.ImportAttributes
}

type OnResolveResult struct {
Expand Down
15 changes: 14 additions & 1 deletion internal/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ type ImportAttribute struct {
}

// This returns a sorted array instead of a map to make determinism easier
func (attrs ImportAttributes) Decode() (result []ImportAttribute) {
func (attrs ImportAttributes) DecodeIntoArray() (result []ImportAttribute) {
if attrs.packedData == "" {
return nil
}
Expand All @@ -289,7 +289,20 @@ func (attrs ImportAttributes) Decode() (result []ImportAttribute) {
return result
}

func (attrs ImportAttributes) DecodeIntoMap() (result map[string]string) {
if array := attrs.DecodeIntoArray(); len(array) > 0 {
result = make(map[string]string, len(array))
for _, attr := range array {
result[attr.Key] = attr.Value
}
}
return
}

func EncodeImportAttributes(value map[string]string) ImportAttributes {
if len(value) == 0 {
return ImportAttributes{}
}
keys := make([]string, 0, len(value))
for k := range value {
keys = append(keys, k)
Expand Down
13 changes: 13 additions & 0 deletions lib/shared/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,7 @@ let handlePlugins = async (
let resolveDir = getFlag(options, keys, 'resolveDir', mustBeString)
let kind = getFlag(options, keys, 'kind', mustBeString)
let pluginData = getFlag(options, keys, 'pluginData', canBeAnything)
let importAttributes = getFlag(options, keys, 'with', mustBeObject)
checkForInvalidFlags(options, keys, 'in resolve() call')

return new Promise((resolve, reject) => {
Expand All @@ -1265,6 +1266,7 @@ let handlePlugins = async (
if (kind != null) request.kind = kind
else throw new Error(`Must specify "kind" when calling "resolve"`)
if (pluginData != null) request.pluginData = details.store(pluginData)
if (importAttributes != null) request.with = sanitizeStringMap(importAttributes, 'with')

sendRequest<protocol.ResolveRequest, protocol.ResolveResponse>(refs, request, (error, response) => {
if (error !== null) reject(new Error(error))
Expand Down Expand Up @@ -1382,6 +1384,7 @@ let handlePlugins = async (
resolveDir: request.resolveDir,
kind: request.kind,
pluginData: details.load(request.pluginData),
with: request.with,
})

if (result != null) {
Expand Down Expand Up @@ -1804,6 +1807,16 @@ function sanitizeStringArray(values: any[], property: string): string[] {
return result
}

function sanitizeStringMap(map: Record<string, any>, property: string): Record<string, string> {
const result: Record<string, string> = Object.create(null)
for (const key in map) {
const value = map[key]
if (typeof value !== 'string') throw new Error(`key ${quote(key)} in object ${quote(property)} must be a string`)
result[key] = value
}
return result
}

function convertOutputFiles({ path, contents, hash }: protocol.BuildOutputFile): types.OutputFile {
// The text is lazily-generated for performance reasons. If no one asks for
// it, then it never needs to be generated.
Expand Down
2 changes: 2 additions & 0 deletions lib/shared/stdio_protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export interface ResolveRequest {
resolveDir?: string
kind?: string
pluginData?: number
with?: Record<string, string>
}

export interface ResolveResponse {
Expand All @@ -194,6 +195,7 @@ export interface OnResolveRequest {
resolveDir: string
kind: types.ImportKind
pluginData: number
with: Record<string, string>
}

export interface OnResolveResponse {
Expand Down
2 changes: 2 additions & 0 deletions lib/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ export interface ResolveOptions {
resolveDir?: string
kind?: ImportKind
pluginData?: any
with?: Record<string, string>
}

/** Documentation: https://esbuild.github.io/plugins/#resolve-results */
Expand Down Expand Up @@ -379,6 +380,7 @@ export interface OnResolveArgs {
resolveDir: string
kind: ImportKind
pluginData: any
with: Record<string, string>
}

export type ImportKind =
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ type ResolveOptions struct {
ResolveDir string
Kind ResolveKind
PluginData interface{}
With map[string]string
}

// Documentation: https://esbuild.github.io/plugins/#resolve-results
Expand Down Expand Up @@ -615,6 +616,7 @@ type OnResolveArgs struct {
ResolveDir string
Kind ResolveKind
PluginData interface{}
With map[string]string
}

// Documentation: https://esbuild.github.io/plugins/#on-resolve-results
Expand Down
8 changes: 3 additions & 5 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,7 @@ func (impl *pluginImpl) onResolve(options OnResolveOptions, callback func(OnReso
ResolveDir: args.ResolveDir,
Kind: importKindToResolveKind(args.Kind),
PluginData: args.PluginData,
With: args.With.DecodeIntoMap(),
})
result.PluginName = response.PluginName
result.AbsWatchFiles = impl.validatePathsArray(response.WatchFiles, "watch file")
Expand Down Expand Up @@ -1940,16 +1941,12 @@ func (impl *pluginImpl) onLoad(options OnLoadOptions, callback func(OnLoadArgs)
Filter: filter,
Namespace: options.Namespace,
Callback: func(args config.OnLoadArgs) (result config.OnLoadResult) {
with := make(map[string]string)
for _, attr := range args.Path.ImportAttributes.Decode() {
with[attr.Key] = attr.Value
}
response, err := callback(OnLoadArgs{
Path: args.Path.Text,
Namespace: args.Path.Namespace,
PluginData: args.PluginData,
Suffix: args.Path.IgnoredSuffix,
With: with,
With: args.Path.ImportAttributes.DecodeIntoMap(),
})
result.PluginName = response.PluginName
result.AbsWatchFiles = impl.validatePathsArray(response.WatchFiles, "watch file")
Expand Down Expand Up @@ -2054,6 +2051,7 @@ func loadPlugins(initialOptions *BuildOptions, fs fs.FS, log logger.Log, caches
logger.Range{}, // importPathRange
logger.Path{Text: options.Importer, Namespace: options.Namespace},
path,
logger.EncodeImportAttributes(options.With),
kind,
absResolveDir,
options.PluginData,
Expand Down
Loading

0 comments on commit 4ad11c3

Please sign in to comment.