mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
The split tools ran oxfmt in the checkout they rewrite. oxfmt looks up its configuration from that directory and from the formatted files' directories, and runs an oxfmt.config.ts it finds there, so a branch that rehome-split rewrites could run code with the maintainer's permissions. The tools now format through format.mjs, which passes the tools checkout's .oxfmtrc.jsonc with --config and disables the nested lookup, so no configuration file of the rewritten checkout is read or run. With no files to format it does nothing, where oxfmt would format the whole directory. The split-tools tests cover split-methods and move-state on a checkout whose root and src directory commit an oxfmt.config.ts that records it ran.
25 lines
2.7 KiB
Markdown
25 lines
2.7 KiB
Markdown
# Split tools
|
|
|
|
These scripts split a large source file into smaller files without changing behavior, and carry open branches across the split. Each transform is deterministic: the same config gives the same result on any version of the tree, and rehoming depends on that.
|
|
|
|
Run them from a checkout with `node_modules` installed. That checkout is the tools directory (`--tools`) and supplies `typescript` and `oxfmt`.
|
|
|
|
## Transforms
|
|
|
|
- `split-module.mjs plan|apply <config>` moves top-level declarations into new files. `plan` computes a manifest from seed declarations, and `apply` does the move.
|
|
- `split-methods.mjs apply <config>` moves class methods into exported functions that take the instance as a parameter (`emitter`). The class keeps a one-line stub with the same signature, so callers don't change.
|
|
- `move-state.mjs apply <config>` moves a group of class fields into a feature's state object, defined in the feature's file with `defineModuleState` (`packages/compiler/src/backend/llvm/emitter-state.ts`). It rewrites `this.<field>` and `emitter.<field>` to `KEY.of(this).<field>` and creates files from templates. It also applies the change's other edits as literal replacements, in order. A replacement whose text isn't found the expected number of times is reported and skipped.
|
|
|
|
Each transform takes `--repo <dir>` (the tree to rewrite) and `--tools <dir>`, and formats what it writes with the tools checkout's oxfmt and its `.oxfmtrc.jsonc` (`format.mjs`). The formatter reads no configuration from `--repo`, so an `oxfmt.config.ts` a branch commits is never run. The tools read and write the tree's files only at real paths inside `--repo`, and a config's templates and manifest only inside the config's directory. A path that resolves elsewhere, through a symlink or `..`, stops the tool with an error (`confine.mjs`). `configs/` holds the configs of splits that have landed. For example, `configs/loop-state/` moves the loop optimizations' state out of `LlEmitter`.
|
|
|
|
## Rehoming an open branch
|
|
|
|
```sh
|
|
git fetch origin pull/<n>/head:pr-<n>
|
|
node scripts/split/rehome-split.mjs --branch pr-<n> --onto origin/main --config scripts/split/configs/<split>/<config>.json
|
|
```
|
|
|
|
The result is the branch `pr-<n>-rehomed`, a merge of `origin/main` into the branch.
|
|
|
|
The script applies the split's transform to the branch's merge base and to its tip, then cherry-picks the difference onto `--onto`. It records the result as a merge commit with parents (branch tip, `--onto`), so it can be pushed without rewriting the branch; `--squash` makes one ordinary commit instead. Conflicts made only of import lines in the split's files resolve to both sides' imports. Any other conflict stops the script, which lists the files and leaves its scratch worktree for you to resolve.
|