Skip to content

Commit

Permalink
feat(esbuild): add support for multiple entry points (#2663)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbedard authored May 10, 2021
1 parent 768d352 commit b4f322a
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 12 deletions.
38 changes: 26 additions & 12 deletions packages/esbuild/esbuild.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ esbuild rule
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "JSModuleInfo", "NpmPackageInfo", "node_modules_aspect", "run_node")
load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "MODULE_MAPPINGS_ASPECT_RESULTS_NAME", "module_mappings_aspect")
load(":helpers.bzl", "filter_files", "generate_path_mapping", "resolve_entry_point", "write_jsconfig_file")
load(":helpers.bzl", "desugar_entry_point_names", "filter_files", "generate_path_mapping", "resolve_entry_point", "write_jsconfig_file")

def _esbuild_impl(ctx):
# For each dep, JSEcmaScriptModuleInfo is used if found, then JSModuleInfo and finally
Expand Down Expand Up @@ -38,18 +38,21 @@ def _esbuild_impl(ctx):
package_name = key.split(":")[0]
path_alias_mappings.update(generate_path_mapping(package_name, value[1].replace(ctx.bin_dir.path + "/", "")))

entry_points = desugar_entry_point_names(ctx.file.entry_point, ctx.files.entry_points)

deps_inputs = depset(transitive = deps_depsets).to_list()
inputs = filter_files(ctx.files.entry_point) + ctx.files.srcs + deps_inputs
inputs = filter_files(entry_points) + ctx.files.srcs + deps_inputs

metafile = ctx.actions.declare_file("%s_metadata.json" % ctx.attr.name)
outputs = [metafile]

entry_point = resolve_entry_point(ctx.file.entry_point, inputs, ctx.files.srcs)

args = ctx.actions.args()
args.use_param_file(param_file_arg = "--esbuild_flags=%s", use_always = True)

args.add("--bundle", entry_point.path)
# the entry point files to bundle
for entry_point in entry_points:
args.add(resolve_entry_point(entry_point, inputs, ctx.files.srcs))
args.add("--bundle")

if len(ctx.attr.sourcemap) > 0:
args.add_joined(["--sourcemap", ctx.attr.sourcemap], join_with = "=")
Expand Down Expand Up @@ -126,7 +129,7 @@ def _esbuild_impl(ctx):
inputs = depset(inputs),
outputs = outputs,
arguments = [launcher_args, args],
progress_message = "%s Javascript %s [esbuild]" % ("Bundling" if not ctx.attr.output_dir else "Splitting", entry_point.short_path),
progress_message = "%s Javascript %s [esbuild]" % ("Bundling" if not ctx.attr.output_dir else "Splitting", " ".join([entry_point.short_path for entry_point in entry_points])),
execution_requirements = execution_requirements,
mnemonic = "esbuild",
env = env,
Expand Down Expand Up @@ -168,9 +171,19 @@ See https://esbuild.github.io/api/#define for more details
doc = "A list of direct dependencies that are required to build the bundle",
),
"entry_point": attr.label(
mandatory = True,
allow_single_file = True,
doc = "The bundle's entry point (e.g. your main.js or app.js or index.js)",
doc = """The bundle's entry point (e.g. your main.js or app.js or index.js)
This is a shortcut for the `entry_points` attribute with a single entry.
Specify either this attribute or `entry_point`, but not both.
""",
),
"entry_points": attr.label_list(
allow_files = True,
doc = """The bundle's entry points (e.g. your main.js or app.js or index.js)
Specify either this attribute or `entry_point`, but not both.
""",
),
"external": attr.string_list(
default = [],
Expand All @@ -183,7 +196,7 @@ See https://esbuild.github.io/api/#external for more details
values = ["iife", "cjs", "esm", ""],
mandatory = False,
doc = """The output format of the bundle, defaults to iife when platform is browser
and cjs when platform is node. If performing code splitting, defaults to esm.
and cjs when platform is node. If performing code splitting or multiple entry_points are specified, defaults to esm.
See https://esbuild.github.io/api/#format for more details
""",
Expand Down Expand Up @@ -229,9 +242,9 @@ file is named 'foo.js', you should set this to 'foo.css'.""",
),
"output_dir": attr.bool(
default = False,
doc = """If true, esbuild produces an output directory containing all the output files from code splitting
doc = """If true, esbuild produces an output directory containing all the output files from code splitting for multiple entry points
See https://esbuild.github.io/api/#splitting for more details
See https://esbuild.github.io/api/#splitting and https://esbuild.github.io/api/#entry-points for more details
""",
),
"output_map": attr.output(
Expand Down Expand Up @@ -308,7 +321,8 @@ def esbuild_macro(name, output_dir = False, **kwargs):
entry_point = Label("@build_bazel_rules_nodejs//packages/esbuild:launcher.js"),
)

