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
19 changes: 11 additions & 8 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ crate.annotation(
patch_args = ["-p1"],
patches = ["//oj:oj-js-code-cache-bounded.patch"],
repositories = ["oj_crates"],
version = "0.2.5",
version = "0.2.16",
)
crate.annotation(
crate = "libsqlite3-sys",
Expand Down Expand Up @@ -162,7 +162,7 @@ crate.annotation(
patch_args = ["-p1"],
patches = ["//oj:oj-server-file-hmr.patch"],
repositories = ["oj_crates"],
version = "0.2.5",
version = "0.2.16",
)
crate.annotation(
additive_build_file_content = 'package(features = ["-static_link_cpp_runtimes"])',
Expand All @@ -171,7 +171,7 @@ crate.annotation(
patch_args = ["-p1"],
patches = ["//oj:oj-napi-link.patch"],
repositories = ["oj_crates"],
version = "0.2.5",
version = "0.2.16",
)
crate.annotation(
crate = "cranelift-assembler-x64",
Expand All @@ -193,7 +193,7 @@ crate.annotation(
patch_args = ["-p1"],
patches = ["//oj:napi-symbol-bytes.patch"],
repositories = ["oj_crates"],
version = "0.2.5",
version = "0.2.16",
)
crate.annotation(
crate = "deno_napi",
Expand Down Expand Up @@ -239,17 +239,20 @@ crate.annotation(
crate.annotation(
crate = "oj_deno_runtime",
patch_args = ["-p1"],
patches = ["//oj:deno-runtime-snapshot-source.patch"],
patches = [
"//oj:deno-runtime-snapshot-source.patch",
"//oj:oj-deno-fs-events-indexed.patch",
],
repositories = ["oj_crates"],
version = "0.2.5",
version = "0.2.16",
)
crate.annotation(
additive_build_file_content = 'package(features = ["-static_link_cpp_runtimes"])',
crate = "deno_snapshots",
crate = "oj_deno_snapshots",
patch_args = ["-p1"],
patches = ["//oj:deno-snapshots-source.patch"],
repositories = ["oj_crates"],
version = "0.74.0",
version = "0.2.16",
)
crate.annotation(
crate = "deno_features",
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TypeScript programs are source-native by default: `ts_compile` and `ts_test` kee

An opinionated Bazel ruleset for TypeScript, built around **Oxc and tsgo**. It builds TypeScript packages and provides a source-built dev server. For a different build model, see [aspect-build/rules_ts](https://github.com/aspect-build/rules_ts).

Rust and Go do the work: [Oxc](https://oxc.rs/) compiles an ES-module program and [tsgo](https://github.com/microsoft/typescript-go) a CommonJS-shaped one; tsgo type-checks. The default dev server is oj 0.2.5. [Vite](https://vite.dev/) remains an explicit option. [Gazelle](https://github.com/bazelbuild/bazel-gazelle) writes the BUILD files. Write `.ts`, run Gazelle, `bazel build //...`. The build reads no `node_modules/`. No system Node. Just Bazelisk.
Rust and Go do the work: [Oxc](https://oxc.rs/) compiles an ES-module program and [tsgo](https://github.com/microsoft/typescript-go) a CommonJS-shaped one; tsgo type-checks. The default dev server is oj 0.2.16. [Vite](https://vite.dev/) remains an explicit option. [Gazelle](https://github.com/bazelbuild/bazel-gazelle) writes the BUILD files. Write `.ts`, run Gazelle, `bazel build //...`. The build reads no `node_modules/`. No system Node. Just Bazelisk.

Coming from an existing TypeScript repository: [Install](#install) is the short
path, and the
Expand All @@ -18,7 +18,7 @@ covers the migration questions.
- **Oxc compiles opted-in programs** — With `emit = True`, Rust-based TypeScript/JSX transformer: `.js` + `.js.map` per file, and `.d.ts` too under `--//ts:declarations=oxc`. A program whose `module` is CommonJS-shaped is tsgo's emit — see [The Module Format](https://mikn.github.io/rules_typescript/rules/ts-compile/#the-module-format).
- **tsgo type-checks** — Go port of TypeScript, and, with `emit = True`, emits declarations too: no export annotations required, and the `.d.ts` are what `tsc` would produce. The check is a validation on every target and fails `bazel build` on a type error; the declarations are emitted where a dependent reads them.
- **Vitest and OJ can consume TypeScript sources** — The default `emit = False` on compile and test targets retains tsgo validation while removing JavaScript and declaration emission from their runtime path. See [source-only programs](docs/rules/ts-compile.md#source-only-programs) for supported consumers and package manifests.
- **The dev server is swappable** — `ts_dev_server(server = ...)` takes any target providing `DevServerInfo`. oj 0.2.5 is the default; select `@rules_typescript//vite:dev_server` for Vite. What a server does not read is declared in its provider, so a target depending on a field its server ignores fails at analysis time naming both.
- **The dev server is swappable** — `ts_dev_server(server = ...)` takes any target providing `DevServerInfo`. oj 0.2.16 is the default; select `@rules_typescript//vite:dev_server` for Vite. What a server does not read is declared in its provider, so a target depending on a field its server ignores fails at analysis time naming both.
- **Isolated declarations** — annotate the exports and build under `--//ts:declarations=oxc`, and Oxc emits the `.d.ts` syntactically, so a dependent waits for a per-file transform rather than for tsgo's declaration emit, which shortens a deep dependency chain substantially. Opt-in, per build — see [Cost of each mode](https://mikn.github.io/rules_typescript/rules/ts-compile/#cost-of-each-mode).
- **Gazelle generates BUILD files** — one package per compiler program, rooted in a `tsconfig.json` or existing manifest entry points, with sources and deps read off tsgo's own listing, without program-membership directives. It regenerates the attributes it owns on every run and names every value it drops, so a value it cannot derive needs `# keep` — see [Attributes Gazelle owns](https://mikn.github.io/rules_typescript/gazelle/directives/#attributes-gazelle-owns).
- **Direct dependencies** — a source may import only what a direct dep provides. A declaration arriving through another dep's own deps does not satisfy an import: the build fails naming the file, the specifier and the label to add, and `bazel run //:gazelle` writes it.
Expand Down
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ What is still thin:
| Gazelle BUILD generation | Production-ready (JS/TS, path aliases from tsconfig.json); alias resolution is deterministic, extension-spelling specifiers resolve, and deps come from the tsgo listing the strict-deps check reads. CI pins four properties of a run over the `gazelle_roundtrip` workspace (the output builds; generating twice from scratch is byte-identical; the test-target set is unchanged; `bazel test //...` passes on the output) and, on this tree, that every test source file is claimed by a test target (`tools/ci/check_test_sources.sh`). A run on this tree is not a no-op: `bazel run //gazelle -- -mode=diff` exits 1; the BUILD files here are hand-written, and nothing pins them to Gazelle's output |
| Testing (vitest) | Solid (DOM run for real, coverage, the user's config, snapshots read; written by `vitest -u` in the package, watch mode, debugging) |
| Bundling | `ts_binary` takes any `BundlerInfo` bundler, in the CLI mode or the generated-Vite-config mode; the ruleset ships no implementation, so nothing in this tree exercises the bundle action |
| Dev server + HMR | Pluggable: `ts_dev_server(server = ...)` takes a `DevServerInfo`, oj 0.2.5 by default; Vite is optional. Serves first-party source with Bazel out of the inner loop. The Vite option resolves bare npm specifiers through the `node_modules` tree via the `bazel:npm-resolve` plugin; codegen rebuilds and config-aware restarts under ibazel; does not typecheck |
| Dev server + HMR | Pluggable: `ts_dev_server(server = ...)` takes a `DevServerInfo`, oj 0.2.16 by default; Vite is optional. Serves first-party source with Bazel out of the inner loop. The Vite option resolves bare npm specifiers through the `node_modules` tree via the `bazel:npm-resolve` plugin; codegen rebuilds and config-aware restarts under ibazel; does not typecheck |
| IDE integration | Generated tsconfig + tsserver hook; `module_name` and `extra_exclude` supported. A package whose targets disagree with the root `compilerOptions` gets its own generated tsconfig, declared in `nested_tsconfigs` and staleness-tested; the root excludes those files individually so unclaimed ones stay in its program. Zero tsc errors across the root and all nine nested programs |
| CSS / assets | A `.css`, an image or a `.json` is a src of the `ts_compile` that imports it, staged beside the compiled `.js` and typed by the tsconfig (`vite/client`, a `declare module`, `resolveJsonModule`); a `*.module.css` is Vite's own CSS modules in the bundle and the dev server, and under vitest the class-name proxy `css.modules.classNameStrategy` shapes. Tailwind v4 works through `vite_config` under the dev server |
| CI/CD | Docs: remote caching (BuildBuddy/EngFlow), RBE, GitLab CI, non-determinism — documented, not exercised by this repo's CI |
Expand Down Expand Up @@ -621,7 +621,7 @@ Sub-projects that unlock real application support:
- Pure TypeScript library monorepo with npm deps, vitest tests, hermetic builds. Good for backend services, shared libraries, CLI tools.
- CSS and asset support: a `.css`, an image or a `.json` is a src of the `ts_compile` that imports it, typed by the tsconfig and staged beside the compiled `.js`.
- Gazelle: generates ts_compile and ts_test targets from TypeScript source files. Reads path aliases from tsconfig.json compilerOptions.paths/baseUrl.
- Dev server: ts_dev_server defaults to oj 0.2.5; the optional Vite server serves first-party source with Bazel out of the inner loop; bazel-bin supplies codegen output, assets and the npm tree. Under ibazel one Vite process lives across rebuilds and restarts only when the config's own inputs change. It does not typecheck, which is native Vite parity but makes the editor load-bearing. The server attr accepts DevServerInfo for custom dev server implementations. With the Vite server, react_refresh = True wires @vitejs/plugin-react for React Fast Refresh.
- Dev server: ts_dev_server defaults to oj 0.2.16; the optional Vite server serves first-party source with Bazel out of the inner loop; bazel-bin supplies codegen output, assets and the npm tree. Under ibazel one Vite process lives across rebuilds and restarts only when the config's own inputs change. It does not typecheck, which is native Vite parity but makes the editor load-bearing. The server attr accepts DevServerInfo for custom dev server implementations. With the Vite server, react_refresh = True wires @vitejs/plugin-react for React Fast Refresh.
- CI/CD: documented remote caching (BuildBuddy/EngFlow/self-hosted), remote execution, GitLab CI template, and known sources of non-determinism. Documented, not exercised: this repository's own CI configures no remote or disk cache.

**What doesn't work today:** production bundling and framework build
Expand Down
8 changes: 8 additions & 0 deletions changelog.d/dev-server-watcher-event-realpaths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### Fixed

- **A bazel-bin watcher event no longer resolves every declared file's
realpath.** vite-plugin-bazel asked whether each event's path was declared,
and a miss realpathed every declared path to compare; with thousands of
declared files, the dev server's own writes under bazel-bin kept the server
busy for minutes. A declared file is only compared against a canonical path;
declared directories are still checked for every path.
17 changes: 17 additions & 0 deletions changelog.d/oj-0.2.16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Fixed

- **The default oj dev server moves from 0.2.5 to 0.2.16.** Among the fixes:
a dependency's extensionless relative import in Start SSR resolves Vite-style
instead of answering 500 with `ERR_MODULE_NOT_FOUND`; a dependency's
`browser` field remaps its own relative imports; linked workspace packages
outside the app root are watched for HMR; a plugin-driven `server.restart()`
reaps every descendant process; plugins see Vite's resolved `server.fs`,
`server` defaults and absolute `cacheDir`; build plugins can spawn workers;
idle engines return memory; and `?v=`/`?t=` edits no longer write a new code
cache entry each. oj 0.2.16 replaces `deno_snapshots` and `deno_process` with
its own `oj_deno_snapshots` and `oj_deno_process` forks; the V8, deno_core and
Cranelift pins are unchanged; the snapshot fork's build script reads its
manifest directory at run time, so the compiled script embeds no absolute
path. The Start fallback-renderer backport is dropped (fixed upstream in
0.2.9); the code cache, plugin-resolved file HMR and `fs.watch` dispatch
patches are carried onto 0.2.16.
10 changes: 10 additions & 0 deletions changelog.d/oj-fs-watch-many-watchers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### Fixed

- **`ts_dev_server` on oj no longer hangs starting an app whose bazel-bin holds
thousands of watched entries.** oj's `fs.watch` matched every event against
every live watcher, opening both files each time, on the one notify thread
that also registers new watches. With vite-plugin-bazel watching each
directory and linked file under bazel-bin, that thread never caught up, and
the plugin host blocked in its next `fs.watch` call before
`configureServer` returned. Events now reach only the watchers indexed under
their path, its ancestors, or its file identity.
4 changes: 2 additions & 2 deletions docs/getting-started/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ at that version. BUILD generation uses the separate aspect-gazelle project.
| **Type-checker** | TypeScript tsc, including [native TypeScript 7][aspect-native] | tsgo |
| **Compilation boundary** | Per [`ts_project`][aspect-project]; `.d.ts` when declarations are enabled; each invokes `tsc --project` | `.d.ts` per target |
| **Bundler** | Bring your own | Bring your own, through `BundlerInfo` on `ts_binary` |
| **Dev server** | [`js_run_devserver`][aspect-devserver] (rules_js) runs the named binary or command; under ibazel it syncs changed `data` | oj 0.2.5 by default, optional Vite; any `DevServerInfo` rule per target |
| **Dev server** | [`js_run_devserver`][aspect-devserver] (rules_js) runs the named binary or command; under ibazel it syncs changed `data` | oj 0.2.16 by default, optional Vite; any `DevServerInfo` rule per target |
| **npm management** | rules_js (pnpm virtual store, symlinks) | Own pnpm lockfile reader: a `pnpm-lock.yaml` is required, npm and yarn lockfiles are not read; one Bazel repository per package, fetched on demand; pnpm's virtual store as Bazel artifacts |
| **BUILD generation** | Can use [aspect-gazelle][aspect-gazelle] with its `js` language (Apache-2.0), from source or a prebuilt binary | Gazelle (one package per `tsconfig.json`) |
| **Framework support** | None built-in | None built-in; a framework's Vite plugin runs in the dev server through `vite_config` |
Expand Down Expand Up @@ -100,7 +100,7 @@ tsgo listing the check reads.

### Dev server choice

oj 0.2.5 is the default dev server and provides native React Fast Refresh.
oj 0.2.16 is the default dev server and provides native React Fast Refresh.
Select Vite explicitly for framework Vite plugins. Both receive a generated
Vite-format config through `DevServerInfo`: `ts_dev_server(server = ...)` is a per-target
choice. With rules_js, [`js_run_devserver`][aspect-devserver] runs the binary
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/dev-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
//src/app:dev` builds the target once. The server then transforms first-party
source in memory, so a save reaches the browser without a Bazel build.

oj 0.2.5 is the default implementation at `@rules_typescript//oj:dev_server`.
oj 0.2.16 is the default implementation at `@rules_typescript//oj:dev_server`.
Vite is optional: set `server = "@rules_typescript//vite:dev_server"`.
Other implementations return `DevServerInfo`; see
[Bringing your own server](#bringing-your-own-server).

## Default oj server

oj and oj_server are pinned to 0.2.5 and built from Rust source. Their V8
oj and oj_server are pinned to 0.2.16 and built from Rust source. Their V8
dependency uses an upstream static library fetched by Bazel with a pinned
SHA256 digest; V8 itself is not compiled from source here. The native
binary reads the generated Vite-format config. Its command line supplies the
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TypeScript programs are source-native by default: `ts_compile` and `ts_test` kee

An opinionated Bazel ruleset for TypeScript, built around **Oxc and tsgo**. It builds TypeScript packages and provides a source-built dev server. For a different build model, see [aspect-build/rules_ts](https://github.com/aspect-build/rules_ts) ([comparison](getting-started/migration.md)).

Rust and Go do the work: [Oxc](https://oxc.rs/) compiles an ES-module program and [tsgo](https://github.com/microsoft/typescript-go) a CommonJS-shaped one; tsgo type-checks. The default dev server is oj 0.2.5. [Vite](https://vite.dev/) remains an explicit option. [Gazelle](https://github.com/bazelbuild/bazel-gazelle) writes the BUILD files. Write `.ts`, run Gazelle, `bazel build //...`. The build reads no `node_modules/`. No system Node. Just Bazelisk.
Rust and Go do the work: [Oxc](https://oxc.rs/) compiles an ES-module program and [tsgo](https://github.com/microsoft/typescript-go) a CommonJS-shaped one; tsgo type-checks. The default dev server is oj 0.2.16. [Vite](https://vite.dev/) remains an explicit option. [Gazelle](https://github.com/bazelbuild/bazel-gazelle) writes the BUILD files. Write `.ts`, run Gazelle, `bazel build //...`. The build reads no `node_modules/`. No system Node. Just Bazelisk.

Coming from an existing TypeScript monorepo, the
[Quick Start](getting-started/quickstart.md) is the whole path: four root files,
Expand All @@ -15,7 +15,7 @@ then `bazel run //:gazelle`. [Install](#install) and

- **Oxc compiles an ES-module program** — Rust-based TypeScript/JSX transformer: `.js` + `.js.map` per file, and `.d.ts` too under `--//ts:declarations=oxc`. A program whose `module` is CommonJS-shaped is tsgo's emit — see [The Module Format](rules/ts-compile.md#the-module-format).
- **tsgo validates every program** — The Go port of TypeScript checks source-mode and emitted programs. With `emit = True`, it is also the default declaration emitter. Unmodified TypeScript compiles: no export annotations required, and the `.d.ts` are what `tsc` would produce. tsgo runs as a build action, not a separate `tsc --noEmit` job, so type errors fail `bazel build`; the declarations are real outputs, and a package type-checks against what its dependency emits.
- **The dev server is swappable** — `ts_dev_server(server = ...)` takes any target providing `DevServerInfo`. oj 0.2.5 is the default; select `@rules_typescript//vite:dev_server` for Vite. Each server declares the config fields it does not read, so a target depending on one fails at analysis time naming the field and the server. `ts_dev_server` hands the source tree to the server; HMR is the server's, not a rebuild. See [Bringing your own server](guides/dev-server.md#bringing-your-own-server).
- **The dev server is swappable** — `ts_dev_server(server = ...)` takes any target providing `DevServerInfo`. oj 0.2.16 is the default; select `@rules_typescript//vite:dev_server` for Vite. Each server declares the config fields it does not read, so a target depending on one fails at analysis time naming the field and the server. `ts_dev_server` hands the source tree to the server; HMR is the server's, not a rebuild. See [Bringing your own server](guides/dev-server.md#bringing-your-own-server).
- **Isolated declarations** — annotate the exports, build under `--//ts:declarations=oxc`, and Oxc emits the `.d.ts` syntactically. A dependent waits for a per-file transform rather than for tsgo's declaration emit, which shortens a deep dependency chain substantially. Opt-in, per build. See [Cost of each mode](rules/ts-compile.md#cost-of-each-mode).
- **Gazelle generates the BUILD files** — one package per `tsconfig.json`, its sources and deps read off tsgo's own listing of the program. The tsconfig, lockfile and manifest own program membership; protobuf product identities are explicit.
- **Direct dependencies** — a source may import only what a direct dep provides. A declaration arriving through another dep's own deps does not satisfy an import; the build names the file, the specifier and the label to add, and Gazelle writes it.
Expand Down
Loading
Loading