diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index ee572ba1054d7..1ebef5b21059b 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1049,7 +1049,7 @@ pub enum Declaration<'a> { impl<'a> Declaration<'a> { pub fn is_typescript_syntax(&self) -> bool { match self { - Self::VariableDeclaration(_) => false, + Self::VariableDeclaration(decl) => decl.is_typescript_syntax(), Self::FunctionDeclaration(func) => func.is_typescript_syntax(), Self::ClassDeclaration(class) => class.is_declare(), _ => true, @@ -1069,6 +1069,12 @@ pub struct VariableDeclaration<'a> { pub modifiers: Modifiers<'a>, } +impl<'a> VariableDeclaration<'a> { + pub fn is_typescript_syntax(&self) -> bool { + self.modifiers.contains(ModifierKind::Declare) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "lowercase"))] pub enum VariableDeclarationKind { diff --git a/crates/oxc_codegen/examples/codegen.rs b/crates/oxc_codegen/examples/codegen.rs index 4d3d20de59ccb..ad4a3b4743a8b 100644 --- a/crates/oxc_codegen/examples/codegen.rs +++ b/crates/oxc_codegen/examples/codegen.rs @@ -10,7 +10,7 @@ use oxc_span::SourceType; // 2. run `cargo run -p oxc_codegen --example codegen` or `just example codegen` fn main() { - let name = env::args().nth(1).unwrap_or_else(|| "test.ts".to_string()); + let name = env::args().nth(1).unwrap_or_else(|| "test.tsx".to_string()); let path = Path::new(&name); let source_text = std::fs::read_to_string(path).unwrap_or_else(|_| panic!("{name} not found")); let source_type = SourceType::from_path(path).unwrap(); @@ -25,11 +25,16 @@ fn main() { return; } + println!("Original:"); + println!("{source_text}"); + let codegen_options = CodegenOptions; - let printed = Codegen::::new(source_text.len(), codegen_options).build(&ret.program); + let printed = Codegen::::new(source_text.len(), codegen_options).build(&ret.program); + println!("Printed:"); println!("{printed}"); let ret = Parser::new(&allocator, &printed, source_type).parse(); - let printed = Codegen::::new(source_text.len(), codegen_options).build(&ret.program); - println!("{printed}"); + let minified = Codegen::::new(source_text.len(), codegen_options).build(&ret.program); + println!("Minified:"); + println!("{minified}"); } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 80af6f64027df..7ad9ce1b057f6 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -65,9 +65,6 @@ fn print_directives_and_statements( p.print_semicolon_if_needed(); stmt.gen(p, ctx); } - if !MINIFY && !statements.is_empty() { - p.print_semicolon_if_needed(); - } } impl Gen for Hashbang { @@ -80,9 +77,9 @@ impl Gen for Hashbang { impl Gen for Directive { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); - p.print(b'"'); + p.print(b'\''); p.print_str(self.directive.as_bytes()); - p.print(b'"'); + p.print(b'\''); p.print_semicolon(); } } @@ -114,15 +111,6 @@ impl<'a, const MINIFY: bool> Gen for Statement<'a> { } } -impl<'a, const MINIFY: bool> Gen for Option> { - fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { - match self { - Some(stmt) => stmt.gen(p, ctx), - None => p.print(b';'), - } - } -} - impl<'a, const MINIFY: bool> Gen for ExpressionStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); @@ -494,7 +482,7 @@ impl<'a, const MINIFY: bool> Gen for ModuleDeclaration<'a> { Self::ExportAllDeclaration(decl) => decl.gen(p, ctx), Self::ExportDefaultDeclaration(decl) => decl.gen(p, ctx), Self::ExportNamedDeclaration(decl) => decl.gen(p, ctx), - _ => {} + _ => p.needs_semicolon = false, } } } @@ -502,19 +490,23 @@ impl<'a, const MINIFY: bool> Gen for ModuleDeclaration<'a> { impl<'a, const MINIFY: bool> Gen for Declaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::VariableDeclaration(stmt) => { - p.print_indent(); - stmt.gen(p, ctx); - p.print_semicolon_after_statement(); + Self::VariableDeclaration(decl) => { + if !decl.is_typescript_syntax() { + p.print_indent(); + decl.gen(p, ctx); + p.print_semicolon_after_statement(); + } } - Self::FunctionDeclaration(stmt) => { - p.print_indent(); - p.print_space_before_identifier(); - stmt.gen(p, ctx); + Self::FunctionDeclaration(decl) => { + if !decl.is_typescript_syntax() { + p.print_indent(); + p.print_space_before_identifier(); + decl.gen(p, ctx); + } } - Self::ClassDeclaration(declaration) => { + Self::ClassDeclaration(decl) => { p.print_space_before_identifier(); - declaration.gen(p, ctx); + decl.gen(p, ctx); } Self::UsingDeclaration(declaration) => { p.print_space_before_identifier(); @@ -738,7 +730,7 @@ impl<'a, const MINIFY: bool> Gen for ExportNamedDeclaration<'a> { p.print_str(b"from"); source.gen(p, ctx); } - p.print_semicolon_after_statement(); + p.needs_semicolon = true; } } } @@ -1747,8 +1739,8 @@ impl<'a, const MINIFY: bool> Gen for JSXAttributeName<'a> { impl<'a, const MINIFY: bool> Gen for JSXAttribute<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.name.gen(p, ctx); - p.print(b'='); if let Some(value) = &self.value { + p.print(b'='); value.gen(p, ctx); } } @@ -1788,7 +1780,9 @@ impl<'a, const MINIFY: bool> Gen for JSXAttributeValue<'a> { impl<'a, const MINIFY: bool> Gen for JSXSpreadAttribute<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { + p.print_str(b"{..."); self.argument.gen_expr(p, Precedence::lowest(), Context::default()); + p.print(b'}'); } } @@ -1806,6 +1800,7 @@ impl<'a, const MINIFY: bool> Gen for JSXOpeningElement<'a> { p.print_str(b"<"); self.name.gen(p, ctx); for attr in &self.attributes { + p.print_hard_space(); attr.gen(p, ctx); } if self.self_closing { diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index a1a66dabdf3a2..d1f56eb9126c6 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -88,7 +88,7 @@ impl Codegen { } } - // pub fn with_mangler(&mut self, mangler: Mangler) { + // fn with_mangler(&mut self, mangler: Mangler) { // self.mangler = Some(mangler); // } @@ -97,7 +97,7 @@ impl Codegen { self.into_code() } - fn into_code(self) -> String { + pub fn into_code(self) -> String { unsafe { String::from_utf8_unchecked(self.code) } } @@ -129,8 +129,7 @@ impl Codegen { self.print(b' '); } - #[inline] - pub fn print_soft_newline(&mut self) { + fn print_soft_newline(&mut self) { if !MINIFY { self.print(b'\n'); } @@ -157,21 +156,19 @@ impl Codegen { unsafe { from_utf8_unchecked(self.code()) }.chars().nth_back(n) } - #[inline] - pub fn indent(&mut self) { + fn indent(&mut self) { if !MINIFY { self.indentation += 1; } } - #[inline] - pub fn dedent(&mut self) { + fn dedent(&mut self) { if !MINIFY { self.indentation -= 1; } } - pub fn print_indent(&mut self) { + fn print_indent(&mut self) { if !MINIFY { for _ in 0..self.indentation { self.print(b'\t'); @@ -219,14 +216,12 @@ impl Codegen { } } - #[inline] fn print_block_start(&mut self) { self.print(b'{'); self.print_soft_newline(); self.indent(); } - #[inline] fn print_block_end(&mut self) { self.dedent(); self.print_indent(); diff --git a/tasks/coverage/codegen_typescript.snap b/tasks/coverage/codegen_typescript.snap index 56c4bd4de684f..48dfc52a25550 100644 --- a/tasks/coverage/codegen_typescript.snap +++ b/tasks/coverage/codegen_typescript.snap @@ -1,22 +1,14 @@ codegen_typescript Summary: AST Parsed : 5063/5063 (100.00%) -Positive Passed: 4867/5063 (96.13%) +Positive Passed: 4968/5063 (98.12%) Expect to Parse: "compiler/binopAssignmentShouldHaveType.ts" Expect to Parse: "compiler/callOfConditionalTypeWithConcreteBranches.ts" -Expect to Parse: "compiler/callsOnComplexSignatures.tsx" Expect to Parse: "compiler/castExpressionParentheses.ts" Expect to Parse: "compiler/collisionExportsRequireAndInternalModuleAliasInGlobalFile.ts" -Expect to Parse: "compiler/collisionRestParameterFunctionExpressions.ts" Expect to Parse: "compiler/collisionThisExpressionAndAliasInGlobal.ts" Expect to Parse: "compiler/commentEmitAtEndOfFile1.ts" -Expect to Parse: "compiler/computedPropertyNameAndTypeParameterConflict.ts" -Expect to Parse: "compiler/conditionalEqualityTestingNullability.ts" -Expect to Parse: "compiler/conditionallyDuplicateOverloadsCausedByOverloadResolution.ts" -Expect to Parse: "compiler/constDeclarations-ambient.ts" -Expect to Parse: "compiler/contextualTypingOfOptionalMembers.tsx" +Expect to Parse: "compiler/commentsemitComments.ts" Expect to Parse: "compiler/contextualTypingOfTooShortOverloads.ts" -Expect to Parse: "compiler/contextuallyTypedJsxAttribute.ts" -Expect to Parse: "compiler/contextuallyTypedJsxChildren.tsx" Expect to Parse: "compiler/declarationEmitDestructuringObjectLiteralPattern2.ts" Expect to Parse: "compiler/declarationEmitNonExportedBindingPattern.ts" Expect to Parse: "compiler/declarationEmitRetainsJsdocyComments.ts" @@ -24,7 +16,7 @@ Expect to Parse: "compiler/declarationMaps.ts" Expect to Parse: "compiler/declarationTypecheckNoUseBeforeReferenceCheck.ts" Expect to Parse: "compiler/declareFileExportAssignment.ts" Expect to Parse: "compiler/declareFileExportAssignmentWithVarFromVariableStatement.ts" -Expect to Parse: "compiler/discriminatedUnionJsxElement.tsx" +Expect to Parse: "compiler/doNotEmitPinnedCommentOnNotEmittedNode.ts" Expect to Parse: "compiler/dottedModuleName2.ts" Expect to Parse: "compiler/downlevelLetConst13.ts" Expect to Parse: "compiler/duplicateVarAndImport.ts" @@ -42,73 +34,38 @@ Expect to Parse: "compiler/exportAsNamespace.d.ts" Expect to Parse: "compiler/exportAssignValueAndType.ts" Expect to Parse: "compiler/exportAssignmentWithPrivacyError.ts" Expect to Parse: "compiler/exportAssignmentWithoutIdentifier1.ts" -Expect to Parse: "compiler/exportDefaultVariable.ts" Expect to Parse: "compiler/exportEqualNamespaces.ts" -Expect to Parse: "compiler/functionReturnTypeQuery.ts" -Expect to Parse: "compiler/genericInferenceDefaultTypeParameterJsxReact.tsx" Expect to Parse: "compiler/genericOverloadSignatures.ts" Expect to Parse: "compiler/identityRelationNeverTypes.ts" Expect to Parse: "compiler/indexingTypesWithNever.ts" Expect to Parse: "compiler/inferenceErasedSignatures.ts" -Expect to Parse: "compiler/innerOverloads.ts" Expect to Parse: "compiler/interfaceWithCommaSeparators.ts" -Expect to Parse: "compiler/jsxCallbackWithDestructuring.tsx" -Expect to Parse: "compiler/jsxComplexSignatureHasApplicabilityError.tsx" -Expect to Parse: "compiler/jsxElementClassTooManyParams.tsx" -Expect to Parse: "compiler/jsxEmitAttributeWithPreserve.tsx" -Expect to Parse: "compiler/jsxFragmentFactoryNoUnusedLocals.tsx" -Expect to Parse: "compiler/jsxGenericComponentWithSpreadingResultOfGenericFunction.tsx" -Expect to Parse: "compiler/jsxHasLiteralType.tsx" -Expect to Parse: "compiler/jsxInferenceProducesLiteralAsExpected.tsx" -Expect to Parse: "compiler/jsxIntrinsicElementsCompatability.tsx" -Expect to Parse: "compiler/jsxIntrinsicUnions.tsx" -Expect to Parse: "compiler/jsxLibraryManagedAttributesUnusedGeneric.tsx" Expect to Parse: "compiler/jsxMultilineAttributeStringValues.tsx" Expect to Parse: "compiler/jsxMultilineAttributeValuesReact.tsx" Expect to Parse: "compiler/jsxNamespacedNameNotComparedToNonMatchingIndexSignature.tsx" -Expect to Parse: "compiler/jsxPartialSpread.tsx" -Expect to Parse: "compiler/jsxPropsAsIdentifierNames.tsx" -Expect to Parse: "compiler/jsxSpreadFirstUnionNoErrors.tsx" -Expect to Parse: "compiler/jsxSpreadTag.ts" Expect to Parse: "compiler/keyofObjectWithGlobalSymbolIncluded.ts" -Expect to Parse: "compiler/keywordInJsxIdentifier.tsx" Expect to Parse: "compiler/localClassesInLoop.ts" Expect to Parse: "compiler/localClassesInLoop_ES6.ts" Expect to Parse: "compiler/moduleVariables.ts" Expect to Parse: "compiler/module_augmentUninstantiatedModule2.ts" Expect to Parse: "compiler/numericIndexerConstraint3.ts" Expect to Parse: "compiler/objectAssignLikeNonUnionResult.ts" -Expect to Parse: "compiler/overloadCallTest.ts" -Expect to Parse: "compiler/parseJsxExtends1.ts" Expect to Parse: "compiler/privacyCheckAnonymousFunctionParameter.ts" Expect to Parse: "compiler/privacyCheckAnonymousFunctionParameter2.ts" Expect to Parse: "compiler/privacyCheckExportAssignmentOnExportedGenericInterface1.ts" Expect to Parse: "compiler/privacyImport.ts" -Expect to Parse: "compiler/reactHOCSpreadprops.tsx" -Expect to Parse: "compiler/reactNamespaceJSXEmit.tsx" -Expect to Parse: "compiler/reactReadonlyHOCAssignabilityReal.tsx" -Expect to Parse: "compiler/reactTagNameComponentWithPropsNoOOM.tsx" -Expect to Parse: "compiler/reactTagNameComponentWithPropsNoOOM2.tsx" Expect to Parse: "compiler/reducibleIndexedAccessTypes.ts" Expect to Parse: "compiler/staticAnonymousTypeNotReferencingTypeParameter.ts" Expect to Parse: "compiler/styledComponentsInstantiaionLimitNotReached.ts" Expect to Parse: "compiler/templateLiteralIntersection.ts" -Expect to Parse: "compiler/tsxAttributeQuickinfoTypesSameAsObjectLiteral.tsx" -Expect to Parse: "compiler/tsxDiscriminantPropertyInference.tsx" -Expect to Parse: "compiler/tsxInferenceShouldNotYieldAnyOnUnions.tsx" -Expect to Parse: "compiler/tsxSpreadDoesNotReportExcessProps.tsx" -Expect to Parse: "compiler/tsxUnionMemberChecksFilterDataProps.tsx" Expect to Parse: "compiler/typeAliasDeclarationEmit3.ts" -Expect to Parse: "compiler/typeInferenceWithExcessPropertiesJsx.tsx" Expect to Parse: "conformance/ambient/ambientDeclarations.ts" -Expect to Parse: "conformance/ambient/ambientInsideNonAmbientExternalModule.ts" Expect to Parse: "conformance/classes/classExpression.ts" Expect to Parse: "conformance/classes/propertyMemberDeclarations/staticPropertyNameConflicts.ts" Expect to Parse: "conformance/constEnums/constEnum4.ts" Expect to Parse: "conformance/decorators/legacyDecorators-contextualTypes.ts" Expect to Parse: "conformance/es6/Symbols/symbolProperty55.ts" Expect to Parse: "conformance/es6/Symbols/symbolProperty56.ts" -Expect to Parse: "conformance/es6/Symbols/symbolProperty60.ts" Expect to Parse: "conformance/es6/yieldExpressions/generatorTypeCheck59.ts" Expect to Parse: "conformance/es6/yieldExpressions/generatorTypeCheck61.ts" Expect to Parse: "conformance/esDecorators/esDecorators-contextualTypes.ts" @@ -126,65 +83,10 @@ Expect to Parse: "conformance/interfaces/declarationMerging/twoMergedInterfacesW Expect to Parse: "conformance/interfaces/interfaceDeclarations/asiPreventsParsingAsInterface03.ts" Expect to Parse: "conformance/internalModules/moduleDeclarations/asiPreventsParsingAsNamespace03.ts" Expect to Parse: "conformance/internalModules/moduleDeclarations/nonInstantiatedModule.ts" -Expect to Parse: "conformance/jsx/checkJsxChildrenProperty1.tsx" -Expect to Parse: "conformance/jsx/checkJsxChildrenProperty12.tsx" -Expect to Parse: "conformance/jsx/checkJsxChildrenProperty16.tsx" -Expect to Parse: "conformance/jsx/checkJsxChildrenProperty6.tsx" -Expect to Parse: "conformance/jsx/checkJsxChildrenProperty8.tsx" -Expect to Parse: "conformance/jsx/checkJsxIntersectionElementPropsType.tsx" -Expect to Parse: "conformance/jsx/checkJsxSubtleSkipContextSensitiveBug.tsx" -Expect to Parse: "conformance/jsx/checkJsxUnionSFXContextualTypeInferredCorrectly.tsx" Expect to Parse: "conformance/jsx/jsxReactTestSuite.tsx" -Expect to Parse: "conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx" -Expect to Parse: "conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx" -Expect to Parse: "conformance/jsx/tsxAttributeResolution16.tsx" -Expect to Parse: "conformance/jsx/tsxDefaultAttributesResolution1.tsx" -Expect to Parse: "conformance/jsx/tsxDefaultAttributesResolution2.tsx" -Expect to Parse: "conformance/jsx/tsxElementResolution.tsx" -Expect to Parse: "conformance/jsx/tsxElementResolution13.tsx" -Expect to Parse: "conformance/jsx/tsxElementResolution14.tsx" -Expect to Parse: "conformance/jsx/tsxElementResolution5.tsx" -Expect to Parse: "conformance/jsx/tsxEmit1.tsx" -Expect to Parse: "conformance/jsx/tsxEmit2.tsx" -Expect to Parse: "conformance/jsx/tsxEmitSpreadAttribute.ts" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType1.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType2.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType3.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType4.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType5.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType6.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType7.tsx" -Expect to Parse: "conformance/jsx/tsxGenericAttributesType9.tsx" -Expect to Parse: "conformance/jsx/tsxInArrowFunction.tsx" Expect to Parse: "conformance/jsx/tsxNamespacedAttributeName1.tsx" Expect to Parse: "conformance/jsx/tsxNamespacedAttributeName2.tsx" -Expect to Parse: "conformance/jsx/tsxReactComponentWithDefaultTypeParameter1.tsx" -Expect to Parse: "conformance/jsx/tsxReactComponentWithDefaultTypeParameter2.tsx" -Expect to Parse: "conformance/jsx/tsxReactEmit1.tsx" -Expect to Parse: "conformance/jsx/tsxReactEmit2.tsx" -Expect to Parse: "conformance/jsx/tsxReactEmit8.tsx" Expect to Parse: "conformance/jsx/tsxReactEmitEntities.tsx" -Expect to Parse: "conformance/jsx/tsxReactEmitNesting.tsx" -Expect to Parse: "conformance/jsx/tsxReactEmitSpreadAttribute.ts" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution11.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution13.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution15.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution3.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution8.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadAttributesResolution9.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadChildren.tsx" -Expect to Parse: "conformance/jsx/tsxSpreadChildrenInvalidType.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentOverload2.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentOverload3.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentOverload6.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentWithDefaultTypeParameter1.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentsWithTypeArguments1.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentsWithTypeArguments3.tsx" -Expect to Parse: "conformance/jsx/tsxStatelessFunctionComponentsWithTypeArguments5.tsx" -Expect to Parse: "conformance/jsx/tsxTypeErrors.tsx" -Expect to Parse: "conformance/jsx/tsxUnionElementType5.tsx" -Expect to Parse: "conformance/jsx/tsxUnionTypeComponent1.tsx" -Expect to Parse: "conformance/jsx/unicodeEscapesInJsxtags.tsx" Expect to Parse: "conformance/types/namedTypes/optionalMethods.ts" Expect to Parse: "conformance/types/rest/genericRestParameters2.ts" Expect to Parse: "conformance/types/stringLiteral/stringLiteralTypesOverloads01.ts" @@ -193,7 +95,4 @@ Expect to Parse: "conformance/types/thisType/inferThisType.ts" Expect to Parse: "conformance/types/thisType/thisTypeInFunctions4.ts" Expect to Parse: "conformance/types/typeAliases/asiPreventsParsingAsTypeAlias02.ts" Expect to Parse: "conformance/types/typeAliases/circularTypeAliasForUnionWithInterface.ts" -Expect to Parse: "conformance/types/typeRelationships/recursiveTypes/recursiveTypesUsedAsFunctionParameters.ts" Expect to Parse: "conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembersOptionality.ts" -Expect to Parse: "conformance/types/typeRelationships/typeAndMemberIdentity/typeParametersAreIdenticalToThemselves.ts" -Expect to Parse: "conformance/types/typeRelationships/typeInference/intraExpressionInferencesJsx.tsx"