Skip to content

Commit 4ad11c3

Browse files
committed
fix #3639, fix #3646: pass with to onResolve
1 parent 516ca31 commit 4ad11c3

File tree

11 files changed

+168
-16
lines changed

11 files changed

+168
-16
lines changed

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,37 @@
106106
function n(){console.log("macOS")}export{n as logPlatform};
107107
```
108108
109+
* 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))
110+
111+
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:
112+
113+
```js
114+
const examplePlugin = {
115+
name: 'Example plugin',
116+
setup(build) {
117+
build.onResolve({ filter: /.*/ }, args => {
118+
if (args.with.type === 'external')
119+
return { external: true }
120+
})
121+
}
122+
}
123+
124+
require('esbuild').build({
125+
stdin: {
126+
contents: `
127+
import foo from "./foo" with { type: "external" }
128+
foo()
129+
`,
130+
},
131+
bundle: true,
132+
format: 'esm',
133+
write: false,
134+
plugins: [examplePlugin],
135+
}).then(result => {
136+
console.log(result.outputFiles[0].text)
137+
})
138+
```
139+
109140
* Formatting support for the `@position-try` rule ([#3773](https://github.com/evanw/esbuild/issues/3773))
110141
111142
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.

cmd/esbuild/service.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,13 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
919919
if value, ok := request["pluginData"]; ok {
920920
options.PluginData = value.(int)
921921
}
922+
if value, ok := request["with"]; ok {
923+
value := value.(map[string]interface{})
924+
options.With = make(map[string]string, len(value))
925+
for k, v := range value {
926+
options.With[k] = v.(string)
927+
}
928+
}
922929

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

980+
with := make(map[string]interface{}, len(args.With))
981+
for k, v := range args.With {
982+
with[k] = v
983+
}
984+
973985
response, ok := service.sendRequest(map[string]interface{}{
974986
"command": "on-resolve",
975987
"key": key,
@@ -980,6 +992,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
980992
"resolveDir": args.ResolveDir,
981993
"kind": resolveKindToString(args.Kind),
982994
"pluginData": args.PluginData,
995+
"with": with,
983996
}).(map[string]interface{})
984997
if !ok {
985998
return result, errors.New("The service was stopped")
@@ -1055,7 +1068,7 @@ func (service *serviceType) convertPlugins(key int, jsPlugins interface{}, activ
10551068
return result, nil
10561069
}
10571070

1058-
with := make(map[string]interface{})
1071+
with := make(map[string]interface{}, len(args.With))
10591072
for k, v := range args.With {
10601073
with[k] = v
10611074
}
@@ -1266,11 +1279,11 @@ func decodeStringArray(values []interface{}) []string {
12661279
func encodeOutputFiles(outputFiles []api.OutputFile) []interface{} {
12671280
values := make([]interface{}, len(outputFiles))
12681281
for i, outputFile := range outputFiles {
1269-
value := make(map[string]interface{})
1270-
values[i] = value
1271-
value["path"] = outputFile.Path
1272-
value["contents"] = outputFile.Contents
1273-
value["hash"] = outputFile.Hash
1282+
values[i] = map[string]interface{}{
1283+
"path": outputFile.Path,
1284+
"contents": outputFile.Contents,
1285+
"hash": outputFile.Hash,
1286+
}
12741287
}
12751288
return values
12761289
}

internal/bundler/bundler.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ func parseFile(args parseArgs) {
463463
record.Range,
464464
source.KeyPath,
465465
record.Path.Text,
466+
attrs,
466467
record.Kind,
467468
absResolveDir,
468469
pluginData,
@@ -865,6 +866,7 @@ func RunOnResolvePlugins(
865866
importPathRange logger.Range,
866867
importer logger.Path,
867868
path string,
869+
importAttributes logger.ImportAttributes,
868870
kind ast.ImportKind,
869871
absResolveDir string,
870872
pluginData interface{},
@@ -875,6 +877,7 @@ func RunOnResolvePlugins(
875877
Kind: kind,
876878
PluginData: pluginData,
877879
Importer: importer,
880+
With: importAttributes,
878881
}
879882
applyPath := logger.Path{
880883
Text: path,
@@ -1057,7 +1060,7 @@ func runOnLoadPlugins(
10571060

10581061
// Reject unsupported import attributes
10591062
loader := config.LoaderDefault
1060-
for _, attr := range source.KeyPath.ImportAttributes.Decode() {
1063+
for _, attr := range source.KeyPath.ImportAttributes.DecodeIntoArray() {
10611064
if attr.Key == "type" {
10621065
if attr.Value == "json" {
10631066
loader = config.LoaderWithTypeJSON
@@ -1625,6 +1628,7 @@ func (s *scanner) preprocessInjectedFiles() {
16251628
logger.Range{},
16261629
importer,
16271630
importPath,
1631+
logger.ImportAttributes{},
16281632
ast.ImportEntryPoint,
16291633
injectAbsResolveDir,
16301634
nil,
@@ -1804,6 +1808,7 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []graph.EntryPoint {
18041808
logger.Range{},
18051809
importer,
18061810
entryPoint.InputPath,
1811+
logger.ImportAttributes{},
18071812
ast.ImportEntryPoint,
18081813
entryPointAbsResolveDir,
18091814
nil,
@@ -2203,7 +2208,7 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
22032208

22042209
for _, sourceIndex := range sourceIndices {
22052210
source := &s.results[sourceIndex].file.inputFile.Source
2206-
attrs := source.KeyPath.ImportAttributes.Decode()
2211+
attrs := source.KeyPath.ImportAttributes.DecodeIntoArray()
22072212
if len(attrs) == 0 {
22082213
continue
22092214
}
@@ -2491,7 +2496,7 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
24912496
} else {
24922497
sb.WriteString("]")
24932498
}
2494-
if attrs := result.file.inputFile.Source.KeyPath.ImportAttributes.Decode(); len(attrs) > 0 {
2499+
if attrs := result.file.inputFile.Source.KeyPath.ImportAttributes.DecodeIntoArray(); len(attrs) > 0 {
24952500
sb.WriteString(",\n \"with\": {")
24962501
for i, attr := range attrs {
24972502
if i > 0 {

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ type OnResolveArgs struct {
771771
PluginData interface{}
772772
Importer logger.Path
773773
Kind ast.ImportKind
774+
With logger.ImportAttributes
774775
}
775776

776777
type OnResolveResult struct {

internal/logger/logger.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ type ImportAttribute struct {
272272
}
273273

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

292+
func (attrs ImportAttributes) DecodeIntoMap() (result map[string]string) {
293+
if array := attrs.DecodeIntoArray(); len(array) > 0 {
294+
result = make(map[string]string, len(array))
295+
for _, attr := range array {
296+
result[attr.Key] = attr.Value
297+
}
298+
}
299+
return
300+
}
301+
292302
func EncodeImportAttributes(value map[string]string) ImportAttributes {
303+
if len(value) == 0 {
304+
return ImportAttributes{}
305+
}
293306
keys := make([]string, 0, len(value))
294307
for k := range value {
295308
keys = append(keys, k)

lib/shared/common.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,7 @@ let handlePlugins = async (
12491249
let resolveDir = getFlag(options, keys, 'resolveDir', mustBeString)
12501250
let kind = getFlag(options, keys, 'kind', mustBeString)
12511251
let pluginData = getFlag(options, keys, 'pluginData', canBeAnything)
1252+
let importAttributes = getFlag(options, keys, 'with', mustBeObject)
12521253
checkForInvalidFlags(options, keys, 'in resolve() call')
12531254

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

12691271
sendRequest<protocol.ResolveRequest, protocol.ResolveResponse>(refs, request, (error, response) => {
12701272
if (error !== null) reject(new Error(error))
@@ -1382,6 +1384,7 @@ let handlePlugins = async (
13821384
resolveDir: request.resolveDir,
13831385
kind: request.kind,
13841386
pluginData: details.load(request.pluginData),
1387+
with: request.with,
13851388
})
13861389

13871390
if (result != null) {
@@ -1804,6 +1807,16 @@ function sanitizeStringArray(values: any[], property: string): string[] {
18041807
return result
18051808
}
18061809

1810+
function sanitizeStringMap(map: Record<string, any>, property: string): Record<string, string> {
1811+
const result: Record<string, string> = Object.create(null)
1812+
for (const key in map) {
1813+
const value = map[key]
1814+
if (typeof value !== 'string') throw new Error(`key ${quote(key)} in object ${quote(property)} must be a string`)
1815+
result[key] = value
1816+
}
1817+
return result
1818+
}
1819+
18071820
function convertOutputFiles({ path, contents, hash }: protocol.BuildOutputFile): types.OutputFile {
18081821
// The text is lazily-generated for performance reasons. If no one asks for
18091822
// it, then it never needs to be generated.

lib/shared/stdio_protocol.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export interface ResolveRequest {
170170
resolveDir?: string
171171
kind?: string
172172
pluginData?: number
173+
with?: Record<string, string>
173174
}
174175

175176
export interface ResolveResponse {
@@ -194,6 +195,7 @@ export interface OnResolveRequest {
194195
resolveDir: string
195196
kind: types.ImportKind
196197
pluginData: number
198+
with: Record<string, string>
197199
}
198200

199201
export interface OnResolveResponse {

lib/shared/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ export interface ResolveOptions {
340340
resolveDir?: string
341341
kind?: ImportKind
342342
pluginData?: any
343+
with?: Record<string, string>
343344
}
344345

345346
/** Documentation: https://esbuild.github.io/plugins/#resolve-results */
@@ -379,6 +380,7 @@ export interface OnResolveArgs {
379380
resolveDir: string
380381
kind: ImportKind
381382
pluginData: any
383+
with: Record<string, string>
382384
}
383385

384386
export type ImportKind =

pkg/api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ type ResolveOptions struct {
576576
ResolveDir string
577577
Kind ResolveKind
578578
PluginData interface{}
579+
With map[string]string
579580
}
580581

581582
// Documentation: https://esbuild.github.io/plugins/#resolve-results
@@ -615,6 +616,7 @@ type OnResolveArgs struct {
615616
ResolveDir string
616617
Kind ResolveKind
617618
PluginData interface{}
619+
With map[string]string
618620
}
619621

620622
// Documentation: https://esbuild.github.io/plugins/#on-resolve-results

pkg/api/api_impl.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,7 @@ func (impl *pluginImpl) onResolve(options OnResolveOptions, callback func(OnReso
18981898
ResolveDir: args.ResolveDir,
18991899
Kind: importKindToResolveKind(args.Kind),
19001900
PluginData: args.PluginData,
1901+
With: args.With.DecodeIntoMap(),
19011902
})
19021903
result.PluginName = response.PluginName
19031904
result.AbsWatchFiles = impl.validatePathsArray(response.WatchFiles, "watch file")
@@ -1940,16 +1941,12 @@ func (impl *pluginImpl) onLoad(options OnLoadOptions, callback func(OnLoadArgs)
19401941
Filter: filter,
19411942
Namespace: options.Namespace,
19421943
Callback: func(args config.OnLoadArgs) (result config.OnLoadResult) {
1943-
with := make(map[string]string)
1944-
for _, attr := range args.Path.ImportAttributes.Decode() {
1945-
with[attr.Key] = attr.Value
1946-
}
19471944
response, err := callback(OnLoadArgs{
19481945
Path: args.Path.Text,
19491946
Namespace: args.Path.Namespace,
19501947
PluginData: args.PluginData,
19511948
Suffix: args.Path.IgnoredSuffix,
1952-
With: with,
1949+
With: args.Path.ImportAttributes.DecodeIntoMap(),
19531950
})
19541951
result.PluginName = response.PluginName
19551952
result.AbsWatchFiles = impl.validatePathsArray(response.WatchFiles, "watch file")
@@ -2054,6 +2051,7 @@ func loadPlugins(initialOptions *BuildOptions, fs fs.FS, log logger.Log, caches
20542051
logger.Range{}, // importPathRange
20552052
logger.Path{Text: options.Importer, Namespace: options.Namespace},
20562053
path,
2054+
logger.EncodeImportAttributes(options.With),
20572055
kind,
20582056
absResolveDir,
20592057
options.PluginData,

0 commit comments

Comments
 (0)