diff --git a/src/NUglify.Tests/App.config b/src/NUglify.Tests/App.config
index 45437954..9fabdef4 100644
--- a/src/NUglify.Tests/App.config
+++ b/src/NUglify.Tests/App.config
@@ -1,6 +1,14 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/src/NUglify.Tests/JavaScript/AllJavascriptSyntaxTest.cs b/src/NUglify.Tests/JavaScript/AllJavascriptSyntaxTest.cs
new file mode 100644
index 00000000..a963614d
--- /dev/null
+++ b/src/NUglify.Tests/JavaScript/AllJavascriptSyntaxTest.cs
@@ -0,0 +1,17 @@
+using NUglify.Tests.JavaScript.Common;
+using NUnit.Framework;
+
+namespace NUglify.Tests.JavaScript
+{
+ [TestFixture]
+ public class AllJavascriptSyntaxTest
+ {
+ ///
+ /// check all possible files in input-directory for syntax errors after minification
+ ///
+ [Test]
+ public void SyntaxTestForAllFilesLineBreaks() {
+ TestHelper.Instance.RunSyntaxTestForAllFilesLineBreaks();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NUglify.Tests/JavaScript/Common/TestHelper.cs b/src/NUglify.Tests/JavaScript/Common/TestHelper.cs
index e85afd6b..d66ae6f5 100644
--- a/src/NUglify.Tests/JavaScript/Common/TestHelper.cs
+++ b/src/NUglify.Tests/JavaScript/Common/TestHelper.cs
@@ -18,10 +18,14 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using System.Xml;
+using Microsoft.ClearScript;
+using Microsoft.ClearScript.V8;
using NUglify.JavaScript;
using NUglify.JavaScript.Visitors;
using NUnit.Framework;
@@ -700,6 +704,113 @@ public void RunErrorTest(params JSError[] expectedErrorArray)
RunErrorTest(string.Empty, expectedErrorArray);
}
+ ///
+ /// check all files in input-directory for syntax errors after minification
+ /// execute javascript in V8 engine
+ /// each file that starts successfully without minification will be rerun after minification.
+ /// in some cases minifier generate endless running code. Example: D:\Git2\NUglify\src\NUglify.Tests\bin\Debug\TestData\JS\Input\ES2015\GeneratorFunction.js
+ ///
+ ///
+ public void RunSyntaxTestForAllFilesLineBreaks() {
+ DirectoryInfo dir = new DirectoryInfo(InputFolder);
+ List allTestFiles = dir.GetFiles("*.js", SearchOption.AllDirectories).ToList();
+ var sw = new Stopwatch();
+ sw.Start();
+ var timeout = TimeSpan.FromSeconds(2);
+ var testResults = allTestFiles.Select(fi => {
+ string jsSource = File.ReadAllText(fi.FullName);
+ var (debugFinished, debugExecutionStarted, _) = Execute(jsSource, fi.Name, "debug", sw, timeout);
+ if (!debugExecutionStarted) {
+ Trace.WriteLine($"{fi.Name}: Syntax error, skipping minification ({fi.FullName}).");
+ return null;
+ } else {
+ var crunchedCode = Minify(jsSource, "-line:1");
+ var (minifiedFinished, minifiedExecutionStarted, minifiedException) = Execute(crunchedCode, fi.Name, "minified", sw, timeout);
+ if (!minifiedExecutionStarted) {
+ //There
+ Assert.NotNull(minifiedException, "There should be an exception if the execution did not even start.");
+ return new {
+ testPassed = false,
+ message = minifiedException.ErrorDetails,
+ filePath = fi.FullName,
+ minifiedContent = crunchedCode
+ };
+ } else if (!minifiedFinished && debugFinished) {
+ //Timeout in minified code, but not in original.
+ return new {
+ testPassed = false,
+ message = "Timeout in minified code, but not in original",
+ filePath = fi.FullName,
+ minifiedContent = crunchedCode
+ };
+ } else {
+ return null;
+ }
+ }
+ }).Where(r => r != null).ToList();
+
+ if (testResults.Any()) {
+ throw new Exception("Test failed, following files had errors." + Environment.NewLine + String.Join(Environment.NewLine, testResults.Select(f => $"{f.filePath} => {f.message}{Environment.NewLine}{f.minifiedContent}{Environment.NewLine}")));
+ }
+ }
+
+ private static String Minify(string code, string minifyParameters) {
+ var switchParser = new UglifyCommandParser();
+ switchParser.Parse(minifyParameters);
+
+ var sb = new StringBuilder();
+ var parser = new JSParser();
+ using (var writer = new StringWriter(sb)) {
+ if (switchParser.JSSettings.PreprocessOnly) {
+ parser.EchoWriter = writer;
+ }
+ // normal -- just run it through the parser
+ var block = parser.Parse(new DocumentContext(code), switchParser.JSSettings);
+ if (!switchParser.JSSettings.PreprocessOnly) {
+ // look at the settings for the proper output visitor
+ if (switchParser.JSSettings.Format == JavaScriptFormat.JSON) {
+ {
+ if (!JsonOutputVisitor.Apply(writer, block, switchParser.JSSettings)) {
+ Trace.WriteLine("JSON OUTPUT ERRORS!");
+ }
+ }
+ } else {
+ OutputVisitor.Apply(writer, block, switchParser.JSSettings);
+ }
+ }
+ }
+ return sb.ToString();
+ }
+
+ private (bool, bool, ScriptEngineException) Execute(string code, string filename, string type, Stopwatch sw, TimeSpan timeout) {
+ var engine = new V8ScriptEngine();
+ bool executionStarted = false;
+ ScriptEngineException exception = null;
+ var task = Task.Run(() => {
+ try {
+ Trace.WriteLine($"{sw.ElapsedMilliseconds}: Starting {filename}.{type}.");
+ engine.Execute(code);
+ Trace.WriteLine($"{sw.ElapsedMilliseconds}: Finished {filename}.{type}.");
+ executionStarted = true;
+ } catch (ScriptEngineException e) {
+ exception = e;
+ if (e.ExecutionStarted) {
+ executionStarted = true;
+ Trace.WriteLine($"{sw.ElapsedMilliseconds}: Exception from {filename}.{type}. Execution did start.");
+ } else {
+ Trace.WriteLine($"{sw.ElapsedMilliseconds}: Exception from {filename}.{type}. Execution did not even start.");
+ }
+ }
+ });
+ bool finishedInTime = task.Wait(timeout);
+ if (!finishedInTime) {
+ //If a timeout occurs, we do just assume that the execution started.
+ executionStarted = true;
+ }
+ Assert.IsTrue(executionStarted || exception != null, "There should be an exception if the execution did not even start.");
+ return (finishedInTime, executionStarted, exception);
+ }
+
public void RunErrorTest(string settingsSwitches, params JSError[] expectedErrorArray)
{
// open the stack trace for this call
diff --git a/src/NUglify.Tests/NUglify.Tests.csproj b/src/NUglify.Tests/NUglify.Tests.csproj
index 4b8c3e01..a44978a3 100644
--- a/src/NUglify.Tests/NUglify.Tests.csproj
+++ b/src/NUglify.Tests/NUglify.Tests.csproj
@@ -1,5 +1,7 @@
+
+
Debug
@@ -43,13 +45,55 @@
+
+ ..\packages\Microsoft.ClearScript.Core.7.3.1\lib\net45\ClearScript.Core.dll
+
+
+ ..\packages\Microsoft.ClearScript.V8.7.3.1\lib\net45\ClearScript.V8.dll
+
+
+ ..\packages\Microsoft.ClearScript.V8.ICUData.7.3.1\lib\netstandard1.0\ClearScript.V8.ICUData.dll
+
+
+ ..\packages\Microsoft.ClearScript.Windows.7.3.1\lib\net45\ClearScript.Windows.dll
+
+
+ ..\packages\Microsoft.ClearScript.Windows.Core.7.3.1\lib\net45\ClearScript.Windows.Core.dll
+
+
+
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll
+
+
+ ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll
+
-
@@ -98,6 +142,7 @@
+
@@ -166,6 +211,7 @@
+
PreserveNewest
diff --git a/src/NUglify.Tests/NUglify.Tests.nuget.props b/src/NUglify.Tests/NUglify.Tests.nuget.props
index 5929be82..9ed58d6e 100644
--- a/src/NUglify.Tests/NUglify.Tests.nuget.props
+++ b/src/NUglify.Tests/NUglify.Tests.nuget.props
@@ -15,7 +15,13 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+
+
+
+
C:\Users\Andrew Bullock\.nuget\packages\nunit3testadapter\3.5.0
+ C:\Users\Andrew Bullock\.nuget\packages\microsoft.clearscript.v8.native.win-x86\7.3.1
+ C:\Users\Andrew Bullock\.nuget\packages\microsoft.clearscript.v8.native.win-x64\7.3.1
\ No newline at end of file
diff --git a/src/NUglify.Tests/packages.config b/src/NUglify.Tests/packages.config
new file mode 100644
index 00000000..0e75d423
--- /dev/null
+++ b/src/NUglify.Tests/packages.config
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/NUglify.Tests/project.json b/src/NUglify.Tests/project.json
index 1159bcaa..fe7a586b 100644
--- a/src/NUglify.Tests/project.json
+++ b/src/NUglify.Tests/project.json
@@ -19,6 +19,7 @@
},
"dependencies": {
"NUnit": "3.5.0",
- "NUnit3TestAdapter": "3.5.0"
+ "NUnit3TestAdapter": "3.5.0",
+ "Microsoft.ClearScript": "7.3.1"
}
}
\ No newline at end of file
diff --git a/src/NUglify/JavaScript/Visitors/OutputVisitor.cs b/src/NUglify/JavaScript/Visitors/OutputVisitor.cs
index 4ea85909..8f497816 100644
--- a/src/NUglify/JavaScript/Visitors/OutputVisitor.cs
+++ b/src/NUglify/JavaScript/Visitors/OutputVisitor.cs
@@ -767,7 +767,7 @@ public void Visit(CallExpression node)
{
if (node.OptionalChaining)
{
- OutputPossibleLineBreak('?');
+ Output('?');
OutputPossibleLineBreak('.');
}
@@ -2549,7 +2549,7 @@ public void Visit(MemberExpression node)
}
if (node.OptionalChaining)
- OutputPossibleLineBreak('?');
+ Output('?');
OutputPossibleLineBreak('.');
MarkSegment(node, node.Name, node.NameContext);
@@ -3985,7 +3985,14 @@ void OutputFunctionArgsAndBody(FunctionObject node)
Unindent();
if (wrapInParens)
{
- OutputPossibleLineBreak(')');
+ if (node.FunctionType == FunctionType.ArrowFunction)
+ {
+ Output(')');
+ }
+ else
+ {
+ OutputPossibleLineBreak(')');
+ }
MarkSegment(node, null, node.ParameterDeclarations.Context);
}
}
@@ -3993,7 +4000,7 @@ void OutputFunctionArgsAndBody(FunctionObject node)
{
// empty arrow function parameters need the empty parentheses
OutputPossibleLineBreak('(');
- OutputPossibleLineBreak(')');
+ Output(')');
m_startOfStatement = false;
}