Skip to content
Draft
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
12 changes: 6 additions & 6 deletions packages/typescript/src/api/node/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ export function encodeNode(node: Node): Uint8Array {
const data = getNodeData(node, strs, extendedDataValues, structuredWriter);
nodeValues.push(
node.kind,
node.pos >= 0 ? node.pos : 0,
node.end >= 0 ? node.end : 0,
node.pos,
node.end,
0, // next (filled in later)
parentIndex,
data,
Expand Down Expand Up @@ -259,8 +259,8 @@ export function encodeNode(node: Node): Uint8Array {

nodeValues.push(
KIND_NODE_LIST,
list.pos >= 0 ? list.pos : 0,
list.end >= 0 ? list.end : 0,
list.pos,
list.end,
0, // next
parentIndex,
list.length, // data for NodeList is its length
Expand Down Expand Up @@ -304,8 +304,8 @@ export function encodeNode(node: Node): Uint8Array {
const rootData = getNodeData(node, strs, extendedDataValues, structuredWriter);
nodeValues.push(
node.kind,
node.pos >= 0 ? node.pos : 0,
node.end >= 0 ? node.end : 0,
node.pos,
node.end,
0,
0,
rootData,
Expand Down
4 changes: 2 additions & 2 deletions packages/typescript/src/api/node/node.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export class RemoteNodeList extends Array<RemoteNode> implements NodeArray<Remot
private _cursorNodeIndex: number = 0;

get pos(): number {
return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);
return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true);
}

get end(): number {
return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);
return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true);
}

get next(): number {
Expand Down
19 changes: 19 additions & 0 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,30 @@ import {
type RootedFilePath,
type SourceFile,
SyntaxKind,
TokenFlags,
tryGetAmbientModuleNameFromSymbolName,
unescapeLeadingUnderscores,
} from "@typescript/typescript/ast";
import {
cloneNode,
createArrayTypeNode,
createCallExpression,
createExpressionStatement,
createFunctionTypeNode,
createIdentifier,
createKeywordTypeNode,
createNumericLiteral,
createParameterDeclaration,
createSourceFile,
createStringLiteral,
createToken,
createTypeAliasDeclaration,
createTypeReferenceNode,
createUnionTypeNode,
createVariableDeclaration,
createVariableDeclarationList,
createVariableStatement,
updateSourceFile,
} from "@typescript/typescript/ast/factory";
import { visitEachChild } from "@typescript/typescript/ast/visitor";
import {
Expand Down Expand Up @@ -7964,6 +7969,20 @@ export const obj = { m: 1, s: "hi", b: true };
assert.strictEqual(text, "string");
});

test("synthesized nodes do not acquire source comments", async () => {
await using api = spawnAPI();
await using lease = await api.createSourceFile("/test.ts", "/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n");
const { sourceFile } = lease;
const added = createExpressionStatement(
createCallExpression(createIdentifier("bar"), undefined, undefined, [createStringLiteral("x", TokenFlags.None)], NodeFlags.None),
);
const updated = updateSourceFile(sourceFile, [added, ...sourceFile.statements], sourceFile.endOfFileToken);
const expected = 'bar("x");\n/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n';

assert.strictEqual(await api.printer.printNode(updated), expected);
assert.strictEqual(await api.printer.printFile(updated), expected);
});

test("printFile", async () => {
await using api = spawnAPI(emitterFiles);

Expand Down
11 changes: 11 additions & 0 deletions packages/typescript/test/encoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ function decode(data: Uint8Array): RemoteSourceFile {
}

describe("Encoder", { concurrency }, () => {
test("preserves synthesized node and list ranges", () => {
const statement = createExpressionStatement(createIdentifier("x"));
const sf = makeSF("", "/test.ts", [statement]);
const decoded = decode(encodeSourceFile(sf));

for (const range of [decoded, decoded.statements!, decoded.statements!.at(0)!, decoded.endOfFileToken!]) {
assert.strictEqual(range.pos, -1);
assert.strictEqual(range.end, -1);
}
});

test("encodes empty source file", () => {
const sf = makeSF("", "/test.ts", []);

Expand Down
19 changes: 19 additions & 0 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,30 @@ import {
type RootedFilePath,
type SourceFile,
SyntaxKind,
TokenFlags,
tryGetAmbientModuleNameFromSymbolName,
unescapeLeadingUnderscores,
} from "@typescript/typescript/ast";
import {
cloneNode,
createArrayTypeNode,
createCallExpression,
createExpressionStatement,
createFunctionTypeNode,
createIdentifier,
createKeywordTypeNode,
createNumericLiteral,
createParameterDeclaration,
createSourceFile,
createStringLiteral,
createToken,
createTypeAliasDeclaration,
createTypeReferenceNode,
createUnionTypeNode,
createVariableDeclaration,
createVariableDeclarationList,
createVariableStatement,
updateSourceFile,
} from "@typescript/typescript/ast/factory";
import { visitEachChild } from "@typescript/typescript/ast/visitor";
import {
Expand Down Expand Up @@ -7765,6 +7770,20 @@ export const obj = { m: 1, s: "hi", b: true };
assert.strictEqual(text, "string");
});

test("synthesized nodes do not acquire source comments", () => {
using api = spawnAPI();
using lease = api.createSourceFile("/test.ts", "/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n");
const { sourceFile } = lease;
const added = createExpressionStatement(
createCallExpression(createIdentifier("bar"), undefined, undefined, [createStringLiteral("x", TokenFlags.None)], NodeFlags.None),
);
const updated = updateSourceFile(sourceFile, [added, ...sourceFile.statements], sourceFile.endOfFileToken);
const expected = 'bar("x");\n/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n';

assert.strictEqual(api.printer.printNode(updated), expected);
assert.strictEqual(api.printer.printFile(updated), expected);
});

test("printFile", () => {
using api = spawnAPI(emitterFiles);

Expand Down
4 changes: 2 additions & 2 deletions tools/scripts/tsc/generate-encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1535,11 +1535,11 @@ function emitRemoteNodeList(w: CodeWriter) {
w.write(` private _cursorNodeIndex: number = 0;`);
w.write(``);
w.write(` get pos(): number {`);
w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);`);
w.write(` return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true);`);
w.write(` }`);
w.write(``);
w.write(` get end(): number {`);
w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);`);
w.write(` return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true);`);
w.write(` }`);
w.write(``);
w.write(` get next(): number {`);
Expand Down
4 changes: 2 additions & 2 deletions tsc/internal/api/encoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ func (d *astDecoder) decode() (*ast.Node, error) {
// Process bottom-up so children exist before parents.
for i := d.nodeCount - 1; i >= 1; i-- {
kind := d.nodeField(i, NodeOffsetKind)
pos := d.nodeField(i, NodeOffsetPos)
end := d.nodeField(i, NodeOffsetEnd)
pos := int32(d.nodeField(i, NodeOffsetPos))
end := int32(d.nodeField(i, NodeOffsetEnd))
data := d.nodeField(i, NodeOffsetData)
childIndices := d.collectChildren(i)

Expand Down
21 changes: 21 additions & 0 deletions tsc/internal/api/encoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package encoder_test

import (
"bytes"
"encoding/binary"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -37,6 +38,26 @@ func TestDecodeSourceFile_Basic(t *testing.T) {
assert.Assert(t, decoded.EndOfFileToken != nil)
}

func TestDecodeSourceFile_SynthesizedRanges(t *testing.T) {
t.Parallel()
sf := parseSourceFile("foo();")
synthesized := core.NewTextRange(-1, -1)
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
nodesOffset := int(binary.LittleEndian.Uint32(buf[encoder.HeaderOffsetNodes:]))
for offset := nodesOffset + encoder.NodeSize; offset < len(buf); offset += encoder.NodeSize {
binary.LittleEndian.PutUint32(buf[offset+encoder.NodeOffsetPos:], ^uint32(0))
binary.LittleEndian.PutUint32(buf[offset+encoder.NodeOffsetEnd:], ^uint32(0))
}
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)

assert.Equal(t, decoded.AsNode().Loc, synthesized)
assert.Equal(t, decoded.Statements.Loc, synthesized)
assert.Equal(t, decoded.Statements.Nodes[0].Loc, synthesized)
assert.Equal(t, decoded.EndOfFileToken.Loc, synthesized)
}

func TestDecodeSourceFile_Metadata(t *testing.T) {
t.Parallel()

Expand Down