Skip to content

Commit c95d9ca

Browse files
authored
feat(esbuild): add support for plugins via supplying a configuration file (#2840)
1 parent 99d3177 commit c95d9ca

16 files changed

+427
-21
lines changed

docs/esbuild.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ This will create an output directory containing all the code split chunks, along
9999
**USAGE**
100100

101101
<pre>
102-
esbuild(<a href="#esbuild-name">name</a>, <a href="#esbuild-args">args</a>, <a href="#esbuild-args_file">args_file</a>, <a href="#esbuild-define">define</a>, <a href="#esbuild-deps">deps</a>, <a href="#esbuild-entry_point">entry_point</a>, <a href="#esbuild-entry_points">entry_points</a>, <a href="#esbuild-external">external</a>, <a href="#esbuild-format">format</a>, <a href="#esbuild-launcher">launcher</a>,
103-
<a href="#esbuild-link_workspace_root">link_workspace_root</a>, <a href="#esbuild-max_threads">max_threads</a>, <a href="#esbuild-minify">minify</a>, <a href="#esbuild-output">output</a>, <a href="#esbuild-output_css">output_css</a>, <a href="#esbuild-output_dir">output_dir</a>, <a href="#esbuild-output_map">output_map</a>,
104-
<a href="#esbuild-platform">platform</a>, <a href="#esbuild-sourcemap">sourcemap</a>, <a href="#esbuild-sources_content">sources_content</a>, <a href="#esbuild-splitting">splitting</a>, <a href="#esbuild-srcs">srcs</a>, <a href="#esbuild-target">target</a>)
102+
esbuild(<a href="#esbuild-name">name</a>, <a href="#esbuild-args">args</a>, <a href="#esbuild-args_file">args_file</a>, <a href="#esbuild-config">config</a>, <a href="#esbuild-define">define</a>, <a href="#esbuild-deps">deps</a>, <a href="#esbuild-entry_point">entry_point</a>, <a href="#esbuild-entry_points">entry_points</a>, <a href="#esbuild-external">external</a>, <a href="#esbuild-format">format</a>,
103+
<a href="#esbuild-launcher">launcher</a>, <a href="#esbuild-link_workspace_root">link_workspace_root</a>, <a href="#esbuild-max_threads">max_threads</a>, <a href="#esbuild-minify">minify</a>, <a href="#esbuild-output">output</a>, <a href="#esbuild-output_css">output_css</a>, <a href="#esbuild-output_dir">output_dir</a>,
104+
<a href="#esbuild-output_map">output_map</a>, <a href="#esbuild-platform">platform</a>, <a href="#esbuild-sourcemap">sourcemap</a>, <a href="#esbuild-sources_content">sources_content</a>, <a href="#esbuild-splitting">splitting</a>, <a href="#esbuild-srcs">srcs</a>, <a href="#esbuild-target">target</a>)
105105
</pre>
106106

107107
Runs the esbuild bundler under Bazel
@@ -126,7 +126,15 @@ Defaults to `{}`
126126

127127
<h4 id="esbuild-args_file">args_file</h4>
128128

129-
(*<a href="https://bazel.build/docs/build-ref.html#labels">Label</a>*): A JSON file containing additional arguments that are passed to esbuild. Note: only one of args or args_file may be set
129+
(*<a href="https://bazel.build/docs/build-ref.html#labels">Label</a>*): Internal use only
130+
131+
Defaults to `None`
132+
133+
<h4 id="esbuild-config">config</h4>
134+
135+
(*<a href="https://bazel.build/docs/build-ref.html#labels">Label</a>*): Configuration file used for esbuild, from the esbuild_config macro. Note that options set in this file may get overwritten.
136+
See https://github.com/bazelbuild/rules_nodejs/tree/stable/packages/esbuild/test/plugins/BUILD.bazel for examples of using esbuild_config and plugins. The dependencies of this attribute must provide: Unknown Provider
137+
130138

131139
Defaults to `None`
132140

@@ -323,6 +331,50 @@ list of platform constraints
323331

324332

325333

334+
## esbuild_config
335+
336+
**USAGE**
337+
338+
<pre>
339+
esbuild_config(<a href="#esbuild_config-name">name</a>, <a href="#esbuild_config-config_file">config_file</a>, <a href="#esbuild_config-srcs">srcs</a>, <a href="#esbuild_config-deps">deps</a>, <a href="#esbuild_config-kwargs">kwargs</a>)
340+
</pre>
341+
342+
Macro for an esbuild configuration file and its assoicated dependencies
343+
344+
**PARAMETERS**
345+
346+
347+
<h4 id="esbuild_config-name">name</h4>
348+
349+
Unique name for this rule
350+
351+
352+
353+
<h4 id="esbuild_config-config_file">config_file</h4>
354+
355+
The configuration file / entrypoint
356+
357+
358+
359+
<h4 id="esbuild_config-srcs">srcs</h4>
360+
361+
List of source files referenced by the configuration
362+
363+
Defaults to `[]`
364+
365+
<h4 id="esbuild_config-deps">deps</h4>
366+
367+
List of dependencies required for this configuration
368+
369+
Defaults to `[]`
370+
371+
<h4 id="esbuild_config-kwargs">kwargs</h4>
372+
373+
Any other common attributes
374+
375+
376+
377+
326378
## esbuild_repositories
327379

328380
**USAGE**

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"conventional-changelog-cli": "^2.0.21",
4343
"core-util-is": "^1.0.2",
4444
"date-fns": "1.30.1",
45+
"esbuild-plugin-svg": "0.1.0",
4546
"google-protobuf": "^3.6.1",
4647
"grpc-web": "1.1.0",
4748
"hello": "file:./tools/npm_packages/hello",

packages/esbuild/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ filegroup(
5858
name = "srcs",
5959
srcs = [
6060
"esbuild.bzl",
61+
"esbuild_config.bzl",
6162
"esbuild_packages.bzl",
6263
"esbuild_repositories.bzl",
6364
"helpers.bzl",

packages/esbuild/esbuild.bzl

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ def _esbuild_impl(ctx):
152152
inputs.append(ctx.file.args_file)
153153
launcher_args.add("--user_args=%s" % ctx.file.args_file.path)
154154

155+
if ctx.attr.config:
156+
configs = ctx.attr.config[JSEcmaScriptModuleInfo].sources.to_list()
157+
if len(configs) != 1:
158+
fail("Expected only one source file: the configuration entrypoint")
159+
160+
inputs.append(configs[0])
161+
launcher_args.add("--config_file=%s" % configs[0].path)
162+
155163
run_node(
156164
ctx = ctx,
157165
inputs = depset(inputs),
@@ -188,7 +196,7 @@ Values are subject to $(location ...) expansion""",
188196
"args_file": attr.label(
189197
allow_single_file = True,
190198
mandatory = False,
191-
doc = "A JSON file containing additional arguments that are passed to esbuild. Note: only one of args or args_file may be set",
199+
doc = "Internal use only",
192200
),
193201
"define": attr.string_dict(
194202
default = {},
@@ -333,6 +341,13 @@ edge16, node10, esnext). Default es2015.
333341
See https://esbuild.github.io/api/#target for more details
334342
""",
335343
),
344+
"config": attr.label(
345+
providers = [JSEcmaScriptModuleInfo],
346+
mandatory = False,
347+
doc = """Configuration file used for esbuild, from the esbuild_config macro. Note that options set in this file may get overwritten.
348+
See https://github.com/bazelbuild/rules_nodejs/tree/stable/packages/esbuild/test/plugins/BUILD.bazel for examples of using esbuild_config and plugins.
349+
""",
350+
),
336351
},
337352
implementation = _esbuild_impl,
338353
toolchains = [
@@ -367,12 +382,12 @@ def esbuild_macro(name, output_dir = False, splitting = False, **kwargs):
367382
deps = kwargs.pop("deps", []) + ["@esbuild_npm//esbuild"]
368383
entry_points = kwargs.get("entry_points", None)
369384

370-
args = kwargs.pop("args", {})
385+
# TODO(mattem): remove `args` and `args_file` in 5.x and everything can go via `config`
371386
args_file = kwargs.pop("args_file", None)
387+
if args_file:
388+
fail("Setting 'args_file' is not supported, set 'config' instead")
372389

373-
if args and args_file:
374-
fail("Both 'args' and 'args_file' attributes set, these are mutually exclusive")
375-
390+
args = kwargs.pop("args", {})
376391
if args:
377392
if type(args) != type(dict()):
378393
fail("Expected 'args' to be of type dict")
@@ -385,6 +400,11 @@ def esbuild_macro(name, output_dir = False, splitting = False, **kwargs):
385400
data = deps + srcs,
386401
)
387402

403+
config = kwargs.pop("config", None)
404+
if config:
405+
kwargs.setdefault("config", config)
406+
deps.append("%s_deps" % config)
407+
388408
if output_dir == True or entry_points or splitting == True:
389409
esbuild(
390410
name = name,

packages/esbuild/esbuild_config.bzl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"esbuild configuration file helper macro"
2+
3+
load("@build_bazel_rules_nodejs//:index.bzl", _js_library = "js_library")
4+
5+
def esbuild_config(name, config_file, srcs = [], deps = [], **kwargs):
6+
"""Macro for an esbuild configuration file and its assoicated dependencies
7+
8+
Args:
9+
name: Unique name for this rule
10+
config_file: The configuration file / entrypoint
11+
srcs: List of source files referenced by the configuration
12+
deps: List of dependencies required for this configuration
13+
**kwargs: Any other common attributes
14+
"""
15+
16+
_js_library(
17+
name = name,
18+
srcs = [config_file],
19+
**kwargs
20+
)
21+
22+
_js_library(
23+
name = "%s_deps" % name,
24+
srcs = srcs,
25+
deps = deps,
26+
**kwargs
27+
)

packages/esbuild/index.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@ load(
1919
"@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl",
2020
_esbuild_macro = "esbuild_macro",
2121
)
22+
load(
23+
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_config.bzl",
24+
_esbuild_config = "esbuild_config",
25+
)
2226
load(
2327
"@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain.bzl",
2428
_configure_esbuild_toolchain = "configure_esbuild_toolchain",
2529
)
2630

2731
esbuild = _esbuild_macro
32+
esbuild_config = _esbuild_config
2833
configure_esbuild_toolchain = _configure_esbuild_toolchain

packages/esbuild/index.docs.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ load(
9292
"@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl",
9393
_esbuild = "esbuild",
9494
)
95+
load(
96+
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_config.bzl",
97+
_esbuild_config = "esbuild_config",
98+
)
9599
load(
96100
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_repositories.bzl",
97101
_esbuild_repositories = "esbuild_repositories",
@@ -102,5 +106,6 @@ load(
102106
)
103107

104108
esbuild = _esbuild
109+
esbuild_config = _esbuild_config
105110
esbuild_repositories = _esbuild_repositories
106111
configure_esbuild_toolchain = _configure_esbuild_toolchain

packages/esbuild/launcher.js

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const {readFileSync, writeFileSync} = require('fs');
2+
const {pathToFileURL} = require('url');
3+
const {join} = require('path');
24
const esbuild = require('esbuild');
35

46
function getFlag(flag, required = true) {
@@ -22,22 +24,82 @@ function getEsbuildArgs(paramsFilePath) {
2224
}
2325
}
2426

27+
async function processConfigFile(configFilePath) {
28+
const fullConfigFileUrl = pathToFileURL(join(process.cwd(), configFilePath));
29+
let config;
30+
try {
31+
config = await import(fullConfigFileUrl);
32+
} catch (e) {
33+
console.error(`Error while loading configuration '${fullConfigFileUrl}':\n`, e);
34+
process.exit(1);
35+
}
36+
37+
if (!config.default) {
38+
console.error(`Config file '${configFilePath}' was loaded, but did not export a configuration object as default`);
39+
process.exit(1);
40+
}
41+
42+
config = config.default;
43+
44+
// These keys of the config can not be overriden
45+
const IGNORED_CONFIG_KEYS = [
46+
'bundle',
47+
'define',
48+
'entryPoints',
49+
'external',
50+
'metafile',
51+
'outdir',
52+
'outfile',
53+
'preserveSymlinks',
54+
'sourcemap',
55+
'splitting',
56+
'tsconfig',
57+
];
58+
59+
return Object.entries(config).reduce((prev, [key, value]) => {
60+
if (IGNORED_CONFIG_KEYS.includes(key)) {
61+
console.error(`[WARNING] esbuild configuration property '${key}' from '${configFilePath}' will be ignored and overriden`);
62+
} else {
63+
prev[key] = value;
64+
}
65+
return prev;
66+
}, {});
67+
}
68+
2569
if (!process.env.ESBUILD_BINARY_PATH) {
2670
console.error('Expected enviournment variable ESBUILD_BINARY_PATH to be set', e);
2771
process.exit(1);
2872
}
2973

30-
let args = getEsbuildArgs(getFlag('--esbuild_args'));
31-
32-
const userArgsFile = getFlag("--user_args", false);
33-
if (userArgsFile) {
34-
args = {
35-
...args,
36-
...getEsbuildArgs(userArgsFile)
37-
};
74+
async function runOneBuild(args, userArgsFilePath, configFilePath) {
75+
if (userArgsFilePath) {
76+
args = {
77+
...args,
78+
...getEsbuildArgs(userArgsFilePath)
79+
}
80+
}
81+
82+
if (configFilePath) {
83+
const config = await processConfigFile(configFilePath);
84+
args = {
85+
...args,
86+
...config
87+
};
88+
}
89+
90+
const metafile = getFlag('--metafile');
91+
92+
try {
93+
const result = await esbuild.build(args);
94+
writeFileSync(metafile, JSON.stringify(result.metafile));
95+
} catch (e) {
96+
console.error(e);
97+
process.exit(1);
98+
}
3899
}
39100

40-
const metafile = getFlag('--metafile');
41-
42-
const result = esbuild.buildSync(args);
43-
writeFileSync(metafile, JSON.stringify(result.metafile));
101+
runOneBuild(
102+
getEsbuildArgs(getFlag("--esbuild_args")),
103+
getFlag("--user_args", false),
104+
getFlag("--config_file", false)
105+
);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
load("//:index.bzl", "generated_file_test", "js_library", "nodejs_binary", "npm_package_bin")
2+
load("//packages/esbuild:index.bzl", "esbuild")
3+
load("//packages/esbuild:esbuild_config.bzl", "esbuild_config")
4+
5+
js_library(
6+
name = "main",
7+
srcs = [
8+
"logo.svg",
9+
"main.js",
10+
"words.txt",
11+
],
12+
)
13+
14+
js_library(
15+
name = "txt_array_plugin",
16+
srcs = [
17+
"txt-array-plugin.js",
18+
],
19+
)
20+
21+
esbuild_config(
22+
name = "esbuild_config",
23+
config_file = "esbuild.config.mjs",
24+
deps = [
25+
":txt_array_plugin",
26+
"@npm//esbuild-plugin-svg",
27+
],
28+
)
29+
30+
esbuild(
31+
name = "bundle",
32+
config = ":esbuild_config",
33+
entry_point = "main.js",
34+
deps = [
35+
":main",
36+
],
37+
)
38+
39+
nodejs_binary(
40+
name = "bin",
41+
data = [
42+
":bundle",
43+
],
44+
entry_point = "bundle.js",
45+
)
46+
47+
npm_package_bin(
48+
name = "runner",
49+
stdout = "out.txt",
50+
tool = ":bin",
51+
)
52+
53+
generated_file_test(
54+
name = "test",
55+
src = "out.golden.txt",
56+
generated = "out.txt",
57+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { default as txtArrayPlugin } from './txt-array-plugin.js';
2+
import { default as svgPlugin } from 'esbuild-plugin-svg';
3+
4+
export default {
5+
plugins: [
6+
txtArrayPlugin,
7+
svgPlugin(),
8+
],
9+
}

0 commit comments

Comments
 (0)