mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
a4478e960a
Add `oxc-codegen` package - a printer written in TS. ### Features - Prints JS, JSX, TS, TSX. - Matches `oxc_codegen` exactly on all Test262, TypeScript, Acorn-JSX fixtures. - Very fast. ### Deficiencies - Pretty-printing only (no compact/minified output). - No support for printing comments. - Source map support is semi-there, but largely untested, and poor API (design doc has some ideas on how to improve it). ### This PR adds - The printer code. - Conformance testing infra. - Further tests for edge cases. - README. - Design doc - how it differs from `oxc_codegen` and why, what produces the perf. ### This PR doesn't add - Not integrated into CI or release pipeline. ### Notes This started as a slop port of `oxc_codegen`. I've iterated on that base, cleaned it up a lot, and at least skimmed through all of it. I think it's in fairly decent shape. The design doc is I think worth a read. It explains the rationale for the choices, in particular the "1 codebase, 4 builds" approach. I've put it in a new top-level directory called `packages`. It's not an app, so didn't seem to me to fit alongside `apps/oxlint` and `apps/oxfmt`. It's pure JS, so not like e.g. `napi/parser`. And it felt too heavyweight to put into `npm` dir. But maybe there's a better place for it. Co-authored-by: Cameron <cameron.clark@hey.com>
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import type { Plugin } from "rolldown";
|
|
|
|
/**
|
|
* Plugin to remove the TypeScript-only regions of `print.ts` from the JS-only builds.
|
|
*
|
|
* The `TS` build-time constant (see `src-js/globals.d.ts`) lets the minifier drop TS-only code
|
|
* in expression positions - `if (TS && node.declare)` folds to nothing when `TS` is `false`.
|
|
* But `case "TSEnumDeclaration":` switch arms are not expressions, and no minifier can remove an arm
|
|
* on the grounds that its node type never occurs. Those arms are fenced with `IF TS` / `END_IF`
|
|
* block-comment markers (the same convention as the raw transfer deserializer builds):
|
|
*
|
|
* ```ts
|
|
* /* IF TS *\/
|
|
* case "TSEnumDeclaration":
|
|
* ...
|
|
* break;
|
|
* /* END_IF *\/
|
|
* ```
|
|
*
|
|
* (`*\/` escapes above only because this is itself a block comment.)
|
|
*
|
|
* This plugin deletes everything from each `IF TS` marker line through its `END_IF` marker line.
|
|
* Once the arms are gone, the TS-only printer functions they called lose their last references,
|
|
* and tree-shaking removes them wholesale.
|
|
*
|
|
* It's a plain text transform (the fences are only visible in source text), so it must run before
|
|
* the AST-based plugins in the plugin list, and only in builds where `TS` is `false`.
|
|
*/
|
|
const REGION = /[ \t]*\/\* IF TS \*\/\n[\s\S]*?\/\* END_IF \*\/\n/g;
|
|
|
|
// A factory, so each build counts its own regions rather than sharing a total with the others
|
|
const stripTsPlugin = (): Plugin => {
|
|
let strippedRegions = 0;
|
|
|
|
return {
|
|
name: "strip-ts",
|
|
|
|
buildStart() {
|
|
strippedRegions = 0;
|
|
},
|
|
|
|
transform: {
|
|
// Only process TS files in `src-js/print` directory
|
|
filter: { id: /\/src-js\/print\/.+\.ts$/ },
|
|
|
|
handler(code) {
|
|
code = code.replace(REGION, () => {
|
|
strippedRegions++;
|
|
return "";
|
|
});
|
|
|
|
// Unbalanced or nested fences leave a marker behind.
|
|
// Most files have no fences at all, so the "did this plugin do anything" check is per build, in `buildEnd`.
|
|
if (code.includes("IF TS") || code.includes("END_IF")) {
|
|
throw new Error("strip-ts: unbalanced or nested `/* IF TS */` / `/* END_IF */` fences");
|
|
}
|
|
|
|
return code;
|
|
},
|
|
},
|
|
|
|
buildEnd() {
|
|
// No fences anywhere means they have been lost and this plugin is silently doing nothing
|
|
if (strippedRegions === 0) {
|
|
throw new Error("strip-ts: no `/* IF TS */` / `/* END_IF */` fences found");
|
|
}
|
|
},
|
|
};
|
|
};
|
|
|
|
export default stripTsPlugin;
|