Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/typescript/src/api/node/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export function encodeNode(node: Node): Uint8Array {
0, // next
parentIndex,
list.length, // data for NodeList is its length
0, // flags
list.hasTrailingComma ? 1 : 0, // NodeLists have no AST flags; this slot is reused for hasTrailingComma
);

const saveParentIndex = parentIndex;
Expand Down
5 changes: 4 additions & 1 deletion packages/typescript/src/api/node/node.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export class RemoteNodeList extends Array<RemoteNode> implements NodeArray<Remot
}

parent: RemoteNode;
hasTrailingComma?: boolean;
transformFlags: number = 0;
protected view: DataView;
protected index: number;
Expand Down Expand Up @@ -71,6 +70,10 @@ export class RemoteNodeList extends Array<RemoteNode> implements NodeArray<Remot
return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true);
}

get hasTrailingComma(): boolean {
return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0;
}
Comment thread
mrazauskas marked this conversation as resolved.

private sourceFile: SourceFileInfo;

constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript/src/api/node/protocol.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const PROTOCOL_VERSION = 7;
export const PROTOCOL_VERSION = 8;
Comment thread
mrazauskas marked this conversation as resolved.

export const HEADER_OFFSET_METADATA = 0;
export const HEADER_OFFSET_HASH_LO0 = 4;
Expand Down
24 changes: 24 additions & 0 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getSynthesizedDeepClone,
InternalSymbolName,
isCallExpression,
isExpressionStatement,
isFunctionDeclaration,
isIdentifier,
isImportDeclaration,
Expand Down Expand Up @@ -1236,6 +1237,29 @@ describe("SourceFile", () => {
});
});

describe("NodeArray", () => {
test("hasTrailingComma", async () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`,
});
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const sourceFile = await project.program.getSourceFile("/src/main.ts");
assert.ok(sourceFile);
const statements = sourceFile.statements.filter(isExpressionStatement);
assert.ok(isCallExpression(statements[0].expression));
assert.equal(statements[0].expression.arguments.hasTrailingComma, true);
assert.ok(isCallExpression(statements[1].expression));
assert.equal(statements[1].expression.arguments.hasTrailingComma, false);
}
finally {
await api.close();
}
});
});

test("unicode escapes", async () => {
const api = spawnAPI({
"/tsconfig.json": "{}",
Expand Down
6 changes: 3 additions & 3 deletions packages/typescript/test/encoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe("Encoder", () => {
// Verify header
const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
const metadata = view.getUint32(0, true);
assert.strictEqual(metadata >>> 24, 7, "protocol version should be 7");
assert.strictEqual(metadata >>> 24, 8, "protocol version should be 8");

// Verify we can decode it
const decoded = decode(encoded);
Expand Down Expand Up @@ -179,11 +179,11 @@ describe("Encoder", () => {
assert.strictEqual(rootKind, SyntaxKind.IfStatement);
});

test("protocol version is 7", () => {
test("protocol version is 8", () => {
const sf = makeSF("", "/test.ts", []);
const encoded = encodeSourceFile(sf);
const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
assert.strictEqual(view.getUint32(0, true) >>> 24, 7);
assert.strictEqual(view.getUint32(0, true) >>> 24, 8);
});

test("encodes source files without content mapping metadata", () => {
Expand Down
24 changes: 24 additions & 0 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
getSynthesizedDeepClone,
InternalSymbolName,
isCallExpression,
isExpressionStatement,
isFunctionDeclaration,
isIdentifier,
isImportDeclaration,
Expand Down Expand Up @@ -1152,6 +1153,29 @@ describe("SourceFile", () => {
});
});

describe("NodeArray", () => {
test("hasTrailingComma", () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`,
});
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const sourceFile = project.program.getSourceFile("/src/main.ts");
assert.ok(sourceFile);
const statements = sourceFile.statements.filter(isExpressionStatement);
assert.ok(isCallExpression(statements[0].expression));
assert.equal(statements[0].expression.arguments.hasTrailingComma, true);
assert.ok(isCallExpression(statements[1].expression));
assert.equal(statements[1].expression.arguments.hasTrailingComma, false);
}
finally {
api.close();
}
});
});

test("unicode escapes", () => {
const api = spawnAPI({
"/tsconfig.json": "{}",
Expand Down
5 changes: 4 additions & 1 deletion tools/scripts/tsc/generate-encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,6 @@ function emitRemoteNodeList(w: CodeWriter) {
w.write(` }`);
w.write(``);
w.write(` parent: RemoteNode;`);
w.write(` hasTrailingComma?: boolean;`);
w.write(` transformFlags: number = 0;`);
w.write(` protected view: DataView;`);
w.write(` protected index: number;`);
Expand Down Expand Up @@ -1546,6 +1545,10 @@ function emitRemoteNodeList(w: CodeWriter) {
w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true);`);
w.write(` }`);
w.write(``);
w.write(` get hasTrailingComma(): boolean {`);
w.write(` return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0;`);
w.write(` }`);
w.write(``);
w.write(` private sourceFile: SourceFileInfo;`);
w.write(``);
w.write(` constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {`);
Expand Down
7 changes: 4 additions & 3 deletions tsc/internal/api/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const (
)

const (
ProtocolVersion uint8 = 7
ProtocolVersion uint8 = 8
Comment thread
mrazauskas marked this conversation as resolved.
)

// Source File Binary Format
Expand Down Expand Up @@ -202,7 +202,8 @@ const (
// NodeLists are represented as normal nodes with the special `kind` value `0xff_ff_ff_ff`. They are considered the parent
// of their contents in the encoded format. A client reconstructing an AST similar to TypeScript's internal representation
// should instead set the `parent` pointers of a NodeList's children to the NodeList's parent. A NodeList's `data` field
// is the uint32 length of the list, and does not use one of the data types described below.
// is the uint32 length of the list, and does not use one of the data types described below. A NodeList's `flags` field
// is not used for AST node flags (NodeLists have none); bit 0 instead encodes `HasTrailingComma`.
//
// For node types other than NodeList, the node data field encodes one of the following, determined by the first 2 bits of
// the field:
Expand Down Expand Up @@ -502,7 +503,7 @@ func encodeTree(rootNode *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIn
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
}

nodes = appendUint32s(nodes, SyntaxKindNodeList, utf16(nodeList.Pos()), utf16(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)), 0)
nodes = appendUint32s(nodes, SyntaxKindNodeList, utf16(nodeList.Pos()), utf16(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)), uint32(boolToByte(nodeList.HasTrailingComma())))

saveParentIndex := parentIndex

Expand Down
4 changes: 2 additions & 2 deletions tsc/internal/api/encoder/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ func TestEncodeSourceFile(t *testing.T) {

func TestEncodeContentMapperSourceFileMetadata(t *testing.T) {
t.Parallel()
if encoder.ProtocolVersion != 7 {
t.Fatalf("protocol version = %d, want 7", encoder.ProtocolVersion)
if encoder.ProtocolVersion != 8 {
t.Fatalf("protocol version = %d, want 8", encoder.ProtocolVersion)
}
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/component.vue",
Expand Down