diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..754649b48349a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,10 @@ +--- +name: Bug report +about: Create a report to help us improve. +title: '' +labels: C-bug +assignees: Boshen + +--- + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000..098efa6c9b716 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +contact_links: + - name: Github discussions + url: https://github.com/oxc-project/oxc/discussions + about: For general discussions. + + - name: Discord server + url: https://discord.gg/9uXCAwqQZW + about: For lightweight questions. + + - name: Website issues + url: https://github.com/oxc-project/oxc-project.github.io + about: For website issues. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..f03f5853cb0f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,10 @@ +--- +name: Feature request +about: Suggest an idea for this project. +title: '' +labels: C-enhancement +assignees: Boshen + +--- + + diff --git a/.github/codecov.yml b/.github/codecov.yml index 0eeaf91b9ae43..978159b59f638 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -21,6 +21,4 @@ ignore: - "crates/oxc_ast/src/span.rs" - "crates/oxc_wasm" # Remove this once wasm is completed - "crates/oxc_diagnostics" - - "crates/oxc_type_synthesis" - - "crates/oxc_transformer" # not ready - "crates/oxc_js_regex" # not ready diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 49207ca733827..736cecd29adb3 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -110,6 +110,7 @@ jobs: benchmark-napi: name: Benchmark NAPI parser runs-on: ubuntu-latest + if: false steps: - name: Checkout Branch uses: taiki-e/checkout-action@v1 @@ -188,7 +189,8 @@ jobs: upload: name: Upload benchmarks - needs: [benchmark, benchmark-napi] + # needs: [benchmark, benchmark-napi] + needs: [benchmark] runs-on: ubuntu-latest steps: - name: Checkout Branch diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index c0bba780a7e06..55635aff703ef 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -28,6 +28,9 @@ jobs: - name: Install Rust Toolchain uses: ./.github/actions/rustup + with: + shared-key: 'codecov' + save-cache: ${{ github.ref_name == 'main' }} - name: Install cargo-llvm-cov uses: taiki-e/install-action@v2 diff --git a/.github/workflows/release_wasm.yml b/.github/workflows/release_wasm.yml index aa9cee61be542..686fbfd26a118 100644 --- a/.github/workflows/release_wasm.yml +++ b/.github/workflows/release_wasm.yml @@ -63,9 +63,10 @@ jobs: corepack enable pnpm install pnpm run build + pnpm run test - name: Publish working-directory: npm/parser-wasm env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish --tag latest --provenance --access public + run: npm publish --provenance --access public # --tag alpha diff --git a/Cargo.lock b/Cargo.lock index d1c06caa13772..db1fcf12324a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1266,7 +1266,7 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" [[package]] name = "oxc" -version = "0.12.1" +version = "0.12.3" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxc_allocator" -version = "0.12.1" +version = "0.12.3" dependencies = [ "bumpalo", "serde", @@ -1293,7 +1293,7 @@ dependencies = [ [[package]] name = "oxc_ast" -version = "0.12.1" +version = "0.12.3" dependencies = [ "bitflags 2.5.0", "num-bigint", @@ -1351,7 +1351,7 @@ dependencies = [ [[package]] name = "oxc_codegen" -version = "0.12.1" +version = "0.12.3" dependencies = [ "base64 0.22.0", "bitflags 2.5.0", @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "oxc_diagnostics" -version = "0.12.1" +version = "0.12.3" dependencies = [ "miette", "owo-colors", @@ -1409,7 +1409,7 @@ dependencies = [ [[package]] name = "oxc_index" -version = "0.12.1" +version = "0.12.3" dependencies = [ "index_vec", "static_assertions", @@ -1494,7 +1494,7 @@ dependencies = [ [[package]] name = "oxc_minifier" -version = "0.12.1" +version = "0.12.3" dependencies = [ "insta", "itertools", @@ -1539,7 +1539,7 @@ dependencies = [ [[package]] name = "oxc_parser" -version = "0.12.1" +version = "0.12.3" dependencies = [ "assert-unchecked", "bitflags 2.5.0", @@ -1635,7 +1635,7 @@ dependencies = [ [[package]] name = "oxc_semantic" -version = "0.12.1" +version = "0.12.3" dependencies = [ "indexmap", "insta", @@ -1657,7 +1657,7 @@ dependencies = [ [[package]] name = "oxc_sourcemap" -version = "0.12.1" +version = "0.12.3" dependencies = [ "base64-simd", "rayon", @@ -1668,7 +1668,7 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.12.1" +version = "0.12.3" dependencies = [ "compact_str", "miette", @@ -1679,7 +1679,7 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.12.1" +version = "0.12.3" dependencies = [ "bitflags 2.5.0", "dashmap", @@ -1728,7 +1728,7 @@ dependencies = [ [[package]] name = "oxc_transformer" -version = "0.12.1" +version = "0.12.3" dependencies = [ "oxc_allocator", "oxc_ast", diff --git a/Cargo.toml b/Cargo.toml index 98ac209d490a4..3ab7f0f516884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,19 +67,19 @@ cargo_common_metadata = "allow" # TODO: fix this [workspace.dependencies] # publish = true -oxc = { version = "0.12.1", path = "crates/oxc" } -oxc_allocator = { version = "0.12.1", path = "crates/oxc_allocator" } -oxc_ast = { version = "0.12.1", path = "crates/oxc_ast" } -oxc_codegen = { version = "0.12.1", path = "crates/oxc_codegen" } -oxc_diagnostics = { version = "0.12.1", path = "crates/oxc_diagnostics" } -oxc_index = { version = "0.12.1", path = "crates/oxc_index" } -oxc_minifier = { version = "0.12.1", path = "crates/oxc_minifier" } -oxc_parser = { version = "0.12.1", path = "crates/oxc_parser" } -oxc_semantic = { version = "0.12.1", path = "crates/oxc_semantic" } -oxc_span = { version = "0.12.1", path = "crates/oxc_span" } -oxc_syntax = { version = "0.12.1", path = "crates/oxc_syntax" } -oxc_transformer = { version = "0.12.1", path = "crates/oxc_transformer" } -oxc_sourcemap = { version = "0.12.1", path = "crates/oxc_sourcemap" } +oxc = { version = "0.12.3", path = "crates/oxc" } +oxc_allocator = { version = "0.12.3", path = "crates/oxc_allocator" } +oxc_ast = { version = "0.12.3", path = "crates/oxc_ast" } +oxc_codegen = { version = "0.12.3", path = "crates/oxc_codegen" } +oxc_diagnostics = { version = "0.12.3", path = "crates/oxc_diagnostics" } +oxc_index = { version = "0.12.3", path = "crates/oxc_index" } +oxc_minifier = { version = "0.12.3", path = "crates/oxc_minifier" } +oxc_parser = { version = "0.12.3", path = "crates/oxc_parser" } +oxc_semantic = { version = "0.12.3", path = "crates/oxc_semantic" } +oxc_span = { version = "0.12.3", path = "crates/oxc_span" } +oxc_syntax = { version = "0.12.3", path = "crates/oxc_syntax" } +oxc_transformer = { version = "0.12.3", path = "crates/oxc_transformer" } +oxc_sourcemap = { version = "0.12.3", path = "crates/oxc_sourcemap" } # publish = false oxc_macros = { path = "crates/oxc_macros" } diff --git a/crates/oxc/Cargo.toml b/crates/oxc/Cargo.toml index 1af2a900eb5ca..87e81c0d51caa 100644 --- a/crates/oxc/Cargo.toml +++ b/crates/oxc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_allocator/Cargo.toml b/crates/oxc_allocator/Cargo.toml index 6b5afba9b6332..65616f4216708 100644 --- a/crates/oxc_allocator/Cargo.toml +++ b/crates/oxc_allocator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_allocator" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_ast/CHANGELOG.md b/crates/oxc_ast/CHANGELOG.md index 3e3b42828bd16..539bfdafc7eb3 100644 --- a/crates/oxc_ast/CHANGELOG.md +++ b/crates/oxc_ast/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Features + +- Add missing ast visits for types (#2938) + +### Refactor + +- Clean up the ts type visit methods + ## [0.11.1] - 2024-04-03 ### Bug Fixes diff --git a/crates/oxc_ast/Cargo.toml b/crates/oxc_ast/Cargo.toml index 428cce436caa4..776b15a1e0ebd 100644 --- a/crates/oxc_ast/Cargo.toml +++ b/crates/oxc_ast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ast" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_ast/src/ast_kind.rs b/crates/oxc_ast/src/ast_kind.rs index 09a11e1765645..3732dda3eaef4 100644 --- a/crates/oxc_ast/src/ast_kind.rs +++ b/crates/oxc_ast/src/ast_kind.rs @@ -151,16 +151,29 @@ ast_kinds! { // NOTE: make sure add these to AstKind::is_type below TSAnyKeyword(&'a TSAnyKeyword), + TSBigIntKeyword(&'a TSBigIntKeyword), + TSBooleanKeyword(&'a TSBooleanKeyword), + TSNeverKeyword(&'a TSNeverKeyword), + TSNullKeyword(&'a TSNullKeyword), + TSNumberKeyword(&'a TSNumberKeyword), + TSObjectKeyword(&'a TSObjectKeyword), + TSStringKeyword(&'a TSStringKeyword), + TSSymbolKeyword(&'a TSSymbolKeyword), + TSUndefinedKeyword(&'a TSUndefinedKeyword), + TSUnknownKeyword(&'a TSUnknownKeyword), + TSVoidKeyword(&'a TSVoidKeyword), + + // NOTE: make sure add these to AstKind::is_type below + TSIndexedAccessType(&'a TSIndexedAccessType<'a>), + TSInferType(&'a TSInferType<'a>), TSIntersectionType(&'a TSIntersectionType<'a>), TSLiteralType(&'a TSLiteralType<'a>), TSMethodSignature(&'a TSMethodSignature<'a>), - TSNullKeyword(&'a TSNullKeyword), + TSTemplateLiteralType(&'a TSTemplateLiteralType<'a>), + TSThisType(&'a TSThisType), TSTypeLiteral(&'a TSTypeLiteral<'a>), TSTypeReference(&'a TSTypeReference<'a>), TSUnionType(&'a TSUnionType<'a>), - TSVoidKeyword(&'a TSVoidKeyword), - - TSIndexedAccessType(&'a TSIndexedAccessType<'a>), TSAsExpression(&'a TSAsExpression<'a>), TSSatisfiesExpression(&'a TSSatisfiesExpression<'a>), @@ -185,6 +198,7 @@ ast_kinds! { TSTypeParameterDeclaration(&'a TSTypeParameterDeclaration<'a>), TSTypeParameterInstantiation(&'a TSTypeParameterInstantiation<'a>), TSImportType(&'a TSImportType<'a>), + TSNamedTupleMember(&'a TSNamedTupleMember<'a>), TSPropertySignature(&'a TSPropertySignature<'a>), } @@ -234,14 +248,14 @@ impl<'a> AstKind<'a> { | Self::LabelIdentifier(_)) } + #[rustfmt::skip] pub fn is_type(self) -> bool { - matches!( - self, - Self::TSIntersectionType(_) - | Self::TSLiteralType(_) - | Self::TSTypeReference(_) - | Self::TSMethodSignature(_) - ) + matches!(self, Self::TSAnyKeyword(_) | Self::TSBigIntKeyword(_) | Self::TSBooleanKeyword(_) | Self::TSNeverKeyword(_) + | Self::TSNullKeyword(_) | Self::TSNumberKeyword(_) | Self::TSObjectKeyword(_) | Self::TSStringKeyword(_) + | Self::TSSymbolKeyword(_) | Self::TSUndefinedKeyword(_) | Self::TSUnknownKeyword(_) | Self::TSVoidKeyword(_) + | Self::TSIndexedAccessType(_) | Self::TSInferType(_) | Self::TSIntersectionType(_) | Self::TSLiteralType(_) + | Self::TSMethodSignature(_) | Self::TSTemplateLiteralType(_) | Self::TSThisType(_) | Self::TSTypeLiteral(_) + | Self::TSTypeReference(_) | Self::TSUnionType(_)) } pub fn is_literal(self) -> bool { @@ -477,6 +491,18 @@ impl<'a> GetSpan for AstKind<'a> { Self::TSTypeReference(x) => x.span, Self::TSUnionType(x) => x.span, Self::TSVoidKeyword(x) => x.span, + Self::TSBigIntKeyword(x) => x.span, + Self::TSBooleanKeyword(x) => x.span, + Self::TSNeverKeyword(x) => x.span, + Self::TSNumberKeyword(x) => x.span, + Self::TSObjectKeyword(x) => x.span, + Self::TSStringKeyword(x) => x.span, + Self::TSSymbolKeyword(x) => x.span, + Self::TSThisType(x) => x.span, + Self::TSUndefinedKeyword(x) => x.span, + Self::TSUnknownKeyword(x) => x.span, + Self::TSInferType(x) => x.span, + Self::TSTemplateLiteralType(x) => x.span, Self::TSIndexedAccessType(x) => x.span, @@ -502,6 +528,7 @@ impl<'a> GetSpan for AstKind<'a> { Self::TSTypeParameterDeclaration(x) => x.span, Self::TSTypeParameterInstantiation(x) => x.span, Self::TSImportType(x) => x.span, + Self::TSNamedTupleMember(x) => x.span, Self::TSPropertySignature(x) => x.span, } @@ -664,6 +691,18 @@ impl<'a> AstKind<'a> { Self::TSTypeReference(_) => "TSTypeReference".into(), Self::TSUnionType(_) => "TSUnionType".into(), Self::TSVoidKeyword(_) => "TSVoidKeyword".into(), + Self::TSBigIntKeyword(_) => "TSBigIntKeyword".into(), + Self::TSBooleanKeyword(_) => "TSBooleanKeyword".into(), + Self::TSNeverKeyword(_) => "TSNeverKeyword".into(), + Self::TSNumberKeyword(_) => "TSNumberKeyword".into(), + Self::TSObjectKeyword(_) => "TSObjectKeyword".into(), + Self::TSStringKeyword(_) => "TSStringKeyword".into(), + Self::TSSymbolKeyword(_) => "TSSymbolKeyword".into(), + Self::TSThisType(_) => "TSThisType".into(), + Self::TSUndefinedKeyword(_) => "TSUndefinedKeyword".into(), + Self::TSUnknownKeyword(_) => "TSUnknownKeyword".into(), + Self::TSInferType(_) => "TSInferType".into(), + Self::TSTemplateLiteralType(_) => "TSTemplateLiteralType".into(), Self::TSIndexedAccessType(_) => "TSIndexedAccessType".into(), @@ -690,6 +729,7 @@ impl<'a> AstKind<'a> { Self::TSTypeParameterDeclaration(_) => "TSTypeParameterDeclaration".into(), Self::TSTypeParameterInstantiation(_) => "TSTypeParameterInstantiation".into(), Self::TSImportType(_) => "TSImportType".into(), + Self::TSNamedTupleMember(_) => "TSNamedTupleMember".into(), Self::TSPropertySignature(_) => "TSPropertySignature".into(), } diff --git a/crates/oxc_ast/src/visit/visit.rs b/crates/oxc_ast/src/visit/visit.rs index 29c5c47af24e6..d40e65d486288 100644 --- a/crates/oxc_ast/src/visit/visit.rs +++ b/crates/oxc_ast/src/visit/visit.rs @@ -636,10 +636,6 @@ pub trait Visit<'a>: Sized { walk_ts_external_module_reference(self, reference); } - fn visit_ts_qualified_name(&mut self, name: &TSQualifiedName<'a>) { - walk_ts_qualified_name(self, name); - } - fn visit_ts_module_declaration(&mut self, decl: &TSModuleDeclaration<'a>) { walk_ts_module_declaration(self, decl); } @@ -684,78 +680,146 @@ pub trait Visit<'a>: Sized { walk_ts_type(self, ty); } - fn visit_ts_type_literal(&mut self, ty: &TSTypeLiteral<'a>) { - walk_ts_type_literal(self, ty); + fn visit_ts_tuple_element(&mut self, ty: &TSTupleElement<'a>) { + walk_ts_tuple_element(self, ty); } - fn visit_ts_indexed_access_type(&mut self, ty: &TSIndexedAccessType<'a>) { - walk_ts_indexed_access_type(self, ty); + fn visit_ts_type_parameter(&mut self, ty: &TSTypeParameter<'a>) { + walk_ts_type_parameter(self, ty); } - fn visit_ts_type_predicate(&mut self, ty: &TSTypePredicate<'a>) { - walk_ts_type_predicate(self, ty); + fn visit_ts_type_parameter_instantiation(&mut self, ty: &TSTypeParameterInstantiation<'a>) { + walk_ts_type_parameter_instantiation(self, ty); } - fn visit_ts_type_operator_type(&mut self, ty: &TSTypeOperator<'a>) { - walk_ts_type_operator_type(self, ty); + fn visit_ts_type_parameter_declaration(&mut self, ty: &TSTypeParameterDeclaration<'a>) { + walk_ts_type_parameter_declaration(self, ty); } - fn visit_ts_tuple_type(&mut self, ty: &TSTupleType<'a>) { - walk_ts_tuple_type(self, ty); + fn visit_ts_any_keyword(&mut self, ty: &TSAnyKeyword) { + walk_ts_any_keyword(self, ty); } - fn visit_ts_tuple_element(&mut self, ty: &TSTupleElement<'a>) { - walk_ts_tuple_element(self, ty); + fn visit_ts_big_int_keyword(&mut self, ty: &TSBigIntKeyword) { + walk_ts_big_int_keyword(self, ty); } - fn visit_ts_mapped_type(&mut self, ty: &TSMappedType<'a>) { - walk_ts_mapped_type(self, ty); + fn visit_ts_boolean_keyword(&mut self, ty: &TSBooleanKeyword) { + walk_ts_boolean_keyword(self, ty); } - fn visit_ts_function_type(&mut self, ty: &TSFunctionType<'a>) { - walk_ts_function_type(self, ty); + fn visit_ts_never_keyword(&mut self, ty: &TSNeverKeyword) { + walk_ts_never_keyword(self, ty); } - fn visit_ts_type_parameter(&mut self, ty: &TSTypeParameter<'a>) { - walk_ts_type_parameter(self, ty); + fn visit_ts_null_keyword(&mut self, ty: &TSNullKeyword) { + walk_ts_null_keyword(self, ty); } - fn visit_ts_type_parameter_instantiation(&mut self, ty: &TSTypeParameterInstantiation<'a>) { - walk_ts_type_parameter_instantiation(self, ty); + fn visit_ts_number_keyword(&mut self, ty: &TSNumberKeyword) { + walk_ts_number_keyword(self, ty); } - fn visit_ts_type_parameter_declaration(&mut self, ty: &TSTypeParameterDeclaration<'a>) { - walk_ts_type_parameter_declaration(self, ty); + fn visit_ts_object_keyword(&mut self, ty: &TSObjectKeyword) { + walk_ts_object_keyword(self, ty); } - fn visit_ts_constructor_type(&mut self, ty: &TSConstructorType<'a>) { - walk_ts_constructor_type(self, ty); + fn visit_ts_string_keyword(&mut self, ty: &TSStringKeyword) { + walk_ts_string_keyword(self, ty); } - fn visit_ts_conditional_type(&mut self, ty: &TSConditionalType<'a>) { - walk_ts_conditional_type(self, ty); + fn visit_ts_symbol_keyword(&mut self, ty: &TSSymbolKeyword) { + walk_ts_symbol_keyword(self, ty); + } + + fn visit_ts_undefined_keyword(&mut self, ty: &TSUndefinedKeyword) { + walk_ts_undefined_keyword(self, ty); + } + + fn visit_ts_unknown_keyword(&mut self, ty: &TSUnknownKeyword) { + walk_ts_unknown_keyword(self, ty); + } + + fn visit_ts_void_keyword(&mut self, ty: &TSVoidKeyword) { + walk_ts_void_keyword(self, ty); } fn visit_ts_array_type(&mut self, ty: &TSArrayType<'a>) { walk_ts_array_type(self, ty); } - fn visit_ts_null_keyword(&mut self, ty: &TSNullKeyword) { - walk_ts_null_keyword(self, ty); + fn visit_ts_conditional_type(&mut self, ty: &TSConditionalType<'a>) { + walk_ts_conditional_type(self, ty); } - fn visit_ts_any_keyword(&mut self, ty: &TSAnyKeyword) { - walk_ts_any_keyword(self, ty); + fn visit_ts_constructor_type(&mut self, ty: &TSConstructorType<'a>) { + walk_ts_constructor_type(self, ty); } - fn visit_ts_void_keyword(&mut self, ty: &TSVoidKeyword) { - walk_ts_void_keyword(self, ty); + fn visit_ts_function_type(&mut self, ty: &TSFunctionType<'a>) { + walk_ts_function_type(self, ty); + } + + fn visit_ts_import_type(&mut self, ty: &TSImportType<'a>) { + walk_ts_import_type(self, ty); + } + + fn visit_ts_indexed_access_type(&mut self, ty: &TSIndexedAccessType<'a>) { + walk_ts_indexed_access_type(self, ty); + } + + fn visit_ts_infer_type(&mut self, ty: &TSInferType<'a>) { + walk_ts_infer_type(self, ty); } fn visit_ts_intersection_type(&mut self, ty: &TSIntersectionType<'a>) { walk_ts_intersection_type(self, ty); } + fn visit_ts_literal_type(&mut self, ty: &TSLiteralType<'a>) { + walk_ts_literal_type(self, ty); + } + + fn visit_ts_mapped_type(&mut self, ty: &TSMappedType<'a>) { + walk_ts_mapped_type(self, ty); + } + + fn visit_ts_named_tuple_member(&mut self, ty: &TSNamedTupleMember<'a>) { + walk_ts_named_tuple_member(self, ty); + } + + fn visit_ts_qualified_name(&mut self, name: &TSQualifiedName<'a>) { + walk_ts_qualified_name(self, name); + } + + fn visit_ts_template_literal_type(&mut self, ty: &TSTemplateLiteralType<'a>) { + walk_ts_template_literal_type(self, ty); + } + + fn visit_ts_this_type(&mut self, ty: &TSThisType) { + walk_ts_this_type(self, ty); + } + + fn visit_ts_tuple_type(&mut self, ty: &TSTupleType<'a>) { + walk_ts_tuple_type(self, ty); + } + + fn visit_ts_type_literal(&mut self, ty: &TSTypeLiteral<'a>) { + walk_ts_type_literal(self, ty); + } + + fn visit_ts_type_operator_type(&mut self, ty: &TSTypeOperator<'a>) { + walk_ts_type_operator_type(self, ty); + } + + fn visit_ts_type_predicate(&mut self, ty: &TSTypePredicate<'a>) { + walk_ts_type_predicate(self, ty); + } + + fn visit_ts_type_query(&mut self, ty: &TSTypeQuery<'a>) { + walk_ts_type_query(self, ty); + } + fn visit_ts_type_reference(&mut self, ty: &TSTypeReference<'a>) { walk_ts_type_reference(self, ty); } @@ -764,10 +828,6 @@ pub trait Visit<'a>: Sized { walk_ts_union_type(self, ty); } - fn visit_ts_literal_type(&mut self, ty: &TSLiteralType<'a>) { - walk_ts_literal_type(self, ty); - } - fn visit_ts_signature(&mut self, signature: &TSSignature<'a>) { walk_ts_signature(self, signature); } @@ -799,14 +859,6 @@ pub trait Visit<'a>: Sized { walk_ts_call_signature_declaration(self, signature); } - fn visit_ts_type_query(&mut self, ty: &TSTypeQuery<'a>) { - walk_ts_type_query(self, ty); - } - - fn visit_ts_import_type(&mut self, ty: &TSImportType<'a>) { - walk_ts_import_type(self, ty); - } - fn visit_ts_import_attributes(&mut self, attributes: &TSImportAttributes<'a>) { walk_ts_import_attributes(self, attributes); } @@ -2605,25 +2657,43 @@ pub mod walk { pub fn walk_ts_type<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSType<'a>) { match ty { + // Keyword TSType::TSAnyKeyword(ty) => visitor.visit_ts_any_keyword(ty), + TSType::TSBigIntKeyword(ty) => visitor.visit_ts_big_int_keyword(ty), + TSType::TSBooleanKeyword(ty) => visitor.visit_ts_boolean_keyword(ty), + TSType::TSNeverKeyword(ty) => visitor.visit_ts_never_keyword(ty), TSType::TSNullKeyword(ty) => visitor.visit_ts_null_keyword(ty), + TSType::TSNumberKeyword(ty) => visitor.visit_ts_number_keyword(ty), + TSType::TSObjectKeyword(ty) => visitor.visit_ts_object_keyword(ty), + TSType::TSStringKeyword(ty) => visitor.visit_ts_string_keyword(ty), + TSType::TSSymbolKeyword(ty) => visitor.visit_ts_symbol_keyword(ty), + TSType::TSUndefinedKeyword(ty) => visitor.visit_ts_undefined_keyword(ty), + TSType::TSUnknownKeyword(ty) => visitor.visit_ts_unknown_keyword(ty), TSType::TSVoidKeyword(ty) => visitor.visit_ts_void_keyword(ty), - TSType::TSIntersectionType(ty) => visitor.visit_ts_intersection_type(ty), - TSType::TSTypeReference(ty) => visitor.visit_ts_type_reference(ty), - TSType::TSUnionType(ty) => visitor.visit_ts_union_type(ty), - TSType::TSLiteralType(ty) => visitor.visit_ts_literal_type(ty), + // Compound TSType::TSArrayType(ty) => visitor.visit_ts_array_type(ty), TSType::TSConditionalType(ty) => visitor.visit_ts_conditional_type(ty), TSType::TSConstructorType(ty) => visitor.visit_ts_constructor_type(ty), TSType::TSFunctionType(ty) => visitor.visit_ts_function_type(ty), + TSType::TSImportType(ty) => visitor.visit_ts_import_type(ty), + TSType::TSIndexedAccessType(ty) => visitor.visit_ts_indexed_access_type(ty), + TSType::TSInferType(ty) => visitor.visit_ts_infer_type(ty), + TSType::TSIntersectionType(ty) => visitor.visit_ts_intersection_type(ty), + TSType::TSLiteralType(ty) => visitor.visit_ts_literal_type(ty), TSType::TSMappedType(ty) => visitor.visit_ts_mapped_type(ty), + TSType::TSNamedTupleMember(ty) => visitor.visit_ts_named_tuple_member(ty), + TSType::TSQualifiedName(ty) => visitor.visit_ts_qualified_name(ty), + TSType::TSTemplateLiteralType(ty) => visitor.visit_ts_template_literal_type(ty), + TSType::TSThisType(ty) => visitor.visit_ts_this_type(ty), TSType::TSTupleType(ty) => visitor.visit_ts_tuple_type(ty), + TSType::TSTypeLiteral(ty) => visitor.visit_ts_type_literal(ty), TSType::TSTypeOperatorType(ty) => visitor.visit_ts_type_operator_type(ty), TSType::TSTypePredicate(ty) => visitor.visit_ts_type_predicate(ty), - TSType::TSTypeLiteral(ty) => visitor.visit_ts_type_literal(ty), - TSType::TSIndexedAccessType(ty) => visitor.visit_ts_indexed_access_type(ty), TSType::TSTypeQuery(ty) => visitor.visit_ts_type_query(ty), - _ => {} + TSType::TSTypeReference(ty) => visitor.visit_ts_type_reference(ty), + TSType::TSUnionType(ty) => visitor.visit_ts_union_type(ty), + // JSDoc + TSType::JSDocNullableType(_) | TSType::JSDocUnknownType(_) => { /* TODO */ } } } @@ -2956,4 +3026,97 @@ pub mod walk { TSImportAttributeName::StringLiteral(ident) => visitor.visit_string_literal(ident), } } + + pub fn walk_ts_big_int_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSBigIntKeyword) { + let kind = AstKind::TSBigIntKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_boolean_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSBooleanKeyword) { + let kind = AstKind::TSBooleanKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_never_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSNeverKeyword) { + let kind = AstKind::TSNeverKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_number_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSNumberKeyword) { + let kind = AstKind::TSNumberKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_object_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSObjectKeyword) { + let kind = AstKind::TSObjectKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_string_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSStringKeyword) { + let kind = AstKind::TSStringKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_symbol_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSSymbolKeyword) { + let kind = AstKind::TSSymbolKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_this_type<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSThisType) { + let kind = AstKind::TSThisType(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_undefined_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSUndefinedKeyword) { + let kind = AstKind::TSUndefinedKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_unknown_keyword<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSUnknownKeyword) { + let kind = AstKind::TSUnknownKeyword(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.leave_node(kind); + } + + pub fn walk_ts_infer_type<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSInferType<'a>) { + let kind = AstKind::TSInferType(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.visit_ts_type_parameter(&ty.type_parameter); + visitor.leave_node(kind); + } + + pub fn walk_ts_named_tuple_member<'a, V: Visit<'a>>( + visitor: &mut V, + ty: &TSNamedTupleMember<'a>, + ) { + let kind = AstKind::TSNamedTupleMember(visitor.alloc(ty)); + visitor.enter_node(kind); + visitor.visit_identifier_name(&ty.label); + visitor.visit_ts_type(&ty.element_type); + visitor.leave_node(kind); + } + + pub fn walk_ts_template_literal_type<'a, V: Visit<'a>>( + visitor: &mut V, + ty: &TSTemplateLiteralType<'a>, + ) { + let kind = AstKind::TSTemplateLiteralType(visitor.alloc(ty)); + visitor.enter_node(kind); + for quasi in &ty.quasis { + visitor.visit_template_element(quasi); + } + for ty in &ty.types { + visitor.visit_ts_type(ty); + } + visitor.leave_node(kind); + } } diff --git a/crates/oxc_cli/src/command/lint.rs b/crates/oxc_cli/src/command/lint.rs index b8affbe4ba1ef..9cbd7c7ab13d4 100644 --- a/crates/oxc_cli/src/command/lint.rs +++ b/crates/oxc_cli/src/command/lint.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use bpaf::Bpaf; use oxc_linter::AllowWarnDeny; @@ -136,8 +136,7 @@ pub struct WarningOptions { #[derive(Debug, Clone, Bpaf)] pub struct OutputOptions { /// Use a specific output format (default, json) - // last flag is the default - #[bpaf(long, short, flag(OutputFormat::Json, OutputFormat::Default))] + #[bpaf(long, short, fallback(OutputFormat::Default))] pub format: OutputFormat, } @@ -147,6 +146,16 @@ pub enum OutputFormat { Json, } +impl FromStr for OutputFormat { + type Err = String; + fn from_str(s: &str) -> Result { + Ok(match s { + "json" => Self::Json, + _ => Self::Default, + }) + } +} + /// Enable Plugins #[allow(clippy::struct_field_names)] #[derive(Debug, Clone, Bpaf)] @@ -304,6 +313,7 @@ mod lint_options { fn format() { let options = get_lint_options("-f json"); assert_eq!(options.output_options.format, OutputFormat::Json); + assert!(options.paths.is_empty()); } #[test] diff --git a/crates/oxc_cli/src/lint/mod.rs b/crates/oxc_cli/src/lint/mod.rs index 8b63afe94c2cc..321ef693c9760 100644 --- a/crates/oxc_cli/src/lint/mod.rs +++ b/crates/oxc_cli/src/lint/mod.rs @@ -145,6 +145,7 @@ impl Runner for LintRunner { number_of_errors: diagnostic_service.errors_count(), max_warnings_exceeded: diagnostic_service.max_warnings_exceeded(), deny_warnings: warning_options.deny_warnings, + print_summary: diagnostic_service.is_graphical_output(), }) } } diff --git a/crates/oxc_cli/src/result.rs b/crates/oxc_cli/src/result.rs index e8334652db269..1b23d1657949a 100644 --- a/crates/oxc_cli/src/result.rs +++ b/crates/oxc_cli/src/result.rs @@ -23,6 +23,7 @@ pub struct LintResult { pub number_of_errors: usize, pub max_warnings_exceeded: bool, pub deny_warnings: bool, + pub print_summary: bool, } #[derive(Debug)] @@ -51,30 +52,35 @@ impl Termination for CliRunResult { number_of_errors, max_warnings_exceeded, deny_warnings, + print_summary, }) => { - let threads = rayon::current_num_threads(); - let number_of_diagnostics = number_of_warnings + number_of_errors; + if print_summary { + let threads = rayon::current_num_threads(); + let number_of_diagnostics = number_of_warnings + number_of_errors; - if number_of_diagnostics > 0 { - println!(); - } + if number_of_diagnostics > 0 { + println!(); + } - let time = Self::get_execution_time(&duration); - let s = if number_of_files == 1 { "" } else { "s" }; - println!( - "Finished in {time} on {number_of_files} file{s} with {number_of_rules} rules using {threads} threads." - ); + let time = Self::get_execution_time(&duration); + let s = if number_of_files == 1 { "" } else { "s" }; + println!( + "Finished in {time} on {number_of_files} file{s} with {number_of_rules} rules using {threads} threads." + ); - if max_warnings_exceeded { - println!("Exceeded maximum number of warnings. Found {number_of_warnings}."); - return ExitCode::from(1); - } + if max_warnings_exceeded { + println!( + "Exceeded maximum number of warnings. Found {number_of_warnings}." + ); + return ExitCode::from(1); + } - println!( - "Found {number_of_warnings} warning{} and {number_of_errors} error{}.", - if number_of_warnings == 1 { "" } else { "s" }, - if number_of_errors == 1 { "" } else { "s" } - ); + println!( + "Found {number_of_warnings} warning{} and {number_of_errors} error{}.", + if number_of_warnings == 1 { "" } else { "s" }, + if number_of_errors == 1 { "" } else { "s" } + ); + } let exit_code = u8::from((number_of_warnings > 0 && deny_warnings) || number_of_errors > 0); diff --git a/crates/oxc_codegen/Cargo.toml b/crates/oxc_codegen/Cargo.toml index 59861af8a22aa..8c572799f3b37 100644 --- a/crates/oxc_codegen/Cargo.toml +++ b/crates/oxc_codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_codegen" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_diagnostics/CHANGELOG.md b/crates/oxc_diagnostics/CHANGELOG.md index 8bbbd0e17904f..8d9f841897f50 100644 --- a/crates/oxc_diagnostics/CHANGELOG.md +++ b/crates/oxc_diagnostics/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Bug Fixes + +- If format is json do not print summary information (#2899) (#2925) + ## [0.10.0] - 2024-03-14 ### Features diff --git a/crates/oxc_diagnostics/Cargo.toml b/crates/oxc_diagnostics/Cargo.toml index f0b74296d4347..0bfed2a603954 100644 --- a/crates/oxc_diagnostics/Cargo.toml +++ b/crates/oxc_diagnostics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_diagnostics" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_diagnostics/src/service.rs b/crates/oxc_diagnostics/src/service.rs index 06d5ebd3c05d9..0e9190be628ae 100644 --- a/crates/oxc_diagnostics/src/service.rs +++ b/crates/oxc_diagnostics/src/service.rs @@ -52,6 +52,10 @@ impl DiagnosticService { self.reporter = DiagnosticReporter::new_json(); } + pub fn is_graphical_output(&self) -> bool { + matches!(self.reporter, DiagnosticReporter::Graphical { .. }) + } + #[must_use] pub fn with_quiet(mut self, yes: bool) -> Self { self.quiet = yes; diff --git a/crates/oxc_index/Cargo.toml b/crates/oxc_index/Cargo.toml index 3b0ed263601ff..2b459595c3c4e 100644 --- a/crates/oxc_index/Cargo.toml +++ b/crates/oxc_index/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_index" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_linter/fixtures/import/cycles/typescript/ts-depth-type-and-value-imports.ts b/crates/oxc_linter/fixtures/import/cycles/typescript/ts-depth-type-and-value-imports.ts new file mode 100644 index 0000000000000..38056d72976c5 --- /dev/null +++ b/crates/oxc_linter/fixtures/import/cycles/typescript/ts-depth-type-and-value-imports.ts @@ -0,0 +1,5 @@ +// @ts-ignore +import { foo } from './ts-value'; + +// @ts-ignore +import type { foo as FooType } from '../depth-zero'; diff --git a/crates/oxc_linter/fixtures/import/cycles/typescript/ts-value.ts b/crates/oxc_linter/fixtures/import/cycles/typescript/ts-value.ts new file mode 100644 index 0000000000000..3329a7d972fae --- /dev/null +++ b/crates/oxc_linter/fixtures/import/cycles/typescript/ts-value.ts @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 447563367de55..74443f27d0f48 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -125,6 +125,7 @@ mod typescript { pub mod ban_ts_comment; pub mod ban_tslint_comment; pub mod ban_types; + pub mod consistent_type_definitions; pub mod no_duplicate_enum_values; pub mod no_empty_interface; pub mod no_explicit_any; @@ -170,6 +171,7 @@ mod jest { pub mod prefer_comparison_matcher; pub mod prefer_equality_matcher; pub mod prefer_expect_resolves; + pub mod prefer_lowercase_title; pub mod prefer_mock_promise_shorthand; pub mod prefer_spy_on; pub mod prefer_strict_equal; @@ -458,6 +460,7 @@ oxc_macros::declare_all_lint_rules! { typescript::ban_ts_comment, typescript::ban_tslint_comment, typescript::ban_types, + typescript::consistent_type_definitions, typescript::no_duplicate_enum_values, typescript::no_empty_interface, typescript::no_explicit_any, @@ -500,6 +503,7 @@ oxc_macros::declare_all_lint_rules! { jest::prefer_comparison_matcher, jest::prefer_equality_matcher, jest::prefer_expect_resolves, + jest::prefer_lowercase_title, jest::prefer_mock_promise_shorthand, jest::prefer_spy_on, jest::prefer_strict_equal, diff --git a/crates/oxc_linter/src/rules/import/no_cycle.rs b/crates/oxc_linter/src/rules/import/no_cycle.rs index 87a3ca01b9247..268bb9e4f52a5 100644 --- a/crates/oxc_linter/src/rules/import/no_cycle.rs +++ b/crates/oxc_linter/src/rules/import/no_cycle.rs @@ -147,10 +147,15 @@ impl NoCycle { for module_record_ref in &module_record.loaded_modules { let resolved_absolute_path = &module_record_ref.resolved_absolute_path; - let was_imported_as_type = - &module_record_ref.import_entries.iter().all(|entry| entry.is_type); - if self.ignore_types && *was_imported_as_type { - continue; + if self.ignore_types { + let was_imported_as_type = &module_record + .import_entries + .iter() + .filter(|entry| entry.module_request.name() == module_record_ref.key()) + .all(|entry| entry.is_type); + if *was_imported_as_type { + continue; + } } if !state.traversed.insert(resolved_absolute_path.clone()) { continue; @@ -229,6 +234,10 @@ fn test() { r#"import { foo } from "./typescript/ts-types-depth-two";"#, Some(json!([{"ignoreTypes":true}])), ), + ( + r#"import { foo } from "./typescript/ts-depth-type-and-value-imports";"#, + Some(json!([{"ignoreTypes":true}])), + ), // Flow not supported // (r#"import { bar } from "./flow-types""#, None), // (r#"import { bar } from "./flow-types-only-importing-type""#, None), diff --git a/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs b/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs new file mode 100644 index 0000000000000..c6c54efbb8c96 --- /dev/null +++ b/crates/oxc_linter/src/rules/jest/prefer_lowercase_title.rs @@ -0,0 +1,668 @@ +use oxc_ast::{ + ast::{Argument, Expression}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::{CompactStr, Span}; + +use crate::{ + context::LintContext, + fixer::Fix, + rule::Rule, + utils::{ + collect_possible_jest_call_node, parse_jest_fn_call, JestFnKind, JestGeneralFnKind, + ParsedJestFnCallNew, PossibleJestNode, + }, +}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names")] +#[diagnostic(severity(warning), help("`{0:?}`s should begin with lowercase"))] +struct UnexpectedLowercase(CompactStr, #[label] Span); + +#[derive(Debug, Default, Clone)] +pub struct PreferLowercaseTitleConfig { + allowed_prefixes: Vec, + ignore: Vec, + ignore_top_level_describe: bool, +} + +impl std::ops::Deref for PreferLowercaseTitle { + type Target = PreferLowercaseTitleConfig; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Default, Clone)] +pub struct PreferLowercaseTitle(Box); + +declare_oxc_lint!( + /// ### What it does + /// + /// Enforce `it`, `test` and `describe` to have descriptions that begin with a + /// lowercase letter. This provides more readable test failures. This rule is not + /// enabled by default. + /// + /// ### Example + /// + /// ```javascript + /// // invalid + /// it('Adds 1 + 2 to equal 3', () => { + /// expect(sum(1, 2)).toBe(3); + /// }); + /// + /// // valid + /// it('adds 1 + 2 to equal 3', () => { + /// expect(sum(1, 2)).toBe(3); + /// }); + /// ``` + /// + /// ## Options + /// ```json + /// { + /// "jest/prefer-lowercase-title": [ + /// "error", + /// { + /// "ignore": ["describe", "test"] + /// } + /// ] + /// } + /// ``` + /// + /// ### `ignore` + /// + /// This array option controls which Jest functions are checked by this rule. There + /// are three possible values: + /// - `"describe"` + /// - `"test"` + /// - `"it"` + /// + /// By default, none of these options are enabled (the equivalent of + /// `{ "ignore": [] }`). + /// + /// Example of **correct** code for the `{ "ignore": ["describe"] }` option: + /// ```js + /// /* eslint jest/prefer-lowercase-title: ["error", { "ignore": ["describe"] }] */ + /// describe('Uppercase description'); + /// ``` + /// + /// Example of **correct** code for the `{ "ignore": ["test"] }` option: + /// + /// ```js + /// /* eslint jest/prefer-lowercase-title: ["error", { "ignore": ["test"] }] */ + /// test('Uppercase description'); + /// ``` + /// + /// Example of **correct** code for the `{ "ignore": ["it"] }` option: + /// ```js + /// /* eslint jest/prefer-lowercase-title: ["error", { "ignore": ["it"] }] */ + /// it('Uppercase description'); + /// ``` + /// + /// ### `allowedPrefixes` + /// This array option allows specifying prefixes, which contain capitals that titles + /// can start with. This can be useful when writing tests for API endpoints, where + /// you'd like to prefix with the HTTP method. + /// By default, nothing is allowed (the equivalent of `{ "allowedPrefixes": [] }`). + /// + /// Example of **correct** code for the `{ "allowedPrefixes": ["GET"] }` option: + /// ```js + /// /* eslint jest/prefer-lowercase-title: ["error", { "allowedPrefixes": ["GET"] }] */ + /// describe('GET /live'); + /// ``` + /// + /// ### `ignoreTopLevelDescribe` + /// This option can be set to allow only the top-level `describe` blocks to have a + /// title starting with an upper-case letter. + /// Example of **correct** code for the `{ "ignoreTopLevelDescribe": true }` option: + /// + /// ```js + /// /* eslint jest/prefer-lowercase-title: ["error", { "ignoreTopLevelDescribe": true }] */ + /// describe('MyClass', () => { + /// describe('#myMethod', () => { + /// it('does things', () => { + /// // + /// }); + /// }); + /// }); + /// ``` + /// + PreferLowercaseTitle, + style, +); + +impl Rule for PreferLowercaseTitle { + fn from_configuration(value: serde_json::Value) -> Self { + let obj = value.get(0); + let ignore_top_level_describe = obj + .and_then(|config| config.get("ignoreTopLevelDescribe")) + .and_then(serde_json::Value::as_bool) + .unwrap_or(false); + let ignore = obj + .and_then(|config| config.get("ignore")) + .and_then(serde_json::Value::as_array) + .map(|v| { + v.iter().filter_map(serde_json::Value::as_str).map(ToString::to_string).collect() + }) + .unwrap_or_default(); + let allowed_prefixes = obj + .and_then(|config| config.get("allowedPrefixes")) + .and_then(serde_json::Value::as_array) + .map(|v| { + v.iter().filter_map(serde_json::Value::as_str).map(ToString::to_string).collect() + }) + .unwrap_or_default(); + + Self(Box::new(PreferLowercaseTitleConfig { + allowed_prefixes, + ignore, + ignore_top_level_describe, + })) + } + + fn run_once(&self, ctx: &LintContext) { + for possible_jest_node in &collect_possible_jest_call_node(ctx) { + self.run(possible_jest_node, ctx); + } + } +} + +impl PreferLowercaseTitle { + fn run<'a>(&self, possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { + let scopes = ctx.scopes(); + let node = possible_jest_node.node; + let ignores = Self::populate_ignores(&self.ignore); + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + let Some(ParsedJestFnCallNew::GeneralJestFnCall(jest_fn_call)) = + parse_jest_fn_call(call_expr, possible_jest_node, ctx) + else { + return; + }; + + if ignores.contains(&jest_fn_call.name.as_ref()) { + return; + } + + if matches!(jest_fn_call.kind, JestFnKind::General(JestGeneralFnKind::Describe)) { + if self.ignore_top_level_describe && scopes.get_flags(node.scope_id()).is_top() { + return; + } + } else if !matches!(jest_fn_call.kind, JestFnKind::General(JestGeneralFnKind::Test)) { + return; + } + + let Some(Argument::Expression(expr)) = call_expr.arguments.first() else { + return; + }; + + if let Expression::StringLiteral(string_expr) = expr { + if string_expr.value.is_empty() + || self.allowed_prefixes.iter().any(|name| string_expr.value.starts_with(name)) + { + return; + } + + let Some(first_char) = string_expr.value.chars().next() else { + return; + }; + + if first_char == first_char.to_ascii_lowercase() { + return; + } + + ctx.diagnostic_with_fix( + UnexpectedLowercase(string_expr.value.to_compact_str(), string_expr.span), + || { + let mut content = ctx.codegen(); + content.print_str(first_char.to_ascii_lowercase().to_string().as_bytes()); + Fix::new( + content.into_source_text(), + Span::new(string_expr.span.start + 1, string_expr.span.start + 2), + ) + }, + ); + } else if let Expression::TemplateLiteral(template_expr) = expr { + let Some(template_string) = template_expr.quasi() else { + return; + }; + + if template_string.is_empty() + || self.allowed_prefixes.iter().any(|name| template_string.starts_with(name)) + { + return; + } + + let Some(first_char) = template_string.chars().next() else { + return; + }; + + if first_char == first_char.to_ascii_lowercase() { + return; + } + + ctx.diagnostic_with_fix( + UnexpectedLowercase(template_string.to_compact_str(), template_expr.span), + || { + let mut content = ctx.codegen(); + content.print_str(first_char.to_ascii_lowercase().to_string().as_bytes()); + Fix::new( + content.into_source_text(), + Span::new(template_expr.span.start + 1, template_expr.span.start + 2), + ) + }, + ); + } + } + + fn populate_ignores(ignore: &[String]) -> Vec<&str> { + let mut ignores: Vec<&str> = vec![]; + let test_case_name = ["fit", "it", "xit", "test", "xtest"]; + let describe_alias = ["describe", "fdescribe", "xdescribe"]; + let test_name = "test"; + let it_name = "it"; + + if ignore.iter().any(|alias| alias == "describe") { + ignores.extend(describe_alias.iter()); + } + + if ignore.iter().any(|alias| alias == test_name) { + ignores.extend(test_case_name.iter().filter(|alias| alias.ends_with(test_name))); + } + + if ignore.iter().any(|alias| alias == it_name) { + ignores.extend(test_case_name.iter().filter(|alias| alias.ends_with(it_name))); + } + + ignores + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("it.each()", None), + ("it.each()(1)", None), + ("randomFunction()", None), + ("foo.bar()", None), + ("it()", None), + ("it(' ', function () {})", None), + ("it(true, function () {})", None), + ("it(MY_CONSTANT, function () {})", None), + ("it(\" \", function () {})", None), + ("it(` `, function () {})", None), + ("it('foo', function () {})", None), + ("it(\"foo\", function () {})", None), + ("it(`foo`, function () {})", None), + ("it(\"\", function () {})", None), + ("it(\"123 foo\", function () {})", None), + ("it(42, function () {})", None), + ("it(``)", None), + ("it(\"\")", None), + ("it(42)", None), + ("test()", None), + ("test('foo', function () {})", None), + ("test(\"foo\", function () {})", None), + ("test(`foo`, function () {})", None), + ("test(\"\", function () {})", None), + ("test(\"123 foo\", function () {})", None), + ("test(\"42\", function () {})", None), + ("test(``)", None), + ("test(\"\")", None), + ("test(42)", None), + ("describe()", None), + ("describe('foo', function () {})", None), + ("describe(\"foo\", function () {})", None), + ("describe(`foo`, function () {})", None), + ("describe(\"\", function () {})", None), + ("describe(\"123 foo\", function () {})", None), + ("describe(\"42\", function () {})", None), + ("describe(function () {})", None), + ("describe(``)", None), + ("describe(\"\")", None), + ( + " + describe.each()(1); + describe.each()(2); + ", + None, + ), + ("jest.doMock(\"my-module\")", None), + ( + " + import { jest } from '@jest/globals'; + jest.doMock('my-module'); + ", + None, + ), + ("describe(42)", None), + ( + "describe(42)", + Some(serde_json::json!([{ "ignore": "undefined", "allowedPrefixes": "undefined" }])), + ), + // ignore = describe + ("describe('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["describe"] }]))), + ( + "describe(\"Foo\", function () {})", + Some(serde_json::json!([{ "ignore": ["describe"] }])), + ), + ("describe(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["describe"] }]))), + ("fdescribe(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["describe"] }]))), + ( + "describe.skip(`Foo`, function () {})", + Some(serde_json::json!([{ "ignore": ["describe"] }])), + ), + // ignore=test + ("test('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("test(\"Foo\", function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("test(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("xtest(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("test.only(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + // ignore=it + ("it('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("it(\"Foo\", function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("it(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("fit(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("it.skip(`Foo`, function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + // allowedPrefixes + ( + "it('GET /live', function () {})", + Some(serde_json::json!([{ "allowedPrefixes": ["GET"] }])), + ), + ( + "it(\"POST /live\", function () {})", + Some(serde_json::json!([{ "allowedPrefixes": ["GET", "POST"] }])), + ), + ( + "it(`PATCH /live`, function () {})", + Some(serde_json::json!([{ "allowedPrefixes": ["GET", "PATCH"] }])), + ), + // ignoreTopLevelDescribe + ( + "describe(\"MyClass\", () => {});", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('MyClass', () => { + describe('#myMethod', () => { + it('does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('Strings', () => { + it('are strings', () => { + expect('abc').toBe('abc'); + }); + }); + + describe('Booleans', () => { + it('are booleans', () => { + expect(true).toBe(true); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ]; + + let fail = vec![ + ("it('Foo', function () {})", None), + ("xit('Foo', function () {})", None), + ("it(\"Foo\", function () {})", None), + ("it(`Foo`, function () {})", None), + ("test('Foo', function () {})", None), + ("xtest('Foo', function () {})", None), + ("test(\"Foo\", function () {})", None), + ("test(`Foo`, function () {})", None), + ("describe('Foo', function () {})", None), + ("describe(\"Foo\", function () {})", None), + ("describe(`Foo`, function () {})", None), + ( + " + import { describe as context } from '@jest/globals'; + context(`Foo`, () => {}); + ", + None, + ), + ("describe(`Some longer description`, function () {})", None), + ("fdescribe(`Some longer description`, function () {})", None), + ("it.each(['green', 'black'])('Should return %', () => {})", None), + ("describe.each(['green', 'black'])('Should return %', () => {})", None), + // ignore describe + ("test('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["describe"] }]))), + ("xit('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["describe"] }]))), + // ignore test + ("describe('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("it('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + ("xit('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["test"] }]))), + // ignore it + ("describe('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("test('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + ("xtest('Foo', function () {})", Some(serde_json::json!([{ "ignore": ["it"] }]))), + // allowedPrefixes + + // ignoreTopLevelDescribe + ( + "it(\"Works!\", () => {});", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + import { describe, describe as context } from '@jest/globals'; + describe('MyClass', () => { + context('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": false }])), + ), + ]; + + let fix = vec![ + ("it('Foo', function () {})", "it('foo', function () {})", None), + ("xit('Foo', function () {})", "xit('foo', function () {})", None), + ("it(\"Foo\", function () {})", "it(\"foo\", function () {})", None), + ("it(`Foo`, function () {})", "it(`foo`, function () {})", None), + ("test('Foo', function () {})", "test('foo', function () {})", None), + ("xtest('Foo', function () {})", "xtest('foo', function () {})", None), + ("test(\"Foo\", function () {})", "test(\"foo\", function () {})", None), + ("test(`Foo`, function () {})", "test(`foo`, function () {})", None), + ("describe('Foo', function () {})", "describe('foo', function () {})", None), + ("describe(\"Foo\", function () {})", "describe(\"foo\", function () {})", None), + ("describe(`Foo`, function () {})", "describe(`foo`, function () {})", None), + ( + " + import { describe as context } from '@jest/globals'; + context(`Foo`, () => {}); + ", + " + import { describe as context } from '@jest/globals'; + context(`foo`, () => {}); + ", + None, + ), + ( + "describe(`Some longer description`, function () {})", + "describe(`some longer description`, function () {})", + None, + ), + ( + "fdescribe(`Some longer description`, function () {})", + "fdescribe(`some longer description`, function () {})", + None, + ), + ( + "it.each(['green', 'black'])('Should return %', () => {})", + "it.each(['green', 'black'])('should return %', () => {})", + None, + ), + ( + "describe.each(['green', 'black'])('Should return %', () => {})", + "describe.each(['green', 'black'])('should return %', () => {})", + None, + ), + // ignore describe + ( + "test('Foo', function () {})", + "test('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["describe"] }])), + ), + ( + "xit('Foo', function () {})", + "xit('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["describe"] }])), + ), + // ignore test + ( + "describe('Foo', function () {})", + "describe('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["test"] }])), + ), + ( + "it('Foo', function () {})", + "it('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["test"] }])), + ), + ( + "xit('Foo', function () {})", + "xit('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["test"] }])), + ), + // ignore it + ( + "describe('Foo', function () {})", + "describe('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["it"] }])), + ), + ( + "test('Foo', function () {})", + "test('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["it"] }])), + ), + ( + "xtest('Foo', function () {})", + "xtest('foo', function () {})", + Some(serde_json::json!([{ "ignore": ["it"] }])), + ), + // allowedPrefixes empty + // ignoreTopLevelDescribe + ( + "it(\"Works!\", () => {});", + "it(\"works!\", () => {});", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + " + describe('MyClass', () => { + describe('myMethod', () => { + it('does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + import { describe, describe as context } from '@jest/globals'; + describe('MyClass', () => { + context('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + " + import { describe, describe as context } from '@jest/globals'; + describe('MyClass', () => { + context('myMethod', () => { + it('does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": true }])), + ), + ( + " + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + ", + " + describe('myClass', () => { + describe('myMethod', () => { + it('does things', () => { + // + }); + }); + }); + ", + Some(serde_json::json!([{ "ignoreTopLevelDescribe": false }])), + ), + ]; + + Tester::new(PreferLowercaseTitle::NAME, pass, fail) + .with_jest_plugin(true) + .expect_fix(fix) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/jsdoc/check_access.rs b/crates/oxc_linter/src/rules/jsdoc/check_access.rs index ade094f0e9aa6..17929575c08a7 100644 --- a/crates/oxc_linter/src/rules/jsdoc/check_access.rs +++ b/crates/oxc_linter/src/rules/jsdoc/check_access.rs @@ -11,7 +11,7 @@ use crate::{context::LintContext, rule::Rule}; #[derive(Debug, Error, Diagnostic)] enum CheckAccessDiagnostic { - #[error("eslint-plugin-jsdoc(check-access): Invalid access level is specified.")] + #[error("eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing.")] #[diagnostic( severity(warning), help("Valid access levels are `package`, `private`, `protected`, and `public`.") @@ -77,19 +77,23 @@ impl Rule for CheckAccess { for jsdoc in ctx.semantic().jsdoc().iter_all() { let mut access_related_tags_count = 0; - for (span, tag) in jsdoc.tags() { - if access_related_tag_names.contains(tag.kind) { + for tag in jsdoc.tags() { + let kind = tag.kind.parsed(); + if access_related_tag_names.contains(kind) { access_related_tags_count += 1; } // Has valid access level? - if tag.kind == resolved_access_tag_name && !ACCESS_LEVELS.contains(&tag.comment()) { - ctx.diagnostic(CheckAccessDiagnostic::InvalidAccessLevel(*span)); + let comment = tag.comment(); + if kind == resolved_access_tag_name && !ACCESS_LEVELS.contains(&comment.parsed()) { + ctx.diagnostic(CheckAccessDiagnostic::InvalidAccessLevel( + comment.span_trimmed_first_line(), + )); } // Has redundant access level? if 1 < access_related_tags_count { - ctx.diagnostic(CheckAccessDiagnostic::RedundantAccessTags(*span)); + ctx.diagnostic(CheckAccessDiagnostic::RedundantAccessTags(tag.kind.span)); } } } diff --git a/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs b/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs index 31d3488cf558a..63b962bd9d9fa 100644 --- a/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs +++ b/crates/oxc_linter/src/rules/jsdoc/empty_tags.rs @@ -11,7 +11,7 @@ use crate::{context::LintContext, rule::Rule}; #[derive(Debug, Error, Diagnostic)] #[error("eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content.")] -#[diagnostic(severity(warning), help("`@{1}` tag should be empty."))] +#[diagnostic(severity(warning), help("`@{1}` tag should not have body."))] struct EmptyTagsDiagnostic(#[label] Span, String); #[derive(Debug, Default, Clone)] @@ -106,15 +106,20 @@ impl Rule for EmptyTags { }; for jsdoc in ctx.semantic().jsdoc().iter_all() { - for (span, tag) in jsdoc.tags() { - if !is_empty_tag_kind(tag.kind) { + for tag in jsdoc.tags() { + let kind = tag.kind.parsed(); + if !is_empty_tag_kind(kind) { continue; } - if tag.comment().is_empty() { + let comment = tag.comment(); + if comment.parsed().is_empty() { continue; } - ctx.diagnostic(EmptyTagsDiagnostic(*span, tag.kind.to_string())); + ctx.diagnostic(EmptyTagsDiagnostic( + comment.span_trimmed_first_line(), + kind.to_string(), + )); } } } @@ -308,7 +313,8 @@ fn test() { ( " /** - * @private {someType} + * @private foo + * bar */ function quux () { @@ -320,7 +326,8 @@ fn test() { ( " /** - * @internal {someType} + * @internal + * foo */ function quux () { diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs index d0ae38150661e..f06ed1e4f651c 100644 --- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs +++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs @@ -3,11 +3,11 @@ use std::cell::{Cell, RefCell}; use oxc_ast::{ ast::{ Argument, ArrayExpressionElement, ArrowFunctionExpression, AssignmentTarget, - BindingPattern, BindingPatternKind, CallExpression, ComputedMemberExpression, Declaration, - Expression, FormalParameter, Function, IdentifierReference, MemberExpression, - ModuleDeclaration, NewExpression, ParenthesizedExpression, PrivateFieldExpression, Program, - SimpleAssignmentTarget, Statement, StaticMemberExpression, ThisExpression, - VariableDeclarator, + BinaryExpression, BindingPattern, BindingPatternKind, CallExpression, + ComputedMemberExpression, Declaration, Expression, FormalParameter, Function, + IdentifierReference, MemberExpression, ModuleDeclaration, NewExpression, + ParenthesizedExpression, PrivateFieldExpression, Program, SimpleAssignmentTarget, + Statement, StaticMemberExpression, ThisExpression, VariableDeclarator, }, AstKind, }; @@ -17,7 +17,7 @@ use rustc_hash::FxHashSet; use crate::{ ast_util::{get_declaration_of_variable, get_symbol_id_of_variable}, - utils::{get_write_expr, has_pure_notation, no_effects, Value}, + utils::{calculate_binary_operation, get_write_expr, has_pure_notation, no_effects, Value}, LintContext, }; @@ -51,8 +51,8 @@ pub trait ListenerMap { fn report_effects_when_assigned(&self, _options: &NodeListenerOptions) {} fn report_effects_when_called(&self, _options: &NodeListenerOptions) {} fn report_effects_when_mutated(&self, _options: &NodeListenerOptions) {} - fn get_value_and_report_effects(&self, _options: &NodeListenerOptions) -> Option { - None + fn get_value_and_report_effects(&self, _options: &NodeListenerOptions) -> Value { + Value::Unknown } } @@ -97,6 +97,9 @@ impl<'a> ListenerMap for Statement<'a> { finalizer.body.iter().for_each(|stmt| stmt.report_effects(options)); }); } + Self::BlockStatement(stmt) => { + stmt.body.iter().for_each(|stmt| stmt.report_effects(options)); + } _ => {} } } @@ -224,6 +227,9 @@ impl<'a> ListenerMap for Expression<'a> { Self::AwaitExpression(expr) => { expr.argument.report_effects(options); } + Self::BinaryExpression(expr) => { + expr.get_value_and_report_effects(options); + } Self::ArrowFunctionExpression(_) | Self::FunctionExpression(_) | Self::Identifier(_) @@ -281,6 +287,18 @@ impl<'a> ListenerMap for Expression<'a> { } } } + fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value { + match self { + Self::BooleanLiteral(_) + | Self::StringLiteral(_) + | Self::NumericLiteral(_) + | Self::TemplateLiteral(_) => Value::new(self), + _ => { + self.report_effects(options); + Value::Unknown + } + } + } } // which kind of Expression defines `report_effects_when_called` method. @@ -297,6 +315,14 @@ fn defined_custom_report_effects_when_called(expr: &Expression) -> bool { ) } +impl<'a> ListenerMap for BinaryExpression<'a> { + fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value { + let left = self.left.get_value_and_report_effects(options); + let right = self.right.get_value_and_report_effects(options); + calculate_binary_operation(self.operator, left, right) + } +} + impl ListenerMap for ThisExpression { fn report_effects_when_mutated(&self, options: &NodeListenerOptions) { if !options.has_valid_this.get() { @@ -331,7 +357,7 @@ impl<'a> ListenerMap for ParenthesizedExpression<'a> { fn report_effects_when_mutated(&self, options: &NodeListenerOptions) { self.expression.report_effects_when_mutated(options); } - fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Option { + fn get_value_and_report_effects(&self, options: &NodeListenerOptions) -> Value { self.expression.get_value_and_report_effects(options) } } diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs index bd9cdfc4d5991..dd61b2e7b953e 100644 --- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs +++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs @@ -99,19 +99,19 @@ fn test() { "const x = []", "const x = [ext,ext]", "const x = [1,,2,]", - // // ArrayPattern + // ArrayPattern "const [x] = []", "const [,x,] = []", - // // ArrowFunctionExpression + // ArrowFunctionExpression "const x = a=>{a(); ext()}", - // // ArrowFunctionExpression when called + // ArrowFunctionExpression when called "(()=>{})()", "(a=>{})()", "((...a)=>{})()", "(({a})=>{})()", - // // ArrowFunctionExpression when mutated + // ArrowFunctionExpression when mutated "const x = ()=>{}; x.y = 1", - // // AssignmentExpression + // AssignmentExpression "var x;x = {}", "var x;x += 1", "const x = {}; x.y = 1", @@ -124,19 +124,19 @@ fn test() { "const {x: y = ext} = {}", "const {[ext]: x = ext} = {}", "const x = ()=>{}, {y = x()} = {}", - // // BinaryExpression - // "const x = 1 + 2", - // "if (1-1) ext()", - // // BlockStatement - // "{}", - // "const x = ()=>{};{const x = ext}x()", - // "const x = ext;{const x = ()=>{}; x()}", - // // BreakStatement - // "while(true){break}", - // // CallExpression + // BinaryExpression + "const x = 1 + 2", + "if (1-1) ext()", + // BlockStatement + "{}", + "const x = ()=>{};{const x = ext}x()", + "const x = ext;{const x = ()=>{}; x()}", + // BreakStatement + "while(true){break}", + // CallExpression "(a=>{const y = a})(ext, ext)", "const x = ()=>{}, y = ()=>{}; x(y())", - // // CatchClause + // CatchClause "try {} catch (error) {}", "const x = ()=>{}; try {} catch (error) {const x = ext}; x()", "const x = ext; try {} catch (error) {const x = ()=>{}; x()}", @@ -393,12 +393,12 @@ fn test() { // AwaitExpression "const x = async ()=>{await ext()}; x()", // // BinaryExpression - // "const x = 1 + ext()", - // "const x = ext() + 1", - // // BlockStatement - // "{ext()}", + "const x = 1 + ext()", + "const x = ext() + 1", + // BlockStatement + "{ext()}", // "var x=()=>{};{var x=ext}x()", - // "var x=ext;{x(); var x=()=>{}}", + "var x=ext;{x(); var x=()=>{}}", // CallExpression "(()=>{})(ext(), 1)", "(()=>{})(1, ext())", diff --git a/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs b/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs new file mode 100644 index 0000000000000..c12c86c4220a8 --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs @@ -0,0 +1,259 @@ +use oxc_ast::{ + ast::{ExportDefaultDeclarationKind, TSType}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error("typescript-eslint(consistent-type-definitions):")] +#[diagnostic(severity(warning), help("Use an `{0}` instead of a `{1}`"))] +struct ConsistentTypeDefinitionsDiagnostic( + &'static str, + &'static str, + #[label("Use an `{0}` instead of a `{1}`")] pub Span, +); + +#[derive(Debug, Default, Clone)] +pub struct ConsistentTypeDefinitions { + config: ConsistentTypeDefinitionsConfig, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +enum ConsistentTypeDefinitionsConfig { + #[default] + Interface, + Type, +} + +declare_oxc_lint!( + /// ### What it does + /// + /// Enforce type definitions to consistently use either interface or type. + /// + /// ### Why is this bad? + /// + /// TypeScript provides two common ways to define an object type: interface and type. + /// The two are generally very similar, and can often be used interchangeably. + /// Using the same type declaration style consistently helps with code readability. + /// + /// ### Example + /// ```ts + /// // incorrect, when set to "interface" + /// type T = { x: number }; + /// + /// // incorrect when set to "type" + /// interface T { + /// x: number; + /// } + /// ``` + ConsistentTypeDefinitions, + style +); + +impl Rule for ConsistentTypeDefinitions { + fn from_configuration(value: serde_json::Value) -> Self { + let config = value.get(0).and_then(serde_json::Value::as_str).map_or_else( + ConsistentTypeDefinitionsConfig::default, + |value| match value { + "type" => ConsistentTypeDefinitionsConfig::Type, + _ => ConsistentTypeDefinitionsConfig::Interface, + }, + ); + Self { config } + } + + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::TSTypeAliasDeclaration(decl) => match &decl.type_annotation { + TSType::TSTypeLiteral(_) + if self.config == ConsistentTypeDefinitionsConfig::Interface => + { + let start = if decl.modifiers.is_contains_declare() { + decl.span.start + 8 + } else { + decl.span.start + }; + + ctx.diagnostic(ConsistentTypeDefinitionsDiagnostic( + "interface", + "type", + Span::new(start, start + 4), + )); + } + _ => {} + }, + + AstKind::ExportDefaultDeclaration(exp) => match &exp.declaration { + ExportDefaultDeclarationKind::TSInterfaceDeclaration(decl) + if self.config == ConsistentTypeDefinitionsConfig::Type => + { + ctx.diagnostic(ConsistentTypeDefinitionsDiagnostic( + "type", + "interface", + Span::new(decl.span.start, decl.span.start + 9), + )); + } + _ => {} + }, + + AstKind::TSInterfaceDeclaration(decl) + if self.config == ConsistentTypeDefinitionsConfig::Type => + { + let start = if decl.modifiers.is_contains_declare() { + decl.span.start + 8 + } else { + decl.span.start + }; + + ctx.diagnostic(ConsistentTypeDefinitionsDiagnostic( + "type", + "interface", + Span::new(start, start + 9), + )); + } + _ => {} + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("var foo = {};", Some(serde_json::json!(["interface"]))), + ("interface A {}", Some(serde_json::json!(["interface"]))), + ( + " + interface A extends B { + x: number; + } + ", + Some(serde_json::json!(["interface"])), + ), + ("type U = string;", Some(serde_json::json!(["interface"]))), + ("type V = { x: number } | { y: string };", Some(serde_json::json!(["interface"]))), + ( + " + type Record = { + [K in T]: U; + }; + ", + Some(serde_json::json!(["interface"])), + ), + ("type T = { x: number };", Some(serde_json::json!(["type"]))), + ("type A = { x: number } & B & C;", Some(serde_json::json!(["type"]))), + ("type A = { x: number } & B & C;", Some(serde_json::json!(["type"]))), + ( + " + export type W = { + x: T; + }; + ", + Some(serde_json::json!(["type"])), + ), + ]; + + let fail = vec![ + ("type T = { x: number; };", Some(serde_json::json!(["interface"]))), + ("type T={ x: number; };", Some(serde_json::json!(["interface"]))), + ("type T= { x: number; };", Some(serde_json::json!(["interface"]))), + ( + " + export type W = { + x: T; + }; + ", + Some(serde_json::json!(["interface"])), + ), + ("interface T { x: number; }", Some(serde_json::json!(["type"]))), + ("interface T{ x: number; }", Some(serde_json::json!(["type"]))), + ("interface T { x: number; }", Some(serde_json::json!(["type"]))), + ("interface A extends B, C { x: number; };", Some(serde_json::json!(["type"]))), + ("interface A extends B, C { x: number; };", Some(serde_json::json!(["type"]))), + ( + " + export interface W { + x: T; + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + namespace JSX { + interface Array { + foo(x: (x: number) => T): T[]; + } + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + global { + interface Array { + foo(x: (x: number) => T): T[]; + } + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + declare global { + interface Array { + foo(x: (x: number) => T): T[]; + } + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + declare global { + namespace Foo { + interface Bar {} + } + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + export default interface Test { + bar(): string; + foo(): number; + } + ", + Some(serde_json::json!(["type"])), + ), + ( + " + export declare type Test = { + foo: string; + bar: string; + }; + ", + Some(serde_json::json!(["interface"])), + ), + ( + " + export declare interface Test { + foo: string; + bar: string; + } + ", + Some(serde_json::json!(["type"])), + ), + ]; + + Tester::new(ConsistentTypeDefinitions::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/check_access.snap b/crates/oxc_linter/src/snapshots/check_access.snap index dcb89a397fffa..6e7534655e9e6 100644 --- a/crates/oxc_linter/src/snapshots/check_access.snap +++ b/crates/oxc_linter/src/snapshots/check_access.snap @@ -2,47 +2,47 @@ source: crates/oxc_linter/src/tester.rs expression: check_access --- - ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified. - ╭─[check_access.tsx:3:17] + ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing. + ╭─[check_access.tsx:3:25] 2 │ /** 3 │ * @access foo - · ─────── + · ─── 4 │ */ ╰──── help: Valid access levels are `package`, `private`, `protected`, and `public`. - ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified. - ╭─[check_access.tsx:3:17] + ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing. + ╭─[check_access.tsx:3:25] 2 │ /** 3 │ * @access foo - · ─────── + · ─── 4 │ */ ╰──── help: Valid access levels are `package`, `private`, `protected`, and `public`. - ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified. - ╭─[check_access.tsx:3:25] + ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing. + ╭─[check_access.tsx:3:38] 2 │ /** 3 │ * @accessLevel foo - · ──────────── + · ─── 4 │ */ ╰──── help: Valid access levels are `package`, `private`, `protected`, and `public`. - ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified. - ╭─[check_access.tsx:3:17] + ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing. + ╭─[check_access.tsx:3:24] 2 │ /** 3 │ * @access - · ─────── + · ▲ 4 │ */ ╰──── help: Valid access levels are `package`, `private`, `protected`, and `public`. - ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified. - ╭─[check_access.tsx:4:15] + ⚠ eslint-plugin-jsdoc(check-access): Invalid access level is specified or missing. + ╭─[check_access.tsx:4:22] 3 │ /** 4 │ * @access - · ─────── + · ▲ 5 │ */ ╰──── help: Valid access levels are `package`, `private`, `protected`, and `public`. diff --git a/crates/oxc_linter/src/snapshots/consistent_type_definitions.snap b/crates/oxc_linter/src/snapshots/consistent_type_definitions.snap new file mode 100644 index 0000000000000..58b6f832d62c4 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/consistent_type_definitions.snap @@ -0,0 +1,157 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: consistent_type_definitions +--- + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ type T = { x: number; }; + · ──┬─ + · ╰── Use an `interface` instead of a `type` + ╰──── + help: Use an `interface` instead of a `type` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ type T={ x: number; }; + · ──┬─ + · ╰── Use an `interface` instead of a `type` + ╰──── + help: Use an `interface` instead of a `type` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ type T= { x: number; }; + · ──┬─ + · ╰── Use an `interface` instead of a `type` + ╰──── + help: Use an `interface` instead of a `type` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:2:11] + 1 │ + 2 │ export type W = { + · ──┬─ + · ╰── Use an `interface` instead of a `type` + 3 │ x: T; + ╰──── + help: Use an `interface` instead of a `type` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ interface T { x: number; } + · ────┬──── + · ╰── Use an `type` instead of a `interface` + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ interface T{ x: number; } + · ────┬──── + · ╰── Use an `type` instead of a `interface` + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ interface T { x: number; } + · ────┬──── + · ╰── Use an `type` instead of a `interface` + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ interface A extends B, C { x: number; }; + · ────┬──── + · ╰── Use an `type` instead of a `interface` + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:1:1] + 1 │ interface A extends B, C { x: number; }; + · ────┬──── + · ╰── Use an `type` instead of a `interface` + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:2:11] + 1 │ + 2 │ export interface W { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 3 │ x: T; + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:3:6] + 2 │ namespace JSX { + 3 │ interface Array { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 4 │ foo(x: (x: number) => T): T[]; + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:3:6] + 2 │ global { + 3 │ interface Array { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 4 │ foo(x: (x: number) => T): T[]; + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:3:6] + 2 │ declare global { + 3 │ interface Array { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 4 │ foo(x: (x: number) => T): T[]; + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:4:8] + 3 │ namespace Foo { + 4 │ interface Bar {} + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 5 │ } + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:2:19] + 1 │ + 2 │ export default interface Test { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 3 │ bar(): string; + ╰──── + help: Use an `type` instead of a `interface` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:2:19] + 1 │ + 2 │ export declare type Test = { + · ──┬─ + · ╰── Use an `interface` instead of a `type` + 3 │ foo: string; + ╰──── + help: Use an `interface` instead of a `type` + + ⚠ typescript-eslint(consistent-type-definitions): + ╭─[consistent_type_definitions.tsx:2:19] + 1 │ + 2 │ export declare interface Test { + · ────┬──── + · ╰── Use an `type` instead of a `interface` + 3 │ foo: string; + ╰──── + help: Use an `type` instead of a `interface` diff --git a/crates/oxc_linter/src/snapshots/empty_tags.snap b/crates/oxc_linter/src/snapshots/empty_tags.snap index 56eac04075132..335f993276397 100644 --- a/crates/oxc_linter/src/snapshots/empty_tags.snap +++ b/crates/oxc_linter/src/snapshots/empty_tags.snap @@ -3,73 +3,73 @@ source: crates/oxc_linter/src/tester.rs expression: empty_tags --- ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:17] + ╭─[empty_tags.tsx:3:27] 2 │ /** 3 │ * @abstract extra text - · ───────── + · ────────── 4 │ */ ╰──── - help: `@abstract` tag should be empty. + help: `@abstract` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:4:17] + ╭─[empty_tags.tsx:4:27] 3 │ /** 4 │ * @abstract extra text - · ───────── + · ────────── 5 │ */ ╰──── - help: `@abstract` tag should be empty. + help: `@abstract` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:17] + ╭─[empty_tags.tsx:3:27] 2 │ /** 3 │ * @abstract extra text - · ───────── + · ────────── 4 │ * @inheritdoc ╰──── - help: `@abstract` tag should be empty. + help: `@abstract` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:5:17] + ╭─[empty_tags.tsx:5:24] 4 │ * @inheritdoc 5 │ * @async out of place - · ────── + · ──────────── 6 │ */ ╰──── - help: `@async` tag should be empty. + help: `@async` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:17] + ╭─[empty_tags.tsx:3:24] 2 │ /** 3 │ * @event anEvent - · ────── + · ─────── 4 │ */ ╰──── - help: `@event` tag should be empty. + help: `@event` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:13] + ╭─[empty_tags.tsx:3:22] 2 │ /** - 3 │ * @private {someType} - · ──────── - 4 │ */ + 3 │ * @private foo + · ─── + 4 │ * bar ╰──── - help: `@private` tag should be empty. + help: `@private` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:13] - 2 │ /** - 3 │ * @internal {someType} - · ───────── - 4 │ */ + ╭─[empty_tags.tsx:4:20] + 3 │ * @internal + 4 │ * foo + · ───── + 5 │ */ ╰──── - help: `@internal` tag should be empty. + help: `@internal` tag should not have body. ⚠ eslint-plugin-jsdoc(empty-tags): Expects the void tags to be empty of any content. - ╭─[empty_tags.tsx:3:13] + ╭─[empty_tags.tsx:3:22] 2 │ /** 3 │ * @private {someType} - · ──────── + · ────────── 4 │ */ ╰──── - help: `@private` tag should be empty. + help: `@private` tag should not have body. diff --git a/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap b/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap index cce5285900d0e..13a01aedd7d57 100644 --- a/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap +++ b/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap @@ -128,6 +128,30 @@ expression: no_side_effects_in_initialization · ─── ╰──── + ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext` + ╭─[no_side_effects_in_initialization.tsx:1:15] + 1 │ const x = 1 + ext() + · ─── + ╰──── + + ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext` + ╭─[no_side_effects_in_initialization.tsx:1:11] + 1 │ const x = ext() + 1 + · ─── + ╰──── + + ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext` + ╭─[no_side_effects_in_initialization.tsx:1:2] + 1 │ {ext()} + · ─── + ╰──── + + ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext` + ╭─[no_side_effects_in_initialization.tsx:1:7] + 1 │ var x=ext;{x(); var x=()=>{}} + · ─── + ╰──── + ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext` ╭─[no_side_effects_in_initialization.tsx:1:10] 1 │ (()=>{})(ext(), 1) diff --git a/crates/oxc_linter/src/snapshots/prefer_lowercase_title.snap b/crates/oxc_linter/src/snapshots/prefer_lowercase_title.snap new file mode 100644 index 0000000000000..844d2e917659d --- /dev/null +++ b/crates/oxc_linter/src/snapshots/prefer_lowercase_title.snap @@ -0,0 +1,244 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 151 +expression: prefer_lowercase_title +--- + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:4] + 1 │ it('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:5] + 1 │ xit('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:4] + 1 │ it("Foo", function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:4] + 1 │ it(`Foo`, function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:6] + 1 │ test('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:7] + 1 │ xtest('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:6] + 1 │ test("Foo", function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:6] + 1 │ test(`Foo`, function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe("Foo", function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe(`Foo`, function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:3:25] + 2 │ import { describe as context } from '@jest/globals'; + 3 │ context(`Foo`, () => {}); + · ───── + 4 │ + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe(`Some longer description`, function () {}) + · ───────────────────────── + ╰──── + help: `"Some longer description"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:11] + 1 │ fdescribe(`Some longer description`, function () {}) + · ───────────────────────── + ╰──── + help: `"Some longer description"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:29] + 1 │ it.each(['green', 'black'])('Should return %', () => {}) + · ───────────────── + ╰──── + help: `"Should return %"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:35] + 1 │ describe.each(['green', 'black'])('Should return %', () => {}) + · ───────────────── + ╰──── + help: `"Should return %"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:6] + 1 │ test('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:5] + 1 │ xit('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:4] + 1 │ it('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:5] + 1 │ xit('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:10] + 1 │ describe('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:6] + 1 │ test('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:7] + 1 │ xtest('Foo', function () {}) + · ───── + ╰──── + help: `"Foo"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:1:4] + 1 │ it("Works!", () => {}); + · ──────── + ╰──── + help: `"Works!"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:4:28] + 3 │ describe('MyMethod', () => { + 4 │ it('Does things', () => { + · ───────────── + 5 │ // + ╰──── + help: `"Does things"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:3:30] + 2 │ describe('MyClass', () => { + 3 │ describe('MyMethod', () => { + · ────────── + 4 │ it('Does things', () => { + ╰──── + help: `"MyMethod"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:4:29] + 3 │ describe('MyClass', () => { + 4 │ context('MyMethod', () => { + · ────────── + 5 │ it('Does things', () => { + ╰──── + help: `"MyMethod"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:5:28] + 4 │ context('MyMethod', () => { + 5 │ it('Does things', () => { + · ───────────── + 6 │ // + ╰──── + help: `"Does things"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:4:28] + 3 │ describe('MyMethod', () => { + 4 │ it('Does things', () => { + · ───────────── + 5 │ // + ╰──── + help: `"Does things"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:3:30] + 2 │ describe('MyClass', () => { + 3 │ describe('MyMethod', () => { + · ────────── + 4 │ it('Does things', () => { + ╰──── + help: `"MyMethod"`s should begin with lowercase + + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:2:26] + 1 │ + 2 │ describe('MyClass', () => { + · ───────── + 3 │ describe('MyMethod', () => { + ╰──── + help: `"MyClass"`s should begin with lowercase diff --git a/crates/oxc_linter/src/utils/tree_shaking.rs b/crates/oxc_linter/src/utils/tree_shaking.rs index b1d22dbccd882..b86e2b0ab9b8e 100644 --- a/crates/oxc_linter/src/utils/tree_shaking.rs +++ b/crates/oxc_linter/src/utils/tree_shaking.rs @@ -1,6 +1,7 @@ use oxc_ast::{ast::Expression, AstKind}; use oxc_semantic::AstNodeId; use oxc_span::Span; +use oxc_syntax::operator::BinaryOperator; use crate::LintContext; @@ -8,6 +9,40 @@ use crate::LintContext; pub enum Value { Boolean(bool), Number(f64), + String(StringValue), + Unknown, +} + +// We only care if it is falsy value (empty string). +pub enum StringValue { + Empty, + NonEmpty, +} + +impl Value { + pub fn new(expr: &Expression) -> Value { + match expr { + Expression::BooleanLiteral(bool_lit) => Value::Boolean(bool_lit.value), + Expression::NumericLiteral(num_lit) => Value::Number(num_lit.value), + Expression::StringLiteral(str_lit) => { + if str_lit.value.is_empty() { + Value::String(StringValue::Empty) + } else { + Value::String(StringValue::NonEmpty) + } + } + Expression::TemplateLiteral(template_lit) => { + if !template_lit.is_no_substitution_template() { + Value::Unknown + } else if template_lit.quasi().is_some_and(|s| s == "") { + Value::String(StringValue::Empty) + } else { + Value::String(StringValue::NonEmpty) + } + } + _ => Value::Unknown, + } + } } pub fn get_write_expr<'a, 'b>( @@ -43,3 +78,21 @@ pub fn has_pure_notation(span: Span, ctx: &LintContext) -> bool { raw.contains("@__PURE__") || raw.contains("#__PURE__") } + +/// Port from https://github.com/lukastaegert/eslint-plugin-tree-shaking/blob/463fa1f0bef7caa2b231a38b9c3557051f506c92/src/rules/no-side-effects-in-initialization.ts#L136-L161 +pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value) -> Value { + match op { + BinaryOperator::Addition => match (left, right) { + (Value::Number(a), Value::Number(b)) => Value::Number(a + b), + (Value::String(str1), Value::String(str2)) => { + if matches!(str1, StringValue::Empty) && matches!(str2, StringValue::Empty) { + Value::String(StringValue::Empty) + } else { + Value::String(StringValue::NonEmpty) + } + } + _ => Value::Unknown, + }, + _ => Value::Unknown, + } +} diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index c146c0c1ab096..3e1f10ac0e6b1 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_minifier" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_parser/CHANGELOG.md b/crates/oxc_parser/CHANGELOG.md index c44450c2d09c2..ccfa207025ad6 100644 --- a/crates/oxc_parser/CHANGELOG.md +++ b/crates/oxc_parser/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Refactor + +- Clean up the ts type visit methods + ## [0.11.0] - 2024-03-30 ### Bug Fixes diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 9f1d7bfd442b9..b1b4385374ec6 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_parser" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_parser/examples/visitor.rs b/crates/oxc_parser/examples/visitor.rs index ee9b8cb2cc426..ad4b5e9566a85 100644 --- a/crates/oxc_parser/examples/visitor.rs +++ b/crates/oxc_parser/examples/visitor.rs @@ -2,8 +2,8 @@ use std::{env, path::Path}; use oxc_allocator::Allocator; use oxc_ast::{ - ast::{Class, Function}, - visit::walk::{walk_class, walk_function}, + ast::{Class, Function, TSImportType}, + visit::walk, Visit, }; use oxc_parser::Parser; @@ -30,7 +30,7 @@ fn main() -> std::io::Result<()> { let program = ret.program; - let mut ast_pass = ASTPass::default(); + let mut ast_pass = CountASTNodes::default(); ast_pass.visit_program(&program); println!("{ast_pass:?}"); @@ -38,19 +38,25 @@ fn main() -> std::io::Result<()> { } #[derive(Debug, Default)] -struct ASTPass { - number_of_functions: usize, - number_of_classes: usize, +struct CountASTNodes { + functions: usize, + classes: usize, + ts_import_types: usize, } -impl<'a> Visit<'a> for ASTPass { +impl<'a> Visit<'a> for CountASTNodes { fn visit_function(&mut self, func: &Function<'a>, flags: Option) { - self.number_of_functions += 1; - walk_function(self, func, flags); + self.functions += 1; + walk::walk_function(self, func, flags); } fn visit_class(&mut self, class: &Class<'a>) { - self.number_of_classes += 1; - walk_class(self, class); + self.classes += 1; + walk::walk_class(self, class); + } + + fn visit_ts_import_type(&mut self, ty: &TSImportType<'a>) { + self.ts_import_types += 1; + walk::walk_ts_import_type(self, ty); } } diff --git a/crates/oxc_semantic/CHANGELOG.md b/crates/oxc_semantic/CHANGELOG.md index 045bcd0a99166..0018eb8f444f8 100644 --- a/crates/oxc_semantic/CHANGELOG.md +++ b/crates/oxc_semantic/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Refactor + +- Rework JSDoc struct for better Span handling (#2917) + +## [0.12.2] - 2024-04-08 + +### Features + +- Implement jsdoc/check-access (#2642) + +### Bug Fixes + +- Symbols inside functions and classes incorrectly flagged as exported (#2896) + ## [0.11.1] - 2024-04-03 ### Bug Fixes diff --git a/crates/oxc_semantic/Cargo.toml b/crates/oxc_semantic/Cargo.toml index 77cd2d697e849..f2d14291cacff 100644 --- a/crates/oxc_semantic/Cargo.toml +++ b/crates/oxc_semantic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_semantic" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_semantic/src/jsdoc/builder.rs b/crates/oxc_semantic/src/jsdoc/builder.rs index 070635ffd6683..d3ba8145af2f2 100644 --- a/crates/oxc_semantic/src/jsdoc/builder.rs +++ b/crates/oxc_semantic/src/jsdoc/builder.rs @@ -429,11 +429,11 @@ mod test { // Should be [farthest, ..., nearest] let mut iter = jsdocs.iter(); let c1 = iter.next().unwrap(); - assert_eq!(c1.comment(), "c1"); + assert_eq!(c1.comment().parsed(), "c1"); let c2 = iter.next().unwrap(); - assert_eq!(c2.comment(), "c2"); + assert_eq!(c2.comment().parsed(), "c2"); let c3 = iter.next().unwrap(); - assert_eq!(c3.comment(), "c3"); + assert_eq!(c3.comment().parsed(), "c3"); } #[test] diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs index 45658210af940..811719735ed0e 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs @@ -1,13 +1,16 @@ +use super::jsdoc_parts::JSDocCommentPart; use super::jsdoc_tag::JSDocTag; use super::parse::parse_jsdoc; use oxc_span::Span; use std::cell::OnceCell; +type ParsedJSDoc<'a> = (JSDocCommentPart<'a>, Vec>); + #[derive(Debug, Clone)] pub struct JSDoc<'a> { raw: &'a str, /// Cached+parsed JSDoc comment and tags - cached: OnceCell<(String, Vec<(Span, JSDocTag<'a>)>)>, + cached: OnceCell>, pub span: Span, } @@ -18,15 +21,15 @@ impl<'a> JSDoc<'a> { Self { raw: comment_content, cached: OnceCell::new(), span } } - pub fn comment(&self) -> &str { - &self.parse().0 + pub fn comment(&self) -> JSDocCommentPart<'a> { + self.parse().0 } - pub fn tags(&self) -> &Vec<(Span, JSDocTag<'a>)> { + pub fn tags(&self) -> &Vec> { &self.parse().1 } - fn parse(&self) -> &(String, Vec<(Span, JSDocTag<'a>)>) { + fn parse(&self) -> &ParsedJSDoc<'a> { self.cached.get_or_init(|| parse_jsdoc(self.raw, self.span.start)) } } @@ -38,12 +41,8 @@ mod test { use oxc_parser::Parser; use oxc_span::SourceType; - fn build_semantic<'a>( - allocator: &'a Allocator, - source_text: &'a str, - source_type: Option, - ) -> Semantic<'a> { - let source_type = source_type.unwrap_or_default(); + fn build_semantic<'a>(allocator: &'a Allocator, source_text: &'a str) -> Semantic<'a> { + let source_type = SourceType::default(); let ret = Parser::new(allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); let semantic = SemanticBuilder::new(source_text, source_type) @@ -54,71 +53,255 @@ mod test { } #[test] - fn get_jsdoc_span() { - let allocator = Allocator::default(); - let semantic = build_semantic( - &allocator, - r" - /** single line */ + fn jsdoc_span() { + for (source_text, span_text) in [ + ("/** single line */", " single line "), + ( + " /** * multi - * line - */ + * line1 + */", + "\n * multi\n * line1\n ", + ), + ( + " /** multi -line +line2 */ +", + "\nmulti\nline2\n ", + ), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let jsdoc = jsdocs.next().unwrap(); + assert_eq!(jsdoc.span.source_text(source_text), span_text); + } + } + + #[test] + fn jsdoc_comment() { + for (source_text, parsed, span_text, tag_len) in [ + ("/** single line @k1 c1 @k2 */", "single line", " single line ", 2), + ( + "/** + * multi + * line + * @k1 c1a + * c1b + * @k2 c2 + * @k3 c3a + * c3b + */", + "multi\nline", + "\n * multi\n * line\n * ", + 3, + ), + (" /** * list */ ", "* list", " * list ", 0), + ("/***/", "", "", 0), + ("/****/", "*", "*", 0), + ("/*****/", "**", "**", 0), + ( + " + /** + * * x + ** y + */ +", + "* x\n* y", + "\n * * x\n ** y\n ", + 0, + ), + ( + " + /** <- trim -> */ +", + "<- trim ->", + " <- trim -> ", + 0, + ), + ( + " + /** + * <- omit this, keep this -> * + */ +", + "<- omit this, keep this -> *", + "\n * <- omit this, keep this -> *\n ", + 0, + ), + ( + " + /** + this is + comment {@link link} ... + @x + */ +", + "this is\ncomment {@link link} ...", + "\n this is\n comment {@link link} ...\n ", + 1, + ), + ( + " +/** + * 日本語とか + * multibyte文字はどう⁉️ + */ +", + "日本語とか\nmultibyte文字はどう⁉️", + "\n * 日本語とか\n * multibyte文字はどう⁉️\n ", + 0, + ), + ( + " +/**\nhello {@see inline} source {@a 2}\n*/ +", + "hello {@see inline} source {@a 2}", + "\nhello {@see inline} source {@a 2}\n", + 0, + ), + ( + " + /** ハロー @comment だよ*/ +", + "ハロー", + " ハロー ", + 1, + ), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let jsdoc = jsdocs.next().unwrap(); + let (comment, tags) = (jsdoc.comment(), jsdoc.tags()); + assert_eq!(comment.parsed(), parsed); + assert_eq!(comment.span.source_text(source_text), span_text); + assert_eq!(tags.len(), tag_len); + } + } + + #[test] + fn parses_practical() { + let allocator = Allocator::default(); + let semantic = build_semantic( + &allocator, + "/** + * @typedef {Object} User - a User account + * @property {string} displayName - the name used to show the user + * @property {number} id - a unique id + */ ", - Some(SourceType::default()), ); + let jsdoc = semantic.jsdoc().iter_all().next().unwrap(); + + assert_eq!(jsdoc.comment().parsed(), ""); + let mut tags = jsdoc.tags().iter(); + assert_eq!(tags.len(), 3); - let mut jsdocs = semantic.jsdoc().iter_all(); + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "typedef"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("Object", "User", "- a User account".to_string()) + ); - let jsdoc = jsdocs.next().unwrap(); - assert_eq!(jsdoc.span.source_text(semantic.source_text), " single line "); - let jsdoc = jsdocs.next().unwrap(); + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "property"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); assert_eq!( - jsdoc.span.source_text(semantic.source_text), - "\n * multi\n * line\n " + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("string", "displayName", "- the name used to show the user".to_string()) + ); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "property"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("number", "id", "- a unique id".to_string()) ); - let jsdoc = jsdocs.next().unwrap(); - assert_eq!(jsdoc.span.source_text(semantic.source_text), "\nmulti\nline\n "); } #[test] - fn get_jsdoc_tag_span() { + fn parses_practical_with_multibyte() { let allocator = Allocator::default(); let semantic = build_semantic( &allocator, - r" - /** single line @k1 d1 */ - /** - * multi - * line - * @k2 d2 - * d2 - * @k3 d3 - * @k4 d4 - * d4 - */ - ", - Some(SourceType::default()), + "/** + * flat tree data on expanded state + * + * @export + * @template T + * @param {*} data : table data + * @param {string} childrenColumnName : 指定树形结构的列名 + * @param {Set} expandedKeys : 展开的行对应的keys + * @param {GetRowKey} getRowKey : 获取当前rowKey的方法 + * @returns flattened data + */", ); + let jsdoc = semantic.jsdoc().iter_all().next().unwrap(); - let mut jsdocs = semantic.jsdoc().iter_all(); - - let jsdoc = jsdocs.next().unwrap(); + assert_eq!(jsdoc.comment().parsed(), "flat tree data on expanded state"); let mut tags = jsdoc.tags().iter(); - let (span, _) = tags.next().unwrap(); - assert_eq!(span.source_text(semantic.source_text), "@k1"); + assert_eq!(tags.len(), 7); - let jsdoc = jsdocs.next().unwrap(); - let mut tags = jsdoc.tags().iter(); - let (span, _) = tags.next().unwrap(); - assert_eq!(span.source_text(semantic.source_text), "@k2"); - let (span, _) = tags.next().unwrap(); - assert_eq!(span.source_text(semantic.source_text), "@k3"); - let (span, _) = tags.next().unwrap(); - assert_eq!(span.source_text(semantic.source_text), "@k4"); + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "export"); + assert_eq!(tag.comment().parsed(), ""); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "template"); + assert_eq!(tag.comment().parsed(), "T"); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "param"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("*", "data", ": table data".to_string()) + ); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "param"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("string", "childrenColumnName", ": 指定树形结构的列名".to_string()) + ); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "param"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("Set", "expandedKeys", ": 展开的行对应的keys".to_string()) + ); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "param"); + let (type_part, name_part, comment_part) = tag.type_name_comment(); + let (type_part, name_part) = (type_part.unwrap(), name_part.unwrap()); + assert_eq!( + (type_part.parsed(), name_part.parsed(), comment_part.parsed()), + ("GetRowKey", "getRowKey", ": 获取当前rowKey的方法".to_string()) + ); + + let tag = tags.next().unwrap(); + assert_eq!(tag.kind.parsed(), "returns"); + let (type_part, comment_part) = tag.type_comment(); + assert_eq!((type_part, comment_part.parsed()), (None, "flattened data".to_string())); } } diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc_parts.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_parts.rs new file mode 100644 index 0000000000000..e69f273bd70c8 --- /dev/null +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_parts.rs @@ -0,0 +1,291 @@ +use oxc_span::Span; + +/// Used for `JSDoc.comment` and `JSDocTag.comment` +#[derive(Debug, Clone, Copy)] +pub struct JSDocCommentPart<'a> { + raw: &'a str, + pub span: Span, +} +impl<'a> JSDocCommentPart<'a> { + pub fn new(part_content: &'a str, span: Span) -> Self { + Self { raw: part_content, span } + } + + // For example, `Span` for the following comment part: + // ``` + // /** + // * @kind1 COMMENT + // * WILL BE ... + // * @kind2 C2 + // * @kind3 + // */ + // ``` + // is ` COMMENT\n * WILL BE ...\n * `. + // + // It includes whitespace and line breaks. + // And it also includes leading `*` prefixes in every line, even in a single line tag. + // (comment `Span` for `kind2` is ` C2\n * `) + // + // Since these are trimmed by `parsed()` output, this raw `Span` may not be suitable for linter diagnostics. + // + // And if the passed `Span` for miette diagnostics is multiline, + // it will just render arrow markers which is not intuitive. + // (It renders a nice undeline for single line span, but not for multiline) + // ``` + // ╭─▶ * @kind1 COMMENT + // │ * WILL BE ... + // ╰─▶ * @kind2 C2 + // ``` + // + // So instead, just indicate the first visible line of the comment part. + // ``` + // * @kind1 COMMENT + // ─────── + // * WILL BE ... + // * @kind2 C2 + // ``` + // It may not be perfect for multiline, but for single line, which is probably the majority, it is enough. + pub fn span_trimmed_first_line(&self) -> Span { + if self.raw.trim().is_empty() { + return Span::new(self.span.start, self.span.start); + } + + let base_len = self.raw.len(); + if self.raw.lines().count() == 1 { + let trimmed_start_offset = base_len - self.raw.trim_start().len(); + let trimmed_end_offset = base_len - self.raw.trim_end().len(); + + return Span::new( + self.span.start + u32::try_from(trimmed_start_offset).unwrap_or_default(), + self.span.end - u32::try_from(trimmed_end_offset).unwrap_or_default(), + ); + } + + let start_trimmed = self.raw.trim_start(); + let trimmed_start_offset = base_len - start_trimmed.len(); + let trimmed_end_offset = + trimmed_start_offset + start_trimmed.find(|c| c == '\n').unwrap_or(0); + Span::new( + self.span.start + u32::try_from(trimmed_start_offset).unwrap_or_default(), + self.span.start + u32::try_from(trimmed_end_offset).unwrap_or_default(), + ) + } + + /// Returns the content of the comment part without leading `*` in each line. + pub fn parsed(&self) -> String { + // If single line, there is no leading `*` + if self.raw.lines().count() == 1 { + return self.raw.trim().to_string(); + } + + self.raw + .lines() + // Trim leading the first `*` in each line + .map(|line| line.trim().strip_prefix('*').unwrap_or(line).trim()) + .filter(|line| !line.is_empty()) + .collect::>() + .join("\n") + } +} + +#[derive(Debug, Clone, Copy)] +pub struct JSDocTagKindPart<'a> { + raw: &'a str, + pub span: Span, +} +impl<'a> JSDocTagKindPart<'a> { + pub fn new(part_content: &'a str, span: Span) -> Self { + debug_assert!(part_content.starts_with('@')); + debug_assert!(part_content.trim() == part_content); + + Self { raw: part_content, span } + } + + /// Returns `kind`, it can be any string like `param`, `type`, `whatever`, ...etc. + pub fn parsed(&self) -> &'a str { + // +1 for `@` + &self.raw[1..] + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct JSDocTagTypePart<'a> { + raw: &'a str, + pub span: Span, +} +impl<'a> JSDocTagTypePart<'a> { + pub fn new(part_content: &'a str, span: Span) -> Self { + debug_assert!(part_content.starts_with('{')); + debug_assert!(part_content.ends_with('}')); + + Self { raw: part_content, span } + } + + /// Returns the type content without `{` and `}`. + pub fn parsed(&self) -> &'a str { + // +1 for `{`, -1 for `}` + &self.raw[1..self.raw.len() - 1] + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct JSDocTagTypeNamePart<'a> { + raw: &'a str, + pub span: Span, +} +impl<'a> JSDocTagTypeNamePart<'a> { + pub fn new(part_content: &'a str, span: Span) -> Self { + debug_assert!(part_content.trim() == part_content); + + Self { raw: part_content, span } + } + + /// Returns the type name itself. + pub fn parsed(&self) -> &'a str { + self.raw + } +} + +#[cfg(test)] +mod test { + use super::{JSDocCommentPart, JSDocTagKindPart, JSDocTagTypeNamePart, JSDocTagTypePart}; + use oxc_span::{Span, SPAN}; + + #[test] + fn comment_part_parsed() { + for (actual, expect) in [ + ("", ""), + ("hello ", "hello"), + (" * single line", "* single line"), + (" * ", "*"), + (" * * ", "* *"), + ("***", "***"), + ( + " + trim + ", + "trim", + ), + ( + " + + ", "", + ), + ( + " + * + * + ", + "", + ), + ( + " + * asterisk + ", + "asterisk", + ), + ( + " + * * li + * * li + ", + "* li\n* li", + ), + ( + " + * list + * list + ", + "list\nlist", + ), + ( + " + * * 1 + ** 2 + ", + "* 1\n* 2", + ), + ( + " + 1 + + 2 + + 3 + ", + "1\n2\n3", + ), + ] { + // `Span` is not used in this test + let comment_part = JSDocCommentPart::new(actual, SPAN); + assert_eq!(comment_part.parsed(), expect); + } + } + + #[test] + fn comment_part_span_trimmed() { + for (actual, expect) in [ + ("", ""), + ("\n", ""), + ("\n\n\n", ""), + ("...", "..."), + ("c1\n", "c1"), + ("\nc2\n", "c2"), + (" c 3\n", "c 3"), + ("\nc4\n * ...\n ", "c4"), + ( + " + extra text +* +", + "extra text", + ), + ( + " + * foo + * bar +", + "* foo", + ), + ] { + let comment_part = + JSDocCommentPart::new(actual, Span::new(0, u32::try_from(actual.len()).unwrap())); + assert_eq!(comment_part.span_trimmed_first_line().source_text(actual), expect); + } + } + + #[test] + fn kind_part_parsed() { + for (actual, expect) in [("@foo", "foo"), ("@", ""), ("@かいんど", "かいんど")] { + // `Span` is not used in this test + let kind_part = JSDocTagKindPart::new(actual, SPAN); + assert_eq!(kind_part.parsed(), expect); + } + } + + #[test] + fn type_part_parsed() { + for (actual, expect) in [ + ("{}", ""), + ("{-}", "-"), + ("{string}", "string"), + ("{ string}", " string"), + ("{ bool }", " bool "), + ("{{x:1}}", "{x:1}"), + ("{[1,2,3]}", "[1,2,3]"), + ] { + // `Span` is not used in this test + let type_part = JSDocTagTypePart::new(actual, SPAN); + assert_eq!(type_part.parsed(), expect); + } + } + + #[test] + fn type_name_part_parsed() { + for (actual, expect) in [("foo", "foo"), ("Bar", "Bar"), ("変数", "変数")] { + // `Span` is not used in this test + let type_name_part = JSDocTagTypeNamePart::new(actual, SPAN); + assert_eq!(type_name_part.parsed(), expect); + } + } +} diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs index babae889f97d4..94849cf4cb457 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs @@ -1,4 +1,8 @@ -use super::utils; +use super::jsdoc_parts::{ + JSDocCommentPart, JSDocTagKindPart, JSDocTagTypeNamePart, JSDocTagTypePart, +}; +use crate::jsdoc::parser::utils; +use oxc_span::Span; // Initially, I attempted to parse into specific structures such as: // - `@param {type} name comment`: `JSDocParameterTag { type, name, comment }` @@ -24,34 +28,29 @@ use super::utils; // // As a result, I ended up providing helper methods that are fit for purpose. -/// General struct for JSDoc tag. -/// -/// `kind` can be any string like `param`, `type`, `whatever`, ...etc. -/// `raw_body` is kept as is, you can use helper methods according to your needs. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct JSDocTag<'a> { - raw_body: &'a str, - pub kind: &'a str, + pub span: Span, + pub kind: JSDocTagKindPart<'a>, + body_raw: &'a str, + body_span: Span, } impl<'a> JSDocTag<'a> { - /// kind: Does not contain the `@` prefix - /// raw_body: The body part of the tag, after the `@kind{HERE_MAY_BE_MULTILINE...}` - pub fn new(kind: &'a str, raw_body: &'a str) -> JSDocTag<'a> { - debug_assert!(!kind.starts_with('@')); - Self { raw_body, kind } + pub fn new(kind: JSDocTagKindPart<'a>, body_content: &'a str, body_span: Span) -> JSDocTag<'a> { + Self { span: kind.span.merge(&body_span), kind, body_raw: body_content, body_span } } /// Use for various simple tags like `@access`, `@deprecated`, ...etc. - /// comment can be multiline. + /// Comment can be multiline. /// /// Variants: /// ``` /// @kind comment /// @kind /// ``` - pub fn comment(&self) -> String { - utils::trim_comment(self.raw_body) + pub fn comment(&self) -> JSDocCommentPart<'a> { + JSDocCommentPart::new(self.body_raw, self.body_span) } /// Use for `@type`, `@satisfies`, ...etc. @@ -61,12 +60,20 @@ impl<'a> JSDocTag<'a> { /// @kind {type} /// @kind /// ``` - pub fn r#type(&self) -> Option<&str> { - utils::find_type_range(self.raw_body).map(|(start, end)| &self.raw_body[start..end]) + pub fn r#type(&self) -> Option> { + utils::find_type_range(self.body_raw).map(|(t_start, t_end)| { + JSDocTagTypePart::new( + &self.body_raw[t_start..t_end], + Span::new( + self.body_span.start + u32::try_from(t_start).unwrap_or_default(), + self.body_span.start + u32::try_from(t_end).unwrap_or_default(), + ), + ) + }) } /// Use for `@yields`, `@returns`, ...etc. - /// comment can be multiline. + /// Comment can be multiline. /// /// Variants: /// ``` @@ -75,21 +82,36 @@ impl<'a> JSDocTag<'a> { /// @kind comment /// @kind /// ``` - pub fn type_comment(&self) -> (Option<&str>, String) { - let (type_part, comment_part) = match utils::find_type_range(self.raw_body) { + pub fn type_comment(&self) -> (Option>, JSDocCommentPart<'a>) { + let (type_part, comment_part) = match utils::find_type_range(self.body_raw) { Some((t_start, t_end)) => { - // +1 for `}`, +1 for whitespace - let c_start = self.raw_body.len().min(t_end + 2); - (Some(&self.raw_body[t_start..t_end]), &self.raw_body[c_start..]) + // Include whitespace for comment trimming + let c_start = t_end; + ( + Some(JSDocTagTypePart::new( + &self.body_raw[t_start..t_end], + Span::new( + self.body_span.start + u32::try_from(t_start).unwrap_or_default(), + self.body_span.start + u32::try_from(t_end).unwrap_or_default(), + ), + )), + JSDocCommentPart::new( + &self.body_raw[c_start..], + Span::new( + self.body_span.start + u32::try_from(c_start).unwrap_or_default(), + self.body_span.end, + ), + ), + ) } - None => (None, self.raw_body), + None => (None, JSDocCommentPart::new(self.body_raw, self.body_span)), }; - (type_part, utils::trim_comment(comment_part)) + (type_part, comment_part) } /// Use for `@param`, `@property`, `@typedef`, ...etc. - /// comment can be multiline. + /// Comment can be multiline. /// /// Variants: /// ``` @@ -100,112 +122,334 @@ impl<'a> JSDocTag<'a> { /// @kind name /// @kind /// ``` - pub fn type_name_comment(&self) -> (Option<&str>, Option<&str>, String) { - let (type_part, name_comment_part) = match utils::find_type_range(self.raw_body) { - Some((t_start, t_end)) => { - // +1 for `}`, +1 for whitespace - let c_start = self.raw_body.len().min(t_end + 2); - (Some(&self.raw_body[t_start..t_end]), &self.raw_body[c_start..]) - } - None => (None, self.raw_body), - }; + pub fn type_name_comment( + &self, + ) -> (Option>, Option>, JSDocCommentPart<'a>) + { + let (type_part, name_comment_content, span_start) = + match utils::find_type_range(self.body_raw) { + Some((t_start, t_end)) => { + // +1 for whitespace + let c_start = self.body_raw.len().min(t_end + 1); + ( + Some(JSDocTagTypePart::new( + &self.body_raw[t_start..t_end], + Span::new( + self.body_span.start + u32::try_from(t_start).unwrap_or_default(), + self.body_span.start + u32::try_from(t_end).unwrap_or_default(), + ), + )), + &self.body_raw[c_start..], + self.body_span.start + u32::try_from(c_start).unwrap_or_default(), + ) + } + None => (None, self.body_raw, self.body_span.start), + }; - let (name_part, comment_part) = match utils::find_token_range(name_comment_part) { + let (name_part, comment_part) = match utils::find_token_range(name_comment_content) { Some((n_start, n_end)) => { - // +1 for whitespace - let c_start = name_comment_part.len().min(n_end + 1); - (Some(&name_comment_part[n_start..n_end]), &name_comment_part[c_start..]) + // Include whitespace for comment trimming + let c_start = n_end; + ( + Some(JSDocTagTypeNamePart::new( + &name_comment_content[n_start..n_end], + Span::new( + span_start + u32::try_from(n_start).unwrap_or_default(), + span_start + u32::try_from(n_end).unwrap_or_default(), + ), + )), + JSDocCommentPart::new( + &name_comment_content[c_start..], + Span::new( + span_start + u32::try_from(c_start).unwrap_or_default(), + self.body_span.end, + ), + ), + ) } - None => (None, ""), + None => ( + None, + JSDocCommentPart::new( + name_comment_content, + Span::new(span_start, self.body_span.end), + ), + ), }; - (type_part, name_part, utils::trim_comment(comment_part)) + (type_part, name_part, comment_part) } } #[cfg(test)] mod test { - use super::JSDocTag; + use crate::{Semantic, SemanticBuilder}; + use oxc_allocator::Allocator; + use oxc_parser::Parser; + use oxc_span::SourceType; + + fn build_semantic<'a>(allocator: &'a Allocator, source_text: &'a str) -> Semantic<'a> { + let source_type = SourceType::default(); + let ret = Parser::new(allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); + let semantic = SemanticBuilder::new(source_text, source_type) + .with_trivias(ret.trivias) + .build(program) + .semantic; + semantic + } + + #[test] + fn jsdoc_tag_span() { + for (source_text, tag_span_text) in [ + ( + " + /** + * multi + * line @k1 c1 + */ + ", + "@k1 c1\n ", + ), + ( + " + /** + * @k2 c2a + * c2b + * + */ + ", + "@k2 c2a\n * c2b\n *\n ", + ), + ( + " + /** + * multi + * @k3 c3 + */ + ", + "@k3 c3\n ", + ), + ("/** single line @k4 c4 */", "@k4 c4 "), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let tag = jsdocs.next().unwrap().tags().first().unwrap(); + assert_eq!(tag.span.source_text(source_text), tag_span_text); + } + } + + #[test] + fn jsdoc_tag_kind() { + for (source_text, tag_kind, tag_kind_span_text) in [ + ("/** single line @k1 c1 */", "k1", "@k1"), + ("/** single line @k2*/", "k2", "@k2"), + ( + "/** + * multi + * line + * @k3 c3a + * c3b + */", + "k3", + "@k3", + ), + ( + "/** + * multi + * line @k4 + */", + "k4", + "@k4", + ), + (" /**@*/ ", "", "@"), + (" /**@@*/ ", "", "@"), + (" /** @あいう え */ ", "あいう", "@あいう"), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let tag = jsdocs.next().unwrap().tags().first().unwrap(); + assert_eq!(tag.kind.parsed(), tag_kind); + assert_eq!(tag.kind.span.source_text(source_text), tag_kind_span_text); + } + } #[test] - fn parses_comment() { - assert_eq!(JSDocTag::new("a", "").comment(), ""); - assert_eq!(JSDocTag::new("a", " c1").comment(), "c1"); - assert_eq!(JSDocTag::new("a", "\nc2 \n z ").comment(), "c2\nz"); - assert_eq!(JSDocTag::new("a", "\n* c3\n * \n z \n\n").comment(), "c3\nz"); - assert_eq!( - JSDocTag::new("a", " comment4 and {@inline tag}!").comment(), - "comment4 and {@inline tag}!" - ); + fn jsdoc_tag_comment() { + for (source_text, parsed_comment_part) in [ + ("/** single line @k1 c1 */", ("c1", " c1 ")), + ("/** single line @k2*/", ("", "")), + ( + "/** + * multi + * line + * @k3 c3a + * c3b + */", + ("c3a\nc3b", " c3a\n * c3b\n "), + ), + ( + "/** + * multi + * line @k4 + */", + ("", "\n "), + ), + ("/**@k5 c5 w/ {@inline}!*/", ("c5 w/ {@inline}!", " c5 w/ {@inline}!")), + (" /**@k6 */ ", ("", " ")), + (" /**@*/ ", ("", "")), + (" /**@@*/ ", ("", "")), + (" /** @あいう え */ ", ("え", " え ")), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let comment = jsdocs.next().unwrap().tags().first().unwrap().comment(); + assert_eq!( + (comment.parsed().as_str(), comment.span.source_text(source_text)), + parsed_comment_part + ); + } } #[test] - fn parses_type() { - assert_eq!(JSDocTag::new("t", " {t1}").r#type(), Some("t1")); - assert_eq!(JSDocTag::new("t", "\n{t2} foo").r#type(), Some("t2")); - assert_eq!(JSDocTag::new("t", " {t3 } ").r#type(), Some("t3 ")); - assert_eq!(JSDocTag::new("t", " ").r#type(), None); - assert_eq!(JSDocTag::new("t", " t4").r#type(), None); - assert_eq!(JSDocTag::new("t", " {t5 ").r#type(), None); - assert_eq!(JSDocTag::new("t", " {t6}\nx").r#type(), Some("t6")); + fn jsdoc_tag_type() { + for (source_text, parsed_type_part) in [ + ("/** @k0 */", None), + ("/** @k1 {t1} */", Some(("t1", "{t1}"))), + ("/** @k1 {} */", Some(("", "{}"))), + ( + "/** @k2 + {t2} */", + Some(("t2", "{t2}")), + ), + ("/** @k3 { t3 } */", Some((" t3 ", "{ t3 }"))), + ("/** @k4 x{t4}y */", Some(("t4", "{t4}"))), + ("/** @k5 {t5}} */", Some(("t5", "{t5}"))), + ("/** @k6 */", None), + ("/** @k7 x */", None), + ("/** @k8 { */", None), + ("/** @k9 {t9 */", None), + ("/** @k10 {{t10} */", None), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let type_part = jsdocs.next().unwrap().tags().first().unwrap().r#type(); + assert_eq!( + type_part.map(|t| (t.parsed(), t.span.source_text(source_text))), + parsed_type_part + ); + } } #[test] - fn parses_type_comment() { - assert_eq!(JSDocTag::new("r", " {t1} c1").type_comment(), (Some("t1"), "c1".to_string())); - assert_eq!(JSDocTag::new("r", "\n{t2}").type_comment(), (Some("t2"), String::new())); - assert_eq!(JSDocTag::new("r", " c3").type_comment(), (None, "c3".to_string())); - assert_eq!(JSDocTag::new("r", " c4 foo").type_comment(), (None, "c4 foo".to_string())); - assert_eq!(JSDocTag::new("r", " ").type_comment(), (None, String::new())); - assert_eq!( - JSDocTag::new("r", "\n{t5}\nc5\n...").type_comment(), - (Some("t5"), "c5\n...".to_string()) - ); - assert_eq!( - JSDocTag::new("r", " {t6} - c6").type_comment(), - (Some("t6"), "- c6".to_string()) - ); - assert_eq!( - JSDocTag::new("r", " {{ 型: t7 }} : c7").type_comment(), - (Some("{ 型: t7 }"), ": c7".to_string()) - ); + fn jsdoc_tag_type_comment() { + for (source_text, parsed_type_part, parsed_comment_part) in [ + ("/** @k */", None, ("", " ")), + ("/** @k1 {t1} c1 */", Some(("t1", "{t1}")), ("c1", " c1 ")), + ( + "/** @k2 +{t2} */", + Some(("t2", "{t2}")), + ("", " "), + ), + ("/** @k3 c3 */", None, ("c3", " c3 ")), + ("/** @k4\nc4 foo */", None, ("c4 foo", "\nc4 foo ")), + ( + "/** @k5 +{t5} +c5 */", + Some(("t5", "{t5}")), + ("c5", "\nc5 "), + ), + ("/** @k6 {t6} - c6 */", Some(("t6", "{t6}")), ("- c6", " - c6 ")), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let (type_part, comment_part) = + jsdocs.next().unwrap().tags().first().unwrap().type_comment(); + assert_eq!( + type_part.map(|t| (t.parsed(), t.span.source_text(source_text))), + parsed_type_part + ); + assert_eq!( + (comment_part.parsed().as_str(), comment_part.span.source_text(source_text)), + parsed_comment_part + ); + } } #[test] - fn parses_type_name_comment() { - assert_eq!( - JSDocTag::new("p", " {t1} n1 c1").type_name_comment(), - (Some("t1"), Some("n1"), "c1".to_string()) - ); - assert_eq!( - JSDocTag::new("p", " {t2} n2").type_name_comment(), - (Some("t2"), Some("n2"), String::new()) - ); - assert_eq!( - JSDocTag::new("p", " n3 c3").type_name_comment(), - (None, Some("n3"), "c3".to_string()) - ); - assert_eq!(JSDocTag::new("p", "").type_name_comment(), (None, None, String::new())); - assert_eq!(JSDocTag::new("p", "\n\n").type_name_comment(), (None, None, String::new())); - assert_eq!( - JSDocTag::new("p", " {t4} n4 c4\n...").type_name_comment(), - (Some("t4"), Some("n4"), "c4\n...".to_string()) - ); - assert_eq!( - JSDocTag::new("p", " {t5} n5 - c5").type_name_comment(), - (Some("t5"), Some("n5"), "- c5".to_string()) - ); - assert_eq!( - JSDocTag::new("p", "\n{t6}\nn6\nc6").type_name_comment(), - (Some("t6"), Some("n6"), "c6".to_string()) - ); - assert_eq!( - JSDocTag::new("p", "\n\n{t7}\nn7\nc\n7").type_name_comment(), - (Some("t7"), Some("n7"), "c\n7".to_string()) - ); - assert_eq!( - JSDocTag::new("p", " {t8}").type_name_comment(), - (Some("t8"), None, String::new()) - ); + fn jsdoc_tag_type_name_comment() { + for (source_text, parsed_type_part, parsed_type_name_part, parsed_comment_part) in [ + ("/** @k */", None, None, ("", " ")), + ("/** @k\n\n*/", None, None, ("", "\n\n")), + ("/** @k1 {t1} n1 c1 */", Some(("t1", "{t1}")), Some(("n1", "n1")), ("c1", " c1 ")), + ("/** @k2 {t2} n2*/", Some(("t2", "{t2}")), Some(("n2", "n2")), ("", "")), + ("/** @k3 n3 c3 */", None, Some(("n3", "n3")), ("c3", " c3 ")), + ( + "/** @k4 n4 c4 +...*/", + None, + Some(("n4", "n4")), + ("c4\n...", " c4\n..."), + ), + ( + "/** @k5 {t5} n5 - c5 */", + Some(("t5", "{t5}")), + Some(("n5", "n5")), + ("- c5", " - c5 "), + ), + ( + "/** @k6 +{t6} +n6 +c6 */", + Some(("t6", "{t6}")), + Some(("n6", "n6")), + ("c6", "\nc6 "), + ), + ( + "/** @k7 + +{t7} + +n7 + +c7 */", + Some(("t7", "{t7}")), + Some(("n7", "n7")), + ("c7", "\n\nc7 "), + ), + ("/** @k8 {t8} */", Some(("t8", "{t8}")), None, ("", "")), + ("/** @k8 n8 */", None, Some(("n8", "n8")), ("", " ")), + ] { + let allocator = Allocator::default(); + let semantic = build_semantic(&allocator, source_text); + let mut jsdocs = semantic.jsdoc().iter_all(); + + let (type_part, type_name_part, comment_part) = + jsdocs.next().unwrap().tags().first().unwrap().type_name_comment(); + assert_eq!( + type_part.map(|t| (t.parsed(), t.span.source_text(source_text))), + parsed_type_part + ); + assert_eq!( + type_name_part.map(|n| (n.parsed(), n.span.source_text(source_text))), + parsed_type_name_part + ); + assert_eq!( + (comment_part.parsed().as_str(), comment_part.span.source_text(source_text)), + parsed_comment_part + ); + } } } diff --git a/crates/oxc_semantic/src/jsdoc/parser/mod.rs b/crates/oxc_semantic/src/jsdoc/parser/mod.rs index 65c45ff0d9e7d..99dfcb7e5b61c 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/mod.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/mod.rs @@ -1,4 +1,5 @@ mod jsdoc; +mod jsdoc_parts; mod jsdoc_tag; mod parse; mod utils; diff --git a/crates/oxc_semantic/src/jsdoc/parser/parse.rs b/crates/oxc_semantic/src/jsdoc/parser/parse.rs index 395cc7a13adae..37f6d108271bb 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/parse.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/parse.rs @@ -1,10 +1,11 @@ +use super::jsdoc_parts::{JSDocCommentPart, JSDocTagKindPart}; use super::jsdoc_tag::JSDocTag; use super::utils; use oxc_span::Span; /// source_text: Inside of /**HERE*/, NOT includes `/**` and `*/` /// span_start: Global positioned `Span` start for this JSDoc comment -pub fn parse_jsdoc(source_text: &str, jsdoc_span_start: u32) -> (String, Vec<(Span, JSDocTag)>) { +pub fn parse_jsdoc(source_text: &str, jsdoc_span_start: u32) -> (JSDocCommentPart, Vec) { debug_assert!(!source_text.starts_with("/*")); debug_assert!(!source_text.ends_with("*/")); @@ -12,7 +13,7 @@ pub fn parse_jsdoc(source_text: &str, jsdoc_span_start: u32) -> (String, Vec<(Sp // - Comment goes first, and tags(`@xxx`) follow // - Both can be optional // - Each tag is also separated by whitespace + `@` - let mut comment = ""; + let mut comment = None; let mut tags = vec![]; // So, find `@` to split comment and each tag. @@ -27,14 +28,15 @@ pub fn parse_jsdoc(source_text: &str, jsdoc_span_start: u32) -> (String, Vec<(Sp '}' => in_braces = false, '@' if !in_braces => { let part = &source_text[start..end]; + let span = Span::new( + jsdoc_span_start + u32::try_from(start).unwrap_or_default(), + jsdoc_span_start + u32::try_from(end).unwrap_or_default(), + ); if comment_found { - tags.push(( - get_tag_kind_span(part, (start, end), jsdoc_span_start), - parse_jsdoc_tag(part), - )); + tags.push(parse_jsdoc_tag(part, span)); } else { - comment = part; + comment = Some(JSDocCommentPart::new(part, span)); comment_found = true; } @@ -50,367 +52,57 @@ pub fn parse_jsdoc(source_text: &str, jsdoc_span_start: u32) -> (String, Vec<(Sp // If `@` not found, flush the last draft if start != end { let part = &source_text[start..end]; + let span = Span::new( + jsdoc_span_start + u32::try_from(start).unwrap_or_default(), + jsdoc_span_start + u32::try_from(end).unwrap_or_default(), + ); if comment_found { - tags.push(( - get_tag_kind_span(part, (start, end), jsdoc_span_start), - parse_jsdoc_tag(part), - )); + tags.push(parse_jsdoc_tag(part, span)); } else { - comment = part; + comment = Some(JSDocCommentPart::new(part, span)); } } - (utils::trim_comment(comment), tags) -} - -// Use `Span` for `@kind` part instead of whole tag. -// -// For example, whole `tag.span` in the following JSDoc will be: -// /** -// * @kind1 bar -// * baz... -// * @kind2 -// */ -// for `@kind1`: `@kind1 bar\n * baz...\n * ` -// for `@kind2`: `@kind2\n ` -// -// It's too verbose and may not fit for linter diagnostics span. -fn get_tag_kind_span( - tag_content: &str, - (tag_offset_start, _): (usize, usize), - jsdoc_span_start: u32, -) -> Span { - debug_assert!(tag_content.starts_with('@')); - // This surely exists, at least `@` itself - let (k_start, k_end) = utils::find_token_range(tag_content).unwrap(); - - let k_len = k_end - k_start; - let (start, end) = ( - u32::try_from(tag_offset_start + k_start).unwrap_or_default(), - u32::try_from(tag_offset_start + k_start + k_len).unwrap_or_default(), - ); - - Span::new(jsdoc_span_start + start, jsdoc_span_start + end) + ( + comment.unwrap_or(JSDocCommentPart::new("", Span::new(jsdoc_span_start, jsdoc_span_start))), + tags, + ) } /// tag_content: Starts with `@`, may be mulitline -fn parse_jsdoc_tag(tag_content: &str) -> JSDocTag { +fn parse_jsdoc_tag(tag_content: &str, jsdoc_tag_span: Span) -> JSDocTag { debug_assert!(tag_content.starts_with('@')); // This surely exists, at least `@` itself let (k_start, k_end) = utils::find_token_range(tag_content).unwrap(); - JSDocTag::new( - // Omit the first `@` - &tag_content[k_start + 1..k_end], - // Includes splitter whitespace to distinguish these cases: - // ``` - // /** - // * @k * <- should not omit - // */ - // - // /** - // * @k - // * <- should omit - // */ - // ``` - // If not included, both body_part will starts with `* <- ...`! - // - // It does not affect the output since it will be trimmed later. - &tag_content[k_end..], - ) -} - -#[cfg(test)] -mod test { - use super::parse_jsdoc_tag; - - fn parse_from_full_text(full_text: &str) -> (String, Vec) { - // Outside of markers can be trimmed - let source_text = full_text.trim().trim_start_matches("/**").trim_end_matches("*/"); - let (comment, tags) = super::parse_jsdoc(source_text, 0); - (comment, tags.iter().map(|(_, t)| t).cloned().collect()) - } - - #[test] - fn parses_jsdoc_comment() { - assert_eq!(parse_from_full_text("/**hello*/"), ("hello".to_string(), vec![])); - assert_eq!( - parse_from_full_text("/** hello full_text */"), - ("hello full_text".to_string(), vec![]) - ); - assert_eq!(parse_from_full_text("/***/"), (String::new(), vec![])); - assert_eq!(parse_from_full_text("/****/"), ("*".to_string(), vec![])); - assert_eq!(parse_from_full_text("/*****/"), ("**".to_string(), vec![])); - assert_eq!( - parse_from_full_text( - "/** - * * x - ** y - */" - ) - .0, - "* x\n* y" - ); - - assert_eq!(parse_from_full_text("/** <- trim -> */").0, "<- trim ->"); - assert_eq!( - parse_from_full_text( - " - /** - * <- omit this, keep this -> * - */ - " - ) - .0, - "<- omit this, keep this -> *" - ); - - assert_eq!( - parse_from_full_text( - "/** -this is -comment {@link link} ... -@x -*/" - ) - .0, - "this is\ncomment {@link link} ..." - ); - assert_eq!( - parse_from_full_text( - "/** -         * 日本語とか -         * multibyte文字はどう⁉️ - */" - ) - .0, - "日本語とか\nmultibyte文字はどう⁉️" - ); - - assert_eq!( - parse_from_full_text("/**\nhello {@see inline} source {@a 2}\n*/").0, - "hello {@see inline} source {@a 2}" - ); - - assert_eq!(parse_from_full_text("/** ハロー @comment だよ*/").0, "ハロー"); - } - - #[test] - fn parses_jsdoc_tags() { - assert_eq!( - parse_from_full_text("/**@deprecated*/").1, - vec![parse_jsdoc_tag("@deprecated")] - ); - assert_eq!( - parse_from_full_text("/**@foo since 2024 */").1, - vec![parse_jsdoc_tag("@foo since 2024 ")] - ); - - assert_eq!( - parse_from_full_text("/** @foo @bar */").1, - vec![parse_jsdoc_tag("@foo "), parse_jsdoc_tag("@bar ")] - ); - - assert_eq!(parse_from_full_text("/**@*/").1, vec![parse_jsdoc_tag("@")]); - - assert_eq!( - parse_from_full_text("/** @aiue あいうえ @o お*/").1, - vec![parse_jsdoc_tag("@aiue あいうえ "), parse_jsdoc_tag("@o お")], - ); - assert_eq!( - parse_from_full_text("/** @a @@ @d */").1, - vec![ - parse_jsdoc_tag("@a "), - parse_jsdoc_tag("@"), - parse_jsdoc_tag("@ "), - parse_jsdoc_tag("@d ") - ], - ); - - assert_eq!( - parse_from_full_text( - "/** @yo - */" - ) - .1, - vec![parse_jsdoc_tag("@yo\n ")] - ); - assert_eq!( - parse_from_full_text( - "/** - * @foo - */" - ) - .1, - vec![parse_jsdoc_tag("@foo\n ")] - ); - assert_eq!( - parse_from_full_text( - " - /** - * @x with asterisk - */ - " - ) - .1, - vec![parse_jsdoc_tag("@x with asterisk\n ")] - ); - assert_eq!( - parse_from_full_text( - " - /** - @y without - asterisk - */ - " - ) - .1, - vec![parse_jsdoc_tag("@y without\n asterisk\n ")] - ); - - assert_eq!( - parse_from_full_text( - " - /** - @foo@bar - * @baz - */ - " - ) - .1, - vec![ - parse_jsdoc_tag("@foo"), - parse_jsdoc_tag("@bar\n * "), - parse_jsdoc_tag("@baz\n ") - ] - ); - assert_eq!( - parse_from_full_text( - "/** - * @one - * - * ... - * - * @two */" - ) - .1, - vec![ - parse_jsdoc_tag("@one\n *\n * ...\n *\n * "), - parse_jsdoc_tag("@two ") - ] - ); - assert_eq!( - parse_from_full_text( - "/** - * ... - * @hey you! - * Are you OK? - * @yes I'm fine - */" - ) - .1, - vec![ - parse_jsdoc_tag( - "@hey you!\n * Are you OK?\n * " - ), - parse_jsdoc_tag("@yes I'm fine\n ") - ] - ); - } - - #[test] - fn parses_practical() { - let jsdoc = parse_from_full_text( - " -/** - * @typedef {Object} User - a User account - * @property {string} displayName - the name used to show the user - * @property {number} id - a unique id - */ -", - ); - let mut tags = jsdoc.1.iter(); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "typedef"); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "property"); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "property"); - - let jsdoc = parse_from_full_text( - " -/** - * Adds two numbers together - * @param {number} a The first number - * @param {number} b The second number - * @returns {number} - */ -", - ); - let mut tags = jsdoc.1.iter(); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "returns"); - } - - #[test] - fn parses_practical_with_multibyte() { - let jsdoc = parse_from_full_text( - "/** - * flat tree data on expanded state - * - * @export - * @template T - * @param {*} data : table data - * @param {string} childrenColumnName : 指定树形结构的列名 - * @param {Set} expandedKeys : 展开的行对应的keys - * @param {GetRowKey} getRowKey : 获取当前rowKey的方法 - * @returns flattened data - */", - ); - assert_eq!(jsdoc.0, "flat tree data on expanded state"); - let mut tags = jsdoc.1.iter(); - assert_eq!(tags.len(), 7); - - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "export"); - assert_eq!(tag.comment(), ""); - - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "template"); - assert_eq!(tag.comment(), "T"); - - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - assert_eq!(tag.type_name_comment(), (Some("*"), Some("data"), ": table data".to_string())); - - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - assert_eq!( - tag.type_name_comment(), - (Some("string"), Some("childrenColumnName"), ": 指定树形结构的列名".to_string()) - ); - - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - assert_eq!( - tag.type_name_comment(), - (Some("Set"), Some("expandedKeys"), ": 展开的行对应的keys".to_string()) - ); + // Kind: @xxx + let kind = JSDocTagKindPart::new( + &tag_content[k_start..k_end], + Span::new( + jsdoc_tag_span.start + u32::try_from(k_start).unwrap_or_default(), + jsdoc_tag_span.start + u32::try_from(k_end).unwrap_or_default(), + ), + ); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "param"); - assert_eq!( - tag.type_name_comment(), - (Some("GetRowKey"), Some("getRowKey"), ": 获取当前rowKey的方法".to_string()) - ); + // Body part: the rest of the tag content. + // Splitter whitespace should be included to distinguish these cases for comment parser. + // ``` + // /** + // * @k * <- should not omit + // */ + // /** + // * @k + // * <- should omit + // */ + // ``` + // If not included, both body content will start with `* <- ...` and fails to parse(trim). + // This is only for comment parser, it will be ignored for type and type name parser. + let body_content = &tag_content[k_end..]; + let body_span = Span::new( + jsdoc_tag_span.start + u32::try_from(k_end).unwrap_or_default(), + jsdoc_tag_span.end, + ); - let tag = tags.next().unwrap(); - assert_eq!(tag.kind, "returns"); - assert_eq!(tag.type_comment(), (None, "flattened data".to_string())); - } + JSDocTag::new(kind, body_content, body_span) } diff --git a/crates/oxc_semantic/src/jsdoc/parser/utils.rs b/crates/oxc_semantic/src/jsdoc/parser/utils.rs index bd5b4601c7dab..789ff33b5b449 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/utils.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/utils.rs @@ -1,20 +1,4 @@ -pub fn trim_comment(s: &str) -> String { - let lines = s.lines(); - - // If single line, there is no leading `*` - if lines.clone().count() == 1 { - return s.trim().to_string(); - } - - s.lines() - // Trim leading the first `*` in each line - .map(|line| line.trim().strip_prefix('*').unwrap_or(line).trim()) - .filter(|line| !line.is_empty()) - .collect::>() - .join("\n") -} - -// For now, just returns inside of most outer braces +// For now, just returns the most outer braces pub fn find_type_range(s: &str) -> Option<(usize, usize)> { let mut start = None; let mut brace_count = 0; @@ -24,7 +8,7 @@ pub fn find_type_range(s: &str) -> Option<(usize, usize)> { brace_count += 1; if start.is_none() { - start = Some(idx + 1); + start = Some(idx); } } '}' => { @@ -32,7 +16,7 @@ pub fn find_type_range(s: &str) -> Option<(usize, usize)> { if brace_count == 0 { if let Some(start) = start { - return Some((start, idx)); + return Some((start, idx + 1)); } } } @@ -55,7 +39,7 @@ pub fn find_token_range(s: &str) -> Option<(usize, usize)> { } } - // Everything is a name + // Everything is a token if let Some(start) = start { return Some((start, s.len())); } @@ -65,90 +49,21 @@ pub fn find_token_range(s: &str) -> Option<(usize, usize)> { #[cfg(test)] mod test { - use super::{find_token_range, find_type_range, trim_comment}; - - #[test] - fn trim_jsdoc_comments() { - for (actual, expect) in [ - ("", ""), - ("hello ", "hello"), - (" * single line", "* single line"), - (" * ", "*"), - (" * * ", "* *"), - ("***", "***"), - ( - " - trim -", "trim", - ), - ( - " - -", "", - ), - ( - " - * - * - ", - "", - ), - ( - " - * asterisk -", - "asterisk", - ), - ( - " - * * li - * * li -", - "* li\n* li", - ), - ( - " -* list -* list -", - "list\nlist", - ), - ( - " - * * 1 - ** 2 -", - "* 1\n* 2", - ), - ( - " -1 - -2 - - -3 - ", - "1\n2\n3", - ), - ] { - assert_eq!(trim_comment(actual), expect); - } - } + use super::{find_token_range, find_type_range}; #[test] fn extract_type_part_range() { for (actual, expect) in [ - ("{t1}", Some("t1")), - (" { t2 } ", Some(" t2 ")), - ("{{ t3: string }}", Some("{ t3: string }")), - ("{t4} name", Some("t4")), - (" {t5} ", Some("t5")), + ("{t1}", Some("{t1}")), + (" { t2 } ", Some("{ t2 }")), + ("x{{ t3: string }}x", Some("{{ t3: string }}")), + ("{t4} name", Some("{t4}")), + (" {t5} ", Some("{t5}")), ("{t6 x", None), ("t7", None), ("{{t8}", None), ("", None), - ("{[ true, false ]}", Some("[ true, false ]")), + ("{[ true, false ]}", Some("{[ true, false ]}")), ] { assert_eq!(find_type_range(actual).map(|(s, e)| &actual[s..e]), expect); } diff --git a/crates/oxc_sourcemap/CHANGELOG.md b/crates/oxc_sourcemap/CHANGELOG.md index 564732161e211..f141e36155a2c 100644 --- a/crates/oxc_sourcemap/CHANGELOG.md +++ b/crates/oxc_sourcemap/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Features + +- Add x_google_ignoreList (#2928) +- Add sourceRoot (#2926) + +## [0.12.2] - 2024-04-08 + +### Features + +- Optional JSONSourceMap fileds (#2910) +- Add methods to mutate SourceMap (#2909) +- Add SourceMapBuilder file (#2908) + ## [0.11.1] - 2024-04-03 ### Bug Fixes diff --git a/crates/oxc_sourcemap/Cargo.toml b/crates/oxc_sourcemap/Cargo.toml index 0dee70ade7611..7b05f651efa07 100644 --- a/crates/oxc_sourcemap/Cargo.toml +++ b/crates/oxc_sourcemap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_sourcemap" -version = "0.12.1" +version = "0.12.3" authors.workspace = true description.workspace = true edition.workspace = true diff --git a/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs b/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs index 520cadfe4f685..cd73ac406a9e4 100644 --- a/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs +++ b/crates/oxc_sourcemap/src/concat_sourcemap_builder.rs @@ -78,6 +78,7 @@ impl ConcatSourceMapBuilder { SourceMap::new( None, self.names, + None, self.sources, Some(self.source_contents), self.tokens, @@ -91,6 +92,7 @@ fn test_concat_sourcemap_builder() { let sm1 = SourceMap::new( None, vec!["foo".into(), "foo2".into()], + None, vec!["foo.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], @@ -99,6 +101,7 @@ fn test_concat_sourcemap_builder() { let sm2 = SourceMap::new( None, vec!["bar".into()], + None, vec!["bar.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], @@ -112,6 +115,7 @@ fn test_concat_sourcemap_builder() { let sm = SourceMap::new( None, vec!["foo".into(), "foo2".into(), "bar".into()], + None, vec!["foo.js".into(), "bar.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0)), Token::new(3, 1, 1, 1, Some(1), Some(2))], diff --git a/crates/oxc_sourcemap/src/decode.rs b/crates/oxc_sourcemap/src/decode.rs index 86e1f766019f4..70fe0c4429387 100644 --- a/crates/oxc_sourcemap/src/decode.rs +++ b/crates/oxc_sourcemap/src/decode.rs @@ -6,10 +6,17 @@ use crate::{SourceMap, Token}; #[derive(serde::Deserialize)] #[serde(rename_all = "camelCase")] struct JSONSourceMap { + // An optional name of the generated code that this source map is associated with. file: Option, + // A string with the encoded mapping data. mappings: Option, + // An optional source root, useful for relocating source files on a server or removing repeated values in the “sources” entry. This value is prepended to the individual entries in the “source” field. + source_root: Option, + // A list of original sources used by the “mappings” entry. sources: Option>>, + // An optional list of source content, useful when the “source” can’t be hosted. The contents are listed in the same order as the sources in line 5. “null” may be used if some original sources should be retrieved by name. sources_content: Option>>, + // A list of symbol names used by the “mappings” entry. names: Option>, } @@ -18,6 +25,7 @@ pub fn decode(value: &str) -> Result { let file = json.file.map(Into::into); let names = json.names.map(|v| v.into_iter().map(Into::into).collect::>()).unwrap_or_default(); + let source_root = json.source_root.map(Into::into); let sources = json .sources .map(|v| v.into_iter().map(Option::unwrap_or_default).map(Into::into).collect::>()) @@ -26,7 +34,7 @@ pub fn decode(value: &str) -> Result { .sources_content .map(|v| v.into_iter().map(Option::unwrap_or_default).map(Into::into).collect::>()); let tokens = decode_mapping(&json.mappings.unwrap_or_default(), names.len(), sources.len())?; - Ok(SourceMap::new(file, names, sources, source_contents, tokens, None)) + Ok(SourceMap::new(file, names, source_root, sources, source_contents, tokens, None)) } #[allow(clippy::cast_possible_truncation)] @@ -140,6 +148,7 @@ fn test_decode_sourcemap() { "mappings": "AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM" }"#; let sm = SourceMap::from_json_string(input).unwrap(); + assert_eq!(sm.get_source_root(), Some("x")); let mut iter = sm.get_source_view_tokens().filter(|token| token.get_name_id().is_some()); assert_eq!(iter.next().unwrap().to_tuple(), (Some("coolstuff.js"), 0, 4, Some("x"))); assert_eq!(iter.next().unwrap().to_tuple(), (Some("coolstuff.js"), 1, 4, Some("x"))); diff --git a/crates/oxc_sourcemap/src/encode.rs b/crates/oxc_sourcemap/src/encode.rs index 201bbd3b51ae5..3ddd29c55ab0d 100644 --- a/crates/oxc_sourcemap/src/encode.rs +++ b/crates/oxc_sourcemap/src/encode.rs @@ -16,6 +16,11 @@ pub fn encode(sourcemap: &SourceMap) -> Result { buf.push_str(file); buf.push_str("\","); } + if let Some(source_root) = sourcemap.get_source_root() { + buf.push_str("\"sourceRoot\":\""); + buf.push_str(source_root); + buf.push_str("\","); + } buf.push_str("\"names\":["); let names = sourcemap .names @@ -42,6 +47,12 @@ pub fn encode(sourcemap: &SourceMap) -> Result { .map_err(Error::from)?; buf.push_str("e_source_contents.join(",")); } + if let Some(x_google_ignore_list) = &sourcemap.x_google_ignore_list { + buf.push_str("],\"x_google_ignoreList\":["); + let x_google_ignore_list = + x_google_ignore_list.iter().map(ToString::to_string).collect::>(); + buf.push_str(&x_google_ignore_list.join(",")); + } buf.push_str("],\"mappings\":\""); buf.push_str(&serialize_sourcemap_mappings(sourcemap)); buf.push_str("\"}"); @@ -144,6 +155,7 @@ fn test_encode() { let input = r#"{ "version": 3, "sources": ["coolstuff.js"], + "sourceRoot": "x", "names": ["x","alert"], "mappings": "AAAA,GAAIA,GAAI,EACR,IAAIA,GAAK,EAAG,CACVC,MAAM" }"#; @@ -158,16 +170,18 @@ fn test_encode() { #[test] fn test_encode_escape_string() { // '\0' should be escaped. - let sm = SourceMap::new( + let mut sm = SourceMap::new( None, vec!["\0".into()], + None, vec!["\0".into()], Some(vec!["\0".into()]), vec![], None, ); + sm.set_x_google_ignore_list(vec![0]); assert_eq!( sm.to_json_string().unwrap(), - r#"{"version":3,"names":["\u0000"],"sources":["\u0000"],"sourcesContent":["\u0000"],"mappings":""}"# + r#"{"version":3,"names":["\u0000"],"sources":["\u0000"],"sourcesContent":["\u0000"],"x_google_ignoreList":[0],"mappings":""}"# ); } diff --git a/crates/oxc_sourcemap/src/sourcemap.rs b/crates/oxc_sourcemap/src/sourcemap.rs index c829940181de3..652e390637dce 100644 --- a/crates/oxc_sourcemap/src/sourcemap.rs +++ b/crates/oxc_sourcemap/src/sourcemap.rs @@ -11,10 +11,15 @@ use std::sync::Arc; pub struct SourceMap { pub(crate) file: Option>, pub(crate) names: Vec>, + pub(crate) source_root: Option, pub(crate) sources: Vec>, pub(crate) source_contents: Option>>, pub(crate) tokens: Vec, pub(crate) token_chunks: Option>, + /// Identifies third-party sources (such as framework code or bundler-generated code), allowing developers to avoid code that they don't want to see or step through, without having to configure this beforehand. + /// The `x_google_ignoreList` field refers to the `sources` array, and lists the indices of all the known third-party sources in that source map. + /// When parsing the source map, developer tools can use this to determine sections of the code that the browser loads and runs that could be automatically ignore-listed. + pub(crate) x_google_ignore_list: Option>, } #[allow(clippy::cast_possible_truncation)] @@ -22,12 +27,22 @@ impl SourceMap { pub fn new( file: Option>, names: Vec>, + source_root: Option, sources: Vec>, source_contents: Option>>, tokens: Vec, token_chunks: Option>, ) -> Self { - Self { file, names, sources, source_contents, tokens, token_chunks } + Self { + file, + names, + source_root, + sources, + source_contents, + tokens, + token_chunks, + x_google_ignore_list: None, + } } /// Convert `SourceMap` to vlq sourcemap string. @@ -63,6 +78,15 @@ impl SourceMap { self.file = Some(file.into()); } + pub fn get_source_root(&self) -> Option<&str> { + self.source_root.as_deref() + } + + /// Set `x_google_ignoreList`. + pub fn set_x_google_ignore_list(&mut self, x_google_ignore_list: Vec) { + self.x_google_ignore_list = Some(x_google_ignore_list); + } + pub fn get_names(&self) -> impl Iterator { self.names.iter().map(AsRef::as_ref) } @@ -224,6 +248,7 @@ fn test_sourcemap_source_view_token() { let sm = SourceMap::new( None, vec!["foo".into()], + None, vec!["foo.js".into()], None, vec![Token::new(1, 1, 1, 1, Some(0), Some(0))], diff --git a/crates/oxc_sourcemap/src/sourcemap_builder.rs b/crates/oxc_sourcemap/src/sourcemap_builder.rs index 3147a6ceb6002..2d77166c6eab2 100644 --- a/crates/oxc_sourcemap/src/sourcemap_builder.rs +++ b/crates/oxc_sourcemap/src/sourcemap_builder.rs @@ -70,6 +70,7 @@ impl SourceMapBuilder { SourceMap::new( self.file, self.names, + None, self.sources, Some(self.source_contents), self.tokens, diff --git a/crates/oxc_span/Cargo.toml b/crates/oxc_span/Cargo.toml index 957f325148f85..3c48c3d694bb0 100644 --- a/crates/oxc_span/Cargo.toml +++ b/crates/oxc_span/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_span" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_syntax/CHANGELOG.md b/crates/oxc_syntax/CHANGELOG.md index 365affba148eb..983f7a84ed653 100644 --- a/crates/oxc_syntax/CHANGELOG.md +++ b/crates/oxc_syntax/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.2] - 2024-04-08 + +### Bug Fixes + +- Symbols inside functions and classes incorrectly flagged as exported (#2896) + ## [0.11.1] - 2024-04-03 ### Features diff --git a/crates/oxc_syntax/Cargo.toml b/crates/oxc_syntax/Cargo.toml index 3c7171e6a6d35..24f927662f214 100644 --- a/crates/oxc_syntax/Cargo.toml +++ b/crates/oxc_syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_syntax" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_transformer/CHANGELOG.md b/crates/oxc_transformer/CHANGELOG.md index c6d7469dda68f..5231ef73fd137 100644 --- a/crates/oxc_transformer/CHANGELOG.md +++ b/crates/oxc_transformer/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this crate will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.12.3] - 2024-04-11 + +### Features + +- Implement plugin-transform-react-display-name top-down (#2937) +- Add transform context to all plugins (#2931) +- Add transform callback methods (#2929) +- Add react preset (#2921) + ## [0.11.1] - 2024-04-03 ### Features diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index f7104d5c09fb4..3200278badc4c 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_transformer" -version = "0.12.1" +version = "0.12.3" publish = true authors.workspace = true description.workspace = true diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs new file mode 100644 index 0000000000000..d9d5cbe40d172 --- /dev/null +++ b/crates/oxc_transformer/src/context.rs @@ -0,0 +1,37 @@ +use std::{cell::RefCell, mem, rc::Rc}; + +use oxc_allocator::Allocator; +use oxc_ast::AstBuilder; +use oxc_diagnostics::Error; +use oxc_semantic::Semantic; +use oxc_span::SourceType; + +pub type Ctx<'a> = Rc>; + +pub struct TransformCtx<'a> { + pub ast: AstBuilder<'a>, + pub source_type: SourceType, + pub semantic: Semantic<'a>, + errors: RefCell>, +} + +impl<'a> TransformCtx<'a> { + pub fn new(allocator: &'a Allocator, source_type: SourceType, semantic: Semantic<'a>) -> Self { + Self { + ast: AstBuilder::new(allocator), + source_type, + semantic, + errors: RefCell::new(vec![]), + } + } + + pub fn take_errors(&self) -> Vec { + mem::take(&mut self.errors.borrow_mut()) + } + + /// Add an Error + #[allow(unused)] + pub fn error>(&self, error: T) { + self.errors.borrow_mut().push(error.into()); + } +} diff --git a/crates/oxc_transformer/src/decorators/mod.rs b/crates/oxc_transformer/src/decorators/mod.rs index 8fbe511f7655f..5e997baf69fa9 100644 --- a/crates/oxc_transformer/src/decorators/mod.rs +++ b/crates/oxc_transformer/src/decorators/mod.rs @@ -1,19 +1,30 @@ +use std::rc::Rc; + use serde::Deserialize; +use oxc_ast::ast::*; + +use crate::context::Ctx; + /// Only `"2023-11"` will be implemented because Babel 8 will only support "2023-11" and "legacy". #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DecoratorsOptions; /// [proposal-decorators](https://babeljs.io/docs/babel-plugin-proposal-decorators) -#[derive(Debug, Default)] -pub struct Decorators { - #[allow(unused)] +#[allow(unused)] +pub struct Decorators<'a> { options: DecoratorsOptions, + ctx: Ctx<'a>, } -impl Decorators { - pub fn new(options: DecoratorsOptions) -> Self { - Self { options } +impl<'a> Decorators<'a> { + pub fn new(options: DecoratorsOptions, ctx: &Ctx<'a>) -> Self { + Self { options, ctx: Rc::clone(ctx) } } } + +// Transformers +impl<'a> Decorators<'a> { + pub fn transform_statement(&mut self, _stmt: &mut Statement<'_>) {} +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 0ab3837322ce7..d79c785f995c8 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::wildcard_imports)] + //! Transformer / Transpiler //! //! References: @@ -7,16 +9,19 @@ // Core mod compiler_assumptions; -// Plugins: +mod context; +// Presets: mod decorators; -mod react_display_name; -mod react_jsx; -mod react_jsx_self; -mod react_jsx_source; +mod react; mod typescript; +use std::rc::Rc; + use oxc_allocator::Allocator; -use oxc_ast::ast::Program; +use oxc_ast::{ + ast::*, + visit::{walk_mut, VisitMut}, +}; use oxc_diagnostics::Error; use oxc_semantic::Semantic; use oxc_span::SourceType; @@ -24,13 +29,12 @@ use oxc_span::SourceType; pub use crate::{ compiler_assumptions::CompilerAssumptions, decorators::{Decorators, DecoratorsOptions}, - react_display_name::{ReactDisplayName, ReactDisplayNameOptions}, - react_jsx::{ReactJsx, ReactJsxOptions}, - react_jsx_self::{ReactJsxSelf, ReactJsxSelfOptions}, - react_jsx_source::{ReactJsxSource, ReactJsxSourceOptions}, + react::{React, ReactDisplayName, ReactJsx, ReactJsxSelf, ReactJsxSource, ReactOptions}, typescript::{TypeScript, TypeScriptOptions}, }; +use crate::context::{Ctx, TransformCtx}; + #[allow(unused)] #[derive(Debug, Default, Clone)] pub struct TransformOptions { @@ -40,30 +44,23 @@ pub struct TransformOptions { pub assumptions: CompilerAssumptions, // Plugins + /// [proposal-decorators](https://babeljs.io/docs/babel-plugin-proposal-decorators) pub decorators: DecoratorsOptions, + + /// [preset-typescript](https://babeljs.io/docs/babel-preset-typescript) pub typescript: TypeScriptOptions, - pub react_jsx: ReactJsxOptions, - pub react_display_name: ReactDisplayNameOptions, - pub react_jsx_self: ReactJsxSelfOptions, - pub react_jsx_source: ReactJsxSourceOptions, + + /// [preset-react](https://babeljs.io/docs/babel-preset-react) + pub react: ReactOptions, } #[allow(unused)] pub struct Transformer<'a> { - allocator: &'a Allocator, - source_type: SourceType, - semantic: Semantic<'a>, - options: TransformOptions, - - // Stage 3 - decorators: Decorators, - // [preset-typescript](https://babeljs.io/docs/babel-preset-typescript) - typescript: TypeScript, - // [preset-react](https://babeljs.io/docs/babel-preset-react) - react_display_name: ReactDisplayName, - react_jsx: ReactJsx, - react_jsx_self: ReactJsxSelf, - react_jsx_source: ReactJsxSource, + ctx: Ctx<'a>, + // NOTE: all callbacks must run in order. + x0_typescript: TypeScript<'a>, + x1_react: React<'a>, + x2_decorators: Decorators<'a>, } impl<'a> Transformer<'a> { @@ -73,24 +70,53 @@ impl<'a> Transformer<'a> { semantic: Semantic<'a>, options: TransformOptions, ) -> Self { + let ctx = Rc::new(TransformCtx::new(allocator, source_type, semantic)); Self { - allocator, - source_type, - semantic, - options, - decorators: Decorators::default(), - typescript: TypeScript::default(), - react_display_name: ReactDisplayName::default(), - react_jsx: ReactJsx::default(), - react_jsx_self: ReactJsxSelf::default(), - react_jsx_source: ReactJsxSource::default(), + ctx: Rc::clone(&ctx), + x0_typescript: TypeScript::new(options.typescript, &ctx), + x1_react: React::new(options.react, &ctx), + x2_decorators: Decorators::new(options.decorators, &ctx), } } /// # Errors /// /// Returns `Vec` if any errors were collected during the transformation. - pub fn build(self, _program: &mut Program<'a>) -> Result<(), Vec> { - Ok(()) + pub fn build(mut self, program: &mut Program<'a>) -> Result<(), Vec> { + self.visit_program(program); + let errors = self.ctx.take_errors(); + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } +} + +impl<'a> VisitMut<'a> for Transformer<'a> { + fn visit_statement(&mut self, stmt: &mut Statement<'a>) { + self.x0_typescript.transform_statement(stmt); + self.x2_decorators.transform_statement(stmt); + walk_mut::walk_statement_mut(self, stmt); + } + + fn visit_expression(&mut self, expr: &mut Expression<'a>) { + self.x1_react.transform_expression(expr); + walk_mut::walk_expression_mut(self, expr); + } + + fn visit_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) { + self.x1_react.transform_variable_declarator(declarator); + walk_mut::walk_variable_declarator_mut(self, declarator); + } + + fn visit_object_property(&mut self, prop: &mut ObjectProperty<'a>) { + self.x1_react.transform_object_property(prop); + walk_mut::walk_object_property_mut(self, prop); + } + + fn visit_export_default_declaration(&mut self, decl: &mut ExportDefaultDeclaration<'a>) { + self.x1_react.transform_export_default_declaration(decl); + walk_mut::walk_export_default_declaration_mut(self, decl); } } diff --git a/crates/oxc_transformer/src/react/display_name/mod.rs b/crates/oxc_transformer/src/react/display_name/mod.rs new file mode 100644 index 0000000000000..a3a029a9377ab --- /dev/null +++ b/crates/oxc_transformer/src/react/display_name/mod.rs @@ -0,0 +1,137 @@ +use std::rc::Rc; + +use oxc_allocator::Box; +use oxc_ast::ast::*; +use oxc_span::{Atom, SPAN}; + +use crate::context::Ctx; + +/// [plugin-transform-react-display-name](https://babeljs.io/docs/babel-plugin-transform-react-display-name) +/// +/// This plugin is included in `preset-react`. +/// +/// ## Example +/// +/// In: `var bar = createReactClass({});` +/// Out: `var bar = createReactClass({ displayName: "bar" });` +/// +/// NOTE: The current implementation uses the top-down approach on `AssignmentExpression`, `VariableDeclaration`, +/// but can be rewritten with a bottom-up approach. +/// See +pub struct ReactDisplayName<'a> { + ctx: Ctx<'a>, +} + +impl<'a> ReactDisplayName<'a> { + pub fn new(ctx: &Ctx<'a>) -> Self { + Self { ctx: Rc::clone(ctx) } + } +} + +// Transforms +impl<'a> ReactDisplayName<'a> { + /// `foo = React.createClass({})` + pub fn transform_assignment_expression(&self, assign_expr: &mut AssignmentExpression<'a>) { + let Some(obj_expr) = Self::get_object_from_create_class(&mut assign_expr.right) else { + return; + }; + let name = match &assign_expr.left { + AssignmentTarget::SimpleAssignmentTarget( + SimpleAssignmentTarget::AssignmentTargetIdentifier(ident), + ) => ident.name.clone(), + AssignmentTarget::SimpleAssignmentTarget( + SimpleAssignmentTarget::MemberAssignmentTarget(target), + ) => { + if let Some(name) = target.static_property_name() { + self.ctx.ast.new_atom(name) + } else { + return; + } + } + _ => return, + }; + self.add_display_name(obj_expr, name); + } + + /// `let foo = React.createClass({})` + pub fn transform_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) { + let Some(init_expr) = declarator.init.as_mut() else { return }; + let Some(obj_expr) = Self::get_object_from_create_class(init_expr) else { + return; + }; + let name = match &declarator.id.kind { + BindingPatternKind::BindingIdentifier(ident) => ident.name.clone(), + _ => return, + }; + self.add_display_name(obj_expr, name); + } + + /// `{foo: React.createClass({})}` + pub fn transform_object_property(&mut self, prop: &mut ObjectProperty<'a>) { + let Some(obj_expr) = Self::get_object_from_create_class(&mut prop.value) else { return }; + let Some(name) = prop.key.static_name() else { return }; + let name = self.ctx.ast.new_atom(&name); + self.add_display_name(obj_expr, name); + } + + /// `export default React.createClass({})` + /// Uses the current file name as the display name. + pub fn transform_export_default_declaration( + &mut self, + decl: &mut ExportDefaultDeclaration<'a>, + ) { + let ExportDefaultDeclarationKind::Expression(expr) = &mut decl.declaration else { return }; + let Some(obj_expr) = Self::get_object_from_create_class(expr) else { return }; + let name = self.ctx.ast.new_atom("input"); // TODO: use the filename + self.add_display_name(obj_expr, name); + } +} + +impl<'a> ReactDisplayName<'a> { + /// Get the object from `React.createClass({})` or `createReactClass({})` + fn get_object_from_create_class<'b>( + e: &'b mut Expression<'a>, + ) -> Option<&'b mut Box<'a, ObjectExpression<'a>>> { + let Expression::CallExpression(call_expr) = e else { return None }; + if match &call_expr.callee { + Expression::MemberExpression(e) => !e.is_specific_member_access("React", "createClass"), + Expression::Identifier(ident) => ident.name != "createReactClass", + _ => true, + } { + return None; + } + // Only 1 argument being the object expression. + if call_expr.arguments.len() != 1 { + return None; + } + let arg = call_expr.arguments.get_mut(0)?; + match arg { + Argument::SpreadElement(_) => None, + Argument::Expression(e) => match e { + Expression::ObjectExpression(obj_expr) => Some(obj_expr), + _ => None, + }, + } + } + + /// Add key value `displayName: name` to the `React.createClass` object. + fn add_display_name(&self, obj_expr: &mut ObjectExpression<'a>, name: Atom<'a>) { + const DISPLAY_NAME: &str = "displayName"; + // Not safe with existing display name. + let not_safe = obj_expr.properties.iter().any(|prop| { + matches!(prop, ObjectPropertyKind::ObjectProperty(p) if p.key.static_name().is_some_and(|name| name == DISPLAY_NAME)) + }); + if not_safe { + return; + } + let object_property = { + let kind = PropertyKind::Init; + let identifier_name = IdentifierName::new(SPAN, self.ctx.ast.new_atom(DISPLAY_NAME)); + let key = self.ctx.ast.property_key_identifier(identifier_name); + let string_literal = StringLiteral::new(SPAN, name); + let value = self.ctx.ast.literal_string_expression(string_literal); + self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false) + }; + obj_expr.properties.insert(0, ObjectPropertyKind::ObjectProperty(object_property)); + } +} diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react/jsx/mod.rs similarity index 64% rename from crates/oxc_transformer/src/react_jsx/mod.rs rename to crates/oxc_transformer/src/react/jsx/mod.rs index 0c65599a74a27..f1007b0afa2cb 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react/jsx/mod.rs @@ -1,6 +1,8 @@ -mod options; +use std::rc::Rc; -pub use self::options::ReactJsxOptions; +use crate::context::Ctx; + +pub use super::options::ReactOptions; /// [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx) /// @@ -15,14 +17,14 @@ pub use self::options::ReactJsxOptions; /// /// * /// * -#[derive(Debug, Default)] -pub struct ReactJsx { - #[allow(unused)] - options: ReactJsxOptions, +#[allow(unused)] +pub struct ReactJsx<'a> { + options: ReactOptions, + ctx: Ctx<'a>, } -impl ReactJsx { - pub fn new(options: ReactJsxOptions) -> Self { - Self { options } +impl<'a> ReactJsx<'a> { + pub fn new(options: ReactOptions, ctx: &Ctx<'a>) -> Self { + Self { options, ctx: Rc::clone(ctx) } } } diff --git a/crates/oxc_transformer/src/react/jsx_self/mod.rs b/crates/oxc_transformer/src/react/jsx_self/mod.rs new file mode 100644 index 0000000000000..d891216257eef --- /dev/null +++ b/crates/oxc_transformer/src/react/jsx_self/mod.rs @@ -0,0 +1,22 @@ +use std::rc::Rc; + +use crate::context::Ctx; + +/// [plugin-transform-react-jsx-self](https://babeljs.io/docs/babel-plugin-transform-react-jsx-self) +/// +/// This plugin is included in `preset-react`. +/// +/// ## Example +/// +/// In: `` +/// Out: `` +#[allow(unused)] +pub struct ReactJsxSelf<'a> { + ctx: Ctx<'a>, +} + +impl<'a> ReactJsxSelf<'a> { + pub fn new(ctx: &Ctx<'a>) -> Self { + Self { ctx: Rc::clone(ctx) } + } +} diff --git a/crates/oxc_transformer/src/react/jsx_source/mod.rs b/crates/oxc_transformer/src/react/jsx_source/mod.rs new file mode 100644 index 0000000000000..bfc54fb08c30e --- /dev/null +++ b/crates/oxc_transformer/src/react/jsx_source/mod.rs @@ -0,0 +1,15 @@ +use std::rc::Rc; + +use crate::context::Ctx; + +/// [plugin-transform-react-jsx-source](https://babeljs.io/docs/babel-plugin-transform-react-jsx-source) +#[allow(unused)] +pub struct ReactJsxSource<'a> { + ctx: Ctx<'a>, +} + +impl<'a> ReactJsxSource<'a> { + pub fn new(ctx: &Ctx<'a>) -> Self { + Self { ctx: Rc::clone(ctx) } + } +} diff --git a/crates/oxc_transformer/src/react/mod.rs b/crates/oxc_transformer/src/react/mod.rs new file mode 100644 index 0000000000000..b176ba9152ac1 --- /dev/null +++ b/crates/oxc_transformer/src/react/mod.rs @@ -0,0 +1,79 @@ +mod display_name; +mod jsx; +mod jsx_self; +mod jsx_source; +mod options; + +use std::rc::Rc; + +use oxc_ast::ast::*; + +use crate::context::Ctx; + +pub use self::{ + display_name::ReactDisplayName, jsx::ReactJsx, jsx_self::ReactJsxSelf, + jsx_source::ReactJsxSource, options::ReactOptions, +}; + +/// [Preset React](https://babel.dev/docs/babel-preset-react) +/// +/// This preset includes the following plugins: +/// +/// * [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx) +/// * [plugin-transform-react-jsx-self](https://babeljs.io/docs/babel-plugin-transform-react-jsx-self) +/// * [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx) +/// * [plugin-transform-react-display-name](https://babeljs.io/docs/babel-plugin-transform-react-display-name) +#[allow(unused)] +pub struct React<'a> { + ctx: Ctx<'a>, + jsx: ReactJsx<'a>, + jsx_self: ReactJsxSelf<'a>, + jsx_source: ReactJsxSource<'a>, + display_name: ReactDisplayName<'a>, +} + +// Constructors +impl<'a> React<'a> { + pub fn new(options: ReactOptions, ctx: &Ctx<'a>) -> Self { + Self { + ctx: Rc::clone(ctx), + jsx: ReactJsx::new(options, ctx), + jsx_self: ReactJsxSelf::new(ctx), + jsx_source: ReactJsxSource::new(ctx), + display_name: ReactDisplayName::new(ctx), + } + } +} + +// Transforms +impl<'a> React<'a> { + pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { + match expr { + Expression::AssignmentExpression(e) => { + self.display_name.transform_assignment_expression(e); + } + Expression::JSXElement(_e) => { + // *expr = unimplemented!(); + } + Expression::JSXFragment(_e) => { + // *expr = unimplemented!(); + } + _ => {} + } + } + + pub fn transform_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) { + self.display_name.transform_variable_declarator(declarator); + } + + pub fn transform_object_property(&mut self, prop: &mut ObjectProperty<'a>) { + self.display_name.transform_object_property(prop); + } + + pub fn transform_export_default_declaration( + &mut self, + decl: &mut ExportDefaultDeclaration<'a>, + ) { + self.display_name.transform_export_default_declaration(decl); + } +} diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react/options.rs similarity index 97% rename from crates/oxc_transformer/src/react_jsx/options.rs rename to crates/oxc_transformer/src/react/options.rs index b7b2c700fec49..f765599a4ce39 100644 --- a/crates/oxc_transformer/src/react_jsx/options.rs +++ b/crates/oxc_transformer/src/react/options.rs @@ -4,7 +4,7 @@ use serde::Deserialize; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ReactJsxOptions { +pub struct ReactOptions { // Both Runtimes // /// Decides which runtime to use. @@ -53,7 +53,7 @@ pub struct ReactJsxOptions { // `useBuiltIns` and `useSpread` are deprecated in babel 8. } -impl Default for ReactJsxOptions { +impl Default for ReactOptions { fn default() -> Self { Self { runtime: ReactJsxRuntime::default(), diff --git a/crates/oxc_transformer/src/react_display_name/mod.rs b/crates/oxc_transformer/src/react_display_name/mod.rs deleted file mode 100644 index 8a417cd8a5ff0..0000000000000 --- a/crates/oxc_transformer/src/react_display_name/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Default, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReactDisplayNameOptions; - -/// [plugin-transform-react-display-name](https://babeljs.io/docs/babel-plugin-transform-react-display-name) -/// -/// This plugin is included in `preset-react`. -/// -/// ## Example -/// -/// In: `var bar = createReactClass({});` -/// Out: `var bar = createReactClass({ displayName: "bar" });` -#[derive(Debug, Default)] -pub struct ReactDisplayName { - #[allow(unused)] - options: ReactDisplayNameOptions, -} - -impl ReactDisplayName { - pub fn new(options: ReactDisplayNameOptions) -> Self { - Self { options } - } -} diff --git a/crates/oxc_transformer/src/react_jsx_self/mod.rs b/crates/oxc_transformer/src/react_jsx_self/mod.rs deleted file mode 100644 index 447dd2b02df13..0000000000000 --- a/crates/oxc_transformer/src/react_jsx_self/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Default, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReactJsxSelfOptions; - -/// [plugin-transform-react-jsx-self](https://babeljs.io/docs/babel-plugin-transform-react-jsx-self) -/// -/// This plugin is included in `preset-react`. -/// -/// ## Example -/// -/// In: `` -/// Out: `` -#[derive(Debug, Default)] -pub struct ReactJsxSelf { - #[allow(unused)] - options: ReactJsxSelfOptions, -} - -impl ReactJsxSelf { - pub fn new(options: ReactJsxSelfOptions) -> Self { - Self { options } - } -} diff --git a/crates/oxc_transformer/src/react_jsx_source/mod.rs b/crates/oxc_transformer/src/react_jsx_source/mod.rs deleted file mode 100644 index ea240fd6555bd..0000000000000 --- a/crates/oxc_transformer/src/react_jsx_source/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Default, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReactJsxSourceOptions; - -/// [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx) -/// -/// This plugin generates production-ready JS code. -/// -/// If you are developing a React app in a development environment, -/// please use @babel/plugin-transform-react-jsx-development for a better debugging experience. -/// -/// This plugin is included in `preset-react`. -/// -/// ## Example -/// -/// In: `` -/// Out: `` -#[derive(Debug, Default)] -pub struct ReactJsxSource { - #[allow(unused)] - options: ReactJsxSourceOptions, -} - -impl ReactJsxSource { - pub fn new(options: ReactJsxSourceOptions) -> Self { - Self { options } - } -} diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index 4b36a6e1e6e6a..52206151981e8 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -1,10 +1,20 @@ +use std::rc::Rc; + use serde::Deserialize; +use oxc_ast::ast::*; + +use crate::context::Ctx; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TypeScriptOptions; -/// [plugin-transform-typescript](https://babeljs.io/docs/babel-plugin-transform-typescript) +/// [Preset TypeScript](https://babeljs.io/docs/babel-preset-typescript) +/// +/// This preset includes the following plugins: +/// +/// * [transform-typescript](https://babeljs.io/docs/babel-plugin-transform-typescript) /// /// This plugin adds support for the types syntax used by the TypeScript programming language. /// However, this plugin does not add the ability to type-check the JavaScript passed to it. @@ -21,14 +31,19 @@ pub struct TypeScriptOptions; /// /// In: `const x: number = 0;` /// Out: `const x = 0;` -#[derive(Debug, Default)] -pub struct TypeScript { - #[allow(unused)] +#[allow(unused)] +pub struct TypeScript<'a> { options: TypeScriptOptions, + ctx: Ctx<'a>, } -impl TypeScript { - pub fn new(options: TypeScriptOptions) -> Self { - Self { options } +impl<'a> TypeScript<'a> { + pub fn new(options: TypeScriptOptions, ctx: &Ctx<'a>) -> Self { + Self { options, ctx: Rc::clone(ctx) } } } + +// Transformers +impl<'a> TypeScript<'a> { + pub fn transform_statement(&mut self, _stmt: &mut Statement<'a>) {} +} diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 944ff3d83e775..94a810c72466b 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -2,7 +2,7 @@ "name": "oxc-vscode", "description": "oxc vscode extension", "license": "MIT", - "version": "0.2.16", + "version": "0.2.17", "icon": "icon.png", "publisher": "oxc", "displayName": "Oxc", diff --git a/npm/.gitignore b/npm/.gitignore new file mode 100644 index 0000000000000..54caab448560b --- /dev/null +++ b/npm/.gitignore @@ -0,0 +1 @@ +parser-wasm diff --git a/npm/oxlint/package.json b/npm/oxlint/package.json index 98f2266e0443b..efb692e5c66a0 100644 --- a/npm/oxlint/package.json +++ b/npm/oxlint/package.json @@ -1,6 +1,6 @@ { "name": "oxlint", - "version": "0.2.16", + "version": "0.2.17", "description": "Linter for the JavaScript Oxidation Compiler", "keywords": [], "author": "Boshen and oxc contributors", diff --git a/tasks/benchmark/benches/sourcemap.rs b/tasks/benchmark/benches/sourcemap.rs index 9bdf9694b8dbf..e13f9ad935f0c 100644 --- a/tasks/benchmark/benches/sourcemap.rs +++ b/tasks/benchmark/benches/sourcemap.rs @@ -10,7 +10,7 @@ use oxc_tasks_common::TestFiles; fn bench_sourcemap(criterion: &mut Criterion) { let mut group = criterion.benchmark_group("sourcemap"); - for file in TestFiles::minimal().files() { + for file in TestFiles::complicated_one(1).files() { let id = BenchmarkId::from_parameter(&file.file_name); let source_type = SourceType::from_path(&file.file_name).unwrap(); group.bench_with_input(id, &file.source_text, |b, source_text| { @@ -18,7 +18,7 @@ fn bench_sourcemap(criterion: &mut Criterion) { let program = Parser::new(&allocator, source_text, source_type).parse().program; let codegen_options = CodegenOptions { enable_source_map: true, ..CodegenOptions::default() }; - b.iter_with_large_drop(|| { + b.iter(|| { let CodegenReturn { source_map, source_text } = Codegen::::new( file.file_name.as_str(), source_text, diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 1b0b67847b2cf..629f6596874b1 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,1409 +1,9 @@ -Passed: 72/1415 +Passed: 15/16 # All Passed: -* babel-plugin-transform-property-literals -# babel-plugin-transform-unicode-sets-regex (0/4) -* basic/basic/input.js -* basic/string-properties/input.js -* transform-u/basic/input.js -* transform-u/string-properties/input.js -# babel-plugin-transform-class-properties (0/271) -* assumption-constantSuper/complex-super-class/input.js -* assumption-constantSuper/instance-field/input.js -* assumption-constantSuper/static-field/input.js -* assumption-noDocumentAll/optional-chain-before-member-call/input.js -* assumption-noDocumentAll/optional-chain-cast-to-boolean/input.js -* assumption-noUninitializedPrivateFieldAccess/static-private/input.js -* assumption-setPublicClassFields/computed/input.js -* assumption-setPublicClassFields/constructor-collision/input.js -* assumption-setPublicClassFields/derived/input.js -* assumption-setPublicClassFields/foobar/input.js -* assumption-setPublicClassFields/instance/input.js -* assumption-setPublicClassFields/instance-computed/input.js -* assumption-setPublicClassFields/instance-undefined/input.js -* assumption-setPublicClassFields/length-name-use-define/input.js -* assumption-setPublicClassFields/non-block-arrow-func/input.mjs -* assumption-setPublicClassFields/regression-T2983/input.mjs -* assumption-setPublicClassFields/regression-T6719/input.js -* assumption-setPublicClassFields/regression-T7364/input.mjs -* assumption-setPublicClassFields/static/input.js -* assumption-setPublicClassFields/static-class-binding/input.js -* assumption-setPublicClassFields/static-export/input.mjs -* assumption-setPublicClassFields/static-infer-name/input.js -* assumption-setPublicClassFields/static-super/input.js -* assumption-setPublicClassFields/static-super-loose/input.js -* assumption-setPublicClassFields/static-this/input.js -* assumption-setPublicClassFields/static-undefined/input.js -* assumption-setPublicClassFields/super-call/input.js -* assumption-setPublicClassFields/super-expression/input.js -* assumption-setPublicClassFields/super-statement/input.js -* assumption-setPublicClassFields/super-with-collision/input.js -* class-name-tdz/decorator-interop/input.js -* class-name-tdz/general/input.js -* class-name-tdz/static-edgest-case/input.js -* class-name-tdz/static-general/input.js -* class-name-tdz/static-loose/input.js -* compile-to-class/constructor-collision-ignores-types/input.js -* compile-to-class/constructor-collision-ignores-types-loose/input.js -* compile-to-class/preserve-comments/input.js -* decorators-legacy-interop/local-define-property/input.js -* decorators-legacy-interop/loose/input.js -* decorators-legacy-interop/strict/input.js -* decorators-legacy-interop/wrong-order/input.js -* nested-class/super-call-in-decorator/input.js -* nested-class/super-call-in-key/input.js -* nested-class/super-property-in-accessor-key/input.js -* nested-class/super-property-in-decorator/input.js -* nested-class/super-property-in-key/input.js -* private/1-helpermemberexpressionfunction/input.js -* private/assignment/input.js -* private/call/input.js -* private/canonical/input.js -* private/class-shadow-builtins/input.mjs -* private/constructor-collision/input.js -* private/declaration-order/input.js -* private/derived/input.js -* private/derived-multiple-supers/input.js -* private/destructuring-array-pattern/input.js -* private/destructuring-array-pattern-1/input.js -* private/destructuring-array-pattern-2/input.js -* private/destructuring-array-pattern-3/input.js -* private/destructuring-array-pattern-static/input.js -* private/destructuring-object-pattern/input.js -* private/destructuring-object-pattern-1/input.js -* private/destructuring-object-pattern-2/input.js -* private/destructuring-object-pattern-3/input.js -* private/destructuring-object-pattern-static/input.js -* private/extracted-this/input.js -* private/foobar/input.js -* private/instance/input.js -* private/instance-undefined/input.js -* private/logical-assignment/input.js -* private/multiple/input.js -* private/native-classes/input.js -* private/nested-class/input.js -* private/nested-class-computed/input.js -* private/nested-class-computed-redeclared/input.js -* private/nested-class-extends-computed/input.js -* private/nested-class-extends-computed-redeclared/input.js -* private/nested-class-other-redeclared/input.js -* private/nested-class-redeclared/input.js -* private/non-block-arrow-func/input.mjs -* private/optional-chain-before-member-call/input.js -* private/optional-chain-before-member-call-with-transform/input.js -* private/optional-chain-before-property/input.js -* private/optional-chain-before-property-with-transform/input.js -* private/optional-chain-cast-to-boolean/input.js -* private/optional-chain-delete-property/input.js -* private/optional-chain-delete-property-with-transform/input.js -* private/optional-chain-in-function-param/input.js -* private/optional-chain-in-function-param-with-transform/input.js -* private/optional-chain-member-optional-call/input.js -* private/optional-chain-member-optional-call-spread-arguments/input.js -* private/optional-chain-member-optional-call-with-transform/input.js -* private/optional-chain-optional-member-call/input.js -* private/optional-chain-optional-member-call-with-transform/input.js -* private/optional-chain-optional-property/input.js -* private/optional-chain-optional-property-with-transform/input.js -* private/parenthesized-optional-member-call/input.js -* private/parenthesized-optional-member-call-with-transform/input.js -* private/preserve-comments/input.js -* private/private-in-derived/input.js -* private/reevaluated/input.js -* private/reference-in-other-property/input.js -* private/regression-T2983/input.mjs -* private/regression-T6719/input.js -* private/regression-T7364/input.mjs -* private/static/input.js -* private/static-call/input.js -* private/static-class-binding/input.js -* private/static-export/input.mjs -* private/static-infer-name/input.js -* private/static-inherited/input.js -* private/static-self-field/input.js -* private/static-self-method/input.js -* private/static-shadow/input.js -* private/static-this/input.js -* private/static-undefined/input.js -* private/super-call/input.js -* private/super-expression/input.js -* private/super-statement/input.js -* private/tagged-template/input.js -* private/tagged-template-static/input.js -* private/update/input.js -* private-loose/assignment/input.js -* private-loose/call/input.js -* private-loose/canonical/input.js -* private-loose/class-shadow-builtins/input.mjs -* private-loose/constructor-collision/input.js -* private-loose/declaration-order/input.js -* private-loose/derived/input.js -* private-loose/derived-multiple-supers/input.js -* private-loose/destructuring-array-pattern/input.js -* private-loose/destructuring-array-pattern-1/input.js -* private-loose/destructuring-array-pattern-2/input.js -* private-loose/destructuring-array-pattern-3/input.js -* private-loose/destructuring-array-pattern-static/input.js -* private-loose/destructuring-object-pattern/input.js -* private-loose/destructuring-object-pattern-1/input.js -* private-loose/destructuring-object-pattern-2/input.js -* private-loose/destructuring-object-pattern-3/input.js -* private-loose/destructuring-object-pattern-static/input.js -* private-loose/extracted-this/input.js -* private-loose/foobar/input.js -* private-loose/instance/input.js -* private-loose/instance-undefined/input.js -* private-loose/logical-assignment/input.js -* private-loose/multiple/input.js -* private-loose/native-classes/input.js -* private-loose/nested-class/input.js -* private-loose/nested-class-computed/input.js -* private-loose/nested-class-computed-redeclared/input.js -* private-loose/nested-class-extends-computed/input.js -* private-loose/nested-class-extends-computed-redeclared/input.js -* private-loose/nested-class-other-redeclared/input.js -* private-loose/nested-class-redeclared/input.js -* private-loose/non-block-arrow-func/input.mjs -* private-loose/optional-chain-before-member-call/input.js -* private-loose/optional-chain-before-member-call-with-transform/input.js -* private-loose/optional-chain-before-property/input.js -* private-loose/optional-chain-before-property-with-transform/input.js -* private-loose/optional-chain-cast-to-boolean/input.js -* private-loose/optional-chain-delete-property/input.js -* private-loose/optional-chain-delete-property-with-transform/input.js -* private-loose/optional-chain-in-function-param/input.js -* private-loose/optional-chain-in-function-param-with-transform/input.js -* private-loose/optional-chain-member-optional-call/input.js -* private-loose/optional-chain-member-optional-call-spread-arguments/input.js -* private-loose/optional-chain-member-optional-call-with-transform/input.js -* private-loose/optional-chain-optional-member-call/input.js -* private-loose/optional-chain-optional-member-call-with-transform/input.js -* private-loose/optional-chain-optional-property/input.js -* private-loose/optional-chain-optional-property-with-transform/input.js -* private-loose/parenthesized-optional-member-call/input.js -* private-loose/parenthesized-optional-member-call-with-transform/input.js -* private-loose/preserve-comments/input.js -* private-loose/private-in-derived/input.js -* private-loose/reevaluated/input.js -* private-loose/reference-in-other-property/input.js -* private-loose/static/input.js -* private-loose/static-call/input.js -* private-loose/static-class-binding/input.js -* private-loose/static-export/input.mjs -* private-loose/static-infer-name/input.js -* private-loose/static-inherited/input.js -* private-loose/static-shadow/input.js -* private-loose/static-this/input.js -* private-loose/static-undefined/input.js -* private-loose/super-expression/input.js -* private-loose/super-statement/input.js -* private-loose/update/input.js -* public/arrow-static-this-without-transform/input.js -* public/arrow-this-without-transform/input.js -* public/assignment/input.js -* public/call/input.js -* public/class-shadow-builtins/input.mjs -* public/computed/input.js -* public/computed-toPrimitive/input.js -* public/computed-without-block/input.js -* public/constructor-collision/input.js -* public/delete-super-property/input.js -* public/delete-this/input.js -* public/derived/input.js -* public/derived-multiple-supers/input.js -* public/derived-super-in-default-params/input.js -* public/derived-super-in-default-params-complex/input.js -* public/derived-super-in-default-params-in-arrow/input.js -* public/extracted-this/input.js -* public/foobar/input.js -* public/instance/input.js -* public/instance-computed/input.js -* public/instance-undefined/input.js -* public/native-classes/input.js -* public/non-block-arrow-func/input.mjs -* public/numeric/input.js -* public/preserve-comments/input.js -* public/regression-T2983/input.mjs -* public/regression-T6719/input.js -* public/regression-T7364/input.mjs -* public/static/input.js -* public/static-class-binding/input.js -* public/static-export/input.mjs -* public/static-infer-name/input.js -* public/static-super/input.js -* public/static-this/input.js -* public/static-undefined/input.js -* public/super-call/input.js -* public/super-destructuring-array-pattern/input.js -* public/super-destructuring-array-pattern-1/input.js -* public/super-destructuring-object-pattern/input.js -* public/super-destructuring-object-pattern-1/input.js -* public/super-expression/input.js -* public/super-statement/input.js -* public/super-with-collision/input.js -* public/update/input.js -* public-loose/arrow-static-this-without-transform/input.js -* public-loose/arrow-this-without-transform/input.js -* public-loose/class-shadow-builtins/input.mjs -* public-loose/computed/input.js -* public-loose/constructor-collision/input.js -* public-loose/derived/input.js -* public-loose/foobar/input.js -* public-loose/instance/input.js -* public-loose/instance-computed/input.js -* public-loose/instance-undefined/input.js -* public-loose/non-block-arrow-func/input.mjs -* public-loose/preserve-comments/input.js -* public-loose/regression-T2983/input.mjs -* public-loose/regression-T6719/input.js -* public-loose/regression-T7364/input.mjs -* public-loose/static/input.js -* public-loose/static-class-binding/input.js -* public-loose/static-export/input.mjs -* public-loose/static-infer-name/input.js -* public-loose/static-super/input.js -* public-loose/static-this/input.js -* public-loose/static-undefined/input.js -* public-loose/super-call/input.js -* public-loose/super-expression/input.js -* public-loose/super-statement/input.js -* public-loose/super-with-collision/input.js -* regression/15098/input.js -* regression/6153/input.js -* regression/6154/input.js -* regression/7371/input.js -* regression/7951/input.mjs -* regression/8110/input.js -* regression/8882/input.js -* regression/T2983/input.mjs -* regression/T6719/input.js -* regression/T7364/input.mjs -* regression/multiple-super-in-termary/input.js - -# babel-plugin-transform-class-static-block (0/21) -* class-static-block/class-binding/input.js -* class-static-block/class-declaration/input.js -* class-static-block/in-class-heritage/input.js -* class-static-block/multiple-static-initializers/input.js -* class-static-block/name-conflict/input.js -* class-static-block/new-target/input.js -* class-static-block/preserve-comments/input.js -* integration/class-binding/input.js -* integration/class-declaration/input.js -* integration/in-class-heritage/input.js -* integration/multiple-static-initializers/input.js -* integration/name-conflict/input.js -* integration/new-target/input.js -* integration/preserve-comments/input.js -* integration-loose/class-binding/input.js -* integration-loose/class-declaration/input.js -* integration-loose/in-class-heritage/input.js -* integration-loose/multiple-static-initializers/input.js -* integration-loose/name-conflict/input.js -* integration-loose/preserve-comments/input.js -* integration-loose/super-static-block/input.js - -# babel-plugin-transform-private-methods (0/148) -* accessors/arguments/input.js -* accessors/basic/input.js -* accessors/class-binding/input.js -* accessors/destructuring/input.js -* accessors/get-only-setter/input.js -* accessors/preserve-comments/input.js -* accessors/reassignment/input.js -* accessors/set-only-getter/input.js -* accessors/tagged-template/input.js -* accessors/updates/input.js -* accessors/updates-bigint/input.js -* accessors-loose/basic/input.js -* accessors-loose/class-binding/input.js -* accessors-loose/get-only-setter/input.js -* accessors-loose/preserve-comments/input.js -* accessors-loose/reassignment/input.js -* accessors-loose/set-only-getter/input.js -* accessors-loose/updates/input.js -* accessors-privateFieldsAsProperties/basic/input.js -* accessors-privateFieldsAsProperties/class-binding/input.js -* accessors-privateFieldsAsProperties/get-only-setter/input.js -* accessors-privateFieldsAsProperties/preserve-comments/input.js -* accessors-privateFieldsAsProperties/set-only-getter/input.js -* accessors-privateFieldsAsProperties/updates/input.js -* accessors-privateFieldsAsSymbols/basic/input.js -* accessors-privateFieldsAsSymbols/class-binding/input.js -* accessors-privateFieldsAsSymbols/get-only-setter/input.js -* accessors-privateFieldsAsSymbols/preserve-comments/input.js -* accessors-privateFieldsAsSymbols/set-only-getter/input.js -* accessors-privateFieldsAsSymbols/updates/input.js -* assumption-constantSuper/private-method-super/input.js -* duplicated-names/get-get/input.js -* duplicated-names/get-method/input.js -* duplicated-names/get-set/input.js -* duplicated-names/method-get/input.js -* duplicated-names/method-method/input.js -* duplicated-names/method-set/input.js -* duplicated-names/set-get/input.js -* duplicated-names/set-method/input.js -* duplicated-names/set-set/input.js -* misc/multiple/input.js -* private-method/assignment/input.js -* private-method/async/input.js -* private-method/before-fields/input.js -* private-method/class-binding/input.js -* private-method/class-expression/input.js -* private-method/context/input.js -* private-method/destructuring/input.js -* private-method/exfiltrated/input.js -* private-method/generator/input.js -* private-method/preserve-comments/input.js -* private-method/read-only/input.js -* private-method/reassignment/input.js -* private-method/super/input.js -* private-method/tagged-template/input.js -* private-method-loose/assignment/input.js -* private-method-loose/async/input.js -* private-method-loose/before-fields/input.js -* private-method-loose/class-binding/input.js -* private-method-loose/class-expression/input.js -* private-method-loose/context/input.js -* private-method-loose/exfiltrated/input.js -* private-method-loose/generator/input.js -* private-method-loose/preserve-comments/input.js -* private-method-loose/reassignment/input.js -* private-method-loose/super/input.js -* private-method-privateFieldsAsProperties/assignment/input.js -* private-method-privateFieldsAsProperties/async/input.js -* private-method-privateFieldsAsProperties/before-fields/input.js -* private-method-privateFieldsAsProperties/class-binding/input.js -* private-method-privateFieldsAsProperties/class-expression/input.js -* private-method-privateFieldsAsProperties/context/input.js -* private-method-privateFieldsAsProperties/exfiltrated/input.js -* private-method-privateFieldsAsProperties/generator/input.js -* private-method-privateFieldsAsProperties/super/input.js -* private-method-privateFieldsAsSymbols/assignment/input.js -* private-method-privateFieldsAsSymbols/async/input.js -* private-method-privateFieldsAsSymbols/before-fields/input.js -* private-method-privateFieldsAsSymbols/class-binding/input.js -* private-method-privateFieldsAsSymbols/class-expression/input.js -* private-method-privateFieldsAsSymbols/context/input.js -* private-method-privateFieldsAsSymbols/exfiltrated/input.js -* private-method-privateFieldsAsSymbols/generator/input.js -* private-method-privateFieldsAsSymbols/super/input.js -* private-static-method/async/input.js -* private-static-method/basic/input.js -* private-static-method/class-check/input.js -* private-static-method/class-expression/input.js -* private-static-method/exfiltrated/input.js -* private-static-method/generator/input.js -* private-static-method/preserve-comments/input.js -* private-static-method/read-only/input.js -* private-static-method/super/input.js -* private-static-method/tagged-template/input.js -* private-static-method/this/input.js -* private-static-method-loose/async/input.js -* private-static-method-loose/basic/input.js -* private-static-method-loose/class-check/input.js -* private-static-method-loose/class-expression/input.js -* private-static-method-loose/exfiltrated/input.js -* private-static-method-loose/generator/input.js -* private-static-method-loose/preserve-comments/input.js -* private-static-method-loose/reassignment/input.js -* private-static-method-loose/super/input.js -* private-static-method-loose/this/input.js -* private-static-method-privateFieldsAsProperties/async/input.js -* private-static-method-privateFieldsAsProperties/basic/input.js -* private-static-method-privateFieldsAsProperties/class-check/input.js -* private-static-method-privateFieldsAsProperties/class-expression/input.js -* private-static-method-privateFieldsAsProperties/exfiltrated/input.js -* private-static-method-privateFieldsAsProperties/generator/input.js -* private-static-method-privateFieldsAsProperties/reassignment/input.js -* private-static-method-privateFieldsAsProperties/super/input.js -* private-static-method-privateFieldsAsProperties/this/input.js -* private-static-method-privateFieldsAsSymbols/async/input.js -* private-static-method-privateFieldsAsSymbols/basic/input.js -* private-static-method-privateFieldsAsSymbols/class-check/input.js -* private-static-method-privateFieldsAsSymbols/class-expression/input.js -* private-static-method-privateFieldsAsSymbols/exfiltrated/input.js -* private-static-method-privateFieldsAsSymbols/generator/input.js -* private-static-method-privateFieldsAsSymbols/reassignment/input.js -* private-static-method-privateFieldsAsSymbols/super/input.js -* private-static-method-privateFieldsAsSymbols/this/input.js -* static-accessors/basic/input.js -* static-accessors/destructure-set/input.js -* static-accessors/get-only-setter/input.js -* static-accessors/preserve-comments/input.js -* static-accessors/set-only-getter/input.js -* static-accessors/tagged-template/input.js -* static-accessors/updates/input.js -* static-accessors-loose/basic/input.js -* static-accessors-loose/destructure-set/input.js -* static-accessors-loose/get-only-setter/input.js -* static-accessors-loose/preserve-comments/input.js -* static-accessors-loose/set-only-getter/input.js -* static-accessors-loose/updates/input.js -* static-accessors-privateFieldsAsProperties/basic/input.js -* static-accessors-privateFieldsAsProperties/destructure-set/input.js -* static-accessors-privateFieldsAsProperties/get-only-setter/input.js -* static-accessors-privateFieldsAsProperties/preserve-comments/input.js -* static-accessors-privateFieldsAsProperties/set-only-getter/input.js -* static-accessors-privateFieldsAsProperties/updates/input.js -* static-accessors-privateFieldsAsSymbols/basic/input.js -* static-accessors-privateFieldsAsSymbols/destructure-set/input.js -* static-accessors-privateFieldsAsSymbols/get-only-setter/input.js -* static-accessors-privateFieldsAsSymbols/preserve-comments/input.js -* static-accessors-privateFieldsAsSymbols/set-only-getter/input.js -* static-accessors-privateFieldsAsSymbols/updates/input.js - -# babel-plugin-transform-private-property-in-object (0/59) -* assumption-privateFieldsAsProperties/accessor/input.js -* assumption-privateFieldsAsProperties/compiled-classes/input.js -* assumption-privateFieldsAsProperties/field/input.js -* assumption-privateFieldsAsProperties/method/input.js -* assumption-privateFieldsAsProperties/nested-class/input.js -* assumption-privateFieldsAsProperties/nested-class-other-redeclared/input.js -* assumption-privateFieldsAsProperties/nested-class-redeclared/input.js -* assumption-privateFieldsAsProperties/static-accessor/input.js -* assumption-privateFieldsAsProperties/static-field/input.js -* assumption-privateFieldsAsProperties/static-method/input.js -* assumption-privateFieldsAsSymbols/accessor/input.js -* assumption-privateFieldsAsSymbols/compiled-classes/input.js -* assumption-privateFieldsAsSymbols/field/input.js -* assumption-privateFieldsAsSymbols/method/input.js -* assumption-privateFieldsAsSymbols/nested-class/input.js -* assumption-privateFieldsAsSymbols/nested-class-other-redeclared/input.js -* assumption-privateFieldsAsSymbols/nested-class-redeclared/input.js -* assumption-privateFieldsAsSymbols/static-accessor/input.js -* assumption-privateFieldsAsSymbols/static-field/input.js -* assumption-privateFieldsAsSymbols/static-method/input.js -* private/accessor/input.js -* private/field/input.js -* private/method/input.js -* private/native-classes/input.js -* private/nested-class/input.js -* private/nested-class-other-redeclared/input.js -* private/nested-class-redeclared/input.js -* private/static-accessor/input.js -* private/static-field/input.js -* private/static-method/input.js -* private/static-shadow/input.js -* private-loose/accessor/input.js -* private-loose/field/input.js -* private-loose/method/input.js -* private-loose/native-classes/input.js -* private-loose/nested-class/input.js -* private-loose/nested-class-other-redeclared/input.js -* private-loose/nested-class-redeclared/input.js -* private-loose/static-accessor/input.js -* private-loose/static-field/input.js -* private-loose/static-method/input.js -* private-loose/static-shadow/input.js -* to-native-fields/accessor/input.js -* to-native-fields/class-expression-in-default-param/input.js -* to-native-fields/class-expression-instance/input.js -* to-native-fields/class-expression-static/input.js -* to-native-fields/field/input.js -* to-native-fields/half-constructed-instance/input.js -* to-native-fields/half-constructed-static/input.js -* to-native-fields/method/input.js -* to-native-fields/multiple-checks/input.js -* to-native-fields/nested-class/input.js -* to-native-fields/nested-class-other-redeclared/input.js -* to-native-fields/nested-class-redeclared/input.js -* to-native-fields/static-accessor/input.js -* to-native-fields/static-field/input.js -* to-native-fields/static-method/input.js -* to-native-fields/static-shadow/input.js -* to-native-fields/static-shadowed-binding/input.js - -# babel-plugin-transform-logical-assignment-operators (0/6) -* logical-assignment/anonymous-functions-transform/input.js -* logical-assignment/arrow-functions-transform/input.js -* logical-assignment/general-semantics/input.js -* logical-assignment/named-functions-transform/input.js -* logical-assignment/null-coalescing/input.js -* logical-assignment/null-coalescing-without-other/input.js - -# babel-plugin-transform-numeric-separator (0/2) -* numeric-separator/removal/bigint/input.js -* numeric-separator/used-with-transform-literals/input.js - -# babel-plugin-transform-export-namespace-from (0/4) -* export-namespace/namespace-default/input.mjs -* export-namespace/namespace-es6/input.mjs -* export-namespace/namespace-string/input.mjs -* export-namespace/namespace-typescript/input.mjs - -# babel-plugin-transform-dynamic-import (0/35) -* amd/missing-plugin/input.mjs -* amd/module/input.mjs -* amd/no-interop/input.js -* amd/script/input.js -* amd/to-string/input.js -* amd/with-other-import-export/input.mjs -* amd-createImportExpression-false/missing-plugin/input.mjs -* amd-createImportExpression-false/module/input.mjs -* amd-createImportExpression-false/no-interop/input.js -* amd-createImportExpression-false/script/input.js -* amd-createImportExpression-false/to-string/input.js -* amd-createImportExpression-false/with-other-import-export/input.mjs -* commonjs/missing-plugin/input.mjs -* commonjs/module/input.mjs -* commonjs/no-interop/input.js -* commonjs/script/input.js -* commonjs/shadowed-require/input.js -* commonjs/template-literal/input.js -* commonjs/to-string/input.js -* commonjs-createImportExpression-false/missing-plugin/input.mjs -* commonjs-createImportExpression-false/module/input.mjs -* commonjs-createImportExpression-false/no-interop/input.js -* commonjs-createImportExpression-false/script/input.js -* commonjs-createImportExpression-false/shadowed-require/input.js -* commonjs-createImportExpression-false/template-literal/input.js -* commonjs-createImportExpression-false/to-string/input.js -* missing-module-transform/missing-module-transform/input.js -* systemjs/missing-plugin/input.mjs -* systemjs/module/input.mjs -* systemjs/script/input.js -* systemjs/to-string/input.js -* systemjs-createImportExpression-false/missing-plugin/input.mjs -* systemjs-createImportExpression-false/module/input.mjs -* systemjs-createImportExpression-false/script/input.js -* systemjs-createImportExpression-false/to-string/input.js - -# babel-plugin-transform-nullish-coalescing-operator (0/12) -* assumption-noDocumentAll/transform/input.js -* assumption-noDocumentAll/transform-in-default-destructuring/input.js -* assumption-noDocumentAll/transform-in-default-param/input.js -* assumption-noDocumentAll/transform-in-function/input.js -* assumption-noDocumentAll/transform-static-refs-in-default/input.js -* assumption-noDocumentAll/transform-static-refs-in-function/input.js -* nullish-coalescing/transform-in-default-destructuring/input.js -* nullish-coalescing/transform-in-default-param/input.js -* nullish-coalescing/transform-in-function/input.js -* nullish-coalescing/transform-loose/input.js -* nullish-coalescing/transform-static-refs-in-default/input.js -* nullish-coalescing/transform-static-refs-in-function/input.js - -# babel-plugin-transform-optional-chaining (1/45) -* assumption-noDocumentAll/assignment/input.js -* assumption-noDocumentAll/cast-to-boolean/input.js -* assumption-noDocumentAll/in-function-params/input.js -* assumption-noDocumentAll/memoize/input.js -* assumption-noDocumentAll/optional-eval-call/input.js -* assumption-noDocumentAll/super-method-call/input.js -* assumption-pureGetters/function-call/input.js -* assumption-pureGetters/memoize/input.js -* assumption-pureGetters/super-method-call/input.js -* general/assignment/input.js -* general/cast-to-boolean/input.js -* general/containers/input.js -* general/delete/input.js -* general/delete-in-function-params/input.js -* general/function-call/input.js -* general/function-call-loose/input.js -* general/function-call-spread/input.js -* general/in-function-params/input.js -* general/in-function-params-loose/input.js -* general/in-method-key/input.js -* general/in-method-key-loose/input.js -* general/in-var-destructuring/input.js -* general/member-access/input.js -* general/memoize/input.js -* general/memoize-loose/input.js -* general/optional-eval-call/input.js -* general/optional-eval-call-loose/input.js -* general/parenthesized-expression-containers/input.js -* general/parenthesized-member-call/input.js -* general/parenthesized-member-call-loose/input.js -* general/super-method-call/input.js -* general/super-method-call-loose/input.js -* general/unary/input.js -* loose/cast-to-boolean/input.js -* regression/10959-transform-optional-chaining/input.ts -* regression/10959-transform-ts-and-optional-chaining/input.ts -* regression/15887/input.js -* regression/7642/input.js -* transparent-expr-wrappers/ts-as-call-context/input.ts -* transparent-expr-wrappers/ts-as-call-context-in-if/input.ts -* transparent-expr-wrappers/ts-as-function-call-loose/input.ts -* transparent-expr-wrappers/ts-as-in-conditional/input.ts -* transparent-expr-wrappers/ts-as-member-expression/input.ts -* transparent-expr-wrappers/ts-parenthesized-expression-member-call/input.ts - -# babel-plugin-transform-optional-catch-binding (2/4) -* optional-catch-bindings/try-catch-block-no-binding/input.js -* optional-catch-bindings/try-catch-finally-no-binding/input.js - -# babel-plugin-transform-json-strings (2/4) -* json-strings/directive-line-separator/input.js -* json-strings/directive-paragraph-separator/input.js - -# babel-plugin-transform-async-generator-functions (0/22) -* async-generators/class-method/input.js -* async-generators/class-private-method/input.js -* async-generators/declaration/input.js -* async-generators/expression/input.js -* async-generators/object-method/input.js -* async-generators/static-method/input.js -* async-generators/transform-class-keys/input.js -* async-generators/yield-star/input.js -* for-await/async-arrow/input.js -* for-await/async-function/input.js -* for-await/async-function-no-transform/input.js -* for-await/async-generator/input.js -* for-await/create-async-from-sync-iterator/input.js -* for-await/destructuring/input.js -* for-await/lhs-member-expression/input.js -* for-await/re-declare-var-in-init-body/input.js -* nested/arrows-in-declaration/input.js -* nested/async-in-params/input.js -* nested/generator-in-async/input.js -* regression/13801/input.js -* regression/5880/input.js -* yield-star/create-async-from-sync-iterator/input.js - -# babel-plugin-transform-object-rest-spread (0/61) -* assumption-ignoreFunctionLength/parameters-object-rest-used-in-default/input.js -* assumption-objectRestNoSymbols/rest-assignment-expression/input.js -* assumption-objectRestNoSymbols/rest-computed/input.js -* assumption-objectRestNoSymbols/rest-nested/input.js -* assumption-objectRestNoSymbols/rest-var-declaration/input.js -* assumption-pureGetters/rest-remove-unused-excluded-keys/input.js -* assumption-pureGetters/spread-single-call/input.js -* assumption-setSpreadProperties/assignment/input.js -* assumption-setSpreadProperties/expression/input.js -* assumption-setSpreadProperties/targets-support-object-assign/input.js -* assumption-setSpreadProperties-with-useBuiltIns/assignment/input.js -* assumption-setSpreadProperties-with-useBuiltIns/expression/input.js -* object-rest/assignment-expression/input.js -* object-rest/catch-clause/input.js -* object-rest/duplicate-decl-bug/input.js -* object-rest/export/input.mjs -* object-rest/for-x/input.js -* object-rest/for-x-array-pattern/input.js -* object-rest/for-x-completion-record/input.js -* object-rest/impure-computed/input.js -* object-rest/nested/input.js -* object-rest/nested-2/input.js -* object-rest/nested-array/input.js -* object-rest/nested-array-2/input.js -* object-rest/nested-computed-key/input.js -* object-rest/nested-default-value/input.js -* object-rest/nested-literal-property/input.js -* object-rest/nested-order/input.js -* object-rest/non-string-computed/input.js -* object-rest/null-destructuring/input.js -* object-rest/null-destructuring-transform-destructuring/input.js -* object-rest/object-ref-computed/input.js -* object-rest/parameters/input.js -* object-rest/parameters-object-rest-used-in-default/input.js -* object-rest/remove-unused-excluded-keys-loose/input.js -* object-rest/symbol/input.js -* object-rest/template-literal-allLiterals-true-no-hoisting/input.js -* object-rest/template-literal-property-allLiterals-false/input.js -* object-rest/template-literal-property-allLiterals-true/input.js -* object-rest/variable-destructuring/input.js -* object-rest/with-array-rest/input.js -* object-spread/assignment/input.js -* object-spread/expression/input.js -* object-spread/side-effect/input.js -* object-spread/variable-declaration/input.js -* object-spread-loose/assignment/input.js -* object-spread-loose/expression/input.js -* object-spread-loose/parameters-object-rest-used-in-default/input.js -* object-spread-loose/side-effect/input.js -* object-spread-loose/targets-support-object-assign/input.js -* object-spread-loose/variable-declaration/input.js -* object-spread-loose-builtins/assignment/input.js -* object-spread-loose-builtins/expression/input.js -* object-spread-loose-builtins/side-effect/input.js -* object-spread-loose-builtins/variable-declaration/input.js -* regression/T7178/input.mjs -* regression/gh-4904/input.js -* regression/gh-5151/input.js -* regression/gh-7304/input.mjs -* regression/gh-7388/input.js -* regression/gh-8323/input.js - -# babel-plugin-transform-dotall-regex (0/3) -* dotall-regex/simple/input.js -* dotall-regex/with-unicode-flag/input.js -* dotall-regex/with-unicode-property-escape/input.js - -# babel-plugin-transform-async-to-generator (1/43) -* assumption-ignoreFunctionLength-true/basic/input.mjs -* assumption-ignoreFunctionLength-true/export-default-function/input.mjs -* assumption-noNewArrows-false/basic/input.js -* async-to-generator/async/input.js -* async-to-generator/async-arrow-in-method/input.js -* async-to-generator/async-default-arguments/input.js -* async-to-generator/async-iife/input.js -* async-to-generator/async-iife-with-regenerator/input.js -* async-to-generator/async-iife-with-regenerator-spec/input.js -* async-to-generator/deeply-nested-asyncs/input.js -* async-to-generator/double-await/input.js -* async-to-generator/expression/input.js -* async-to-generator/function-arity/input.js -* async-to-generator/named-expression/input.js -* async-to-generator/no-parameters-and-no-id/input.js -* async-to-generator/object-method/input.js -* async-to-generator/object-method-with-arrows/input.js -* async-to-generator/object-method-with-super/input.js -* async-to-generator/parameters/input.js -* async-to-generator/shadowed-promise/input.js -* async-to-generator/shadowed-promise-import/input.mjs -* async-to-generator/shadowed-promise-nested/input.js -* async-to-generator/statement/input.js -* bluebird-coroutines/arrow-function/input.js -* bluebird-coroutines/class/input.js -* bluebird-coroutines/expression/input.js -* bluebird-coroutines/named-expression/input.js -* bluebird-coroutines/statement/input.js -* export-async/default-arrow-export/input.mjs -* export-async/default-export/input.mjs -* export-async/import-and-export/input.mjs -* export-async/lone-export/input.mjs -* regression/15978/input.js -* regression/4599/input.js -* regression/4943/input.js -* regression/7178/input.js -* regression/8783/input.js -* regression/T7108/input.js -* regression/T7194/input.js -* regression/gh-6923/input.js -* regression/in-uncompiled-class-fields/input.js -* regression/regression-2765/input.js - -# babel-plugin-transform-exponentiation-operator (0/4) -* exponentiation-operator/assignment/input.js -* exponentiation-operator/binary/input.js -* regression/4349/input.js -* regression/4403/input.js - -# babel-plugin-transform-arrow-functions (0/26) -* arrow-functions/arguments/input.js -* arrow-functions/arguments-global-undeclared/input.js -* arrow-functions/arguments-global-var/input.js -* arrow-functions/default-parameters/input.js -* arrow-functions/destructuring-parameters/input.js -* arrow-functions/empty-arguments/input.js -* arrow-functions/empty-block/input.js -* arrow-functions/expression/input.js -* arrow-functions/implicit-var-arguments/input.js -* arrow-functions/inside-call/input.js -* arrow-functions/multiple-arguments/input.js -* arrow-functions/nested/input.js -* arrow-functions/paran-insertion/input.js -* arrow-functions/self-referential/input.js -* arrow-functions/single-argument/input.js -* arrow-functions/spec/input.js -* arrow-functions/statement/input.js -* arrow-functions/super-call/input.js -* arrow-functions/super-prop/input.js -* arrow-functions/this/input.js -* assumption-newableArrowFunctions-false/basic/input.js -* assumption-newableArrowFunctions-false/naming/input.js -* assumption-newableArrowFunctions-false/self-referential/input.js -* spec/newableArrowFunction-default/input.js -* spec/newableArrowFunction-vs-spec-false/input.js -* spec/newableArrowFunction-vs-spec-true/input.js - -# babel-plugin-transform-function-name (1/33) -* function-name/assignment/input.js -* function-name/await/input.mjs -* function-name/basic/input.js -* function-name/class-method/input.js -* function-name/collisions/input.js -* function-name/eval/input.js -* function-name/export/input.mjs -* function-name/export-default-arrow-renaming/input.mjs -* function-name/export-default-arrow-renaming-2/input.mjs -* function-name/export-default-arrow-renaming-3/input.mjs -* function-name/export-default-arrow-renaming-es3/input.mjs -* function-name/export-default-arrow-renaming-module-amd/input.mjs -* function-name/export-default-arrow-renaming-module-es6/input.mjs -* function-name/export-default-arrow-renaming-module-system/input.mjs -* function-name/export-default-arrow-renaming-module-umd/input.mjs -* function-name/function-assignment/input.js -* function-name/function-collision/input.js -* function-name/global/input.js -* function-name/modules/input.mjs -* function-name/modules-2/input.mjs -* function-name/modules-3/input.mjs -* function-name/modules-4/input.mjs -* function-name/object/input.js -* function-name/own-bindings/input.js -* function-name/self-reference/input.js -* function-name/shorthand-property/input.js -* function-name/unicode-id-not-supported/input.js -* function-name/unicode-id-supported/input.js -* function-name/with-arrow-functions-transform/input.js -* function-name/with-arrow-functions-transform-spec/input.js -* issues/5004/input.mjs -* issues/7199/input.js - -# babel-plugin-transform-shorthand-properties (1/7) -* shorthand-properties/method-plain/input.js -* shorthand-properties/proto/input.js -* shorthand-properties/shorthand-comments/input.js -* shorthand-properties/shorthand-mixed/input.js -* shorthand-properties/shorthand-multiple/input.js -* shorthand-properties/shorthand-single/input.js - -# babel-plugin-transform-sticky-regex (1/2) -* sticky-regex/basic/input.js - -# babel-plugin-transform-unicode-regex (1/4) -* unicode-regex/basic/input.js -* unicode-regex/negated-set/input.js -* unicode-regex/slash/input.js - -# babel-plugin-transform-template-literals (1/32) -* assumption-ignoreToPrimitiveHint/escape-quotes/input.js -* assumption-ignoreToPrimitiveHint/expression-first/input.js -* assumption-ignoreToPrimitiveHint/functions/input.js -* assumption-ignoreToPrimitiveHint/literals/input.js -* assumption-ignoreToPrimitiveHint/multiline/input.js -* assumption-ignoreToPrimitiveHint/multiple/input.js -* assumption-ignoreToPrimitiveHint/none/input.js -* assumption-ignoreToPrimitiveHint/only/input.js -* assumption-ignoreToPrimitiveHint/single/input.js -* assumption-ignoreToPrimitiveHint/statement/input.js -* assumption-ignoreToPrimitiveHint/tag/input.js -* assumption-mutableTemplateObject/no-tag/input.js -* assumption-mutableTemplateObject/tag/input.js -* assumption-mutableTemplateObject/template-revision/input.js -* default/cache-revision/input.js -* default/escape-quotes/input.js -* default/expression-first/input.js -* default/functions/input.js -* default/literals/input.js -* default/multiline/input.js -* default/multiple/input.js -* default/none/input.js -* default/only/input.js -* default/simple-tag/input.js -* default/single/input.js -* default/statement/input.js -* default/tag/input.js -* default/tag-with-unicode-escapes/input.js -* default/template-revision/input.js -* loose/ignoreToPrimitiveHint/input.js -* loose/mutableTemplateObject/input.js - -# babel-plugin-transform-duplicate-keys (2/8) -* combination/dupes/input.js -* duplicate-keys/both-quoted/input.js -* duplicate-keys/dupes/input.js -* duplicate-keys/getter/input.js -* duplicate-keys/getters-and-setters/input.js -* duplicate-keys/one-quoted/input.js - -# babel-plugin-transform-instanceof (0/1) -* instanceof/instanceof/input.js - -# babel-plugin-transform-new-target (0/8) -* general/arrow/input.js -* general/class/input.js -* general/class-properties/input.js -* general/class-properties-loose/input.js -* general/extended-class/input.js -* general/function/input.js -* general/function-duplicate-name/input.js -* general/object/input.js - -# babel-plugin-transform-typescript (55/158) -* class/abstract-class-decorated/input.ts -* class/abstract-class-decorated-method/input.ts -* class/abstract-class-decorated-parameter/input.ts -* class/accessor-allowDeclareFields-false/input.ts -* class/accessor-allowDeclareFields-true/input.ts -* class/declare/input.ts -* class/decorated-declare-properties/input.ts -* class/parameter-properties/input.ts -* class/parameter-properties-late-super/input.ts -* class/parameter-properties-with-class/input.ts -* class/parameter-properties-with-class-and-super/input.ts -* class/parameter-properties-with-parameters/input.ts -* class/parameter-properties-with-super/input.ts -* class/private-method-override-transform-private/input.ts -* class/transform-properties-declare-wrong-order/input.ts -* declarations/erased/input.ts -* declarations/export-declare-enum/input.ts -* declarations/nested-namespace/input.mjs -* exports/declared-types/input.ts -* exports/export-const-enums/input.ts -* exports/export-type/input.ts -* exports/export-type-from/input.ts -* exports/export-type-star-from/input.ts -* exports/export=/input.ts -* exports/export=-to-cjs/input.ts -* exports/imported-types/input.ts -* exports/imported-types-only-remove-type-imports/input.ts -* exports/issue-9916-3/input.ts -* exports/type-only-export-specifier-1/input.ts -* exports/type-only-export-specifier-2/input.ts -* exports/type-only-export-specifier-3/input.ts -* function/overloads-exports/input.mjs -* imports/elide-injected/input.ts -* imports/elide-preact/input.ts -* imports/elide-react/input.ts -* imports/elide-type-referenced-in-imports-equal-no/input.ts -* imports/elide-typeof/input.ts -* imports/elision/input.ts -* imports/elision-export-type/input.ts -* imports/elision-locations/input.ts -* imports/elision-qualifiedname/input.ts -* imports/elision-rename/input.ts -* imports/enum-id/input.ts -* imports/enum-value/input.ts -* imports/import-removed-exceptions/input.ts -* imports/import-type/input.ts -* imports/import-type-func-with-duplicate-name/input.ts -* imports/import-type-not-removed/input.ts -* imports/import=-declaration/input.ts -* imports/import=-module/input.ts -* imports/import=-module-to-cjs/input.ts -* imports/only-remove-type-imports/input.ts -* imports/parameter-decorators/input.ts -* imports/property-signature/input.ts -* imports/type-only-export-specifier-1/input.ts -* imports/type-only-export-specifier-2/input.ts -* imports/type-only-import-specifier-1/input.ts -* imports/type-only-import-specifier-2/input.ts -* imports/type-only-import-specifier-3/input.ts -* imports/type-only-import-specifier-4/input.ts -* namespace/alias/input.ts -* namespace/ambient-module-nested/input.ts -* namespace/ambient-module-nested-exported/input.ts -* namespace/canonical/input.ts -* namespace/clobber-class/input.ts -* namespace/clobber-enum/input.ts -* namespace/clobber-export/input.ts -* namespace/clobber-import/input.ts -* namespace/contentious-names/input.ts -* namespace/declare/input.ts -* namespace/declare-global-nested-namespace/input.ts -* namespace/empty-removed/input.ts -* namespace/export/input.ts -* namespace/export-type-only/input.ts -* namespace/module-nested/input.ts -* namespace/module-nested-export/input.ts -* namespace/multiple/input.ts -* namespace/mutable-fail/input.ts -* namespace/namespace-flag/input.ts -* namespace/namespace-nested-module/input.ts -* namespace/nested/input.ts -* namespace/nested-destructuring/input.ts -* namespace/nested-namespace/input.ts -* namespace/nested-shorthand/input.ts -* namespace/nested-shorthand-export/input.ts -* namespace/same-name/input.ts -* namespace/undeclared/input.ts -* optimize-const-enums/custom-values/input.ts -* optimize-const-enums/custom-values-exported/input.ts -* optimize-const-enums/declare/input.ts -* optimize-const-enums/export-const-enum/input.ts -* optimize-const-enums/export-const-enum-type-and-value/input.ts -* optimize-const-enums/export-const-enum-type-no-deopt/input.ts -* optimize-const-enums/exported/input.ts -* optimize-const-enums/local/input.ts -* optimize-const-enums/local-shadowed/input.ts -* optimize-const-enums/merged/input.ts -* optimize-const-enums/merged-exported/input.ts -* regression/10162/input.ts -* regression/10338/input.ts -* regression/11061/input.mjs -* regression/15768/input.ts -* variable-declaration/non-null-in-optional-chain/input.ts - -# babel-plugin-transform-react-jsx (1/156) -* autoImport/after-polyfills/input.mjs -* autoImport/after-polyfills-2/input.mjs -* autoImport/after-polyfills-compiled-to-cjs/input.mjs -* autoImport/after-polyfills-script-not-supported/input.js -* autoImport/auto-import-react-source-type-module/input.js -* autoImport/auto-import-react-source-type-script/input.js -* autoImport/complicated-scope-module/input.js -* autoImport/complicated-scope-script/input.js -* autoImport/import-source/input.js -* autoImport/import-source-pragma/input.js -* autoImport/react-defined/input.js -* pure/false-default-pragma-automatic-runtime/input.js -* pure/false-default-pragma-classic-runtime/input.js -* pure/false-pragma-comment-automatic-runtime/input.js -* pure/false-pragma-comment-classic-runtime/input.js -* pure/false-pragma-option-automatic-runtime/input.js -* pure/false-pragma-option-classic-runtime/input.js -* pure/true-default-pragma-automatic-runtime/input.js -* pure/true-default-pragma-classic-runtime/input.js -* pure/true-pragma-comment-automatic-runtime/input.js -* pure/true-pragma-comment-classic-runtime/input.js -* pure/true-pragma-option-automatic-runtime/input.js -* pure/true-pragma-option-classic-runtime/input.js -* pure/unset-default-pragma-automatic-runtime/input.js -* pure/unset-default-pragma-classic-runtime/input.js -* pure/unset-pragma-comment-automatic-runtime/input.js -* pure/unset-pragma-comment-classic-runtime/input.js -* pure/unset-pragma-option-automatic-runtime/input.js -* pure/unset-pragma-option-classic-runtime/input.js -* react/adds-appropriate-newlines-when-using-spread-attribute/input.js -* react/arrow-functions/input.js -* react/assignment/input.js -* react/concatenates-adjacent-string-literals/input.js -* react/does-not-add-source-self/input.mjs -* react/dont-coerce-expression-containers/input.js -* react/duplicate-props/input.js -* react/flattens-spread/input.js -* react/handle-spread-with-proto/input.js -* react/honor-custom-jsx-comment/input.js -* react/honor-custom-jsx-comment-if-jsx-pragma-option-set/input.js -* react/honor-custom-jsx-pragma-option/input.js -* react/jsx-with-retainlines-option/input.js -* react/jsx-without-retainlines-option/input.js -* react/optimisation.react.constant-elements/input.js -* react/pragma-works-with-no-space-at-the-end/input.js -* react/proto-in-jsx-attribute/input.js -* react/should-add-quotes-es3/input.js -* react/should-allow-constructor-as-prop/input.js -* react/should-allow-deeper-js-namespacing/input.js -* react/should-allow-elements-as-attributes/input.js -* react/should-allow-js-namespacing/input.js -* react/should-allow-jsx-docs-comment-with-pragma/input.js -* react/should-allow-nested-fragments/input.js -* react/should-allow-no-pragmafrag-if-frag-unused/input.js -* react/should-allow-pragmafrag-and-frag/input.js -* react/should-avoid-wrapping-in-extra-parens-if-not-needed/input.js -* react/should-convert-simple-tags/input.js -* react/should-convert-simple-text/input.js -* react/should-disallow-spread-children/input.js -* react/should-disallow-valueless-key/input.js -* react/should-disallow-xml-namespacing/input.js -* react/should-escape-xhtml-jsxattribute/input.js -* react/should-escape-xhtml-jsxtext/input.js -* react/should-handle-attributed-elements/input.js -* react/should-handle-has-own-property-correctly/input.js -* react/should-have-correct-comma-in-nested-children/input.js -* react/should-insert-commas-after-expressions-before-whitespace/input.js -* react/should-not-add-quotes-to-identifier-names/input.js -* react/should-not-allow-jsx-pragma-to-be-anywhere-in-comment/input.js -* react/should-not-mangle-expressioncontainer-attribute-values/input.js -* react/should-not-strip-nbsp-even-coupled-with-other-whitespace/input.js -* react/should-not-strip-tags-with-a-single-child-of-nbsp/input.js -* react/should-properly-handle-comments-between-props/input.js -* react/should-quote-jsx-attributes/input.js -* react/should-support-xml-namespaces-if-flag/input.js -* react/should-throw-error-namespaces-if-not-flag/input.js -* react/should-transform-known-hyphenated-tags/input.js -* react/should-warn-when-importSource-is-set/input.js -* react/should-warn-when-importSource-pragma-is-set/input.js -* react/this-tag-name/input.js -* react/weird-symbols/input.js -* react/wraps-props-in-react-spread-for-first-spread-attributes/input.js -* react/wraps-props-in-react-spread-for-last-spread-attributes/input.js -* react/wraps-props-in-react-spread-for-middle-spread-attributes/input.js -* react-automatic/adds-appropriate-newlines-when-using-spread-attribute/input.js -* react-automatic/arrow-functions/input.js -* react-automatic/assignment/input.js -* react-automatic/concatenates-adjacent-string-literals/input.js -* react-automatic/does-not-add-source-self-automatic/input.mjs -* react-automatic/dont-coerce-expression-containers/input.js -* react-automatic/duplicate-props/input.js -* react-automatic/flattens-spread/input.js -* react-automatic/handle-fragments/input.js -* react-automatic/handle-fragments-with-key/input.js -* react-automatic/handle-fragments-with-no-children/input.js -* react-automatic/handle-nonstatic-children/input.js -* react-automatic/handle-spread-with-proto/input.js -* react-automatic/handle-static-children/input.js -* react-automatic/jsx-with-retainlines-option/input.js -* react-automatic/jsx-without-retainlines-option/input.js -* react-automatic/key-undefined-works/input.js -* react-automatic/optimisation.react.constant-elements/input.js -* react-automatic/pragma-works-with-no-space-at-the-end/input.js -* react-automatic/should-add-quotes-es3/input.js -* react-automatic/should-allow-constructor-as-prop/input.js -* react-automatic/should-allow-deeper-js-namespacing/input.js -* react-automatic/should-allow-elements-as-attributes/input.js -* react-automatic/should-allow-js-namespacing/input.js -* react-automatic/should-allow-nested-fragments/input.js -* react-automatic/should-avoid-wrapping-in-extra-parens-if-not-needed/input.js -* react-automatic/should-convert-simple-tags/input.js -* react-automatic/should-convert-simple-text/input.js -* react-automatic/should-disallow-spread-children/input.js -* react-automatic/should-disallow-valueless-key/input.js -* react-automatic/should-disallow-xml-namespacing/input.js -* react-automatic/should-escape-xhtml-jsxattribute/input.js -* react-automatic/should-escape-xhtml-jsxtext/input.js -* react-automatic/should-handle-attributed-elements/input.js -* react-automatic/should-handle-has-own-property-correctly/input.js -* react-automatic/should-have-correct-comma-in-nested-children/input.js -* react-automatic/should-insert-commas-after-expressions-before-whitespace/input.js -* react-automatic/should-not-add-quotes-to-identifier-names/input.js -* react-automatic/should-not-mangle-expressioncontainer-attribute-values/input.js -* react-automatic/should-not-strip-nbsp-even-coupled-with-other-whitespace/input.js -* react-automatic/should-not-strip-tags-with-a-single-child-of-nbsp/input.js -* react-automatic/should-properly-handle-comments-between-props/input.js -* react-automatic/should-properly-handle-keys/input.js -* react-automatic/should-properly-handle-null-prop-spread/input.js -* react-automatic/should-quote-jsx-attributes/input.js -* react-automatic/should-support-xml-namespaces-if-flag/input.js -* react-automatic/should-throw-error-namespaces-if-not-flag/input.js -* react-automatic/should-throw-when-filter-is-specified/input.js -* react-automatic/should-transform-known-hyphenated-tags/input.js -* react-automatic/should-use-createElement-when-key-comes-after-spread/input.js -* react-automatic/should-use-jsx-when-key-comes-before-spread/input.js -* react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js -* react-automatic/this-tag-name/input.js -* react-automatic/weird-symbols/input.js -* react-automatic/wraps-props-in-react-spread-for-last-spread-attributes/input.js -* react-automatic/wraps-props-in-react-spread-for-middle-spread-attributes/input.js -* regression/issue-12478-automatic/input.js -* regression/issue-12478-classic/input.js -* regression/pragma-frag-set-default-classic-runtime/input.js -* removed-options/invalid-use-builtins-false/input.js -* removed-options/invalid-use-builtins-true/input.js -* removed-options/invalid-use-spread-false/input.js -* removed-options/invalid-use-spread-true/input.js -* runtime/classic/input.js -* runtime/defaults-to-automatic/input.js -* runtime/invalid-runtime/input.js -* runtime/pragma-runtime-classsic/input.js -* runtime/runtime-automatic/input.js -* sourcemaps/JSXText/input.js -* spread-transform/transform-to-babel-extend/input.js -* spread-transform/transform-to-object-assign/input.js - -# babel-plugin-proposal-decorators (2/231) -* 2018-09-transformation/async-generator-method/input.js -* 2018-09-transformation/class-decorators-yield-await/input.js -* 2021-12-accessors/context-name/input.js -* 2021-12-accessors--to-es2015/context-name/input.js -* 2021-12-fields/context-name/input.js -* 2021-12-fields--to-es2015/context-name/input.js -* 2021-12-getters/context-name/input.js -* 2021-12-getters--to-es2015/context-name/input.js -* 2021-12-methods/context-name/input.js -* 2021-12-methods--to-es2015/context-name/input.js -* 2021-12-misc/initProto-existing-derived-constructor/input.js -* 2021-12-misc/setting-private-method/input.js -* 2021-12-misc/setting-private-method-via-array-pattern/input.js -* 2021-12-misc/setting-private-method-via-for-of/input.js -* 2021-12-misc/setting-private-method-via-object-pattern/input.js -* 2021-12-misc/setting-private-method-via-rest/input.js -* 2021-12-misc/setting-private-method-via-update/input.js -* 2021-12-setters/context-name/input.js -* 2021-12-setters--to-es2015/context-name/input.js -* 2022-03-accessors/context-name/input.js -* 2022-03-accessors--to-es2015/context-name/input.js -* 2022-03-classes/decorator-access-modified-fields/input.js -* 2022-03-classes/decorator-access-modified-methods/input.js -* 2022-03-fields/context-name/input.js -* 2022-03-fields--to-es2015/context-name/input.js -* 2022-03-getters/context-name/input.js -* 2022-03-getters--to-es2015/context-name/input.js -* 2022-03-methods/context-name/input.js -* 2022-03-methods--to-es2015/context-name/input.js -* 2022-03-misc/initProto-existing-derived-constructor/input.js -* 2022-03-misc/setting-private-method/input.js -* 2022-03-misc/setting-private-method-via-array-pattern/input.js -* 2022-03-misc/setting-private-method-via-for-of/input.js -* 2022-03-misc/setting-private-method-via-object-pattern/input.js -* 2022-03-misc/setting-private-method-via-rest/input.js -* 2022-03-misc/setting-private-method-via-update/input.js -* 2022-03-setters/context-name/input.js -* 2022-03-setters--to-es2015/context-name/input.js -* 2023-01-accessors/context-name/input.js -* 2023-01-accessors--to-es2015/context-name/input.js -* 2023-01-classes/decorator-access-modified-fields/input.js -* 2023-01-classes/decorator-access-modified-methods/input.js -* 2023-01-fields/context-name/input.js -* 2023-01-fields--to-es2015/context-name/input.js -* 2023-01-getters/context-name/input.js -* 2023-01-getters--to-es2015/context-name/input.js -* 2023-01-methods/context-name/input.js -* 2023-01-methods--to-es2015/context-name/input.js -* 2023-01-misc/initProto-existing-derived-constructor/input.js -* 2023-01-misc/setting-private-method/input.js -* 2023-01-misc/setting-private-method-via-array-pattern/input.js -* 2023-01-misc/setting-private-method-via-for-of/input.js -* 2023-01-misc/setting-private-method-via-object-pattern/input.js -* 2023-01-misc/setting-private-method-via-rest/input.js -* 2023-01-misc/setting-private-method-via-update/input.js -* 2023-01-setters/context-name/input.js -* 2023-01-setters--to-es2015/context-name/input.js -* 2023-05-accessors/context-name/input.js -* 2023-05-accessors--to-es2015/context-name/input.js -* 2023-05-classes/decorator-access-modified-fields/input.js -* 2023-05-classes/decorator-access-modified-methods/input.js -* 2023-05-fields/context-name/input.js -* 2023-05-fields--to-es2015/context-name/input.js -* 2023-05-getters/context-name/input.js -* 2023-05-getters--to-es2015/context-name/input.js -* 2023-05-methods/context-name/input.js -* 2023-05-methods--to-es2015/context-name/input.js -* 2023-05-misc/initProto-existing-derived-constructor/input.js -* 2023-05-misc/setting-private-method/input.js -* 2023-05-misc/setting-private-method-in-body/input.js -* 2023-05-misc/setting-private-method-via-array-pattern/input.js -* 2023-05-misc/setting-private-method-via-for-of/input.js -* 2023-05-misc/setting-private-method-via-object-pattern/input.js -* 2023-05-misc/setting-private-method-via-rest/input.js -* 2023-05-misc/setting-private-method-via-update/input.js -* 2023-05-misc/this/input.js -* 2023-05-setters/context-name/input.js -* 2023-05-setters--to-es2015/context-name/input.js -* 2023-11-accessors/context-name/input.js -* 2023-11-accessors/private/input.js -* 2023-11-accessors/public/input.js -* 2023-11-accessors/static-private/input.js -* 2023-11-accessors/static-public/input.js -* 2023-11-accessors/undecorated-private/input.js -* 2023-11-accessors/undecorated-public/input.js -* 2023-11-accessors/undecorated-static-private/input.js -* 2023-11-accessors/undecorated-static-public/input.js -* 2023-11-accessors--to-es2015/context-name/input.js -* 2023-11-accessors--to-es2015/private/input.js -* 2023-11-accessors--to-es2015/public/input.js -* 2023-11-accessors--to-es2015/static-private/input.js -* 2023-11-accessors--to-es2015/static-public/input.js -* 2023-11-accessors--to-es2015/undecorated-private/input.js -* 2023-11-accessors--to-es2015/undecorated-public/input.js -* 2023-11-accessors--to-es2015/undecorated-static-private/input.js -* 2023-11-accessors--to-es2015/undecorated-static-public/input.js -* 2023-11-assumption-constantSuper/super-in-nested-constructor-expression/input.js -* 2023-11-assumption-constantSuper/super-in-private-accessor/input.js -* 2023-11-assumption-constantSuper/super-in-private-method/input.js -* 2023-11-classes/decorator-access-modified-fields/input.js -* 2023-11-classes/decorator-access-modified-methods/input.js -* 2023-11-classes/expressions/input.js -* 2023-11-classes/expressions-static-blocks/input.js -* 2023-11-classes/inheritance/input.js -* 2023-11-classes/initializers/input.js -* 2023-11-classes/replacement/input.js -* 2023-11-classes/replacement-static-decorator-initializer-this/input.js -* 2023-11-classes/replacement-static-installed-on-correct-class/input.js -* 2023-11-classes/replacement-static-method-private-instance-element-super/input.js -* 2023-11-classes/replacement-static-method-private-instance-element-super-assumption-ignoreFunctionLength/input.js -* 2023-11-classes/replacement-static-private-instance-element-access/input.js -* 2023-11-classes/replacement-static-private-instance-element-destructuring/input.js -* 2023-11-classes/replacement-static-this/input.js -* 2023-11-classes/replacement-with-expr/input.js -* 2023-11-classes--to-es2015/decorator-access-modified-fields/input.js -* 2023-11-classes--to-es2015/decorator-access-modified-methods/input.js -* 2023-11-classes--to-es2015/expressions/input.js -* 2023-11-classes--to-es2015/expressions-static-blocks/input.js -* 2023-11-classes--to-es2015/inheritance/input.js -* 2023-11-classes--to-es2015/initializers/input.js -* 2023-11-classes--to-es2015/replacement/input.js -* 2023-11-classes--to-es2015/replacement-static-decorator-initializer-this/input.js -* 2023-11-classes--to-es2015/replacement-static-installed-on-correct-class/input.js -* 2023-11-classes--to-es2015/replacement-static-method-private-instance-element-super/input.js -* 2023-11-classes--to-es2015/replacement-static-method-private-instance-element-super-assumption-ignoreFunctionLength/input.js -* 2023-11-classes--to-es2015/replacement-static-private-instance-element-access/input.js -* 2023-11-classes--to-es2015/replacement-static-private-instance-element-destructuring/input.js -* 2023-11-classes--to-es2015/replacement-static-this/input.js -* 2023-11-classes--to-es2015/replacement-with-expr/input.js -* 2023-11-duplicated-keys/computed-keys-same-ast/input.js -* 2023-11-duplicated-keys/computed-keys-same-value/input.js -* 2023-11-duplicated-keys/method-and-field/input.js -* 2023-11-duplicated-keys/methods-with-same-key/input.js -* 2023-11-duplicated-keys--to-es2015/computed-keys-same-ast/input.js -* 2023-11-duplicated-keys--to-es2015/computed-keys-same-value/input.js -* 2023-11-duplicated-keys--to-es2015/method-and-field/input.js -* 2023-11-duplicated-keys--to-es2015/methods-with-same-key/input.js -* 2023-11-exported/default-anonymous/input.mjs -* 2023-11-exported/default-named/input.mjs -* 2023-11-exported/member-decorator/input.mjs -* 2023-11-exported/named/input.mjs -* 2023-11-fields/context-name/input.js -* 2023-11-fields/private/input.js -* 2023-11-fields/public/input.js -* 2023-11-fields/static-private/input.js -* 2023-11-fields/static-public/input.js -* 2023-11-fields--to-es2015/context-name/input.js -* 2023-11-fields--to-es2015/private/input.js -* 2023-11-fields--to-es2015/public/input.js -* 2023-11-fields--to-es2015/static-private/input.js -* 2023-11-fields--to-es2015/static-public/input.js -* 2023-11-getters/context-name/input.js -* 2023-11-getters/private/input.js -* 2023-11-getters/public/input.js -* 2023-11-getters/static-private/input.js -* 2023-11-getters/static-public/input.js -* 2023-11-getters--to-es2015/context-name/input.js -* 2023-11-getters--to-es2015/private/input.js -* 2023-11-getters--to-es2015/public/input.js -* 2023-11-getters--to-es2015/static-private/input.js -* 2023-11-getters--to-es2015/static-public/input.js -* 2023-11-getters-and-setters/private/input.js -* 2023-11-getters-and-setters/public/input.js -* 2023-11-getters-and-setters/static-private/input.js -* 2023-11-getters-and-setters/static-public/input.js -* 2023-11-getters-and-setters--to-es2015/private/input.js -* 2023-11-getters-and-setters--to-es2015/public/input.js -* 2023-11-getters-and-setters--to-es2015/static-private/input.js -* 2023-11-getters-and-setters--to-es2015/static-public/input.js -* 2023-11-methods/context-name/input.js -* 2023-11-methods/private/input.js -* 2023-11-methods/private-async-and-generator/input.js -* 2023-11-methods/public/input.js -* 2023-11-methods/static-private/input.js -* 2023-11-methods/static-public/input.js -* 2023-11-methods--to-es2015/context-name/input.js -* 2023-11-methods--to-es2015/private/input.js -* 2023-11-methods--to-es2015/private-async-and-generator/input.js -* 2023-11-methods--to-es2015/public/input.js -* 2023-11-methods--to-es2015/static-private/input.js -* 2023-11-methods--to-es2015/static-public/input.js -* 2023-11-misc/all-decorators/input.js -* 2023-11-misc/initProto-existing-derived-constructor/input.js -* 2023-11-misc/initProto-existing-derived-constructor-multiple-super/input.js -* 2023-11-misc/private-keys-in-enclosing-class/input.js -* 2023-11-misc/private-name-in-class-decorator/input.js -* 2023-11-misc/setting-private-method/input.js -* 2023-11-misc/setting-private-method-in-body/input.js -* 2023-11-misc/setting-private-method-via-array-pattern/input.js -* 2023-11-misc/setting-private-method-via-for-of/input.js -* 2023-11-misc/setting-private-method-via-object-pattern/input.js -* 2023-11-misc/setting-private-method-via-rest/input.js -* 2023-11-misc/setting-private-method-via-update/input.js -* 2023-11-misc/setting-shadowed-private-method-valid/input.js -* 2023-11-misc/super-in-decorator/input.js -* 2023-11-misc/super-in-nested-constructor-expression/input.js -* 2023-11-misc/super-in-private-accessor/input.js -* 2023-11-misc/super-in-private-method/input.js -* 2023-11-misc/symbol-key/input.js -* 2023-11-misc/this/input.js -* 2023-11-misc/valid-expression-formats/input.js -* 2023-11-misc--to-es2015/destructuring-transform-integration/input.js -* 2023-11-misc--to-es2015/initProto-existing-derived-constructor/input.js -* 2023-11-misc--to-es2015/private-name-in-class-decorator/input.js -* 2023-11-misc--to-es2015/super-in-decorator/input.js -* 2023-11-misc--to-es2015/this/input.js -* 2023-11-misc--to-es2015/valid-expression-formats/input.js -* 2023-11-ordering/class-decorators-without-element-decorators/input.js -* 2023-11-ordering/decorators-evaluation-with-this-caching/input.js -* 2023-11-ordering/field-initializers-after-methods-private/input.js -* 2023-11-ordering--to-es2015/class-decorators-without-element-decorators/input.js -* 2023-11-ordering--to-es2015/decorators-evaluation-with-this-caching/input.js -* 2023-11-setters/context-name/input.js -* 2023-11-setters/private/input.js -* 2023-11-setters/public/input.js -* 2023-11-setters/static-private/input.js -* 2023-11-setters/static-public/input.js -* 2023-11-setters--to-es2015/context-name/input.js -* 2023-11-setters--to-es2015/private/input.js -* 2023-11-setters--to-es2015/public/input.js -* 2023-11-setters--to-es2015/static-private/input.js -* 2023-11-setters--to-es2015/static-public/input.js -* 2023-11-typescript/computed-key-ts-as-expression/input.ts -* legacy-decl-to-expression/class-decorators/input.mjs -* legacy-decl-to-expression/method-decorators/input.mjs -* legacy-regression/10264/input.mjs -* legacy-regression/7030/input.js -* legacy-regression/8041/input.mjs -* legacy-regression/8559/input.mjs +# babel-plugin-transform-react-display-name (15/16) +* display-name/nested/input.js diff --git a/tasks/transform_conformance/babel_exec.snap.md b/tasks/transform_conformance/babel_exec.snap.md index 55b8ca8eb4e74..99fcdd3bacf3f 100644 --- a/tasks/transform_conformance/babel_exec.snap.md +++ b/tasks/transform_conformance/babel_exec.snap.md @@ -1,292 +1,6 @@ -Passed: 459/716 +Passed: 0/0 # All Passed: -* babel-plugin-transform-class-static-block -* babel-plugin-transform-logical-assignment-operators -* babel-plugin-transform-numeric-separator -* babel-plugin-transform-nullish-coalescing-operator -* babel-plugin-transform-optional-catch-binding -* babel-plugin-transform-json-strings -* babel-plugin-transform-async-to-generator -* babel-plugin-transform-exponentiation-operator -* babel-plugin-transform-template-literals -* babel-plugin-transform-instanceof -# babel-plugin-transform-class-properties (140/149) -* nested-class/super-call-in-decorator/exec.js -* nested-class/super-property-in-accessor-key/exec.js -* nested-class/super-property-in-decorator/exec.js -* private/parenthesized-optional-member-call/exec.js -* private/parenthesized-optional-member-call-with-transform/exec.js -* private-loose/access-before-declaration/exec.js -* private-loose/parenthesized-optional-member-call/exec.js -* private-loose/parenthesized-optional-member-call-with-transform/exec.js -* public/computed-toPrimitive/exec.js - -# babel-plugin-transform-private-methods (117/124) -* accessors-loose/get-only-setter/exec.js -* accessors-privateFieldsAsProperties/get-only-setter/exec.js -* accessors-privateFieldsAsSymbols/get-only-setter/exec.js -* static-accessors/get-only-setter/exec.js -* static-accessors-loose/get-only-setter/exec.js -* static-accessors-privateFieldsAsProperties/get-only-setter/exec.js -* static-accessors-privateFieldsAsSymbols/get-only-setter/exec.js - -# babel-plugin-transform-private-property-in-object (7/12) -* assumption-privateFieldsAsProperties/rhs-not-object/exec.js -* assumption-privateFieldsAsSymbols/rhs-not-object/exec.js -* private/rhs-not-object/exec.js -* private-loose/rhs-not-object/exec.js -* to-native-fields/rhs-not-object/exec.js - -# babel-plugin-transform-dynamic-import (4/18) -* commonjs/exec-interop/exec.js -* commonjs/exec-interop-null/exec.js -* commonjs/exec-interop-string/exec.js -* commonjs/exec-template-literal/exec.js -* commonjs/exec-to-primitive/exec.js -* commonjs/exec-to-string-order/exec.js -* commonjs/exec-transpiled-dep/exec.js -* commonjs-createImportExpression-false/exec-interop/exec.js -* commonjs-createImportExpression-false/exec-interop-null/exec.js -* commonjs-createImportExpression-false/exec-interop-string/exec.js -* commonjs-createImportExpression-false/exec-template-literal/exec.js -* commonjs-createImportExpression-false/exec-to-primitive/exec.js -* commonjs-createImportExpression-false/exec-to-string-order/exec.js -* commonjs-createImportExpression-false/exec-transpiled-dep/exec.js - -# babel-plugin-transform-optional-chaining (11/16) -* assumption-noDocumentAll/parenthesized-expression-member-call/exec.js -* general/parenthesized-expression-member-call/exec.js -* general/parenthesized-expression-member-call-loose/exec.js -* general/parenthesized-member-call/exec.js -* general/parenthesized-member-call-loose/exec.js - -# babel-plugin-transform-async-generator-functions (25/26) -* yield-star/ecma262-pr-2819/exec.js - -# babel-plugin-transform-object-rest-spread (18/33) -* assumption-objectRestNoSymbols/rest-ignore-symbols/exec.js -* assumption-pureGetters/rest-remove-unused-excluded-keys/exec.js -* assumption-pureGetters/spread-single-call/exec.js -* assumption-setSpreadProperties/expression/exec.js -* assumption-setSpreadProperties/no-object-assign-exec/exec.js -* assumption-setSpreadProperties-with-useBuiltIns/expression/exec.js -* assumption-setSpreadProperties-with-useBuiltIns/no-object-assign-exec/exec.js -* object-rest/null-destructuring/exec.js -* object-rest/null-destructuring-transform-destructuring/exec.js -* object-spread-loose/expression/exec.js -* object-spread-loose/no-object-assign-exec/exec.js -* object-spread-loose/side-effect/exec.js -* object-spread-loose-builtins/expression/exec.js -* object-spread-loose-builtins/no-object-assign-exec/exec.js -* object-spread-loose-builtins/side-effect/exec.js - -# babel-plugin-transform-arrow-functions (2/3) -* arrow-functions/implicit-var-arguments/exec.js - -# babel-plugin-transform-new-target (8/9) -* exec/reflect-function/exec.js - -# babel-plugin-proposal-decorators (59/258) -* 2021-12-fields/context-name/exec.js -* 2021-12-fields--to-es2015/context-name/exec.js -* 2021-12-getters/context-name/exec.js -* 2021-12-getters--to-es2015/context-name/exec.js -* 2021-12-methods/context-name/exec.js -* 2021-12-methods--to-es2015/context-name/exec.js -* 2021-12-misc/decorator-evaluation-scope/exec.js -* 2021-12-misc/initProto-existing-derived-constructor/exec.js -* 2021-12-misc--to-es2015/accessor-old-initializer-prop-support/exec.js -* 2021-12-setters/context-name/exec.js -* 2021-12-setters--to-es2015/context-name/exec.js -* 2022-03-classes/decorator-access-modified-fields/exec.js -* 2022-03-classes/decorator-access-modified-methods/exec.js -* 2022-03-fields/context-name/exec.js -* 2022-03-fields--to-es2015/context-name/exec.js -* 2022-03-getters/context-name/exec.js -* 2022-03-getters--to-es2015/context-name/exec.js -* 2022-03-methods/context-name/exec.js -* 2022-03-methods--to-es2015/context-name/exec.js -* 2022-03-misc/decorator-evaluation-scope/exec.js -* 2022-03-misc/initProto-existing-derived-constructor/exec.js -* 2022-03-misc/initializer-property-ignored/exec.js -* 2022-03-setters/context-name/exec.js -* 2022-03-setters--to-es2015/context-name/exec.js -* 2023-01-classes/decorator-access-modified-fields/exec.js -* 2023-01-classes/decorator-access-modified-methods/exec.js -* 2023-01-fields/context-name/exec.js -* 2023-01-fields--to-es2015/context-name/exec.js -* 2023-01-getters/context-name/exec.js -* 2023-01-getters--to-es2015/context-name/exec.js -* 2023-01-methods/context-name/exec.js -* 2023-01-methods--to-es2015/context-name/exec.js -* 2023-01-misc/decorator-evaluation-scope/exec.js -* 2023-01-misc/initProto-existing-derived-constructor/exec.js -* 2023-01-misc/initializer-property-ignored/exec.js -* 2023-01-setters/context-name/exec.js -* 2023-01-setters--to-es2015/context-name/exec.js -* 2023-05-classes/ctx/exec.js -* 2023-05-classes/decorator-access-modified-classes/exec.js -* 2023-05-classes/decorator-access-modified-fields/exec.js -* 2023-05-classes/decorator-access-modified-methods/exec.js -* 2023-05-fields/context-name/exec.js -* 2023-05-fields--to-es2015/context-name/exec.js -* 2023-05-getters/context-name/exec.js -* 2023-05-getters--to-es2015/context-name/exec.js -* 2023-05-methods/context-name/exec.js -* 2023-05-methods--to-es2015/context-name/exec.js -* 2023-05-misc/class-tdz-during-decorators/exec.js -* 2023-05-misc/decorator-evaluation-scope/exec.js -* 2023-05-misc/initProto-existing-derived-constructor/exec.js -* 2023-05-misc/initializer-property-ignored/exec.js -* 2023-05-misc/this/exec.js -* 2023-05-setters/context-name/exec.js -* 2023-05-setters--to-es2015/context-name/exec.js -* 2023-11-accessors--to-es2015/private/exec.js -* 2023-11-accessors--to-es2015/public/exec.js -* 2023-11-accessors--to-es2015/undecorated-private/exec.js -* 2023-11-accessors--to-es2015/undecorated-public/exec.js -* 2023-11-classes/ctx/exec.js -* 2023-11-classes/decorator-access-modified-classes/exec.js -* 2023-11-classes/decorator-access-modified-fields/exec.js -* 2023-11-classes/decorator-access-modified-methods/exec.js -* 2023-11-classes/replacement-static-method-private-instance-element-super/exec.js -* 2023-11-classes/replacement-static-method-private-instance-element-super-assumption-ignoreFunctionLength/exec.js -* 2023-11-classes/replacement-static-private-environment-super/exec.js -* 2023-11-classes/replacement-static-private-instance-element-access/exec.js -* 2023-11-classes--to-es2015/ctx/exec.js -* 2023-11-classes--to-es2015/decorator-access-modified-classes/exec.js -* 2023-11-classes--to-es2015/decorator-access-modified-fields/exec.js -* 2023-11-classes--to-es2015/decorator-access-modified-methods/exec.js -* 2023-11-classes--to-es2015/inheritance/exec.js -* 2023-11-classes--to-es2015/initializers/exec.js -* 2023-11-classes--to-es2015/replacement/exec.js -* 2023-11-classes--to-es2015/replacement-static-method-private-instance-element-super/exec.js -* 2023-11-classes--to-es2015/replacement-static-method-private-instance-element-super-assumption-ignoreFunctionLength/exec.js -* 2023-11-classes--to-es2015/replacement-static-private-environment-super/exec.js -* 2023-11-classes--to-es2015/replacement-static-private-instance-element-access/exec.js -* 2023-11-classes--to-es2015/replacement-static-this/exec.js -* 2023-11-classes--to-es2015/replacement-with-expr/exec.js -* 2023-11-duplicated-keys--to-es2015/computed-keys-same-ast/exec.js -* 2023-11-duplicated-keys--to-es2015/computed-keys-same-value/exec.js -* 2023-11-duplicated-keys--to-es2015/method-and-field/exec.js -* 2023-11-duplicated-keys--to-es2015/methods-with-same-key/exec.js -* 2023-11-fields/context-name/exec.js -* 2023-11-fields--to-es2015/context-name/exec.js -* 2023-11-fields--to-es2015/private/exec.js -* 2023-11-fields--to-es2015/public/exec.js -* 2023-11-fields--to-es2015/static-private/exec.js -* 2023-11-fields--to-es2015/static-public/exec.js -* 2023-11-getters/context-name/exec.js -* 2023-11-getters--to-es2015/context-name/exec.js -* 2023-11-getters--to-es2015/private/exec.js -* 2023-11-getters--to-es2015/public/exec.js -* 2023-11-getters--to-es2015/static-private/exec.js -* 2023-11-getters--to-es2015/static-public/exec.js -* 2023-11-getters-and-setters--to-es2015/private/exec.js -* 2023-11-getters-and-setters--to-es2015/public/exec.js -* 2023-11-getters-and-setters--to-es2015/static-private/exec.js -* 2023-11-getters-and-setters--to-es2015/static-public/exec.js -* 2023-11-methods/context-name/exec.js -* 2023-11-methods/private-async-and-generator/exec.js -* 2023-11-methods--to-es2015/context-name/exec.js -* 2023-11-methods--to-es2015/private/exec.js -* 2023-11-methods--to-es2015/private-async-and-generator/exec.js -* 2023-11-methods--to-es2015/private-with-initializers/exec.js -* 2023-11-methods--to-es2015/public/exec.js -* 2023-11-methods--to-es2015/public-with-initializers/exec.js -* 2023-11-methods--to-es2015/static-private/exec.js -* 2023-11-methods--to-es2015/static-private-with-initializers/exec.js -* 2023-11-methods--to-es2015/static-public/exec.js -* 2023-11-methods--to-es2015/static-public-with-initializers/exec.js -* 2023-11-misc/class-in-for-loop/exec.js -* 2023-11-misc/class-not-available-during-decorators/exec.js -* 2023-11-misc/class-underscore-property/exec.js -* 2023-11-misc/decorator-evaluation-arguments/exec.js -* 2023-11-misc/decorator-evaluation-new-target/exec.js -* 2023-11-misc/decorator-evaluation-this/exec.js -* 2023-11-misc/initProto-existing-derived-constructor/exec.js -* 2023-11-misc/initializer-property-ignored/exec.js -* 2023-11-misc/private-name-in-class-decorator/exec.js -* 2023-11-misc/this/exec.js -* 2023-11-misc--to-es2015/class-and-method-decorators/exec.js -* 2023-11-misc--to-es2015/class-and-property-decorators/exec.js -* 2023-11-misc--to-es2015/class-in-for-loop/exec.js -* 2023-11-misc--to-es2015/class-underscore-property/exec.js -* 2023-11-misc--to-es2015/decorator-evaluation-arguments/exec.js -* 2023-11-misc--to-es2015/decorator-evaluation-new-target/exec.js -* 2023-11-misc--to-es2015/decorator-evaluation-this/exec.js -* 2023-11-misc--to-es2015/initProto-existing-derived-constructor/exec.js -* 2023-11-misc--to-es2015/initializer-property-ignored/exec.js -* 2023-11-misc--to-es2015/initializer-timing/exec.js -* 2023-11-misc--to-es2015/leaked-context-addInitializer/exec.js -* 2023-11-misc--to-es2015/leaked-context-addInitializer-throw/exec.js -* 2023-11-misc--to-es2015/leaked-context-addInitializer-throw-2/exec.js -* 2023-11-misc--to-es2015/private-name-in-class-decorator/exec.js -* 2023-11-misc--to-es2015/this/exec.js -* 2023-11-ordering/accessor-init-and-set-consistent/exec.js -* 2023-11-ordering/accessor-initializers-fields/exec.js -* 2023-11-ordering/accessor-initializers-setters/exec.js -* 2023-11-ordering/accessor-method-initializers/exec.js -* 2023-11-ordering/accessor-static-method-initializers/exec.js -* 2023-11-ordering/class-decorators-without-element-decorators/exec.js -* 2023-11-ordering/field/exec.js -* 2023-11-ordering/field-initializers-after-methods/exec.js -* 2023-11-ordering/field-initializers-after-methods-private/exec.js -* 2023-11-ordering/method-initializers-field-value/exec.js -* 2023-11-ordering/static-field-initializers-after-methods/exec.js -* 2023-11-ordering--to-es2015/accessor-initializers-fields/exec.js -* 2023-11-ordering--to-es2015/accessor-initializers-setters/exec.js -* 2023-11-ordering--to-es2015/accessor-method-initializers/exec.js -* 2023-11-ordering--to-es2015/accessor-static-method-initializers/exec.js -* 2023-11-ordering--to-es2015/class-decorators-without-element-decorators/exec.js -* 2023-11-ordering--to-es2015/decorators-evaluation-with-this-caching/exec.js -* 2023-11-ordering--to-es2015/field/exec.js -* 2023-11-ordering--to-es2015/field-initializers-after-methods/exec.js -* 2023-11-ordering--to-es2015/method-initializers-field-value/exec.js -* 2023-11-ordering--to-es2015/static-field-initializers-after-methods/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-accessor-decorator-return/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-add-initializer/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-class-decorator-return/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-field-decorator-return/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-getter-decorator-return/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-method-decorator-return/exec.js -* 2023-11-runtime-errors--to-es2015/invalid-setter-decorator-return/exec.js -* 2023-11-setters/context-name/exec.js -* 2023-11-setters--to-es2015/context-name/exec.js -* 2023-11-setters--to-es2015/private/exec.js -* 2023-11-setters--to-es2015/public/exec.js -* 2023-11-setters--to-es2015/static-private/exec.js -* 2023-11-setters--to-es2015/static-public/exec.js -* 2023-11-typescript/computed-key-ts-as-expression/exec.ts -* legacy-class-constructors/mutate-existing-constructor/exec.js -* legacy-class-constructors/return-new-constructor/exec.js -* legacy-class-export-default/exec.mjs -* legacy-class-ordering/order/exec.js -* legacy-class-ordering/reverse-order/exec.js -* legacy-class-prototype-methods/mutate-descriptor/exec.js -* legacy-class-prototype-methods/numeric-props/exec.js -* legacy-class-prototype-methods/return-descriptor/exec.js -* legacy-class-prototype-methods/string-props/exec.js -* legacy-class-prototype-properties/child-classes-properties/exec.js -* legacy-class-prototype-properties/mutate-descriptor/exec.js -* legacy-class-prototype-properties/mutate-initialzer/exec.js -* legacy-class-prototype-properties/properties-without-initializer/exec.js -* legacy-class-prototype-properties/return-descriptor/exec.js -* legacy-class-prototype-properties/string-literal-properties/exec.js -* legacy-class-static-methods/mutate-descriptor/exec.js -* legacy-class-static-methods/numeric-props/exec.js -* legacy-class-static-methods/return-descriptor/exec.js -* legacy-class-static-methods/string-props/exec.js -* legacy-class-static-properties/mutate-descriptor/exec.js -* legacy-class-static-properties/mutate-initialzer/exec.js -* legacy-class-static-properties/properties-without-initializer/exec.js -* legacy-class-static-properties/return-descriptor/exec.js -* legacy-regression/8512/exec.js -* metadata/class/exec.js -* metadata/element/exec.js -* metadata/subclass-no-super-decorators/exec.js -* metadata/subclass-super-decorators/exec.js diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index 3e42de1a55c9f..99fcdd3bacf3f 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,9 +1,6 @@ -Passed: 0/1 +Passed: 0/0 # All Passed: -# babel-plugin-transform-optional-catch-binding (0/1) -* try-catch-shadow/input.js - diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index 8030ef9302aff..09d4eb9fce224 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -45,56 +45,57 @@ fn fixture_root() -> PathBuf { } const CASES: &[&str] = &[ - // ES2024 - "babel-plugin-transform-unicode-sets-regex", - // ES2022 - "babel-plugin-transform-class-properties", - "babel-plugin-transform-class-static-block", - "babel-plugin-transform-private-methods", - "babel-plugin-transform-private-property-in-object", - // [Syntax] "babel-plugin-transform-syntax-top-level-await", - // ES2021 - "babel-plugin-transform-logical-assignment-operators", - "babel-plugin-transform-numeric-separator", - // ES2020 - "babel-plugin-transform-export-namespace-from", - "babel-plugin-transform-dynamic-import", - "babel-plugin-transform-nullish-coalescing-operator", - "babel-plugin-transform-optional-chaining", - // [Syntax] "babel-plugin-transform-syntax-bigint", - // [Syntax] "babel-plugin-transform-syntax-dynamic-import", - // [Syntax] "babel-plugin-transform-syntax-import-meta", - // ES2019 - "babel-plugin-transform-optional-catch-binding", - "babel-plugin-transform-json-strings", - // ES2018 - "babel-plugin-transform-async-generator-functions", - "babel-plugin-transform-object-rest-spread", - // [Regex] "babel-plugin-transform-unicode-property-regex", - "babel-plugin-transform-dotall-regex", - // [Regex] "babel-plugin-transform-named-capturing-groups-regex", - // ES2017 - "babel-plugin-transform-async-to-generator", - // ES2016 - "babel-plugin-transform-exponentiation-operator", - // ES2015 - "babel-plugin-transform-arrow-functions", - "babel-plugin-transform-function-name", - "babel-plugin-transform-shorthand-properties", - "babel-plugin-transform-sticky-regex", - "babel-plugin-transform-unicode-regex", - "babel-plugin-transform-template-literals", - "babel-plugin-transform-duplicate-keys", - "babel-plugin-transform-instanceof", - "babel-plugin-transform-new-target", - // ES3 - "babel-plugin-transform-property-literals", - // TypeScript - "babel-plugin-transform-typescript", - // React - "babel-plugin-transform-react-jsx", - // Proposal - "babel-plugin-proposal-decorators", + // // ES2024 + // "babel-plugin-transform-unicode-sets-regex", + // // ES2022 + // "babel-plugin-transform-class-properties", + // "babel-plugin-transform-class-static-block", + // "babel-plugin-transform-private-methods", + // "babel-plugin-transform-private-property-in-object", + // // [Syntax] "babel-plugin-transform-syntax-top-level-await", + // // ES2021 + // "babel-plugin-transform-logical-assignment-operators", + // "babel-plugin-transform-numeric-separator", + // // ES2020 + // "babel-plugin-transform-export-namespace-from", + // "babel-plugin-transform-dynamic-import", + // "babel-plugin-transform-nullish-coalescing-operator", + // "babel-plugin-transform-optional-chaining", + // // [Syntax] "babel-plugin-transform-syntax-bigint", + // // [Syntax] "babel-plugin-transform-syntax-dynamic-import", + // // [Syntax] "babel-plugin-transform-syntax-import-meta", + // // ES2019 + // "babel-plugin-transform-optional-catch-binding", + // "babel-plugin-transform-json-strings", + // // ES2018 + // "babel-plugin-transform-async-generator-functions", + // "babel-plugin-transform-object-rest-spread", + // // [Regex] "babel-plugin-transform-unicode-property-regex", + // "babel-plugin-transform-dotall-regex", + // // [Regex] "babel-plugin-transform-named-capturing-groups-regex", + // // ES2017 + // "babel-plugin-transform-async-to-generator", + // // ES2016 + // "babel-plugin-transform-exponentiation-operator", + // // ES2015 + // "babel-plugin-transform-arrow-functions", + // "babel-plugin-transform-function-name", + // "babel-plugin-transform-shorthand-properties", + // "babel-plugin-transform-sticky-regex", + // "babel-plugin-transform-unicode-regex", + // "babel-plugin-transform-template-literals", + // "babel-plugin-transform-duplicate-keys", + // "babel-plugin-transform-instanceof", + // "babel-plugin-transform-new-target", + // // ES3 + // "babel-plugin-transform-property-literals", + // // TypeScript + // "babel-plugin-transform-typescript", + // // React + // "babel-plugin-transform-react-jsx", + "babel-plugin-transform-react-display-name", + // // Proposal + // "babel-plugin-proposal-decorators", ]; const EXCLUDE_TESTS: &[&str] = &["babel-plugin-transform-typescript/test/fixtures/enum"]; diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index e835c1c9da0a1..bdcdb97f688b5 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -14,12 +14,12 @@ use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, VALID_EXTENSIONS}; use oxc_tasks_common::{normalize_path, print_diff_in_terminal, BabelOptions}; use oxc_transformer::{ - DecoratorsOptions, ReactDisplayNameOptions, ReactJsxOptions, ReactJsxSelfOptions, - ReactJsxSourceOptions, TransformOptions, Transformer, TypeScriptOptions, + DecoratorsOptions, ReactOptions, TransformOptions, Transformer, TypeScriptOptions, }; use crate::{fixture_root, root, TestRunnerEnv}; +#[derive(Debug)] pub enum TestCaseKind { Transform(ConformanceTestCase), Exec(ExecTestCase), @@ -96,21 +96,9 @@ pub trait TestCase { .get_plugin("transform-typescript") .map(get_options::) .unwrap_or_default(), - react_display_name: options - .get_plugin("transform-react-display-name") - .map(get_options::) - .unwrap_or_default(), - react_jsx: options + react: options .get_plugin("transform-react-jsx") - .map(get_options::) - .unwrap_or_default(), - react_jsx_self: options - .get_plugin("transform-react-jsx-self") - .map(get_options::) - .unwrap_or_default(), - react_jsx_source: options - .get_plugin("transform-react-jsx-source") - .map(get_options::) + .map(get_options::) .unwrap_or_default(), } } @@ -172,6 +160,7 @@ pub trait TestCase { } } +#[derive(Debug)] pub struct ConformanceTestCase { path: PathBuf, options: BabelOptions, @@ -297,6 +286,7 @@ impl TestCase for ConformanceTestCase { } } +#[derive(Debug)] pub struct ExecTestCase { path: PathBuf, options: BabelOptions, diff --git a/wasm/parser/README.md b/wasm/parser/README.md index fdfdec730cd27..3f4d31ca79023 100644 --- a/wasm/parser/README.md +++ b/wasm/parser/README.md @@ -2,8 +2,20 @@ Experimental wasm package for the oxc parser, with full TypeScript typings support. -This package is built with `wasm-pack build --release --target web` for bundler (webpack / vite) consumption. -Checkout [oxc-parser](https://www.npmjs.com/package/oxc-parser) for usage in node.js. +This package is built with different [wasm-pack's target](https://rustwasm.github.io/docs/wasm-bindgen/reference/deployment.html) builds: + +* `wasm-pack build --target web` for bundler (webpack / vite) consumption. +* `wasm-pack build --target nodejs` for node.js + +And exports the files as + +```json +"main": "./node/oxc_parser_wasm.js", +"browser": "./web/oxc_parser_wasm.js", +"types": "./node/oxc_parser_wasm.d.ts", +``` + +Checkout [oxc-parser](https://www.npmjs.com/package/oxc-parser) for usage in node.js via napi bindings. Source code: https://github.com/oxc-project/oxc/tree/main/wasm/parser diff --git a/wasm/parser/package.json b/wasm/parser/package.json index 8f5579f7e16c6..69e4c5e68d491 100644 --- a/wasm/parser/package.json +++ b/wasm/parser/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-parser/wasm", - "version": "0.0.5", + "version": "0.1.0", "description": "Wasm target for the oxc parser.", "keywords": [ "JavaScript", @@ -18,18 +18,18 @@ "funding": { "url": "https://github.com/sponsors/Boshen" }, - "files": [ - "oxc_parser_wasm.d.ts", - "oxc_parser_wasm.js", - "oxc_parser_wasm_bg.wasm", - "oxc_parser_wasm_bg.wasm.d.ts", - "README.md" - ], - "module": "oxc_parser_wasm.js", - "types": "oxc_parser_wasm.d.ts", + "main": "./node/oxc_parser_wasm.js", + "browser": "./web/oxc_parser_wasm.js", + "types": "./node/oxc_parser_wasm.d.ts", + "files": ["node", "web"], "scripts": { - "build": "wasm-pack build --release --no-pack --target web --out-dir ../../npm/parser-wasm . && pnpm run copy-files", - "copy-files": "cp ./package.json ../../npm/parser-wasm/package.json && cp ./README.md ../../npm/parser-wasm/README.md" + "build": "pnpm run build-node && pnpm run build-web && pnpm run copy-files && pnpm run clean-files", + "build-node": "pnpm run build-base --target nodejs --out-dir ../../npm/parser-wasm/node .", + "build-web": "pnpm run build-base --target web --out-dir ../../npm/parser-wasm/web .", + "build-base": "wasm-pack build --release --no-pack", + "copy-files": "cp ./package.json ../../npm/parser-wasm/package.json && cp ./README.md ../../npm/parser-wasm/README.md", + "clean-files": "rm ../../npm/parser-wasm/*/.gitignore", + "test": "node ./test-node.mjs" }, "devDependencies": { "wasm-pack": "^0.12.1" diff --git a/wasm/parser/test-node.mjs b/wasm/parser/test-node.mjs new file mode 100644 index 0000000000000..b4a66e63458bb --- /dev/null +++ b/wasm/parser/test-node.mjs @@ -0,0 +1,7 @@ +import assert from 'assert' +import { parseSync } from "../../npm/parser-wasm/node/oxc_parser_wasm.js"; + +const code = "let foo"; +const result = parseSync(code, { sourceFilename: "test.ts" }); +assert(result.errors.length === 0); +assert(result.program.body.length === 1);