diff --git a/src/NUglify.Tests/Core/CommandLine.cs b/src/NUglify.Tests/Core/CommandLine.cs index 99c24ddf..d5575d1c 100644 --- a/src/NUglify.Tests/Core/CommandLine.cs +++ b/src/NUglify.Tests/Core/CommandLine.cs @@ -79,47 +79,45 @@ public void ToArguments() [Test] public void ToSettings() { - var testData = new ArgumentsSettings[] { - new ArgumentsSettings(){CommandLine="-warn:4 -ei:utf-8 -enc:out utf-8 /g:jQuery,$,Msn -p", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines, LocalRenaming=LocalRenaming.KeepAll, KnownGlobalNamesList="jQuery,$,Msn", MinifyCode=false, KillSwitch=-2}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines, KillSwitch=-2}, WarningLevel=4, EncodingInputName="utf-8", EncodingOutputName="utf-8"}, - new ArgumentsSettings(){CommandLine="-minify:false -rename:none", JSSettings=new CodeSettings(){MinifyCode=false, LocalRenaming=LocalRenaming.KeepAll}, CssSettings=null, WarningLevel=0}, - new ArgumentsSettings(){CommandLine="-define:foo,bar,ack,gag,42", JSSettings=new CodeSettings(){PreprocessorDefineList="foo,bar,ack,gag"}, CssSettings=new CssSettings(){PreprocessorDefineList="foo,bar,ack,gag"}}, - new ArgumentsSettings(){CommandLine="-ignore:foo,bar,ack,gag", JSSettings=new CodeSettings(){IgnoreErrorList="foo,bar,ack,gag"}, CssSettings=new CssSettings(){IgnoreErrorList="foo,bar,ack,gag"}}, - new ArgumentsSettings(){CommandLine="/aspnet:T -pretty:8 -term:Yes", JSSettings=new CodeSettings(){AllowEmbeddedAspNetBlocks=true,TermSemicolons=true,LocalRenaming=LocalRenaming.KeepAll,OutputMode=OutputMode.MultipleLines,IndentSize=8, MinifyCode=false, KillSwitch=-2}, CssSettings=new CssSettings(){AllowEmbeddedAspNetBlocks=true,TermSemicolons=true,OutputMode=OutputMode.MultipleLines,IndentSize=8, KillSwitch=-2}}, - new ArgumentsSettings(){CommandLine="/aspnet:F -minify:1 -kill:0 -pretty:0 -term:N", JSSettings=new CodeSettings(){AllowEmbeddedAspNetBlocks=false,TermSemicolons=false,LocalRenaming=LocalRenaming.KeepAll,OutputMode=OutputMode.MultipleLines,IndentSize=0}, CssSettings=new CssSettings(){AllowEmbeddedAspNetBlocks=false,TermSemicolons=false,OutputMode=OutputMode.MultipleLines,IndentSize=0}}, - new ArgumentsSettings(){CommandLine="-expr:minify -colors:hex -comments:All", JSSettings=null, CssSettings=new CssSettings(){MinifyExpressions=true,ColorNames=CssColor.Hex,CommentMode=CssComment.All}}, - new ArgumentsSettings(){CommandLine="-expr:raw -colors:MAJOR /comments:HaCkS", JSSettings=null, CssSettings=new CssSettings(){MinifyExpressions=false,ColorNames=CssColor.Major,CommentMode=CssComment.Hacks}}, - new ArgumentsSettings(){CommandLine="-cc:1 -comments:None -debug:true -inline:yes -literals:keep -literals:evAL -mac:Y -minify:T -new:Keep -reorder:yes -unused:remove -rename:all", - JSSettings=new CodeSettings(){IgnoreConditionalCompilation=false,PreserveImportantComments=false,StripDebugStatements=false,InlineSafeStrings=true,EvalLiteralExpressions=true,MacSafariQuirks=true,MinifyCode=true,CollapseToLiteral=false,ReorderScopeDeclarations=true,RemoveUnneededCode=true,LocalRenaming=LocalRenaming.CrunchAll, PreprocessorDefineList="debug"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-cc:0 -comments:ImportanT -debug:false -inline:no /literals:combine -literals:NoEval -mac:N -minify:F -new:Collapse -reorder:N -unused:keep -rename:localization", - JSSettings=new CodeSettings(){IgnoreConditionalCompilation=true,PreserveImportantComments=true,StripDebugStatements=true,InlineSafeStrings=false,EvalLiteralExpressions=false,MacSafariQuirks=false,MinifyCode=false,CollapseToLiteral=true,ReorderScopeDeclarations=false,RemoveUnneededCode=false, LocalRenaming=LocalRenaming.KeepLocalizationVars}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="–debug:,", JSSettings=new CodeSettings(){StripDebugStatements=false,DebugLookupList="",PreprocessorDefineList="debug"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-debug:N,", JSSettings=new CodeSettings(){StripDebugStatements=true,DebugLookupList=""}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-debug:N,Foo,Bar,Ack.Gag.Barf,14,Name.42.First", JSSettings=new CodeSettings(){StripDebugStatements=true,DebugLookupList="Foo,Bar,Ack.Gag.Barf"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-global:foo,bar,ack,gag,212", JSSettings=new CodeSettings(){KnownGlobalNamesList="foo,bar,ack,gag"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-norename:foo,bar,ack,gag,105 -rename:NoProps", JSSettings=new CodeSettings(){NoAutoRenameList="foo,bar,ack,gag", ManualRenamesProperties=false}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-rename:foo=bar,ack=gag,105=106", JSSettings=new CodeSettings(){RenamePairs="foo=bar,ack=gag", ManualRenamesProperties=true}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="–fnames:lock -evals:ignore", JSSettings=new CodeSettings(){PreserveFunctionNames=true, RemoveFunctionExpressionNames=false, EvalTreatment=EvalTreatment.Ignore}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-fnames:keep -evals:immediate", JSSettings=new CodeSettings(){PreserveFunctionNames=false, RemoveFunctionExpressionNames=false, EvalTreatment=EvalTreatment.MakeImmediateSafe}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-fnames:onlyref -evals:safeall", JSSettings=new CodeSettings(){PreserveFunctionNames=false, RemoveFunctionExpressionNames=true, EvalTreatment=EvalTreatment.MakeAllSafe}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="–kill:-1", JSSettings=new CodeSettings(){KillSwitch=-1}, CssSettings=new CssSettings(){KillSwitch=-1,CommentMode=CssComment.None}}, - new ArgumentsSettings(){CommandLine="–kill:0x1", JSSettings=new CodeSettings(){KillSwitch=1}, CssSettings=new CssSettings(){KillSwitch=1,CommentMode=CssComment.None}}, - new ArgumentsSettings(){CommandLine="-kill:2", JSSettings=new CodeSettings(){KillSwitch=2}, CssSettings=new CssSettings(){KillSwitch=2}}, - new ArgumentsSettings(){CommandLine="-kill:0xDAB0 -cc:BOOYAH! -warn:-1", JSSettings=new CodeSettings(){KillSwitch=0xdab0}, CssSettings=new CssSettings(){KillSwitch=55984}, WarningLevel=0}, - new ArgumentsSettings(){CommandLine="-enc:in ascii -EO:big5 -warn", JSSettings=null, CssSettings=null, EncodingInputName="ascii", EncodingOutputName="big5", WarningLevel=int.MaxValue}, - new ArgumentsSettings(){CommandLine="-css /js", JSSettings=new CodeSettings(), CssSettings=new CssSettings()}, - new ArgumentsSettings(){CommandLine="-rename:All -pretty:WHAM", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-16777218}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-2}}, - new ArgumentsSettings(){CommandLine="-rename:All -pretty:-10", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-16777218}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-2}}, - new ArgumentsSettings(){CommandLine="–rename:foo=bar,foo=ack", JSSettings=new CodeSettings(){RenamePairs="foo=bar"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="–d -h -j -k -m", JSSettings=new CodeSettings(), CssSettings=null}, - new ArgumentsSettings(){CommandLine="-l -z -hCL", JSSettings=new CodeSettings() {CollapseToLiteral=false, TermSemicolons=true, LocalRenaming=LocalRenaming.KeepLocalizationVars}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="/HC", JSSettings=new CodeSettings() {}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-define:debug", JSSettings=new CodeSettings() {StripDebugStatements=false, PreprocessorDefineList="debug"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-define:debug -debug:0", JSSettings=new CodeSettings() {StripDebugStatements=true}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-debug:0 -define:debug", JSSettings=new CodeSettings() {StripDebugStatements=false, PreprocessorDefineList="debug"}, CssSettings=null}, - new ArgumentsSettings(){CommandLine="-define:foo=bar,bob,ack=gag,BaT=CrAzY", JSSettings=new CodeSettings() {PreprocessorDefineList="foo=bar,bob,ack=gag,BaT=CrAzY"}, CssSettings=new CssSettings(){PreprocessorDefineList="foo=bar,bob,ack=gag,BaT=CrAzY"}}, - new ArgumentsSettings(){CommandLine="-define:foo=", JSSettings=null, CssSettings=new CssSettings(){PreprocessorDefineList="foo"}}, - new ArgumentsSettings(){CommandLine="-define:configuration=Debug -define:debug=Y,ralph=driver", JSSettings=new CodeSettings() {StripDebugStatements=false,PreprocessorDefineList="configuration=Debug,debug=Y,ralph=driver"}, CssSettings=new CssSettings(){PreprocessorDefineList="configuration=Debug,debug=Y,ralph=driver"}}, - new ArgumentsSettings(){CommandLine="-define:once=first -define:OnCE=Last", JSSettings=new CodeSettings() {PreprocessorDefineList="once=Last"}, CssSettings=new CssSettings(){PreprocessorDefineList="once=Last"}}, + var testData = new [] { + new ArgumentsSettings{CommandLine="-warn:4 -ei:utf-8 -enc:out utf-8 /g:jQuery,$,Msn -p", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines, LocalRenaming=LocalRenaming.KeepAll, KnownGlobalNamesList="jQuery,$,Msn", MinifyCode=false, KillSwitch=-2}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines, KillSwitch=-2}, WarningLevel=4, EncodingInputName="utf-8", EncodingOutputName="utf-8"}, + new ArgumentsSettings{CommandLine="-minify:false -rename:none", JSSettings=new CodeSettings(){MinifyCode=false, LocalRenaming=LocalRenaming.KeepAll}, CssSettings=null, WarningLevel=0}, + new ArgumentsSettings{CommandLine="-define:foo,bar,ack,gag,42", JSSettings=new CodeSettings(){PreprocessorDefineList="foo,bar,ack,gag"}, CssSettings=new CssSettings(){PreprocessorDefineList="foo,bar,ack,gag"}}, + new ArgumentsSettings{CommandLine="-ignore:foo,bar,ack,gag", JSSettings=new CodeSettings(){IgnoreErrorList="foo,bar,ack,gag"}, CssSettings=new CssSettings(){IgnoreErrorList="foo,bar,ack,gag"}}, + new ArgumentsSettings{CommandLine="/aspnet:T -pretty:8 -term:Yes", JSSettings=new CodeSettings(){AllowEmbeddedAspNetBlocks=true,TermSemicolons=true,LocalRenaming=LocalRenaming.KeepAll,OutputMode=OutputMode.MultipleLines,IndentSize=8, MinifyCode=false, KillSwitch=-2}, CssSettings=new CssSettings(){AllowEmbeddedAspNetBlocks=true,TermSemicolons=true,OutputMode=OutputMode.MultipleLines,IndentSize=8, KillSwitch=-2}}, + new ArgumentsSettings{CommandLine="/aspnet:F -minify:1 -kill:0 -pretty:0 -term:N", JSSettings=new CodeSettings(){AllowEmbeddedAspNetBlocks=false,TermSemicolons=false,LocalRenaming=LocalRenaming.KeepAll,OutputMode=OutputMode.MultipleLines,IndentSize=0}, CssSettings=new CssSettings(){AllowEmbeddedAspNetBlocks=false,TermSemicolons=false,OutputMode=OutputMode.MultipleLines,IndentSize=0}}, + new ArgumentsSettings{CommandLine="-expr:minify -colors:hex -comments:All", JSSettings=null, CssSettings=new CssSettings(){MinifyExpressions=true,ColorNames=CssColor.Hex,CommentMode=CssComment.All}}, + new ArgumentsSettings{CommandLine="-expr:raw -colors:MAJOR /comments:HaCkS", JSSettings=null, CssSettings=new CssSettings(){MinifyExpressions=false,ColorNames=CssColor.Major,CommentMode=CssComment.Hacks}}, + new ArgumentsSettings{CommandLine="-cc:1 -comments:None -debug:true -inline:yes -literals:keep -literals:evAL -mac:Y -minify:T -new:Keep -reorder:yes -unused:remove -rename:all", JSSettings=new CodeSettings{IgnoreConditionalCompilation=false,CommentMode=JsComment.PreserveNone,StripDebugStatements=false,InlineSafeStrings=true,EvalLiteralExpressions=true,MacSafariQuirks=true,MinifyCode=true,CollapseToLiteral=false,ReorderScopeDeclarations=true,RemoveUnneededCode=true,LocalRenaming=LocalRenaming.CrunchAll, PreprocessorDefineList="debug"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-cc:0 -comments:ImportanT -debug:false -inline:no /literals:combine -literals:NoEval -mac:N -minify:F -new:Collapse -reorder:N -unused:keep -rename:localization", JSSettings=new CodeSettings(){IgnoreConditionalCompilation=true,CommentMode=JsComment.PreserveImportant,StripDebugStatements=true,InlineSafeStrings=false,EvalLiteralExpressions=false,MacSafariQuirks=false,MinifyCode=false,CollapseToLiteral=true,ReorderScopeDeclarations=false,RemoveUnneededCode=false, LocalRenaming=LocalRenaming.KeepLocalizationVars}, CssSettings=null}, + new ArgumentsSettings{CommandLine="–debug:,", JSSettings=new CodeSettings(){StripDebugStatements=false,DebugLookupList="",PreprocessorDefineList="debug"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-debug:N,", JSSettings=new CodeSettings(){StripDebugStatements=true,DebugLookupList=""}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-debug:N,Foo,Bar,Ack.Gag.Barf,14,Name.42.First", JSSettings=new CodeSettings(){StripDebugStatements=true,DebugLookupList="Foo,Bar,Ack.Gag.Barf"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-global:foo,bar,ack,gag,212", JSSettings=new CodeSettings(){KnownGlobalNamesList="foo,bar,ack,gag"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-norename:foo,bar,ack,gag,105 -rename:NoProps", JSSettings=new CodeSettings(){NoAutoRenameList="foo,bar,ack,gag", ManualRenamesProperties=false}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-rename:foo=bar,ack=gag,105=106", JSSettings=new CodeSettings(){RenamePairs="foo=bar,ack=gag", ManualRenamesProperties=true}, CssSettings=null}, + new ArgumentsSettings{CommandLine="–fnames:lock -evals:ignore", JSSettings=new CodeSettings(){PreserveFunctionNames=true, RemoveFunctionExpressionNames=false, EvalTreatment=EvalTreatment.Ignore}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-fnames:keep -evals:immediate", JSSettings=new CodeSettings(){PreserveFunctionNames=false, RemoveFunctionExpressionNames=false, EvalTreatment=EvalTreatment.MakeImmediateSafe}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-fnames:onlyref -evals:safeall", JSSettings=new CodeSettings(){PreserveFunctionNames=false, RemoveFunctionExpressionNames=true, EvalTreatment=EvalTreatment.MakeAllSafe}, CssSettings=null}, + new ArgumentsSettings{CommandLine="–kill:-1", JSSettings=new CodeSettings(){KillSwitch=-1}, CssSettings=new CssSettings(){KillSwitch=-1,CommentMode=CssComment.None}}, + new ArgumentsSettings{CommandLine="–kill:0x1", JSSettings=new CodeSettings(){KillSwitch=1}, CssSettings=new CssSettings(){KillSwitch=1,CommentMode=CssComment.None}}, + new ArgumentsSettings{CommandLine="-kill:2", JSSettings=new CodeSettings(){KillSwitch=2}, CssSettings=new CssSettings(){KillSwitch=2}}, + new ArgumentsSettings{CommandLine="-kill:0xDAB0 -cc:BOOYAH! -warn:-1", JSSettings=new CodeSettings(){KillSwitch=0xdab0}, CssSettings=new CssSettings(){KillSwitch=55984}, WarningLevel=0}, + new ArgumentsSettings{CommandLine="-enc:in ascii -EO:big5 -warn", JSSettings=null, CssSettings=null, EncodingInputName="ascii", EncodingOutputName="big5", WarningLevel=int.MaxValue}, + new ArgumentsSettings{CommandLine="-css /js", JSSettings=new CodeSettings(), CssSettings=new CssSettings()}, + new ArgumentsSettings{CommandLine="-rename:All -pretty:WHAM", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-16777218}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-2}}, + new ArgumentsSettings{CommandLine="-rename:All -pretty:-10", JSSettings=new CodeSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-16777218}, CssSettings=new CssSettings(){OutputMode=OutputMode.MultipleLines,IndentSize=4, KillSwitch=-2}}, + new ArgumentsSettings{CommandLine="–rename:foo=bar,foo=ack", JSSettings=new CodeSettings(){RenamePairs="foo=bar"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="–d -h -j -k -m", JSSettings=new CodeSettings(), CssSettings=null}, + new ArgumentsSettings{CommandLine="-l -z -hCL", JSSettings=new CodeSettings() {CollapseToLiteral=false, TermSemicolons=true, LocalRenaming=LocalRenaming.KeepLocalizationVars}, CssSettings=null}, + new ArgumentsSettings{CommandLine="/HC", JSSettings=new CodeSettings() {}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-define:debug", JSSettings=new CodeSettings() {StripDebugStatements=false, PreprocessorDefineList="debug"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-define:debug -debug:0", JSSettings=new CodeSettings() {StripDebugStatements=true}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-debug:0 -define:debug", JSSettings=new CodeSettings() {StripDebugStatements=false, PreprocessorDefineList="debug"}, CssSettings=null}, + new ArgumentsSettings{CommandLine="-define:foo=bar,bob,ack=gag,BaT=CrAzY", JSSettings=new CodeSettings() {PreprocessorDefineList="foo=bar,bob,ack=gag,BaT=CrAzY"}, CssSettings=new CssSettings(){PreprocessorDefineList="foo=bar,bob,ack=gag,BaT=CrAzY"}}, + new ArgumentsSettings{CommandLine="-define:foo=", JSSettings=null, CssSettings=new CssSettings(){PreprocessorDefineList="foo"}}, + new ArgumentsSettings{CommandLine="-define:configuration=Debug -define:debug=Y,ralph=driver", JSSettings=new CodeSettings() {StripDebugStatements=false,PreprocessorDefineList="configuration=Debug,debug=Y,ralph=driver"}, CssSettings=new CssSettings(){PreprocessorDefineList="configuration=Debug,debug=Y,ralph=driver"}}, + new ArgumentsSettings{CommandLine="-define:once=first -define:OnCE=Last", JSSettings=new CodeSettings() {PreprocessorDefineList="once=Last"}, CssSettings=new CssSettings(){PreprocessorDefineList="once=Last"}}, }; var ndxTest = 0; diff --git a/src/NUglify.Tests/Css/Bugs.cs b/src/NUglify.Tests/Css/Bugs.cs index 3b0f4cf1..03614c7b 100644 --- a/src/NUglify.Tests/Css/Bugs.cs +++ b/src/NUglify.Tests/Css/Bugs.cs @@ -57,6 +57,11 @@ public void Bug250() TestHelper.Instance.RunTest(); } + [Test] + public void Bug189() + { + TestHelper.Instance.RunTest("-lines:m -comments:all"); + } [Test] public void Bug270() { diff --git a/src/NUglify.Tests/Css/Common/TestHelper.cs b/src/NUglify.Tests/Css/Common/TestHelper.cs index dd1b5336..787f97a0 100644 --- a/src/NUglify.Tests/Css/Common/TestHelper.cs +++ b/src/NUglify.Tests/Css/Common/TestHelper.cs @@ -68,6 +68,8 @@ public static TestHelper Instance /// TestHelper() { + Trace.Listeners.Add(new ConsoleTraceListener()); + // start with the unit test DLL. All test data folders will be deployed there by testrun configuration. // In order to do that, make sure that "Deployment" section in .testrunconfig file contains the "TestData" folder. If // this is the case, then everything in the folder will be copied down right next to unit test DLL. diff --git a/src/NUglify.Tests/JavaScript/Bugs.cs b/src/NUglify.Tests/JavaScript/Bugs.cs index 7c10f6e7..5372bf4d 100644 --- a/src/NUglify.Tests/JavaScript/Bugs.cs +++ b/src/NUglify.Tests/JavaScript/Bugs.cs @@ -164,6 +164,19 @@ public void Bug181() Assert.AreEqual("function foo()\n{\n return 1\n}", uglifyResult.Code); } + [Test] + public void Bug189() + { + TestHelper.Instance.RunTest("-pretty -comments:all"); + } + + + [Test] + public void Bug189Important() + { + TestHelper.Instance.RunTest("-pretty -comments:important"); + } + [Test] public void Bug197() { diff --git a/src/NUglify.Tests/JavaScript/JsPopular.cs b/src/NUglify.Tests/JavaScript/JsPopular.cs index e32bb45c..d3ed1286 100644 --- a/src/NUglify.Tests/JavaScript/JsPopular.cs +++ b/src/NUglify.Tests/JavaScript/JsPopular.cs @@ -59,9 +59,9 @@ static void AssertCompile(string url, string file) // https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.js var client = new WebClient(); var jqueryText = client.DownloadString(url); - var result = Uglify.Js(jqueryText, file, new CodeSettings() + var result = Uglify.Js(jqueryText, file, new CodeSettings { - PreserveImportantComments = false, + CommentMode = JsComment.PreserveNone, StripDebugStatements = true, LineBreakThreshold = int.MaxValue, }); diff --git a/src/NUglify.Tests/NUglify.Tests.csproj b/src/NUglify.Tests/NUglify.Tests.csproj index 78ac4d72..095ed992 100644 --- a/src/NUglify.Tests/NUglify.Tests.csproj +++ b/src/NUglify.Tests/NUglify.Tests.csproj @@ -395,6 +395,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -437,6 +440,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -470,6 +476,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -494,6 +503,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -2833,6 +2845,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -2854,6 +2869,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/NUglify.Tests/TestData/CSS/Expected/Bugs/Bug189.css b/src/NUglify.Tests/TestData/CSS/Expected/Bugs/Bug189.css new file mode 100644 index 00000000..30c16fbd --- /dev/null +++ b/src/NUglify.Tests/TestData/CSS/Expected/Bugs/Bug189.css @@ -0,0 +1,6 @@ +h6 +{ + font-family: 'Hammersmith One',sans-serif;/* + font-family: 'Questrial', sans-serif; + */ +} \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/CSS/Input/Bugs/Bug189.css b/src/NUglify.Tests/TestData/CSS/Input/Bugs/Bug189.css new file mode 100644 index 00000000..4fc4d731 --- /dev/null +++ b/src/NUglify.Tests/TestData/CSS/Input/Bugs/Bug189.css @@ -0,0 +1,7 @@ +h6 +{ + font-family: 'Hammersmith One', sans-serif; + /* + font-family: 'Questrial', sans-serif; + */ +} \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/BlockOpts/DirectivePrologue.js b/src/NUglify.Tests/TestData/JS/Expected/BlockOpts/DirectivePrologue.js index 42cdf68f..35056cd8 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/BlockOpts/DirectivePrologue.js +++ b/src/NUglify.Tests/TestData/JS/Expected/BlockOpts/DirectivePrologue.js @@ -1 +1,2 @@ -function one(a,b){"use strict";return a=b+b,b+a}function two(c,d){"some other prologue";"use strict";return d+c+d}function three(e,f){"use strict";var d=e+f;return d} \ No newline at end of file +function one(a,b){"use strict";return a=b+b,b+a}function two(c,d){//! no semi-colons -- they are implicit and should still count +"some other prologue";"use strict";return d+c+d}function three(e,f){"use strict";var d=e+f;return d} \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189.js b/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189.js new file mode 100644 index 00000000..3282d162 --- /dev/null +++ b/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189.js @@ -0,0 +1,8 @@ +let x = 1 +// a comment +// another comment +//! important comment +/*! + another important comment + */; +let y = 2 diff --git a/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189Important.js b/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189Important.js new file mode 100644 index 00000000..230f3b95 --- /dev/null +++ b/src/NUglify.Tests/TestData/JS/Expected/Bugs/Bug189Important.js @@ -0,0 +1,6 @@ +let x = 1 +//! important comment +/*! + another important comment + */; +let y = 2 diff --git a/src/NUglify.Tests/TestData/JS/Expected/Comments/EndWithImportantComment.js b/src/NUglify.Tests/TestData/JS/Expected/Comments/EndWithImportantComment.js index f3bde12f..25cb9e18 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Comments/EndWithImportantComment.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Comments/EndWithImportantComment.js @@ -1,3 +1 @@ -/*! START OF BLOCK NOTICE -- we need to retain these comments in the output */ -var a=10; -/*! END OF BLOCK NOTICE */ +/*! START OF BLOCK NOTICE -- we need to retain these comments in the output */var a=10/*! END OF BLOCK NOTICE */ \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantComment.js b/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantComment.js index 784f1f62..4623444b 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantComment.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantComment.js @@ -1,18 +1,6 @@ /*! * this is an example of an "important" comment * that we want to keep - */ -function foo(){ -/*! this too */ -}var a=12; -/*! - * this is another one, but it's between a couple var statements - */ -var b=13; -/** @preserve This is an important comment because of the @preserve token */ -/** + */function foo(){/*! this too */}var a=12/*! * this is another one, but it's between a couple var statements */;var b=13/** @preserve This is an important comment because of the @preserve token *//** ** @license And so is this, because of the @license token - */ -for(var ndx=0;ndx<10;++ndx) -/*! inside a block */ -; \ No newline at end of file + */;for(var ndx=0;ndx<10;++ndx)/*! inside a block */; \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantIgnore.js b/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantIgnore.js index 7e21def8..ee3fd3a0 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantIgnore.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Comments/ImportantIgnore.js @@ -1,7 +1,3 @@ -var p,i;if(location.hash)throw"hash exists";else alert("no hash");for(p in window)alert(p+" = "+window[p]);for(;;)if(window.timer)break;do{ -//! keep one -if(+new Date-1e6){ -//! keep two -break} -/*! keep three */ -}while(1);foo:for(i=0;i<100;++i)if(i%3)break foo \ No newline at end of file +var p,i;if(location.hash)throw"hash exists";else alert("no hash");for(p in window)alert(p+" = "+window[p]);for(;;)if(window.timer)break;do{//! keep one +if(+new Date-1e6){//! keep two +break}/*! keep three */}while(1);foo:for(i=0;i<100;++i)if(i%3)break foo \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Comments/OnlyImportantComment.js b/src/NUglify.Tests/TestData/JS/Expected/Comments/OnlyImportantComment.js index 7a8bb275..13d7672a 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Comments/OnlyImportantComment.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Comments/OnlyImportantComment.js @@ -1 +1 @@ -/*! Copyright 2010 by Contoso Corp. All rights reserved */ +/*! Copyright 2010 by Contoso Corp. All rights reserved */ \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Comments/TwoImportantComments.js b/src/NUglify.Tests/TestData/JS/Expected/Comments/TwoImportantComments.js index b48a59f2..f3fabe83 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Comments/TwoImportantComments.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Comments/TwoImportantComments.js @@ -1,3 +1 @@ -/*! First Important Comment */ -/*! Second Important Comment */ -var a=10 \ No newline at end of file +/*! First Important Comment *//*! Second Important Comment */var a=10 \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Expected/Modules/ExportCombine.js b/src/NUglify.Tests/TestData/JS/Expected/Modules/ExportCombine.js index cfaeb0f0..2751ee14 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Modules/ExportCombine.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Modules/ExportCombine.js @@ -1,3 +1,2 @@ //! start my implicit module -function n(n,t){return n+t}export function mul(n,t){return n*t}function t(n){return mul(n,n)}function i(n,t){if(t==0)return 1;for(var i=1;--t;)i=mul(i,n);return i}export const pi=3.1415927,negOne=-1;export var accumulator=0,foo="bar";export{n as sum,t as square,i as pow}; -//! end my implicit module +function n(n,t){return n+t}export function mul(n,t){return n*t}function t(n){return mul(n,n)}function i(n,t){if(t==0)return 1;for(var i=1;--t;)i=mul(i,n);return i}export const pi=3.1415927,negOne=-1;export var accumulator=0,foo="bar";export{n as sum,t as square,i as pow}//! end my implicit module diff --git a/src/NUglify.Tests/TestData/JS/Expected/Syntax/EmptyStatement.js b/src/NUglify.Tests/TestData/JS/Expected/Syntax/EmptyStatement.js index 6899b709..8d207989 100644 --- a/src/NUglify.Tests/TestData/JS/Expected/Syntax/EmptyStatement.js +++ b/src/NUglify.Tests/TestData/JS/Expected/Syntax/EmptyStatement.js @@ -1,3 +1 @@ -function foo(){for(var ndx=0;ndx<10;++ndx);}if(ndx) -/*! important */ -; \ No newline at end of file +function foo(){for(var ndx=0;ndx<10;++ndx);}if(ndx)/*! important */; \ No newline at end of file diff --git a/src/NUglify.Tests/TestData/JS/Input/BlockOpts/AfterReturn.js b/src/NUglify.Tests/TestData/JS/Input/BlockOpts/AfterReturn.js index e814cb43..51cb54b1 100644 --- a/src/NUglify.Tests/TestData/JS/Input/BlockOpts/AfterReturn.js +++ b/src/NUglify.Tests/TestData/JS/Input/BlockOpts/AfterReturn.js @@ -1,5 +1,5 @@ -function test(a,b) -{ +function test(a,b) { + var d = foo(c); return a + b + d; diff --git a/src/NUglify.Tests/TestData/JS/Input/BlockOpts/DirectivePrologue.js b/src/NUglify.Tests/TestData/JS/Input/BlockOpts/DirectivePrologue.js index 15a071ff..6a7b963d 100644 --- a/src/NUglify.Tests/TestData/JS/Input/BlockOpts/DirectivePrologue.js +++ b/src/NUglify.Tests/TestData/JS/Input/BlockOpts/DirectivePrologue.js @@ -12,7 +12,7 @@ function one(a,b) function two(c,d) { - // no semi-colons -- they are implicit and should still count + //! no semi-colons -- they are implicit and should still count "some other prologue" "use strict" c = d + c + d; diff --git a/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189.js b/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189.js new file mode 100644 index 00000000..9e4e66ec --- /dev/null +++ b/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189.js @@ -0,0 +1,7 @@ +let x = 1; // a comment +// another comment +//! important comment +/*! + another important comment + */ +let y = 2; diff --git a/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189Important.js b/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189Important.js new file mode 100644 index 00000000..9e4e66ec --- /dev/null +++ b/src/NUglify.Tests/TestData/JS/Input/Bugs/Bug189Important.js @@ -0,0 +1,7 @@ +let x = 1; // a comment +// another comment +//! important comment +/*! + another important comment + */ +let y = 2; diff --git a/src/NUglify.Tests/TestData/JS/Input/Comments/ImportantComment.js b/src/NUglify.Tests/TestData/JS/Input/Comments/ImportantComment.js index 7e96be5a..8bd0c543 100644 --- a/src/NUglify.Tests/TestData/JS/Input/Comments/ImportantComment.js +++ b/src/NUglify.Tests/TestData/JS/Input/Comments/ImportantComment.js @@ -5,9 +5,7 @@ function foo() {/*! this too */} /* regular comment that will get stripped */ var a = 12; -/*! - * this is another one, but it's between a couple var statements - */ +/*! * this is another one, but it's between a couple var statements */ var b = 13; /** @preserve This is an important comment because of the @preserve token */ diff --git a/src/NUglify/Css/CssParser.cs b/src/NUglify/Css/CssParser.cs index ea855406..4b6e03d7 100644 --- a/src/NUglify/Css/CssParser.cs +++ b/src/NUglify/Css/CssParser.cs @@ -3923,15 +3923,14 @@ string NextSignificantToken() // add the comment to the builder sb.Append(commentText); + if(Settings.OutputMode == OutputMode.MultipleLines) + sb.Append(Settings.LineTerminator); } } // next token m_currentToken = m_scanner.NextToken(!m_insideCalc); - if (EchoWriter != null) - { - EchoWriter.Write(CurrentTokenText); - } + EchoWriter?.Write(CurrentTokenText); m_encounteredNewLine = m_encounteredNewLine || m_scanner.GotEndOfLine; } diff --git a/src/NUglify/JavaScript/CodeSettings.cs b/src/NUglify/JavaScript/CodeSettings.cs index 504a2768..00b03679 100644 --- a/src/NUglify/JavaScript/CodeSettings.cs +++ b/src/NUglify/JavaScript/CodeSettings.cs @@ -40,7 +40,7 @@ public CodeSettings() this.EvalTreatment = EvalTreatment.Ignore; this.InlineSafeStrings = true; this.MacSafariQuirks = true; - this.PreserveImportantComments = true; + this.CommentMode = JsComment.PreserveImportant; this.QuoteObjectLiteralProperties = false; this.StrictMode = false; this.StripDebugStatements = true; @@ -114,7 +114,7 @@ public CodeSettings Clone() PreprocessOnly = this.PreprocessOnly, PreprocessorDefineList = this.PreprocessorDefineList, PreserveFunctionNames = this.PreserveFunctionNames, - PreserveImportantComments = this.PreserveImportantComments, + CommentMode = this.CommentMode, QuoteObjectLiteralProperties = this.QuoteObjectLiteralProperties, RemoveFunctionExpressionNames = this.RemoveFunctionExpressionNames, RemoveUnneededCode = this.RemoveUnneededCode, @@ -742,16 +742,20 @@ public bool PreserveFunctionNames get; set; } - /// - /// Gets or sets a value indicating whether to preserve important comments in the output. - /// Default is true, preserving important comments. Important comments have an exclamation - /// mark as the very first in-comment character (//! or /*!). - /// + [Obsolete("Use CommentMode instead")] public bool PreserveImportantComments { - get; set; + get => this.CommentMode != JsComment.PreserveNone; + set => this.CommentMode = this.CommentMode == JsComment.PreserveNone ? JsComment.PreserveImportant : this.CommentMode; } + /// + /// Gets or sets a value indicating whether to preserve comments in the output. + /// Default is Important, preserving important comments only. + /// Important comments have an exclamation mark as the very first in-comment character (//! or /*!). + /// + public JsComment CommentMode { get; set; } + /// /// Gets or sets a value indicating whether to always quote object literal property names. /// Default is false. diff --git a/src/NUglify/JavaScript/Comment.cs b/src/NUglify/JavaScript/Comment.cs new file mode 100644 index 00000000..8d1002dd --- /dev/null +++ b/src/NUglify/JavaScript/Comment.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace NUglify.JavaScript +{ + [DebuggerDisplay("{Context}")] + public class Comment + { + public SourceContext Context { get; set; } + public bool IsImportant { get; set; } + public bool IsMultiLine { get; set; } + } +} \ No newline at end of file diff --git a/src/NUglify/JavaScript/JSParser.cs b/src/NUglify/JavaScript/JSParser.cs index 3b2f7836..a3dba213 100644 --- a/src/NUglify/JavaScript/JSParser.cs +++ b/src/NUglify/JavaScript/JSParser.cs @@ -42,20 +42,20 @@ public class JSParser { #region private fields - private static bool[] s_skippableTokens = InitializeSkippableTokens(); + static bool[] s_skippableTokens = InitializeSkippableTokens(); - private GlobalScope m_globalScope; - private JSScanner m_scanner; - private SourceContext m_currentToken; + GlobalScope m_globalScope; + JSScanner m_scanner; + SourceContext m_currentToken; - private bool m_newModule; + bool m_newModule; - private CodeSettings m_settings;// = null; + CodeSettings m_settings;// = null; - private bool m_foundEndOfLine; - private IList m_importantComments; + bool foundEndOfLine; + IList comments; - private Dictionary m_labelInfo; + Dictionary m_labelInfo; #endregion @@ -163,7 +163,7 @@ public GlobalScope GlobalScope /// public JSParser() { - m_importantComments = new List(); + comments = new List(); m_labelInfo = new Dictionary(); } @@ -199,7 +199,7 @@ public BlockStatement Parse(DocumentContext sourceContext) // clear out some collections in case there was stuff left over from // a previous parse run - m_importantComments.Clear(); + comments.Clear(); m_labelInfo.Clear(); return InternalParse(); @@ -425,6 +425,8 @@ private BlockStatement InternalParse() timePoints[--timeIndex] = stopWatch.ElapsedTicks; + RemoveCommentsVisitor.Apply(scriptBlock, this); + // resolve everything ResolutionVisitor.Apply(scriptBlock, GlobalScope, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; @@ -439,6 +441,7 @@ private BlockStatement InternalParse() if (scriptBlock != null && Settings.MinifyCode && !Settings.PreprocessOnly) { + // this visitor doesn't just reorder scopes. It also combines the adjacent var variables, // unnests blocks, identifies prologue directives, and sets the strict mode on scopes. ReorderScopeVisitor.Apply(scriptBlock, this); @@ -683,7 +686,7 @@ private BlockStatement ParseStatements(BlockStatement block) } } - AppendImportantComments(block); + AppendComments(block); } catch (EndOfStreamException) @@ -730,170 +733,167 @@ private BlockStatement ParseStatements(BlockStatement block) // ParseXXX routine does it as well, it should return directly from the switch statement // without any further execution in the ParseStatement [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - private AstNode ParseStatement(bool fSourceElement, bool skipImportantComment = false) + AstNode ParseStatement(bool fSourceElement, bool skipComments = false) { - AstNode statement = null; + // if we want to skip comments, now is a good time to clear anything we may have picked up already. + if (skipComments) + comments.Clear(); - // if we want to skip important comments, now is a good time to clear anything we may - // have picked up already. - if (skipImportantComment) + if (comments.Count > 0 && m_settings.CommentMode != JsComment.PreserveNone) + //&& m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { - m_importantComments.Clear(); - } + // we have at least one comment before the upcoming statement. + // pop the first comment off the queue, return that node instead. + // don't advance the token -- we'll probably be coming back again for the next one (if any) - if (m_importantComments.Count > 0 - && m_settings.PreserveImportantComments - && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) - { - // we have at least one important comment before the upcoming statement. - // pop the first important comment off the queue, return that node instead. - // don't advance the token -- we'll probably be coming back again for the next one (if any) - statement = new ImportantComment(m_importantComments[0]); - m_importantComments.RemoveAt(0); + var comment = comments[0]; + comments.RemoveAt(0); + + return new Syntax.Comment(comment.Context, comment.IsImportant, comment.IsMultiLine); } - else + + AstNode statement = null; + + switch (m_currentToken.Token) { - switch (m_currentToken.Token) - { - case JSToken.EndOfFile: - ReportError(JSError.ErrorEndOfFile); - return null; // abort parsing, get back to the main parse routine + case JSToken.EndOfFile: + ReportError(JSError.ErrorEndOfFile); + return null; // abort parsing, get back to the main parse routine - case JSToken.Semicolon: - // make an empty statement - statement = new EmptyStatement(m_currentToken.Clone()); - GetNextToken(); - return statement; + case JSToken.Semicolon: + // make an empty statement + statement = new EmptyStatement(m_currentToken.Clone()); + GetNextToken(); + return statement; - case JSToken.RightCurly: - ReportError(JSError.SyntaxError); - GetNextToken(); - break; + case JSToken.RightCurly: + ReportError(JSError.SyntaxError); + GetNextToken(); + break; - case JSToken.LeftCurly: - return ParseBlock(); + case JSToken.LeftCurly: + return ParseBlock(); - case JSToken.Debugger: - return ParseDebuggerStatement(); + case JSToken.Debugger: + return ParseDebuggerStatement(); - case JSToken.Var: - case JSToken.Const: - case JSToken.Let: - return ParseVariableStatement(); + case JSToken.Var: + case JSToken.Const: + case JSToken.Let: + return ParseVariableStatement(); - case JSToken.If: - return ParseIfStatement(); + case JSToken.If: + return ParseIfStatement(); - case JSToken.For: - return ParseForStatement(); + case JSToken.For: + return ParseForStatement(); - case JSToken.Do: - return ParseDoStatement(); + case JSToken.Do: + return ParseDoStatement(); - case JSToken.While: - return ParseWhileStatement(); + case JSToken.While: + return ParseWhileStatement(); - case JSToken.Continue: - return ParseContinueStatement(); + case JSToken.Continue: + return ParseContinueStatement(); - case JSToken.Break: - return ParseBreakStatement(); + case JSToken.Break: + return ParseBreakStatement(); - case JSToken.Return: - return ParseReturnStatement(); + case JSToken.Return: + return ParseReturnStatement(); - case JSToken.With: - return ParseWithStatement(); + case JSToken.With: + return ParseWithStatement(); - case JSToken.Switch: - return ParseSwitchStatement(); + case JSToken.Switch: + return ParseSwitchStatement(); - case JSToken.Throw: - return ParseThrowStatement(); + case JSToken.Throw: + return ParseThrowStatement(); - case JSToken.Try: - return ParseTryStatement(); + case JSToken.Try: + return ParseTryStatement(); - case JSToken.Async: + case JSToken.Async: - var peekToken = PeekToken(); - if (peekToken == JSToken.Function) - { - // Treat 'async function' as a function declaration - goto case (JSToken.Function); - } - else - { - // Treat `async(` as if its a normal expression - goto default; - } - case JSToken.Function: - { - // parse a function declaration - var function = ParseFunction(FunctionType.Declaration, m_currentToken.Clone()); - function.IsSourceElement = fSourceElement; - return function; - } - case JSToken.Class: - return ParseClassNode(ClassType.Declaration); + var peekToken = PeekToken(); + if (peekToken == JSToken.Function) + { + // Treat 'async function' as a function declaration + goto case (JSToken.Function); + } + else + { + // Treat `async(` as if its a normal expression + goto default; + } + case JSToken.Function: + { + // parse a function declaration + var function = ParseFunction(FunctionType.Declaration, m_currentToken.Clone()); + function.IsSourceElement = fSourceElement; + return function; + } + case JSToken.Class: + return ParseClassNode(ClassType.Declaration); - case JSToken.Else: - ReportError(JSError.InvalidElse); - GetNextToken(); - break; + case JSToken.Else: + ReportError(JSError.InvalidElse); + GetNextToken(); + break; - case JSToken.ConditionalCommentStart: - return ParseStatementLevelConditionalComment(fSourceElement); + case JSToken.ConditionalCommentStart: + return ParseStatementLevelConditionalComment(fSourceElement); - case JSToken.ConditionalCompilationOn: - var ccOn = new ConditionalCompilationOn(m_currentToken.Clone()); - GetNextToken(); - return ccOn; + case JSToken.ConditionalCompilationOn: + var ccOn = new ConditionalCompilationOn(m_currentToken.Clone()); + GetNextToken(); + return ccOn; - case JSToken.ConditionalCompilationSet: - return ParseConditionalCompilationSet(); + case JSToken.ConditionalCompilationSet: + return ParseConditionalCompilationSet(); - case JSToken.ConditionalCompilationIf: - return ParseConditionalCompilationIf(false); + case JSToken.ConditionalCompilationIf: + return ParseConditionalCompilationIf(false); - case JSToken.ConditionalCompilationElseIf: - return ParseConditionalCompilationIf(true); + case JSToken.ConditionalCompilationElseIf: + return ParseConditionalCompilationIf(true); - case JSToken.ConditionalCompilationElse: - var elseStatement = new ConditionalCompilationElse(m_currentToken.Clone()); - GetNextToken(); - return elseStatement; + case JSToken.ConditionalCompilationElse: + var elseStatement = new ConditionalCompilationElse(m_currentToken.Clone()); + GetNextToken(); + return elseStatement; - case JSToken.ConditionalCompilationEnd: - var endStatement = new ConditionalCompilationEnd(m_currentToken.Clone()); - GetNextToken(); - return endStatement; + case JSToken.ConditionalCompilationEnd: + var endStatement = new ConditionalCompilationEnd(m_currentToken.Clone()); + GetNextToken(); + return endStatement; - case JSToken.Import: - return ParseImport(); + case JSToken.Import: + return ParseImport(); - case JSToken.Export: - // export can't be an identifier name, so it must be an export statement - return ParseExport(); + case JSToken.Export: + // export can't be an identifier name, so it must be an export statement + return ParseExport(); - case JSToken.Identifier: - if (m_currentToken.Is("module")) - { - goto case JSToken.Module; - } - goto default; + case JSToken.Identifier: + if (m_currentToken.Is("module")) + { + goto case JSToken.Module; + } + goto default; - case JSToken.Module: - if (PeekCanBeModule()) - { - return ParseModule(); - } - goto default; + case JSToken.Module: + if (PeekCanBeModule()) + { + return ParseModule(); + } + goto default; - default: - statement = ParseExpressionStatement(fSourceElement); - break; - } + default: + statement = ParseExpressionStatement(fSourceElement); + break; } return statement; @@ -1173,7 +1173,7 @@ private BlockStatement ParseBlock() { ForceBraces = true }; - codeBlock.BraceOnNewLine = m_foundEndOfLine; + codeBlock.BraceOnNewLine = foundEndOfLine; GetNextToken(); while (m_currentToken.IsNot(JSToken.RightCurly) && m_currentToken.IsNot(JSToken.EndOfFile)) @@ -1183,8 +1183,8 @@ private BlockStatement ParseBlock() codeBlock.Append(ParseStatement(false)); } - // make sure any important comments before the closing brace are kept - AppendImportantComments(codeBlock); + // make sure any comments before the closing brace are kept + AppendComments(codeBlock); if (m_currentToken.IsNot(JSToken.RightCurly)) { @@ -1944,7 +1944,7 @@ private ContinueStatement ParseContinueStatement() GetNextToken(); string label = null; - if (!m_foundEndOfLine && (m_currentToken.Is(JSToken.Identifier) || (label = JSKeyword.CanBeIdentifier(m_currentToken.Token)) != null)) + if (!foundEndOfLine && (m_currentToken.Is(JSToken.Identifier) || (label = JSKeyword.CanBeIdentifier(m_currentToken.Token)) != null)) { continueNode.UpdateWith(m_currentToken); continueNode.LabelContext = m_currentToken.Clone(); @@ -1989,7 +1989,7 @@ private BreakStatement ParseBreakStatement() GetNextToken(); string label = null; - if (!m_foundEndOfLine && (m_currentToken.Is(JSToken.Identifier) || (label = JSKeyword.CanBeIdentifier(m_currentToken.Token)) != null)) + if (!foundEndOfLine && (m_currentToken.Is(JSToken.Identifier) || (label = JSKeyword.CanBeIdentifier(m_currentToken.Token)) != null)) { breakNode.UpdateWith(m_currentToken); breakNode.LabelContext = m_currentToken.Clone(); @@ -2034,7 +2034,7 @@ private ReturnStatement ParseReturnStatement() GetNextToken(); // CAN'T have a line-break between the "return" and its expression. - if (!m_foundEndOfLine) + if (!foundEndOfLine) { if (m_currentToken.IsNot(JSToken.Semicolon) && m_currentToken.IsNot(JSToken.RightCurly)) { @@ -2163,7 +2163,7 @@ private AstNode ParseSwitchStatement() } else { - braceOnNewLine = m_foundEndOfLine; + braceOnNewLine = foundEndOfLine; braceContext = m_currentToken.Clone(); GetNextToken(); } @@ -2258,7 +2258,7 @@ private AstNode ParseThrowStatement() GetNextToken(); // cannot have a line break between "throw" and it's expression - if (!m_foundEndOfLine) + if (!foundEndOfLine) { if (m_currentToken.IsNot(JSToken.Semicolon)) { @@ -2399,7 +2399,7 @@ private AstNode ParseModule() SourceContext fromContext = null; if (m_currentToken.Is(JSToken.StringLiteral)) { - if (m_foundEndOfLine) + if (foundEndOfLine) { // throw an error, but keep on parsing ReportError(JSError.NewLineNotAllowed, null, true); @@ -2940,7 +2940,7 @@ private FunctionObject ParseFunctionPart2(FunctionType functionType, SourceConte { // parse the block locally to get the exact end of function body = new BlockStatement(m_currentToken.Clone()); - body.BraceOnNewLine = m_foundEndOfLine; + body.BraceOnNewLine = foundEndOfLine; GetNextToken(); // parse the function body statements @@ -3007,7 +3007,7 @@ private void ParseFunctionBody(BlockStatement body) }; } } - else if (!m_newModule) + else if (!m_newModule && !(statement is Syntax.Comment)) { // no longer considering constant wrappers possibleDirectivePrologue = false; @@ -3024,7 +3024,7 @@ private void ParseFunctionBody(BlockStatement body) } // make sure any important comments before the closing brace are kept - AppendImportantComments(body); + AppendComments(body); } private AstNodeList ParseFormalParameters() @@ -3827,7 +3827,7 @@ private AstNode ParsePostfixExpression(AstNode ast, out bool isLeftHandSideExpr) SourceContext exprCtx = null; if (null != ast) { - if (!m_foundEndOfLine) + if (!foundEndOfLine) { if (m_currentToken.Is(JSToken.Increment)) { @@ -5515,7 +5515,7 @@ private void SetDocumentContext(DocumentContext documentContext) // we also want to assume that we found a newline character after // the comment - m_foundEndOfLine = true; + foundEndOfLine = true; }; } @@ -5828,22 +5828,20 @@ public bool ConvertBigIntLiteralToBigInteger(string str, out double bigIntegerVa } } - void AppendImportantComments(BlockStatement block) + void AppendComments(BlockStatement block) { if (block != null) { - // make sure any important comments before the closing brace are kept - if (m_importantComments.Count > 0 - && m_settings.PreserveImportantComments - && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) + // make sure any comments before the closing brace are kept + if (comments.Count > 0 + && m_settings.CommentMode != JsComment.PreserveNone) + //&& m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { - // we have important comments before the EOF. Add the comment(s) to the program. - foreach (var importantComment in m_importantComments) - { - block.Append(new ImportantComment(importantComment)); - } + // we have comments before the EOF. Add the comment(s) to the program. + foreach (var comment in comments) + block.Append(new Syntax.Comment(comment.Context, comment.IsImportant, comment.IsMultiLine)); - m_importantComments.Clear(); + comments.Clear(); } } } @@ -5890,25 +5888,28 @@ private SourceContext ScanNextToken() } m_newModule = false; - m_foundEndOfLine = false; - m_importantComments.Clear(); + foundEndOfLine = false; + comments.Clear(); var nextToken = m_scanner.ScanNextToken(); while (nextToken.IsOne(s_skippableTokens)) { - if (nextToken.Is(JSToken.EndOfLine)) - { - m_foundEndOfLine = true; - } - else if (nextToken.IsEither(JSToken.MultipleLineComment, JSToken.SingleLineComment)) - { - if (nextToken.HasCode - && ((nextToken.Code.Length > 2 && nextToken.Code[2] == '!') - || (nextToken.Code.IndexOf("@preserve", StringComparison.OrdinalIgnoreCase) >= 0) - || (nextToken.Code.IndexOf("@license", StringComparison.OrdinalIgnoreCase) >= 0))) - { - // this is an important comment -- save it for later - m_importantComments.Add(nextToken.Clone()); + if (nextToken.Is(JSToken.EndOfLine)) + foundEndOfLine = true; + + else if (nextToken.IsEither(JSToken.MultipleLineComment, JSToken.SingleLineComment)) + { + if(nextToken.HasCode){ + var important = nextToken.Code.Length > 2 && nextToken.Code[2] == '!' + || nextToken.Code.IndexOf("@preserve", StringComparison.OrdinalIgnoreCase) >= 0 + || nextToken.Code.IndexOf("@license", StringComparison.OrdinalIgnoreCase) >= 0; + + comments.Add(new Comment + { + Context = nextToken.Clone(), + IsImportant = important, + IsMultiLine = nextToken.Is(JSToken.MultipleLineComment) + }); } } @@ -5923,7 +5924,7 @@ private SourceContext ScanNextToken() if (nextToken.Is(JSToken.EndOfFile)) { - m_foundEndOfLine = true; + foundEndOfLine = true; } return nextToken; @@ -6027,7 +6028,7 @@ private void ExpectSemicolon(AstNode node) node.TerminatingContext = m_currentToken.Clone(); GetNextToken(); } - else if (m_foundEndOfLine || m_currentToken.IsEither(JSToken.RightCurly, JSToken.EndOfFile)) + else if (foundEndOfLine || m_currentToken.IsEither(JSToken.RightCurly, JSToken.EndOfFile)) { // semicolon insertion rules // a right-curly or an end of line is something we don't WANT to throw a warning for. diff --git a/src/NUglify/JavaScript/JsComment.cs b/src/NUglify/JavaScript/JsComment.cs new file mode 100644 index 00000000..315cd7d4 --- /dev/null +++ b/src/NUglify/JavaScript/JsComment.cs @@ -0,0 +1,20 @@ +namespace NUglify.JavaScript +{ + public enum JsComment + { + /// + /// Don't output any comments + /// + PreserveNone = 0, + + /// + /// Only output important comments (//! or /*!) + /// + PreserveImportant = 1, + + /// + /// Output all comments + /// + PreserveAll = 2 + } +} \ No newline at end of file diff --git a/src/NUglify/JavaScript/SourceContext.cs b/src/NUglify/JavaScript/SourceContext.cs index 0ade290f..41e4cdfd 100644 --- a/src/NUglify/JavaScript/SourceContext.cs +++ b/src/NUglify/JavaScript/SourceContext.cs @@ -52,42 +52,20 @@ public class SourceContext public JSToken Token { get; internal set; } - public int StartColumn - { - get - { - return StartPosition - StartLinePosition; - } - } + public int StartColumn => StartPosition - StartLinePosition; - public int EndColumn - { - get - { - return EndPosition - EndLinePosition; - } - } + public int EndColumn => EndPosition - EndLinePosition; - public bool HasCode - { - get - { - return !Document.IsGenerated - && EndPosition > StartPosition - && EndPosition <= Document.Source.Length - && EndPosition != StartPosition; - } - } + public bool HasCode => + !Document.IsGenerated + && EndPosition > StartPosition + && EndPosition <= Document.Source.Length + && EndPosition != StartPosition; - public String Code - { - get - { - return (!Document.IsGenerated && EndPosition > StartPosition && EndPosition <= Document.Source.Length) - ? Document.Source.Substring(StartPosition, EndPosition - StartPosition) - : null; - } - } + public string Code => + (!Document.IsGenerated && EndPosition > StartPosition && EndPosition <= Document.Source.Length) + ? Document.Source.Substring(StartPosition, EndPosition - StartPosition) + : null; string ErrorSegment { @@ -97,30 +75,20 @@ string ErrorSegment // just pull out the string that's between start position and end position if (this.StartPosition >= source.Length) - { - return string.Empty; - } - else - { - int length = this.EndPosition - this.StartPosition; - if (this.StartPosition + length <= source.Length) - { - return source.Substring(this.StartPosition, length).Trim(); - } - else - { - return source.Substring(this.StartPosition).Trim(); - } - } + return string.Empty; + + int length = this.EndPosition - this.StartPosition; + if (this.StartPosition + length <= source.Length) + return source.Substring(this.StartPosition, length).Trim(); + + return source.Substring(this.StartPosition).Trim(); } } public SourceContext(DocumentContext document) { if (document == null) - { - throw new ArgumentNullException("document"); - } + throw new ArgumentNullException("document"); Document = document; diff --git a/src/NUglify/JavaScript/Syntax/AstNode.cs b/src/NUglify/JavaScript/Syntax/AstNode.cs index 8da282c4..06e5b622 100644 --- a/src/NUglify/JavaScript/Syntax/AstNode.cs +++ b/src/NUglify/JavaScript/Syntax/AstNode.cs @@ -330,7 +330,7 @@ internal static IEnumerable EnumerateNonNullNodes(AstNode n1, AstNode n internal void UnlinkParent(AstNode node) { - if ((node != null) && (node.Parent == this)) + if (node != null && node.Parent == this) { node.Parent = null; } diff --git a/src/NUglify/JavaScript/Syntax/BlockStatement.cs b/src/NUglify/JavaScript/Syntax/BlockStatement.cs index 3e1490d1..020fcf3f 100644 --- a/src/NUglify/JavaScript/Syntax/BlockStatement.cs +++ b/src/NUglify/JavaScript/Syntax/BlockStatement.cs @@ -26,7 +26,7 @@ namespace NUglify.JavaScript.Syntax [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public sealed class BlockStatement : Statement, IEnumerable { - List m_list; + List nodes; /// /// Gets a particular statement in the list of statements making up this block @@ -35,18 +35,18 @@ public sealed class BlockStatement : Statement, IEnumerable /// abstract syntax tree node public AstNode this[int index] { - get { return m_list[index]; } + get => nodes[index]; set { - UnlinkParent(m_list[index]); + UnlinkParent(nodes[index]); if (value != null) { - m_list[index] = value; - m_list[index].Parent = this; + nodes[index] = value; + nodes[index].Parent = this; } else { - m_list.RemoveAt(index); + nodes.RemoveAt(index); } } } @@ -54,10 +54,7 @@ public AstNode this[int index] /// /// Gets the count of statements making up this block /// - public int Count - { - get { return m_list.Count; } - } + public int Count => nodes.Count; /// /// Gets or sets a boolean value indicating whether the brace for this block (if there was one) started @@ -70,15 +67,10 @@ public int Count /// public bool IsModule { get; set; } - public override SourceContext TerminatingContext - { - get - { - // if we have one, return it. If not, see if there's only one - // line in our block, and if so, return it's terminator. - return base.TerminatingContext ?? (m_list.Count == 1 ? m_list[0].TerminatingContext : null); - } - } + public override SourceContext TerminatingContext => + // if we have one, return it. If not, see if there's only one + // line in our block, and if so, return it's terminator. + base.TerminatingContext ?? (nodes.Count == 1 ? nodes[0].TerminatingContext : null); /// /// Gets or sets whether to force this block to always have curly-braces around it @@ -94,18 +86,13 @@ public override SourceContext TerminatingContext /// /// Returns false unless the block constains only a single statement that is itself an expression. /// - public override bool IsExpression - { - get - { - // if this block contains a single statement, then recurse. - // otherwise it isn't. - // - // TODO: if there are no statements -- empty block -- then is is an expression? - // I mean, we can make an empty block be an expression by just making it a zero. - return m_list.Count == 1 && m_list[0].IsExpression; - } - } + public override bool IsExpression => + // if this block contains a single statement, then recurse. + // otherwise it isn't. + // + // TODO: if there are no statements -- empty block -- then is is an expression? + // I mean, we can make an empty block be an expression by just making it a zero. + nodes.Count == 1 && nodes[0].IsExpression; /// /// Gets an enumerator for the syntax tree nodes making up this block @@ -114,22 +101,19 @@ public override IEnumerable Children { get { - return FastEnumerateNonNullNodes(m_list); + return FastEnumerateNonNullNodes(nodes); } } public BlockStatement(SourceContext context) : base(context) { - m_list = new List(); + nodes = new List(); } public override void Accept(IVisitor visitor) { - if (visitor != null) - { - visitor.Visit(this); - } + visitor?.Visit(this); } /// @@ -137,12 +121,12 @@ public override void Accept(IVisitor visitor) /// public void Clear() { - foreach (var item in m_list) + foreach (var item in nodes) { item.IfNotNull(n => n.Parent = (n.Parent == this) ? null : n.Parent); } - m_list.Clear(); + nodes.Clear(); this.IsConcise = false; } @@ -150,7 +134,7 @@ internal override bool EncloseBlock(EncloseBlockType type) { // if there's more than one item, then return false. // otherwise recurse the call - return (m_list.Count == 1 && m_list[0].EncloseBlock(type)); + return (nodes.Count == 1 && nodes[0].EncloseBlock(type)); } /// @@ -161,9 +145,9 @@ internal override bool EncloseBlock(EncloseBlockType type) /// true if the replacement was a succeess; false otherwise public override bool ReplaceChild(AstNode oldNode, AstNode newNode) { - for (int ndx = m_list.Count - 1; ndx >= 0; --ndx) + for (int ndx = nodes.Count - 1; ndx >= 0; --ndx) { - if (m_list[ndx] == oldNode) + if (nodes[ndx] == oldNode) { if ((oldNode != null) && (oldNode.Parent == this)) { @@ -178,7 +162,7 @@ public override bool ReplaceChild(AstNode oldNode, AstNode newNode) this.IsConcise = false; // just remove it - m_list.RemoveAt(ndx); + nodes.RemoveAt(ndx); // if this was a concise block, it shouldn't be anymore this.IsConcise = false; @@ -190,13 +174,13 @@ public override bool ReplaceChild(AstNode oldNode, AstNode newNode) { // the new "statement" is a block. That means we need to insert all // the statements from the new block at the location of the old item. - m_list.RemoveAt(ndx); - InsertRange(ndx, newBlock.m_list); + nodes.RemoveAt(ndx); + InsertRange(ndx, newBlock.nodes); } else { // not a block -- slap it in there - m_list[ndx] = newNode; + nodes[ndx] = newNode; newNode.Parent = this; // if we were concise and we are replacing our single expression with @@ -231,7 +215,7 @@ public void Append(AstNode item) } item.Parent = this; - m_list.Add(item); + nodes.Add(item); Context.UpdateWith(item.Context); } } @@ -243,7 +227,7 @@ public void Append(AstNode item) /// zero-based index of the node in the block, or -1 if the node is not a direct child of the block public int IndexOf(AstNode item) { - return m_list.IndexOf(item); + return nodes.IndexOf(item); } /// @@ -255,7 +239,7 @@ public void InsertAfter(AstNode after, AstNode item) { if (item != null) { - int index = m_list.IndexOf(after); + int index = nodes.IndexOf(after); if (index >= 0) { if (this.IsConcise) @@ -275,7 +259,7 @@ public void InsertAfter(AstNode after, AstNode item) else { item.Parent = this; - m_list.Insert(index + 1, item); + nodes.Insert(index + 1, item); } } } @@ -305,7 +289,7 @@ public void Insert(int index, AstNode item) else { item.Parent = this; - m_list.Insert(index, item); + nodes.Insert(index, item); } } } @@ -319,7 +303,7 @@ public void RemoveLast() // the conciseness, because there's only one statement and we're going // to be deleting it. this.IsConcise = false; - RemoveAt(m_list.Count - 1); + RemoveAt(nodes.Count - 1); } /// @@ -328,15 +312,15 @@ public void RemoveLast() /// Zero-based position index public void RemoveAt(int index) { - if (0 <= index && index < m_list.Count) + if (0 <= index && index < nodes.Count) { // if this was concise, we're not anymore. Don't need to undo // the conciseness, because there's only one statement and we're going // to be deleting it. this.IsConcise = false; - UnlinkParent(m_list[index]); - m_list.RemoveAt(index); + UnlinkParent(nodes[index]); + nodes.RemoveAt(index); } } @@ -355,7 +339,7 @@ public void InsertRange(int index, IEnumerable newItems) Unconcise(); } - m_list.InsertRange(index, newItems); + nodes.InsertRange(index, newItems); foreach (AstNode newItem in newItems) { newItem.Parent = this; @@ -372,12 +356,12 @@ void Unconcise() // there should be a single statement that's an expression. Make it the argument of // a return statement. - if (m_list.Count == 1) + if (nodes.Count == 1) { - var expression = m_list[0]; + var expression = nodes[0]; if (expression.IsExpression) { - m_list[0] = new ReturnStatement(expression.Context) + nodes[0] = new ReturnStatement(expression.Context) { Operand = expression, Parent = this @@ -386,18 +370,14 @@ void Unconcise() } } - #region IEnumerable implementation - public IEnumerator GetEnumerator() { - return m_list.GetEnumerator(); + return nodes.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return m_list.GetEnumerator(); + return nodes.GetEnumerator(); } - - #endregion } } \ No newline at end of file diff --git a/src/NUglify/JavaScript/Syntax/ClassNode.cs b/src/NUglify/JavaScript/Syntax/ClassNode.cs index 932cc29a..08c59d99 100644 --- a/src/NUglify/JavaScript/Syntax/ClassNode.cs +++ b/src/NUglify/JavaScript/Syntax/ClassNode.cs @@ -99,10 +99,7 @@ public ClassNode(SourceContext context) public override void Accept(IVisitor visitor) { - if (visitor != null) - { - visitor.Visit(this); - } + visitor?.Visit(this); } public override IEnumerable Children diff --git a/src/NUglify/JavaScript/Syntax/Comment.cs b/src/NUglify/JavaScript/Syntax/Comment.cs new file mode 100644 index 00000000..f916a551 --- /dev/null +++ b/src/NUglify/JavaScript/Syntax/Comment.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using NUglify.JavaScript.Visitors; + +namespace NUglify.JavaScript.Syntax +{ + [DebuggerDisplay("{Value}")] + public class Comment : AstNode + { + // Indicates if this was a multiline comment (by syntax only, doesn't necessarily contain a \n) + public bool IsMultiLine { get; set; } + public bool IsImportant { get; set; } + public string Value { get; set; } + + // this is for determining if a node in a block AFTER a return/break/continue should be removed. Comments are removed early, so when this becomes relevant, we know we don't want to remove this, so SAY it's a declaration. + public override bool IsDeclaration => true; + + public Comment(SourceContext context, bool isImportant, bool isMultiLine) : base(context) + { + IsImportant = isImportant; + IsMultiLine = isMultiLine; + Value = Context.Code; + } + + public override void Accept(IVisitor visitor) + { + visitor?.Visit(this); + } + } +} diff --git a/src/NUglify/JavaScript/Syntax/ImportantComment.cs b/src/NUglify/JavaScript/Syntax/ImportantComment.cs deleted file mode 100644 index b556894b..00000000 --- a/src/NUglify/JavaScript/Syntax/ImportantComment.cs +++ /dev/null @@ -1,50 +0,0 @@ -// importantcomment.cs -// -// Copyright 2010 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using NUglify.JavaScript.Visitors; - -namespace NUglify.JavaScript.Syntax -{ - public class ImportantComment : AstNode - { - public string Comment { get; set; } - - public override bool IsDeclaration - { - get - { - // this is for determining if a node in a block AFTER a return/break/continue - // should be removed. We don't want to remove an important comment, so SAY it's - // a declaration. - return true; - } - } - - public ImportantComment(SourceContext context) - : base(context) - { - Comment = Context.Code; - } - - public override void Accept(IVisitor visitor) - { - if (visitor != null) - { - visitor.Visit(this); - } - } - } -} diff --git a/src/NUglify/JavaScript/Visitors/AnalyzeNodeVisitor.cs b/src/NUglify/JavaScript/Visitors/AnalyzeNodeVisitor.cs index 8a50af40..3ff0e0f2 100644 --- a/src/NUglify/JavaScript/Visitors/AnalyzeNodeVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/AnalyzeNodeVisitor.cs @@ -610,7 +610,7 @@ static AstNode FindLastStatement(BlockStatement node) // or important comments until we get the last statement var lastStatementIndex = node.Count - 1; while (lastStatementIndex >= 0 - && (node[lastStatementIndex] is FunctionObject || node[lastStatementIndex] is ImportantComment)) + && (node[lastStatementIndex] is FunctionObject || node[lastStatementIndex] is Syntax.Comment)) { --lastStatementIndex; } @@ -1565,7 +1565,7 @@ static int PreviousStatementIndex(BlockStatement node, AstNode child) // get the index of the statement before the last return // (skip over function decls and importand comments) var indexPrevious = node.IndexOf(child) - 1; - while (indexPrevious >= 0 && (node[indexPrevious] is FunctionObject || node[indexPrevious] is ImportantComment)) + while (indexPrevious >= 0 && (node[indexPrevious] is FunctionObject || node[indexPrevious] is Syntax.Comment)) { --indexPrevious; } diff --git a/src/NUglify/JavaScript/Visitors/BindingsVisitor.cs b/src/NUglify/JavaScript/Visitors/BindingsVisitor.cs index 3743d8ba..e685baaa 100644 --- a/src/NUglify/JavaScript/Visitors/BindingsVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/BindingsVisitor.cs @@ -386,7 +386,7 @@ public void Visit(IfStatement node) ReportError(node); } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { ReportError(node); } diff --git a/src/NUglify/JavaScript/Visitors/IVisitor.cs b/src/NUglify/JavaScript/Visitors/IVisitor.cs index edb15c3f..1c0ac1ab 100644 --- a/src/NUglify/JavaScript/Visitors/IVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/IVisitor.cs @@ -58,7 +58,7 @@ public interface IVisitor void Visit(GetterSetter node); void Visit(GroupingOperator node); void Visit(IfStatement node); - void Visit(ImportantComment node); + void Visit(Syntax.Comment node); void Visit(ImportExportSpecifier node); void Visit(ImportStatement node); void Visit(InitializerNode node); diff --git a/src/NUglify/JavaScript/Visitors/JsonOutputVisitor.cs b/src/NUglify/JavaScript/Visitors/JsonOutputVisitor.cs index 40a3cb75..00f28140 100644 --- a/src/NUglify/JavaScript/Visitors/JsonOutputVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/JsonOutputVisitor.cs @@ -549,7 +549,7 @@ public void Visit(IfStatement node) IsValid = false; } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { // invalid! ignore IsValid = false; diff --git a/src/NUglify/JavaScript/Visitors/MatchPropertiesVisitor.cs b/src/NUglify/JavaScript/Visitors/MatchPropertiesVisitor.cs index a4f9c908..ab109dee 100644 --- a/src/NUglify/JavaScript/Visitors/MatchPropertiesVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/MatchPropertiesVisitor.cs @@ -311,7 +311,7 @@ public void Visit(IfStatement node) // not applicable; terminate } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { // not applicable; terminate } diff --git a/src/NUglify/JavaScript/Visitors/NewParensVisitor.cs b/src/NUglify/JavaScript/Visitors/NewParensVisitor.cs index ba46e416..f97a498c 100644 --- a/src/NUglify/JavaScript/Visitors/NewParensVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/NewParensVisitor.cs @@ -194,7 +194,7 @@ public virtual void Visit(GroupingOperator node) // output parens ourselves. And don't bother recursing. } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { // we're good? } diff --git a/src/NUglify/JavaScript/Visitors/OutputVisitor.cs b/src/NUglify/JavaScript/Visitors/OutputVisitor.cs index 068ef55b..3e8b8b1f 100644 --- a/src/NUglify/JavaScript/Visitors/OutputVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/OutputVisitor.cs @@ -62,7 +62,7 @@ public class OutputVisitor : IVisitor CodeSettings settings; - RequiresSeparatorVisitor m_requiresSeparator; + RequiresSeparatorVisitor requiresSeparator; static string[] s_exponents = { null, @@ -107,7 +107,7 @@ public class OutputVisitor : IVisitor outputStream = writer; this.settings = settings ?? new CodeSettings(); onNewLine = true; - m_requiresSeparator = new RequiresSeparatorVisitor(this.settings); + requiresSeparator = new RequiresSeparatorVisitor(this.settings); m_hasReplacementTokens = settings.ReplacementTokens.Count > 0; writer.NewLine = settings.LineTerminator; } @@ -594,18 +594,40 @@ public void Visit(BlockStatement node) } AstNode prevStatement = null; + AstNode prevNonCommentStatement = null; for (var ndx = 0; ndx < node.Count; ++ndx) { var statement = node[ndx]; if (statement != null) { - if (prevStatement != null && m_requiresSeparator.Query(prevStatement)) - { - OutputPossibleLineBreak(';'); - MarkSegment(prevStatement, null, prevStatement.TerminatingContext); - } - - if (!(statement is DirectivePrologue)) + if (prevStatement != null) + { + if (statement is Syntax.Comment) + { + // no need for a ; yet + MarkSegment(prevStatement, null, prevStatement.TerminatingContext); + } + else if (requiresSeparator.Query(prevNonCommentStatement) && !(prevStatement is Syntax.Comment comment && !comment.IsMultiLine)) + { + OutputPossibleLineBreak(';'); + MarkSegment(prevStatement, null, prevStatement.TerminatingContext); + } + + } + // + // if (prevStatement != null && requiresSeparator.Query(prevStatement)) + // { + // // only output a ; if the prev statement wasnt a comment + // /* + // * -- if the previous statement was a comment, and we have previous non-comment code + // * if prevstatement is comment and prevnoncomment != null + // * + // */ + // OutputPossibleLineBreak(';'); + // MarkSegment(prevStatement, null, prevStatement.TerminatingContext); + // } + + if (!(statement is DirectivePrologue || statement is Syntax.Comment)) { if (m_needsStrictDirective) { @@ -622,6 +644,8 @@ public void Visit(BlockStatement node) m_startOfStatement = true; statement.Accept(this); prevStatement = statement; + if (!(statement is Syntax.Comment)) + prevNonCommentStatement = statement; } } @@ -639,7 +663,7 @@ public void Visit(BlockStatement node) OutputPossibleLineBreak('}'); MarkSegment(node, null, node.Context); } - else if (prevStatement != null && m_requiresSeparator.Query(prevStatement) && settings.TermSemicolons) + else if (prevStatement != null && requiresSeparator.Query(prevStatement) && settings.TermSemicolons) { // this is the root block (parent is null) and we want to make sure we end // with a terminating semicolon, so don't replace it @@ -1047,7 +1071,7 @@ public void Visit(ConditionalCompilationComment node) statement = node.Statements[ndx]; if (statement != null) { - if (prevStatement != null && m_requiresSeparator.Query(prevStatement)) + if (prevStatement != null && requiresSeparator.Query(prevStatement)) { OutputPossibleLineBreak(';'); MarkSegment(prevStatement, null, prevStatement.TerminatingContext); @@ -1573,7 +1597,7 @@ public void Visit(DoWhileStatement node) m_startOfStatement = true; node.Body[0].Accept(this); - if (m_requiresSeparator.Query(node.Body[0])) + if (requiresSeparator.Query(node.Body[0])) { // because the next thing we are going to output is a while keyword, if the // semicolon would be at the end of a line, we can skip it and just let the @@ -2064,16 +2088,16 @@ public void Visit(IfStatement node) m_startOfStatement = true; node.TrueBlock[0].Accept(this); - if (node.TrueBlock[0] is ImportantComment) + if (node.TrueBlock[0] is Syntax.Comment) { - // the true-block only contained a single important comment. + // the true-block only contained a single comment. // that's not a true statement, so terminate it with an empty-statement // semicolon OutputPossibleLineBreak(';'); } if (node.FalseBlock != null && node.FalseBlock.Count > 0 - && m_requiresSeparator.Query(node.TrueBlock[0])) + && requiresSeparator.Query(node.TrueBlock[0])) { // we have only one statement, we did not wrap it in braces, // and we have an else-block, and the one true-statement needs @@ -2127,16 +2151,14 @@ public void Visit(IfStatement node) } } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { if (node != null) { var symbol = StartSymbol(node); - // make sure we force the important comments to start on a new line, regardless - // of whether or not we are in multi- or single-line mode, and the statement after - // should also be on a new line. - BreakLine(true); + if(settings.OutputMode == OutputMode.MultipleLines) + BreakLine(true); node.Context.OutputLine = lineCount; @@ -2145,22 +2167,26 @@ public void Visit(ImportantComment node) // the entire comment as a single line, AND we want to normalize the line-feed characters, // so lets process the comment line-by-line var startIndex = 0; - var firstLF = node.Comment.IndexOfAny(LineFeedCharacters, startIndex); + var firstLF = node.Value.IndexOfAny(LineFeedCharacters, startIndex); if (firstLF < 0) { // no line-breaks at all! - Output(node.Comment); + Output(node.Value); + + // force a line-break + if(!node.IsMultiLine) + BreakLine(true); } else { // output the first segment -- from start to first line break - Output(node.Comment.Substring(0, firstLF)); + Output(node.Value.Substring(0, firstLF)); while (true) { // advance the next segment pointer - if (node.Comment[firstLF] == '\r' - && firstLF < node.Comment.Length - 1 - && node.Comment[firstLF + 1] == '\n') + if (node.Value[firstLF] == '\r' + && firstLF < node.Value.Length - 1 + && node.Value[firstLF + 1] == '\n') { startIndex = firstLF + 2; } @@ -2173,24 +2199,23 @@ public void Visit(ImportantComment node) BreakLine(true); // look for the next line break - firstLF = node.Comment.IndexOfAny(LineFeedCharacters, startIndex); + firstLF = node.Value.IndexOfAny(LineFeedCharacters, startIndex); if (firstLF > startIndex) { // only output something if there was something before the next line break - Output(node.Comment.Substring(startIndex, firstLF - startIndex)); + Output(node.Value.Substring(startIndex, firstLF - startIndex)); } else if (firstLF < 0) { // no more line-breaks -- output the last segment and break out of the loop - Output(node.Comment.Substring(startIndex)); + Output(node.Value.Substring(startIndex)); break; } } + } - // force a line-break AFTER teh important comment as well - BreakLine(true); EndSymbol(symbol); } } @@ -2860,7 +2885,7 @@ public void Visit(SwitchStatement node) var switchCase = node.Cases[ndx]; if (switchCase != null) { - if (prevSwitchCase != null && m_requiresSeparator.Query(prevSwitchCase)) + if (prevSwitchCase != null && requiresSeparator.Query(prevSwitchCase)) { // because the next switch-case will always start with either the case or default // keyword, if the semicolon we are about the output would be at the end of a newline, @@ -2920,7 +2945,7 @@ public void Visit(SwitchCase node) var statement = node.Statements[ndx]; if (statement != null) { - if (prevStatement != null && m_requiresSeparator.Query(prevStatement)) + if (prevStatement != null && requiresSeparator.Query(prevStatement)) { OutputPossibleLineBreak(';'); MarkSegment(prevStatement, null, prevStatement.TerminatingContext); @@ -4066,7 +4091,7 @@ void OutputBlock(BlockStatement block) { Indent(); NewLine(); - if (block[0] is ImportantComment) + if (block[0] is Syntax.Comment) { // not a REAL statement, so follow the comment with a semicolon to // be the actual statement for this block. diff --git a/src/NUglify/JavaScript/Visitors/RemoveCommentsVisitor.cs b/src/NUglify/JavaScript/Visitors/RemoveCommentsVisitor.cs new file mode 100644 index 00000000..bf3620c9 --- /dev/null +++ b/src/NUglify/JavaScript/Visitors/RemoveCommentsVisitor.cs @@ -0,0 +1,47 @@ +using System; +using NUglify.JavaScript.Syntax; + +namespace NUglify.JavaScript.Visitors +{ + public class RemoveCommentsVisitor : TreeVisitor + { + readonly JSParser parser; + + RemoveCommentsVisitor(JSParser parser) + { + this.parser = parser; + } + + public static void Apply(BlockStatement block, JSParser parser) + { + if (parser == null) + throw new ArgumentNullException(nameof(parser)); + + if (block != null) + { + // create a new instance of the visitor and apply it to the block + var visitor = new RemoveCommentsVisitor(parser); + block.Accept(visitor); + } + } + public override void Visit(BlockStatement node) + { + if (node != null) + { + // iterate backwards as we are likely to remove nodes + for (var ndx = node.Count - 1; ndx >= 0; --ndx) + node[ndx].Accept(this); + } + } + + + public override void Visit(Syntax.Comment node) + { + if (parser.Settings.CommentMode != JsComment.PreserveNone && (parser.Settings.CommentMode != JsComment.PreserveImportant || node.IsImportant)) + return; + + node.Parent.ReplaceChild(node, null); + node.Parent = null; + } + } +} \ No newline at end of file diff --git a/src/NUglify/JavaScript/Visitors/ReorderScopeVisitor.cs b/src/NUglify/JavaScript/Visitors/ReorderScopeVisitor.cs index bb4543a6..6346e5c2 100644 --- a/src/NUglify/JavaScript/Visitors/ReorderScopeVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/ReorderScopeVisitor.cs @@ -101,8 +101,7 @@ public static void Apply(BlockStatement block, JSParser parser) // Make sure that we skip over any remaining comments and directive prologues. // we do NOT want to insert anything between the start of the scope and any directive prologues. - while (insertAt < block.Count - && (block[insertAt] is DirectivePrologue || block[insertAt] is ImportantComment)) + while (insertAt < block.Count && (block[insertAt] is DirectivePrologue || block[insertAt] is Syntax.Comment)) { ++insertAt; } @@ -158,11 +157,9 @@ public static void Apply(BlockStatement block, JSParser parser) static int RelocateDirectivePrologue(BlockStatement block, int insertAt, DirectivePrologue directivePrologue) { - // skip over any important comments - while (insertAt < block.Count && (block[insertAt] is ImportantComment)) - { - ++insertAt; - } + // skip over any comments + while (insertAt < block.Count && (block[insertAt] is Syntax.Comment)) + ++insertAt; // if the one we want to insert is already at this spot, then we're good to go if (block[insertAt] != directivePrologue) @@ -598,10 +595,8 @@ public override void Visit(BlockStatement node) { // walk backwards until we find the last non-comment statement var ndxLast = node.Count - 1; - while (ndxLast >= 0 && node[ndxLast] is ImportantComment) - { - --ndxLast; - } + while (ndxLast >= 0 && (node[ndxLast] is Syntax.Comment)) + --ndxLast; // if the last non-comment statement isn't the first.... if (ndxLast > 0) diff --git a/src/NUglify/JavaScript/Visitors/RequiresSeparatorVisitor.cs b/src/NUglify/JavaScript/Visitors/RequiresSeparatorVisitor.cs index 4daa81c5..72555bcb 100644 --- a/src/NUglify/JavaScript/Visitors/RequiresSeparatorVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/RequiresSeparatorVisitor.cs @@ -415,12 +415,10 @@ public void Visit(IfStatement node) } } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { if (node != null) - { - DoesRequire = false; - } + DoesRequire = false; } public void Visit(ImportExportSpecifier node) diff --git a/src/NUglify/JavaScript/Visitors/ResolutionVisitor.cs b/src/NUglify/JavaScript/Visitors/ResolutionVisitor.cs index 5873a545..1ccb0bda 100644 --- a/src/NUglify/JavaScript/Visitors/ResolutionVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/ResolutionVisitor.cs @@ -1309,7 +1309,8 @@ public void Visit(IfStatement node) } } - public void Visit(ImportantComment node) + + public void Visit(Syntax.Comment node) { // nothing to do } diff --git a/src/NUglify/JavaScript/Visitors/StatementStartVisitor.cs b/src/NUglify/JavaScript/Visitors/StatementStartVisitor.cs index 2c1b73b0..d8f7be5d 100644 --- a/src/NUglify/JavaScript/Visitors/StatementStartVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/StatementStartVisitor.cs @@ -279,7 +279,7 @@ public void Visit(IfStatement node) // starts with an 'if', so we don't care } - public void Visit(ImportantComment node) + public void Visit(Syntax.Comment node) { // comment, so we need to keep going } diff --git a/src/NUglify/JavaScript/Visitors/TreeVisitor.cs b/src/NUglify/JavaScript/Visitors/TreeVisitor.cs index 162b776a..cd06d84c 100644 --- a/src/NUglify/JavaScript/Visitors/TreeVisitor.cs +++ b/src/NUglify/JavaScript/Visitors/TreeVisitor.cs @@ -14,6 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Linq; using NUglify.JavaScript.Syntax; namespace NUglify.JavaScript.Visitors @@ -83,13 +84,8 @@ public virtual void Visit(BlockStatement node) { if (node != null) { - foreach (var statement in node.Children) - { - if (statement != null) - { - statement.Accept(this); - } - } + foreach (var statement in node.Children) + statement?.Accept(this); } } @@ -100,39 +96,21 @@ public virtual void Visit(BreakStatement node) public virtual void Visit(CallExpression node) { - if (node != null) - { - if (node.Arguments != null) - { - node.Arguments.Accept(this); - } + if (node == null) + return; - if (node.Function != null) - { - node.Function.Accept(this); - } - } + node.Arguments?.Accept(this); + node.Function?.Accept(this); } public virtual void Visit(ClassNode node) { - if (node != null) - { - if (node.Binding != null) - { - node.Binding.Accept(this); - } + if (node == null) + return; - if (node.Heritage != null) - { - node.Heritage.Accept(this); - } - - if (node.Elements != null) - { - node.Elements.Accept(this); - } - } + node.Binding?.Accept(this); + node.Heritage?.Accept(this); + node.Elements?.Accept(this); } public void Visit(ClassField node) @@ -444,7 +422,7 @@ public virtual void Visit(IfStatement node) } } - public virtual void Visit(ImportantComment node) + public virtual void Visit(Syntax.Comment node) { // no children } diff --git a/src/NUglify/UglifyCommandParser.cs b/src/NUglify/UglifyCommandParser.cs index 482b3af1..3a67a0bc 100644 --- a/src/NUglify/UglifyCommandParser.cs +++ b/src/NUglify/UglifyCommandParser.cs @@ -530,22 +530,22 @@ public void Parse(string[] args) case "COMMENTS": // four options for css: none, all, important, or hacks - // two options for js: none, important + // three options for js: none, important, all // (default is important) if (paramPartUpper == "NONE") { CssSettings.CommentMode = CssComment.None; - JSSettings.PreserveImportantComments = false; + JSSettings.CommentMode = JsComment.PreserveNone; } else if (paramPartUpper == "ALL") { CssSettings.CommentMode = CssComment.All; - OnCssOnlyParameter(); + JSSettings.CommentMode = JsComment.PreserveAll; } else if (paramPartUpper == "IMPORTANT") { CssSettings.CommentMode = CssComment.Important; - JSSettings.PreserveImportantComments = true; + JSSettings.CommentMode = JsComment.PreserveImportant; } else if (paramPartUpper == "HACKS") { @@ -1997,26 +1997,17 @@ protected virtual int OnUnknownParameter(IList arguments, int index, str protected virtual void OnInvalidSwitch(string switchPart, string parameterPart) { - if (InvalidSwitch != null) - { - InvalidSwitch(this, new InvalidSwitchEventArgs() { SwitchPart = switchPart, ParameterPart = parameterPart }); - } + InvalidSwitch?.Invoke(this, new InvalidSwitchEventArgs { SwitchPart = switchPart, ParameterPart = parameterPart }); } protected virtual void OnJSOnlyParameter() { - if (JSOnlyParameter != null) - { - JSOnlyParameter(this, new EventArgs()); - } + JSOnlyParameter?.Invoke(this, new EventArgs()); } protected virtual void OnCssOnlyParameter() { - if (CssOnlyParameter != null) - { - CssOnlyParameter(this, new EventArgs()); - } + CssOnlyParameter?.Invoke(this, new EventArgs()); } #endregion