From 813e5e846eb73e0edd699fa378b97776f3b5162b Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Wed, 7 Jun 2023 14:39:53 -0700 Subject: [PATCH] feat: set explicit and configurable tsconfig By default esbuild uses any local tsconfig.json file. With sandboxing issues (https://github.com/aspect-build/rules_esbuild/issues/58) this may unexpectedly cause `esbuild_bundle` to pickup any local tsconfig.json. A custom tsconfig can now be set using `esbuild_bundle(tsconfig)`. By default an empty tsconfig is used to prevent unexpected sandboxing issues. --- .github/workflows/ci.yaml | 1 + docs/esbuild.md | 3 ++- e2e/tsconfig/.bazelrc | 2 ++ e2e/tsconfig/BUILD.bazel | 48 +++++++++++++++++++++++++++++++++++ e2e/tsconfig/MODULE.bazel | 8 ++++++ e2e/tsconfig/WORKSPACE | 22 ++++++++++++++++ e2e/tsconfig/WORKSPACE.bzlmod | 0 e2e/tsconfig/libs/a.js | 1 + e2e/tsconfig/libs/b.js | 1 + e2e/tsconfig/main.js | 3 +++ e2e/tsconfig/tsconfig-a.json | 7 +++++ e2e/tsconfig/tsconfig-b.json | 7 +++++ e2e/tsconfig/tsconfig.json | 1 + esbuild/defs.bzl | 3 +++ esbuild/private/BUILD.bazel | 7 +++++ esbuild/private/empty.json | 1 + esbuild/private/esbuild.bzl | 12 ++++++++- 17 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 e2e/tsconfig/.bazelrc create mode 100644 e2e/tsconfig/BUILD.bazel create mode 100644 e2e/tsconfig/MODULE.bazel create mode 100644 e2e/tsconfig/WORKSPACE create mode 100644 e2e/tsconfig/WORKSPACE.bzlmod create mode 100644 e2e/tsconfig/libs/a.js create mode 100644 e2e/tsconfig/libs/b.js create mode 100644 e2e/tsconfig/main.js create mode 100644 e2e/tsconfig/tsconfig-a.json create mode 100644 e2e/tsconfig/tsconfig-b.json create mode 100644 e2e/tsconfig/tsconfig.json create mode 100644 esbuild/private/empty.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 864f88a..e56d998 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,6 +71,7 @@ jobs: - 'e2e/smoke' - 'e2e/npm-links' - 'e2e/sourcemaps' + - 'e2e/tsconfig' exclude: # Don't test macos with Bazel 5 to minimize macOS minutes (billed at 10X) # https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#included-storage-and-minutes diff --git a/docs/esbuild.md b/docs/esbuild.md index 6c79c20..0d388ad 100644 --- a/docs/esbuild.md +++ b/docs/esbuild.md @@ -9,7 +9,7 @@
 esbuild_bundle(name, args_file, config, data, define, deps, entry_point, entry_points, external,
                format, launcher, max_threads, metafile, minify, output, output_css, output_dir,
-               output_map, platform, sourcemap, sources_content, splitting, srcs, target)
+               output_map, platform, sourcemap, sources_content, splitting, srcs, target, tsconfig)
 
Runs the esbuild bundler under Bazel @@ -46,6 +46,7 @@ For further information about esbuild, see https://esbuild.github.io/ | splitting | 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 and https://esbuild.github.io/api/#entry-points for more details | Boolean | optional | False | | srcs | Source files to be made available to esbuild | List of labels | optional | [] | | target | Environment target (e.g. es2017, chrome58, firefox57, safari11, edge16, node10, esnext). Default es2015.

See https://esbuild.github.io/api/#target for more details | String | optional | "es2015" | +| tsconfig | TypeScript configuration file used by esbuild. Default to an empty file with no configuration.

