perf(packages/codegen): add functions for writing/marking unnamed mappings (#25975)

Small optimization to sourcemap builds.

Add functions for writing with a source mapping, where it's known from what's being written that the mapping cannot need a name, because `node.name` is never a string, and change all call sites to use the appropriate function.

This allows removing 2 checks from ``recordSourceMapping``:

- `typeof node.name === "string"` - because that's now statically known.
- `printedName.length > 0` - because identifiers cannot be length.

All `write` and `markMap` functions take a `NamedMappableNode` or `UnnamedMappableNode`, which makes it a type error to call the wrong function.

`markMapNamed` becomes dead code, and is removed.
This commit is contained in:
overlookmotel committed 2026-08-24 07:24:06 +00:00
1 parent 343e1aa80c
commit 215a6a51ad
16 files changed
+260 -166

No files matched your search

+16 -11
View File
@@ -255,17 +255,22 @@ No operator merges with a plain `!`, so storing it must not cost `printSpaceBefo
Not every write needs to update `last`.
When one token is written in pieces - a string's opening quote, its contents, its closing quote -
only the last piece's category can possibly be read. Hence five write primitives in [`print/write.ts`]:
only the last piece's category can possibly be read. Hence seven write primitives in [`print/write.ts`]:
| Function | Updates `last` | Records a source mapping |
| :------------------------ | :------------- | :--------------------------- |
| `write` | Yes | No |
| `writeWithMapNamed` | Yes | At the node's start |
| `writeWithMapEnd` | Yes | At the node's last character |
| `writeNoLast` | No | No |
| `writeWithMapNamedNoLast` | No | At the node's start |
| Function | Updates `last` | Records a source mapping |
| :------------------------ | :------------- | :----------------------------- |
| `write` | Yes | No |
| `writeWithMap` | Yes | At the node's start |
| `writeWithMapNamed` | Yes | At the node's start, with name |
| `writeWithMapEnd` | Yes | At the node's last character |
| `writeNoLast` | No | No |
| `writeWithMapNoLast` | No | At the node's start |
| `writeWithMapNamedNoLast` | No | At the node's start, with name |
- The four `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.
- The `*Named` functions record the name the node had in the source, and take a `NamedMappableNode`,
which covers nodes with a string `name`.
- The other `writeWithMap*` functions take an `UnnamedMappableNode` which covers nodes without a string `name`.
- The three `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.
- The rule is exact: **only sound where the value of `last` is provably dead**.
Another real write must follow before anything reads it.
- JSX carries the longest runs. Printing `<a.b x="1">hi</a.b>` updates `last` exactly once, on the final `>`.
@@ -365,8 +370,8 @@ A plugin silently doing nothing is a failure mode worth guarding against.
#### `unmap_writes`
Rewrites every mapped write into the plain one it becomes with no mapping to record - so
`writeWithMapNamed(state, code, cat, node)` turns into `write(state, code, cat)` - and rewrites the import
to match. `writeWithMapEnd` becomes `write` too, and `writeWithMapNamedNoLast` becomes `writeNoLast`.
`writeWithMap(state, code, cat, node)` turns into `write(state, code, cat)` - and rewrites the import to match.
`writeWithMapNamed` and `writeWithMapEnd` become `write` too, and both `NoLast` forms become `writeNoLast`.
`printString` and `printNonNegativeFloat` take a node only to hand on to a mapped write. They keep their names,
but the argument comes off every call, and the parameter off their declarations - which, unlike the mapped writes,
@@ -2,7 +2,14 @@
import { typeAssertIs } from "../asserts.ts";
import { printPropertyKey } from "./binding_pattern.ts";
import { CAT_CLOSE_BRACKET, CAT_IDENT, CAT_OTHER, write, writeWithMapNamed } from "./write.ts";
import {
CAT_CLOSE_BRACKET,
CAT_IDENT,
CAT_OTHER,
write,
writeWithMap,
writeWithMapNamed,
} from "./write.ts";
import { printExpression, printMemberExpression } from "./expression.ts";
import { printSpaceBeforeIdentifier } from "./space.ts";
import { CTX_NONE } from "./operators.ts";
@@ -53,7 +60,7 @@ export function printAssignmentTarget(node: ESTree.AssignmentTarget, state: Stat
* Print an object destructuring target, as in `({ a, b: c } = obj)`.
*/
function printObjectAssignmentTarget(node: ESTree.ObjectAssignmentTarget, state: State): void {
writeWithMapNamed(state, "{", CAT_OTHER, node);
writeWithMap(state, "{", CAT_OTHER, node);
const { properties } = node;
const { length } = properties;
@@ -62,7 +69,7 @@ function printObjectAssignmentTarget(node: ESTree.ObjectAssignmentTarget, state:
const property = properties[i];
if (property.type === "RestElement") {
writeWithMapNamed(state, "...", CAT_OTHER, property);
writeWithMap(state, "...", CAT_OTHER, property);
printAssignmentTarget(property.argument, state);
} else {
printAssignmentTargetProperty(property, state);
@@ -139,7 +146,7 @@ function printArrayAssignmentTarget(node: ESTree.ArrayAssignmentTarget, state: S
}
}
writeWithMapNamed(state, "[", CAT_OTHER, node);
writeWithMap(state, "[", CAT_OTHER, node);
for (let i = 0; i < length; i++) {
if (i !== 0) write(state, ", ", CAT_OTHER);
@@ -155,7 +162,7 @@ function printArrayAssignmentTarget(node: ESTree.ArrayAssignmentTarget, state: S
if (rest !== null) {
if (length > 0) write(state, " ", CAT_OTHER);
writeWithMapNamed(state, "...", CAT_OTHER, rest);
writeWithMap(state, "...", CAT_OTHER, rest);
printAssignmentTarget(rest.argument, state);
}
@@ -7,6 +7,7 @@ import {
CAT_OTHER,
CAT_QUESTION,
write,
writeWithMap,
writeWithMapNamed,
writeWithMapNamedNoLast,
} from "./write.ts";
@@ -59,7 +60,7 @@ export function printBindingPattern(node: BindingPatternNode | UnknownNode, stat
printExpression(node.right, state, PREC_COMMA, CTX_NONE);
break;
case "RestElement":
writeWithMapNamed(state, "...", CAT_OTHER, node);
writeWithMap(state, "...", CAT_OTHER, node);
printBindingPattern(node.argument, state);
if (TS && node.typeAnnotation != null) printTypeAnnotation(node.typeAnnotation, state);
break;
@@ -76,17 +77,17 @@ function printObjectBindingPattern(node: ESTree.ObjectPattern, state: State): vo
const { length } = properties;
if (length === 0) {
writeWithMapNamed(state, "{}", CAT_OTHER, node);
writeWithMap(state, "{}", CAT_OTHER, node);
return;
}
writeWithMapNamed(state, "{ ", CAT_OTHER, node);
writeWithMap(state, "{ ", CAT_OTHER, node);
for (let i = 0; i < length; i++) {
if (i > 0) write(state, ", ", CAT_OTHER);
const property = properties[i];
if (property.type === "RestElement") {
writeWithMapNamed(state, "...", CAT_OTHER, property);
writeWithMap(state, "...", CAT_OTHER, property);
printBindingPattern(property.argument, state);
} else {
printBindingProperty(property, state);
@@ -177,7 +178,7 @@ function printArrayBindingPattern(node: ESTree.ArrayPattern, state: State): void
}
}
writeWithMapNamed(state, "[", CAT_OTHER, node);
writeWithMap(state, "[", CAT_OTHER, node);
for (let i = 0; i < length; i++) {
if (i !== 0) write(state, ", ", CAT_OTHER);
+16 -15
View File
@@ -10,12 +10,13 @@ import {
CAT_QUESTION,
CAT_START_OF_STMT,
debugAssertLastFresh,
markMapNamed,
markMapStart,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamedNoLast,
writeWithMapNamed,
writeWithMapNoLast,
} from "./write.ts";
import { printExpression } from "./expression.ts";
import { printFunctionBody, printParenParams } from "./function.ts";
@@ -64,18 +65,18 @@ export function printClass(node: ESTree.Class, state: State): void {
const abstract = TS && node.abstract;
// The node's mapping goes on whichever of these is written first
if (declare) writeWithMapNamed(state, "declare ", CAT_OTHER, node);
if (declare) writeWithMap(state, "declare ", CAT_OTHER, node);
if (abstract) {
if (declare) {
write(state, "abstract ", CAT_OTHER);
} else {
writeWithMapNamed(state, "abstract ", CAT_OTHER, node);
writeWithMap(state, "abstract ", CAT_OTHER, node);
}
}
if (declare || abstract) {
write(state, "class", CAT_IDENT);
} else {
writeWithMapNamed(state, "class", CAT_IDENT, node);
writeWithMap(state, "class", CAT_IDENT, node);
}
if (node.id != null) {
@@ -119,7 +120,7 @@ export function printDecorators(decorators: ESTree.Decorator[], state: State): v
for (let i = 0; i < length; i++) {
const decorator = decorators[i];
writeWithMapNamed(state, "@", CAT_OTHER, decorator);
writeWithMap(state, "@", CAT_OTHER, decorator);
const { expression } = decorator;
const wrap = decoratorNeedsWrap(expression);
@@ -161,12 +162,12 @@ function printClassBody(node: ClassBodyNode, state: State): void {
const { body } = node;
const { length } = body;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", node);
writeWithMapNoLast(state, "{", node);
writeWithMapEnd(state, "}", CAT_OTHER, node);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, node);
writeWithMap(state, "{\n", CAT_OTHER, node);
state.indentLevel++;
@@ -221,7 +222,7 @@ function printClassBody(node: ClassBodyNode, state: State): void {
* Print a method, including getters, setters, constructors and their modifiers.
*/
function printMethodDefinition(node: MethodDefinitionNode, state: State): void {
markMapNamed(state, node);
markMapStart(state, node);
const { decorators } = node;
if (decorators != null && decorators.length > 0) printDecorators(decorators, state);
@@ -293,7 +294,7 @@ function printMethodDefinition(node: MethodDefinitionNode, state: State): void {
* Print a class field, with its modifiers and initializer.
*/
function printPropertyDefinition(node: PropertyDefinitionNode, state: State): void {
markMapNamed(state, node);
markMapStart(state, node);
const { decorators } = node;
if (decorators != null && decorators.length > 0) printDecorators(decorators, state);
@@ -356,17 +357,17 @@ function printPropertyDefinition(node: PropertyDefinitionNode, state: State): vo
function printStaticBlock(node: ESTree.StaticBlock, state: State): void {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "static ", CAT_OTHER, node);
writeWithMap(state, "static ", CAT_OTHER, node);
const { body } = node;
const { length } = body;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", node);
writeWithMapNoLast(state, "{", node);
writeWithMapEnd(state, "}", CAT_OTHER, node);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, node);
writeWithMap(state, "{\n", CAT_OTHER, node);
state.indentLevel++;
@@ -384,7 +385,7 @@ function printStaticBlock(node: ESTree.StaticBlock, state: State): void {
* Print an `accessor` field.
*/
function printAccessorProperty(node: AccessorPropertyNode, state: State): void {
markMapNamed(state, node);
markMapStart(state, node);
const { decorators } = node;
if (decorators != null && decorators.length > 0) printDecorators(decorators, state);
+26 -24
View File
@@ -16,14 +16,16 @@ import {
CAT_START_OF_ARROW_EXPR,
CAT_START_OF_STMT,
debugAssertLastFresh,
markMapNamed,
markMapAfter,
markMapAtStartOffset,
markMapStart,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamed,
writeWithMapNamedNoLast,
writeWithMapNoLast,
} from "./write.ts";
import { printClass } from "./class.ts";
import {
@@ -148,11 +150,11 @@ export function printExpression(
break;
case "ThisExpression":
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "this", CAT_IDENT, node);
writeWithMap(state, "this", CAT_IDENT, node);
break;
case "Super":
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "super", CAT_IDENT, node);
writeWithMap(state, "super", CAT_IDENT, node);
break;
case "NewExpression":
printNewExpression(node, state, precedence);
@@ -161,7 +163,7 @@ export function printExpression(
printTemplateLiteral(node, state);
break;
case "TaggedTemplateExpression":
markMapNamed(state, node);
markMapStart(state, node);
printExpression(node.tag, state, PREC_POSTFIX, ctx & CTX_FORBID_CALL);
if (TS) printTypeArguments(node.typeArguments, state);
printTemplateLiteral(node.quasi, state);
@@ -180,7 +182,7 @@ export function printExpression(
break;
case "MetaProperty":
printSpaceBeforeIdentifier(state);
writeWithMapNamedNoLast(state, node.meta.name, node);
writeWithMapNoLast(state, node.meta.name, node);
writeNoLast(state, ".");
write(state, node.property.name, CAT_IDENT);
break;
@@ -363,7 +365,7 @@ function printArguments(
const arg = args[i];
if (arg.type === "SpreadElement") {
writeWithMapNamed(state, "...", CAT_OTHER, arg);
writeWithMap(state, "...", CAT_OTHER, arg);
printExpression(arg.argument, state, PREC_COMMA, CTX_NONE);
} else {
printExpression(arg, state, PREC_COMMA, CTX_NONE);
@@ -388,7 +390,7 @@ export function printPrivateInExpression(
const wrap = precedence >= PREC_COMPARE;
if (wrap) write(state, "(", CAT_OTHER);
markMapNamed(state, node);
markMapStart(state, node);
writeWithMapNamedNoLast(state, "#", node.left);
write(state, node.left.name, CAT_IDENT);
write(state, " in ", CAT_OTHER);
@@ -415,7 +417,7 @@ function printObjectExpression(node: ESTree.ObjectExpression, state: State): voi
const { length } = properties;
const isMultiLine = length > 1;
writeWithMapNamed(state, "{", CAT_OTHER, node);
writeWithMap(state, "{", CAT_OTHER, node);
if (isMultiLine) {
state.indentLevel++;
@@ -448,7 +450,7 @@ function printObjectExpression(node: ESTree.ObjectExpression, state: State): voi
*/
function printObjectProperty(node: ESTree.ObjectPropertyKind, state: State): void {
if (node.type === "SpreadElement") {
writeWithMapNamed(state, "...", CAT_OTHER, node);
writeWithMap(state, "...", CAT_OTHER, node);
printExpression(node.argument, state, PREC_COMMA, CTX_NONE);
return;
}
@@ -458,7 +460,7 @@ function printObjectProperty(node: ESTree.ObjectPropertyKind, state: State): voi
value.type === "FunctionExpression"
|| (TS && value.type === "TSEmptyBodyFunctionExpression")
) {
markMapNamed(state, node);
markMapStart(state, node);
const { kind } = node;
const isGetter = kind === "get";
@@ -561,7 +563,7 @@ function printArrayExpression(node: ESTree.ArrayExpression, state: State): void
const { length } = elements;
const isMultiLine = length > 2;
writeWithMapNamed(state, "[", CAT_OTHER, node);
writeWithMap(state, "[", CAT_OTHER, node);
if (isMultiLine) state.indentLevel++;
@@ -576,7 +578,7 @@ function printArrayExpression(node: ESTree.ArrayExpression, state: State): void
const element = elements[i];
if (element != null) {
if (element.type === "SpreadElement") {
writeWithMapNamed(state, "...", CAT_OTHER, element);
writeWithMap(state, "...", CAT_OTHER, element);
printExpression(element.argument, state, PREC_COMMA, CTX_NONE);
} else {
printExpression(element, state, PREC_COMMA, CTX_NONE);
@@ -621,7 +623,7 @@ function printAssignmentExpression(
if (wrap) write(state, "(", CAT_OTHER);
markMapNamed(state, node);
markMapStart(state, node);
printAssignmentTarget(left, state);
write(state, PADDED_ASSIGN_OPERATORS[node.operator], CAT_OTHER);
@@ -651,10 +653,10 @@ function printUpdateExpression(
if (node.prefix) {
printSpaceBeforeOperator(state, operatorCode);
writeWithMapNamed(state, node.operator, operatorCode, node);
writeWithMap(state, node.operator, operatorCode, node);
printExpression(node.argument, state, PREC_PREFIX, ctx);
} else {
markMapNamed(state, node);
markMapStart(state, node);
printExpression(node.argument, state, PREC_POSTFIX, ctx);
printSpaceBeforeOperator(state, operatorCode);
write(state, node.operator, operatorCode);
@@ -684,7 +686,7 @@ function printUnaryExpression(
if (operator.length > 1) {
// typeof, void, delete
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, operator, CAT_IDENT, node);
writeWithMap(state, operator, CAT_IDENT, node);
write(state, " ", CAT_OTHER);
// `delete Infinity` is a syntax error in strict mode
isDeleteInfinity =
@@ -696,7 +698,7 @@ function printUnaryExpression(
if (operatorCode === CAT_OP_UN_NOT && state.last === CAT_LT) {
operatorCode = CAT_OP_UN_NOT_AFTER_LT;
}
writeWithMapNamed(state, operator, operatorCode, node);
writeWithMap(state, operator, operatorCode, node);
}
if (isDeleteInfinity) write(state, "(0, ", CAT_OTHER);
@@ -796,7 +798,7 @@ function printArrowFunctionExpression(
if (node.async) {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "async ", CAT_OTHER, node);
writeWithMap(state, "async ", CAT_OTHER, node);
}
if (TS) printTypeParameters(node.typeParameters, state);
@@ -832,7 +834,7 @@ function printNewExpression(node: ESTree.NewExpression, state: State, precedence
if (wrap) write(state, "(", CAT_OTHER);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "new ", CAT_OTHER, node);
writeWithMap(state, "new ", CAT_OTHER, node);
printExpression(node.callee, state, PREC_NEW, CTX_FORBID_CALL);
if (TS) printTypeArguments(node.typeArguments, state);
printArguments(node, node.arguments, state);
@@ -847,7 +849,7 @@ function printNewExpression(node: ESTree.NewExpression, state: State, precedence
* Substitutions print from `PREC_LOWEST` with no context flags, since `${` and `}` fence them from everything around.
*/
function printTemplateLiteral(node: ESTree.TemplateLiteral, state: State): void {
writeWithMapNamedNoLast(state, "`", node);
writeWithMapNoLast(state, "`", node);
const { quasis, expressions } = node;
const { length } = expressions;
@@ -898,7 +900,7 @@ function printAwaitExpression(
if (wrap) write(state, "(", CAT_OTHER);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "await ", CAT_OTHER, node);
writeWithMap(state, "await ", CAT_OTHER, node);
printExpression(node.argument, state, PREC_EXPONENTIATION, ctx);
if (wrap) write(state, ")", CAT_CLOSE_BRACKET);
@@ -917,7 +919,7 @@ function printYieldExpression(
if (wrap) write(state, "(", CAT_OTHER);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "yield", CAT_IDENT, node);
writeWithMap(state, "yield", CAT_IDENT, node);
if (node.delegate) write(state, "*", CAT_OTHER);
@@ -945,7 +947,7 @@ function printImportExpression(
if (wrap) write(state, "(", CAT_OTHER);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "import", CAT_IDENT, node);
writeWithMap(state, "import", CAT_IDENT, node);
if (node.phase != null) {
writeNoLast(state, ".");
+7 -6
View File
@@ -11,9 +11,10 @@ import {
markMapStart,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamedNoLast,
writeWithMapNamed,
writeWithMapNoLast,
} from "./write.ts";
import { printDecorators } from "./class.ts";
import { printSpaceBeforeIdentifier } from "./space.ts";
@@ -45,10 +46,10 @@ export function printFunction(node: ESTree.Function, state: State): void {
// The node's mapping goes on whichever of these is written first
const declare = TS && node.declare;
if (declare) {
writeWithMapNamed(state, "declare ", CAT_OTHER, node);
writeWithMap(state, "declare ", CAT_OTHER, node);
write(state, node.async ? "async function" : "function", CAT_IDENT);
} else {
writeWithMapNamed(state, node.async ? "async function" : "function", CAT_IDENT, node);
writeWithMap(state, node.async ? "async function" : "function", CAT_IDENT, node);
}
if (node.generator) write(state, "* ", CAT_OTHER);
@@ -152,12 +153,12 @@ export function printFunctionBody(body: ESTree.FunctionBody, state: State): void
// `body` is a BlockStatement holding directives + statements.
const statements = body.body;
if (statements.length === 0) {
writeWithMapNamedNoLast(state, "{", body);
writeWithMapNoLast(state, "{", body);
writeWithMapEnd(state, "}", CAT_OTHER, body);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, body);
writeWithMap(state, "{\n", CAT_OTHER, body);
state.indentLevel++;
printDirectivesAndStatements(statements, state);
state.indentLevel--;
+9 -8
View File
@@ -5,9 +5,10 @@ import {
CAT_OTHER,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamedNoLast,
writeWithMapNoLast,
} from "./write.ts";
import { printExpression } from "./expression.ts";
import { CTX_NONE } from "./operators.ts";
@@ -27,7 +28,7 @@ import type * as ESTree from "../../../../npm/oxc-types/types.d.ts";
export function printJSXElement(node: ESTree.JSXElement, state: State): void {
const { openingElement } = node;
writeWithMapNamedNoLast(state, "<", openingElement);
writeWithMapNoLast(state, "<", openingElement);
printJSXElementName(openingElement.name, state);
if (TS) printTypeArguments(openingElement.typeArguments, state);
@@ -38,7 +39,7 @@ export function printJSXElement(node: ESTree.JSXElement, state: State): void {
writeNoLast(state, " ");
const attribute = attributes[i];
if (attribute.type === "JSXSpreadAttribute") {
writeWithMapNamed(state, "{...", CAT_OTHER, attribute);
writeWithMap(state, "{...", CAT_OTHER, attribute);
printExpression(attribute.argument, state, PREC_COMMA, CTX_NONE);
writeWithMapEnd(state, "}", CAT_OTHER, attribute);
} else {
@@ -60,7 +61,7 @@ export function printJSXElement(node: ESTree.JSXElement, state: State): void {
printJSXChild(children[i], state);
}
writeWithMapNamedNoLast(state, "</", closingElement);
writeWithMapNoLast(state, "</", closingElement);
printJSXElementName(closingElement.name, state);
write(state, ">", CAT_OTHER);
}
@@ -87,7 +88,7 @@ function printJSXElementName(
writeWithMapNamedNoLast(state, node.name.name, node.name);
break;
case "ThisExpression":
writeWithMapNamedNoLast(state, "this", node);
writeWithMapNoLast(state, "this", node);
break;
default:
throw new Error(`Unknown JSX name type: ${node.type}`);
@@ -178,7 +179,7 @@ function printJSXExpressionContainer(node: ESTree.JSXExpressionContainer, state:
* Print a `<>...</>` fragment.
*/
export function printJSXFragment(node: ESTree.JSXFragment, state: State): void {
writeWithMapNamedNoLast(state, "<>", node.openingFragment);
writeWithMapNoLast(state, "<>", node.openingFragment);
const { children } = node;
const { length } = children;
@@ -186,7 +187,7 @@ export function printJSXFragment(node: ESTree.JSXFragment, state: State): void {
printJSXChild(children[i], state);
}
writeWithMapNamed(state, "</>", CAT_OTHER, node.closingFragment);
writeWithMap(state, "</>", CAT_OTHER, node.closingFragment);
}
/**
@@ -198,7 +199,7 @@ export function printJSXFragment(node: ESTree.JSXFragment, state: State): void {
function printJSXChild(node: ESTree.JSXChild | UnknownNode, state: State): void {
switch (node.type) {
case "JSXText":
writeWithMapNamedNoLast(state, node.raw != null ? node.raw : node.value, node);
writeWithMapNoLast(state, node.raw != null ? node.raw : node.value, node);
break;
case "JSXExpressionContainer":
printJSXExpressionContainer(node, state);
+11 -11
View File
@@ -10,8 +10,8 @@ import {
CAT_REGEX_SLASH,
write,
writeNoLast,
writeWithMapNamed,
writeWithMapNamedNoLast,
writeWithMap,
writeWithMapNoLast,
} from "./write.ts";
import { printSpaceBeforeIdentifier, printSpaceBeforeOperator } from "./space.ts";
import { printNonNegativeFloat } from "./number.ts";
@@ -60,7 +60,7 @@ export function printLiteral(
break;
case "boolean":
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, value ? "true" : "false", CAT_IDENT, node);
writeWithMap(state, value ? "true" : "false", CAT_IDENT, node);
break;
default:
typeAssertIs<LiteralExtras>(node);
@@ -73,7 +73,7 @@ export function printLiteral(
} else {
// `null`
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "null", CAT_IDENT, node);
writeWithMap(state, "null", CAT_IDENT, node);
}
break;
}
@@ -102,7 +102,7 @@ function printNumericLiteral(
// `CAT_IDENT` rather than `CAT_INT_DIGIT` - raw text only prints inside a TS type,
// where no member `.` can follow, so there is no `0 .toExponential()` hazard to separate.
const { raw } = node;
writeWithMapNamed(state, raw, raw[raw.length - 1] === "." ? CAT_OTHER : CAT_IDENT, node);
writeWithMap(state, raw, raw[raw.length - 1] === "." ? CAT_OTHER : CAT_IDENT, node);
return;
}
@@ -113,7 +113,7 @@ function printNumericLiteral(
printNonNegativeFloat(state, value, node);
} else if (Number.isNaN(value)) {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "NaN", CAT_IDENT, node);
writeWithMap(state, "NaN", CAT_IDENT, node);
} else if (!Number.isFinite(value)) {
const negative = value < 0;
const wrap = negative && precedence >= PREC_PREFIX;
@@ -125,13 +125,13 @@ function printNumericLiteral(
} else {
printSpaceBeforeIdentifier(state);
}
writeWithMapNamed(state, "Infinity", CAT_IDENT, node);
writeWithMap(state, "Infinity", CAT_IDENT, node);
if (wrap) write(state, ")", CAT_CLOSE_BRACKET);
} else if (Object.is(value, 0)) {
// +0 exactly - `Object.is` rejects `-0`, which the negative paths below print as `-0`
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "0", CAT_INT_DIGIT, node);
writeWithMap(state, "0", CAT_INT_DIGIT, node);
} else if (precedence >= PREC_PREFIX) {
writeNoLast(state, "(-");
printNonNegativeFloat(state, -value, node);
@@ -169,7 +169,7 @@ function printRegExpLiteral(node: ESTree.RegExpLiteral, state: State): void {
// * `last === CAT_REGEX_SLASH` keeps `/a//b/` from lexing as a line comment
// * `CAT_LT` arm keeps `<` followed by `/script...` from closing a host `<script>` element.
writeWithMapNamedNoLast(state, "/", node);
writeWithMapNoLast(state, "/", node);
writeNoLast(state, node.regex.pattern);
// `CAT_REGEX_SLASH` rather than `CAT_OTHER`. It means "a regex just closed", which is what the
@@ -192,11 +192,11 @@ function printBigIntLiteral(node: ESTree.BigIntLiteral, state: State, precedence
const value = node.bigint;
if (value.startsWith("-") && precedence >= PREC_PREFIX) {
writeWithMapNamedNoLast(state, "(", node);
writeWithMapNoLast(state, "(", node);
writeNoLast(state, value);
write(state, "n)", CAT_CLOSE_BRACKET);
} else {
writeWithMapNamedNoLast(state, value, node);
writeWithMapNoLast(state, value, node);
write(state, "n", CAT_IDENT);
}
}
+7 -6
View File
@@ -6,6 +6,7 @@ import {
CAT_START_OF_DEFAULT_EXPORT,
write,
writeNoLast,
writeWithMap,
writeWithMapNamed,
} from "./write.ts";
import { printClass } from "./class.ts";
@@ -39,7 +40,7 @@ export function printImportDeclaration(node: ESTree.ImportDeclaration, state: St
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "import", CAT_IDENT, node);
writeWithMap(state, "import", CAT_IDENT, node);
if (TS && node.importKind === "type") write(state, " type", CAT_IDENT);
@@ -74,7 +75,7 @@ export function printImportDeclaration(node: ESTree.ImportDeclaration, state: St
}
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, specifier.local.name, CAT_IDENT, specifier);
writeWithMap(state, specifier.local.name, CAT_IDENT, specifier);
if (i === length - 1) write(state, " ", CAT_OTHER);
@@ -142,7 +143,7 @@ function printImportAttributes(
// ESTree omits the `WithClause` wrapper. The Rust reference normalizes its mapping anchor
// to the first attribute, which is the first location both representations carry.
writeNoLast(state, " ");
writeWithMapNamed(state, "with { ", CAT_OTHER, attributes[0]);
writeWithMap(state, "with { ", CAT_OTHER, attributes[0]);
for (let i = 0; i < length; i++) {
if (i > 0) write(state, ", ", CAT_OTHER);
@@ -189,7 +190,7 @@ function moduleExportName(node: ESTree.ModuleExportName, state: State): string {
export function printExportNamedDeclaration(node: ExportNamedDeclarationNode, state: State): void {
printIndent(state);
writeWithMapNamed(state, "export ", CAT_OTHER, node);
writeWithMap(state, "export ", CAT_OTHER, node);
const { declaration } = node;
if (declaration != null) {
@@ -281,7 +282,7 @@ export function printExportNamedDeclaration(node: ExportNamedDeclarationNode, st
export function printExportAllDeclaration(node: ESTree.ExportAllDeclaration, state: State): void {
printIndent(state);
writeWithMapNamed(
writeWithMap(
state,
TS && node.exportKind === "type" ? "export type *" : "export *",
CAT_OTHER,
@@ -311,7 +312,7 @@ export function printExportDefaultDeclaration(
): void {
printIndent(state);
writeWithMapNamed(state, "export default ", CAT_OTHER, node);
writeWithMap(state, "export default ", CAT_OTHER, node);
const { declaration } = node;
switch (declaration.type) {
+34 -33
View File
@@ -9,12 +9,13 @@ import {
CAT_OP_UN_NOT,
CAT_OTHER,
CAT_START_OF_STMT,
markMapNamed,
markMapStart,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamedNoLast,
writeWithMapNamed,
writeWithMapNoLast,
} from "./write.ts";
import { printClass } from "./class.ts";
import { printExpression } from "./expression.ts";
@@ -87,7 +88,7 @@ export function printDirectivesAndStatements(
if (inner.type === "Literal" && typeof inner.value === "string") {
const mapsIndent = state.indentLevel > 0 || state.pendingIndentAsSpace;
printIndent(state);
if (mapsIndent) markMapNamed(state, stmt);
if (mapsIndent) markMapStart(state, stmt);
writeNoLast(state, "(");
printString(state, inner.value, inner);
write(state, ");\n", CAT_OTHER);
@@ -128,7 +129,7 @@ function printDirective(stmt: ESTree.Directive, state: State): void {
}
}
writeWithMapNamedNoLast(state, quote, stmt);
writeWithMapNoLast(state, quote, stmt);
writeNoLast(state, directive);
writeNoLast(state, quote);
write(state, ";\n", CAT_OTHER);
@@ -185,7 +186,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "BreakStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "break", CAT_IDENT, node);
writeWithMap(state, "break", CAT_IDENT, node);
if (node.label != null) {
write(state, " ", CAT_OTHER);
writeWithMapNamed(state, node.label.name, CAT_IDENT, node.label);
@@ -195,7 +196,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "ContinueStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "continue", CAT_IDENT, node);
writeWithMap(state, "continue", CAT_IDENT, node);
if (node.label != null) {
write(state, " ", CAT_OTHER);
writeWithMapNamed(state, node.label.name, CAT_IDENT, node.label);
@@ -208,7 +209,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "ThrowStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "throw ", CAT_OTHER, node);
writeWithMap(state, "throw ", CAT_OTHER, node);
printExpression(node.argument, state, PREC_LOWEST, CTX_NONE);
write(state, ";\n", CAT_OTHER);
break;
@@ -226,14 +227,14 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "LabeledStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
markMapNamed(state, node);
markMapStart(state, node);
writeWithMapNamed(state, node.label.name, CAT_IDENT, node.label);
write(state, ":", CAT_OTHER);
printBody(node.body, state);
break;
case "EmptyStatement":
printIndent(state);
writeWithMapNamed(state, ";\n", CAT_OTHER, node);
writeWithMap(state, ";\n", CAT_OTHER, node);
break;
case "ImportDeclaration":
printImportDeclaration(node, state);
@@ -250,7 +251,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "WithStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "with(", CAT_OTHER, node);
writeWithMap(state, "with(", CAT_OTHER, node);
printExpression(node.object, state, PREC_LOWEST, CTX_NONE);
write(state, ")", CAT_CLOSE_BRACKET);
printBody(node.body, state);
@@ -258,7 +259,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
case "DebuggerStatement":
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "debugger;\n", CAT_OTHER, node);
writeWithMap(state, "debugger;\n", CAT_OTHER, node);
break;
/* IF TS */
case "TSModuleDeclaration":
@@ -318,7 +319,7 @@ export function printStatement(node: ESTree.Statement | UnknownNode, state: Stat
function printExpressionStatement(node: ESTree.ExpressionStatement, state: State): void {
const mapsIndent = state.indentLevel > 0 || state.pendingIndentAsSpace;
printIndent(state);
if (mapsIndent) markMapNamed(state, node);
if (mapsIndent) markMapStart(state, node);
state.last = CAT_START_OF_STMT;
printExpression(node.expression, state, PREC_LOWEST, CTX_NONE);
write(state, ";\n", CAT_OTHER);
@@ -341,10 +342,10 @@ export function printVariableDeclaration(
// The node's mapping goes on whichever of these is written first
const declare = TS && node.declare;
if (declare) {
writeWithMapNamed(state, "declare ", CAT_OTHER, node);
writeWithMap(state, "declare ", CAT_OTHER, node);
write(state, node.kind, CAT_IDENT);
} else {
writeWithMapNamed(state, node.kind, CAT_IDENT, node);
writeWithMap(state, node.kind, CAT_IDENT, node);
}
const { declarations } = node;
@@ -387,12 +388,12 @@ function printBlockStatement(block: ESTree.BlockStatement, state: State): void {
const { body } = block;
const { length } = body;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", block);
writeWithMapNoLast(state, "{", block);
writeWithMapEnd(state, "}", CAT_OTHER, block);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, block);
writeWithMap(state, "{\n", CAT_OTHER, block);
state.indentLevel++;
for (let i = 0; i < length; i++) {
@@ -414,7 +415,7 @@ function printBlockStatement(block: ESTree.BlockStatement, state: State): void {
function printIf(node: ESTree.IfStatement, state: State): void {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "if (", CAT_OTHER, node);
writeWithMap(state, "if (", CAT_OTHER, node);
printExpression(node.test, state, PREC_LOWEST, CTX_NONE);
@@ -425,7 +426,7 @@ function printIf(node: ESTree.IfStatement, state: State): void {
write(state, alternate != null ? " " : "\n", CAT_OTHER);
} else if (wrapToAvoidAmbiguousElse(consequent)) {
writeNoLast(state, ") ");
writeWithMapNamed(state, "{\n", CAT_OTHER, consequent);
writeWithMap(state, "{\n", CAT_OTHER, consequent);
state.indentLevel++;
printStatement(consequent, state);
state.indentLevel--;
@@ -493,10 +494,10 @@ function printReturnStatement(node: ESTree.ReturnStatement, state: State): void
const { argument } = node;
if (argument != null) {
writeWithMapNamed(state, "return ", CAT_OTHER, node);
writeWithMap(state, "return ", CAT_OTHER, node);
printExpression(argument, state, PREC_LOWEST, CTX_NONE);
} else {
writeWithMapNamed(state, "return", CAT_IDENT, node);
writeWithMap(state, "return", CAT_IDENT, node);
}
write(state, ";\n", CAT_OTHER);
@@ -510,7 +511,7 @@ function printTryStatement(node: ESTree.TryStatement, state: State): void {
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "try ", CAT_OTHER, node);
writeWithMap(state, "try ", CAT_OTHER, node);
printBlockStatement(node.block, state);
@@ -544,19 +545,19 @@ function printSwitchStatement(node: ESTree.SwitchStatement, state: State): void
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "switch (", CAT_OTHER, node);
writeWithMap(state, "switch (", CAT_OTHER, node);
printExpression(node.discriminant, state, PREC_LOWEST, CTX_NONE);
write(state, ") ", CAT_OTHER);
const { cases } = node;
const { length } = cases;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", node);
writeWithMapNoLast(state, "{", node);
writeWithMapEnd(state, "}\n", CAT_OTHER, node);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, node);
writeWithMap(state, "{\n", CAT_OTHER, node);
state.indentLevel++;
for (let i = 0; i < length; i++) {
@@ -576,10 +577,10 @@ function printSwitchCase(node: ESTree.SwitchCase, state: State): void {
printIndent(state);
if (node.test != null) {
writeWithMapNamed(state, "case ", CAT_OTHER, node);
writeWithMap(state, "case ", CAT_OTHER, node);
printExpression(node.test, state, PREC_LOWEST, CTX_NONE);
} else {
writeWithMapNamed(state, "default", CAT_IDENT, node);
writeWithMap(state, "default", CAT_IDENT, node);
}
write(state, ":", CAT_OTHER);
@@ -609,7 +610,7 @@ function printWhileStatement(node: ESTree.WhileStatement, state: State): void {
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "while (", CAT_OTHER, node);
writeWithMap(state, "while (", CAT_OTHER, node);
printExpression(node.test, state, PREC_LOWEST, CTX_NONE);
write(state, ")", CAT_CLOSE_BRACKET);
@@ -627,7 +628,7 @@ function printDoWhileStatement(node: ESTree.DoWhileStatement, state: State): voi
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "do", CAT_IDENT, node);
writeWithMap(state, "do", CAT_IDENT, node);
const { body } = node;
if (body.type === "BlockStatement") {
@@ -636,7 +637,7 @@ function printDoWhileStatement(node: ESTree.DoWhileStatement, state: State): voi
write(state, " ", CAT_OTHER);
} else if (body.type === "EmptyStatement") {
printIndent(state);
writeWithMapNamed(state, ";\n", CAT_OTHER, body);
writeWithMap(state, ";\n", CAT_OTHER, body);
} else {
write(state, "\n", CAT_OTHER);
state.indentLevel++;
@@ -661,7 +662,7 @@ function printForStatement(node: ESTree.ForStatement, state: State): void {
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "for (", CAT_OTHER, node);
writeWithMap(state, "for (", CAT_OTHER, node);
const { init } = node;
if (init != null) {
@@ -699,7 +700,7 @@ function printForInStatement(node: ESTree.ForInStatement, state: State): void {
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "for (", CAT_OTHER, node);
writeWithMap(state, "for (", CAT_OTHER, node);
const { left } = node;
if (left.type === "VariableDeclaration") {
@@ -727,7 +728,7 @@ function printForOfStatement(node: ESTree.ForOfStatement, state: State): void {
printIndent(state);
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "for", CAT_IDENT, node);
writeWithMap(state, "for", CAT_IDENT, node);
if (node.await) write(state, " await", CAT_IDENT);
+4 -4
View File
@@ -1,10 +1,10 @@
// String literal printing (port of `str.rs`, pretty mode: fixed quote).
import { CAT_OTHER, write, writeNoLast, writeWithMapNamedNoLast } from "./write.ts";
import { CAT_OTHER, write, writeNoLast, writeWithMapNoLast } from "./write.ts";
import { debugAssert } from "../asserts.ts";
import type { State } from "../state.ts";
import type * as ESTree from "../../../../npm/oxc-types/types.d.ts";
import type { UnnamedMappableNode } from "./types.ts";
/**
* Characters or sequences which may need escaping or special handling.
@@ -22,9 +22,9 @@ const STRING_ESCAPE_REGEX =
* Pretty mode always uses double quotes, matching Oxc's default options, so there is no quote to choose.
* Almost no string needs escaping, so the scan for characters or sequences which do is all that happens on the common path.
*/
export function printString(state: State, value: string, node: ESTree.Node): void {
export function printString(state: State, value: string, node: UnnamedMappableNode): void {
// Quote is fixed - double, matching `oxc_codegen`'s default option
writeWithMapNamedNoLast(state, '"', node);
writeWithMapNoLast(state, '"', node);
// Almost no string needs escaping, so the loop which performs escaping sits in its own function
if (!STRING_ESCAPE_REGEX.test(value)) {
+25 -1
View File
@@ -58,10 +58,34 @@ export type ExportNamedDeclarationNode = Omit<ESTree.ExportNamedDeclaration, "de
/**
* A node carrying the offsets needed for source mappings.
*
* No `name` - a mapping recorded for one of these carries none. `NamedMappableNode` below is the
* shape the named forms take, and requiring `name` on it is what steers a named call which cannot
* produce a name to the plain form instead.
*/
export interface MappableNode {
type: string;
name?: unknown;
start?: number;
end?: number;
}
/**
* A node whose mapping records the name it had in the source.
*
* These are the identifier kinds Rust `oxc_codegen` names mappings for - identifier references and
* names, binding, label, JSX and private identifiers.
*/
export interface NamedMappableNode extends MappableNode {
name: string;
}
/**
* A node whose mapping records no name, because it has no string `name` to record.
*
* `name?: object` is how a node which does have one is rejected - `string` is not assignable to it.
* A `name` holding a node rather than a string, as a JSX element's does, is not a name a mapping could record,
* and stays allowed.
*/
export interface UnnamedMappableNode extends MappableNode {
name?: object;
}
+12 -10
View File
@@ -12,9 +12,11 @@ import {
markMapAtStartOffset,
write,
writeNoLast,
writeWithMapNamed,
writeWithMap,
writeWithMapEnd,
writeWithMapNamed,
writeWithMapNamedNoLast,
writeWithMapNoLast,
} from "./write.ts";
import { printExpression } from "./expression.ts";
import { printParenParams, printParenParamsArrow } from "./function.ts";
@@ -285,7 +287,7 @@ function printTSTypeName(
writeWithMapNamed(state, node.right.name, CAT_IDENT, node.right);
} else if (node.type === "ThisExpression") {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, "this", CAT_IDENT, node);
writeWithMap(state, "this", CAT_IDENT, node);
} else {
printSpaceBeforeIdentifier(state);
writeWithMapNamed(state, node.name, CAT_IDENT, node);
@@ -432,12 +434,12 @@ function printTSTypeLiteral(node: ESTree.TSTypeLiteral, state: State): void {
const { members } = node;
const { length } = members;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", node);
writeWithMapNoLast(state, "{", node);
writeWithMapEnd(state, "}", CAT_OTHER, node);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, node);
writeWithMap(state, "{\n", CAT_OTHER, node);
state.indentLevel++;
for (let i = 0; i < length; i++) {
@@ -957,12 +959,12 @@ export function printTSModuleDeclaration(
function printModuleBlock(body: ESTree.TSModuleBlock, state: State): void {
const statements = body.body;
if (statements.length === 0) {
writeWithMapNamedNoLast(state, "{", body);
writeWithMapNoLast(state, "{", body);
writeWithMapEnd(state, "}", CAT_OTHER, body);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, body);
writeWithMap(state, "{\n", CAT_OTHER, body);
state.indentLevel++;
printDirectivesAndStatements(statements, state);
@@ -1012,12 +1014,12 @@ export function printTSInterfaceDeclaration(
const members = node.body.body;
const { length } = members;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", node.body);
writeWithMapNoLast(state, "{", node.body);
writeWithMapEnd(state, "}", CAT_OTHER, node.body);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, node.body);
writeWithMap(state, "{\n", CAT_OTHER, node.body);
state.indentLevel++;
for (let i = 0; i < length; i++) {
@@ -1117,12 +1119,12 @@ export function printTSEnumDeclaration(node: ESTree.TSEnumDeclaration, state: St
const { members } = body;
const { length } = members;
if (length === 0) {
writeWithMapNamedNoLast(state, "{", body);
writeWithMapNoLast(state, "{", body);
writeWithMapEnd(state, "}", CAT_OTHER, body);
return;
}
writeWithMapNamed(state, "{\n", CAT_OTHER, body);
writeWithMap(state, "{\n", CAT_OTHER, body);
state.indentLevel++;
const lastIndex = length - 1;
+70 -23
View File
@@ -5,7 +5,7 @@
import { debugAssert } from "../asserts.ts";
import type { MappableNode } from "./types.ts";
import type { MappableNode, NamedMappableNode, UnnamedMappableNode } from "./types.ts";
import type { State } from "../state.ts";
// `state.last` records what was written last, by category, not the last character itself.
@@ -260,7 +260,40 @@ export function write(state: State, code: string, last: Category): void {
}
/**
* Append `code` to the output, record what it ends with, and record a source mapping for `node`.
* Append `code` to the output, record what it ends with, and record an unnamed source mapping for `node`.
*
* The mapping is only recorded where the caller asked for source maps and `node` carries `start` / `end` offsets.
*
* Builds without source map support have no use for this. For those builds, TSDown plugin rewrites every call
* into `write` and drops the `node` argument, leaving this unreferenced for the minifier to remove.
*
* @param state - Printer state
* @param code - Text to append, never empty
* @param last - Category of the last character of `code`
* @param node - Node this text came from
*/
export function writeWithMap(
state: State,
code: string,
last: Category,
node: UnnamedMappableNode,
): void {
debugAssert(code.length > 0, "`code` should not be an empty string");
debugAssertCategoryMatches(state, code, last);
recordSourceMapping(state, node, LOCATION_START);
state.last = last;
state.output += code;
if (DEBUG) {
state.lastIsStale = false;
state.lastCharWritten = code[code.length - 1];
}
}
/**
* Append `code` to the output, record what it ends with, and record a named source mapping for `node`.
*
* The mapping is only recorded where the caller asked for source maps and `node` carries `start` / `end` offsets.
*
@@ -276,7 +309,7 @@ export function writeWithMapNamed(
state: State,
code: string,
last: Category,
node: MappableNode,
node: NamedMappableNode,
): void {
debugAssert(code.length > 0, "`code` should not be an empty string");
debugAssertCategoryMatches(state, code, last);
@@ -317,7 +350,34 @@ export function writeNoLast(state: State, code: string): void {
}
/**
* Append `code` and record a source mapping for `node`, leaving `state.last` alone.
* Append `code` and record an unnamed source mapping for `node`, leaving `state.last` alone.
*
* The mapping is only recorded where the caller asked for source maps and `node` carries `start` / `end` offsets.
*
* `writeNoLast`'s rule about `last` applies here too - another write must follow before anything reads it.
*
* Builds without source map support have no use for this. For those builds, TSDown plugin rewrites every call
* into `writeNoLast` and drops the `node` argument, leaving this unreferenced for the minifier to remove.
*
* @param state - Printer state
* @param code - Text to append, which unlike `writeWithMap` may be empty
* @param node - Node this text came from
*/
export function writeWithMapNoLast(state: State, code: string, node: UnnamedMappableNode): void {
recordSourceMapping(state, node, LOCATION_START);
state.output += code;
if (DEBUG) {
state.lastIsStale = true;
if (code.length > 0) state.lastCharWritten = code[code.length - 1];
}
}
/**
* Append `code` and record a named source mapping for `node`, leaving `state.last` alone.
*
* The mapping is only recorded where the caller asked for source maps and `node` carries `start` / `end` offsets.
*
* `writeNoLast`'s rule about `last` applies here too - another write must follow before anything reads it.
*
@@ -328,7 +388,7 @@ export function writeNoLast(state: State, code: string): void {
* @param code - Text to append, which unlike `writeWithMapNamed` may be empty
* @param node - Node this text came from
*/
export function writeWithMapNamedNoLast(state: State, code: string, node: MappableNode): void {
export function writeWithMapNamedNoLast(state: State, code: string, node: NamedMappableNode): void {
recordSourceMapping(state, node, LOCATION_NAMED);
state.output += code;
@@ -375,21 +435,6 @@ export function writeWithMapEnd(
}
}
/**
* Record a mapping for `node`'s start offset at the current output position, with a name.
*
* Where `node` is an identifier, the mapping carries the name it had in the source.
*
* The mapping is only recorded where the caller asked for source maps and `node` carries `start` / `end` offsets.
* Builds without source map support have no use for this. In those builds, minifier removes it.
*
* @param state - Printer state
* @param node - Node the mapping points at
*/
export function markMapNamed(state: State, node: MappableNode): void {
if (SOURCEMAPS) recordSourceMapping(state, node, LOCATION_NAMED);
}
/**
* Record a mapping for `node`'s start offset at the current output position.
*
@@ -499,7 +544,10 @@ function recordSourceMapping(state: State, node: MappableNode, location: Locatio
// recovering a name or retaining the mapping, since member-level marks commonly duplicate keys.
if (state.mapPositions[state.mapPositions.length - 1] === sourceOffset) return;
if (location === LOCATION_NAMED && typeof node.name === "string") {
if (location === LOCATION_NAMED) {
// Only the named forms pass `LOCATION_NAMED`, and they take a `NamedMappableNode`
debugAssert("name" in node && typeof node.name === "string");
// A mapping carries a name only when the identifier printed differs from the one in the source.
// When possible, the mapping records the name from source, but if the source range is invalid,
// it falls back to the printed name.
@@ -524,8 +572,7 @@ function recordSourceMapping(state: State, node: MappableNode, location: Locatio
const nameStart = start + hashLength;
const nameEnd = nameStart + printedName.length;
const matchesSource =
printedName.length > 0
&& end <= sourceText.length
end <= sourceText.length
&& nameEnd <= end
&& (hashLength === 0 || sourceText.charCodeAt(start) === 35) /* # */
&& sourceText.startsWith(printedName, nameStart)
+2 -2
View File
@@ -71,8 +71,8 @@ const minifyConfig = DEBUG
// `src-js/index.ts` loads whichever build the caller's options call for.
//
// In builds without source maps, nothing reads the `node` argument the mapping writes take,
// so `unmap_writes` rewrites every `writeWithMapNamed` / `writeWithMapNamedNoLast` call, and the imports
// which bring them in, into the plain `write` / `writeNoLast` they become without it.
// so `unmap_writes` rewrites every mapped write call, and the imports which bring them in,
// into the plain `write` / `writeNoLast` they become without it.
const printerConfig = (name: string, { sourcemaps, ts }: { sourcemaps: boolean; ts: boolean }) => ({
...commonConfig,
minify: minifyConfig,
@@ -7,7 +7,7 @@ import type { Plugin } from "rolldown";
*
* ```ts
* // Original code
* writeWithMapNamed(state, "declare ", CAT_OTHER, node);
* writeWithMap(state, "declare ", CAT_OTHER, node);
*
* // After transform
* write(state, "declare ", CAT_OTHER);
@@ -35,11 +35,12 @@ import type { Plugin } from "rolldown";
*/
const REWRITES = {
// `write` takes the `last` category between the code and the node, `writeNoLast` does not
writeWithMap: { arity: 4, remove: 1, rename: "write" },
writeWithMapNamed: { arity: 4, remove: 1, rename: "write" },
writeWithMapNoLast: { arity: 3, remove: 1, rename: "writeNoLast" },
writeWithMapNamedNoLast: { arity: 3, remove: 1, rename: "writeNoLast" },
writeWithMapEnd: { arity: 4, remove: 1, rename: "write" },
// `rename: null` because a standalone mark has no non-sourcemap equivalent
markMapNamed: { arity: 2, remove: 1, rename: null },
markMapStart: { arity: 2, remove: 1, rename: null },
markMapAfter: { arity: 2, remove: 1, rename: null },
markMapAtStartOffset: { arity: 3, remove: 2, rename: null },