if output_dir == True:
entry_points = kwargs.get("entry_points", None)
if output_dir == True or entry_points:
esbuild(
name = name,
output_dir = True,
Expand Down
22 changes: 22 additions & 0 deletions packages/esbuild/helpers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ def resolve_entry_point(f, inputs, srcs):

fail("Could not find corresponding entry point for %s. Add the %s.js to your deps or %s.ts to your srcs" % (f.path, no_ext, no_ext))

def desugar_entry_point_names(entry_point, entry_points):
"""Users can specify entry_point (sugar) or entry_points (long form).
This function allows our code to treat it like they always used the long form.
It also validates that exactly one of these attributes should be specified.
Args:
entry_point: the simple argument for specifying a single entry
entry_points: the long form argument for specifing one or more entry points
Returns:
the array of entry poitns
"""
if entry_point and entry_points:
fail("Cannot specify both entry_point and entry_points")
if not entry_point and not entry_points:
fail("One of entry_point or entry_points must be specified")
if entry_point:
return [entry_point]
return entry_points

def filter_files(input, endings = ALLOWED_EXTENSIONS):
"""Filters a list of files for specific endings
Expand Down
36 changes: 36 additions & 0 deletions packages/esbuild/test/entries/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
load("//:index.bzl", "nodejs_test")
load("//packages/esbuild/test:tests.bzl", "esbuild")
load("//packages/typescript:index.bzl", "ts_library")

ts_library(
name = "index",
srcs = ["index.ts"],
module_name = "lib",
)

ts_library(
name = "lib",
srcs = [
"a.ts",
"b.ts",
],
deps = [":index"],
)

esbuild(
name = "bundle",
entry_points = [
"a.ts",
"b.ts",
],
deps = [":lib"],
)

nodejs_test(
name = "bundle_test",
data = [
"bundle.spec.js",
":bundle",
],
entry_point = ":bundle.spec.js",
)
3 changes: 3 additions & 0 deletions packages/esbuild/test/entries/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {NAME} from 'lib';

console.log(`Hello ${NAME}`);
3 changes: 3 additions & 0 deletions packages/esbuild/test/entries/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {NAME} from 'lib';

console.log(`Hello ${NAME} from b.ts`);
25 changes: 25 additions & 0 deletions packages/esbuild/test/entries/bundle.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const {join} = require('path');
const {readFileSync, lstatSync} = require('fs');

const helper = require(process.env.BAZEL_NODE_RUNFILES_HELPER);
const location = helper.resolve('build_bazel_rules_nodejs/packages/esbuild/test/entries/bundle/');

const a = readFileSync(join(location, 'a.js'), {encoding: 'utf8'});
const b = readFileSync(join(location, 'b.js'), {encoding: 'utf8'});
const aHasImportOfChunk = a.match(/\/(chunk-[a-zA-Z0-9]+\.js)";/);
const bHasImportOfChunk = b.match(/\/(chunk-[a-zA-Z0-9]+\.js)";/);

if (!aHasImportOfChunk || !bHasImportOfChunk) {
console.error(`Expected entry_points 'a.js' and 'b.js' to have an import of './chunk-[hash].js'`);
}

if (aHasImportOfChunk[1] !== bHasImportOfChunk[1]) {
console.error(`Expected entry_points 'a.js' and 'b.js' to the same shared chunk`);
}

// throws if file does not exist
lstatSync(join(location, aHasImportOfChunk && aHasImportOfChunk[1]));

process.exit(
(aHasImportOfChunk && bHasImportOfChunk && aHasImportOfChunk[1] === bHasImportOfChunk[1]) ? 0 :
1);
1 change: 1 addition & 0 deletions packages/esbuild/test/entries/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const NAME = 'bazel';

0 comments on commit b4f322a

Please sign in to comment.