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>
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
// Source map generation.
|
|
//
|
|
// `write` records the output offset and original position of every mapped node as it goes,
|
|
// and this converts those offsets to generated line/column in one pass at the end.
|
|
|
|
import { debugAssert, typeAssertIs } from "../asserts.ts";
|
|
|
|
import type { Mapping, MutableMapping, SourceMapGenerator } from "./options.ts";
|
|
import type { State } from "../state.ts";
|
|
|
|
/**
|
|
* Convert the offsets recorded during printing into mappings, and hand them to the generator.
|
|
*
|
|
* Printing records only an output offset per mapped node, which is why this walks the output once
|
|
* at the end counting newlines, rather than tracking a line and column throughout.
|
|
*
|
|
* @param sourceMap - Generator the mappings are added to, which the caller was given in its options
|
|
*/
|
|
export function emitMappings(state: State, sourceMap: SourceMapGenerator): void {
|
|
debugAssert(
|
|
state.mapOffsets !== null && state.mapPositions !== null && state.mapNames !== null,
|
|
"Source map arrays should exist when a `sourceMap` was given",
|
|
);
|
|
|
|
const { output, mapOffsets, mapPositions, mapNames } = state;
|
|
const source = sourceMap.file || sourceMap._file;
|
|
|
|
// The `generated` and `mapping` objects are reused across `addMapping` calls to avoid generating ephemeral objects
|
|
const generated = { line: 1, column: 0 };
|
|
const mapping: MutableMapping = { original: null, generated, name: undefined, source };
|
|
|
|
let line = 1;
|
|
let lineStart = 0;
|
|
let nextNewline = output.indexOf("\n");
|
|
if (nextNewline === -1) nextNewline = Infinity;
|
|
|
|
const { length } = mapOffsets;
|
|
for (let i = 0; i < length; i++) {
|
|
const offset = mapOffsets[i];
|
|
while (offset > nextNewline) {
|
|
line++;
|
|
lineStart = nextNewline + 1;
|
|
const next = output.indexOf("\n", lineStart);
|
|
nextNewline = next === -1 ? Infinity : next;
|
|
}
|
|
|
|
generated.line = line;
|
|
generated.column = offset - lineStart;
|
|
mapping.original = mapPositions[i];
|
|
mapping.name = mapNames[i];
|
|
|
|
typeAssertIs<Mapping>(mapping);
|
|
sourceMap.addMapping(mapping);
|
|
}
|
|
}
|