mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 18:43:44 +00:00
perf(estree/tokens): introduce ESTreeTokenConfig trait (#19842)
Introduce `ESTreeTokenConfig` trait and allow option for the "flavor" of tokens to be set at compile time. When producing Espree-style tokens, this allows cutting out all the special-case logic related to `JSXIdentifier` tokens and removing 2 branches from `emit_identifier_at`. Small effect on perf - only +0.3% on ESTree tokens benchmark.
This commit is contained in:
1 parent
5036bb69e8
commit
4b0611a5dd
6 files changed
+238
-91
No files matched your search
@@ -8,7 +8,7 @@ use napi_derive::napi;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast_visit::utf8_to_utf16::Utf8ToUtf16;
|
||||
use oxc_estree_tokens::{ESTreeTokenOptions, to_estree_tokens_json};
|
||||
use oxc_estree_tokens::{ESTreeTokenOptionsJS, to_estree_tokens_json};
|
||||
use oxc_linter::RawTransferMetadata2 as RawTransferMetadata;
|
||||
use oxc_napi::get_source_type;
|
||||
use oxc_parser::{ParseOptions, Parser, ParserReturn, config::RuntimeParserConfig};
|
||||
@@ -219,7 +219,7 @@ unsafe fn parse_raw_impl(
|
||||
program,
|
||||
original_source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::test262(),
|
||||
ESTreeTokenOptionsJS,
|
||||
);
|
||||
|
||||
span_converter.convert_program(program);
|
||||
|
||||
@@ -16,50 +16,10 @@ use oxc_estree::{
|
||||
use oxc_parser::{Kind, Token};
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
/// Options for serializing tokens.
|
||||
///
|
||||
/// Espree (`test262`) and TS-ESLint (`typescript`) differ in several ways:
|
||||
///
|
||||
/// * `yield`, `let`, `static` used as identifiers (`obj = { yield: 1, let: 2, static: 3 };`)
|
||||
/// * Espree emits these as `Keyword` tokens.
|
||||
/// * TS-ESLint as `Identifier` tokens.
|
||||
/// * Escaped identifiers (e.g. `\u0061`)
|
||||
/// * Espree decodes escapes in the token `value`.
|
||||
/// * TS-ESLint preserves the raw source text.
|
||||
/// * JSX namespaced names (`<ns:tag>`)
|
||||
/// * Espree emits `JSXIdentifier` tokens for both parts,
|
||||
/// * TS-ESLint leaves them as their default token type (`Identifier`).
|
||||
/// * Member expressions in JSX expressions (`<C x={a.b}>`)
|
||||
/// * Espree emits them as `Identifier` tokens.
|
||||
/// * TS-ESLint emits `JSXIdentifier` tokens for non-computed member expression identifiers
|
||||
/// inside JSX expression containers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ESTreeTokenOptions {
|
||||
pub exclude_legacy_keyword_identifiers: bool,
|
||||
pub decode_identifier_escapes: bool,
|
||||
pub jsx_namespace_jsx_identifiers: bool,
|
||||
pub member_expr_in_jsx_expression_jsx_identifiers: bool,
|
||||
}
|
||||
|
||||
impl ESTreeTokenOptions {
|
||||
pub const fn test262() -> Self {
|
||||
Self {
|
||||
exclude_legacy_keyword_identifiers: true,
|
||||
decode_identifier_escapes: true,
|
||||
jsx_namespace_jsx_identifiers: true,
|
||||
member_expr_in_jsx_expression_jsx_identifiers: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn typescript() -> Self {
|
||||
Self {
|
||||
exclude_legacy_keyword_identifiers: false,
|
||||
decode_identifier_escapes: false,
|
||||
jsx_namespace_jsx_identifiers: false,
|
||||
member_expr_in_jsx_expression_jsx_identifiers: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
mod options;
|
||||
pub use options::{
|
||||
ESTreeTokenConfig, ESTreeTokenOptions, ESTreeTokenOptionsJS, ESTreeTokenOptionsTS,
|
||||
};
|
||||
|
||||
/// Serialize tokens to JSON.
|
||||
///
|
||||
@@ -68,12 +28,12 @@ impl ESTreeTokenOptions {
|
||||
///
|
||||
/// `source_text` must be the original source text, prior to BOM removal.
|
||||
/// i.e. if the file has a BOM, it must be present at the start of `source_text`.
|
||||
pub fn to_estree_tokens_json(
|
||||
pub fn to_estree_tokens_json<O: ESTreeTokenConfig>(
|
||||
tokens: &[Token],
|
||||
program: &Program<'_>,
|
||||
source_text: &str,
|
||||
span_converter: &Utf8ToUtf16,
|
||||
options: ESTreeTokenOptions,
|
||||
options: O,
|
||||
) -> String {
|
||||
// Estimated size of a single token serialized to JSON, in bytes.
|
||||
// TODO: Estimate this better based on real-world usage.
|
||||
@@ -92,12 +52,12 @@ pub fn to_estree_tokens_json(
|
||||
///
|
||||
/// `source_text` must be the original source text, prior to BOM removal.
|
||||
/// i.e. if the file has a BOM, it must be present at the start of `source_text`.
|
||||
pub fn to_estree_tokens_pretty_json(
|
||||
pub fn to_estree_tokens_pretty_json<O: ESTreeTokenConfig>(
|
||||
tokens: &[Token],
|
||||
program: &Program<'_>,
|
||||
source_text: &str,
|
||||
span_converter: &Utf8ToUtf16,
|
||||
options: ESTreeTokenOptions,
|
||||
options: O,
|
||||
) -> String {
|
||||
// Estimated size of a single token serialized to JSON, in bytes.
|
||||
// TODO: Estimate this better based on real-world usage.
|
||||
@@ -333,13 +293,13 @@ use u32_string::U32String;
|
||||
/// When a visitor method encounters an AST node that requires a token type override
|
||||
/// (e.g. a keyword used as an identifier), it serializes all preceding tokens with their default types,
|
||||
/// then serializes the overridden token with its corrected type.
|
||||
fn serialize_tokens(
|
||||
fn serialize_tokens<O: ESTreeTokenConfig>(
|
||||
serializer: impl Serializer,
|
||||
tokens: &[Token],
|
||||
program: &Program<'_>,
|
||||
source_text: &str,
|
||||
span_converter: &Utf8ToUtf16,
|
||||
options: ESTreeTokenOptions,
|
||||
options: O,
|
||||
) {
|
||||
let mut context = ESTreeTokenContext {
|
||||
seq: serializer.serialize_sequence(),
|
||||
@@ -347,23 +307,72 @@ fn serialize_tokens(
|
||||
source_text,
|
||||
span_converter: span_converter.converter(),
|
||||
options,
|
||||
jsx_state: JSXState::default(),
|
||||
jsx_state: O::JSXState::default(),
|
||||
};
|
||||
context.visit_program(program);
|
||||
context.finish();
|
||||
}
|
||||
|
||||
/// JSX state.
|
||||
/// Trait for JSX state.
|
||||
///
|
||||
/// Used in TS style for tracking when to emit JSX identifiers.
|
||||
///
|
||||
/// Implemented by [`JSXStateJS`] and [`JSXStateTS`].
|
||||
/// In JS style, there is no state to track, so [`JSXStateJS`] is a ZST which implements all methods as no-ops.
|
||||
/// In TS style, [`JSXStateTS`] implements all methods, because state is required.
|
||||
pub trait JSXState: Default {
|
||||
/// Call when entering a JSX expression.
|
||||
fn enter_jsx_expression(&mut self);
|
||||
|
||||
/// Call when exiting a JSX expression.
|
||||
fn exit_jsx_expression(&mut self);
|
||||
|
||||
/// Call when entering a `MemberExpression`.
|
||||
fn enter_member_expression(&mut self, member_expr: &MemberExpression<'_>);
|
||||
|
||||
/// Call when exiting a `MemberExpression`.
|
||||
fn exit_member_expression(&mut self, member_expr: &MemberExpression<'_>);
|
||||
|
||||
/// Returns `true` if current `Identifier` token should be emitted as a `JSXIdentifier` token.
|
||||
fn should_emit_jsx_identifier(&self) -> bool;
|
||||
}
|
||||
|
||||
/// No-op JSX state for JS-style tokens.
|
||||
#[derive(Default)]
|
||||
pub struct JSXStateJS;
|
||||
|
||||
impl JSXState for JSXStateJS {
|
||||
#[inline(always)]
|
||||
fn enter_jsx_expression(&mut self) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn exit_jsx_expression(&mut self) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn enter_member_expression(&mut self, _member_expr: &MemberExpression<'_>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn exit_member_expression(&mut self, _member_expr: &MemberExpression<'_>) {}
|
||||
|
||||
#[expect(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
fn should_emit_jsx_identifier(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// JSX state for TS-style tokens.
|
||||
///
|
||||
/// Used in TS style for tracking when to emit JSX identifiers.
|
||||
#[derive(Default)]
|
||||
#[expect(clippy::struct_field_names)]
|
||||
struct JSXState {
|
||||
pub struct JSXStateTS {
|
||||
jsx_expression_depth: u32,
|
||||
member_expression_depth: u32,
|
||||
computed_member_depth: u32,
|
||||
}
|
||||
|
||||
impl JSXState {
|
||||
impl JSXState for JSXStateTS {
|
||||
#[inline]
|
||||
fn enter_jsx_expression(&mut self) {
|
||||
self.jsx_expression_depth += 1;
|
||||
@@ -409,7 +418,7 @@ impl JSXState {
|
||||
/// After the AST walk, any remaining tokens are serialized with default types.
|
||||
///
|
||||
/// This works because AST visitation occurs in source order, so same order as tokens in the iterator.
|
||||
struct ESTreeTokenContext<'b, S: SequenceSerializer> {
|
||||
struct ESTreeTokenContext<'b, O: ESTreeTokenConfig, S: SequenceSerializer> {
|
||||
/// JSON sequence serializer.
|
||||
/// Tokens are serialized into this serializer.
|
||||
seq: S,
|
||||
@@ -421,23 +430,23 @@ struct ESTreeTokenContext<'b, S: SequenceSerializer> {
|
||||
/// `None` if source is ASCII-only.
|
||||
span_converter: Option<Utf8ToUtf16Converter<'b>>,
|
||||
/// Options
|
||||
options: ESTreeTokenOptions,
|
||||
// JSX state. Used in TS-ESLint emulation mode.
|
||||
jsx_state: JSXState,
|
||||
options: O,
|
||||
// JSX state. Used when outputting tokens in TS style.
|
||||
jsx_state: O::JSXState,
|
||||
}
|
||||
|
||||
impl<'b, S: SequenceSerializer> ESTreeTokenContext<'b, S> {
|
||||
/// Emit the token at `start` as `Identifier`, unless it's a legacy keyword and
|
||||
/// `exclude_legacy_keyword_identifiers` is set (in which case it gets `Keyword` type).
|
||||
impl<'b, O: ESTreeTokenConfig, S: SequenceSerializer> ESTreeTokenContext<'b, O, S> {
|
||||
/// Emit the token at `start` as `Identifier`, unless it's a legacy keyword and serializing in JS style
|
||||
/// (in which case it gets `Keyword` type).
|
||||
///
|
||||
/// `name` is the decoded identifier name from the AST node.
|
||||
/// When the token has no escapes, `name` points into the source text, same as slicing it.
|
||||
/// When the token has escapes and `decode_identifier_escapes` is enabled, `name` provides
|
||||
/// the decoded value. Only when escapes are present but decoding is disabled do we need to
|
||||
/// fall back to slicing the raw source text (preserving the escape sequences in the output).
|
||||
/// When the token has escapes and using JS style, `name` provides the decoded value.
|
||||
/// Only when escapes are present but decoding is disabled (TS style) do we need to fall back to
|
||||
/// slicing the raw source text (preserving the escape sequences in the output).
|
||||
fn emit_identifier_at(&mut self, start: u32, name: &str) {
|
||||
let token = self.advance_to(start);
|
||||
let token_type = if self.options.exclude_legacy_keyword_identifiers
|
||||
let token_type = if self.options.is_js()
|
||||
&& matches!(token.kind(), Kind::Yield | Kind::Let | Kind::Static)
|
||||
{
|
||||
TokenType::new("Keyword")
|
||||
@@ -452,13 +461,13 @@ impl<'b, S: SequenceSerializer> ESTreeTokenContext<'b, S> {
|
||||
// Only fall back to raw source text when the token contains escapes *and* decoding is disabled,
|
||||
// since escape sequences contain `\` which needs JSON escaping.
|
||||
// Escaped identifiers are extremely rare, so handle them in `#[cold]` branch.
|
||||
if self.options.decode_identifier_escapes || !token.escaped() {
|
||||
if self.options.is_js() || !token.escaped() {
|
||||
self.serialize_safe_token(token, token_type, name);
|
||||
} else {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn emit<S: SequenceSerializer>(
|
||||
ctx: &mut ESTreeTokenContext<'_, S>,
|
||||
fn emit<O: ESTreeTokenConfig, S: SequenceSerializer>(
|
||||
ctx: &mut ESTreeTokenContext<'_, O, S>,
|
||||
token: &Token,
|
||||
token_type: TokenType,
|
||||
) {
|
||||
@@ -570,7 +579,7 @@ impl<'b, S: SequenceSerializer> ESTreeTokenContext<'b, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
impl<'a, O: ESTreeTokenConfig, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, O, S> {
|
||||
fn visit_ts_type_name(&mut self, type_name: &TSTypeName<'a>) {
|
||||
// `this` is emitted as `Identifier` token instead of `Keyword`
|
||||
match type_name {
|
||||
@@ -625,9 +634,7 @@ impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
}
|
||||
|
||||
fn visit_identifier_name(&mut self, identifier: &IdentifierName<'a>) {
|
||||
if self.options.member_expr_in_jsx_expression_jsx_identifiers
|
||||
&& self.jsx_state.should_emit_jsx_identifier()
|
||||
{
|
||||
if self.options.is_ts() && self.jsx_state.should_emit_jsx_identifier() {
|
||||
self.emit_jsx_identifier_at(identifier.span.start, &identifier.name);
|
||||
} else {
|
||||
self.emit_identifier_at(identifier.span.start, &identifier.name);
|
||||
@@ -635,9 +642,7 @@ impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
}
|
||||
|
||||
fn visit_identifier_reference(&mut self, identifier: &IdentifierReference<'a>) {
|
||||
if self.options.member_expr_in_jsx_expression_jsx_identifiers
|
||||
&& self.jsx_state.should_emit_jsx_identifier()
|
||||
{
|
||||
if self.options.is_ts() && self.jsx_state.should_emit_jsx_identifier() {
|
||||
self.emit_jsx_identifier_at(identifier.span.start, &identifier.name);
|
||||
} else {
|
||||
self.emit_identifier_at(identifier.span.start, &identifier.name);
|
||||
@@ -657,17 +662,20 @@ impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
|
||||
// `identifier.name` has `#` stripped and escapes decoded by the parser, and is JSON-safe.
|
||||
// Use it in most cases — if token is not marked as escaped, it's JSON-safe, so can skip JSON encoding.
|
||||
// When `self.options.decode_identifier_escapes` is `true`, token `value` should *always* be
|
||||
// the unescaped version, so can also use `name` from AST node and skip JSON encoding.
|
||||
// When `self.is_js()` is `true`, token `value` should *always* be the unescaped version,
|
||||
// so can also use `name` from AST node and skip JSON encoding.
|
||||
// Only fall back to raw source text when the token contains escapes *and* decoding is disabled,
|
||||
// since escape sequences contain `\` which needs JSON escaping.
|
||||
// Escaped identifiers are extremely rare, so handle them in `#[cold]` branch.
|
||||
if self.options.decode_identifier_escapes || !token.escaped() {
|
||||
if self.options.is_js() || !token.escaped() {
|
||||
self.serialize_safe_token(token, TokenType::new("PrivateIdentifier"), &identifier.name);
|
||||
} else {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn emit<S: SequenceSerializer>(ctx: &mut ESTreeTokenContext<'_, S>, token: &Token) {
|
||||
fn emit<O: ESTreeTokenConfig, S: SequenceSerializer>(
|
||||
ctx: &mut ESTreeTokenContext<'_, O, S>,
|
||||
token: &Token,
|
||||
) {
|
||||
// Strip leading `#`
|
||||
let value = &ctx.source_text[token.start() as usize + 1..token.end() as usize];
|
||||
ctx.serialize_unsafe_token(token, TokenType::new("PrivateIdentifier"), value);
|
||||
@@ -763,11 +771,12 @@ impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
}
|
||||
|
||||
fn visit_jsx_namespaced_name(&mut self, name: &JSXNamespacedName<'a>) {
|
||||
if self.options.jsx_namespace_jsx_identifiers {
|
||||
if self.options.is_js() {
|
||||
self.emit_jsx_identifier_at(name.namespace.span.start, &name.namespace.name);
|
||||
self.emit_jsx_identifier_at(name.name.span.start, &name.name.name);
|
||||
} else {
|
||||
// In TS mode, these tokens retain their default type (`Identifier`)
|
||||
}
|
||||
// When `!jsx_namespace_jsx_identifiers`, these tokens retain their default type
|
||||
}
|
||||
|
||||
fn visit_jsx_expression_container(&mut self, container: &JSXExpressionContainer<'a>) {
|
||||
@@ -838,7 +847,7 @@ impl<'a, S: SequenceSerializer> Visit<'a> for ESTreeTokenContext<'_, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SequenceSerializer> ESTreeTokenContext<'_, S> {
|
||||
impl<O: ESTreeTokenConfig, S: SequenceSerializer> ESTreeTokenContext<'_, O, S> {
|
||||
/// Emit template quasis interleaved with their interpolated parts (expressions or TS types).
|
||||
///
|
||||
/// `TemplateElement.span` excludes delimiters (parser adjusts `start + 1`),
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// `#[inline(always)]` used on all methods as they're tiny, and to ensure compiler removes dead code
|
||||
// resulting from static values
|
||||
#![expect(clippy::inline_always)]
|
||||
|
||||
use crate::{JSXState, JSXStateJS, JSXStateTS};
|
||||
|
||||
/// Trait for options for serializing tokens.
|
||||
///
|
||||
/// # Token styles
|
||||
///
|
||||
/// Espree (JS) and TS-ESLint (TS) differ in several ways in the tokens they produce.
|
||||
///
|
||||
/// 1. `yield`, `let`, `static` used as identifiers (`obj = { yield: 1, let: 2, static: 3 };`)
|
||||
/// * Espree emits these as `Keyword` tokens.
|
||||
/// * TS-ESLint as `Identifier` tokens.
|
||||
///
|
||||
/// 2. Escaped identifiers (e.g. `\u0061`)
|
||||
/// * Espree decodes escapes in the token `value`.
|
||||
/// * TS-ESLint preserves the raw source text.
|
||||
///
|
||||
/// 3. JSX namespaced names (`<ns:tag>`)
|
||||
/// * Espree emits `JSXIdentifier` tokens for both parts,
|
||||
/// * TS-ESLint leaves them as their default token type (`Identifier`).
|
||||
///
|
||||
/// 4. Member expressions in JSX expressions (`<C x={a.b}>`)
|
||||
/// * Espree emits them as `Identifier` tokens.
|
||||
/// * TS-ESLint emits `JSXIdentifier` tokens for non-computed member expression identifiers
|
||||
/// inside JSX expression containers.
|
||||
///
|
||||
/// # Options
|
||||
///
|
||||
/// This trait is implemented by 3 structs which can be passed as options to [`to_estree_tokens_json`] and
|
||||
/// [`to_estree_tokens_pretty_json`]:
|
||||
///
|
||||
/// * [`ESTreeTokenOptions`]
|
||||
/// * [`ESTreeTokenOptionsJS`]
|
||||
/// * [`ESTreeTokenOptionsTS`]
|
||||
///
|
||||
/// The difference between them is:
|
||||
/// * [`ESTreeTokenOptions`] supports both JS and TS styles, and branches at runtime to handle the differences.
|
||||
/// * [`ESTreeTokenOptionsJS`] and [`ESTreeTokenOptionsTS`] only support a single style, and branches for the other
|
||||
/// style are removed as dead code at compile time.
|
||||
///
|
||||
/// If your application only uses one style of tokens, use [`ESTreeTokenOptionsJS`] or [`ESTreeTokenOptionsTS`].
|
||||
/// That will produce the smallest binary size and fastest runtime performance.
|
||||
///
|
||||
/// If your application uses either JS and TS style tokens depending on a condition only known at runtime,
|
||||
/// [`ESTreeTokenOptions`] is likely preferable. It is marginally slower, but avoids compiling the code for serializing
|
||||
/// tokens twice in the binary.
|
||||
///
|
||||
/// [`to_estree_tokens_json`]: crate::to_estree_tokens_json
|
||||
/// [`to_estree_tokens_pretty_json`]: crate::to_estree_tokens_pretty_json
|
||||
pub trait ESTreeTokenConfig {
|
||||
/// JSX state type.
|
||||
type JSXState: JSXState;
|
||||
|
||||
/// Returns `true` if serializing in TS style.
|
||||
fn is_ts(&self) -> bool;
|
||||
|
||||
/// Returns `true` if serializing in JS style.
|
||||
#[inline(always)]
|
||||
fn is_js(&self) -> bool {
|
||||
!self.is_ts()
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for serializing tokens in JS style.
|
||||
///
|
||||
/// Prefer this over [`ESTreeTokenOptions`] when your application only uses JS style tokens,
|
||||
/// as the code paths for TS style can be removed.
|
||||
///
|
||||
/// If your application uses both JS and TS style tokens, [`ESTreeTokenOptions`] is probably preferable
|
||||
/// as it can be used to serialize both styles without binary including all this code twice.
|
||||
///
|
||||
/// See [`ESTreeTokenConfig`] for more details.
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct ESTreeTokenOptionsJS;
|
||||
|
||||
impl ESTreeTokenConfig for ESTreeTokenOptionsJS {
|
||||
/// No-op JSX state, because no state is required for JS-style tokens.
|
||||
type JSXState = JSXStateJS;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_ts(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for serializing tokens in TS style.
|
||||
///
|
||||
/// Prefer this over [`ESTreeTokenOptions`] when your application only uses TS style tokens,
|
||||
/// as the code paths for JS style can be removed.
|
||||
///
|
||||
/// If your application uses both JS and TS style tokens, [`ESTreeTokenOptions`] is probably preferable
|
||||
/// as it can be used to serialize both styles without binary including all this code twice.
|
||||
///
|
||||
/// See [`ESTreeTokenConfig`] for more details.
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct ESTreeTokenOptionsTS;
|
||||
|
||||
impl ESTreeTokenConfig for ESTreeTokenOptionsTS {
|
||||
/// Working JSX state, because state is required for TS-style tokens.
|
||||
type JSXState = JSXStateTS;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_ts(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for serializing tokens in either JS or TS style.
|
||||
///
|
||||
/// If your application uses both JS and TS style tokens, [`ESTreeTokenOptions`] is probably preferable
|
||||
/// over [`ESTreeTokenOptionsJS`] and [`ESTreeTokenOptionsTS`], as it can be used to serialize both styles
|
||||
/// without binary including all this code twice.
|
||||
///
|
||||
/// See [`ESTreeTokenConfig`] for more details.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ESTreeTokenOptions {
|
||||
is_ts: bool,
|
||||
}
|
||||
|
||||
impl ESTreeTokenOptions {
|
||||
#[inline(always)]
|
||||
pub fn new(is_ts: bool) -> Self {
|
||||
Self { is_ts }
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTreeTokenConfig for ESTreeTokenOptions {
|
||||
/// Working JSX state, because state is required for TS-style tokens.
|
||||
type JSXState = JSXStateTS;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_ts(&self) -> bool {
|
||||
self.is_ts
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ use oxc_ast_macros::ast;
|
||||
use oxc_ast_visit::utf8_to_utf16::Utf8ToUtf16;
|
||||
use oxc_data_structures::box_macros::boxed_array;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_estree_tokens::{ESTreeTokenOptions, to_estree_tokens_json};
|
||||
use oxc_estree_tokens::{ESTreeTokenOptionsJS, to_estree_tokens_json};
|
||||
use oxc_semantic::AstNode;
|
||||
use oxc_span::Span;
|
||||
|
||||
@@ -576,7 +576,7 @@ impl Linter {
|
||||
program,
|
||||
original_source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::test262(),
|
||||
ESTreeTokenOptionsJS,
|
||||
);
|
||||
let tokens_json = allocator.alloc_str(&tokens_json);
|
||||
let tokens_offset = tokens_json.as_ptr() as u32;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast_visit::utf8_to_utf16::Utf8ToUtf16;
|
||||
use oxc_benchmark::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
||||
use oxc_estree_tokens::{ESTreeTokenOptions, to_estree_tokens_json};
|
||||
use oxc_estree_tokens::{ESTreeTokenOptionsJS, to_estree_tokens_json};
|
||||
use oxc_parser::{ParseOptions, Parser, ParserReturn, config::RuntimeParserConfig};
|
||||
use oxc_tasks_common::TestFiles;
|
||||
|
||||
@@ -145,7 +145,7 @@ fn bench_estree_tokens(criterion: &mut Criterion) {
|
||||
&program,
|
||||
program.source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::test262(),
|
||||
ESTreeTokenOptionsJS,
|
||||
);
|
||||
let tokens_json = black_box(tokens_json);
|
||||
// Allocate tokens JSON into arena, same as linter and NAPI parser package do
|
||||
|
||||
@@ -854,7 +854,7 @@ pub fn run_estree_test262_tokens(files: &[Test262File]) -> Vec<CoverageResult> {
|
||||
&program,
|
||||
source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::test262(),
|
||||
ESTreeTokenOptions::new(false),
|
||||
);
|
||||
|
||||
span_converter.convert_program_with_ascending_order_checks(&mut program);
|
||||
@@ -904,7 +904,7 @@ pub fn run_estree_acorn_jsx_tokens(files: &[AcornJsxFile]) -> Vec<CoverageResult
|
||||
&program,
|
||||
source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::test262(),
|
||||
ESTreeTokenOptions::new(false),
|
||||
);
|
||||
|
||||
span_converter.convert_program_with_ascending_order_checks(&mut program);
|
||||
@@ -1080,7 +1080,7 @@ pub fn run_estree_typescript_tokens(files: &[TypeScriptFile]) -> Vec<CoverageRes
|
||||
&program,
|
||||
source_text,
|
||||
&span_converter,
|
||||
ESTreeTokenOptions::typescript(),
|
||||
ESTreeTokenOptions::new(true),
|
||||
);
|
||||
|
||||
span_converter.convert_program_with_ascending_order_checks(&mut program);
|
||||
|
||||
Reference in new issue
Block a user