Skip to content

Commit

Permalink
feat(esbuild): add support for plugins via supplying a configuration …
Browse files Browse the repository at this point in the history
…file (#2840)
  • Loading branch information
mattem authored Aug 12, 2021
1 parent 99d3177 commit c95d9ca
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 21 deletions.
60 changes: 56 additions & 4 deletions docs/esbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ This will create an output directory containing all the code split chunks, along
**USAGE**

<pre>
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>,
<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>,
<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>)
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>,
<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>,
<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>)
</pre>

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

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

(*<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
(*<a href="https://bazel.build/docs/build-ref.html#labels">Label</a>*): Internal use only

Defaults to `None`

<h4 id="esbuild-config">config</h4>

(*<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.
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


Defaults to `None`

Expand Down Expand Up @@ -323,6 +331,50 @@ list of platform constraints



## esbuild_config

**USAGE**

<pre>
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>)
</pre>

Macro for an esbuild configuration file and its assoicated dependencies

**PARAMETERS**


<h4 id="esbuild_config-name">name</h4>

Unique name for this rule



<h4 id="esbuild_config-config_file">config_file</h4>

The configuration file / entrypoint



<h4 id="esbuild_config-srcs">srcs</h4>

List of source files referenced by the configuration

Defaults to `[]`

<h4 id="esbuild_config-deps">deps</h4>

List of dependencies required for this configuration

Defaults to `[]`

<h4 id="esbuild_config-kwargs">kwargs</h4>

Any other common attributes




## esbuild_repositories

**USAGE**
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"conventional-changelog-cli": "^2.0.21",
"core-util-is": "^1.0.2",
"date-fns": "1.30.1",
"esbuild-plugin-svg": "0.1.0",
"google-protobuf": "^3.6.1",
"grpc-web": "1.1.0",
"hello": "file:./tools/npm_packages/hello",
Expand Down
1 change: 1 addition & 0 deletions packages/esbuild/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ filegroup(
name = "srcs",
srcs = [
"esbuild.bzl",
"esbuild_config.bzl",
"esbuild_packages.bzl",
"esbuild_repositories.bzl",
"helpers.bzl",
Expand Down
30 changes: 25 additions & 5 deletions packages/esbuild/esbuild.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ def _esbuild_impl(ctx):
inputs.append(ctx.file.args_file)
launcher_args.add("--user_args=%s" % ctx.file.args_file.path)

if ctx.attr.config:
configs = ctx.attr.config[JSEcmaScriptModuleInfo].sources.to_list()
if len(configs) != 1:
fail("Expected only one source file: the configuration entrypoint")

inputs.append(configs[0])
launcher_args.add("--config_file=%s" % configs[0].path)

run_node(
ctx = ctx,
inputs = depset(inputs),
Expand Down Expand Up @@ -188,7 +196,7 @@ Values are subject to $(location ...) expansion""",
"args_file": attr.label(
allow_single_file = True,
mandatory = False,
doc = "A JSON file containing additional arguments that are passed to esbuild. Note: only one of args or args_file may be set",
doc = "Internal use only",
),
"define": attr.string_dict(
default = {},
Expand Down Expand Up @@ -333,6 +341,13 @@ edge16, node10, esnext). Default es2015.
See https://esbuild.github.io/api/#target for more details
""",
),
"config": attr.label(
providers = [JSEcmaScriptModuleInfo],
mandatory = False,
doc = """Configuration file used for esbuild, from the esbuild_config macro. Note that options set in this file may get overwritten.
See https://github.com/bazelbuild/rules_nodejs/tree/stable/packages/esbuild/test/plugins/BUILD.bazel for examples of using esbuild_config and plugins.
""",
),
},
implementation = _esbuild_impl,
toolchains = [
Expand Down Expand Up @@ -367,12 +382,12 @@ def esbuild_macro(name, output_dir = False, splitting = False, **kwargs):
deps = kwargs.pop("deps", []) + ["@esbuild_npm//esbuild"]
entry_points = kwargs.get("entry_points", None)

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

if args and args_file:
fail("Both 'args' and 'args_file' attributes set, these are mutually exclusive")

args = kwargs.pop("args", {})
if args:
if type(args) != type(dict()):
fail("Expected 'args' to be of type dict")
Expand All @@ -385,6 +400,11 @@ def esbuild_macro(name, output_dir = False, splitting = False, **kwargs):
data = deps + srcs,
)

config = kwargs.pop("config", None)
if config:
kwargs.setdefault("config", config)
deps.append("%s_deps" % config)

if output_dir == True or entry_points or splitting == True:
esbuild(
name = name,
Expand Down
27 changes: 27 additions & 0 deletions packages/esbuild/esbuild_config.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"esbuild configuration file helper macro"

load("@build_bazel_rules_nodejs//:index.bzl", _js_library = "js_library")

def esbuild_config(name, config_file, srcs = [], deps = [], **kwargs):
"""Macro for an esbuild configuration file and its assoicated dependencies
Args:
name: Unique name for this rule
config_file: The configuration file / entrypoint
srcs: List of source files referenced by the configuration
deps: List of dependencies required for this configuration
**kwargs: Any other common attributes
"""

_js_library(
name = name,
srcs = [config_file],
**kwargs
)

_js_library(
name = "%s_deps" % name,
srcs = srcs,
deps = deps,
**kwargs
)
5 changes: 5 additions & 0 deletions packages/esbuild/index.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ load(
"@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl",
_esbuild_macro = "esbuild_macro",
)
load(
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_config.bzl",
_esbuild_config = "esbuild_config",
)
load(
"@build_bazel_rules_nodejs//packages/esbuild/toolchain:toolchain.bzl",
_configure_esbuild_toolchain = "configure_esbuild_toolchain",
)

esbuild = _esbuild_macro
esbuild_config = _esbuild_config
configure_esbuild_toolchain = _configure_esbuild_toolchain
5 changes: 5 additions & 0 deletions packages/esbuild/index.docs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ load(
"@build_bazel_rules_nodejs//packages/esbuild:esbuild.bzl",
_esbuild = "esbuild",
)
load(
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_config.bzl",
_esbuild_config = "esbuild_config",
)
load(
"@build_bazel_rules_nodejs//packages/esbuild:esbuild_repositories.bzl",
_esbuild_repositories = "esbuild_repositories",
Expand All @@ -102,5 +106,6 @@ load(
)

esbuild = _esbuild
esbuild_config = _esbuild_config
esbuild_repositories = _esbuild_repositories
configure_esbuild_toolchain = _configure_esbuild_toolchain
86 changes: 74 additions & 12 deletions packages/esbuild/launcher.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const {readFileSync, writeFileSync} = require('fs');
const {pathToFileURL} = require('url');
const {join} = require('path');
const esbuild = require('esbuild');

function getFlag(flag, required = true) {
Expand All @@ -22,22 +24,82 @@ function getEsbuildArgs(paramsFilePath) {
}
}

async function processConfigFile(configFilePath) {
const fullConfigFileUrl = pathToFileURL(join(process.cwd(), configFilePath));
let config;
try {
config = await import(fullConfigFileUrl);
} catch (e) {
console.error(`Error while loading configuration '${fullConfigFileUrl}':\n`, e);
process.exit(1);
}

if (!config.default) {
console.error(`Config file '${configFilePath}' was loaded, but did not export a configuration object as default`);
process.exit(1);
}

config = config.default;

// These keys of the config can not be overriden
const IGNORED_CONFIG_KEYS = [
'bundle',
'define',
'entryPoints',
'external',
'metafile',
'outdir',
'outfile',
'preserveSymlinks',
'sourcemap',
'splitting',
'tsconfig',
];

return Object.entries(config).reduce((prev, [key, value]) => {
if (IGNORED_CONFIG_KEYS.includes(key)) {
console.error(`[WARNING] esbuild configuration property '${key}' from '${configFilePath}' will be ignored and overriden`);
} else {
prev[key] = value;
}
return prev;
}, {});
}

if (!process.env.ESBUILD_BINARY_PATH) {
console.error('Expected enviournment variable ESBUILD_BINARY_PATH to be set', e);
process.exit(1);
}

let args = getEsbuildArgs(getFlag('--esbuild_args'));

const userArgsFile = getFlag("--user_args", false);
if (userArgsFile) {
args = {
...args,
...getEsbuildArgs(userArgsFile)
};
async function runOneBuild(args, userArgsFilePath, configFilePath) {
if (userArgsFilePath) {
args = {
...args,
...getEsbuildArgs(userArgsFilePath)
}
}

if (configFilePath) {
const config = await processConfigFile(configFilePath);
args = {
...args,
...config
};
}

const metafile = getFlag('--metafile');

try {
const result = await esbuild.build(args);
writeFileSync(metafile, JSON.stringify(result.metafile));
} catch (e) {
console.error(e);
process.exit(1);
}
}

const metafile = getFlag('--metafile');

const result = esbuild.buildSync(args);
writeFileSync(metafile, JSON.stringify(result.metafile));
runOneBuild(
getEsbuildArgs(getFlag("--esbuild_args")),
getFlag("--user_args", false),
getFlag("--config_file", false)
);
57 changes: 57 additions & 0 deletions packages/esbuild/test/plugins/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
load("//:index.bzl", "generated_file_test", "js_library", "nodejs_binary", "npm_package_bin")
load("//packages/esbuild:index.bzl", "esbuild")
load("//packages/esbuild:esbuild_config.bzl", "esbuild_config")

js_library(
name = "main",
srcs = [
"logo.svg",
"main.js",
"words.txt",
],
)

js_library(
name = "txt_array_plugin",
srcs = [
"txt-array-plugin.js",
],
)

esbuild_config(
name = "esbuild_config",
config_file = "esbuild.config.mjs",
deps = [
":txt_array_plugin",
"@npm//esbuild-plugin-svg",
],
)

esbuild(
name = "bundle",
config = ":esbuild_config",
entry_point = "main.js",
deps = [
":main",
],
)

nodejs_binary(
name = "bin",
data = [
":bundle",
],
entry_point = "bundle.js",
)

npm_package_bin(
name = "runner",
stdout = "out.txt",
tool = ":bin",
)

generated_file_test(
name = "test",
src = "out.golden.txt",
generated = "out.txt",
)
9 changes: 9 additions & 0 deletions packages/esbuild/test/plugins/esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { default as txtArrayPlugin } from './txt-array-plugin.js';
import { default as svgPlugin } from 'esbuild-plugin-svg';

export default {
plugins: [
txtArrayPlugin,
svgPlugin(),
],
}
Loading

0 comments on commit c95d9ca

Please sign in to comment.