diff --git a/e2e/path_alias_repro/.bazelrc b/e2e/path_alias_repro/.bazelrc new file mode 100644 index 0000000..0a284db --- /dev/null +++ b/e2e/path_alias_repro/.bazelrc @@ -0,0 +1 @@ +common --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig diff --git a/e2e/path_alias_repro/BUILD.bazel b/e2e/path_alias_repro/BUILD.bazel new file mode 100644 index 0000000..d54d92a --- /dev/null +++ b/e2e/path_alias_repro/BUILD.bazel @@ -0,0 +1,7 @@ +load("@aspect_rules_ts//ts:defs.bzl", "ts_config") + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + visibility = ["//visibility:public"], +) diff --git a/e2e/path_alias_repro/MODULE.bazel b/e2e/path_alias_repro/MODULE.bazel new file mode 100644 index 0000000..2b39197 --- /dev/null +++ b/e2e/path_alias_repro/MODULE.bazel @@ -0,0 +1,23 @@ +module( + name = "esbuild_repro", + version = "0.0.0", +) + +bazel_dep(name = "aspect_rules_esbuild", version = "0.0.0", dev_dependency = True) +local_path_override( + module_name = "aspect_rules_esbuild", + path = "../..", +) + +bazel_dep(name = "aspect_rules_ts", version = "3.3.2") +bazel_dep(name = "aspect_rules_js", version = "2.1.0") + +rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext") +rules_ts_ext.deps() +use_repo(rules_ts_ext, "npm_typescript") + +rules_esbuild_ext = use_extension("@aspect_rules_esbuild//esbuild:extensions.bzl", "esbuild") +rules_esbuild_ext.toolchain(esbuild_version = "0.23.0") +use_repo(rules_esbuild_ext, "esbuild_toolchains") + +register_toolchains("@esbuild_toolchains//:all") diff --git a/e2e/path_alias_repro/app/BUILD.bazel b/e2e/path_alias_repro/app/BUILD.bazel new file mode 100644 index 0000000..3a49330 --- /dev/null +++ b/e2e/path_alias_repro/app/BUILD.bazel @@ -0,0 +1,21 @@ +load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild") +load("@aspect_rules_ts//ts:defs.bzl", "ts_project") + +ts_project( + name = "lib", + srcs = ["index.ts"], + declaration = True, + transpiler = "tsc", + tsconfig = "//:tsconfig", + deps = ["//lib"], +) + +esbuild( + name = "bundle", + srcs = [":lib"], + bundle = True, + entry_point = "index.js", + minify = True, + tsconfig = "//:tsconfig", + deps = ["//lib"], +) diff --git a/e2e/path_alias_repro/app/index.ts b/e2e/path_alias_repro/app/index.ts new file mode 100644 index 0000000..a51181a --- /dev/null +++ b/e2e/path_alias_repro/app/index.ts @@ -0,0 +1,3 @@ +import { hello } from 'esbuild_repro/lib/index'; + +console.log(hello); diff --git a/e2e/path_alias_repro/lib/BUILD.bazel b/e2e/path_alias_repro/lib/BUILD.bazel new file mode 100644 index 0000000..1a392ba --- /dev/null +++ b/e2e/path_alias_repro/lib/BUILD.bazel @@ -0,0 +1,10 @@ +load("@aspect_rules_ts//ts:defs.bzl", "ts_project") + +ts_project( + name = "lib", + srcs = ["index.ts"], + declaration = True, + transpiler = "tsc", + tsconfig = "//:tsconfig", + visibility = ["//visibility:public"], +) diff --git a/e2e/path_alias_repro/lib/index.ts b/e2e/path_alias_repro/lib/index.ts new file mode 100644 index 0000000..1e29c45 --- /dev/null +++ b/e2e/path_alias_repro/lib/index.ts @@ -0,0 +1 @@ +export const hello = "world"; diff --git a/e2e/path_alias_repro/tsconfig.json b/e2e/path_alias_repro/tsconfig.json new file mode 100644 index 0000000..1c46d25 --- /dev/null +++ b/e2e/path_alias_repro/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "moduleResolution": "node", + "baseUrl": ".", + "declaration": true, + "paths": { + "esbuild_repro/*": [ + "./*" + ] + } + } +} \ No newline at end of file diff --git a/esbuild/private/plugins/bazel-sandbox.js b/esbuild/private/plugins/bazel-sandbox.js index 5477b18..7aa66f8 100644 --- a/esbuild/private/plugins/bazel-sandbox.js +++ b/esbuild/private/plugins/bazel-sandbox.js @@ -1,5 +1,6 @@ const path = require('path') const process = require('process') +const fs = require('fs') const bindir = process.env.BAZEL_BINDIR const execroot = process.env.JS_BINARY__EXECROOT @@ -39,6 +40,31 @@ async function resolveInExecroot(build, importPath, otherOptions) { return result } + // If the resolution points to a TypeScript file, check if a corresponding + // JavaScript file exists and use that instead. This handles cases where tsconfig paths + // resolve to the source .ts file, but we want to bundle the compiled .js file which + // is present in the sandbox (via dependencies). + if (result.path && !result.external) { + const ext = path.extname(result.path) + if (['.ts', '.tsx', '.mts', '.cts'].includes(ext)) { + const jsExts = { + '.ts': '.js', + '.tsx': '.js', + '.mts': '.mjs', + '.cts': '.cjs', + } + const jsPath = result.path.substring(0, result.path.length - ext.length) + jsExts[ext] + if (fs.existsSync(jsPath)) { + if (!!process.env.JS_BINARY__LOG_DEBUG) { + console.error( + `DEBUG: [bazel-sandbox] falling back from ${result.path} to ${jsPath}` + ) + } + result.path = jsPath + } + } + } + // External modules are intentionally outside the bundle and don't need path validation if (result.external) { if (!!process.env.JS_BINARY__LOG_DEBUG) {