├── .aspect ├── bazelrc │ ├── .gitignore │ ├── BUILD.bazel │ ├── bazel7.bazelrc │ ├── ci.bazelrc │ ├── convenience.bazelrc │ ├── correctness.bazelrc │ ├── debug.bazelrc │ ├── javascript.bazelrc │ └── performance.bazelrc ├── cli │ └── config.yaml └── workflows │ └── config.yaml ├── .bazelignore ├── .bazeliskrc ├── .bazelrc ├── .bazelversion ├── .bcr ├── metadata.template.json ├── presubmit.yml └── source.template.json ├── .gitattributes ├── .github └── workflows │ ├── BUILD.bazel │ ├── bazel6.bazelrc │ ├── bazel7.bazelrc │ ├── buildifier.yaml │ ├── ci.bazelrc │ ├── ci.yaml │ ├── mirror.yml │ ├── new_issue.yaml │ ├── publish.yaml │ ├── release.yml │ └── release_prep.sh ├── .gitignore ├── .npmrc ├── .pre-commit-config.yaml ├── .prettierignore ├── BUILD.bazel ├── CONTRIBUTING.md ├── LICENSE ├── MODULE.bazel ├── README.md ├── WORKSPACE ├── docs ├── .bazelversion ├── BUILD.bazel ├── MODULE.bazel ├── repositories.md ├── swc.md └── tsconfig.md ├── e2e └── smoke │ ├── .bazelignore │ ├── .bazelrc │ ├── .bazelversion │ ├── .npmrc │ ├── .swcrc │ ├── BUILD.bazel │ ├── MODULE.bazel │ ├── README.md │ ├── WORKSPACE.bazel │ ├── WORKSPACE.bzlmod │ ├── foo.ts │ ├── package.json │ └── pnpm-lock.yaml ├── examples ├── BUILD.bazel ├── allow_js │ ├── BUILD.bazel │ ├── expected.cjs │ ├── expected.js │ ├── expected.jsx │ ├── expected.mjs │ ├── expected.ts │ ├── in.cjs │ ├── in.d.ts │ ├── in.js │ ├── in.jsx │ ├── in.mjs │ └── in_ts.ts ├── custom_outs │ ├── BUILD.bazel │ ├── a.ts │ ├── b.mts │ ├── expected.cjs │ └── expected.js ├── directory │ ├── BUILD.bazel │ ├── mocks.bzl │ └── test.sh ├── emit_types │ ├── BUILD.bazel │ ├── expected.a.d.ts │ ├── expected.b.d.ts │ └── src │ │ ├── a.ts │ │ └── b.ts ├── filegroup │ ├── .swcrc │ ├── BUILD.bazel │ ├── a.ts │ ├── b.ts │ ├── check_outputs.sh │ └── sub │ │ └── c.ts ├── generate_swcrc │ ├── BUILD.bazel │ ├── package.json │ ├── some.ts │ └── tsconfig.json ├── genrule │ ├── .swcrc │ ├── BUILD.bazel │ └── check_outputs.sh ├── macro │ ├── BUILD.bazel │ ├── expected.js │ ├── in.ts │ └── ts_project.bzl ├── opaque_src │ ├── BUILD.bazel │ ├── expected.js │ └── in.ts ├── out_dir │ ├── BUILD.bazel │ ├── a.ts │ ├── b.ts │ ├── expected │ │ ├── a.js │ │ ├── a.js.map.golden │ │ ├── b.js │ │ ├── b.js.map.golden │ │ └── sub │ │ │ ├── c.js │ │ │ └── c.js.map.golden │ └── sub │ │ └── c.ts ├── package.json ├── paths │ ├── .swcrc │ ├── BUILD.bazel │ ├── expected.js │ ├── expected_tsc │ │ ├── index.js │ │ └── modules │ │ │ ├── moduleA │ │ │ └── index.js │ │ │ └── moduleB │ │ │ └── index.js │ ├── src │ │ ├── index.ts │ │ └── modules │ │ │ ├── moduleA │ │ │ └── index.ts │ │ │ └── moduleB │ │ │ └── index.ts │ └── tsconfig.json ├── plugins │ ├── BUILD.bazel │ ├── expected_rc.js_ │ ├── expected_rcdict.js_ │ ├── expected_simple.js_ │ ├── in.ts │ ├── minify.swcrc │ └── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── rc │ ├── .swcrc │ ├── BUILD.bazel │ └── src │ │ ├── BUILD.bazel │ │ ├── expected.js │ │ ├── expected.js.map │ │ └── in.ts ├── rcdict │ ├── BUILD.bazel │ ├── expected_es2015.js │ ├── expected_es5.js │ └── in.ts ├── resolve_json_module │ ├── BUILD.bazel │ ├── data.json │ └── index.ts ├── root_dir │ ├── BUILD.bazel │ ├── expected │ │ ├── a.js │ │ ├── a.js.map.golden │ │ ├── b.js │ │ ├── b.js.map.golden │ │ └── sub │ │ │ ├── c.js │ │ │ └── c.js.map.golden │ ├── expected_dot │ │ └── src │ │ │ ├── a.js │ │ │ ├── a.js.map.golden │ │ │ ├── b.js │ │ │ ├── b.js.map.golden │ │ │ └── sub │ │ │ ├── c.js │ │ │ └── c.js.map.golden │ ├── expected_nested │ │ ├── c.js │ │ └── c.js.map.golden │ └── src │ │ ├── a.ts │ │ ├── b.ts │ │ └── sub │ │ └── c.ts ├── simple │ ├── BUILD.bazel │ ├── expected.js │ └── in.ts ├── source_map_support │ ├── BUILD.bazel │ ├── defs.bzl │ ├── stack-trace-support.js │ └── test │ │ ├── BUILD.bazel │ │ ├── a.ts │ │ └── b.ts ├── source_root │ ├── BUILD.bazel │ ├── expected.js │ ├── expected.js.map.golden │ ├── expected_subdir.js │ ├── expected_subdir.js.map.golden │ ├── in.ts │ └── src │ │ └── subdir.ts └── transitive │ ├── .swcrc │ ├── BUILD.bazel │ ├── app │ ├── BUILD.bazel │ └── a.ts │ ├── expected │ ├── lib_b │ ├── BUILD.bazel │ └── b.ts │ └── lib_c │ ├── BUILD.bazel │ └── c.ts ├── renovate.json ├── scripts ├── filter.jq └── mirror_releases.sh └── swc ├── BUILD.bazel ├── defs.bzl ├── dependencies.bzl ├── extensions.bzl ├── private ├── BUILD.bazel ├── resolved_toolchain.bzl ├── swc.bzl ├── swc_plugin.bzl ├── toolchains_repo.bzl └── versions.bzl ├── providers.bzl ├── repositories.bzl └── toolchain.bzl /.aspect/bazelrc/.gitignore: -------------------------------------------------------------------------------- 1 | user.bazelrc -------------------------------------------------------------------------------- /.aspect/bazelrc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | "Aspect bazelrc presets; see https://docs.aspect.build/guides/bazelrc" 2 | 3 | load("@aspect_bazel_lib//lib:bazelrc_presets.bzl", "write_aspect_bazelrc_presets") 4 | 5 | write_aspect_bazelrc_presets( 6 | name = "update_aspect_bazelrc_presets", 7 | presets = [ 8 | "ci", 9 | "convenience", 10 | "correctness", 11 | "debug", 12 | "javascript", 13 | "performance", 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /.aspect/bazelrc/bazel7.bazelrc: -------------------------------------------------------------------------------- 1 | ../../.github/workflows/bazel7.bazelrc -------------------------------------------------------------------------------- /.aspect/bazelrc/ci.bazelrc: -------------------------------------------------------------------------------- 1 | # Set this flag to enable re-tries of failed tests on CI. 2 | # When any test target fails, try one or more times. This applies regardless of whether the "flaky" 3 | # tag appears on the target definition. 4 | # This is a tradeoff: legitimately failing tests will take longer to report, 5 | # but we can paper over flaky tests that pass most of the time. 6 | # The alternative is to mark every flaky test with the `flaky = True` attribute, but this requires 7 | # the buildcop to make frequent code edits. 8 | # Not recommended for local builds so that the flakiness is observed during development and thus 9 | # is more likely to get fixed. 10 | # Note that when passing after the first attempt, Bazel will give a special "FLAKY" status. 11 | # Docs: https://bazel.build/docs/user-manual#flaky-test-attempts 12 | test --flaky_test_attempts=2 13 | 14 | # Announce all announces command options read from the bazelrc file(s) when starting up at the 15 | # beginning of each Bazel invocation. This is very useful on CI to be able to inspect what Bazel rc 16 | # settings are being applied on each run. 17 | # Docs: https://bazel.build/docs/user-manual#announce-rc 18 | build --announce_rc 19 | 20 | # Add a timestamp to each message generated by Bazel specifying the time at which the message was 21 | # displayed. 22 | # Docs: https://bazel.build/docs/user-manual#show-timestamps 23 | build --show_timestamps 24 | 25 | # Only show progress every 60 seconds on CI. 26 | # We want to find a compromise between printing often enough to show that the build isn't stuck, 27 | # but not so often that we produce a long log file that requires a lot of scrolling. 28 | # https://bazel.build/reference/command-line-reference#flag--show_progress_rate_limit 29 | build --show_progress_rate_limit=60 30 | 31 | # Use cursor controls in screen output. 32 | # Docs: https://bazel.build/docs/user-manual#curses 33 | build --curses=yes 34 | 35 | # Use colors to highlight output on the screen. Set to `no` if your CI does not display colors. 36 | # Docs: https://bazel.build/docs/user-manual#color 37 | build --color=yes 38 | 39 | # The terminal width in columns. Configure this to override the default value based on what your CI system renders. 40 | # Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/runtime/UiOptions.java#L151 41 | build --terminal_columns=143 42 | 43 | ###################################### 44 | # Generic remote cache configuration # 45 | ###################################### 46 | 47 | # Only download remote outputs of top level targets to the local machine. 48 | # Docs: https://bazel.build/reference/command-line-reference#flag--remote_download_toplevel 49 | build --remote_download_toplevel 50 | 51 | # The maximum amount of time to wait for remote execution and cache calls. 52 | # https://bazel.build/reference/command-line-reference#flag--remote_timeout 53 | build --remote_timeout=3600 54 | 55 | # Upload locally executed action results to the remote cache. 56 | # Docs: https://bazel.build/reference/command-line-reference#flag--remote_upload_local_results 57 | build --remote_upload_local_results 58 | 59 | # Fall back to standalone local execution strategy if remote execution fails. If the grpc remote 60 | # cache connection fails, it will fail the build, add this so it falls back to the local cache. 61 | # Docs: https://bazel.build/reference/command-line-reference#flag--remote_local_fallback 62 | build --remote_local_fallback 63 | 64 | # Fixes builds hanging on CI that get the TCP connection closed without sending RST packets. 65 | # Docs: https://bazel.build/reference/command-line-reference#flag--grpc_keepalive_time 66 | build --grpc_keepalive_time=30s 67 | -------------------------------------------------------------------------------- /.aspect/bazelrc/convenience.bazelrc: -------------------------------------------------------------------------------- 1 | # Attempt to build & test every target whose prerequisites were successfully built. 2 | # Docs: https://bazel.build/docs/user-manual#keep-going 3 | build --keep_going 4 | 5 | # Output test errors to stderr so users don't have to `cat` or open test failure log files when test 6 | # fail. This makes the log noisier in exchange for reducing the time-to-feedback on test failures for 7 | # users. 8 | # Docs: https://bazel.build/docs/user-manual#test-output 9 | test --test_output=errors 10 | 11 | # Show the output files created by builds that requested more than one target. This helps users 12 | # locate the build outputs in more cases 13 | # Docs: https://bazel.build/docs/user-manual#show-result 14 | build --show_result=20 15 | 16 | # Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is 17 | # Linux and you run bazel build, Bazel picks up lines starting with build:linux. Supported OS 18 | # identifiers are `linux`, `macos`, `windows`, `freebsd`, and `openbsd`. Enabling this flag is 19 | # equivalent to using `--config=linux` on Linux, `--config=windows` on Windows, etc. 20 | # Docs: https://bazel.build/reference/command-line-reference#flag--enable_platform_specific_config 21 | common --enable_platform_specific_config 22 | 23 | # Output a heap dump if an OOM is thrown during a Bazel invocation 24 | # (including OOMs due to `--experimental_oom_more_eagerly_threshold`). 25 | # The dump will be written to `/.heapdump.hprof`. 26 | # You may need to configure CI to capture this artifact and upload for later use. 27 | # Docs: https://bazel.build/reference/command-line-reference#flag--heap_dump_on_oom 28 | common --heap_dump_on_oom 29 | -------------------------------------------------------------------------------- /.aspect/bazelrc/correctness.bazelrc: -------------------------------------------------------------------------------- 1 | # Do not upload locally executed action results to the remote cache. 2 | # This should be the default for local builds so local builds cannot poison the remote cache. 3 | # It should be flipped to `--remote_upload_local_results` on CI 4 | # by using `--bazelrc=.aspect/bazelrc/ci.bazelrc`. 5 | # Docs: https://bazel.build/reference/command-line-reference#flag--remote_upload_local_results 6 | build --noremote_upload_local_results 7 | 8 | # Don't allow network access for build actions in the sandbox. 9 | # Ensures that you don't accidentally make non-hermetic actions/tests which depend on remote 10 | # services. 11 | # Developers should tag targets with `tags=["requires-network"]` to opt-out of the enforcement. 12 | # Docs: https://bazel.build/reference/command-line-reference#flag--sandbox_default_allow_network 13 | build --sandbox_default_allow_network=false 14 | 15 | # Warn if a test's timeout is significantly longer than the test's actual execution time. 16 | # Bazel's default for test_timeout is medium (5 min), but most tests should instead be short (1 min). 17 | # While a test's timeout should be set such that it is not flaky, a test that has a highly 18 | # over-generous timeout can hide real problems that crop up unexpectedly. 19 | # For instance, a test that normally executes in a minute or two should not have a timeout of 20 | # ETERNAL or LONG as these are much, much too generous. 21 | # Docs: https://bazel.build/docs/user-manual#test-verbose-timeout-warnings 22 | test --test_verbose_timeout_warnings 23 | 24 | # Allow the Bazel server to check directory sources for changes. Ensures that the Bazel server 25 | # notices when a directory changes, if you have a directory listed in the srcs of some target. 26 | # Recommended when using 27 | # [copy_directory](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/copy_directory.md) and 28 | # [rules_js](https://github.com/aspect-build/rules_js) since npm package are source directories 29 | # inputs to copy_directory actions. 30 | # Docs: https://bazel.build/reference/command-line-reference#flag--host_jvm_args 31 | startup --host_jvm_args=-DBAZEL_TRACK_SOURCE_DIRECTORIES=1 32 | 33 | # Allow exclusive tests to run in the sandbox. Fixes a bug where Bazel doesn't enable sandboxing for 34 | # tests with `tags=["exclusive"]`. 35 | # Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_exclusive_test_sandboxed 36 | test --incompatible_exclusive_test_sandboxed 37 | 38 | # Use a static value for `PATH` and does not inherit `LD_LIBRARY_PATH`. Doesn't let environment 39 | # variables like `PATH` sneak into the build, which can cause massive cache misses when they change. 40 | # Use `--action_env=ENV_VARIABLE` if you want to inherit specific environment variables from the 41 | # client, but note that doing so can prevent cross-user caching if a shared cache is used. 42 | # Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_strict_action_env 43 | build --incompatible_strict_action_env 44 | 45 | # Do not automatically create `__init__.py` files in the runfiles of Python targets. Fixes the wrong 46 | # default that comes from Google's internal monorepo by using `__init__.py` to delimit a Python 47 | # package. Precisely, when a `py_binary` or `py_test` target has `legacy_create_init` set to `auto (the 48 | # default), it is treated as false if and only if this flag is set. See 49 | # https://github.com/bazelbuild/bazel/issues/10076. 50 | # Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_default_to_explicit_init_py 51 | build --incompatible_default_to_explicit_init_py 52 | 53 | # Set default value of `allow_empty` to `False` in `glob()`. This prevents a common mistake when 54 | # attempting to use `glob()` to match files in a subdirectory that is opaque to the current package 55 | # because it contains a BUILD file. See https://github.com/bazelbuild/bazel/issues/8195. 56 | # Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_disallow_empty_glob 57 | common --incompatible_disallow_empty_glob 58 | 59 | # Always download coverage files for tests from the remote cache. By default, coverage files are not 60 | # downloaded on test result cache hits when --remote_download_minimal is enabled, making it impossible 61 | # to generate a full coverage report. 62 | # Docs: https://bazel.build/reference/command-line-reference#flag--experimental_fetch_all_coverage_outputs 63 | # detching remote cache results 64 | test --experimental_fetch_all_coverage_outputs 65 | -------------------------------------------------------------------------------- /.aspect/bazelrc/debug.bazelrc: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Use `bazel test --config=debug` to enable these settings # 3 | ############################################################ 4 | 5 | # Stream stdout/stderr output from each test in real-time. 6 | # Docs: https://bazel.build/docs/user-manual#test-output 7 | test:debug --test_output=streamed 8 | 9 | # Run one test at a time. 10 | # Docs: https://bazel.build/reference/command-line-reference#flag--test_strategy 11 | test:debug --test_strategy=exclusive 12 | 13 | # Prevent long running tests from timing out. 14 | # Docs: https://bazel.build/docs/user-manual#test-timeout 15 | test:debug --test_timeout=9999 16 | 17 | # Always run tests even if they have cached results. 18 | # Docs: https://bazel.build/docs/user-manual#cache-test-results 19 | test:debug --nocache_test_results 20 | -------------------------------------------------------------------------------- /.aspect/bazelrc/javascript.bazelrc: -------------------------------------------------------------------------------- 1 | # Aspect recommended Bazel flags when using Aspect's JavaScript rules: https://github.com/aspect-build/rules_js 2 | # Docs for Node.js flags: https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options 3 | 4 | # Support for debugging Node.js tests. Use bazel run with `--config=debug` to turn on the NodeJS 5 | # inspector agent. The node process will break before user code starts and wait for the debugger to 6 | # connect. Pass the --inspect-brk option to all tests which enables the node inspector agent. See 7 | # https://nodejs.org/de/docs/guides/debugging-getting-started/#command-line-options for more 8 | # details. 9 | # Docs: https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options 10 | run:debug -- --node_options=--inspect-brk 11 | test:debug --test_env=NODE_OPTIONS=--inspect-brk 12 | -------------------------------------------------------------------------------- /.aspect/bazelrc/performance.bazelrc: -------------------------------------------------------------------------------- 1 | # Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs. 2 | # Save time on Sandbox creation and deletion when many of the same kind of action run during the 3 | # build. 4 | # No longer experimental in Bazel 6: https://github.com/bazelbuild/bazel/commit/c1a95501a5611878e5cc43a3cc531f2b9e47835b 5 | # Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories 6 | build --experimental_reuse_sandbox_directories 7 | 8 | # Avoid creating a runfiles tree for binaries or tests until it is needed. 9 | # Docs: https://bazel.build/reference/command-line-reference#flag--build_runfile_links 10 | # See https://github.com/bazelbuild/bazel/issues/6627 11 | # 12 | # This may break local workflows that `build` a binary target, then run the resulting program 13 | # outside of `bazel run`. In those cases, the script will need to call 14 | # `bazel build --build_runfile_links //my/binary:target` and then execute the resulting program. 15 | build --nobuild_runfile_links 16 | 17 | # Needed prior to Bazel 8; see 18 | # https://github.com/bazelbuild/bazel/issues/20577 19 | coverage --build_runfile_links 20 | -------------------------------------------------------------------------------- /.aspect/cli/config.yaml: -------------------------------------------------------------------------------- 1 | configure: 2 | languages: 3 | bzl: true -------------------------------------------------------------------------------- /.aspect/workflows/config.yaml: -------------------------------------------------------------------------------- 1 | # See https://docs.aspect.build/workflows/configuration 2 | tasks: 3 | - checkout: 4 | update_strategy: rebase 5 | - buildifier: 6 | queue: aspect-medium 7 | - configure: 8 | - test: 9 | - finalization: 10 | queue: aspect-small 11 | workspaces: 12 | - . 13 | - e2e/smoke: 14 | tasks: 15 | - buildifier: 16 | without: true 17 | - docs: 18 | tasks: 19 | - buildifier: 20 | without: true 21 | - configure: 22 | without: true 23 | notifications: 24 | github: {} 25 | -------------------------------------------------------------------------------- /.bazelignore: -------------------------------------------------------------------------------- 1 | # Nested modules 2 | docs/ 3 | e2e/ 4 | examples/node_modules 5 | examples/generate_swcrc/node_modules 6 | examples/plugins/node_modules 7 | -------------------------------------------------------------------------------- /.bazeliskrc: -------------------------------------------------------------------------------- 1 | BAZELISK_BASE_URL=https://static.aspect.build/aspect 2 | USE_BAZEL_VERSION=aspect/2025.19.5 -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | # Import Aspect bazelrc presets 2 | try-import %workspace%/.aspect/bazelrc/bazel7.bazelrc 3 | import %workspace%/.aspect/bazelrc/convenience.bazelrc 4 | import %workspace%/.aspect/bazelrc/correctness.bazelrc 5 | import %workspace%/.aspect/bazelrc/debug.bazelrc 6 | import %workspace%/.aspect/bazelrc/javascript.bazelrc 7 | import %workspace%/.aspect/bazelrc/performance.bazelrc 8 | 9 | ### YOUR PROJECT SPECIFIC OPTIONS GO HERE ### 10 | 11 | # Don't build protoc from the cc_binary, it's slow and spammy when cache miss 12 | common --per_file_copt=external/.*protobuf.*@--PROTOBUF_WAS_NOT_SUPPOSED_TO_BE_BUILT 13 | common --host_per_file_copt=external/.*protobuf.*@--PROTOBUF_WAS_NOT_SUPPOSED_TO_BE_BUILT 14 | 15 | # Don’t want to push a rules author to update their deps if not needed. 16 | # https://bazel.build/reference/command-line-reference#flag--check_direct_dependencies 17 | # https://bazelbuild.slack.com/archives/C014RARENH0/p1691158021917459?thread_ts=1691156601.420349&cid=C014RARENH0 18 | common --check_direct_dependencies=off 19 | 20 | # Load any settings & overrides specific to the current user from `.aspect/bazelrc/user.bazelrc`. 21 | # This file should appear in `.gitignore` so that settings are not shared with team members. This 22 | # should be last statement in this config so the user configuration is able to overwrite flags from 23 | # this file. See https://bazel.build/configure/best-practices#bazelrc-file. 24 | try-import %workspace%/.aspect/bazelrc/user.bazelrc 25 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 7.1.1 2 | -------------------------------------------------------------------------------- /.bcr/metadata.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://docs.aspect.build/rules/aspect_rules_swc", 3 | "maintainers": [ 4 | { 5 | "email": "alex@aspect.build", 6 | "github": "alexeagle", 7 | "name": "Alex Eagle" 8 | }, 9 | { 10 | "email": "jason@aspect.build", 11 | "github": "jbedard", 12 | "name": "Jason Bedard" 13 | } 14 | ], 15 | "repository": ["github:aspect-build/rules_swc"], 16 | "versions": [], 17 | "yanked_versions": {} 18 | } 19 | -------------------------------------------------------------------------------- /.bcr/presubmit.yml: -------------------------------------------------------------------------------- 1 | bcr_test_module: 2 | module_path: "e2e/smoke" 3 | matrix: 4 | bazel: ["7.x", "6.x"] 5 | platform: ["debian10", "macos", "ubuntu2004", "windows"] 6 | tasks: 7 | run_tests: 8 | name: "Run test module" 9 | bazel: ${{ bazel }} 10 | platform: ${{ platform }} 11 | test_targets: 12 | - "//..." 13 | -------------------------------------------------------------------------------- /.bcr/source.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "integrity": "**leave this alone**", 3 | "strip_prefix": "{REPO}-{VERSION}", 4 | "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/rules_swc-{TAG}.tar.gz" 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/repositories.md linguist-generated=true 2 | docs/swc.md linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/workflows/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:bazelrc_presets.bzl", "write_aspect_bazelrc_presets") 2 | 3 | write_aspect_bazelrc_presets( 4 | name = "update_aspect_bazelrc_presets", 5 | presets = [ 6 | "bazel6", 7 | "bazel7", 8 | ], 9 | ) 10 | -------------------------------------------------------------------------------- /.github/workflows/bazel6.bazelrc: -------------------------------------------------------------------------------- 1 | # Speed up all builds by not checking if external repository files have been modified. 2 | # Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java#L244 3 | build --noexperimental_check_external_repository_files 4 | fetch --noexperimental_check_external_repository_files 5 | query --noexperimental_check_external_repository_files 6 | 7 | # Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs. 8 | # Save time on Sandbox creation and deletion when many of the same kind of action run during the 9 | # build. 10 | # Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories 11 | build --reuse_sandbox_directories 12 | 13 | # Avoid this flag being enabled by remote_download_minimal or remote_download_toplevel 14 | # See https://meroton.com/blog/bazel-6-errors-build-without-the-bytes/ 15 | build --noexperimental_action_cache_store_output_metadata 16 | 17 | # Speed up all builds by not checking if output files have been modified. Lets you make changes to 18 | # the output tree without triggering a build for local debugging. For example, you can modify 19 | # [rules_js](https://github.com/aspect-build/rules_js) 3rd party npm packages in the output tree 20 | # when local debugging. 21 | # Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/pkgcache/PackageOptions.java#L185 22 | # NB: This flag is in bazel6.bazelrc as when used in Bazel 7 is has been observed to break 23 | # "build without the bytes" --remote_download_outputs=toplevel. See https://github.com/bazel-contrib/bazel-lib/pull/711 24 | # for more info. 25 | build --noexperimental_check_output_files 26 | fetch --noexperimental_check_output_files 27 | query --noexperimental_check_output_files 28 | 29 | # Don't apply `--noremote_upload_local_results` and `--noremote_accept_cached` to the disk cache. 30 | # If you have both `--noremote_upload_local_results` and `--disk_cache`, then this fixes a bug where 31 | # Bazel doesn't write to the local disk cache as it treats as a remote cache. 32 | # Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_remote_results_ignore_disk 33 | # NB: This flag is in bazel6.bazelrc because it became a no-op in Bazel 7 and has been removed 34 | # in Bazel 8. 35 | build --incompatible_remote_results_ignore_disk 36 | 37 | # Propagate tags from a target declaration to the actions' execution requirements. 38 | # Ensures that tags applied in your BUILD file, like `tags=["no-remote"]` 39 | # get propagated to actions created by the rule. 40 | # Without this option, you rely on rules authors to manually check the tags you passed 41 | # and apply relevant ones to the actions they create. 42 | # See https://github.com/bazelbuild/bazel/issues/8830 for details. 43 | # Docs: https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation 44 | build --experimental_allow_tags_propagation 45 | fetch --experimental_allow_tags_propagation 46 | query --experimental_allow_tags_propagation 47 | 48 | # Do not build runfiles symlink forests for external repositories under 49 | # `.runfiles/wsname/external/repo` (in addition to `.runfiles/repo`). This reduces runfiles & 50 | # sandbox creation times & prevents accidentally depending on this feature which may flip to off by 51 | # default in the future. Note, some rules may fail under this flag, please file issues with the rule 52 | # author. 53 | # Docs: https://bazel.build/reference/command-line-reference#flag--legacy_external_runfiles 54 | build --nolegacy_external_runfiles 55 | -------------------------------------------------------------------------------- /.github/workflows/bazel7.bazelrc: -------------------------------------------------------------------------------- 1 | # Speed up all builds by not checking if external repository files have been modified. 2 | # Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java#L244 3 | common --noexperimental_check_external_repository_files 4 | 5 | # Don't report when the root module's lower bound for a dependency happens to be less than the resolved version. 6 | # This is expected and should NOT prompt an engineer to update our lower bound to match. 7 | # WARNING: For repository 'aspect_bazel_lib', the root module requires module version aspect_bazel_lib@1.30.2, 8 | # but got aspect_bazel_lib@1.31.2 in the resolved dependency graph. 9 | common --check_direct_dependencies=off 10 | 11 | # Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs. 12 | # Save time on Sandbox creation and deletion when many of the same kind of action run during the 13 | # build. 14 | # Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories 15 | build --reuse_sandbox_directories 16 | 17 | # Do not build runfiles symlink forests for external repositories under 18 | # `.runfiles/wsname/external/repo` (in addition to `.runfiles/repo`). This reduces runfiles & 19 | # sandbox creation times & prevents accidentally depending on this feature which may flip to off by 20 | # default in the future. Note, some rules may fail under this flag, please file issues with the rule 21 | # author. 22 | # Docs: https://bazel.build/reference/command-line-reference#flag--legacy_external_runfiles 23 | build --nolegacy_external_runfiles 24 | -------------------------------------------------------------------------------- /.github/workflows/buildifier.yaml: -------------------------------------------------------------------------------- 1 | name: Buildifier 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | check: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: buildifier 20 | run: bazel run --enable_bzlmod //:buildifier.check 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.bazelrc: -------------------------------------------------------------------------------- 1 | # Directories caches by GitHub actions 2 | common --disk_cache=~/.cache/bazel-disk-cache 3 | common --repository_cache=~/.cache/bazel-repository-cache 4 | 5 | # Debug where options came from 6 | common --announce_rc 7 | 8 | # Allows tests to run bazelisk-in-bazel, since this is the cache folder used 9 | common --test_env=XDG_CACHE_HOME 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | concurrency: 15 | # Cancel previous actions from the same PR or branch except 'main' branch. 16 | # See https://docs.github.com/en/actions/using-jobs/using-concurrency and https://docs.github.com/en/actions/learn-github-actions/contexts for more info. 17 | group: concurrency-group::${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}${{ github.ref_name == 'main' && format('::{0}', github.run_id) || ''}} 18 | cancel-in-progress: ${{ github.ref_name != 'main' }} 19 | 20 | jobs: 21 | # Prepares dynamic test matrix values 22 | matrix-prep: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - id: bazel-version 27 | name: Prepare 'bazel-version' matrix axis 28 | run: | 29 | v=$(head -n 1 .bazelversion) 30 | m=${v::1} 31 | a=( 32 | "major:$m, version:\"$v\"" 33 | "major:6, version:\"6.5.0\"" 34 | ) 35 | printf -v j '{%s},' "${a[@]}" 36 | echo "res=[${j%,}]" | tee -a $GITHUB_OUTPUT 37 | - id: os 38 | name: Prepare 'os' matrix axis 39 | # Only run MacOS and Windows on main branch (not PRs) to minimize minutes (billed at 10X and 2X respectively) 40 | # https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#included-storage-and-minutes 41 | run: | 42 | a=( ubuntu ) 43 | if [[ "${{ github.ref_name }}" == "main" ]] || [[ "${{ github.head_ref }}" == *"macos"* ]]; then 44 | a+=( macos ) 45 | fi 46 | if [[ "${{ github.ref_name }}" == "main" ]] || [[ "${{ github.head_ref }}" == *"windows"* ]]; then 47 | a+=( windows ) 48 | fi 49 | printf -v j '"%s",' "${a[@]}" 50 | echo "res=[${j%,}]" | tee -a $GITHUB_OUTPUT 51 | outputs: 52 | bazel-version: ${{ steps.bazel-version.outputs.res }} 53 | os: ${{ steps.os.outputs.res }} 54 | 55 | test: 56 | runs-on: ${{ matrix.os }}-latest 57 | needs: 58 | - matrix-prep 59 | defaults: 60 | run: 61 | working-directory: ${{ matrix.folder }} 62 | strategy: 63 | fail-fast: false 64 | matrix: 65 | bazel-version: ${{ fromJSON(needs.matrix-prep.outputs.bazel-version) }} 66 | bzlmod: [1, 0] 67 | os: ${{ fromJSON(needs.matrix-prep.outputs.os) }} 68 | folder: 69 | - "." 70 | - "e2e/smoke" 71 | exclude: 72 | # Don't test MacOS and Windows against secondary bazel version to minimize minutes (billed at 10X and 2X respectively) 73 | # https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#included-storage-and-minutes 74 | - os: macos 75 | bazel-version: 76 | major: 6 77 | - os: windows 78 | bazel-version: 79 | major: 6 80 | # Root folder is bzlmod-only 81 | - folder: . 82 | bzlmod: 0 83 | include: 84 | - bazel-version: 85 | major: 7 86 | version: 7.1.1 87 | bzlmod: 1 88 | os: ubuntu 89 | folder: docs 90 | 91 | steps: 92 | - uses: actions/checkout@v4 93 | 94 | - name: Mount bazel caches 95 | uses: actions/cache@v4 96 | with: 97 | path: | 98 | ~/.cache/bazel-disk-cache 99 | ~/.cache/bazel-repository-cache 100 | ~/.cache/xdg-cache 101 | key: bazel-cache-${{ matrix.bazel-version.version }}-${{ matrix.bzlmod }}-${{ matrix.os }}-${{ matrix.folder }}-${{ hashFiles('.bazelrc', '.bazelversion', '.bazeliskrc', '**/BUILD', '**/BUILD.bazel', '**/*.bzl', 'WORKSPACE', 'WORKSPACE.bazel', 'WORKSPACE.bzlmod', 'MODULE.bazel') }} 102 | restore-keys: bazel-cache-${{ matrix.bazel-version.version }}-${{ matrix.bzlmod }}-${{ matrix.os }}-${{ matrix.folder }}- 103 | 104 | - name: Configure Bazel version 105 | shell: bash 106 | run: | 107 | # Overwrite the .bazelversion instead of using USE_BAZEL_VERSION so that Bazelisk 108 | # still bootstraps Aspect CLI from configuration in .bazeliskrc. Aspect CLI will 109 | # then use .bazelversion to determine which Bazel version to use. 110 | echo "${{ matrix.bazel-version.version }}" > .bazelversion 111 | # Delete all the version specific bazelrc files that are used for local development 112 | # since the version we're testing against is dynamic. These are just symlinks and the 113 | # root .bazelrc brings these in with try-imports. In this CI workflows, we explicitly 114 | # bring in the version specific bazelrc file with --bazelrc when we invoke bazel. 115 | rm ${GITHUB_WORKSPACE//\\/\/}/.aspect/bazelrc/bazel*.bazelrc 116 | 117 | - name: bazel test //... 118 | shell: bash 119 | run: | 120 | bazel \ 121 | --bazelrc=${GITHUB_WORKSPACE//\\/\/}/.github/workflows/bazel${{ matrix.bazel-version.major }}.bazelrc \ 122 | --bazelrc=${GITHUB_WORKSPACE//\\/\/}/.aspect/bazelrc/ci.bazelrc \ 123 | --bazelrc=${GITHUB_WORKSPACE//\\/\/}/.github/workflows/ci.bazelrc \ 124 | test \ 125 | --test_tag_filters=-skip-on-bazel${{ matrix.bazel-version.major }} \ 126 | --build_tag_filters=-skip-on-bazel${{ matrix.bazel-version.major }} \ 127 | --enable_bzlmod=${{ matrix.bzlmod }} \ 128 | //... -------------------------------------------------------------------------------- /.github/workflows/mirror.yml: -------------------------------------------------------------------------------- 1 | name: Mirror Releases 2 | on: 3 | # Trigger manually in the UI 4 | workflow_dispatch: 5 | # Trigger daily at 06:50 UTC 6 | schedule: 7 | - cron: "50 6 * * *" 8 | 9 | jobs: 10 | mirror: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - run: | 15 | ./scripts/mirror_releases.sh 16 | npx @bazel/buildifier swc/private/versions.bzl 17 | bazel run docs:update 18 | - name: Create Pull Request 19 | uses: peter-evans/create-pull-request@v5 20 | with: 21 | commit-message: "chore: mirror external releases" 22 | -------------------------------------------------------------------------------- /.github/workflows/new_issue.yaml: -------------------------------------------------------------------------------- 1 | name: New issue 2 | on: 3 | issues: 4 | types: 5 | - opened 6 | - reopened 7 | jobs: 8 | new_issue: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | # https://docs.github.com/en/actions/managing-issues-and-pull-requests/adding-labels-to-issues 14 | - uses: actions/github-script@v6 15 | with: 16 | script: | 17 | github.rest.issues.addLabels({ 18 | issue_number: context.issue.number, 19 | owner: context.repo.owner, 20 | repo: context.repo.repo, 21 | labels: ["untriaged"] 22 | }) 23 | # https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects#adding-an-item-to-a-project 24 | - run: | 25 | gh api graphql -f query="mutation { addProjectV2ItemById(input: {projectId: \"$PROJECT_ID\" contentId: \"$CONTENT_ID\"}) { item { id } } }" 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GH_PROJECTS_RW_TOKEN }} 28 | OWNER: ${{ github.repository_owner }} 29 | REPO: ${{ github.event.repository.name }} 30 | CONTENT_ID: ${{ github.event.issue.node_id }} 31 | PROJECT_ID: PVT_kwDOA6IKMs4ALj2o # Aspect OSS Bazel Rules 32 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | # Publish new releases to Bazel Central Registry. 2 | name: Publish 3 | on: 4 | # Run the publish workflow after a successful release 5 | # Will be triggered from the release.yaml workflow 6 | workflow_call: 7 | inputs: 8 | tag_name: 9 | required: true 10 | type: string 11 | secrets: 12 | publish_token: 13 | required: true 14 | # In case of problems, let release engineers retry by manually dispatching 15 | # the workflow from the GitHub UI 16 | workflow_dispatch: 17 | inputs: 18 | tag_name: 19 | required: true 20 | type: string 21 | jobs: 22 | publish: 23 | uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v0.0.4 24 | with: 25 | tag_name: ${{ inputs.tag_name }} 26 | # GitHub repository which is a fork of the upstream where the Pull Request will be opened. 27 | registry_fork: aspect-build/bazel-central-registry 28 | permissions: 29 | attestations: write 30 | contents: write 31 | id-token: write 32 | secrets: 33 | # Necessary to push to the BCR fork, and to open a pull request against a registry 34 | publish_token: ${{ secrets.publish_token || secrets.BCR_PUBLISH_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Cut a release whenever a new tag is pushed to the repo. 2 | # You should use an annotated tag, like `git tag -a v1.2.3` 3 | # and put the release notes into the commit message for the tag. 4 | name: Release 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*.*.*" 10 | permissions: 11 | id-token: write 12 | attestations: write 13 | contents: write 14 | jobs: 15 | release: 16 | uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.2.2 17 | with: 18 | release_files: rules_swc-*.tar.gz 19 | prerelease: false 20 | tag_name: ${{ github.ref_name }} 21 | publish: 22 | needs: release 23 | uses: ./.github/workflows/publish.yaml 24 | with: 25 | tag_name: ${{ github.ref_name }} 26 | secrets: 27 | publish_token: ${{ secrets.BCR_PUBLISH_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/release_prep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit -o nounset -o pipefail 4 | 5 | # Set by GH actions, see 6 | # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables 7 | TAG=${GITHUB_REF_NAME} 8 | # The prefix is chosen to match what GitHub generates for source archives 9 | PREFIX="rules_swc-${TAG:1}" 10 | ARCHIVE="rules_swc-$TAG.tar.gz" 11 | git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE 12 | SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}') 13 | 14 | cat << EOF 15 | ## Using [Bzlmod] with Bazel 6: 16 | 17 | Add to your \`MODULE.bazel\` file: 18 | 19 | \`\`\`starlark 20 | bazel_dep(name = "aspect_rules_swc", version = "${TAG:1}") 21 | \`\`\` 22 | 23 | [Bzlmod]: https://bazel.build/build/bzlmod 24 | 25 | ## Using WORKSPACE 26 | 27 | \`\`\`starlark 28 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 29 | http_archive( 30 | name = "aspect_rules_swc", 31 | sha256 = "${SHA}", 32 | strip_prefix = "${PREFIX}", 33 | url = "https://github.com/aspect-build/rules_swc/releases/download/${TAG}/${ARCHIVE}", 34 | ) 35 | EOF 36 | 37 | awk 'f;/--SNIP--/{f=1}' e2e/smoke/WORKSPACE.bazel 38 | echo "\`\`\`" 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | .bazelrc.user 3 | node_modules/ 4 | .pnpm-* 5 | 6 | .idea/ 7 | .ijwb/ 8 | .DS_Store 9 | 10 | # Bazel's MODULE lockfile isn't ready to check in yet as of Bazel 7.1. 11 | # Do allow for it to be created, however, since it gives a performance boost for local development. 12 | # [Store resolved repository attributes in the Bzlmod lockfile](https://github.com/bazelbuild/bazel/issues/19026) 13 | # [MODULE.bazel.lock file contains user specific paths](https://github.com/bazelbuild/bazel/issues/19621) 14 | # [Consider skipping bazel_tools@_ from lockfile](https://github.com/bazelbuild/bazel/issues/19971) 15 | # [MODULE.bazel.lock file "reads through" already-locked package manager](https://github.com/bazelbuild/bazel/issues/20272) 16 | # [moduleFileHash in MODULE.bazel.lock causes frequent Git merge conflicts](https://github.com/bazelbuild/bazel/issues/20369) 17 | MODULE.bazel.lock 18 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Disabling pnpm [hoisting](https://pnpm.io/npmrc#hoist) by setting `hoist=false` is recommended on 2 | # projects using rules_js so that pnpm outside of Bazel lays out a node_modules tree similar to what 3 | # rules_js lays out under Bazel (without a hidden node_modules/.pnpm/node_modules) 4 | hoist=false 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See CONTRIBUTING.md for instructions. 2 | # See https://pre-commit.com for more information 3 | # See https://pre-commit.com/hooks.html for more hooks 4 | 5 | # Commitizen runs in commit-msg stage 6 | # but we don't want to run the other hooks on commit messages 7 | default_stages: [commit] 8 | 9 | repos: 10 | # Check formatting and lint for starlark code 11 | - repo: https://github.com/keith/pre-commit-buildifier 12 | rev: 6.4.0 13 | hooks: 14 | - id: buildifier 15 | - id: buildifier-lint 16 | # Enforce that commit messages allow for later changelog generation 17 | - repo: https://github.com/commitizen-tools/commitizen 18 | rev: v3.24.0 19 | hooks: 20 | # Requires that commitizen is already installed 21 | - id: commitizen 22 | stages: [commit-msg] 23 | - repo: https://github.com/pre-commit/mirrors-prettier 24 | rev: v3.1.0 25 | hooks: 26 | - id: prettier 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs/*.md 2 | examples/pnpm-lock.yaml 3 | **/expected.* 4 | **/expected_tsc 5 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@buildifier_prebuilt//:rules.bzl", "buildifier") 2 | 3 | buildifier( 4 | name = "buildifier", 5 | exclude_patterns = ["./.git/*"], 6 | lint_mode = "fix", 7 | mode = "fix", 8 | tags = ["manual"], # tag as manual so windows ci does not build it by default 9 | ) 10 | 11 | buildifier( 12 | name = "buildifier.check", 13 | exclude_patterns = ["./.git/*"], 14 | lint_mode = "warn", 15 | mode = "diff", 16 | tags = ["manual"], # tag as manual so windows ci does not build it by default 17 | ) 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Formatting 4 | 5 | Starlark files should be formatted by buildifier. 6 | We suggest using a pre-commit hook to automate this. 7 | First [install pre-commit](https://pre-commit.com/#installation), 8 | then run 9 | 10 | ```shell 11 | pre-commit install 12 | ``` 13 | 14 | Otherwise later tooling on CI may yell at you about formatting/linting violations. 15 | 16 | ## Updating BUILD files 17 | 18 | Some targets are generated from sources. 19 | Currently this is just the `bzl_library` targets. 20 | Run `aspect configure` to keep them up-to-date. 21 | 22 | ## Using this as a development dependency of other rules 23 | 24 | You'll commonly find that you develop in another WORKSPACE, such as 25 | some other ruleset that depends on rules_swc, or in a nested 26 | WORKSPACE in the integration_tests folder. 27 | 28 | To always tell Bazel to use this directory rather than some release 29 | artifact or a version fetched from the internet, run this from this 30 | directory: 31 | 32 | ```sh 33 | OVERRIDE="--override_repository=rules_swc=$(pwd)/rules_swc" 34 | echo "build $OVERRIDE" >> ~/.bazelrc 35 | echo "query $OVERRIDE" >> ~/.bazelrc 36 | ``` 37 | 38 | This means that any usage of `@rules_swc` on your system will point to this folder. 39 | 40 | ## Releasing 41 | 42 | 1. Determine the next release version, following semver (could automate in the future from changelog) 43 | 1. Push a tag to the repo, or create one on the GH UI 44 | 1. Watch the automation run on GitHub actions 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | "aspect-build/rules_swc" 2 | 3 | module( 4 | name = "aspect_rules_swc", 5 | version = "0.0.0", 6 | compatibility_level = 1, 7 | ) 8 | 9 | # Lower-bounds (minimum) versions for direct runtime dependencies 10 | bazel_dep(name = "aspect_bazel_lib", version = "2.11.0") 11 | bazel_dep(name = "aspect_rules_js", version = "2.0.0") # Note: only used for provider symbols, we don't spawn nodejs actions 12 | bazel_dep(name = "bazel_skylib", version = "1.5.0") 13 | bazel_dep(name = "platforms", version = "0.0.7") 14 | 15 | swc = use_extension("@aspect_rules_swc//swc:extensions.bzl", "swc") 16 | swc.toolchain( 17 | name = "swc", 18 | swc_version = "v1.7.40", 19 | ) 20 | use_repo(swc, "swc_toolchains") 21 | 22 | register_toolchains("@swc_toolchains//:all") 23 | 24 | ####### Dev dependencies ######## 25 | 26 | bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) 27 | 28 | npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True) 29 | npm.npm_translate_lock( 30 | name = "npm", 31 | npmrc = "//:.npmrc", 32 | pnpm_lock = "//examples:pnpm-lock.yaml", 33 | verify_node_modules_ignored = "//:.bazelignore", 34 | ) 35 | use_repo(npm, "npm") 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bazel rules for swc 2 | 3 | SWC () is a fast JavaScript compiler, written in Rust. 4 | It performs the same work as Babel among other things, but is 20x faster. 5 | 6 | Many companies are successfully building with rules_swc. If you're getting value from the project, please let us know! Just comment on our [Adoption Discussion](https://github.com/aspect-build/rules_js/discussions/1000). 7 | 8 | SWC is a natural fit with Bazel. 9 | Bazel does best when it orchestrates the work of many short-lived compiler processes. 10 | NodeJS is not a good runtime for such tools, because it is an interpreter, and is only fast after the code has been running for a while and is Just-In-Time optimized. 11 | SWC is fast from the beginning since it is compiled to optimized machine code. 12 | This makes it a better choice for most developer workflows than tools like `tsc` or `babel`. 13 | 14 | At the same, Bazel is a good fit for SWC too. Instead of waiting for swc plugins, 15 | can already use whatever tools you like, for example you could choose any bundler, such as esbuild. 16 | Bazel's agnostic unix-pipeline-style composition of tools allows you to mix-and-match the best parts of 17 | the JS ecosystem, regardless what language they run on. 18 | With Bazel, you won't need to figure out SWC's plugin infrastructure. 19 | You can have SWC do what it's good at, and not even have it involved with the rest. 20 | 21 | _Need help?_ This ruleset has support provided by https://aspect.build/services. 22 | 23 | ## Features 24 | 25 | These rules provide a hermetic toolchain that runs the SWC cli, so it doesn't matter what is 26 | already installed on a developer's machine, they're guaranteed to get the same result. 27 | It caches all the tools using Bazel's downloader. 28 | This means that even when Bazel determines that a repository is invalidated and re-runs the setup 29 | (due to running `bazel sync --configure` for example, or after a `bazel clean --expunge`) 30 | nothing is fetched from the network. Also the downloader can be configured to use corporate policy 31 | like fetching exclusively through Artifactory. 32 | 33 | We use a [Bazel toolchain](https://docs.bazel.build/versions/main/toolchains.html) to expose 34 | the SWC compiler to the Bazel actions that run it. 35 | This allows a user to register their own toolchain, for example to build SWC Rust code from source. 36 | By default, we download pre-built binaries from https://github.com/swc-project/swc/releases. 37 | This means we do not run `npm install` or `yarn`. 38 | 39 | ## Installation 40 | 41 | Follow instructions from the release you wish to use: 42 | . 43 | 44 | ## Usage 45 | 46 | ### From a BUILD file 47 | 48 | The simplest usage is with the [swc rule](/docs/swc.md), used to compile TypeScript code to JavaScript in a tight developer loop. Each `.ts` or `.tsx` file is compiled to `bazel-bin/[.../file].js` and available to downstream 49 | tools such as bundlers, which are in their own Bazel rules. 50 | 51 | See the example in /examples/simple. 52 | 53 | ### In a macro 54 | 55 | Often the repetition of hand-writing BUILD files needs to be overcome with a Bazel macro. 56 | This composes a few rules together into a common pattern which is shared in your repo. 57 | 58 | See the example in /examples/macro. 59 | 60 | ### In a custom rule 61 | 62 | The most advanced usage is to write your own rule that uses the swc toolchain. 63 | 64 | This is a good choice if you need to integrate with other Bazel rules via 65 | [Providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers) 66 | 67 | You'll basically follow the example of /swc/private/swc.bzl in this repo, by using 68 | the `ctx.actions.run` Starlark API. 69 | 70 | - Use `ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.swc_binary` to locate the binary tool to execute 71 | - To pass the relevant files to the action, `inputs` should include 72 | `ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.tool_files` 73 | 74 | You can load helper functions from the private API like our implementation does, 75 | but note that this may have breaking changes between major releases. 76 | 77 | Alternatively you can write a rule from scratch, using the toolchains and 78 | SWC cli provided in aspect_rules_swc. 79 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Needed for reference from e2e/smoke 2 | workspace(name = "aspect_rules_swc") 3 | -------------------------------------------------------------------------------- /docs/.bazelversion: -------------------------------------------------------------------------------- 1 | ../.bazelversion -------------------------------------------------------------------------------- /docs/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This load statement must be in the docs/ package rather than anything users depend on 2 | # so that the dependency on stardoc doesn't leak to them. 3 | load("@aspect_bazel_lib//lib:docs.bzl", "stardoc_with_diff_test", "update_docs") 4 | 5 | stardoc_with_diff_test( 6 | name = "swc", 7 | bzl_library_target = "@aspect_rules_swc//swc:defs", 8 | ) 9 | 10 | stardoc_with_diff_test( 11 | name = "repositories", 12 | bzl_library_target = "@aspect_rules_swc//swc:repositories", 13 | ) 14 | 15 | update_docs( 16 | name = "update", 17 | tags = ["skip-on-bazel6"], # slight docs difference in Bazel 6 18 | ) 19 | -------------------------------------------------------------------------------- /docs/MODULE.bazel: -------------------------------------------------------------------------------- 1 | bazel_dep(name = "aspect_bazel_lib", version = "2.14.0") 2 | bazel_dep(name = "aspect_rules_swc", version = "0.0.0") 3 | bazel_dep(name = "stardoc", version = "0.7.0", repo_name = "io_bazel_stardoc") 4 | 5 | local_path_override( 6 | module_name = "aspect_rules_swc", 7 | path = "..", 8 | ) 9 | -------------------------------------------------------------------------------- /docs/repositories.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Repository rules for fetching the swc toolchain. 4 | 5 | For typical usage, see the snippets provided in the rules_swc release notes. 6 | 7 | ### Version matching 8 | 9 | To keep the swc version in sync with non-Bazel tooling, use `swc_version_from`. 10 | 11 | Currently this only works when a single, pinned version appears, see: 12 | https://github.com/aspect-build/rules_ts/issues/308 13 | 14 | For example, `package.json`: 15 | 16 | ```json 17 | { 18 | "devDependencies": { 19 | "@swc/core": "1.3.37" 20 | } 21 | } 22 | ``` 23 | 24 | Allows this in `WORKSPACE`: 25 | 26 | ```starlark 27 | swc_register_toolchains( 28 | name = "swc", 29 | swc_version_from = "//:package.json", 30 | ) 31 | ``` 32 | 33 | 34 | 35 | ## swc_register_toolchains 36 | 37 |
38 | load("@aspect_rules_swc//swc:repositories.bzl", "swc_register_toolchains")
39 | 
40 | swc_register_toolchains(name, swc_version, swc_version_from, register, kwargs)
41 | 
42 | 43 | Convenience macro for users which does typical setup. 44 | 45 | - create a repository for each built-in platform like "swc_linux_amd64" 46 | - create a repository exposing toolchains for each platform like "swc_platforms" 47 | - register a toolchain pointing at each platform 48 | Users can avoid this macro and do these steps themselves, if they want more control. 49 | 50 | 51 | **PARAMETERS** 52 | 53 | 54 | | Name | Description | Default Value | 55 | | :------------- | :------------- | :------------- | 56 | | name | base name for all created repos; we recommend `swc` | none | 57 | | swc_version | version of the swc project, from https://github.com/swc-project/swc/releases Exactly one of `swc_version` or `swc_version_from` must be set. | `None` | 58 | | swc_version_from | label of a json file which declares an `@swc/core` version.

This may be a `package.json` file, with "@swc/core" in the dependencies or devDependencies property, and the version exactly specified.

With rules_js v1.32.0 or greater, it may also be a `resolved.json` file produced by `npm_translate_lock`, such as `@npm//path/to/linked:@swc/core/resolved.json`

Exactly one of `swc_version` or `swc_version_from` must be set. | `None` | 59 | | register | whether to call through to native.register_toolchains. Should be True for WORKSPACE users, but false when used under bzlmod extension | `True` | 60 | | kwargs | passed to each swc_repositories call | none | 61 | 62 | 63 | 64 | 65 | ## swc_repositories 66 | 67 |
68 | load("@aspect_rules_swc//swc:repositories.bzl", "swc_repositories")
69 | 
70 | swc_repositories(name, integrity_hashes, platform, repo_mapping, swc_version, swc_version_from)
71 | 
72 | 73 | Fetch external dependencies needed to run the SWC cli 74 | 75 | **ATTRIBUTES** 76 | 77 | 78 | | Name | Description | Type | Mandatory | Default | 79 | | :------------- | :------------- | :------------- | :------------- | :------------- | 80 | | name | A unique name for this repository. | Name | required | | 81 | | integrity_hashes | A mapping from platform to integrity hash. | Dictionary: String -> String | optional | `{}` | 82 | | platform | - | String | required | | 83 | | repo_mapping | In `WORKSPACE` context only: a dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.

For example, an entry `"@foo": "@bar"` declares that, for any time this repository depends on `@foo` (such as a dependency on `@foo//some:target`, it should actually resolve that dependency within globally-declared `@bar` (`@bar//some:target`).

This attribute is _not_ supported in `MODULE.bazel` context (when invoking a repository rule inside a module extension's implementation function). | Dictionary: String -> String | optional | | 84 | | swc_version | Explicit version. If provided, the package.json is not read. | String | optional | `""` | 85 | | swc_version_from | Location of package.json which has a version for @swc/core. | Label | optional | `None` | 86 | 87 | 88 | -------------------------------------------------------------------------------- /docs/swc.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | API for running the SWC cli under Bazel 4 | 5 | The simplest usage relies on the `swcrc` attribute automatically discovering `.swcrc`: 6 | 7 | ```starlark 8 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 9 | 10 | swc( 11 | name = "compile", 12 | srcs = ["file.ts"], 13 | ) 14 | ``` 15 | 16 | 17 | 18 | ## swc_compile 19 | 20 |
 21 | load("@aspect_rules_swc//swc:defs.bzl", "swc_compile")
 22 | 
 23 | swc_compile(name, srcs, data, allow_js, args, default_ext, dts_outs, emit_isolated_dts, js_outs,
 24 |             map_outs, out_dir, output_dir, plugins, root_dir, source_maps, source_root, swcrc)
 25 | 
26 | 27 | Underlying rule for the `swc` macro. 28 | 29 | Most users should use [swc](#swc) instead, as it predicts the output files 30 | and has convenient default values. 31 | 32 | Use this if you need more control over how the rule is called, 33 | for example to set your own output labels for `js_outs`. 34 | 35 | **ATTRIBUTES** 36 | 37 | 38 | | Name | Description | Type | Mandatory | Default | 39 | | :------------- | :------------- | :------------- | :------------- | :------------- | 40 | | name | A unique name for this target. | Name | required | | 41 | | srcs | source files, typically .ts files in the source tree | List of labels | required | | 42 | | data | Runtime dependencies to include in binaries/tests that depend on this target.

Follows the same semantics as `js_library` `data` attribute. See https://docs.aspect.build/rulesets/aspect_rules_js/docs/js_library#data for more info. | List of labels | optional | `[]` | 43 | | allow_js | Allow JavaScript sources to be transpiled.

If False, only TypeScript sources will be transpiled. | Boolean | optional | `True` | 44 | | args | Additional arguments to pass to swcx cli (NOT swc!).

NB: this is not the same as the CLI arguments for @swc/cli npm package. For performance, rules_swc does not call a Node.js program wrapping the swc rust binding. Instead, we directly spawn the (somewhat experimental) native Rust binary shipped inside the @swc/core npm package, which the swc project calls "swcx" Tracking issue for feature parity: https://github.com/swc-project/swc/issues/4017 | List of strings | optional | `[]` | 45 | | default_ext | Default extension for output files.

If a source file does not indicate a specific module type, this extension is used.

If unset, extensions will be determined based on the `js_outs` outputs attribute or source file extensions. | String | optional | `""` | 46 | | dts_outs | list of expected TypeScript declaration files.

Can be empty, meaning no dts files should be produced. If non-empty, there should be one for each entry in srcs. | List of labels | optional | `[]` | 47 | | emit_isolated_dts | Emit .d.ts files instead of .js for TypeScript sources

EXPERIMENTAL: this API is undocumented, experimental and may change without notice | Boolean | optional | `False` | 48 | | js_outs | list of expected JavaScript output files.

There should be one for each entry in srcs. | List of labels | optional | `[]` | 49 | | map_outs | list of expected source map output files.

Can be empty, meaning no source maps should be produced. If non-empty, there should be one for each entry in srcs. | List of labels | optional | `[]` | 50 | | out_dir | With output_dir=False, output files will have this directory prefix.

With output_dir=True, this is the name of the output directory. | String | optional | `""` | 51 | | output_dir | Whether to produce a directory output rather than individual files.

If out_dir is also specified, it is used as the name of the output directory. Otherwise, the directory is named the same as the target. | Boolean | optional | `False` | 52 | | plugins | swc compilation plugins, created with swc_plugin rule | List of labels | optional | `[]` | 53 | | root_dir | a subdirectory under the input package which should be consider the root directory of all the input files | String | optional | `""` | 54 | | source_maps | Create source map files for emitted JavaScript files.

see https://swc.rs/docs/usage/cli#--source-maps--s | String | optional | `"false"` | 55 | | source_root | Specify the root path for debuggers to find the reference source code.

see https://swc.rs/docs/usage/cli#--source-root

If not set, then the directory containing the source file is used. | String | optional | `""` | 56 | | swcrc | label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc | Label | optional | `None` | 57 | 58 | 59 | 60 | 61 | ## swc 62 | 63 |
 64 | load("@aspect_rules_swc//swc:defs.bzl", "swc")
 65 | 
 66 | swc(name, srcs, args, data, plugins, output_dir, swcrc, source_maps, out_dir, root_dir, default_ext,
 67 |     allow_js, kwargs)
 68 | 
69 | 70 | Execute the SWC compiler 71 | 72 | **PARAMETERS** 73 | 74 | 75 | | Name | Description | Default Value | 76 | | :------------- | :------------- | :------------- | 77 | | name | A name for this target | none | 78 | | srcs | List of labels of TypeScript source files. | none | 79 | | args | Additional options to pass to `swcx` cli, see https://github.com/swc-project/swc/discussions/3859 Note: we do **not** run the [NodeJS wrapper `@swc/cli`](https://swc.rs/docs/usage/cli) | `[]` | 80 | | data | Files needed at runtime by binaries or tests that transitively depend on this target. See https://bazel.build/reference/be/common-definitions#typical-attributes | `[]` | 81 | | plugins | List of plugin labels created with `swc_plugin`. | `[]` | 82 | | output_dir | Whether to produce a directory output rather than individual files.

If `out_dir` is set, then that is used as the name of the output directory. Otherwise, the output directory is named the same as the target. | `False` | 83 | | swcrc | Label of a .swcrc configuration file for the SWC cli, see https://swc.rs/docs/configuration/swcrc Instead of a label, you can pass a dictionary matching the JSON schema. If this attribute isn't specified, and a file `.swcrc` exists in the same folder as this rule, it is used.

Note that some settings in `.swcrc` also appear in `tsconfig.json`. See the notes in [/docs/tsconfig.md]. | `None` | 84 | | source_maps | If set, the --source-maps argument is passed to the SWC cli with the value. See https://swc.rs/docs/usage/cli#--source-maps--s. True/False are automaticaly converted to "true"/"false" string values the cli expects. | `False` | 85 | | out_dir | The base directory for output files relative to the output directory for this package.

If output_dir is True, then this is used as the name of the output directory. | `None` | 86 | | root_dir | A subdirectory under the input package which should be considered the root directory of all the input files | `None` | 87 | | default_ext | The default extension to use for output files. If not set, the default is ".js". | `".js"` | 88 | | allow_js | If `True` (default), then .js/.mjs/.cjs input files are transpiled. If `False`, they are ignored. This can be used to mimic the behavior of tsc when using `ts_project(transpiler)`. | `True` | 89 | | kwargs | additional keyword arguments passed through to underlying [`swc_compile`](#swc_compile), eg. `visibility`, `tags` | none | 90 | 91 | 92 | 93 | 94 | ## swc_plugin 95 | 96 |
 97 | load("@aspect_rules_swc//swc:defs.bzl", "swc_plugin")
 98 | 
 99 | swc_plugin(name, srcs, config, kwargs)
100 | 
101 | 102 | Configure an SWC plugin 103 | 104 | **PARAMETERS** 105 | 106 | 107 | | Name | Description | Default Value | 108 | | :------------- | :------------- | :------------- | 109 | | name | A name for this target | none | 110 | | srcs | Plugin files. Either a directory containing a package.json pointing at a wasm file as the main entrypoint, or a wasm file. Usually a linked npm package target via rules_js. | `[]` | 111 | | config | Optional configuration dict for the plugin. This is passed as a JSON object into the `jsc.experimental.plugins` entry for the plugin. | `{}` | 112 | | kwargs | additional keyword arguments passed through to underlying rule, eg. `visibility`, `tags` | none | 113 | 114 | 115 | -------------------------------------------------------------------------------- /docs/tsconfig.md: -------------------------------------------------------------------------------- 1 | # Synchronizing settings with tsconfig.json 2 | 3 | TypeScript and SWC both need to be configured in their own files, 4 | typically `tsconfig.json` and `.swcrc`, respectively. Some settings are meant to be common between the two, such as 5 | how dependencies are resolved on the disk. 6 | 7 | This is not a Bazel-specific problem, so we can just look for existing solutions in the ecosystem, and adapt those to be run under Bazel. 8 | 9 | Ideally, we'd like SWC to simply read from `tsconfig.json`, as it 10 | is the "source-of-truth" for how editors understand `.ts` files. 11 | There is [an issue](https://github.com/swc-project/swc/issues/1348) already filed for this, but as of Jan 2023 it's not yet supported. 12 | 13 | This document explores our options. 14 | 15 | ## Maintain two files 16 | 17 | You might just check in both files as sources. 18 | 19 | Since both `tsconfig.json` and `.swcrc` are JSON files, we recommend adding an [`assert_json_matches`](https://docs.aspect.build/rules/aspect_bazel_lib/docs/testing#assert_json_matches) rule to guarantee that they don't accidentally diverge. 20 | 21 | A typical example looks like this: 22 | 23 | ```python 24 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 25 | 26 | # Verify that the "paths" entry is the same 27 | # between swc and TS language service (in the editor) 28 | assert_json_matches( 29 | name = "check_paths", 30 | file1 = "tsconfig.json", 31 | file2 = ".swcrc", 32 | filter1 = ".compilerOptions.paths", 33 | filter2 = ".jsc.paths", 34 | ) 35 | ``` 36 | 37 | ## Generate the .swcrc 38 | 39 | Another option provided by the community is to convert the `tsconfig.json` file. Under Bazel we can model this as a codegen step that happens automatically. 40 | 41 | The relevant package is [tsconfig-to-swcconfig](https://www.npmjs.com/package/tsconfig-to-swcconfig). Let's see how to wire it up. 42 | 43 | First, add the package to your devDependencies as usual. 44 | 45 | Then, invoke it in your `BUILD` file, replacing `[my/pkg]` with the Bazel package where the `package.json` appears: 46 | 47 | ```python 48 | load("@npm//[my/pkg]:tsconfig-to-swcconfig/package_json.bzl", tsconfig_to_swcconfig = "bin") 49 | 50 | tsconfig_to_swcconfig.t2s( 51 | name = "write_swcrc", 52 | srcs = ["tsconfig.json"], 53 | args = ["--filename", "$(location tsconfig.json)"], 54 | stdout = ".swcrc", 55 | visibility = ["//:__subpackages__"], 56 | ) 57 | ``` 58 | 59 | Now you can just use the standard `swc` rule with the `swcrc` attribute. 60 | See `/examples/generate_swcrc` for a full example. 61 | -------------------------------------------------------------------------------- /e2e/smoke/.bazelignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /e2e/smoke/.bazelrc: -------------------------------------------------------------------------------- 1 | # Import Aspect bazelrc presets 2 | try-import %workspace%/../../.aspect/bazelrc/bazel7.bazelrc 3 | import %workspace%/../../.aspect/bazelrc/convenience.bazelrc 4 | import %workspace%/../../.aspect/bazelrc/correctness.bazelrc 5 | import %workspace%/../../.aspect/bazelrc/debug.bazelrc 6 | import %workspace%/../../.aspect/bazelrc/javascript.bazelrc 7 | import %workspace%/../../.aspect/bazelrc/performance.bazelrc 8 | 9 | ### YOUR PROJECT SPECIFIC OPTIONS GO HERE ### 10 | 11 | # Load any settings & overrides specific to the current user from `.aspect/bazelrc/user.bazelrc`. 12 | # This file should appear in `.gitignore` so that settings are not shared with team members. This 13 | # should be last statement in this config so the user configuration is able to overwrite flags from 14 | # this file. See https://bazel.build/configure/best-practices#bazelrc-file. 15 | try-import %workspace%/../../.aspect/bazelrc/user.bazelrc 16 | -------------------------------------------------------------------------------- /e2e/smoke/.bazelversion: -------------------------------------------------------------------------------- 1 | ../../.bazelversion -------------------------------------------------------------------------------- /e2e/smoke/.npmrc: -------------------------------------------------------------------------------- 1 | # Disabling pnpm [hoisting](https://pnpm.io/npmrc#hoist) by setting `hoist=false` is recommended on 2 | # projects using rules_js so that pnpm outside of Bazel lays out a node_modules tree similar to what 3 | # rules_js lays out under Bazel (without a hidden node_modules/.pnpm/node_modules) 4 | hoist=false 5 | -------------------------------------------------------------------------------- /e2e/smoke/.swcrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /e2e/smoke/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | load("@bazel_skylib//rules:build_test.bzl", "build_test") 3 | 4 | swc( 5 | name = "compile", 6 | srcs = ["foo.ts"], 7 | ) 8 | 9 | build_test( 10 | name = "test", 11 | targets = [":compile"], 12 | ) 13 | -------------------------------------------------------------------------------- /e2e/smoke/MODULE.bazel: -------------------------------------------------------------------------------- 1 | bazel_dep(name = "aspect_rules_swc", version = "0.0.0", dev_dependency = True) 2 | local_path_override( 3 | module_name = "aspect_rules_swc", 4 | path = "../..", 5 | ) 6 | 7 | bazel_dep(name = "bazel_skylib", version = "1.5.0", dev_dependency = True) 8 | 9 | bazel_dep(name = "aspect_rules_js", version = "2.0.0") 10 | 11 | npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True) 12 | npm.npm_translate_lock( 13 | name = "npm", 14 | npmrc = "//:.npmrc", 15 | pnpm_lock = "//:pnpm-lock.yaml", 16 | verify_node_modules_ignored = "//:.bazelignore", 17 | ) 18 | use_repo(npm, "npm") 19 | 20 | # Optional: specify a custom swc toolchain instead of the default 21 | swc = use_extension("@aspect_rules_swc//swc:extensions.bzl", "swc", dev_dependency = True) 22 | swc.toolchain( 23 | name = "swc", 24 | swc_version_from = "@npm//:@swc/core/resolved.json", 25 | ) 26 | -------------------------------------------------------------------------------- /e2e/smoke/README.md: -------------------------------------------------------------------------------- 1 | # smoke test 2 | 3 | This e2e exercises the repo from an end-users perpective. 4 | It catches mistakes in our install instructions, or usages that fail when called from an "external" repository to rules_swc. 5 | -------------------------------------------------------------------------------- /e2e/smoke/WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | # Override http_archive for local testing 2 | local_repository( 3 | name = "aspect_rules_swc", 4 | path = "../..", 5 | ) 6 | 7 | #---SNIP--- Below here is re-used in the snippet published on releases 8 | 9 | ################### 10 | # rules_swc setup # 11 | ################### 12 | 13 | # Fetches the rules_swc dependencies. 14 | # If you want to have a different version of some dependency, 15 | # you should fetch it *before* calling this. 16 | # Alternatively, you can skip calling this function, so long as you've 17 | # already fetched all the dependencies. 18 | load("@aspect_rules_swc//swc:dependencies.bzl", "rules_swc_dependencies") 19 | 20 | rules_swc_dependencies() 21 | 22 | # Fetches a SWC cli from 23 | # https://github.com/swc-project/swc/releases 24 | # If you'd rather compile it from source, you can use rules_rust, fetch the project, 25 | # then register the toolchain yourself. (Note, this is not yet documented) 26 | load("@aspect_rules_swc//swc:repositories.bzl", "LATEST_SWC_VERSION", "swc_register_toolchains") 27 | 28 | swc_register_toolchains( 29 | name = "swc", 30 | swc_version = LATEST_SWC_VERSION, 31 | ) 32 | 33 | # To use rules_swc with bazel-lib 2.x, you must additionally register the coreutils toolchain. 34 | load("@aspect_bazel_lib//lib:repositories.bzl", "register_coreutils_toolchains") 35 | 36 | register_coreutils_toolchains() 37 | -------------------------------------------------------------------------------- /e2e/smoke/WORKSPACE.bzlmod: -------------------------------------------------------------------------------- 1 | # This file replaces `WORKSPACE.bazel` when --enable_bzlmod is set. 2 | -------------------------------------------------------------------------------- /e2e/smoke/foo.ts: -------------------------------------------------------------------------------- 1 | export const hello = () => { 2 | console.log("hello world"); 3 | }; 4 | -------------------------------------------------------------------------------- /e2e/smoke/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@swc/core": "~1.11.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /e2e/smoke/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | devDependencies: 8 | '@swc/core': 9 | specifier: ~1.11.0 10 | version: 1.11.16 11 | 12 | packages: 13 | 14 | /@swc/core-darwin-arm64@1.11.16: 15 | resolution: {integrity: sha512-l6uWMU+MUdfLHCl3dJgtVEdsUHPskoA4BSu0L1hh9SGBwPZ8xeOz8iLIqZM27lTuXxL4KsYH6GQR/OdQ/vhLtg==} 16 | engines: {node: '>=10'} 17 | cpu: [arm64] 18 | os: [darwin] 19 | requiresBuild: true 20 | dev: true 21 | optional: true 22 | 23 | /@swc/core-darwin-x64@1.11.16: 24 | resolution: {integrity: sha512-TH0IW8Ao1WZ4ARFHIh29dAQHYBEl4YnP74n++rjppmlCjY+8v3s5nXMA7IqxO3b5LVHyggWtU4+46DXTyMJM7g==} 25 | engines: {node: '>=10'} 26 | cpu: [x64] 27 | os: [darwin] 28 | requiresBuild: true 29 | dev: true 30 | optional: true 31 | 32 | /@swc/core-linux-arm-gnueabihf@1.11.16: 33 | resolution: {integrity: sha512-2IxD9t09oNZrbv37p4cJ9cTHMUAK6qNiShi9s2FJ9LcqSnZSN4iS4hvaaX6KZuG54d58vWnMU7yycjkdOTQcMg==} 34 | engines: {node: '>=10'} 35 | cpu: [arm] 36 | os: [linux] 37 | requiresBuild: true 38 | dev: true 39 | optional: true 40 | 41 | /@swc/core-linux-arm64-gnu@1.11.16: 42 | resolution: {integrity: sha512-AYkN23DOiPh1bf3XBf/xzZQDKSsgZTxlbyTyUIhprLJpAAAT0ZCGAUcS5mHqydk0nWQ13ABUymodvHoroutNzw==} 43 | engines: {node: '>=10'} 44 | cpu: [arm64] 45 | os: [linux] 46 | requiresBuild: true 47 | dev: true 48 | optional: true 49 | 50 | /@swc/core-linux-arm64-musl@1.11.16: 51 | resolution: {integrity: sha512-n/nWXDRCIhM51dDGELfBcTMNnCiFatE7LDvsbYxb7DJt1HGjaCNvHHCKURb/apJTh/YNtWfgFap9dbsTgw8yPA==} 52 | engines: {node: '>=10'} 53 | cpu: [arm64] 54 | os: [linux] 55 | requiresBuild: true 56 | dev: true 57 | optional: true 58 | 59 | /@swc/core-linux-x64-gnu@1.11.16: 60 | resolution: {integrity: sha512-xr182YQrF47n7Awxj+/ruI21bYw+xO/B26KFVnb+i3ezF9NOhqoqTX+33RL1ZLA/uFTq8ksPZO/y+ZVS/odtQA==} 61 | engines: {node: '>=10'} 62 | cpu: [x64] 63 | os: [linux] 64 | requiresBuild: true 65 | dev: true 66 | optional: true 67 | 68 | /@swc/core-linux-x64-musl@1.11.16: 69 | resolution: {integrity: sha512-k2JBfiwWfXCIKrBRjFO9/vEdLSYq0QLJ+iNSLdfrejZ/aENNkbEg8O7O2GKUSb30RBacn6k8HMfJrcPLFiEyCQ==} 70 | engines: {node: '>=10'} 71 | cpu: [x64] 72 | os: [linux] 73 | requiresBuild: true 74 | dev: true 75 | optional: true 76 | 77 | /@swc/core-win32-arm64-msvc@1.11.16: 78 | resolution: {integrity: sha512-taOb5U+abyEhQgex+hr6cI48BoqSvSdfmdirWcxprIEUBHCxa1dSriVwnJRAJOFI9T+5BEz88by6rgbB9MjbHA==} 79 | engines: {node: '>=10'} 80 | cpu: [arm64] 81 | os: [win32] 82 | requiresBuild: true 83 | dev: true 84 | optional: true 85 | 86 | /@swc/core-win32-ia32-msvc@1.11.16: 87 | resolution: {integrity: sha512-b7yYggM9LBDiMY+XUt5kYWvs5sn0U3PXSOGvF3CbLufD/N/YQiDcYON2N3lrWHYL8aYnwbuZl45ojmQHSQPcdA==} 88 | engines: {node: '>=10'} 89 | cpu: [ia32] 90 | os: [win32] 91 | requiresBuild: true 92 | dev: true 93 | optional: true 94 | 95 | /@swc/core-win32-x64-msvc@1.11.16: 96 | resolution: {integrity: sha512-/ibq/YDc3B5AROkpOKPGxVkSyCKOg+ml8k11RxrW7FAPy6a9y5y9KPcWIqV74Ahq4RuaMNslTQqHWAGSm0xJsQ==} 97 | engines: {node: '>=10'} 98 | cpu: [x64] 99 | os: [win32] 100 | requiresBuild: true 101 | dev: true 102 | optional: true 103 | 104 | /@swc/core@1.11.16: 105 | resolution: {integrity: sha512-wgjrJqVUss8Lxqilg0vkiE0tkEKU3mZkoybQM1Ehy+PKWwwB6lFAwKi20cAEFlSSWo8jFR8hRo19ZELAoLDowg==} 106 | engines: {node: '>=10'} 107 | requiresBuild: true 108 | peerDependencies: 109 | '@swc/helpers': '*' 110 | peerDependenciesMeta: 111 | '@swc/helpers': 112 | optional: true 113 | dependencies: 114 | '@swc/counter': 0.1.3 115 | '@swc/types': 0.1.21 116 | optionalDependencies: 117 | '@swc/core-darwin-arm64': 1.11.16 118 | '@swc/core-darwin-x64': 1.11.16 119 | '@swc/core-linux-arm-gnueabihf': 1.11.16 120 | '@swc/core-linux-arm64-gnu': 1.11.16 121 | '@swc/core-linux-arm64-musl': 1.11.16 122 | '@swc/core-linux-x64-gnu': 1.11.16 123 | '@swc/core-linux-x64-musl': 1.11.16 124 | '@swc/core-win32-arm64-msvc': 1.11.16 125 | '@swc/core-win32-ia32-msvc': 1.11.16 126 | '@swc/core-win32-x64-msvc': 1.11.16 127 | dev: true 128 | 129 | /@swc/counter@0.1.3: 130 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 131 | dev: true 132 | 133 | /@swc/types@0.1.21: 134 | resolution: {integrity: sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==} 135 | dependencies: 136 | '@swc/counter': 0.1.3 137 | dev: true 138 | -------------------------------------------------------------------------------- /examples/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//:defs.bzl", "npm_link_all_packages") 2 | 3 | # Required to create the virtual store at the root of the pnpm workspace 4 | # We don't have any node_modules to lay out in this directory. 5 | npm_link_all_packages() 6 | -------------------------------------------------------------------------------- /examples/allow_js/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Simple use case for swc: transpiling .js using the `swc` rule, similar to tsc with 'allowJs' 2 | """ 3 | 4 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_outputs") 5 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 6 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 7 | 8 | # Runs `swc in.js > ../../bazel-bin/examples/js_outs/in.js` 9 | swc( 10 | name = "compile-js", 11 | srcs = ["in.js"], 12 | ) 13 | 14 | # Assert that the output of "compile" rule matches the expected file. 15 | write_source_files( 16 | name = "test", 17 | # There is no pre-declared output of the "compile" rule because the input is .js 18 | # so the the target name is used instead of output file. 19 | files = {"expected.js": ":compile-js"}, 20 | ) 21 | 22 | # Runs swc on all js files 23 | swc( 24 | name = "compile-js-exts", 25 | srcs = [ 26 | "in.cjs", 27 | "in.d.ts", 28 | "in.jsx", 29 | "in.mjs", 30 | "in_ts.ts", 31 | ], 32 | out_dir = "js_outs", 33 | ) 34 | 35 | # Assert the extensions and output of each js file type via pre-declared outputs. 36 | write_source_files( 37 | name = "test-x", 38 | files = { 39 | "expected.jsx": "js_outs/in.js", 40 | "expected.mjs": "js_outs/in.mjs", 41 | "expected.cjs": "js_outs/in.cjs", 42 | "expected.ts": "js_outs/in_ts.js", 43 | }, 44 | ) 45 | 46 | swc( 47 | name = "compile-js-exts-no-js", 48 | srcs = [ 49 | "in.cjs", 50 | "in.d.ts", 51 | "in.jsx", 52 | "in.mjs", 53 | "in_ts.ts", 54 | ], 55 | allow_js = False, 56 | out_dir = "no_js_outs", 57 | ) 58 | 59 | assert_outputs( 60 | name = "test-x-no-js", 61 | actual = ":compile-js-exts-no-js", 62 | expected = [ 63 | "examples/allow_js/no_js_outs/in_ts.js", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /examples/allow_js/expected.cjs: -------------------------------------------------------------------------------- 1 | exports.a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/expected.jsx: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/expected.mjs: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/expected.ts: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in.cjs: -------------------------------------------------------------------------------- 1 | exports.a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in.d.ts: -------------------------------------------------------------------------------- 1 | export const a: string; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in.js: -------------------------------------------------------------------------------- 1 | export const a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in.jsx: -------------------------------------------------------------------------------- 1 | export const a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in.mjs: -------------------------------------------------------------------------------- 1 | export const a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/allow_js/in_ts.ts: -------------------------------------------------------------------------------- 1 | export const a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/custom_outs/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Demonstrate that js_outs can go to the desired file extensions.""" 2 | 3 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_outputs") 4 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 5 | load("@aspect_rules_swc//swc:defs.bzl", "swc", "swc_compile") 6 | 7 | ############################### 8 | # Case 1: js extension variants 9 | # .mts -> .mjs 10 | swc( 11 | name = "esmodule", 12 | srcs = ["b.mts"], 13 | source_maps = True, 14 | ) 15 | 16 | assert_outputs( 17 | name = "check_esmodule", 18 | actual = "esmodule", 19 | expected = [ 20 | "examples/custom_outs/b.mjs", 21 | "examples/custom_outs/b.mjs.map", 22 | ], 23 | ) 24 | 25 | ############################### 26 | # Case 2: user-defined js output paths 27 | 28 | [ 29 | # Here, we use the underlying rule rather than the macro, so that we can control what js output 30 | # paths are pre-declared. 31 | swc_compile( 32 | name = "compile_" + format, 33 | srcs = ["a.ts"], 34 | args = [ 35 | "--config-json", 36 | """{"module": {"type": "%s"}}""" % format, 37 | ], 38 | # The extension of the outputs can be modified using js_outs 39 | js_outs = [format + "/a." + ("cjs" if format == "commonjs" else "js")], 40 | out_dir = format, 41 | ) 42 | for format in [ 43 | "commonjs", 44 | "es6", 45 | ] 46 | ] 47 | 48 | write_source_files( 49 | name = "tests", 50 | files = { 51 | "expected.cjs": ":commonjs/a.cjs", 52 | "expected.js": ":es6/a.js", 53 | }, 54 | ) 55 | -------------------------------------------------------------------------------- /examples/custom_outs/a.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "a"; 2 | -------------------------------------------------------------------------------- /examples/custom_outs/b.mts: -------------------------------------------------------------------------------- 1 | export const b = 3; 2 | -------------------------------------------------------------------------------- /examples/custom_outs/expected.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { 3 | value: true 4 | }); 5 | Object.defineProperty(exports, "a", { 6 | enumerable: true, 7 | get: function() { 8 | return a; 9 | } 10 | }); 11 | var a = "a"; 12 | -------------------------------------------------------------------------------- /examples/custom_outs/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "a"; 2 | -------------------------------------------------------------------------------- /examples/directory/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Directory use case for swc: minifying a folder of code-split files 2 | 3 | This has to be a directory-in, directory-out pattern since the codesplitter produces unpredictable filenames. 4 | 5 | Note that this example also depends on the setup in /WORKSPACE at the root of this repository. 6 | """ 7 | 8 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 9 | 10 | # gazelle:exclude *.bzl 11 | load(":mocks.bzl", "mock_codesplit") 12 | 13 | mock_codesplit( 14 | name = "split_app", 15 | ) 16 | 17 | # Runs `swc path/to/split_app --out-dir ../../examples/directory/minify` 18 | # You can run `bazel build --subcommands //examples/directory:minify` 19 | # to see the exact command line Bazel runs. 20 | swc( 21 | name = "minify", 22 | srcs = ["split_app"], 23 | output_dir = True, 24 | source_maps = True, 25 | ) 26 | 27 | sh_test( 28 | name = "minify_test", 29 | srcs = ["test.sh"], 30 | args = [ 31 | "split_app", 32 | "minify", 33 | ], 34 | data = ["minify"], 35 | env = { 36 | "BAZEL_BINDIR": "$(BINDIR)", 37 | }, 38 | deps = ["@bazel_tools//tools/bash/runfiles"], 39 | ) 40 | 41 | # Like above, but outputs to examples/directory/test-out_dir instead. 42 | swc( 43 | name = "out_dir", 44 | srcs = ["split_app"], 45 | out_dir = "test-out_dir", 46 | output_dir = True, 47 | source_maps = True, 48 | ) 49 | 50 | sh_test( 51 | name = "out_dir_test", 52 | srcs = ["test.sh"], 53 | args = [ 54 | "split_app", 55 | "test-out_dir", 56 | ], 57 | data = ["out_dir"], 58 | env = { 59 | "BAZEL_BINDIR": "$(BINDIR)", 60 | }, 61 | deps = ["@bazel_tools//tools/bash/runfiles"], 62 | ) 63 | 64 | swc( 65 | name = "out_dir_w_config", 66 | srcs = ["split_app"], 67 | out_dir = "config-out_dir", 68 | output_dir = True, 69 | swcrc = { 70 | "minify": False, 71 | }, 72 | ) 73 | -------------------------------------------------------------------------------- /examples/directory/mocks.bzl: -------------------------------------------------------------------------------- 1 | "Placeholder for a rule like webpack which may produce an unpredictable number of JS chunks" 2 | 3 | load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") 4 | load("@bazel_skylib//rules:write_file.bzl", "write_file") 5 | 6 | def mock_codesplit(name): 7 | write_file( 8 | name = "write1", 9 | out = "file1.js", 10 | content = ["const a = 1"], 11 | ) 12 | 13 | write_file( 14 | name = "write2", 15 | out = "file2.js", 16 | content = ["const a = 2"], 17 | ) 18 | 19 | copy_to_directory( 20 | name = name, 21 | srcs = ["file1.js", "file2.js"], 22 | ) 23 | -------------------------------------------------------------------------------- /examples/directory/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # --- begin runfiles.bash initialization v2 --- 4 | # Copy-pasted from the Bazel Bash runfiles library v2. 5 | set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash 6 | source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ 7 | source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ 8 | source "$0.runfiles/$f" 2>/dev/null || \ 9 | source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 10 | source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ 11 | { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e 12 | # --- end runfiles.bash initialization v2 --- 13 | 14 | set -o errexit 15 | 16 | readonly in_folder=$(rlocation $TEST_WORKSPACE/$(dirname $TEST_BINARY)/$2/$BAZEL_BINDIR) 17 | 18 | if ! [[ -e $in_folder/examples/directory/$1/file1.js ]]; then 19 | echo >&2 "Missing expected output file in directory" 20 | ls -R $in_folder 21 | exit 1 22 | fi 23 | 24 | if ! [[ -e $in_folder/examples/directory/$1/file2.js ]]; then 25 | echo >&2 "Missing expected output file in directory" 26 | ls -R $in_folder 27 | exit 1 28 | fi 29 | 30 | if ! [[ -e $in_folder/examples/directory/$1/file1.js.map ]]; then 31 | echo >&2 "Missing expected output file in directory" 32 | ls -R $in_folder 33 | exit 1 34 | fi 35 | 36 | if ! [[ -e $in_folder/examples/directory/$1/file2.js.map ]]; then 37 | echo >&2 "Missing expected output file in directory" 38 | ls -R $in_folder 39 | exit 1 40 | fi -------------------------------------------------------------------------------- /examples/emit_types/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 2 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 3 | load("@bazel_skylib//rules:build_test.bzl", "build_test") 4 | 5 | # No root/out 6 | swc( 7 | name = "emit_dts", 8 | srcs = [ 9 | "src/a.ts", 10 | "src/b.ts", 11 | ], 12 | emit_isolated_dts = True, 13 | ) 14 | 15 | build_test( 16 | name = "emit_dts-test", 17 | targets = [ 18 | "src/a.js", 19 | "src/a.d.ts", 20 | "src/b.js", 21 | "src/b.d.ts", 22 | ], 23 | ) 24 | 25 | # With out_dir 26 | swc( 27 | name = "emit_dts_outdir", 28 | srcs = [ 29 | "src/a.ts", 30 | "src/b.ts", 31 | ], 32 | emit_isolated_dts = True, 33 | out_dir = "out", 34 | ) 35 | 36 | build_test( 37 | name = "emit_dts_outdir-test", 38 | targets = [ 39 | "out/src/a.js", 40 | "out/src/a.d.ts", 41 | "out/src/b.js", 42 | "out/src/b.d.ts", 43 | ], 44 | ) 45 | 46 | # With root_dir 47 | swc( 48 | name = "emit_dts_rootdir", 49 | srcs = [ 50 | "src/a.ts", 51 | "src/b.ts", 52 | ], 53 | emit_isolated_dts = True, 54 | root_dir = "src", 55 | ) 56 | 57 | build_test( 58 | name = "emit_dts_rootdir-test", 59 | targets = [ 60 | "a.js", 61 | "a.d.ts", 62 | "b.js", 63 | "b.d.ts", 64 | ], 65 | ) 66 | 67 | # With out_dir and root_dir 68 | swc( 69 | name = "emit_dts_outdir_rootdir", 70 | srcs = [ 71 | "src/a.ts", 72 | "src/b.ts", 73 | ], 74 | emit_isolated_dts = True, 75 | out_dir = "out_root", 76 | root_dir = "src", 77 | ) 78 | 79 | build_test( 80 | name = "emit_dts_outdir_rootdir-test", 81 | targets = [ 82 | "out_root/a.js", 83 | "out_root/a.d.ts", 84 | "out_root/b.js", 85 | "out_root/b.d.ts", 86 | ], 87 | ) 88 | 89 | # Assert the output files are correct, have not changed etc. 90 | write_source_files( 91 | name = "outputs_test", 92 | files = { 93 | "expected.a.d.ts": "src/a.d.ts", 94 | "expected.b.d.ts": "src/b.d.ts", 95 | }, 96 | ) 97 | -------------------------------------------------------------------------------- /examples/emit_types/expected.a.d.ts: -------------------------------------------------------------------------------- 1 | /* A basic interface */ export interface Foo { 2 | // teh name! 3 | name: string; 4 | } 5 | // Implicit type 6 | export declare const A: number; 7 | // Explicit type 8 | export declare const AF: Foo; 9 | -------------------------------------------------------------------------------- /examples/emit_types/expected.b.d.ts: -------------------------------------------------------------------------------- 1 | // Imported and used as types 2 | import { A, Foo } from "./a"; 3 | // Use of imported types 4 | export declare const B: typeof A; 5 | /** Another use of imported types */ export declare const BF: Foo; 6 | -------------------------------------------------------------------------------- /examples/emit_types/src/a.ts: -------------------------------------------------------------------------------- 1 | /* A basic interface */ 2 | export interface Foo { 3 | // teh name! 4 | name: string; 5 | } 6 | 7 | // Implicit type 8 | export const A = 1; 9 | 10 | // Explicit type 11 | export const AF: Foo = { 12 | name: "bar", 13 | }; 14 | -------------------------------------------------------------------------------- /examples/emit_types/src/b.ts: -------------------------------------------------------------------------------- 1 | // Imported and used as types 2 | import { A, Foo } from "./a"; 3 | 4 | // Use of imported types 5 | export const B: typeof A = 1; 6 | 7 | /** Another use of imported types */ 8 | export const BF: Foo = { 9 | name: "baz", 10 | }; 11 | -------------------------------------------------------------------------------- /examples/filegroup/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "typescript" 5 | } 6 | }, 7 | "sourceMaps": true 8 | } 9 | -------------------------------------------------------------------------------- /examples/filegroup/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | 3 | filegroup( 4 | name = "srcs", 5 | srcs = [ 6 | "a.ts", 7 | "b.ts", 8 | "sub/c.ts", 9 | ], 10 | ) 11 | 12 | swc( 13 | name = "compile", 14 | srcs = ["srcs"], 15 | source_maps = True, 16 | # We don't have to specify this. When a .swcrc configuration file is present in the current directory, it's used automatically. 17 | # swcrc = ".swcrc", 18 | ) 19 | 20 | # Since the srcs were in a filegroup, the swc macro cannot pre-declare the outputs. 21 | # So there is no label ":a.js" that we can reference from the build file. 22 | # However, a.js is still produced as one of the default outputs of the compile rule. 23 | # We can verify this in an action that depends on the ":compile" rule and reads the files. 24 | sh_test( 25 | name = "check_outputs", 26 | srcs = ["check_outputs.sh"], 27 | data = [":compile"], 28 | target_compatible_with = select({ 29 | "@platforms//os:windows": ["@platforms//:incompatible"], 30 | "//conditions:default": [], 31 | }), 32 | ) 33 | -------------------------------------------------------------------------------- /examples/filegroup/a.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "a"; 2 | -------------------------------------------------------------------------------- /examples/filegroup/b.ts: -------------------------------------------------------------------------------- 1 | export const b: string = "b"; 2 | -------------------------------------------------------------------------------- /examples/filegroup/check_outputs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | cd "$TEST_SRCDIR/$TEST_WORKSPACE/$(dirname $TEST_TARGET)" 5 | 6 | grep "export var a" filegroup/a.js 7 | grep "sourceMappingURL=a.js.map" filegroup/a.js 8 | grep -v --fixed-strings '"sourceRoot"' filegroup/a.js.map 9 | grep --fixed-strings '"sources":["a.ts"]' filegroup/a.js.map 10 | 11 | grep "export var b" filegroup/b.js 12 | grep "sourceMappingURL=b.js.map" filegroup/b.js 13 | grep -v --fixed-strings '"sourceRoot"' filegroup/b.js.map 14 | grep --fixed-strings '"sources":["b.ts"]' filegroup/b.js.map 15 | 16 | grep "export var c" filegroup/sub/c.js 17 | grep "sourceMappingURL=c.js.map" filegroup/sub/c.js 18 | grep -v --fixed-strings '"sourceRoot"' filegroup/sub/c.js.map 19 | grep --fixed-strings '"sources":["c.ts"]' filegroup/sub/c.js.map -------------------------------------------------------------------------------- /examples/filegroup/sub/c.ts: -------------------------------------------------------------------------------- 1 | export const c: string = "c"; 2 | -------------------------------------------------------------------------------- /examples/generate_swcrc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 2 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 3 | load("@npm//:defs.bzl", "npm_link_all_packages") 4 | load("@npm//examples/generate_swcrc:tsconfig-to-swcconfig/package_json.bzl", tsconfig_to_swcconfig = "bin") 5 | 6 | npm_link_all_packages(name = "node_modules") 7 | 8 | tsconfig_to_swcconfig.t2s( 9 | name = "write_swcrc", 10 | srcs = ["tsconfig.json"], 11 | args = [ 12 | "--filename", 13 | "$(location tsconfig.json)", 14 | ], 15 | stdout = ".swcrc", 16 | visibility = ["//:__subpackages__"], 17 | ) 18 | 19 | # Demonstrate that it works 20 | swc( 21 | name = "compile", 22 | srcs = ["some.ts"], 23 | swcrc = ".swcrc", 24 | ) 25 | 26 | # Verify that our options got passed through 27 | assert_json_matches( 28 | name = "test", 29 | file1 = ".swcrc", 30 | file2 = "tsconfig.json", 31 | filter1 = ".jsc.target", 32 | filter2 = ".compilerOptions.target", 33 | ) 34 | -------------------------------------------------------------------------------- /examples/generate_swcrc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "tsconfig-to-swcconfig": "^2.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/generate_swcrc/some.ts: -------------------------------------------------------------------------------- 1 | export const a = 1; 2 | -------------------------------------------------------------------------------- /examples/generate_swcrc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "AMD" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/genrule/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "typescript" 5 | } 6 | }, 7 | "sourceMaps": true 8 | } 9 | -------------------------------------------------------------------------------- /examples/genrule/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | load("@bazel_skylib//rules:build_test.bzl", "build_test") 3 | 4 | genrule( 5 | name = "a", 6 | outs = ["a.ts"], 7 | cmd = "echo 'export const a: string = \"a\";' > $@", 8 | ) 9 | 10 | genrule( 11 | name = "b", 12 | outs = ["b.ts"], 13 | cmd = "echo 'export const b: string = \"b\";' > $@", 14 | ) 15 | 16 | genrule( 17 | name = "c", 18 | outs = ["sub/c.ts"], 19 | cmd = "echo 'export const c: string = \"c\";' > $@", 20 | ) 21 | 22 | swc( 23 | name = "compile", 24 | srcs = [ 25 | "b.ts", 26 | ":a", 27 | ":sub/c.ts", 28 | ], # reference by label, output file, :outputfile 29 | source_maps = True, 30 | swcrc = ".swcrc", 31 | ) 32 | 33 | build_test( 34 | name = "predeclared_test", 35 | targets = [ 36 | "b.js", 37 | "b.js.map", 38 | "sub/c.js", 39 | "sub/c.js.map", 40 | ], 41 | ) 42 | 43 | # Since the srcs were in a filegroup, the swc macro cannot pre-declare the outputs. 44 | # So there is no label ":a.js" that we can reference from the build file. 45 | # However, a.js is still produced as one of the default outputs of the compile rule. 46 | # We can verify this in an action that depends on the ":compile" rule and reads the files. 47 | sh_test( 48 | name = "check_outputs", 49 | srcs = ["check_outputs.sh"], 50 | data = [":compile"], 51 | target_compatible_with = select({ 52 | "@platforms//os:windows": ["@platforms//:incompatible"], 53 | "//conditions:default": [], 54 | }), 55 | ) 56 | 57 | swc( 58 | name = "compile2", 59 | srcs = [ 60 | "b.ts", 61 | ":a", 62 | ":sub/c.ts", 63 | ], 64 | out_dir = "out2", 65 | source_maps = True, 66 | swcrc = ".swcrc", 67 | ) 68 | 69 | build_test( 70 | name = "out_dir_predeclared_test", 71 | targets = [ 72 | "out2/b.js", 73 | "out2/b.js.map", 74 | "out2/sub/c.js", 75 | "out2/sub/c.js.map", 76 | ], 77 | ) 78 | 79 | sh_test( 80 | name = "check_out_dir_outputs", 81 | srcs = ["check_outputs.sh"], 82 | args = ["out2"], 83 | data = [":compile"], 84 | target_compatible_with = select({ 85 | "@platforms//os:windows": ["@platforms//:incompatible"], 86 | "//conditions:default": [], 87 | }), 88 | ) 89 | 90 | swc( 91 | name = "compile3", 92 | srcs = [ 93 | "sub/c.ts", 94 | ], 95 | out_dir = "out3", 96 | root_dir = "sub", 97 | source_maps = True, 98 | swcrc = ".swcrc", 99 | ) 100 | 101 | build_test( 102 | name = "root_out_predeclared_test", 103 | targets = [ 104 | "out3/c.js", 105 | "out3/c.js.map", 106 | ], 107 | ) 108 | -------------------------------------------------------------------------------- /examples/genrule/check_outputs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | 4 | dir="genrule" 5 | if $1; then 6 | dir="genrule/$1" 7 | fi 8 | 9 | cd "$TEST_SRCDIR/$TEST_WORKSPACE/$(dirname $TEST_TARGET)" 10 | 11 | grep "export var a" $dir/a.js 12 | grep "sourceMappingURL=a.js.map" $dir/a.js 13 | grep -v --fixed-strings '"sourceRoot"' $dir/a.js.map 14 | grep --fixed-strings '"sources":["a.ts"]' $dir/a.js.map 15 | 16 | grep "export var b" $dir/b.js 17 | grep "sourceMappingURL=b.js.map" $dir/b.js 18 | grep -v --fixed-strings '"sourceRoot"' $dir/b.js.map 19 | grep --fixed-strings '"sources":["b.ts"]' $dir/b.js.map 20 | 21 | grep "export var c" $dir/sub/c.js 22 | grep "sourceMappingURL=c.js.map" $dir/sub/c.js 23 | grep -v --fixed-strings '"sourceRoot"' $dir/sub/c.js.map 24 | grep --fixed-strings '"sources":["c.ts"]' $dir/sub/c.js.map 25 | -------------------------------------------------------------------------------- /examples/macro/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 2 | 3 | # gazelle:exclude *.bzl 4 | load(":ts_project.bzl", "my_ts_project") 5 | 6 | # In your org, my_ts_project can be whatever macro fits your use case, composing swc with other tools 7 | my_ts_project( 8 | name = "compile", 9 | srcs = ["in.ts"], 10 | ) 11 | 12 | write_source_files( 13 | name = "test", 14 | # This is a pre-declared output of the "compile" rule, so we can refer to it directly using a Bazel label 15 | # even though the file itself is generated by Bazel in ../../bazel-bin/examples/macro/in.js 16 | files = {"expected.js": ":in.js"}, 17 | ) 18 | -------------------------------------------------------------------------------- /examples/macro/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "macro"; 2 | -------------------------------------------------------------------------------- /examples/macro/in.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "macro"; 2 | -------------------------------------------------------------------------------- /examples/macro/ts_project.bzl: -------------------------------------------------------------------------------- 1 | """An example macro that abstracts processing of a TypeScript library in your repo. 2 | 3 | See explanation in ./BUILD.bazel""" 4 | 5 | load("@bazel_skylib//rules:write_file.bzl", "write_file") 6 | 7 | def my_ts_project(name, srcs = []): 8 | """TODO: docs 9 | 10 | Args: 11 | name: name of the resulting target 12 | srcs: todo 13 | """ 14 | 15 | write_file( 16 | name = "_{}_config".format(name), 17 | out = ".swcrc", 18 | content = [json.encode({ 19 | "jsc": { 20 | "parser": { 21 | "syntax": "typescript", 22 | }, 23 | }, 24 | })], 25 | ) 26 | 27 | for idx, src in enumerate(srcs): 28 | # Run the swc rust cli directly with arguments we choose. 29 | # See https://docs.bazel.build/versions/main/be/general.html#genrule 30 | # Most users would use the `swc` rule instead, this example uses 31 | # genrule to have more control over the command line arguments. 32 | native.genrule( 33 | name = "run_{}".format(idx), 34 | srcs = [src], 35 | outs = [src.replace(".ts", ".js")], 36 | cmd = "$(SWC_BINARY_PATH) compile --config-file $(location {}) --out-file $@ < $<".format( 37 | "_{}_config".format(name), 38 | ), 39 | toolchains = ["@aspect_rules_swc//swc:resolved_toolchain"], 40 | tools = [ 41 | "_{}_config".format(name), 42 | "@aspect_rules_swc//swc:resolved_toolchain", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /examples/opaque_src/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Simple use case for swc: transpiling TypeScript using the `swc` rule 2 | 3 | Note that this example also depends on the setup in /WORKSPACE at the root of this repository. 4 | """ 5 | 6 | load("@aspect_bazel_lib//lib:output_files.bzl", "output_files") 7 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 8 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 9 | 10 | filegroup( 11 | name = "opaque_src", 12 | srcs = ["in.ts"], 13 | ) 14 | 15 | # In this case we pass a target containing sources so the swc macro can't pre-declare the .js output targets 16 | swc( 17 | name = "compile", 18 | srcs = [":opaque_src"], 19 | ) 20 | 21 | # But we can extract the opaque outputs using output_files to make a friendly label for the output .js file (used below) 22 | output_files( 23 | name = "in.js", 24 | paths = ["%s/in.js" % package_name()], 25 | target = ":compile", 26 | ) 27 | 28 | # Assert that this predeclared output of "compile" rule matches the expected file. 29 | write_source_files( 30 | name = "test", 31 | files = {"expected.js": ":in.js"}, 32 | ) 33 | -------------------------------------------------------------------------------- /examples/opaque_src/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/opaque_src/in.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "simple"; 2 | -------------------------------------------------------------------------------- /examples/out_dir/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 2 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 3 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 4 | 5 | swc( 6 | name = "compile", 7 | srcs = [ 8 | "a.ts", 9 | "b.ts", 10 | "sub/c.ts", 11 | ], 12 | out_dir = "out", 13 | source_maps = True, 14 | ) 15 | 16 | # Assert that the output of "compile" rule matches the expected file. 17 | write_source_files( 18 | name = "test", 19 | files = { 20 | "expected/a.js": ":out/a.js", 21 | "expected/b.js": ":out/b.js", 22 | "expected/sub/c.js": ":out/sub/c.js", 23 | }, 24 | ) 25 | 26 | [ 27 | assert_json_matches( 28 | name = "test_%s" % f.replace("/", "-"), 29 | file1 = ":out/%s.js.map" % f, 30 | file2 = "expected/%s.js.map.golden" % f, 31 | filter1 = ".sourceRoot,.sources", 32 | filter2 = ".sourceRoot,.sources", 33 | ) 34 | for f in [ 35 | "a", 36 | "b", 37 | "sub/c", 38 | ] 39 | ] 40 | -------------------------------------------------------------------------------- /examples/out_dir/a.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "a"; 2 | -------------------------------------------------------------------------------- /examples/out_dir/b.ts: -------------------------------------------------------------------------------- 1 | export const b: string = "b"; 2 | -------------------------------------------------------------------------------- /examples/out_dir/expected/a.js: -------------------------------------------------------------------------------- 1 | export var a = "a"; 2 | 3 | //# sourceMappingURL=a.js.map -------------------------------------------------------------------------------- /examples/out_dir/expected/a.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../a.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/out_dir/expected/b.js: -------------------------------------------------------------------------------- 1 | export var b = "b"; 2 | 3 | //# sourceMappingURL=b.js.map -------------------------------------------------------------------------------- /examples/out_dir/expected/b.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../b.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/out_dir/expected/sub/c.js: -------------------------------------------------------------------------------- 1 | export var c = "c"; 2 | 3 | //# sourceMappingURL=c.js.map -------------------------------------------------------------------------------- /examples/out_dir/expected/sub/c.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../../sub/c.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/out_dir/sub/c.ts: -------------------------------------------------------------------------------- 1 | export const c: string = "c"; 2 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "source-map-support": "^0.5.21" 5 | }, 6 | "devDependencies": { 7 | "typescript": "5.2.2" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/paths/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "typescript" 5 | }, 6 | "target": "es2021", 7 | "baseUrl": "./src", 8 | "paths": { 9 | "@modules/*": [ 10 | "./modules/*" 11 | ] 12 | } 13 | }, 14 | "module": { 15 | "type": "commonjs" 16 | }, 17 | "sourceMaps": true 18 | } 19 | -------------------------------------------------------------------------------- /examples/paths/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Path-alias use case for swc: specifying the paths options in .swcrc 2 | 3 | Note that this example also depends on the setup in /WORKSPACE at the root of this repository. 4 | """ 5 | 6 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 7 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 8 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 9 | load("@npm//examples:typescript/package_json.bzl", "bin") 10 | 11 | # Note: The .swcrc configuration file will be automatically used, if it is present in the current directory 12 | swc( 13 | name = "compile", 14 | srcs = glob(["**/*.ts"]), 15 | ) 16 | 17 | # Produce a folder of the transpiled outputs of `tsc`. 18 | # This is useful when reporting a bug to the SWC project, as their goal is to emit the same. 19 | # For example, https://github.com/swc-project/swc/issues/8265 20 | bin.tsc( 21 | name = "tsc", 22 | srcs = [ 23 | "src/index.ts", 24 | "tsconfig.json", 25 | ] + glob(["src/modules/**/*.ts"]), 26 | args = [ 27 | "--outDir", 28 | package_name() + "/tsc_out", 29 | "--listFiles", 30 | "-p", 31 | "$(execpath tsconfig.json)", 32 | ], 33 | out_dirs = [ 34 | "tsc_out", 35 | ], 36 | ) 37 | 38 | # Verify that the "paths" entry is agreed between swc and TS language service (in the editor) 39 | assert_json_matches( 40 | name = "check_paths", 41 | file1 = "tsconfig.json", 42 | file2 = ".swcrc", 43 | filter1 = ".compilerOptions.paths, .compilerOptions.baseUrl", 44 | filter2 = ".jsc.paths, .jsc.baseUrl", 45 | ) 46 | 47 | write_source_files( 48 | name = "test", 49 | files = { 50 | "expected.js": "src/index.js", 51 | "expected_tsc": ":tsc", 52 | }, 53 | ) 54 | -------------------------------------------------------------------------------- /examples/paths/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { 3 | value: true 4 | }); 5 | const _moduleA = require("./modules/moduleA"); 6 | (0, _moduleA.moduleA)(); 7 | -------------------------------------------------------------------------------- /examples/paths/expected_tsc/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var moduleA_1 = require("@modules/moduleA"); 4 | (0, moduleA_1.moduleA)(); 5 | -------------------------------------------------------------------------------- /examples/paths/expected_tsc/modules/moduleA/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.moduleA = void 0; 4 | var moduleB_1 = require("@modules/moduleB"); 5 | var moduleA = function () { 6 | console.log("This is module A"); 7 | (0, moduleB_1.moduleB)(); 8 | }; 9 | exports.moduleA = moduleA; 10 | -------------------------------------------------------------------------------- /examples/paths/expected_tsc/modules/moduleB/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.moduleB = void 0; 4 | var moduleB = function () { return console.log("This is module B!"); }; 5 | exports.moduleB = moduleB; 6 | -------------------------------------------------------------------------------- /examples/paths/src/index.ts: -------------------------------------------------------------------------------- 1 | import { moduleA } from "@modules/moduleA"; 2 | 3 | moduleA(); 4 | -------------------------------------------------------------------------------- /examples/paths/src/modules/moduleA/index.ts: -------------------------------------------------------------------------------- 1 | import { moduleB } from "@modules/moduleB"; 2 | 3 | export const moduleA = () => { 4 | console.log("This is module A"); 5 | moduleB(); 6 | }; 7 | -------------------------------------------------------------------------------- /examples/paths/src/modules/moduleB/index.ts: -------------------------------------------------------------------------------- 1 | export const moduleB = () => console.log("This is module B!"); 2 | -------------------------------------------------------------------------------- /examples/paths/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "paths": { 5 | "@modules/*": ["./modules/*"] 6 | } 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/plugins/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Examples showing how to use SWC plugins 2 | 3 | See https://github.com/swc-project/plugins 4 | """ 5 | 6 | load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") 7 | load("@aspect_bazel_lib//lib:directory_path.bzl", "directory_path") 8 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 9 | load("@aspect_rules_swc//swc:defs.bzl", "swc", "swc_plugin") 10 | load("@npm//:defs.bzl", "npm_link_all_packages") 11 | 12 | npm_link_all_packages(name = "node_modules") 13 | 14 | # Example 1: Typical usage 15 | # Loads a plugin from an npm package. 16 | # This requires that the package.json includes a main entrypoint pointing at the plugin wasm file. 17 | swc_plugin( 18 | name = "npm_plugin", 19 | srcs = [ 20 | # reference the location where the "@swc/plugin-transform-imports" npm package was linked in our root Bazel package. 21 | ":node_modules/@swc/plugin-transform-imports/dir", 22 | ], 23 | # optional plugin config, the JSON object for the plugin passed into jsc.experimental.plugins 24 | # https://swc.rs/docs/configuration/compilation#jscexperimentalplugins 25 | config = { 26 | "lodash": { 27 | "transform": "lodash/{{member}}", 28 | }, 29 | }, 30 | ) 31 | 32 | # Now we just pass our swc_plugin target to the plugins attribute: 33 | swc( 34 | name = "simple", 35 | srcs = ["in.ts"], 36 | out_dir = "simple", 37 | plugins = [":npm_plugin"], 38 | ) 39 | 40 | # Example 2 41 | # Instead of a plugin from npm, you could reference a wasm file directly. 42 | # This would make sense if you use a rust_binary target to build wasm output. 43 | # However, we don't have an example that uses rules_rust yet, 44 | # see https://github.com/aspect-build/rules_swc/issues/159 45 | # To illustrate the example, we'll just extract the wasm file from the same package we used in 46 | # the previous example. 47 | directory_path( 48 | name = "plugin_wasm_path", 49 | directory = ":node_modules/@swc/plugin-transform-imports/dir", 50 | path = "swc_plugin_transform_imports.wasm", 51 | ) 52 | 53 | copy_file( 54 | name = "plugin_wasm", 55 | src = ":plugin_wasm_path", 56 | out = "plugin.wasm", 57 | ) 58 | 59 | swc_plugin( 60 | name = "file_plugin", 61 | src = ":plugin_wasm", 62 | config = { 63 | "lodash2": { 64 | "transform": "lodash/test/{{member}}", 65 | }, 66 | }, 67 | ) 68 | 69 | # Illustrates that the plugins work with swcrc settings in BUILD.bazel. 70 | swc( 71 | name = "rcdict", 72 | srcs = ["in.ts"], 73 | out_dir = "rcdict", 74 | plugins = [ 75 | ":npm_plugin", 76 | ":file_plugin", 77 | ], 78 | swcrc = { 79 | "jsc": { 80 | "target": "es2015", 81 | }, 82 | }, 83 | ) 84 | 85 | # Illustrates that the plugins work when using a .swcrc file. 86 | swc( 87 | name = "rc", 88 | srcs = ["in.ts"], 89 | out_dir = "rc", 90 | plugins = [ 91 | ":npm_plugin", 92 | ":file_plugin", 93 | ], 94 | swcrc = "minify.swcrc", 95 | ) 96 | 97 | # Test that the output of the swc targets above match our "golden" files. 98 | write_source_files( 99 | name = "test", 100 | files = { 101 | "expected_simple.js_": ":simple/in.js", 102 | "expected_rc.js_": ":rc/in.js", 103 | "expected_rcdict.js_": ":rcdict/in.js", 104 | }, 105 | ) 106 | -------------------------------------------------------------------------------- /examples/plugins/expected_rc.js_: -------------------------------------------------------------------------------- 1 | import map from"lodash/map";import sort from"lodash/test/sort";var foo="bar";var _m=map;var _s=sort; -------------------------------------------------------------------------------- /examples/plugins/expected_rcdict.js_: -------------------------------------------------------------------------------- 1 | import map from "lodash/map"; 2 | import sort from "lodash/test/sort"; 3 | const foo = "bar"; 4 | let _m = map; 5 | let _s = sort; 6 | -------------------------------------------------------------------------------- /examples/plugins/expected_simple.js_: -------------------------------------------------------------------------------- 1 | import map from "lodash/map"; 2 | import { sort } from "lodash2"; 3 | var foo = "bar"; 4 | var _m = map; 5 | var _s = sort; 6 | -------------------------------------------------------------------------------- /examples/plugins/in.ts: -------------------------------------------------------------------------------- 1 | import { map } from "lodash"; 2 | import { sort } from "lodash2"; 3 | 4 | const foo = "bar"; 5 | let _m = map; 6 | let _s = sort; 7 | -------------------------------------------------------------------------------- /examples/plugins/minify.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "minify": true 3 | } 4 | -------------------------------------------------------------------------------- /examples/plugins/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "@swc/plugin-transform-imports": "^3.0.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | source-map-support: 12 | specifier: ^0.5.21 13 | version: 0.5.21 14 | devDependencies: 15 | typescript: 16 | specifier: 5.2.2 17 | version: 5.2.2 18 | 19 | generate_swcrc: 20 | devDependencies: 21 | tsconfig-to-swcconfig: 22 | specifier: ^2.4.0 23 | version: 2.4.0 24 | 25 | plugins: 26 | devDependencies: 27 | '@swc/plugin-transform-imports': 28 | specifier: ^3.0.0 29 | version: 3.0.4 30 | 31 | packages: 32 | 33 | /@fastify/deepmerge@1.3.0: 34 | resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} 35 | dev: true 36 | 37 | /@swc/counter@0.1.3: 38 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 39 | dev: true 40 | 41 | /@swc/plugin-transform-imports@3.0.4: 42 | resolution: {integrity: sha512-ubmNyCgaXtRNstdU/rv33QGFt6i4YZM9mL1IrrMkVbCVC+2xFNN6SKaJUdcNHkzgLWumK8H29Xman3CAOtR1Jw==} 43 | dependencies: 44 | '@swc/counter': 0.1.3 45 | dev: true 46 | 47 | /buffer-from@1.1.2: 48 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 49 | dev: false 50 | 51 | /joycon@3.1.1: 52 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 53 | engines: {node: '>=10'} 54 | dev: true 55 | 56 | /jsonc-parser@3.2.0: 57 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 58 | dev: true 59 | 60 | /source-map-support@0.5.21: 61 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 62 | dependencies: 63 | buffer-from: 1.1.2 64 | source-map: 0.6.1 65 | dev: false 66 | 67 | /source-map@0.6.1: 68 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 69 | engines: {node: '>=0.10.0'} 70 | dev: false 71 | 72 | /tsconfig-to-swcconfig@2.4.0: 73 | resolution: {integrity: sha512-vT1hUG06TC3XCRQoina91VGH5HKfxYgA4hN2Ly0uNxHKNeCds++7aBE2Je62FUTKvvdkTpLjmynF8bzgYcDymA==} 74 | hasBin: true 75 | dependencies: 76 | '@fastify/deepmerge': 1.3.0 77 | joycon: 3.1.1 78 | jsonc-parser: 3.2.0 79 | dev: true 80 | 81 | /typescript@5.2.2: 82 | resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} 83 | engines: {node: '>=14.17'} 84 | hasBin: true 85 | dev: true 86 | -------------------------------------------------------------------------------- /examples/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - generate_swcrc 3 | - plugins 4 | -------------------------------------------------------------------------------- /examples/rc/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsc": { 3 | "parser": { 4 | "syntax": "typescript", 5 | "tsx": false, 6 | "decorators": true, 7 | "dynamicImport": false 8 | }, 9 | "transform": { 10 | "legacyDecorator": true, 11 | "decoratorMetadata": true 12 | }, 13 | "target": "es2016" 14 | }, 15 | "module": { 16 | "type": "commonjs", 17 | "strict": false, 18 | "strictMode": true, 19 | "lazy": false, 20 | "noInterop": false 21 | }, 22 | "sourceMaps": true 23 | } 24 | -------------------------------------------------------------------------------- /examples/rc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | exports_files([".swcrc"]) 2 | -------------------------------------------------------------------------------- /examples/rc/src/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 2 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 3 | 4 | # Runs `swc in.ts > ../../bazel-bin/examples/simple/in.js` 5 | # You can run `bazel build --subcommands //examples/simple:compile` 6 | # to see the exact command line Bazel runs. 7 | swc( 8 | name = "compile", 9 | srcs = ["in.ts"], 10 | source_maps = True, 11 | swcrc = "//examples/rc:.swcrc", 12 | ) 13 | 14 | write_source_files( 15 | name = "tests", 16 | files = { 17 | "expected.js": ":in.js", 18 | "expected.js.map": ":in.js.map", 19 | }, 20 | ) 21 | -------------------------------------------------------------------------------- /examples/rc/src/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { 3 | value: true 4 | }); 5 | Object.defineProperty(exports, "a", { 6 | enumerable: true, 7 | get: function() { 8 | return a; 9 | } 10 | }); 11 | const a = "foo"; 12 | 13 | //# sourceMappingURL=in.js.map -------------------------------------------------------------------------------- /examples/rc/src/expected.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["in.ts"],"sourcesContent":["export const a: string = \"foo\";\n"],"names":["a"],"mappings":";;;;+BAAaA;;;eAAAA;;;AAAN,MAAMA,IAAY"} -------------------------------------------------------------------------------- /examples/rc/src/in.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "foo"; 2 | -------------------------------------------------------------------------------- /examples/rcdict/BUILD.bazel: -------------------------------------------------------------------------------- 1 | "Showcases how to specify the SWC configuration file as a dict" 2 | 3 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 4 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 5 | 6 | swc( 7 | name = "es5", 8 | srcs = ["in.ts"], 9 | out_dir = "es5", 10 | swcrc = { 11 | "jsc": { 12 | "target": "es5", 13 | }, 14 | }, 15 | ) 16 | 17 | swc( 18 | name = "es2015", 19 | srcs = ["in.ts"], 20 | out_dir = "es2015", 21 | swcrc = { 22 | "jsc": { 23 | "target": "es2015", 24 | }, 25 | }, 26 | ) 27 | 28 | write_source_files( 29 | name = "test", 30 | files = { 31 | "expected_es5.js": ":es5/in.js", 32 | "expected_es2015.js": ":es2015/in.js", 33 | }, 34 | ) 35 | -------------------------------------------------------------------------------- /examples/rcdict/expected_es2015.js: -------------------------------------------------------------------------------- 1 | const foo = "bar"; 2 | -------------------------------------------------------------------------------- /examples/rcdict/expected_es5.js: -------------------------------------------------------------------------------- 1 | var foo = "bar"; 2 | -------------------------------------------------------------------------------- /examples/rcdict/in.ts: -------------------------------------------------------------------------------- 1 | const foo = "bar"; 2 | -------------------------------------------------------------------------------- /examples/resolve_json_module/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_js//js:defs.bzl", "js_test") 2 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 3 | 4 | swc( 5 | name = "ts", 6 | srcs = [ 7 | "data.json", 8 | "index.ts", 9 | ], 10 | args = [ 11 | "--config-json", 12 | """{"module": {"type": "commonjs"}}""", 13 | ], 14 | ) 15 | 16 | swc( 17 | name = "ts-out_dir", 18 | srcs = [ 19 | "data.json", 20 | "index.ts", 21 | ], 22 | args = [ 23 | "--config-json", 24 | """{"module": {"type": "commonjs"}}""", 25 | ], 26 | out_dir = "ts-out_dir", 27 | ) 28 | 29 | js_test( 30 | # Test that the json is available at runtime. 31 | name = "ts-with-json", 32 | data = [":ts"], 33 | entry_point = "index.js", 34 | ) 35 | 36 | js_test( 37 | # Test that the json is available at runtime with an out_dir. 38 | name = "ts-with-json-out_dir", 39 | data = [":ts-out_dir"], 40 | entry_point = "ts-out_dir/index.js", 41 | ) 42 | -------------------------------------------------------------------------------- /examples/resolve_json_module/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "a": "b" 4 | } 5 | ] 6 | -------------------------------------------------------------------------------- /examples/resolve_json_module/index.ts: -------------------------------------------------------------------------------- 1 | import data from "./data.json"; 2 | export const a: string = "hello" + JSON.stringify(data); 3 | -------------------------------------------------------------------------------- /examples/root_dir/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 2 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 3 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 4 | 5 | swc( 6 | name = "compile", 7 | srcs = [ 8 | "src/a.ts", 9 | "src/b.ts", 10 | "src/sub/c.ts", 11 | ], 12 | out_dir = "out", 13 | root_dir = "src", 14 | source_maps = True, 15 | ) 16 | 17 | # Test case for root_dir="." (should have depth 0) 18 | swc( 19 | name = "compile_dot", 20 | srcs = [ 21 | "src/a.ts", 22 | "src/b.ts", 23 | "src/sub/c.ts", 24 | ], 25 | out_dir = "out_dot", 26 | root_dir = ".", 27 | source_maps = True, 28 | ) 29 | 30 | # Test case for nested root_dir 31 | swc( 32 | name = "compile_nested", 33 | srcs = [ 34 | "src/sub/c.ts", 35 | ], 36 | out_dir = "out_nested", 37 | root_dir = "src/sub", 38 | source_maps = True, 39 | ) 40 | 41 | # Assert that the output of "compile" rule matches the expected file. 42 | write_source_files( 43 | name = "test", 44 | files = { 45 | "expected/a.js": ":out/a.js", 46 | "expected/b.js": ":out/b.js", 47 | }, 48 | ) 49 | 50 | # Assert outputs for dot root_dir test 51 | write_source_files( 52 | name = "test_dot", 53 | files = { 54 | "expected_dot/src/a.js": ":out_dot/src/a.js", 55 | "expected_dot/src/b.js": ":out_dot/src/b.js", 56 | "expected_dot/src/sub/c.js": ":out_dot/src/sub/c.js", 57 | }, 58 | ) 59 | 60 | # Assert outputs for nested root_dir test 61 | write_source_files( 62 | name = "test_nested", 63 | files = { 64 | "expected_nested/c.js": ":out_nested/c.js", 65 | }, 66 | ) 67 | 68 | [ 69 | assert_json_matches( 70 | name = "test_%s" % f.replace("/", "-"), 71 | file1 = ":out/%s.js.map" % f, 72 | file2 = "expected/%s.js.map.golden" % f, 73 | filter1 = ".sourceRoot,.sources", 74 | filter2 = ".sourceRoot,.sources", 75 | ) 76 | for f in [ 77 | "a", 78 | "b", 79 | "sub/c", 80 | ] 81 | ] 82 | 83 | [ 84 | assert_json_matches( 85 | name = "test_dot_%s" % f.replace("/", "-"), 86 | file1 = ":out_dot/%s.js.map" % f, 87 | file2 = "expected_dot/%s.js.map.golden" % f, 88 | filter1 = ".sourceRoot,.sources", 89 | filter2 = ".sourceRoot,.sources", 90 | ) 91 | for f in [ 92 | "src/a", 93 | "src/b", 94 | "src/sub/c", 95 | ] 96 | ] 97 | 98 | assert_json_matches( 99 | name = "test_nested_c", 100 | file1 = ":out_nested/c.js.map", 101 | file2 = "expected_nested/c.js.map.golden", 102 | filter1 = ".sourceRoot,.sources", 103 | filter2 = ".sourceRoot,.sources", 104 | ) 105 | -------------------------------------------------------------------------------- /examples/root_dir/expected/a.js: -------------------------------------------------------------------------------- 1 | export var a = "a"; 2 | 3 | //# sourceMappingURL=a.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected/a.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../src/a.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected/b.js: -------------------------------------------------------------------------------- 1 | export var b = "b"; 2 | 3 | //# sourceMappingURL=b.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected/b.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../src/b.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected/sub/c.js: -------------------------------------------------------------------------------- 1 | export var c = "c"; 2 | 3 | //# sourceMappingURL=c.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected/sub/c.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../../src/sub/c.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/a.js: -------------------------------------------------------------------------------- 1 | export var a = "a"; 2 | 3 | //# sourceMappingURL=a.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/a.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../../src/a.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/b.js: -------------------------------------------------------------------------------- 1 | export var b = "b"; 2 | 3 | //# sourceMappingURL=b.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/b.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../../src/b.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/sub/c.js: -------------------------------------------------------------------------------- 1 | export var c = "c"; 2 | 3 | //# sourceMappingURL=c.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected_dot/src/sub/c.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../../../src/sub/c.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/expected_nested/c.js: -------------------------------------------------------------------------------- 1 | export var c = "c"; 2 | 3 | //# sourceMappingURL=c.js.map -------------------------------------------------------------------------------- /examples/root_dir/expected_nested/c.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "../src/sub/c.ts" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/root_dir/src/a.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "a"; 2 | -------------------------------------------------------------------------------- /examples/root_dir/src/b.ts: -------------------------------------------------------------------------------- 1 | export const b: string = "b"; 2 | -------------------------------------------------------------------------------- /examples/root_dir/src/sub/c.ts: -------------------------------------------------------------------------------- 1 | export const c: string = "c"; 2 | -------------------------------------------------------------------------------- /examples/simple/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Simple use case for swc: transpiling TypeScript using the `swc` rule 2 | 3 | Note that this example also depends on the setup in /WORKSPACE at the root of this repository. 4 | """ 5 | 6 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 7 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 8 | 9 | # Runs `swc in.ts > ../../bazel-bin/examples/simple/in.js` 10 | # You can run `bazel build --subcommands //examples/simple:compile` 11 | # to see the exact command line Bazel runs. 12 | swc( 13 | name = "compile", 14 | srcs = ["in.ts"], 15 | ) 16 | 17 | # Assert that the output of "compile" rule matches the expected file. 18 | write_source_files( 19 | name = "test", 20 | # This is a pre-declared output of the "compile" rule, so we can refer to it directly using a Bazel label 21 | # even though the file itself is generated by Bazel in ../../bazel-bin/examples/simple/in.js 22 | files = {"expected.js": ":in.js"}, 23 | ) 24 | -------------------------------------------------------------------------------- /examples/simple/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "simple"; 2 | -------------------------------------------------------------------------------- /examples/simple/in.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "simple"; 2 | -------------------------------------------------------------------------------- /examples/source_map_support/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin") 2 | load("@bazel_skylib//:bzl_library.bzl", "bzl_library") 3 | 4 | exports_files([ 5 | "defs.bzl", 6 | ]) 7 | 8 | copy_to_bin( 9 | name = "stack-trace-support", 10 | srcs = ["stack-trace-support.js"], 11 | visibility = ["//visibility:public"], 12 | ) 13 | 14 | bzl_library( 15 | name = "defs", 16 | srcs = ["defs.bzl"], 17 | visibility = ["//visibility:public"], 18 | deps = ["@aspect_rules_js//js:defs"], 19 | ) 20 | -------------------------------------------------------------------------------- /examples/source_map_support/defs.bzl: -------------------------------------------------------------------------------- 1 | """ 2 | Macro wrappers around rules_js's `js_binary` and `js_test` that improve the DX of stack traces by automatically 3 | registering source-map-support and removing the runfiles directory prefix. 4 | 5 | Use them wherever you would use rules_js's `js_binary` and `js_test`. 6 | """ 7 | 8 | load("@aspect_rules_js//js:defs.bzl", _js_binary = "js_binary", _js_test = "js_test") 9 | 10 | def js_binary(data = [], chdir = None, node_options = [], **kwargs): 11 | rel = "./" 12 | if chdir: 13 | rel = rel + "/".join([".." for _ in chdir.split("/")]) + "/" 14 | 15 | _js_binary( 16 | chdir = chdir, 17 | data = [ 18 | "//examples:node_modules/source-map-support", 19 | "//examples/source_map_support:stack-trace-support", 20 | ] + data, 21 | node_options = ["--require", rel + "$(rootpath //examples/source_map_support:stack-trace-support)"] + node_options, 22 | **kwargs 23 | ) 24 | 25 | def js_test(data = [], chdir = None, node_options = [], **kwargs): 26 | rel = "./" 27 | if chdir: 28 | rel = rel + "/".join([".." for _ in chdir.split("/")]) + "/" 29 | 30 | _js_test( 31 | chdir = chdir, 32 | data = [ 33 | "//examples:node_modules/source-map-support", 34 | "//examples/source_map_support:stack-trace-support", 35 | ] + data, 36 | node_options = ["--require", rel + "$(rootpath //examples/source_map_support:stack-trace-support)"] + node_options, 37 | **kwargs 38 | ) 39 | -------------------------------------------------------------------------------- /examples/source_map_support/stack-trace-support.js: -------------------------------------------------------------------------------- 1 | // See defs.bzl for where this is used and what it does. 2 | 3 | require('source-map-support/register') 4 | 5 | let basePath = process.env.RUNFILES 6 | ? `${process.env.RUNFILES}/${process.env.JS_BINARY__WORKSPACE}` 7 | : process.cwd() 8 | 9 | if (!basePath.endsWith('/')) { 10 | basePath = basePath + '/' 11 | } 12 | 13 | /* 14 | Before: 15 | Error: test 16 | at foo (/private/var/tmp/_bazel_john/67beefda950d56283b98d96980e6e332/execroot/figma/bazel-out/darwin_arm64-fastbuild/bin/bazel/js/test/stack_trace_support.sh.runfiles/figma/bazel/js/test/b.js:2:11) 17 | at Object. (/private/var/tmp/_bazel_john/67beefda950d56283b98d96980e6e332/execroot/figma/bazel-out/darwin_arm64-fastbuild/bin/bazel/js/test/stack_trace_support.sh.runfiles/figma/bazel/js/test/a.js:4:1) 18 | ... 19 | 20 | After: 21 | Error: test 22 | at foo (bazel/js/test/b.ts:2:9) 23 | at Object. (bazel/js/test/a.ts:5:1) 24 | ... 25 | */ 26 | 27 | const basePathRegex = new RegExp( 28 | `(at | \\()${basePath 29 | .replace(/\\/g, '/') 30 | // Escape regex meta-characters. 31 | .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') 32 | .replace(/-/g, '\\x2d')}`, 33 | 'g', 34 | ) 35 | 36 | const prepareStackTrace = Error.prepareStackTrace 37 | Error.prepareStackTrace = function (error, stack) { 38 | return prepareStackTrace(error, stack) 39 | .split('\n') 40 | .map((line) => line.replace(basePathRegex, '$1')) 41 | .join('\n') 42 | } 43 | -------------------------------------------------------------------------------- /examples/source_map_support/test/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | load("//examples/source_map_support:defs.bzl", "js_test") 3 | 4 | swc( 5 | name = "compile", 6 | srcs = [ 7 | "a.ts", 8 | "b.ts", 9 | ], 10 | source_maps = True, 11 | ) 12 | 13 | js_test( 14 | name = "stack_trace_support_test", 15 | data = [":compile"], 16 | entry_point = ":a.js", 17 | target_compatible_with = select({ 18 | # TODO(jbedard): fix CI failure, like 19 | # https://github.com/aspect-build/rules_swc/actions/runs/4471322159/jobs/7856057314?pr=187 20 | "@platforms//os:windows": ["@platforms//:incompatible"], 21 | "//conditions:default": [], 22 | }), 23 | ) 24 | 25 | js_test( 26 | name = "stack_trace_support_with_chdir_test", 27 | chdir = "examples", 28 | data = [":compile"], 29 | entry_point = ":a.js", 30 | target_compatible_with = select({ 31 | # TODO(jbedard): fix CI failure, like 32 | # https://github.com/aspect-build/rules_swc/actions/runs/4471322159/jobs/7856057314?pr=187 33 | "@platforms//os:windows": ["@platforms//:incompatible"], 34 | "//conditions:default": [], 35 | }), 36 | ) 37 | -------------------------------------------------------------------------------- /examples/source_map_support/test/a.ts: -------------------------------------------------------------------------------- 1 | try { 2 | require('./b')() 3 | } catch (e) { 4 | const assert = require('assert') 5 | const frames = e.stack 6 | .split('\n') 7 | .slice(1) 8 | .map((s) => s.trim()) 9 | assert.deepEqual( 10 | frames.filter((f) => f.includes('source_map_support/test/a')), 11 | [`at Object. (examples/source_map_support/test/a.ts:2:11)`], 12 | ) 13 | assert.deepEqual( 14 | frames.filter((f) => f.includes('source_map_support/test/b')), 15 | [`at foo (examples/source_map_support/test/b.ts:2:9)`], 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /examples/source_map_support/test/b.ts: -------------------------------------------------------------------------------- 1 | module.exports = function foo() { 2 | throw new Error('test') 3 | } 4 | -------------------------------------------------------------------------------- /examples/source_root/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches") 2 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 3 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 4 | 5 | # Specifies a custom location where a debugger should locate source files instead of relative source 6 | # locations. This string is treated verbatim inside the source-map where you can use a path or a URL. 7 | swc( 8 | name = "compile", 9 | srcs = ["in.ts"], 10 | source_maps = True, 11 | # The custom location can an URL 12 | source_root = "https://my-website.com/debug/source/", 13 | ) 14 | 15 | swc( 16 | name = "compile_subdir", 17 | srcs = ["src/subdir.ts"], 18 | root_dir = "src", 19 | source_maps = True, 20 | # The custom location can be a path 21 | source_root = "../../../debug/source", 22 | ) 23 | 24 | # Assert that the output of "compile" rule matches the expected file. 25 | write_source_files( 26 | name = "test", 27 | files = { 28 | "expected.js": ":in.js", 29 | }, 30 | ) 31 | 32 | assert_json_matches( 33 | name = "test_map", 34 | file1 = "in.js.map", 35 | file2 = "expected.js.map.golden", 36 | filter1 = ".sourceRoot,.sources", 37 | filter2 = ".sourceRoot,.sources", 38 | ) 39 | 40 | # Assert that the output of "compile_subdir" rule matches the expected file. 41 | write_source_files( 42 | name = "test_subdir", 43 | files = { 44 | "expected_subdir.js": ":subdir.js", 45 | }, 46 | ) 47 | 48 | assert_json_matches( 49 | name = "test_subdir_map", 50 | file1 = "subdir.js.map", 51 | file2 = "expected_subdir.js.map.golden", 52 | filter1 = ".sourceRoot,.sources", 53 | filter2 = ".sourceRoot,.sources", 54 | ) 55 | -------------------------------------------------------------------------------- /examples/source_root/expected.js: -------------------------------------------------------------------------------- 1 | export var a = "foo"; 2 | 3 | //# sourceMappingURL=in.js.map -------------------------------------------------------------------------------- /examples/source_root/expected.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "in.ts" 4 | ], 5 | "sourceRoot": "https://my-website.com/debug/source/" 6 | } -------------------------------------------------------------------------------- /examples/source_root/expected_subdir.js: -------------------------------------------------------------------------------- 1 | export var a = "a"; 2 | 3 | //# sourceMappingURL=subdir.js.map -------------------------------------------------------------------------------- /examples/source_root/expected_subdir.js.map.golden: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | "src/subdir.ts" 4 | ], 5 | "sourceRoot": "../../../debug/source" 6 | } -------------------------------------------------------------------------------- /examples/source_root/in.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "foo"; 2 | -------------------------------------------------------------------------------- /examples/source_root/src/subdir.ts: -------------------------------------------------------------------------------- 1 | export const a: string = "a"; 2 | -------------------------------------------------------------------------------- /examples/transitive/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "type": "commonjs" 4 | } 5 | } -------------------------------------------------------------------------------- /examples/transitive/BUILD.bazel: -------------------------------------------------------------------------------- 1 | """Test transitive dependencies between typescript files 2 | which are compiled separately as "libraries" but which 3 | have runtime dependencies. 4 | """ 5 | 6 | load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") 7 | 8 | exports_files([".swcrc"]) 9 | 10 | genrule( 11 | name = "run", 12 | outs = ["actual"], 13 | cmd = "BAZEL_BINDIR=$(BINDIR) $(location //examples/transitive/app:bin) > $@", 14 | tools = ["//examples/transitive/app:bin"], 15 | ) 16 | 17 | # Assert the output of the program matches the expected file 18 | write_source_files( 19 | name = "assertion", 20 | files = { 21 | "expected": "actual", 22 | }, 23 | ) 24 | -------------------------------------------------------------------------------- /examples/transitive/app/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_js//js:defs.bzl", "js_binary") 2 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 3 | 4 | package(default_visibility = ["//:__subpackages__"]) 5 | 6 | swc( 7 | name = "app", 8 | srcs = ["a.ts"], 9 | # In Bazel, a "data" dependency is a runtime dependency. 10 | # This file isn't required to transpile b.ts -> b.js 11 | # however any program which depends on lib_b should have 12 | # lib_c available in the "runfiles". 13 | data = ["//examples/transitive/lib_b"], 14 | swcrc = "//examples/transitive:.swcrc", 15 | ) 16 | 17 | js_binary( 18 | name = "bin", 19 | data = [":app"], 20 | entry_point = "a.js", 21 | ) 22 | -------------------------------------------------------------------------------- /examples/transitive/app/a.ts: -------------------------------------------------------------------------------- 1 | import { b } from "../lib_b/b"; 2 | 3 | console.log("output of app:", b); 4 | -------------------------------------------------------------------------------- /examples/transitive/expected: -------------------------------------------------------------------------------- 1 | output of app: 3 2 | -------------------------------------------------------------------------------- /examples/transitive/lib_b/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | 3 | package(default_visibility = ["//:__subpackages__"]) 4 | 5 | swc( 6 | name = "lib_b", 7 | srcs = ["b.ts"], 8 | # In Bazel, a "data" dependency is a runtime dependency. 9 | # This file isn't required to transpile b.ts -> b.js 10 | # however any program which depends on lib_b should have 11 | # lib_c available in the "runfiles". 12 | data = ["//examples/transitive/lib_c"], 13 | swcrc = "//examples/transitive:.swcrc", 14 | ) 15 | -------------------------------------------------------------------------------- /examples/transitive/lib_b/b.ts: -------------------------------------------------------------------------------- 1 | import { c } from "../lib_c/c"; 2 | 3 | export const b = 2 + c; 4 | -------------------------------------------------------------------------------- /examples/transitive/lib_c/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 2 | 3 | package(default_visibility = ["//:__subpackages__"]) 4 | 5 | swc( 6 | name = "lib_c", 7 | srcs = ["c.ts"], 8 | swcrc = "//examples/transitive:.swcrc", 9 | ) 10 | -------------------------------------------------------------------------------- /examples/transitive/lib_c/c.ts: -------------------------------------------------------------------------------- 1 | export const c = 1; 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | 4 | "extends": [ 5 | "config:base", 6 | ":preserveSemverRanges", 7 | ":rebaseStalePrs", 8 | "schedule:weekly", 9 | "group:recommended", 10 | "group:monorepos", 11 | "workarounds:all" 12 | ], 13 | 14 | "labels": ["deps"], 15 | "dependencyDashboard": true 16 | } 17 | -------------------------------------------------------------------------------- /scripts/filter.jq: -------------------------------------------------------------------------------- 1 | map(select(.tag_name | contains("nightly") | not)) 2 | | 3 | map( 4 | { 5 | "key": .tag_name, 6 | "value": .assets 7 | | map({ 8 | # filter out the node bindings and convert swc-linux-x64-gnu -> linux-x64-gnu 9 | "key": .name | select((contains(".node") | not) and (contains("musl") | not)) | split("swc-")[1], 10 | # We'll replace the url with the shasum of that referenced file in a later processing step 11 | "value": .browser_download_url 12 | }) 13 | | from_entries 14 | } 15 | ) | from_entries -------------------------------------------------------------------------------- /scripts/mirror_releases.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit -o nounset -o pipefail 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | VERSIONS_BZL="$SCRIPT_DIR/../swc/private/versions.bzl" 7 | 8 | releases=$(curl -sSL -H 'Accept: application/vnd.github.v3+json' https://api.github.com/repos/swc-project/swc/releases?per_page=20 | jq -f "$SCRIPT_DIR/filter.jq") 9 | versions=$(echo $releases | jq --raw-output 'keys[]') 10 | 11 | # Combine the new versions with the existing ones. 12 | # New versions should appear first, but existing content should overwrite new 13 | OUT=$(mktemp) 14 | python3 -c "import json; exec(open('$VERSIONS_BZL').read()); print(json.dumps(TOOL_VERSIONS))" > $OUT 15 | 16 | # Sadly swc doesn't publish the checksums for their releases, so we have to compute them ourselves 17 | EACH_VERSION=$(mktemp) 18 | for version in $versions; do 19 | assets=$(echo $releases | jq --raw-output --arg v "$version" '.["\($v)"] | keys[]') 20 | 21 | ASSETS_FOR_VERSION="" 22 | for asset in $assets; do 23 | url=$(echo $releases | jq --raw-output --arg v "$version" --arg a "$asset" '.["\($v)"] | .["\($a)"]') 24 | sha=$(curl -sSL $url | shasum -b -a 384 | awk "{ print \$1 }" | xxd -r -p | base64) 25 | ASSETS_FOR_VERSION="${ASSETS_FOR_VERSION} \"${asset%.exe}\": \"sha384-${sha}\"," 26 | done 27 | 28 | # Remove final trailing comma so it's valid JSON 29 | ASSETS_FOR_VERSION=${ASSETS_FOR_VERSION%,} 30 | echo "{\"$version\": { ${ASSETS_FOR_VERSION} }}" >$EACH_VERSION 31 | 32 | TMP=$(mktemp) 33 | jq --slurp '.[0] * .[1]' $EACH_VERSION $OUT > $TMP 34 | mv $TMP $OUT 35 | done 36 | 37 | # Locate the TOOL_VERSIONS declaration in the source file and replace it 38 | NEW=$(mktemp) 39 | sed '/TOOL_VERSIONS =/Q' $VERSIONS_BZL > $NEW 40 | echo -n "TOOL_VERSIONS = " >> $NEW 41 | cat $OUT >> $NEW 42 | cat $NEW 43 | cp $NEW $VERSIONS_BZL 44 | -------------------------------------------------------------------------------- /swc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:utils.bzl", bazel_lib_utils = "utils") 2 | load("@bazel_skylib//:bzl_library.bzl", "bzl_library") 3 | load("//swc/private:resolved_toolchain.bzl", "resolved_toolchain") 4 | 5 | # For stardoc to reference the files 6 | exports_files([ 7 | "defs.bzl", 8 | "repositories.bzl", 9 | ]) 10 | 11 | # This is the target rule authors should put in their "toolchains" 12 | # attribute in order to get a runtime for the correct platform. 13 | # See https://docs.bazel.build/versions/main/toolchains.html#writing-rules-that-use-toolchains 14 | toolchain_type( 15 | name = "toolchain_type", 16 | visibility = ["//visibility:public"], 17 | ) 18 | 19 | resolved_toolchain( 20 | name = "resolved_toolchain", 21 | # Marked manual so that `bazel test //...` passes 22 | # even if no toolchain is registered. 23 | tags = ["manual"], 24 | visibility = ["//visibility:public"], 25 | ) 26 | 27 | bzl_library( 28 | name = "repositories", 29 | srcs = ["repositories.bzl"], 30 | visibility = ["//visibility:public"], 31 | deps = [ 32 | "//swc/private:toolchains_repo", 33 | "//swc/private:versions", 34 | "@bazel_skylib//lib:versions", 35 | ], 36 | ) 37 | 38 | bzl_library( 39 | name = "dependencies", 40 | srcs = ["dependencies.bzl"], 41 | visibility = ["//visibility:public"], 42 | deps = [ 43 | "@bazel_tools//tools/build_defs/repo:http.bzl", 44 | "@bazel_tools//tools/build_defs/repo:utils.bzl", 45 | ] + (["@bazel_tools//tools/build_defs/repo:cache.bzl"] if bazel_lib_utils.is_bazel_7_or_greater() else []), 46 | ) 47 | 48 | bzl_library( 49 | name = "defs", 50 | srcs = ["defs.bzl"], 51 | visibility = ["//visibility:public"], 52 | deps = [ 53 | "//swc/private:swc", 54 | "//swc/private:swc_plugin", 55 | "@aspect_bazel_lib//lib:copy_file", 56 | "@bazel_skylib//lib:types", 57 | "@bazel_skylib//rules:write_file", 58 | ] + (["@bazel_tools//tools/build_defs/repo:cache.bzl"] if bazel_lib_utils.is_bazel_7_or_greater() else []), 59 | ) 60 | 61 | bzl_library( 62 | name = "extensions", 63 | srcs = ["extensions.bzl"], 64 | visibility = ["//visibility:public"], 65 | deps = [":repositories"], 66 | ) 67 | 68 | bzl_library( 69 | name = "providers", 70 | srcs = ["providers.bzl"], 71 | visibility = ["//visibility:public"], 72 | ) 73 | 74 | bzl_library( 75 | name = "toolchain", 76 | srcs = ["toolchain.bzl"], 77 | visibility = ["//visibility:public"], 78 | ) 79 | -------------------------------------------------------------------------------- /swc/defs.bzl: -------------------------------------------------------------------------------- 1 | """API for running the SWC cli under Bazel 2 | 3 | The simplest usage relies on the `swcrc` attribute automatically discovering `.swcrc`: 4 | 5 | ```starlark 6 | load("@aspect_rules_swc//swc:defs.bzl", "swc") 7 | 8 | swc( 9 | name = "compile", 10 | srcs = ["file.ts"], 11 | ) 12 | ``` 13 | """ 14 | 15 | load("@aspect_bazel_lib//lib:utils.bzl", "file_exists", "to_label") 16 | load("@bazel_skylib//lib:types.bzl", "types") 17 | load("@bazel_skylib//rules:write_file.bzl", "write_file") 18 | load("//swc/private:swc.bzl", _swc_lib = "swc") 19 | load("//swc/private:swc_plugin.bzl", _swc_plugin_lib = "swc_plugin") 20 | 21 | swc_compile = rule( 22 | doc = """Underlying rule for the `swc` macro. 23 | 24 | Most users should use [swc](#swc) instead, as it predicts the output files 25 | and has convenient default values. 26 | 27 | Use this if you need more control over how the rule is called, 28 | for example to set your own output labels for `js_outs`. 29 | """, 30 | implementation = _swc_lib.implementation, 31 | attrs = _swc_lib.attrs, 32 | toolchains = _swc_lib.toolchains, 33 | ) 34 | 35 | def swc(name, srcs, args = [], data = [], plugins = [], output_dir = False, swcrc = None, source_maps = False, out_dir = None, root_dir = None, default_ext = ".js", allow_js = True, **kwargs): 36 | """Execute the SWC compiler 37 | 38 | Args: 39 | name: A name for this target 40 | 41 | srcs: List of labels of TypeScript source files. 42 | 43 | data: Files needed at runtime by binaries or tests that transitively depend on this target. 44 | See https://bazel.build/reference/be/common-definitions#typical-attributes 45 | 46 | output_dir: Whether to produce a directory output rather than individual files. 47 | 48 | If `out_dir` is set, then that is used as the name of the output directory. 49 | Otherwise, the output directory is named the same as the target. 50 | 51 | args: Additional options to pass to `swcx` cli, see https://github.com/swc-project/swc/discussions/3859 52 | Note: we do **not** run the [NodeJS wrapper `@swc/cli`](https://swc.rs/docs/usage/cli) 53 | 54 | source_maps: If set, the --source-maps argument is passed to the SWC cli with the value. 55 | See https://swc.rs/docs/usage/cli#--source-maps--s. 56 | True/False are automaticaly converted to "true"/"false" string values the cli expects. 57 | 58 | swcrc: Label of a .swcrc configuration file for the SWC cli, see https://swc.rs/docs/configuration/swcrc 59 | Instead of a label, you can pass a dictionary matching the JSON schema. 60 | If this attribute isn't specified, and a file `.swcrc` exists in the same folder as this rule, it is used. 61 | 62 | Note that some settings in `.swcrc` also appear in `tsconfig.json`. 63 | See the notes in [/docs/tsconfig.md]. 64 | 65 | plugins: List of plugin labels created with `swc_plugin`. 66 | 67 | out_dir: The base directory for output files relative to the output directory for this package. 68 | 69 | If output_dir is True, then this is used as the name of the output directory. 70 | 71 | root_dir: A subdirectory under the input package which should be considered the root directory of all the input files 72 | 73 | default_ext: The default extension to use for output files. If not set, the default is ".js". 74 | 75 | allow_js: If `True` (default), then .js/.mjs/.cjs input files are transpiled. If `False`, 76 | they are ignored. This can be used to mimic the behavior of tsc when using `ts_project(transpiler)`. 77 | 78 | **kwargs: additional keyword arguments passed through to underlying [`swc_compile`](#swc_compile), eg. `visibility`, `tags` 79 | """ 80 | if not types.is_list(srcs): 81 | fail("srcs must be a list, not a " + type(srcs)) 82 | 83 | if swcrc == None: 84 | if file_exists(to_label(":.swcrc")): 85 | swcrc = to_label(":.swcrc") 86 | elif type(swcrc) == type(dict()): 87 | swcrc.setdefault("sourceMaps", source_maps) 88 | rcfile = "{}_swcrc.json".format(name) 89 | write_file( 90 | name = "_gen_swcrc_" + name, 91 | out = rcfile, 92 | content = [json.encode(swcrc)], 93 | ) 94 | 95 | # From here, the configuration becomes a file path, the same as if the 96 | # user supplied a .swcrc InputArtifact 97 | swcrc = rcfile 98 | 99 | # Convert source_maps True/False to "true"/"false" args value 100 | if source_maps == True: 101 | source_maps = "true" 102 | elif source_maps == False: 103 | source_maps = "false" 104 | 105 | # Determine js & map outputs 106 | js_outs = [] 107 | map_outs = [] 108 | dts_outs = [] 109 | 110 | if not output_dir: 111 | js_outs = _swc_lib.calculate_js_outs(default_ext, srcs, allow_js, out_dir, root_dir) 112 | map_outs = _swc_lib.calculate_map_outs(default_ext, srcs, source_maps, allow_js, out_dir, root_dir) 113 | dts_outs = _swc_lib.calculate_dts_outs(srcs, kwargs.get("emit_isolated_dts", False), allow_js, out_dir, root_dir) 114 | 115 | swc_compile( 116 | name = name, 117 | srcs = srcs, 118 | plugins = plugins, 119 | js_outs = js_outs, 120 | map_outs = map_outs, 121 | dts_outs = dts_outs, 122 | allow_js = allow_js, 123 | output_dir = output_dir, 124 | source_maps = source_maps, 125 | args = args, 126 | data = data, 127 | swcrc = swcrc, 128 | out_dir = out_dir, 129 | root_dir = root_dir, 130 | default_ext = default_ext, 131 | **kwargs 132 | ) 133 | 134 | _swc_plugin = rule( 135 | doc = "Configure an SWC plugin", 136 | implementation = _swc_plugin_lib.implementation, 137 | attrs = _swc_plugin_lib.attrs, 138 | provides = _swc_plugin_lib.provides, 139 | ) 140 | 141 | def swc_plugin(name, srcs = [], config = {}, **kwargs): 142 | """Configure an SWC plugin 143 | 144 | Args: 145 | name: A name for this target 146 | 147 | srcs: Plugin files. Either a directory containing a package.json pointing at a wasm file 148 | as the main entrypoint, or a wasm file. Usually a linked npm package target via rules_js. 149 | 150 | config: Optional configuration dict for the plugin. This is passed as a JSON object into the 151 | `jsc.experimental.plugins` entry for the plugin. 152 | 153 | **kwargs: additional keyword arguments passed through to underlying rule, eg. `visibility`, `tags` 154 | """ 155 | 156 | if not types.is_dict(config): 157 | fail("config must be a dict, not a " + type(config)) 158 | 159 | # For backward compat 160 | src = kwargs.pop("src", None) 161 | if src: 162 | srcs = srcs[:] + [src] 163 | 164 | _swc_plugin( 165 | name = name, 166 | srcs = srcs, 167 | config = json.encode(config), 168 | **kwargs 169 | ) 170 | -------------------------------------------------------------------------------- /swc/dependencies.bzl: -------------------------------------------------------------------------------- 1 | """Starlark helper to fetch rules_swc dependencies. 2 | 3 | These are needed for local dev, and users must install them as well. 4 | See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies 5 | 6 | Replaced by bzlmod for users of Bazel 6.0 and above. 7 | """ 8 | 9 | load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive") 10 | load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") 11 | 12 | def http_archive(**kwargs): 13 | maybe(_http_archive, **kwargs) 14 | 15 | def rules_swc_dependencies(): 16 | http_archive( 17 | name = "bazel_skylib", 18 | sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", 19 | urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"], 20 | ) 21 | 22 | http_archive( 23 | name = "aspect_bazel_lib", 24 | integrity = "sha256-yW22ndJxSjfzKYM4oaQrJ+OiaWw7Nt1EQbm/ehoSvuA=", 25 | strip_prefix = "bazel-lib-2.11.0", 26 | url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.11.0/bazel-lib-v2.11.0.tar.gz", 27 | ) 28 | 29 | http_archive( 30 | name = "aspect_rules_js", 31 | sha256 = "6b7e73c35b97615a09281090da3645d9f03b2a09e8caa791377ad9022c88e2e6", 32 | strip_prefix = "rules_js-2.0.0", 33 | url = "https://github.com/aspect-build/rules_js/releases/download/v2.0.0/rules_js-v2.0.0.tar.gz", 34 | ) 35 | 36 | http_archive( 37 | name = "rules_nodejs", 38 | sha256 = "87c6171c5be7b69538d4695d9ded29ae2626c5ed76a9adeedce37b63c73bef67", 39 | strip_prefix = "rules_nodejs-6.2.0", 40 | url = "https://github.com/bazelbuild/rules_nodejs/releases/download/v6.2.0/rules_nodejs-v6.2.0.tar.gz", 41 | ) 42 | -------------------------------------------------------------------------------- /swc/extensions.bzl: -------------------------------------------------------------------------------- 1 | "extensions for bzlmod" 2 | 3 | load(":repositories.bzl", "swc_register_toolchains") 4 | 5 | swc_toolchain = tag_class(attrs = { 6 | "name": attr.string(doc = "Base name for generated repositories"), 7 | "swc_version": attr.string(doc = "Explicit version of @swc/core. If provided, the package.json is not read."), 8 | "swc_version_from": attr.label(doc = "Location of package.json which may have a version for @swc/core."), 9 | }) 10 | 11 | default_repository = "swc" 12 | 13 | def _toolchain_extension(module_ctx): 14 | registrations = {} 15 | for mod in module_ctx.modules: 16 | for toolchain in mod.tags.toolchain: 17 | if toolchain.name != default_repository and not mod.is_root: 18 | fail("Only the root module may provide a name for the {} toolchain.".format(toolchain.name)) 19 | 20 | if toolchain.name in registrations.keys(): 21 | if toolchain.name == default_repository: 22 | # Prioritize the root-most registration of the default toolchain version and 23 | # ignore any further registrations (modules are processed breadth-first) 24 | continue 25 | if toolchain.swc_version == registrations[toolchain.name].swc_version and toolchain.swc_version_from == registrations[toolchain.name].swc_version_from: 26 | # No problem to register a matching toolchain twice 27 | continue 28 | fail("Multiple conflicting toolchains declared for name {} ({} and {}".format( 29 | toolchain.name, 30 | toolchain.swc_version, 31 | registrations[toolchain.name], 32 | )) 33 | else: 34 | registrations[toolchain.name] = struct( 35 | swc_version = toolchain.swc_version, 36 | swc_version_from = toolchain.swc_version_from, 37 | ) 38 | 39 | for name, registration in registrations.items(): 40 | swc_register_toolchains( 41 | name = name, 42 | swc_version = registration.swc_version, 43 | swc_version_from = registration.swc_version_from, 44 | register = False, 45 | ) 46 | 47 | swc = module_extension( 48 | implementation = _toolchain_extension, 49 | tag_classes = {"toolchain": swc_toolchain}, 50 | ) 51 | -------------------------------------------------------------------------------- /swc/private/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@bazel_skylib//:bzl_library.bzl", "bzl_library") 2 | 3 | bzl_library( 4 | name = "swc_plugin", 5 | srcs = ["swc_plugin.bzl"], 6 | visibility = ["//swc:__subpackages__"], 7 | deps = ["//swc:providers"], 8 | ) 9 | 10 | bzl_library( 11 | name = "swc", 12 | srcs = ["swc.bzl"], 13 | visibility = ["//swc:__subpackages__"], 14 | deps = [ 15 | "//swc:providers", 16 | "@aspect_bazel_lib//lib:copy_file", 17 | "@aspect_bazel_lib//lib:platform_utils", 18 | "@aspect_rules_js//js:libs", 19 | "@aspect_rules_js//js:providers", 20 | "@bazel_skylib//lib:paths", 21 | ], 22 | ) 23 | 24 | bzl_library( 25 | name = "resolved_toolchain", 26 | srcs = ["resolved_toolchain.bzl"], 27 | visibility = ["//swc:__subpackages__"], 28 | ) 29 | 30 | bzl_library( 31 | name = "toolchains_repo", 32 | srcs = ["toolchains_repo.bzl"], 33 | visibility = ["//swc:__subpackages__"], 34 | ) 35 | 36 | bzl_library( 37 | name = "versions", 38 | srcs = ["versions.bzl"], 39 | visibility = ["//swc:__subpackages__"], 40 | ) 41 | -------------------------------------------------------------------------------- /swc/private/resolved_toolchain.bzl: -------------------------------------------------------------------------------- 1 | """This module implements an alias rule to the resolved toolchain. 2 | """ 3 | 4 | DOC = """\ 5 | Exposes a concrete toolchain which is the result of Bazel resolving the 6 | toolchain for the execution or target platform. 7 | Workaround for https://github.com/bazelbuild/bazel/issues/14009 8 | """ 9 | 10 | # Forward all the providers 11 | def _resolved_toolchain_impl(ctx): 12 | toolchain_info = ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"] 13 | return [ 14 | toolchain_info, 15 | toolchain_info.default, 16 | toolchain_info.swcinfo, 17 | toolchain_info.template_variables, 18 | ] 19 | 20 | # Copied from java_toolchain_alias 21 | # https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl 22 | resolved_toolchain = rule( 23 | implementation = _resolved_toolchain_impl, 24 | toolchains = ["@aspect_rules_swc//swc:toolchain_type"], 25 | incompatible_use_toolchain_transition = True, 26 | ) 27 | -------------------------------------------------------------------------------- /swc/private/swc.bzl: -------------------------------------------------------------------------------- 1 | "Internal implementation details" 2 | 3 | load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file_action") 4 | load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "COPY_FILE_TO_BIN_TOOLCHAINS") 5 | load("@aspect_bazel_lib//lib:platform_utils.bzl", "platform_utils") 6 | load("@aspect_rules_js//js:libs.bzl", "js_lib_helpers") 7 | load("@aspect_rules_js//js:providers.bzl", "js_info") 8 | load("@bazel_skylib//lib:paths.bzl", "paths") 9 | load("//swc:providers.bzl", "SwcPluginConfigInfo") 10 | 11 | _attrs = { 12 | "srcs": attr.label_list( 13 | doc = "source files, typically .ts files in the source tree", 14 | allow_files = True, 15 | mandatory = True, 16 | ), 17 | "args": attr.string_list( 18 | doc = """Additional arguments to pass to swcx cli (NOT swc!). 19 | 20 | NB: this is not the same as the CLI arguments for @swc/cli npm package. 21 | For performance, rules_swc does not call a Node.js program wrapping the swc rust binding. 22 | Instead, we directly spawn the (somewhat experimental) native Rust binary shipped inside the 23 | @swc/core npm package, which the swc project calls "swcx" 24 | Tracking issue for feature parity: https://github.com/swc-project/swc/issues/4017 25 | """, 26 | ), 27 | "source_maps": attr.string( 28 | doc = """Create source map files for emitted JavaScript files. 29 | 30 | see https://swc.rs/docs/usage/cli#--source-maps--s""", 31 | values = ["true", "false", "inline", "both"], 32 | default = "false", 33 | ), 34 | "source_root": attr.string( 35 | doc = """Specify the root path for debuggers to find the reference source code. 36 | 37 | see https://swc.rs/docs/usage/cli#--source-root 38 | 39 | If not set, then the directory containing the source file is used.""", 40 | ), 41 | "output_dir": attr.bool( 42 | doc = """Whether to produce a directory output rather than individual files. 43 | 44 | If out_dir is also specified, it is used as the name of the output directory. 45 | Otherwise, the directory is named the same as the target.""", 46 | ), 47 | "data": attr.label_list( 48 | doc = """Runtime dependencies to include in binaries/tests that depend on this target. 49 | 50 | Follows the same semantics as `js_library` `data` attribute. See 51 | https://docs.aspect.build/rulesets/aspect_rules_js/docs/js_library#data for more info. 52 | """, 53 | allow_files = True, 54 | ), 55 | "swcrc": attr.label( 56 | doc = "label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc", 57 | allow_single_file = True, 58 | ), 59 | "plugins": attr.label_list( 60 | doc = "swc compilation plugins, created with swc_plugin rule", 61 | providers = [[DefaultInfo, SwcPluginConfigInfo]], 62 | ), 63 | "out_dir": attr.string( 64 | doc = """With output_dir=False, output files will have this directory prefix. 65 | 66 | With output_dir=True, this is the name of the output directory.""", 67 | ), 68 | "root_dir": attr.string( 69 | doc = "a subdirectory under the input package which should be consider the root directory of all the input files", 70 | ), 71 | "emit_isolated_dts": attr.bool( 72 | doc = """Emit .d.ts files instead of .js for TypeScript sources 73 | 74 | EXPERIMENTAL: this API is undocumented, experimental and may change without notice 75 | """, 76 | default = False, 77 | ), 78 | "default_ext": attr.string( 79 | doc = """Default extension for output files. 80 | 81 | If a source file does not indicate a specific module type, this extension is used. 82 | 83 | If unset, extensions will be determined based on the `js_outs` outputs attribute 84 | or source file extensions.""", 85 | ), 86 | "allow_js": attr.bool( 87 | doc = """Allow JavaScript sources to be transpiled. 88 | 89 | If False, only TypeScript sources will be transpiled.""", 90 | default = True, 91 | ), 92 | } 93 | 94 | _outputs = { 95 | "js_outs": attr.output_list(doc = """list of expected JavaScript output files. 96 | 97 | There should be one for each entry in srcs."""), 98 | "map_outs": attr.output_list(doc = """list of expected source map output files. 99 | 100 | Can be empty, meaning no source maps should be produced. 101 | If non-empty, there should be one for each entry in srcs."""), 102 | "dts_outs": attr.output_list(doc = """list of expected TypeScript declaration files. 103 | 104 | Can be empty, meaning no dts files should be produced. 105 | If non-empty, there should be one for each entry in srcs."""), 106 | } 107 | 108 | def _is_ts_src(src): 109 | return src.endswith(".ts") or src.endswith(".mts") or src.endswith(".cts") or src.endswith(".tsx") 110 | 111 | def _is_typings_src(src): 112 | return src.endswith(".d.ts") or src.endswith(".d.mts") or src.endswith(".d.cts") 113 | 114 | def _is_js_src(src): 115 | return src.endswith(".mjs") or src.endswith(".cjs") or src.endswith(".js") or src.endswith(".jsx") 116 | 117 | def _is_supported_src(src, allow_js): 118 | return _is_ts_src(src) or (allow_js and _is_js_src(src)) 119 | 120 | def _is_data_src(src): 121 | return src.endswith(".json") 122 | 123 | # TODO: vendored from rules_ts - aspect_bazel_lib should provide this? 124 | # https://github.com/aspect-build/rules_ts/blob/v3.2.1/ts/private/ts_lib.bzl#L194-L200 125 | def _relative_to_package(path, ctx): 126 | path = path.removeprefix(ctx.bin_dir.path + "/") 127 | path = path.removeprefix("external/") 128 | path = path.removeprefix(ctx.label.workspace_name + "/") 129 | if ctx.label.package: 130 | path = path.removeprefix(ctx.label.package + "/") 131 | return path 132 | 133 | # TODO: vendored from rules_ts - aspect_bazel_lib should provide this? 134 | # https://github.com/aspect-build/rules_ts/blob/v3.2.1/ts/private/ts_lib.bzl#L220-L226 135 | def _to_out_path(f, out_dir, root_dir): 136 | f = f[f.find(":") + 1:] 137 | if root_dir: 138 | f = f.removeprefix(root_dir + "/") 139 | if out_dir and out_dir != ".": 140 | f = out_dir + "/" + f 141 | return f 142 | 143 | def _remove_extension(f): 144 | i = f.rfind(".") 145 | return f if i <= 0 else f[:-(len(f) - i)] 146 | 147 | def _to_js_out(default_ext, src, allow_js, out_dir, root_dir, js_outs = []): 148 | if not _is_supported_src(src, allow_js) or _is_typings_src(src): 149 | return None 150 | 151 | exts = { 152 | ".mts": ".mjs", 153 | ".mjs": ".mjs", 154 | ".cjs": ".cjs", 155 | ".cts": ".cjs", 156 | } 157 | ext_index = src.rindex(".") 158 | js_out_base = src[:ext_index] 159 | js_out_ext = exts.get(src[ext_index:], default_ext if default_ext else ".js") 160 | js_out = _to_out_path(js_out_base + js_out_ext, out_dir, root_dir) 161 | 162 | # If a default extension was specified then use js_out with the defaults 163 | if default_ext: 164 | return js_out 165 | 166 | # If no default_ext was provided allow customizing the output extension via js_outs. 167 | # See https://github.com/aspect-build/rules_swc/commit/edc6421cf42a7174bcc38e91b0812abd0bfb0f09 168 | # TODO(3.0): remove this feature in favour of standard logic above. 169 | alt_js_out = None 170 | 171 | # Check if a custom out was requested with a potentially different extension 172 | no_ext = _remove_extension(js_out) 173 | for maybe_out in js_outs: 174 | # Always use an exact match if it exists 175 | if maybe_out == js_out: 176 | return js_out 177 | 178 | # Try to match on a potential output with a different extension 179 | # Initial startswith() check to avoid the expensive _remove_extension() 180 | if maybe_out.startswith(no_ext) and no_ext == _remove_extension(maybe_out): 181 | alt_js_out = maybe_out 182 | 183 | # Return the matched custom out if it exists otherwise fallback to the default 184 | return alt_js_out or js_out 185 | 186 | def _calculate_js_outs(default_ext, srcs, allow_js, out_dir = None, root_dir = None): 187 | out = [] 188 | for f in srcs: 189 | js_out = _to_js_out(default_ext, f, allow_js, out_dir, root_dir) 190 | if js_out and js_out != f: 191 | out.append(js_out) 192 | return out 193 | 194 | def _to_map_out(default_ext, src, source_maps, allow_js, out_dir, root_dir): 195 | if source_maps == "false" or source_maps == "inline": 196 | return None 197 | if not _is_supported_src(src, allow_js) or _is_typings_src(src): 198 | return None 199 | exts = { 200 | ".mts": ".mjs.map", 201 | ".cts": ".cjs.map", 202 | ".mjs": ".mjs.map", 203 | ".cjs": ".cjs.map", 204 | } 205 | ext_index = src.rindex(".") 206 | map_out = src[:ext_index] + exts.get(src[ext_index:], default_ext + ".map") 207 | map_out = _to_out_path(map_out, out_dir, root_dir) 208 | return map_out 209 | 210 | def _calculate_map_outs(default_ext, srcs, source_maps, allow_js, out_dir, root_dir): 211 | if source_maps == "false" or source_maps == "inline": 212 | return [] 213 | 214 | out = [] 215 | for f in srcs: 216 | map_out = _to_map_out(default_ext, f, source_maps, allow_js, out_dir, root_dir) 217 | if map_out: 218 | out.append(map_out) 219 | return out 220 | 221 | def _to_dts_out(src, emit_isolated_dts, allow_js, out_dir, root_dir): 222 | if not emit_isolated_dts: 223 | return None 224 | if not _is_supported_src(src, allow_js) or _is_typings_src(src): 225 | return None 226 | dts_out = src[:src.rindex(".")] + ".d.ts" 227 | dts_out = _to_out_path(dts_out, out_dir, root_dir) 228 | return dts_out 229 | 230 | def _calculate_dts_outs(srcs, emit_isolated_dts, allow_js, out_dir, root_dir): 231 | out = [] 232 | for f in srcs: 233 | dts_out = _to_dts_out(f, emit_isolated_dts, allow_js, out_dir, root_dir) 234 | if dts_out: 235 | out.append(dts_out) 236 | return out 237 | 238 | def _calculate_source_file(ctx, src): 239 | if not (ctx.attr.out_dir or ctx.attr.root_dir): 240 | return src.basename 241 | 242 | src_pkg = src.dirname[len(ctx.label.package) + 1:] if ctx.label.package else "" 243 | s = "" 244 | 245 | # out of src subdir 246 | if src_pkg: 247 | src_pkg_depth = len(src_pkg.split("/")) 248 | root_dir_depth = len(ctx.attr.root_dir.split("/")) if ctx.attr.root_dir and ctx.attr.root_dir != "." else 0 249 | effective_depth = max(0, src_pkg_depth - root_dir_depth) 250 | s = paths.join(s, "/".join([".." for _ in range(effective_depth)])) 251 | 252 | # out of the out dir 253 | if ctx.attr.out_dir: 254 | s = paths.join(s, "/".join([".." for _ in ctx.attr.out_dir.split("/")])) 255 | 256 | # back into the src dir, including into the root_dir 257 | return paths.join(s, src_pkg, src.basename) 258 | 259 | def _swc_action(ctx, swc_binary, **kwargs): 260 | ctx.actions.run( 261 | mnemonic = "SWCCompile", 262 | progress_message = "Compiling %{label} [swc %{input}]", 263 | executable = swc_binary, 264 | **kwargs 265 | ) 266 | 267 | def _swc_impl(ctx): 268 | swc_toolchain = ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"] 269 | 270 | inputs = swc_toolchain.swcinfo.tool_files[:] 271 | 272 | args = ctx.actions.args() 273 | args.add("compile") 274 | 275 | # The root config file. Config options may be overridden by additional args. 276 | if ctx.attr.swcrc: 277 | args.add("--config-file", ctx.file.swcrc) 278 | inputs.append(ctx.file.swcrc) 279 | 280 | # Add user specified arguments *before* rule supplied arguments 281 | args.add_all(ctx.attr.args) 282 | 283 | args.add("--source-maps", ctx.attr.source_maps) 284 | if ctx.attr.source_maps != "false" and ctx.attr.source_root: 285 | args.add("--source-root", ctx.attr.source_root) 286 | 287 | if ctx.attr.plugins: 288 | plugin_cache = [ctx.actions.declare_directory("{}_plugin_cache".format(ctx.label.name))] 289 | plugin_args = ["--config-json", json.encode({ 290 | "jsc": { 291 | "experimental": { 292 | "cacheRoot": plugin_cache[0].path, 293 | "plugins": [["./" + p[DefaultInfo].files.to_list()[0].path, json.decode(p[SwcPluginConfigInfo].config)] for p in ctx.attr.plugins], 294 | }, 295 | }, 296 | })] 297 | 298 | null_file = "NUL" if platform_utils.host_platform_is_windows() else "/dev/null" 299 | 300 | # run swc once with a null input to compile the plugins into the plugin cache 301 | _swc_action( 302 | ctx, 303 | swc_toolchain.swcinfo.swc_binary, 304 | arguments = ["compile"] + plugin_args + ["--source-maps", "false", "--out-file", null_file, null_file], 305 | inputs = inputs + ctx.files.plugins, 306 | outputs = plugin_cache, 307 | ) 308 | 309 | inputs.extend(plugin_cache) 310 | inputs.extend(ctx.files.plugins) 311 | args.add_all(plugin_args) 312 | 313 | if ctx.attr.emit_isolated_dts: 314 | args.add_all(["--config-json", json.encode({ 315 | "jsc": { 316 | "experimental": { 317 | "emitIsolatedDts": True, 318 | }, 319 | }, 320 | })]) 321 | 322 | if ctx.attr.output_dir: 323 | if len(ctx.attr.srcs) != 1: 324 | fail("Under output_dir, there must be a single entry in srcs") 325 | if not ctx.files.srcs[0].is_directory: 326 | fail("Under output_dir, the srcs must be directories, not files") 327 | output_dir = ctx.actions.declare_directory(ctx.attr.out_dir if ctx.attr.out_dir else ctx.label.name) 328 | 329 | inputs.extend(ctx.files.srcs) 330 | 331 | output_sources = [output_dir] 332 | 333 | args.add("--out-dir", output_dir.path) 334 | 335 | _swc_action( 336 | ctx, 337 | swc_toolchain.swcinfo.swc_binary, 338 | inputs = inputs, 339 | arguments = [ 340 | args, 341 | ctx.files.srcs[0].path, 342 | ], 343 | outputs = output_sources, 344 | ) 345 | else: 346 | # Disable sandboxing for the SWC action by default since there is normally only 347 | # the source and config files as inputs and not complex dependency tree. 348 | # 349 | # This may be required for SWC issues with symlinks in the sandbox. 350 | execution_requirements = { 351 | "no-sandbox": "1", 352 | } 353 | 354 | output_sources = [] 355 | 356 | js_outs_relative = [_relative_to_package(f.path, ctx) for f in ctx.outputs.js_outs] 357 | 358 | for src in ctx.files.srcs: 359 | src_args = ctx.actions.args() 360 | 361 | if ctx.attr.source_maps != "false": 362 | src_args.add("--source-file-name", _calculate_source_file(ctx, src)) 363 | 364 | src_path = _relative_to_package(src.path, ctx) 365 | 366 | # This source file is a typings file and not transpiled 367 | if _is_typings_src(src_path): 368 | # Copy to the output directory if emitting dts files is enabled 369 | if ctx.attr.emit_isolated_dts: 370 | output_sources.append(src) 371 | continue 372 | 373 | if _is_data_src(src_path): 374 | # Copy data to the output directory. 375 | # NOTE: assumes json must be resolved at runtime, see ts_project(resolve_json_module) 376 | if ctx.attr.out_dir: 377 | out_path = "%s/%s" % (ctx.attr.out_dir if ctx.attr.out_dir else ".", src_path) 378 | out_file = ctx.actions.declare_file(out_path) 379 | copy_file_action( 380 | ctx = ctx, 381 | src = src, 382 | dst = out_file, 383 | ) 384 | output_sources.append(out_file) 385 | else: 386 | output_sources.append(src) 387 | continue 388 | 389 | js_out_path = _to_js_out(ctx.attr.default_ext, src_path, ctx.attr.allow_js, ctx.attr.out_dir, ctx.attr.root_dir, js_outs_relative) 390 | if not js_out_path: 391 | # This source file is not a supported src 392 | continue 393 | js_out = ctx.actions.declare_file(js_out_path) 394 | outputs = [js_out] 395 | 396 | map_out_path = _to_map_out(ctx.attr.default_ext, src_path, ctx.attr.source_maps, ctx.attr.allow_js, ctx.attr.out_dir, ctx.attr.root_dir) 397 | if map_out_path: 398 | js_map_out = ctx.actions.declare_file(map_out_path) 399 | outputs.append(js_map_out) 400 | 401 | dts_out_path = _to_dts_out(src_path, ctx.attr.emit_isolated_dts, ctx.attr.allow_js, ctx.attr.out_dir, ctx.attr.root_dir) 402 | if dts_out_path: 403 | dts_out = ctx.actions.declare_file(dts_out_path) 404 | outputs.append(dts_out) 405 | 406 | src_args.add("--out-file", js_out) 407 | 408 | output_sources.extend(outputs) 409 | 410 | _swc_action( 411 | ctx, 412 | swc_toolchain.swcinfo.swc_binary, 413 | inputs = [src] + inputs, 414 | arguments = [ 415 | args, 416 | src_args, 417 | src.path, 418 | ], 419 | outputs = outputs, 420 | execution_requirements = execution_requirements, 421 | ) 422 | 423 | output_sources_depset = depset(output_sources) 424 | 425 | transitive_sources = js_lib_helpers.gather_transitive_sources( 426 | sources = output_sources, 427 | targets = ctx.attr.srcs, 428 | ) 429 | 430 | transitive_types = js_lib_helpers.gather_transitive_types( 431 | types = [], 432 | targets = ctx.attr.srcs, 433 | ) 434 | 435 | npm_sources = js_lib_helpers.gather_npm_sources( 436 | srcs = ctx.attr.srcs, 437 | deps = [], 438 | ) 439 | 440 | npm_package_store_infos = js_lib_helpers.gather_npm_package_store_infos( 441 | targets = ctx.attr.srcs + ctx.attr.data, 442 | ) 443 | 444 | runfiles = js_lib_helpers.gather_runfiles( 445 | ctx = ctx, 446 | sources = transitive_sources, 447 | data = ctx.attr.data, 448 | deps = ctx.attr.srcs, 449 | ) 450 | 451 | return [ 452 | js_info( 453 | target = ctx.label, 454 | sources = output_sources_depset, 455 | types = depset(), # swc does not emit types directly 456 | transitive_sources = transitive_sources, 457 | transitive_types = transitive_types, 458 | npm_sources = npm_sources, 459 | npm_package_store_infos = npm_package_store_infos, 460 | ), 461 | DefaultInfo( 462 | files = output_sources_depset, 463 | runfiles = runfiles, 464 | ), 465 | ] 466 | 467 | swc = struct( 468 | implementation = _swc_impl, 469 | attrs = dict(_attrs, **_outputs), 470 | toolchains = ["@aspect_rules_swc//swc:toolchain_type"] + COPY_FILE_TO_BIN_TOOLCHAINS, 471 | calculate_js_outs = _calculate_js_outs, 472 | calculate_map_outs = _calculate_map_outs, 473 | calculate_dts_outs = _calculate_dts_outs, 474 | ) 475 | -------------------------------------------------------------------------------- /swc/private/swc_plugin.bzl: -------------------------------------------------------------------------------- 1 | "Implementation details for swc_plugin rule" 2 | 3 | load("//swc:providers.bzl", "SwcPluginConfigInfo") 4 | 5 | _attrs = { 6 | "srcs": attr.label_list( 7 | doc = "label for the plugin, either a directory containing a package.json pointing at a wasm file as the main entrypoint, or a wasm file", 8 | providers = [DefaultInfo], 9 | mandatory = True, 10 | allow_files = True, 11 | ), 12 | "config": attr.string( 13 | doc = "configuration object for the plugin, serialized JSON object", 14 | default = "{}", 15 | ), 16 | } 17 | 18 | def _swc_plugin_impl(ctx): 19 | return [ 20 | DefaultInfo( 21 | files = depset(ctx.files.srcs), 22 | ), 23 | SwcPluginConfigInfo( 24 | label = ctx.label, 25 | config = ctx.attr.config, 26 | ), 27 | ] 28 | 29 | swc_plugin = struct( 30 | attrs = _attrs, 31 | implementation = _swc_plugin_impl, 32 | provides = [DefaultInfo, SwcPluginConfigInfo], 33 | ) 34 | -------------------------------------------------------------------------------- /swc/private/toolchains_repo.bzl: -------------------------------------------------------------------------------- 1 | """Create a repository to hold the toolchains 2 | 3 | This follows guidance here: 4 | https://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains 5 | " 6 | Note that in order to resolve toolchains in the analysis phase 7 | Bazel needs to analyze all toolchain targets that are registered. 8 | Bazel will not need to analyze all targets referenced by toolchain.toolchain attribute. 9 | If in order to register toolchains you need to perform complex computation in the repository, 10 | consider splitting the repository with toolchain targets 11 | from the repository with _toolchain targets. 12 | Former will be always fetched, 13 | and the latter will only be fetched when user actually needs to build code. 14 | " 15 | The "complex computation" in our case is simply downloading large artifacts. 16 | This guidance tells us how to avoid that: we put the toolchain targets in the alias repository 17 | with only the toolchain attribute pointing into the platform-specific repositories. 18 | """ 19 | 20 | # Based on the list of pre-compiled binaries at 21 | # https://github.com/swc-project/swc/releases/tag/v1.3.56 22 | # TODO: how to represent these three? 23 | # linux-arm-gnueabihf 24 | # linux-arm64-musl 25 | # linux-x64-musl 26 | PLATFORMS = { 27 | "darwin-arm64": struct( 28 | compatible_with = [ 29 | "@platforms//os:macos", 30 | "@platforms//cpu:aarch64", 31 | ], 32 | ), 33 | "darwin-x64": struct( 34 | compatible_with = [ 35 | "@platforms//os:macos", 36 | "@platforms//cpu:x86_64", 37 | ], 38 | ), 39 | "linux-arm64-gnu": struct( 40 | compatible_with = [ 41 | "@platforms//os:linux", 42 | "@platforms//cpu:aarch64", 43 | ], 44 | ), 45 | "linux-x64-gnu": struct( 46 | compatible_with = [ 47 | "@platforms//os:linux", 48 | "@platforms//cpu:x86_64", 49 | ], 50 | ), 51 | "win32-arm64-msvc": struct( 52 | compatible_with = [ 53 | "@platforms//os:windows", 54 | "@platforms//cpu:aarch64", 55 | ], 56 | ), 57 | "win32-ia32-msvc": struct( 58 | compatible_with = [ 59 | "@platforms//os:windows", 60 | "@platforms//cpu:i386", 61 | ], 62 | ), 63 | "win32-x64-msvc": struct( 64 | compatible_with = [ 65 | "@platforms//os:windows", 66 | "@platforms//cpu:x86_64", 67 | ], 68 | ), 69 | } 70 | 71 | def _toolchains_repo_impl(repository_ctx): 72 | build_content = """# Generated by toolchains_repo.bzl 73 | # 74 | # These can be registered in the workspace file or passed to --extra_toolchains flag. 75 | # By default all these toolchains are registered by the swc_register_toolchains macro 76 | # so you don't normally need to interact with these targets. 77 | 78 | # backward-compatibility, see 79 | # https://github.com/aspect-build/rules_swc/pull/157 80 | alias( 81 | name = "resolved_toolchain", 82 | actual = "@aspect_rules_swc//swc:resolved_toolchain", 83 | visibility = ["//visibility:public"], 84 | ) 85 | """ 86 | 87 | for [platform, meta] in PLATFORMS.items(): 88 | build_content += """ 89 | toolchain( 90 | name = "{platform}_toolchain", 91 | exec_compatible_with = {compatible_with}, 92 | toolchain = "@{user_repository_name}_{platform}//:swc_toolchain", 93 | toolchain_type = "@aspect_rules_swc//swc:toolchain_type", 94 | ) 95 | """.format( 96 | platform = platform, 97 | name = repository_ctx.attr.name, 98 | user_repository_name = repository_ctx.attr.user_repository_name, 99 | compatible_with = meta.compatible_with, 100 | ) 101 | 102 | # Base BUILD file for this repository 103 | repository_ctx.file("BUILD.bazel", build_content) 104 | 105 | toolchains_repo = repository_rule( 106 | _toolchains_repo_impl, 107 | doc = """Creates a repository with toolchain definitions for all known platforms 108 | which can be registered or selected.""", 109 | attrs = { 110 | "user_repository_name": attr.string(doc = "what the user chose for the base name"), 111 | }, 112 | ) 113 | -------------------------------------------------------------------------------- /swc/providers.bzl: -------------------------------------------------------------------------------- 1 | """Providers for building derivative rules""" 2 | 3 | SwcPluginConfigInfo = provider( 4 | doc = "Provides a configuration for an SWC plugin", 5 | fields = { 6 | "label": "the label of the target that created this provider", 7 | "config": "the plugin configuration string, encoded from a JSON object", 8 | }, 9 | ) 10 | -------------------------------------------------------------------------------- /swc/repositories.bzl: -------------------------------------------------------------------------------- 1 | """Repository rules for fetching the swc toolchain. 2 | 3 | For typical usage, see the snippets provided in the rules_swc release notes. 4 | 5 | ### Version matching 6 | 7 | To keep the swc version in sync with non-Bazel tooling, use `swc_version_from`. 8 | 9 | Currently this only works when a single, pinned version appears, see: 10 | https://github.com/aspect-build/rules_ts/issues/308 11 | 12 | For example, `package.json`: 13 | 14 | ```json 15 | { 16 | "devDependencies": { 17 | "@swc/core": "1.3.37" 18 | } 19 | } 20 | ``` 21 | 22 | Allows this in `WORKSPACE`: 23 | 24 | ```starlark 25 | swc_register_toolchains( 26 | name = "swc", 27 | swc_version_from = "//:package.json", 28 | ) 29 | ``` 30 | 31 | """ 32 | 33 | load("@bazel_skylib//lib:versions.bzl", "versions") 34 | load("//swc/private:toolchains_repo.bzl", "PLATFORMS", "toolchains_repo") 35 | load("//swc/private:versions.bzl", "TOOL_VERSIONS") 36 | 37 | # Expose as Public API 38 | # NB: we don't use the "most recent release" since swc has a history of often breaking Bazel usage 39 | # with subtle changes that get through their tests. 40 | # So instead, this reflects the latest version that is "known good" according to our test suite. 41 | LATEST_SWC_VERSION = "v1.7.40" 42 | 43 | # TODO(2.0): remove this alias 44 | LATEST_VERSION = "v1.6.6" 45 | 46 | _DOC = "Fetch external dependencies needed to run the SWC cli" 47 | _ATTRS = { 48 | "swc_version": attr.string(doc = "Explicit version. If provided, the package.json is not read."), 49 | "swc_version_from": attr.label(doc = "Location of package.json which has a version for @swc/core."), 50 | "platform": attr.string(mandatory = True, values = PLATFORMS.keys()), 51 | "integrity_hashes": attr.string_dict(doc = "A mapping from platform to integrity hash."), 52 | } 53 | 54 | # This package is versioned the same as the underlying rust binary we download 55 | _NPM_PKG = "@swc/core" 56 | 57 | _SWC_TOO_OLD = """ 58 | 59 | FATAL: swc version must be at least 1.3.25, as prior versions had bugs in the pure-rust CLI. 60 | 61 | If you need swc version {}, then use rules_swc v0.20.2 or earlier. 62 | Those releases of rules_swc call the NodeJS @swc/cli to access the Rust binding, 63 | so they aren't affected by these bugs. 64 | 65 | """ 66 | 67 | # Read the swc version from package.json if requested 68 | def _determine_version(rctx): 69 | if rctx.attr.swc_version: 70 | return rctx.attr.swc_version 71 | 72 | json_path = rctx.path(rctx.attr.swc_version_from) 73 | p = json.decode(rctx.read(json_path)) 74 | 75 | # Allow use of "resolved.json", see https://github.com/aspect-build/rules_js/pull/1221 76 | if "$schema" in p.keys() and p["$schema"] == "https://docs.aspect.build/rules/aspect_rules_js/docs/npm_translate_lock": 77 | v = p["version"] 78 | elif "devDependencies" in p.keys() and _NPM_PKG in p["devDependencies"]: 79 | v = p["devDependencies"][_NPM_PKG] 80 | elif "dependencies" in p.keys() and _NPM_PKG in p["dependencies"]: 81 | v = p["dependencies"][_NPM_PKG] 82 | else: 83 | fail("key '{}' not found in either dependencies or devDependencies of {}".format(_NPM_PKG, json_path)) 84 | if any([not seg.isdigit() for seg in v.split(".")]): 85 | fail("{} version in package.json must be exactly specified, not a semver range: {}.\n".format(_NPM_PKG, v) + 86 | "You can supply an exact 'swc_version' attribute to 'swc_register_toolchains' to bypass this check.") 87 | 88 | # package.json versions don't have a "v" prefix, but github distribution/tag does. 89 | return "v" + v 90 | 91 | def _swc_repo_impl(repository_ctx): 92 | version = _determine_version(repository_ctx) 93 | if not versions.is_at_least("1.3.25", version.lstrip("v")): 94 | fail(_SWC_TOO_OLD.format(version)) 95 | filename = "swc-" + repository_ctx.attr.platform 96 | 97 | # The binaries of the SWC cli releases for windows are suffixed with ".exe" 98 | if repository_ctx.attr.platform.startswith("win32"): 99 | filename += ".exe" 100 | 101 | url = "https://github.com/swc-project/swc/releases/download/{0}/{1}".format( 102 | version, 103 | filename, 104 | ) 105 | 106 | integrity = repository_ctx.attr.integrity_hashes.get( 107 | repository_ctx.attr.platform, 108 | None, 109 | ) 110 | if not integrity: 111 | if version not in TOOL_VERSIONS.keys(): 112 | fail("""\ 113 | swc version {} does not have hashes mirrored in aspect_rules_swc, please either 114 | - Set the integrity_hashes attribute to a dictionary of platform/hash 115 | - Choose one of the mirrored versions: {} 116 | """.format(version, TOOL_VERSIONS.keys())) 117 | 118 | integrity = TOOL_VERSIONS[version][repository_ctx.attr.platform] 119 | 120 | repository_ctx.download( 121 | output = filename, 122 | url = url, 123 | integrity = integrity, 124 | executable = True, 125 | ) 126 | build_content = """#Generated by swc/repositories.bzl 127 | load("@aspect_rules_swc//swc:toolchain.bzl", "swc_toolchain") 128 | swc_toolchain( 129 | name = "swc_toolchain", 130 | target_tool = "%s", 131 | ) 132 | """ % filename 133 | 134 | # Base BUILD file for this repository 135 | repository_ctx.file("BUILD.bazel", build_content) 136 | 137 | swc_repositories = repository_rule( 138 | _swc_repo_impl, 139 | doc = _DOC, 140 | attrs = _ATTRS, 141 | ) 142 | 143 | # Wrapper macro around everything above, this is the primary API 144 | def swc_register_toolchains(name, swc_version = None, swc_version_from = None, register = True, **kwargs): 145 | """Convenience macro for users which does typical setup. 146 | 147 | - create a repository for each built-in platform like "swc_linux_amd64" 148 | - create a repository exposing toolchains for each platform like "swc_platforms" 149 | - register a toolchain pointing at each platform 150 | Users can avoid this macro and do these steps themselves, if they want more control. 151 | 152 | Args: 153 | name: base name for all created repos; we recommend `swc` 154 | swc_version_from: label of a json file which declares an `@swc/core` version. 155 | 156 | This may be a `package.json` file, with "@swc/core" in the dependencies or 157 | devDependencies property, and the version exactly specified. 158 | 159 | With rules_js v1.32.0 or greater, it may also be a `resolved.json` file 160 | produced by `npm_translate_lock`, such as 161 | `@npm//path/to/linked:@swc/core/resolved.json` 162 | 163 | Exactly one of `swc_version` or `swc_version_from` must be set. 164 | swc_version: version of the swc project, from https://github.com/swc-project/swc/releases 165 | Exactly one of `swc_version` or `swc_version_from` must be set. 166 | register: whether to call through to native.register_toolchains. 167 | Should be True for WORKSPACE users, but false when used under bzlmod extension 168 | **kwargs: passed to each swc_repositories call 169 | """ 170 | 171 | if (swc_version and swc_version_from) or (not swc_version_from and not swc_version): 172 | fail("Exactly one of 'swc_version' or 'swc_version_from' must be set.") 173 | 174 | for platform in PLATFORMS.keys(): 175 | swc_repositories( 176 | name = name + "_" + platform, 177 | platform = platform, 178 | swc_version = swc_version, 179 | swc_version_from = swc_version_from, 180 | **kwargs 181 | ) 182 | if register: 183 | native.register_toolchains("@%s_toolchains//:%s_toolchain" % (name, platform)) 184 | 185 | toolchains_repo( 186 | name = name + "_toolchains", 187 | user_repository_name = name, 188 | ) 189 | -------------------------------------------------------------------------------- /swc/toolchain.bzl: -------------------------------------------------------------------------------- 1 | """This module implements the language-specific toolchain rule. 2 | """ 3 | 4 | SwcInfo = provider( 5 | doc = "Information about how to invoke the tool executable.", 6 | fields = { 7 | "swc_binary": "Path to the rust-native 'swcx' cli for the target platform.", 8 | "tool_files": """Files required in runfiles to make the tool executable available. 9 | 10 | May be empty if the target_tool_path points to a locally installed tool binary.""", 11 | }, 12 | ) 13 | 14 | # Avoid using non-normalized paths (workspace/../other_workspace/path) 15 | def _to_manifest_path(ctx, file): 16 | if file.short_path.startswith("../"): 17 | return "external/" + file.short_path[3:] 18 | else: 19 | return ctx.workspace_name + "/" + file.short_path 20 | 21 | def _swc_toolchain_impl(ctx): 22 | if ctx.attr.target_tool and ctx.attr.target_tool_path: 23 | fail("Can only set one of swc_binary or target_tool_path but both were set.") 24 | if not ctx.attr.target_tool and not ctx.attr.target_tool_path: 25 | fail("Must set one of swc_binary or target_tool_path.") 26 | 27 | tool_files = [] 28 | target_tool_path = ctx.attr.target_tool_path 29 | 30 | if ctx.attr.target_tool: 31 | tool_files = ctx.attr.target_tool.files.to_list() 32 | target_tool_path = _to_manifest_path(ctx, tool_files[0]) 33 | 34 | # Make the $(tool_BIN) variable available in places like genrules. 35 | # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables 36 | template_variables = platform_common.TemplateVariableInfo({ 37 | "SWC_BINARY_PATH": target_tool_path, 38 | }) 39 | default = DefaultInfo( 40 | files = depset(tool_files), 41 | runfiles = ctx.runfiles(files = tool_files), 42 | ) 43 | swcinfo = SwcInfo( 44 | swc_binary = target_tool_path, 45 | tool_files = tool_files, 46 | ) 47 | 48 | # Export all the providers inside our ToolchainInfo 49 | # so the resolved_toolchain rule can grab and re-export them. 50 | toolchain_info = platform_common.ToolchainInfo( 51 | swcinfo = swcinfo, 52 | template_variables = template_variables, 53 | default = default, 54 | ) 55 | return [ 56 | default, 57 | toolchain_info, 58 | template_variables, 59 | ] 60 | 61 | swc_toolchain = rule( 62 | implementation = _swc_toolchain_impl, 63 | attrs = { 64 | "target_tool": attr.label( 65 | doc = "A hermetically downloaded 'swcx' cli for the target platform.", 66 | mandatory = False, 67 | allow_single_file = True, 68 | ), 69 | "target_tool_path": attr.string( 70 | doc = "Path to an existing 'swcx' cli for the target platform.", 71 | mandatory = False, 72 | ), 73 | }, 74 | doc = """Defines a swc compiler/runtime toolchain. 75 | 76 | For usage see https://docs.bazel.build/versions/main/toolchains.html#defining-toolchains. 77 | """, 78 | ) 79 | --------------------------------------------------------------------------------