Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .bazelignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
e2e/
e2e/
examples/node_modules
examples/generate_swcrc/node_modules
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
docs/*.md linguist-generated=true
docs/repositories.md linguist-generated=true
docs/swc.md linguist-generated=true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
bazel-*
node_modules
11 changes: 11 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ nodejs_register_toolchains(
node_version = DEFAULT_NODE_VERSION,
)

load("@aspect_rules_js//npm:npm_import.bzl", "npm_translate_lock")

npm_translate_lock(
name = "npm",
pnpm_lock = "//examples:pnpm-lock.yaml",
)

load("@npm//:repositories.bzl", "npm_repositories")

npm_repositories()

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")

bazel_skylib_workspace()
Expand Down
2 changes: 1 addition & 1 deletion docs/swc.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions docs/tsconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Synchronizing settings with tsconfig.json

TypeScript and SWC both need to be configured in their own files,
typically `tsconfig.json` and `.swcrc`, respectively. Some settings are meant to be common between the two, such as
how dependencies are resolved on the disk.

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.

Ideally, we'd like SWC to simply read from `tsconfig.json`, as it
is the "source-of-truth" for how editors understand `.ts` files.
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.

This document explores our options.

## Maintain two files

You might just check in both files as sources.

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.

A typical example looks like this:

```python
load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches")

# Verify that the "paths" entry is the same
# between swc and TS language service (in the editor)
assert_json_matches(
name = "check_paths",
file1 = "tsconfig.json",
file2 = ".swcrc",
filter1 = ".compilerOptions.paths",
filter2 = ".jsc.paths",
)
```

## Generate the .swcrc

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.

The relevant package is [tsconfig-to-swcconfig](https://www.npmjs.com/package/tsconfig-to-swcconfig). Let's see how to wire it up.

There's a corresponding CLI tool, tswc, but it doesn't work well to just generate the `.swcrc` file,
see <https://github.com/Songkeys/tswc/issues/1>

So we'll make our own tiny CLI for the underlying package, let's call it `write_swcrc.js`, containing:

```javascript
const {convert} = require('tsconfig-to-swcconfig');
const [tsconfig] = process.argv.slice(2);
console.log(JSON.stringify(convert(tsconfig), undefined, 2));
```

And a bit of BUILD file content to invoke it (you might wrap this in a macro for better developer experience):

```python
js_binary(
name = "converter",
entry_point = "write_swcrc.js",
data = [":node_modules/tsconfig-to-swcconfig"],
)

js_run_binary(
name = "write_swcrc",
tool = "converter",
chdir = package_name(),
args = ["./tsconfig.json"],
srcs = ["tsconfig.json"],
stdout = ".swcrc",
)
```

Now you can just use the standard `swc` rule with the `swcrc` attribute.
See `/examples/generate_swcrc` for a full example.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be a link?

5 changes: 5 additions & 0 deletions examples/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
load("@npm//:defs.bzl", "npm_link_all_packages")

# Required to create the virtual store at the root of the pnpm workspace
# We don't have any node_modules to lay out in this directory.
npm_link_all_packages()
45 changes: 45 additions & 0 deletions examples/generate_swcrc/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
load("@aspect_bazel_lib//lib:testing.bzl", "assert_json_matches")
load("@aspect_rules_swc//swc:defs.bzl", "swc")
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary")
load("@npm//:defs.bzl", "npm_link_all_packages")

npm_link_all_packages(name = "node_modules")

# Doesn't work: https://github.com/Songkeys/tswc/issues/1
# load("@npm//examples/generate_swcrc:tswc/package_json.bzl", "bin")
# bin.tswc(
# name = "convert",
# srcs = ["tsconfig.json"],
# stdout = ".swcrc0",
# args = ["--debug"],
# )

js_binary(
name = "converter",
data = [":node_modules/tsconfig-to-swcconfig"],
entry_point = "write_swcrc.js",
)

js_run_binary(
name = "write_swcrc",
srcs = ["tsconfig.json"],
args = ["./tsconfig.json"],
chdir = package_name(),
stdout = ".swcrc",
tool = "converter",
)

# Demonstrate that it works
swc(
name = "compile",
swcrc = ".swcrc",
)

# Verify that our options got passed through
assert_json_matches(
name = "test",
file1 = ".swcrc",
file2 = "tsconfig.json",
filter1 = ".jsc.target",
filter2 = ".compilerOptions.target",
)
6 changes: 6 additions & 0 deletions examples/generate_swcrc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"private": true,
"devDependencies": {
"tsconfig-to-swcconfig": "~2.0.1"
}
}
1 change: 1 addition & 0 deletions examples/generate_swcrc/some.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 1;
6 changes: 6 additions & 0 deletions examples/generate_swcrc/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "es2017",
"module": "AMD"
}
}
3 changes: 3 additions & 0 deletions examples/generate_swcrc/write_swcrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { convert } = require("tsconfig-to-swcconfig");
const [tsconfig] = process.argv.slice(2);
console.log(JSON.stringify(convert(tsconfig), undefined, 2));
59 changes: 59 additions & 0 deletions examples/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions examples/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- generate_swcrc
4 changes: 1 addition & 3 deletions swc/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = Non
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`.
We recommend adding an `assert_json_matches` rule to guarantee
these don't accidentally diverge.
See an example in `examples/paths/BUILD.bazel`.
See the notes in [/docs/tsconfig.md].

out_dir: The base directory for output files relative to the output directory for this package

Expand Down