diff --git a/docs/api.md b/docs/api.md index 9e7f0da..26c697b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -39,7 +39,7 @@ and application-specific output can still contain sensitive data. | `base_url` / `base_url_env` | Unset | Existing HTTP(S) URL or its environment-variable name | | `config` | Generated | Compiled ESM Playwright config | | `data` | `[]` | Additional runtime inputs | -| `runtime_only` | `True` | Exclude source/type/debug files from compiled inputs and prune maps from built directories and shell assets in the test target's repository; explicit `data` stays unfiltered. Set `False` to retain source/debug inputs. See [runtime-only inputs](e2e.md#runtime-only-inputs). | +| `runtime_only` | `True` | Exclude source/type/debug files from compiled inputs and prune maps from built directories and shell assets in the test target's repository; npm packages and explicit `data` stay unfiltered. Set `False` to retain source/debug inputs. See [runtime-only inputs](e2e.md#runtime-only-inputs). | | `env` / `env_inherit` | `{}` / `[]` | Explicit values / inherited names; inheritance host-only | | `network_origins` / `network_origins_env` | `[]` / `[]` | Extra allowed origins / env names containing them; host-only | | `args` | `[]` | Default selection flags; see below | diff --git a/docs/e2e.md b/docs/e2e.md index 2421ad9..f050929 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -217,8 +217,11 @@ remain. Directory artifacts owned by the test's repository are copied without aliases. This also applies to built `browser_shell` assets. Other files within directories are preserved. A map-only change may rerun the copy action without invalidating the browser result when the projected contents are identical. -Other repositories' files, browser/runtime packages, explicit `data`, and snapshot -inputs are not filtered. Explicit `data` wins when the same directory also appears +Other repositories' files, npm packages under `node_modules`, browser/runtime +packages, explicit `data`, and snapshot inputs are not filtered. npm package trees +retain their original artifact paths so Node can resolve imports from real +compiled-file paths without relying on previously materialized build outputs. +Explicit `data` wins when the same directory also appears in compiled inputs. Declare a source file or map in `data` if the test intentionally reads it. diff --git a/internal/runtime_inputs.bzl b/internal/runtime_inputs.bzl index 1c0dcd2..88170d4 100644 --- a/internal/runtime_inputs.bzl +++ b/internal/runtime_inputs.bzl @@ -3,14 +3,18 @@ load("@bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action") load("@bazel_lib//lib:paths.bzl", "to_repository_relative_path") +def _is_package_file(ctx, file): + # Node resolves imports from real compiled-file paths, so npm trees must stay put. + return file.owner.workspace_name != ctx.label.workspace_name or "node_modules" in file.short_path.split("/") + def _is_runtime_file(ctx, file): # Other repositories may publish sources as runtime package contents. - if file.is_directory or file.owner.workspace_name != ctx.label.workspace_name: + if file.is_directory or _is_package_file(ctx, file): return True return not file.basename.endswith((".ts", ".tsx", ".mts", ".cts", ".map", ".tsbuildinfo")) def _runtime_directory(ctx, file, directories): - if not file.is_directory or file.owner.workspace_name != ctx.label.workspace_name: + if not file.is_directory or _is_package_file(ctx, file): return file if file not in directories: output = ctx.actions.declare_directory(ctx.label.name + ".runtime/" + str(len(directories))) diff --git a/tests/runtime_inputs_test.bzl b/tests/runtime_inputs_test.bzl index 1717983..de01d29 100644 --- a/tests/runtime_inputs_test.bzl +++ b/tests/runtime_inputs_test.bzl @@ -37,7 +37,7 @@ printf 'debug metadata' > "$1/nested/asset.js.map" ctx.label.name + "/root-source-alias": files["source.ts"], ctx.label.name + "/root-directory-alias": tree, }, - ).merge(ctx.attr._playwright[DefaultInfo].default_runfiles), + ).merge(ctx.attr._playwright[DefaultInfo].default_runfiles).merge(ctx.attr._npm[DefaultInfo].default_runfiles), data_runfiles = ctx.runfiles(files = [files["data-only.json"]]), ), OutputGroupInfo(transitive_typecheck = depset([files["semantic-check.txt"]]), assets = depset([tree])), @@ -49,6 +49,7 @@ _fixture = rule( "module": attr.string(), "external": attr.label_list(allow_files = True), "_playwright": attr.label(default = "//runtime:playwright"), + "_npm": attr.label(default = "//:node_modules/typescript"), }, outputs = {"source": "%{name}/source.ts"}, ) @@ -82,6 +83,10 @@ def _runtime_inputs_test_impl(ctx): if file.is_directory: asserts.true(env, file in files, "Runtime package must remain unfiltered: " + file.short_path) asserts.equals(env, file, aliases.get(file.short_path, file)) + for file in ctx.attr._npm[DefaultInfo].default_runfiles.files.to_list(): + if file.is_directory: + asserts.true(env, file in files, "Node resolves compiled modules against original npm package trees: " + file.short_path) + asserts.equals(env, file, aliases.get(file.short_path, file)) return analysistest.end(env) _runtime_inputs_test = analysistest.make( @@ -91,6 +96,7 @@ _runtime_inputs_test = analysistest.make( "explicit_source": attr.bool(), "external": attr.label_list(allow_files = True), "_playwright": attr.label(default = "//runtime:playwright"), + "_npm": attr.label(default = "//:node_modules/typescript"), }, )