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
18 changes: 2 additions & 16 deletions apps/oxlint/src-js/plugins/source_code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
SOURCE_LEN_OFFSET,
IS_JSX_FLAG_POS,
IS_TS_FLAG_POS,
TOKENS_OFFSET_POS_32,
TOKENS_LEN_POS_32,
} from "../generated/constants.ts";

// We use the deserializer which removes `ParenthesizedExpression`s from AST,
Expand All @@ -31,10 +29,10 @@ import type { BufferWithArrays, Comment, Node } from "./types.ts";
import type { ScopeManager } from "./scope.ts";

// Text decoder, for decoding source text from buffer
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true });
export const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true });

// Buffer containing AST. Set before linting a file by `setupSourceForFile`.
let buffer: BufferWithArrays | null = null;
export let buffer: BufferWithArrays | null = null;

// Indicates if the original source text has a BOM. Set before linting a file by `setupSourceForFile`.
let hasBOM = false;
Expand Down Expand Up @@ -161,18 +159,6 @@ export function fileIsTs(): boolean {
return buffer[IS_TS_FLAG_POS] === 1;
}

/**
* Get serialized ESTree tokens JSON from buffer, if present.
*/
export function getSerializedTokensJSON(): string | null {
debugAssertIsNonNull(buffer);
const tokensLen = buffer.uint32[TOKENS_LEN_POS_32];
if (tokensLen === 0) return null;

const tokensOffset = buffer.uint32[TOKENS_OFFSET_POS_32];
return textDecoder.decode(buffer.subarray(tokensOffset, tokensOffset + tokensLen));
}

// `SourceCode` object.
//
// Only one file is linted at a time, so we can reuse a single object for all files.
Expand Down
45 changes: 25 additions & 20 deletions apps/oxlint/src-js/plugins/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*/

import { ast, initAst } from "./source_code.ts";
import { getSerializedTokensJSON } from "./source_code.ts";
import { buffer, textDecoder } from "./source_code.ts";
import { getNodeLoc } from "./location.ts";
import { TOKENS_OFFSET_POS_32, TOKENS_LEN_POS_32 } from "../generated/constants.ts";
import { debugAssert, debugAssertIsNonNull } from "../utils/asserts.ts";

import type { Comment, Node, NodeOrToken } from "./types.ts";
Expand Down Expand Up @@ -149,35 +150,39 @@ export let tokensAndComments: TokenOrComment[] | null = null;
* Caller must ensure `filePath` and `sourceText` are initialized before calling this function.
*/
export function initTokens() {
const serializedTokens = getSerializedTokensJSON();
debugAssertIsNonNull(
serializedTokens,
"Serialized tokens should be provided by Rust parser transfer",
);
tokens = parseSerializedTokens(serializedTokens);
debugAssert(tokens === null, "Tokens already initialized");

// Check `tokens` have valid ranges and are in ascending order
debugCheckValidRanges(tokens, "token");
}
// Get tokens JSON from buffer, and deserialize it
debugAssertIsNonNull(buffer);

/**
* Deserialize serialized ESTree tokens from Rust and hydrate ESLint-compatible token fields.
*/
function parseSerializedTokens(serializedTokens: string): Token[] {
const parsed = JSON.parse(serializedTokens) as Token[];
for (const token of parsed) {
const { uint32 } = buffer;
const tokensJsonLen = uint32[TOKENS_LEN_POS_32];
if (tokensJsonLen === 0) {
tokens = [];
return;
}

const tokensJsonOffset = uint32[TOKENS_OFFSET_POS_32];
const tokensJson = textDecoder.decode(
buffer.subarray(tokensJsonOffset, tokensJsonOffset + tokensJsonLen),
);
tokens = JSON.parse(tokensJson) as Token[];

// Add `range` property to each token, and set prototype of each to `TokenProto` which provides getter for `loc`
for (const token of tokens) {
const { start, end } = token;
debugAssert(
typeof start === "number" && typeof end === "number",
"Precomputed tokens should include `start` and `end`",
);

// `TokenProto` provides getter for `loc`
// @ts-expect-error - TS doesn't understand `__proto__`
token.__proto__ = TokenProto;
token.range = [start, end];
// `TokenProto` provides getter for `loc`
Object.setPrototypeOf(token, TokenProto);
}
return parsed;

// Check `tokens` have valid ranges and are in ascending order
debugCheckValidRanges(tokens, "token");
}

/**
Expand Down
Loading