See https://esbuild.github.io/api/#tsconfig for more details | Label | required | | diff --git a/e2e/tsconfig/.bazelrc b/e2e/tsconfig/.bazelrc new file mode 100644 index 0000000..79033cf --- /dev/null +++ b/e2e/tsconfig/.bazelrc @@ -0,0 +1,2 @@ +build --enable_runfiles +common:bzlmod --enable_bzlmod diff --git a/e2e/tsconfig/BUILD.bazel b/e2e/tsconfig/BUILD.bazel new file mode 100644 index 0000000..3593993 --- /dev/null +++ b/e2e/tsconfig/BUILD.bazel @@ -0,0 +1,48 @@ +load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild") +load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") + +SRCS = ["main.js", "libs/a.js", "libs/b.js"] +ENTRY = "main.js" + +esbuild( + name = "target-a", + srcs = SRCS, + entry_point = ENTRY, + tsconfig = "tsconfig-a.json", + output = "a.js", +) + +esbuild( + name = "target-b", + srcs = SRCS, + entry_point = ENTRY, + tsconfig = "tsconfig-b.json", + output = "b.js", +) + +esbuild( + name = "target-none", + # include the tsconfig.json with bad syntax and ensure it is not used + srcs = [ENTRY, "tsconfig.json"], + entry_point = ENTRY, + output = "none.js", + external = ["var-lib"], +) + +assert_contains( + name = "config-a", + actual = "a.js", + expected = "library: A" +) + +assert_contains( + name = "config-b", + actual = "b.js", + expected = "library: B" +) + +assert_contains( + name = "config-none", + actual = "none.js", + expected = "var-lib" +) \ No newline at end of file diff --git a/e2e/tsconfig/MODULE.bazel b/e2e/tsconfig/MODULE.bazel new file mode 100644 index 0000000..2f0fcc6 --- /dev/null +++ b/e2e/tsconfig/MODULE.bazel @@ -0,0 +1,8 @@ +"Bazel dependencies" +bazel_dep(name = "aspect_rules_esbuild", dev_dependency = True, version = "0.0.0") +bazel_dep(name = "aspect_bazel_lib", dev_dependency = True, version = "1.29.2") + +local_path_override( + module_name = "aspect_rules_esbuild", + path = "../..", +) diff --git a/e2e/tsconfig/WORKSPACE b/e2e/tsconfig/WORKSPACE new file mode 100644 index 0000000..1ca80c2 --- /dev/null +++ b/e2e/tsconfig/WORKSPACE @@ -0,0 +1,22 @@ +local_repository( + name = "aspect_rules_esbuild", + path = "../..", +) + +load("@aspect_rules_esbuild//esbuild:dependencies.bzl", "rules_esbuild_dependencies") + +rules_esbuild_dependencies() + +load("@rules_nodejs//nodejs:repositories.bzl", "DEFAULT_NODE_VERSION", "nodejs_register_toolchains") + +nodejs_register_toolchains( + name = "node", + node_version = DEFAULT_NODE_VERSION, +) + +load("@aspect_rules_esbuild//esbuild:repositories.bzl", "LATEST_ESBUILD_VERSION", "esbuild_register_toolchains") + +esbuild_register_toolchains( + name = "esbuild", + esbuild_version = LATEST_ESBUILD_VERSION, +) diff --git a/e2e/tsconfig/WORKSPACE.bzlmod b/e2e/tsconfig/WORKSPACE.bzlmod new file mode 100644 index 0000000..e69de29 diff --git a/e2e/tsconfig/libs/a.js b/e2e/tsconfig/libs/a.js new file mode 100644 index 0000000..3b4bcc5 --- /dev/null +++ b/e2e/tsconfig/libs/a.js @@ -0,0 +1 @@ +export const ANSWER = "library: A" \ No newline at end of file diff --git a/e2e/tsconfig/libs/b.js b/e2e/tsconfig/libs/b.js new file mode 100644 index 0000000..583a803 --- /dev/null +++ b/e2e/tsconfig/libs/b.js @@ -0,0 +1 @@ +export const ANSWER = "library: B" \ No newline at end of file diff --git a/e2e/tsconfig/main.js b/e2e/tsconfig/main.js new file mode 100644 index 0000000..db807f9 --- /dev/null +++ b/e2e/tsconfig/main.js @@ -0,0 +1,3 @@ +import { ANSWER } from "var-lib" + +console.log(ANSWER) \ No newline at end of file diff --git a/e2e/tsconfig/tsconfig-a.json b/e2e/tsconfig/tsconfig-a.json new file mode 100644 index 0000000..8f08a13 --- /dev/null +++ b/e2e/tsconfig/tsconfig-a.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "var-lib": ["./libs/a.js"] + } + } +} \ No newline at end of file diff --git a/e2e/tsconfig/tsconfig-b.json b/e2e/tsconfig/tsconfig-b.json new file mode 100644 index 0000000..82a5c27 --- /dev/null +++ b/e2e/tsconfig/tsconfig-b.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "var-lib": ["./libs/b.js"] + } + } +} \ No newline at end of file diff --git a/e2e/tsconfig/tsconfig.json b/e2e/tsconfig/tsconfig.json new file mode 100644 index 0000000..c0a99e3 --- /dev/null +++ b/e2e/tsconfig/tsconfig.json @@ -0,0 +1 @@ +with invalid syntax, esbuild shouldn't use this \ No newline at end of file diff --git a/esbuild/defs.bzl b/esbuild/defs.bzl index 5b58141..25cbabd 100644 --- a/esbuild/defs.bzl +++ b/esbuild/defs.bzl @@ -23,6 +23,7 @@ def esbuild(name, output_dir = False, splitting = False, config = None, **kwargs srcs = kwargs.pop("srcs", []) deps = kwargs.pop("deps", []) entry_points = kwargs.get("entry_points", None) + tsconfig = kwargs.pop("tsconfig", Label("@aspect_rules_esbuild//esbuild/private:empty-json")) if types.is_dict(config): config_file = "_%s_config.mjs" % name @@ -37,6 +38,7 @@ def esbuild(name, output_dir = False, splitting = False, config = None, **kwargs _esbuild( name = name, config = config, + tsconfig = tsconfig, srcs = srcs, splitting = splitting, output_dir = True, @@ -61,6 +63,7 @@ def esbuild(name, output_dir = False, splitting = False, config = None, **kwargs name = name, srcs = srcs, config = config, + tsconfig = tsconfig, output = output, output_map = output_map, deps = deps, diff --git a/esbuild/private/BUILD.bazel b/esbuild/private/BUILD.bazel index 7f5f44f..8147200 100644 --- a/esbuild/private/BUILD.bazel +++ b/esbuild/private/BUILD.bazel @@ -1,3 +1,4 @@ +load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin") load("@bazel_skylib//:bzl_library.bzl", "bzl_library") exports_files( @@ -10,6 +11,12 @@ exports_files( visibility = ["//visibility:public"], ) +copy_to_bin( + name = "empty-json", + srcs = ["empty.json"], + visibility = ["//visibility:public"], +) + bzl_library( name = "esbuild", srcs = ["esbuild.bzl"], diff --git a/esbuild/private/empty.json b/esbuild/private/empty.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/esbuild/private/empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/esbuild/private/esbuild.bzl b/esbuild/private/esbuild.bzl index 39e9c69..3599649 100644 --- a/esbuild/private/esbuild.bzl +++ b/esbuild/private/esbuild.bzl @@ -163,6 +163,14 @@ See https://esbuild.github.io/api/#target for more details TODO: show how to write a config file that depends on plugins, similar to the esbuild_config macro in rules_nodejs. """, ), + "tsconfig": attr.label( + mandatory = True, + allow_single_file = True, + doc = """TypeScript configuration file used by esbuild. Default to an empty file with no configuration. + + See https://esbuild.github.io/api/#tsconfig for more details + """ + ) } def _bin_relative_path(ctx, file): @@ -181,6 +189,7 @@ def _esbuild_impl(ctx): entry_points = desugar_entry_point_names(ctx.file.entry_point, ctx.files.entry_points) entry_points_bin_copy = copy_files_to_bin_actions(ctx, entry_points) + tsconfig_bin_copy = copy_file_to_bin_action(ctx, ctx.file.tsconfig) args = dict({ "bundle": True, @@ -199,6 +208,7 @@ def _esbuild_impl(ctx): # Also disable the log limit and show all logs "logLevel": "warning", "logLimit": 0, + "tsconfig": _bin_relative_path(ctx, tsconfig_bin_copy), "metafile": ctx.attr.metafile, "platform": ctx.attr.platform, # Don't preserve symlinks since doing so breaks node_modules resolution @@ -311,7 +321,7 @@ def _esbuild_impl(ctx): file for file in ctx.files.srcs if not (file.path.endswith(".d.ts") or file.path.endswith(".tsbuildinfo")) - ]) + entry_points_bin_copy + other_inputs + node_toolinfo.tool_files + esbuild_toolinfo.tool_files, + ]) + entry_points_bin_copy + [tsconfig_bin_copy] + other_inputs + node_toolinfo.tool_files + esbuild_toolinfo.tool_files, transitive = [js_lib_helpers.gather_files_from_js_providers( targets = ctx.attr.srcs + ctx.attr.deps, include_transitive_sources = True,