diff --git a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= index 5c041728c..5287958c2 100755 --- a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= +++ b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= @@ -18,6 +18,7 @@ examples/npm_package/packages/pkg_b/package.json=1041247977 examples/npm_package/packages/pkg_d/package.json=1110895851 examples/npm_package/packages/pkg_e/package.json=-2145239245 examples/runfiles/package.json=-1545884645 +examples/stack_traces/package.json=2011229626 examples/webpack_cli/package.json=1911342006 js/private/coverage/bundle/package.json=-24783848 js/private/image/package.json=-789619063 @@ -31,5 +32,5 @@ npm/private/test/vendored/is-odd/package.json=1041695223 npm/private/test/vendored/lodash-4.17.21.tgz=-1206623349 npm/private/test/vendored/semver-max/package.json=578664053 package.json=1510979981 -pnpm-lock.yaml=1372779172 +pnpm-lock.yaml=-101429861 pnpm-workspace.yaml=-1123429050 diff --git a/.bazelignore b/.bazelignore index 4336544ed..1acf2063f 100644 --- a/.bazelignore +++ b/.bazelignore @@ -15,6 +15,7 @@ examples/npm_package/packages/pkg_c/node_modules/ examples/npm_package/packages/pkg_d/node_modules/ examples/npm_package/packages/pkg_e/node_modules/ examples/runfiles/node_modules +examples/stack_traces/node_modules examples/webpack_cli/node_modules/ js/private/coverage/bundle/node_modules js/private/image/node_modules diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 735efd098..6e43cdba5 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -138,6 +138,15 @@ eslint_bin.eslint_test( > NB: We plan to add support for the `.npmrc` `public-hoist-pattern` setting to `rules_js` in a future release. > For now, you must emulate public-hoist-pattern in `rules_js` using the `public_hoist_packages` attribute shown above. +## Ugly stack traces + +Bazel's sandboxing and runfiles directory layouts can make stack traces and logs hard to read. This issue is common in many +languages when used within bazel, not only JavaScript. + +One solution involving `Error.prepareStackTrace` was [suggested on bazelbuild slack](https://bazelbuild.slack.com/archives/CA31HN1T3/p1733518986229749?thread_ts=1733516180.969159&cid=CA31HN1T3) by [John Firebaugh](https://github.com/jfirebaugh). This overrides `Error.prepareStackTrace` to strip the bazel sandbox and runfiles paths from error stack traces. This also uses [`source-map-support`](https://www.npmjs.com/package/source-map-support) to also apply source maps to the stack traces. + +See [examples/stack_traces](../examples/stack_traces) for a working example. + ## Performance For general bazel performance tips see the [Aspect bazelrc guide](https://docs.aspect.build/guides/bazelrc/#performance-options). diff --git a/examples/stack_traces/BUILD.bazel b/examples/stack_traces/BUILD.bazel new file mode 100644 index 000000000..c27efe379 --- /dev/null +++ b/examples/stack_traces/BUILD.bazel @@ -0,0 +1,33 @@ +load("@aspect_rules_js//js:defs.bzl", "js_library", "js_test") +load("@npm//:defs.bzl", "npm_link_all_packages") + +npm_link_all_packages(name = "node_modules") + +js_test( + name = "stack_traces", + data = [ + ":library", + + # Include the stack-traces library - this could be wrapped in a `js_test` macro + ":stack-traces", + ], + entry_point = "main.js", + node_options = [ + # Load the stack-traces library at node startup time - this could be wrapped in a `js_test` macro + "--require", + "$$RUNFILES/_main/examples/stack_traces/stacks.cjs", + ], +) + +js_library( + name = "library", + srcs = glob(["lib/**/*.js"]), +) + +# Put the stacks.cjs in a `js_library` that can also declare any dependencies that +# the --require script may have such as the source-map-support package. +js_library( + name = "stack-traces", + srcs = ["stacks.cjs"], + deps = [":node_modules/source-map-support"], +) diff --git a/examples/stack_traces/lib/a.js b/examples/stack_traces/lib/a.js new file mode 100644 index 000000000..fb7114338 --- /dev/null +++ b/examples/stack_traces/lib/a.js @@ -0,0 +1,7 @@ +module.exports.a = function a() { + return b() +} + +function b() { + throw new Error('the error') +} diff --git a/examples/stack_traces/main.js b/examples/stack_traces/main.js new file mode 100644 index 000000000..417b448bf --- /dev/null +++ b/examples/stack_traces/main.js @@ -0,0 +1,12 @@ +const { a } = require('./lib/a') + +try { + a() + throw new Error('a() should throw for this test') +} catch (e) { + if (e.stack.indexOf('.runfiles/') !== -1) { + throw new Error( + 'stacks.cjs should have stripped runfiles directories from the stack trace' + ) + } +} diff --git a/examples/stack_traces/package.json b/examples/stack_traces/package.json new file mode 100644 index 000000000..65fe30e65 --- /dev/null +++ b/examples/stack_traces/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "source-map-support": "^0.5.21" + } +} diff --git a/examples/stack_traces/stacks.cjs b/examples/stack_traces/stacks.cjs new file mode 100644 index 000000000..df934dc47 --- /dev/null +++ b/examples/stack_traces/stacks.cjs @@ -0,0 +1,41 @@ +// Register source-map-support for source maps to be applied on stack traces. +require('source-map-support/register') + +let basePath = process.env.RUNFILES + ? `${process.env.RUNFILES}/${process.env.JS_BINARY__WORKSPACE}` + : process.cwd() + +if (!basePath.endsWith('/')) { + basePath = basePath + '/' +} + +/* +Before: + Error: test + at foo (/private/var/tmp/_bazel_username/67beefda950d56283b98d96980e6e332/execroot/aspect_rules_js/bazel-out/darwin_arm64-fastbuild/bin/examples/stack_traces/stack_trace_support.sh.runfiles/aspect_rules_js/examples/stack_traces/b.js:2:11) + at Object. (/private/var/tmp/_bazel_username/67beefda950d56283b98d96980e6e332/execroot/aspect_rules_js/bazel-out/darwin_arm64-fastbuild/bin/examples/stack_traces/stack_trace_support.sh.runfiles/aspect_rules_js/examples/stack_traces/a.js:4:1) + ... + +After: + Error: test + at foo (examples/stack_traces/b.ts:2:9) + at Object. (examples/stack_traces/a.ts:5:1) + ... +*/ + +const basePathRegex = new RegExp( + `(at | \\()${basePath + .replace(/\\/g, '/') + // Escape regex meta-characters. + .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + .replace(/-/g, '\\x2d')}`, + 'g' +) + +const prepareStackTrace = Error.prepareStackTrace +Error.prepareStackTrace = function (error, stack) { + return prepareStackTrace(error, stack) + .split('\n') + .map((line) => line.replace(basePathRegex, '$1')) + .join('\n') +} diff --git a/npm/private/test/snapshots/bzlmod/npm_defs.bzl b/npm/private/test/snapshots/bzlmod/npm_defs.bzl index 27541e506..77a49b330 100644 --- a/npm/private/test/snapshots/bzlmod/npm_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/npm_defs.bzl @@ -876,7 +876,7 @@ load("@@_main~npm~npm__smob__1.5.0__links//:defs.bzl", store_872 = "npm_imported load("@@_main~npm~npm__socks-proxy-agent__7.0.0__links//:defs.bzl", store_873 = "npm_imported_package_store") load("@@_main~npm~npm__socks__2.7.1__links//:defs.bzl", store_874 = "npm_imported_package_store") load("@@_main~npm~npm__source-map-js__1.0.2__links//:defs.bzl", store_875 = "npm_imported_package_store") -load("@@_main~npm~npm__source-map-support__0.5.21__links//:defs.bzl", store_876 = "npm_imported_package_store") +load("@@_main~npm~npm__source-map-support__0.5.21__links//:defs.bzl", link_876 = "npm_link_imported_package_store", store_876 = "npm_imported_package_store") load("@@_main~npm~npm__source-map__0.6.1__links//:defs.bzl", store_877 = "npm_imported_package_store") load("@@_main~npm~npm__sourcemap-codec__1.4.8__links//:defs.bzl", store_878 = "npm_imported_package_store") load("@@_main~npm~npm__sshpk__1.17.0__links//:defs.bzl", store_879 = "npm_imported_package_store") @@ -1026,7 +1026,7 @@ load("@aspect_rules_js//npm/private:npm_link_package_store.bzl", _npm_link_packa # buildifier: disable=bzl-visibility load("@aspect_rules_js//npm/private:npm_package_store.bzl", _npm_package_store = "npm_package_store") -_LINK_PACKAGES = ["", "examples/js_binary", "examples/js_lib_pkg/a", "examples/js_lib_pkg/b", "examples/linked_consumer", "examples/linked_empty_node_modules", "examples/linked_lib", "examples/linked_pkg", "examples/macro", "examples/npm_deps", "examples/npm_package/libs/lib_a", "examples/npm_package/packages/pkg_a", "examples/npm_package/packages/pkg_b", "examples/npm_package/packages/pkg_d", "examples/npm_package/packages/pkg_e", "examples/runfiles", "examples/webpack_cli", "js/private/coverage/bundle", "js/private/image", "js/private/test/image", "js/private/test/js_run_devserver", "js/private/worker/src", "npm/private/test", "npm/private/test/npm_package", "npm/private/test/npm_package_publish"] +_LINK_PACKAGES = ["", "examples/js_binary", "examples/js_lib_pkg/a", "examples/js_lib_pkg/b", "examples/linked_consumer", "examples/linked_empty_node_modules", "examples/linked_lib", "examples/linked_pkg", "examples/macro", "examples/npm_deps", "examples/npm_package/libs/lib_a", "examples/npm_package/packages/pkg_a", "examples/npm_package/packages/pkg_b", "examples/npm_package/packages/pkg_d", "examples/npm_package/packages/pkg_e", "examples/runfiles", "examples/stack_traces", "examples/webpack_cli", "js/private/coverage/bundle", "js/private/image", "js/private/test/image", "js/private/test/js_run_devserver", "js/private/worker/src", "npm/private/test", "npm/private/test/npm_package", "npm/private/test/npm_package_publish"] # buildifier: disable=function-docstring def npm_link_all_packages(name = "node_modules", imported_links = []): @@ -2439,6 +2439,9 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): link_targets.append(":{}/mocha-multi-reporters".format(name)) link_682(name = "{}/mocha".format(name)) link_targets.append(":{}/mocha".format(name)) + elif bazel_package == "examples/stack_traces": + link_876(name = "{}/source-map-support".format(name)) + link_targets.append(":{}/source-map-support".format(name)) if is_root: _npm_package_store( @@ -2859,6 +2862,8 @@ def npm_link_targets(name = "node_modules", package = None): link_targets.append(":{}/mocha-junit-reporter".format(name)) link_targets.append(":{}/mocha-multi-reporters".format(name)) link_targets.append(":{}/mocha".format(name)) + elif bazel_package == "examples/stack_traces": + link_targets.append(":{}/source-map-support".format(name)) if bazel_package in ["examples/js_binary", "examples/npm_deps", "js/private/test/image"]: link_targets.append(":{}/@mycorp/pkg-a".format(name)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 736ed6258..07108d13d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -206,6 +206,12 @@ importers: specifier: ^6.3.0 version: 6.3.0 + examples/stack_traces: + devDependencies: + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 + examples/webpack_cli: dependencies: '@vanilla-extract/css':