diff --git a/src/Cake.ESLint.Tests/Cake.ESLint.Tests.csproj b/src/Cake.ESLint.Tests/Cake.ESLint.Tests.csproj index 49efde78..d5d4b124 100644 --- a/src/Cake.ESLint.Tests/Cake.ESLint.Tests.csproj +++ b/src/Cake.ESLint.Tests/Cake.ESLint.Tests.csproj @@ -15,10 +15,6 @@ all - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - diff --git a/src/Cake.ESLint.Tests/ESLintAliasesTests.Runs_tool_with_given_action.verified.txt b/src/Cake.ESLint.Tests/ESLintAliasesTests.Runs_tool_with_given_action.verified.txt index 8420bbf0..cd57286f 100644 --- a/src/Cake.ESLint.Tests/ESLintAliasesTests.Runs_tool_with_given_action.verified.txt +++ b/src/Cake.ESLint.Tests/ESLintAliasesTests.Runs_tool_with_given_action.verified.txt @@ -9,6 +9,9 @@ ] }, Process: { + Arguments: [ + {} + ], WorkingDirectory: { FullPath: /Working, Separator: "/", @@ -17,5 +20,5 @@ ] } }, - Args: + Args: "**/*" } \ No newline at end of file diff --git a/src/Cake.ESLint.Tests/ESLintAliasesTests.cs b/src/Cake.ESLint.Tests/ESLintAliasesTests.cs index 20b90bb0..7a287a48 100644 --- a/src/Cake.ESLint.Tests/ESLintAliasesTests.cs +++ b/src/Cake.ESLint.Tests/ESLintAliasesTests.cs @@ -64,6 +64,7 @@ public async Task Runs_tool_with_given_settings() [Fact] public async Task Runs_tool_with_given_action() { + fixture.Settings = null; fixture.Action = x => x.Files = new[] {new FilePath("**/*")}; var result = fixture.Run(); diff --git a/src/Cake.ESLint.Tests/ESLintRunnerTests.cs b/src/Cake.ESLint.Tests/ESLintRunnerTests.cs index c1f43ccd..77d7742a 100644 --- a/src/Cake.ESLint.Tests/ESLintRunnerTests.cs +++ b/src/Cake.ESLint.Tests/ESLintRunnerTests.cs @@ -59,6 +59,16 @@ public void Should_Throw_If_Settings_Are_Null() result.ShouldThrow(); } + [Fact] + public void Should_Throw_If_Log_Is_Null() + { + fixture.Log = null; + + Action result = () => fixture.Run(); + + result.ShouldThrow(); + } + [Fact] public void Should_Throw_If_ESLint_Executable_Was_Not_Found() { @@ -84,6 +94,18 @@ public void Should_use_local_eslint_from_nodeModules_if_exists() actual.Path.FullPath.ShouldBe(expected.Path.FullPath); } + [Fact] + public void Should_use_eslint_cmd_on_windows() + { + fixture.GivenDefaultToolDoNotExist(); + var expected = fixture.GivenFileExists("/some/project/node_modules/.bin/eslint"); + fixture.Settings.WorkingDirectory = "/some/project"; + + var actual = fixture.Run(); + + actual.Path.FullPath.ShouldBe(expected.Path.FullPath); + } + [Fact] public void Should_use_default_eslint_when_workingDirectory_contains_no_nodeModules() { @@ -290,6 +312,17 @@ public void Should_add_rulesdir_arg_when_rulesDirs_is_set() actual.Args.ShouldContain("--rulesdir \"my-rules\" --rulesdir \"my-other-rules\""); } + [Fact] + public void Should_add_rulesdir_arg_when_rulesDirs_is_set_multiple_times() + { + fixture.Settings.AddRulesDir("my-rules"); + fixture.Settings.AddRulesDir("my-other-rules"); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--rulesdir \"my-rules\" --rulesdir \"my-other-rules\""); + } + [Fact] public void Should_add_plugin_arg_when_plugins_is_set() { @@ -300,6 +333,17 @@ public void Should_add_plugin_arg_when_plugins_is_set() actual.Args.ShouldContain("--plugin jquery --plugin eslint-plugin-mocha"); } + [Fact] + public void Should_add_plugin_arg_when_plugins_is_set_multiple_times() + { + fixture.Settings.AddPlugin("jquery"); + fixture.Settings.AddPlugin("eslint-plugin-mocha"); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--plugin jquery --plugin eslint-plugin-mocha"); + } + [Fact] public void Should_add_rule_arg_when_Rules_is_set() { @@ -310,6 +354,88 @@ public void Should_add_rule_arg_when_Rules_is_set() actual.Args.ShouldContain("--rule \"guard-for-in: 2\" --rule \"brace-style: [2, 1tbs]\""); } + [Fact] + public void Should_add_rule_arg_when_Rules_is_set_multiple_times() + { + fixture.Settings.AddRule("guard-for-in: 2"); + fixture.Settings.AddRule("brace-style: [2, 1tbs]"); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--rule \"guard-for-in: 2\" --rule \"brace-style: [2, 1tbs]\""); + } + + [Fact] + public void Should_add_fix_arg_when_Fix_is_set() + { + fixture.Settings.Fix = true; + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--fix"); + } + + [Fact] + public void Should_add_fix_dry_run_arg_when_FixDryRun_is_set() + { + fixture.Settings.FixDryRun = true; + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--fix-dry-run"); + } + + [Fact] + public void Should_add_fix_type_arg_when_FixType_is_set() + { + fixture.Settings.AddFixType(ESLintFixType.Layout); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--fix-type layout"); + } + + [Fact] + public void Should_add_fix_type_arg_when_FixType_is_set_with_multiple_flags() + { + fixture.Settings.AddFixType(ESLintFixType.Problem, ESLintFixType.Suggestion); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--fix-type problem --fix-type suggestion"); + } + + [Fact] + public void Should_add_ignore_path_arg_when_IgnorePath_is_set() + { + fixture.Settings.IgnorePath = "tmp/.eslintignore"; + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--ignore-path \"tmp/.eslintignore\""); + } + + [Fact] + public void Should_add_ignore_pattern_arg_when_IgnorePatterns_is_set() + { + fixture.Settings.AddIgnorePattern(new DirectoryPath("/lib/")); + fixture.Settings.AddIgnorePattern(new FilePath("/src/vendor/*")); + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--ignore-pattern \"/lib/\" --ignore-pattern \"/src/vendor/*\""); + } + + [Fact] + public void Should_add_no_ignore_arg_when_NoIgnore_is_set() + { + fixture.Settings.NoIgnore = true; + + var actual = fixture.Run(); + + actual.Args.ShouldContain("--no-ignore"); + } + // ReSharper disable once ClassNeverInstantiated.Local private class OutputFormatDataGenerator : IEnumerable { diff --git a/src/Cake.ESLint.Tests/Fixtures/ESLintRunnerFixture.cs b/src/Cake.ESLint.Tests/Fixtures/ESLintRunnerFixture.cs index 4f49fe49..048d5a17 100644 --- a/src/Cake.ESLint.Tests/Fixtures/ESLintRunnerFixture.cs +++ b/src/Cake.ESLint.Tests/Fixtures/ESLintRunnerFixture.cs @@ -36,7 +36,7 @@ public ESLintRunnerFixture() Log = new FakeLog(); } - internal FakeLog Log { get; } + internal FakeLog Log { get; set; } protected override void RunTool() { diff --git a/src/Cake.ESLint.sln.DotSettings b/src/Cake.ESLint.sln.DotSettings index b2ab76b7..1d231384 100644 --- a/src/Cake.ESLint.sln.DotSettings +++ b/src/Cake.ESLint.sln.DotSettings @@ -1,5 +1,7 @@  ES True + True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/Cake.ESLint/ESLintFixType.cs b/src/Cake.ESLint/ESLintFixType.cs new file mode 100644 index 00000000..cf354c61 --- /dev/null +++ b/src/Cake.ESLint/ESLintFixType.cs @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2021 Nils Andresen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; + +namespace Cake.ESLint +{ + /// + /// Options for --fix-type. + /// + public sealed class ESLintFixType + { + private ESLintFixType(string name) + { + this.Name = name; + } + + /// + /// Gets the fix-type: Problem. + /// + public static ESLintFixType Problem { get; } = new ESLintFixType("problem"); + + /// + /// Gets the fix-type: Suggestion. + /// + public static ESLintFixType Suggestion { get; } = new ESLintFixType("suggestion"); + + /// + /// Gets the fix-type: Layout. + /// + public static ESLintFixType Layout { get; } = new ESLintFixType("layout"); + + /// + /// Gets the name of the problem. + /// + public string Name { get; } + } +} diff --git a/src/Cake.ESLint/ESLintRunner.cs b/src/Cake.ESLint/ESLintRunner.cs index 5af4652f..4549bd1e 100644 --- a/src/Cake.ESLint/ESLintRunner.cs +++ b/src/Cake.ESLint/ESLintRunner.cs @@ -193,6 +193,36 @@ private ProcessArgumentBuilder GetArguments(ESLintSettings settings) builder.AppendSwitchQuoted("--rule", rule); } + if (settings.Fix) + { + builder.Append("--fix"); + } + + if (settings.FixDryRun) + { + builder.Append("--fix-dry-run"); + } + + foreach (var type in settings.FixTypes.EnsureNotNull()) + { + builder.AppendSwitch("--fix-type", type.Name); + } + + if (settings.IgnorePath != null) + { + builder.AppendSwitchQuoted("--ignore-path", settings.IgnorePath.FullPath); + } + + if (settings.NoIgnore) + { + builder.Append("--no-ignore"); + } + + foreach (var pattern in settings.IgnorePatterns.EnsureNotNull()) + { + builder.AppendSwitchQuoted("--ignore-pattern", pattern); + } + // render arguments foreach (var file in settings.Files.EnsureNotNull()) { diff --git a/src/Cake.ESLint/ESLintSettings.cs b/src/Cake.ESLint/ESLintSettings.cs index 266e0e73..ecf568c2 100644 --- a/src/Cake.ESLint/ESLintSettings.cs +++ b/src/Cake.ESLint/ESLintSettings.cs @@ -129,6 +129,44 @@ public ESLintSettings() /// public IEnumerable Rules { get; set; } + /// + /// Gets or sets a value indicating whether to fix problems. + /// Option: --fix. + /// + public bool Fix { get; set; } + + /// + /// Gets or sets a value indicating whether to fix problems without saving the changes to the file system. + /// Option: --fix-dry-run. + /// + public bool FixDryRun { get; set; } + + /// + /// Gets or sets the types of fixes to apply. + /// Option: --fix-types. + /// + public IEnumerable FixTypes { get; set; } + + /// + /// Gets or sets the path to the ignoreFile. + /// Option: --ignore-path. + /// + public FilePath IgnorePath { get; set; } + + /// + /// Gets or sets a value indicating whether + /// to disable use of ignore files and patterns. + /// Option: --no-ignore. + /// + public bool NoIgnore { get; set; } + + /// + /// Gets or sets patterns of files to ignore + /// (in addition to those in .eslintignore). + /// Option: --ignore-pattern. + /// + public IEnumerable IgnorePatterns { get; set; } + /// /// Gets or sets a value indicating whether to continue on lint errors. /// diff --git a/src/Cake.ESLint/ESLintSettingsExtensions.cs b/src/Cake.ESLint/ESLintSettingsExtensions.cs index acafa32f..dae6b5cb 100644 --- a/src/Cake.ESLint/ESLintSettingsExtensions.cs +++ b/src/Cake.ESLint/ESLintSettingsExtensions.cs @@ -22,6 +22,7 @@ * SOFTWARE. */ +using System; using System.Collections.Generic; using System.Linq; @@ -51,9 +52,10 @@ public static void SetParser(this ESLintSettings @this, FilePath pathToPlugin) /// The paths to add. public static void AddDirectory(this ESLintSettings @this, params DirectoryPath[] directoryPaths) { - var paths = @this.Directories?.ToList() ?? new List(); - paths.AddRange(directoryPaths); - @this.Directories = paths; + @this.AddToList( + x => x.Directories, + (x, y) => x.Directories = y, + directoryPaths); } /// @@ -63,9 +65,10 @@ public static void AddDirectory(this ESLintSettings @this, params DirectoryPath[ /// The paths to add. public static void AddFile(this ESLintSettings @this, params FilePath[] filePaths) { - var paths = @this.Files?.ToList() ?? new List(); - paths.AddRange(filePaths); - @this.Files = paths; + @this.AddToList( + x => x.Files, + (x, y) => x.Files = y, + filePaths); } /// @@ -75,9 +78,10 @@ public static void AddFile(this ESLintSettings @this, params FilePath[] filePath /// The paths to add. public static void AddRulesDir(this ESLintSettings @this, params DirectoryPath[] rulesDir) { - var paths = @this.RulesDirs?.ToList() ?? new List(); - paths.AddRange(rulesDir); - @this.RulesDirs = paths; + @this.AddToList( + x => x.RulesDirs, + (x, y) => x.RulesDirs = y, + rulesDir); } /// @@ -87,9 +91,10 @@ public static void AddRulesDir(this ESLintSettings @this, params DirectoryPath[] /// The plugins to add. public static void AddPlugin(this ESLintSettings @this, params string[] plugin) { - var plugins = @this.Plugins?.ToList() ?? new List(); - plugins.AddRange(plugin); - @this.Plugins = plugins; + @this.AddToList( + x => x.Plugins, + (x, y) => x.Plugins = y, + plugin); } /// @@ -99,9 +104,61 @@ public static void AddPlugin(this ESLintSettings @this, params string[] plugin) /// The plugins to add. public static void AddRule(this ESLintSettings @this, params string[] rule) { - var plugins = @this.Rules?.ToList() ?? new List(); - plugins.AddRange(rule); - @this.Rules = plugins; + @this.AddToList( + x => x.Rules, + (x, y) => x.Rules = y, + rule); + } + + /// + /// adds to . + /// + /// The . + /// The FixType to add. + public static void AddFixType(this ESLintSettings @this, params ESLintFixType[] fixType) + { + @this.AddToList( + x => x.FixTypes, + (x, y) => x.FixTypes = y, + fixType); + } + + /// + /// adds to . + /// + /// The . + /// The pattern to add. + public static void AddIgnorePattern(this ESLintSettings @this, params FilePath[] pattern) + { + @this.AddToList( + x => x.IgnorePatterns, + (x, y) => x.IgnorePatterns = y, + pattern.Select(x => x.FullPath)); + } + + /// + /// adds to . + /// + /// The . + /// The pattern to add. + public static void AddIgnorePattern(this ESLintSettings @this, params DirectoryPath[] pattern) + { + // For directories we ensure a trailing "/" at the end of the pattern. + @this.AddToList( + x => x.IgnorePatterns, + (x, y) => x.IgnorePatterns = y, + pattern.Select(x => x.FullPath + x.Separator)); + } + + private static void AddToList( + this ESLintSettings @this, + Func> getter, + Action> setter, + IEnumerable toAdd) + { + var existing = getter(@this)?.ToList() ?? new List(); + existing.AddRange(toAdd); + setter(@this, existing); } } }