From 49592279ad462949fb68c3643ee85be0a062d839 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Mon, 28 Nov 2022 19:55:57 +0100 Subject: [PATCH 01/37] extract base serve code --- .../spotless/npm/NpmResourceHelper.java | 10 +++++- .../spotless/npm/PrettierFormatterStep.java | 4 ++- .../spotless/npm/TsFmtFormatterStep.java | 4 ++- .../com/diffplug/spotless/npm/common-serve.js | 32 +++++++++++++++++++ .../diffplug/spotless/npm/prettier-serve.js | 30 ----------------- .../com/diffplug/spotless/npm/tsfmt-serve.js | 31 ------------------ 6 files changed, 47 insertions(+), 64 deletions(-) create mode 100644 lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java index ad1211d717..cb6fff3290 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.Duration; +import java.util.Arrays; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import com.diffplug.spotless.ThrowingEx; @@ -45,6 +47,12 @@ static void deleteFileIfExists(File file) throws IOException { } } + static String readUtf8StringFromClasspath(Class clazz, String... resourceNames) { + return Arrays.stream(resourceNames) + .map(resourceName -> readUtf8StringFromClasspath(clazz, resourceName)) + .collect(Collectors.joining("\n")); + } + static String readUtf8StringFromClasspath(Class clazz, String resourceName) { try (InputStream input = clazz.getResourceAsStream(resourceName)) { return readUtf8StringFromInputStream(input); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java index 49d692d62c..5a5662eee4 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java @@ -70,7 +70,9 @@ private static class State extends NpmFormatterStepStateBase implements Serializ NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, "/com/diffplug/spotless/npm/prettier-package.json"), new TreeMap<>(devDependencies)), "prettier", - NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, "/com/diffplug/spotless/npm/prettier-serve.js"), + NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, + "/com/diffplug/spotless/npm/common-serve.js", + "/com/diffplug/spotless/npm/prettier-serve.js"), npmPathResolver.resolveNpmrcContent()), buildDir, npmPathResolver.resolveNpmExecutable()); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java index 14080ecf56..e9c098d709 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java @@ -76,7 +76,9 @@ public State(String stepName, Map versions, File buildDir, NpmPa new NpmConfig( replaceDevDependencies(NpmResourceHelper.readUtf8StringFromClasspath(TsFmtFormatterStep.class, "/com/diffplug/spotless/npm/tsfmt-package.json"), new TreeMap<>(versions)), "typescript-formatter", - NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, "/com/diffplug/spotless/npm/tsfmt-serve.js"), + NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, + "/com/diffplug/spotless/npm/common-serve.js", + "/com/diffplug/spotless/npm/tsfmt-serve.js"), npmPathResolver.resolveNpmrcContent()), buildDir, npmPathResolver.resolveNpmExecutable()); diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js new file mode 100644 index 0000000000..3e031cedea --- /dev/null +++ b/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js @@ -0,0 +1,32 @@ +// this file will be glued to the top of the specific xy-server.js file +const GracefulShutdownManager = require("@moebius/http-graceful-shutdown").GracefulShutdownManager; +const express = require("express"); +const app = express(); + +app.use(express.json({ limit: "50mb" })); + +const fs = require("fs"); + +var listener = app.listen(0, "127.0.0.1", () => { + console.log("Server running on port " + listener.address().port); + fs.writeFile("server.port.tmp", "" + listener.address().port, function(err) { + if (err) { + return console.log(err); + } else { + fs.rename("server.port.tmp", "server.port", function(err) { + if (err) { + return console.log(err); + } + }); // try to be as atomic as possible + } + }); +}); +const shutdownManager = new GracefulShutdownManager(listener); + +app.post("/shutdown", (req, res) => { + res.status(200).send("Shutting down"); + setTimeout(function() { + shutdownManager.terminate(() => console.log("graceful shutdown finished.")); + }, 200); +}); + diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js index 351ef73f9a..d4ce13bbbc 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js @@ -1,35 +1,5 @@ -const GracefulShutdownManager = require("@moebius/http-graceful-shutdown").GracefulShutdownManager; -const express = require("express"); -const app = express(); - -app.use(express.json({ limit: "50mb" })); const prettier = require("prettier"); -const fs = require("fs"); - -var listener = app.listen(0, "127.0.0.1", () => { - console.log("Server running on port " + listener.address().port); - fs.writeFile("server.port.tmp", "" + listener.address().port, function(err) { - if (err) { - return console.log(err); - } else { - fs.rename("server.port.tmp", "server.port", function(err) { - if (err) { - return console.log(err); - } - }); // try to be as atomic as possible - } - }); -}); -const shutdownManager = new GracefulShutdownManager(listener); - -app.post("/shutdown", (req, res) => { - res.status(200).send("Shutting down"); - setTimeout(function() { - shutdownManager.terminate(() => console.log("graceful shutdown finished.")); - }, 200); -}); - app.post("/prettier/config-options", (req, res) => { var config_data = req.body; var prettier_config_path = config_data.prettier_config_path; diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js index b25048d410..8ec25565ff 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js @@ -1,36 +1,5 @@ -const GracefulShutdownManager = require("@moebius/http-graceful-shutdown").GracefulShutdownManager; -const express = require("express"); -const app = express(); -app.use(express.json({ limit: "50mb" })); - const tsfmt = require("typescript-formatter"); -const fs = require("fs"); - -var listener = app.listen(0, "127.0.0.1", () => { - console.log("Server running on port " + listener.address().port); - fs.writeFile("server.port.tmp", "" + listener.address().port, function(err) { - if (err) { - return console.log(err); - } else { - fs.rename("server.port.tmp", "server.port", function(err) { - if (err) { - return console.log(err); - } - }); // try to be as atomic as possible - } - }); -}); - -const shutdownManager = new GracefulShutdownManager(listener); - -app.post("/shutdown", (req, res) => { - res.status(200).send("Shutting down"); - setTimeout(function() { - shutdownManager.terminate(() => console.log("graceful shutdown finished.")); - }, 200); -}); - app.post("/tsfmt/format", (req, res) => { var format_data = req.body; tsfmt.processString("spotless-format-string.ts", format_data.file_content, format_data.config_options).then(resultMap => { From c70bd347f4b855035ddc466b0d7d5361ee77f415 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Mon, 28 Nov 2022 19:56:11 +0100 Subject: [PATCH 02/37] upgrade express to latest --- .../resources/com/diffplug/spotless/npm/prettier-package.json | 2 +- .../main/resources/com/diffplug/spotless/npm/tsfmt-package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-package.json b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-package.json index 113f20bc3f..7bda08db8a 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-package.json +++ b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-package.json @@ -9,7 +9,7 @@ }, "devDependencies": { ${devDependencies}, - "express": "4.17.1", + "express": "4.18.2", "@moebius/http-graceful-shutdown": "1.1.0" }, "dependencies": {}, diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-package.json b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-package.json index d6e5eff3b2..7037bd2ec1 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-package.json +++ b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-package.json @@ -9,7 +9,7 @@ }, "devDependencies": { ${devDependencies}, - "express": "4.17.1", + "express": "4.18.2", "@moebius/http-graceful-shutdown": "1.1.0" }, "dependencies": {}, From 3884e14d7ffd9a3c4fbfbe9bdeeb1172810d0b17 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Sat, 3 Dec 2022 19:50:03 +0100 Subject: [PATCH 03/37] extract common rest code on client side --- .../spotless/npm/BaseNpmRestService.java | 30 +++++++++++++++++++ .../spotless/npm/PrettierRestService.java | 13 ++------ .../spotless/npm/TsFmtRestService.java | 13 ++------ 3 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java new file mode 100644 index 0000000000..e8582c15ec --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +abstract class BaseNpmRestService { + + protected final SimpleRestClient restClient; + + BaseNpmRestService(String baseUrl) { + this.restClient = SimpleRestClient.forBaseUrl(baseUrl); + } + + public String shutdown() { + return restClient.post("/shutdown"); + } + +} diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java index ced08b013f..2a62823aa0 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,10 @@ import java.util.LinkedHashMap; import java.util.Map; -public class PrettierRestService { - - private final SimpleRestClient restClient; +public class PrettierRestService extends BaseNpmRestService { PrettierRestService(String baseUrl) { - this.restClient = SimpleRestClient.forBaseUrl(baseUrl); + super(baseUrl); } public String resolveConfig(File prettierConfigPath, Map prettierConfigOptions) { @@ -48,9 +46,4 @@ public String format(String fileContent, String configOptionsJsonString) { return restClient.postJson("/prettier/format", jsonProperties); } - - public String shutdown() { - return restClient.post("/shutdown"); - } - } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java index a47b608c36..f77510c3b6 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,10 @@ import java.util.LinkedHashMap; import java.util.Map; -public class TsFmtRestService { - - private final SimpleRestClient restClient; +public class TsFmtRestService extends BaseNpmRestService { TsFmtRestService(String baseUrl) { - this.restClient = SimpleRestClient.forBaseUrl(baseUrl); + super(baseUrl); } public String format(String fileContent, Map configOptions) { @@ -35,9 +33,4 @@ public String format(String fileContent, Map configOptions) { return restClient.postJson("/tsfmt/format", jsonProperties); } - - public String shutdown() { - return restClient.post("/shutdown"); - } - } From c20d3bb14adae3b90654b0531ac12a06d468185a Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Sat, 3 Dec 2022 19:53:24 +0100 Subject: [PATCH 04/37] add "eslint --fix" as formatter --- .../diffplug/spotless/npm/EslintConfig.java | 61 +++++ .../spotless/npm/EslintFormatterStep.java | 219 ++++++++++++++++++ .../spotless/npm/EslintRestService.java | 46 ++++ .../spotless/npm/NpmResourceHelper.java | 11 + .../diffplug/spotless/npm/eslint-package.json | 19 ++ .../com/diffplug/spotless/npm/eslint-serve.js | 66 ++++++ .../gradle/spotless/FormatExtension.java | 87 ++++++- .../gradle/spotless/TypescriptExtension.java | 56 ++++- .../spotless/TypescriptExtensionTest.java | 21 +- .../resources/npm/eslint/config/.eslintrc.js | 38 +++ .../eslint/config/typescript.configfile.clean | 15 ++ .../eslint/config/typescript.defaults.clean | 11 + .../npm/eslint/config/typescript.dirty | 10 + .../eslint/config/typescript.override.clean | 9 + .../eslint/filetypes/typescript/.eslintrc.js | 59 +++++ .../filetypes/typescript/typescript.clean | 11 + .../filetypes/typescript/typescript.dirty | 11 + 17 files changed, 737 insertions(+), 13 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java create mode 100644 lib/src/main/resources/com/diffplug/spotless/npm/eslint-package.json create mode 100644 lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js create mode 100644 testlib/src/main/resources/npm/eslint/config/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/config/typescript.configfile.clean create mode 100644 testlib/src/main/resources/npm/eslint/config/typescript.defaults.clean create mode 100644 testlib/src/main/resources/npm/eslint/config/typescript.dirty create mode 100644 testlib/src/main/resources/npm/eslint/config/typescript.override.clean create mode 100644 testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean create mode 100644 testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java new file mode 100644 index 0000000000..a6d41f9bc0 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.diffplug.spotless.FileSignature; +import com.diffplug.spotless.ThrowingEx; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +public class EslintConfig implements Serializable { + + private static final long serialVersionUID = -6196834313082791248L; + + @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") + @Nullable + private final transient File eslintConfigPath; + + @SuppressWarnings("unused") + private final FileSignature eslintConfigPathSignature; + + private final String eslintConfigJs; + + public EslintConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs) { + try { + this.eslintConfigPath = eslintConfigPath; + this.eslintConfigPathSignature = eslintConfigPath != null ? FileSignature.signAsList(this.eslintConfigPath) : FileSignature.signAsList(); + this.eslintConfigJs = eslintConfigJs; + } catch (IOException e) { + throw ThrowingEx.asRuntime(e); + } + } + + @Nullable + public File getEslintConfigPath() { + return eslintConfigPath; + } + + @Nullable + public String getEslintConfigJs() { + return eslintConfigJs; + } +} diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java new file mode 100644 index 0000000000..83d60a72c1 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -0,0 +1,219 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +import static java.util.Objects.requireNonNull; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +import javax.annotation.Nonnull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.diffplug.spotless.FormatterFunc; +import com.diffplug.spotless.FormatterFunc.Closeable; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.Provisioner; +import com.diffplug.spotless.ThrowingEx; +import com.diffplug.spotless.npm.EslintRestService.FormatOption; + +public class EslintFormatterStep { + + private static final Logger logger = LoggerFactory.getLogger(EslintFormatterStep.class); + + public static final String NAME = "eslint-format"; + + public static final String DEFAULT_ESLINT_VERSION = "8.28.0"; + + public enum PopularStyleGuide { + STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-standard-with-typescript", "23.0.0"); + dependencies.put("eslint-plugin-import", "2.26.0"); + dependencies.put("eslint-plugin-n", "15.5.1"); + dependencies.put("eslint-plugin-promise", "6.1.1"); + dependencies.put("typescript", "4.9.3"); + return dependencies; + } + }, + XO_TYPESCRIPT("xo-typescript") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-xo", "0.43.1"); + dependencies.put("eslint-config-xo-typescript", "0.55.1"); + dependencies.put("typescript", "4.9.3"); + return dependencies; + } + }; + + private final String popularStyleGuideName; + + PopularStyleGuide(String popularStyleGuideName) { + this.popularStyleGuideName = popularStyleGuideName; + } + + public String getPopularStyleGuideName() { + return popularStyleGuideName; + } + + public abstract Map devDependencies(); + + public static PopularStyleGuide fromNameOrNull(String popularStyleGuideName) { + for (PopularStyleGuide popularStyleGuide : PopularStyleGuide.values()) { + if (popularStyleGuide.popularStyleGuideName.equals(popularStyleGuideName)) { + return popularStyleGuide; + } + } + return null; + } + } + + public static Map defaultDevDependenciesForTypescript() { + return defaultDevDependenciesTypescriptWithEslint(DEFAULT_ESLINT_VERSION); + } + + public static Map defaultDevDependenciesTypescriptWithEslint(String eslintVersion) { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("@typescript-eslint/eslint-plugin", "5.45.0"); + dependencies.put("@typescript-eslint/parser", "5.45.0"); + dependencies.put("eslint", Objects.requireNonNull(eslintVersion)); + return dependencies; + } + + public static Map defaultDevDependencies() { + return defaultDevDependenciesWithEslint(DEFAULT_ESLINT_VERSION); + } + + public static Map defaultDevDependenciesWithEslint(String version) { + return Collections.singletonMap("eslint", version); + } + + public static FormatterStep create(Map devDependencies, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) { + requireNonNull(devDependencies); + requireNonNull(provisioner); + requireNonNull(buildDir); + return FormatterStep.createLazy(NAME, + () -> new State(NAME, devDependencies, buildDir, npmPathResolver, eslintConfig), + State::createFormatterFunc); + } + + private static class State extends NpmFormatterStepStateBase implements Serializable { + + private static final long serialVersionUID = -539537027004745812L; + private final EslintConfig eslintConfig; + + State(String stepName, Map devDependencies, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) throws IOException { + super(stepName, + new NpmConfig( + replaceDevDependencies( + NpmResourceHelper.readUtf8StringFromClasspath(EslintFormatterStep.class, "/com/diffplug/spotless/npm/eslint-package.json"), + new TreeMap<>(devDependencies)), + "eslint", + NpmResourceHelper.readUtf8StringFromClasspath(EslintFormatterStep.class, + "/com/diffplug/spotless/npm/common-serve.js", + "/com/diffplug/spotless/npm/eslint-serve.js"), + npmPathResolver.resolveNpmrcContent()), + buildDir, + npmPathResolver.resolveNpmExecutable()); + this.eslintConfig = localCopyFiles(requireNonNull(eslintConfig)); + } + + private EslintConfig localCopyFiles(EslintConfig orig) { + if (orig.getEslintConfigPath() == null) { + return orig; + } + // If a config file is provided, we need to make sure it is at the same location as the node modules + // as eslint will try to resolve plugin/config names relatively to the config file location + FormattedPrinter.SYSOUT.print("Copying config file <%s> to <%s> and using the copy", orig.getEslintConfigPath(), nodeModulesDir); + File configFileCopy = NpmResourceHelper.copyFileToDir(orig.getEslintConfigPath(), nodeModulesDir); + return new EslintConfig(configFileCopy, orig.getEslintConfigJs()); + } + + @Override + @Nonnull + public FormatterFunc createFormatterFunc() { + try { + FormattedPrinter.SYSOUT.print("creating formatter function (starting server)"); + ServerProcessInfo eslintRestServer = npmRunServer(); + EslintRestService restService = new EslintRestService(eslintRestServer.getBaseUrl()); + + // String prettierConfigOptions = restService.resolveConfig(this.prettierConfig.getPrettierConfigPath(), this.prettierConfig.getOptions()); + return Closeable.ofDangerous(() -> endServer(restService, eslintRestServer), new EslintFilePathPassingFormatterFunc(nodeModulesDir, eslintConfig, restService)); + } catch (IOException e) { + throw ThrowingEx.asRuntime(e); + } + } + + private void endServer(BaseNpmRestService restService, ServerProcessInfo restServer) throws Exception { + FormattedPrinter.SYSOUT.print("Closing formatting function (ending server)."); + try { + restService.shutdown(); + } catch (Throwable t) { + logger.info("Failed to request shutdown of rest service via api. Trying via process.", t); + } + restServer.close(); + } + + } + + private static class EslintFilePathPassingFormatterFunc implements FormatterFunc.NeedsFile { + private final File nodeModulesDir; + private final EslintConfig eslintConfig; + private final EslintRestService restService; + + public EslintFilePathPassingFormatterFunc(File nodeModulesDir, EslintConfig eslintConfig, EslintRestService restService) { + this.nodeModulesDir = nodeModulesDir; + this.eslintConfig = requireNonNull(eslintConfig); + this.restService = requireNonNull(restService); + } + + @Override + public String applyWithFile(String unix, File file) throws Exception { + FormattedPrinter.SYSOUT.print("formatting String '" + unix.substring(0, Math.min(50, unix.length())) + "[...]' in file '" + file + "'"); + + Map eslintCallOptions = new HashMap<>(); + setConfigToCallOptions(eslintCallOptions); + setFilePathToCallOptions(eslintCallOptions, file); + return restService.format(unix, eslintCallOptions); + } + + private void setFilePathToCallOptions(Map eslintCallOptions, File fileToBeFormatted) { + eslintCallOptions.put(FormatOption.FILE_PATH, fileToBeFormatted.getAbsolutePath()); + } + + private void setConfigToCallOptions(Map eslintCallOptions) { + if (eslintConfig.getEslintConfigPath() != null) { + eslintCallOptions.put(FormatOption.ESLINT_OVERRIDE_CONFIG_FILE, eslintConfig.getEslintConfigPath().getAbsolutePath()); + } + if (eslintConfig.getEslintConfigJs() != null) { + eslintCallOptions.put(FormatOption.ESLINT_OVERRIDE_CONFIG, eslintConfig.getEslintConfigJs()); + } + eslintCallOptions.put(FormatOption.NODE_MODULES_DIR, nodeModulesDir.getAbsolutePath()); + } + } +} diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java new file mode 100644 index 0000000000..541b77bab8 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class EslintRestService extends BaseNpmRestService { + + EslintRestService(String baseUrl) { + super(baseUrl); + } + + public String format(String fileContent, Map formatOptions) { + Map jsonProperties = new LinkedHashMap<>(); + jsonProperties.put("file_content", fileContent); + for (Entry option : formatOptions.entrySet()) { + jsonProperties.put(option.getKey().backendName, option.getValue()); + } + return restClient.postJson("/eslint/format", jsonProperties); + } + + enum FormatOption { + ESLINT_OVERRIDE_CONFIG("eslint_override_config"), ESLINT_OVERRIDE_CONFIG_FILE("eslint_override_config_file"), FILE_PATH("file_path"), NODE_MODULES_DIR("node_modules_dir"); + + private final String backendName; + + FormatOption(String backendName) { + this.backendName = backendName; + } + } +} diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java index cb6fff3290..4f97e7e732 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java @@ -18,6 +18,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.Arrays; import java.util.concurrent.TimeoutException; @@ -100,4 +101,14 @@ static void awaitReadableFile(File file, Duration maxWaitTime) throws TimeoutExc } } } + + static File copyFileToDir(File file, File targetDir) { + try { + File copiedFile = new File(targetDir, file.getName()); + Files.copy(file.toPath(), copiedFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + return copiedFile; + } catch (IOException e) { + throw ThrowingEx.asRuntime(e); + } + } } diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-package.json b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-package.json new file mode 100644 index 0000000000..dcd91e729d --- /dev/null +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-package.json @@ -0,0 +1,19 @@ +{ + "name": "spotless-eslint-formatter-step", + "version": "2.0.0", + "description": "Spotless formatter step for running eslint as a rest service.", + "repository": "https://github.com/diffplug/spotless", + "license": "Apache-2.0", + "scripts": { + "start": "node serve.js" + }, + "devDependencies": { +${devDependencies}, + "express": "4.18.2", + "@moebius/http-graceful-shutdown": "1.1.0" + }, + "dependencies": {}, + "engines": { + "node": ">=6" + } +} diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js new file mode 100644 index 0000000000..8c56b8ef08 --- /dev/null +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -0,0 +1,66 @@ +const {ESLint} = require("eslint"); + +app.post("/eslint/format", async (req, res) => { + + const format_data = req.body; + + const ESLintOverrideConfig = format_data.eslint_override_config; + + const ESLintOverrideConfigFile = format_data.eslint_override_config_file; + + if (!ESLintOverrideConfig && !ESLintOverrideConfigFile) { + res.status(501).send("Error while formatting: No config provided"); + return; + } + + const filePath = format_data.file_path; + + if (!filePath) { + res.status(501).send("Error while formatting: No file path provided"); + return; + } + + const ESLintOptions = { + fix: true, + useEslintrc: false, // would result in (gradle) cache issues + resolvePluginsRelativeTo: format_data.node_modules_dir + }; + + + if (ESLintOverrideConfigFile) { + ESLintOptions.overrideConfigFile = ESLintOverrideConfigFile; + } + if (ESLintOverrideConfig) { + ESLintOptions.overrideConfig = ESLintOverrideConfig; + } + + const eslint = new ESLint(ESLintOptions); + + + try { + console.log("using options: " + JSON.stringify(ESLintOptions)); + console.log("format input", format_data.file_content); + const lintTextOptions = { + filePath: filePath, + } + console.log("lintTextOptions", lintTextOptions); + // LintResult[] // https://eslint.org/docs/latest/developer-guide/nodejs-api#-lintresult-type + const results = await eslint.lintText(format_data.file_content, lintTextOptions); + if (results.length !== 1) { + res.status(501).send("Error while formatting: Unexpected number of results"); + return; + } + const result = results[0]; + console.log("result: " + JSON.stringify(result)); + if (result.fatalErrorCount && result.fatalErrorCount > 0) { + res.status(501).send("Fatal error while formatting: " + JSON.stringify(result.messages)); + return; + } + const formatted = result.output || result.source || format_data.file_content; + res.set("Content-Type", "text/plain"); + res.send(formatted); + } catch (err) { + console.log("error", err); + res.status(501).send("Error while formatting: " + err); + } +}); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index de0313eab4..99e21bd8c8 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -16,6 +16,7 @@ package com.diffplug.gradle.spotless; import static com.diffplug.gradle.spotless.PluginGradlePreconditions.requireElementsNonNull; +import static java.util.Objects.requireNonNull; import java.io.File; import java.io.Serializable; @@ -23,9 +24,9 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Random; import java.util.TreeMap; @@ -59,6 +60,8 @@ import com.diffplug.spotless.generic.ReplaceRegexStep; import com.diffplug.spotless.generic.ReplaceStep; import com.diffplug.spotless.generic.TrimTrailingWhitespaceStep; +import com.diffplug.spotless.npm.EslintConfig; +import com.diffplug.spotless.npm.EslintFormatterStep; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; @@ -71,7 +74,7 @@ public class FormatExtension { @Inject public FormatExtension(SpotlessExtension spotless) { - this.spotless = Objects.requireNonNull(spotless); + this.spotless = requireNonNull(spotless); } protected final Provisioner provisioner() { @@ -96,7 +99,7 @@ public LineEnding getLineEndings() { /** Sets the line endings to use (defaults to {@link SpotlessExtensionImpl#getLineEndings()}. */ public void setLineEndings(LineEnding lineEndings) { - this.lineEndings = Objects.requireNonNull(lineEndings); + this.lineEndings = requireNonNull(lineEndings); } Charset encoding; @@ -108,7 +111,7 @@ public Charset getEncoding() { /** Sets the encoding to use (defaults to {@link SpotlessExtensionImpl#getEncoding()}. */ public void setEncoding(String name) { - setEncoding(Charset.forName(Objects.requireNonNull(name))); + setEncoding(Charset.forName(requireNonNull(name))); } /** Sentinel to distinguish between "don't ratchet this format" and "use spotless parent format". */ @@ -136,19 +139,19 @@ public void ratchetFrom(String ratchetFrom) { /** Sets the encoding to use (defaults to {@link SpotlessExtensionImpl#getEncoding()}. */ public void setEncoding(Charset charset) { - encoding = Objects.requireNonNull(charset); + encoding = requireNonNull(charset); } final FormatExceptionPolicyStrict exceptionPolicy = new FormatExceptionPolicyStrict(); /** Ignores errors in the given step. */ public void ignoreErrorForStep(String stepName) { - exceptionPolicy.excludeStep(Objects.requireNonNull(stepName)); + exceptionPolicy.excludeStep(requireNonNull(stepName)); } /** Ignores errors for the given relative path. */ public void ignoreErrorForPath(String relativePath) { - exceptionPolicy.excludePath(Objects.requireNonNull(relativePath)); + exceptionPolicy.excludePath(requireNonNull(relativePath)); } /** Sets encoding to use (defaults to {@link SpotlessExtensionImpl#getEncoding()}). */ @@ -290,7 +293,7 @@ private static void relativizeIfSubdir(List relativePaths, File root, Fi /** Adds a new step. */ public void addStep(FormatterStep newStep) { - Objects.requireNonNull(newStep); + requireNonNull(newStep); int existingIdx = getExistingStepIdx(newStep.getName()); if (existingIdx != -1) { throw new GradleException("Multiple steps with name '" + newStep.getName() + "' for spotless format '" + formatName() + "'"); @@ -356,13 +359,13 @@ protected Integer calculateState() throws Exception { /** Adds a custom step. Receives a string with unix-newlines, must return a string with unix newlines. */ public void custom(String name, Closure formatter) { - Objects.requireNonNull(formatter, "formatter"); + requireNonNull(formatter, "formatter"); custom(name, formatter::call); } /** Adds a custom step. Receives a string with unix-newlines, must return a string with unix newlines. */ public void custom(String name, FormatterFunc formatter) { - Objects.requireNonNull(formatter, "formatter"); + requireNonNull(formatter, "formatter"); addStep(FormatterStep.createLazy(name, () -> globalState, unusedState -> formatter)); } @@ -560,7 +563,7 @@ public class PrettierConfig extends NpmStepConfig { final Map devDependencies; PrettierConfig(Map devDependencies) { - this.devDependencies = Objects.requireNonNull(devDependencies); + this.devDependencies = requireNonNull(devDependencies); } public PrettierConfig configFile(final Object prettierConfigFile) { @@ -605,6 +608,68 @@ public PrettierConfig prettier(Map devDependencies) { return prettierConfig; } + public class EslintFormatExtension extends NpmStepConfig { + + Map devDependencies = new LinkedHashMap<>(); + + @Nullable + Object configFilePath = null; + + @Nullable + String configJs = null; + + public EslintFormatExtension(Map devDependencies) { + this.devDependencies.putAll(requireNonNull(devDependencies)); + } + + public EslintFormatExtension devDependencies(Map devDependencies) { + this.devDependencies.putAll(devDependencies); + replaceStep(createStep()); + return this; + } + + public EslintFormatExtension configJs(String configJs) { + this.configJs = requireNonNull(configJs); + replaceStep(createStep()); + return this; + } + + public EslintFormatExtension configFile(Object configFilePath) { + this.configFilePath = requireNonNull(configFilePath); + replaceStep(createStep()); + return this; + } + + public FormatterStep createStep() { + final Project project = getProject(); + + return EslintFormatterStep.create( + devDependencies, + provisioner(), + project.getBuildDir(), + new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), + eslintConfig()); + } + + private EslintConfig eslintConfig() { + return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); + } + } + + public EslintFormatExtension eslint() { + return eslint(EslintFormatterStep.defaultDevDependencies()); + } + + public EslintFormatExtension eslint(String version) { + return eslint(EslintFormatterStep.defaultDevDependenciesWithEslint(version)); + } + + public EslintFormatExtension eslint(Map devDependencies) { + EslintFormatExtension eslint = new EslintFormatExtension(devDependencies); + addStep(eslint.createStep()); + return eslint; + } + /** Uses the default version of clang-format. */ public ClangFormatConfig clangFormat() { return clangFormat(ClangFormatStep.defaultVersion()); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index 58b9d4acbb..e80730d5ed 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,12 @@ import static java.util.Objects.requireNonNull; +import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; @@ -28,6 +30,8 @@ import org.gradle.api.Project; import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; import com.diffplug.spotless.npm.TsConfigFileType; @@ -169,6 +173,56 @@ private void fixParserToTypescript() { } } + @Override + public TypescriptEslintFormatExtension eslint() { + return eslint(EslintFormatterStep.defaultDevDependenciesForTypescript()); + } + + @Override + public TypescriptEslintFormatExtension eslint(String version) { + return eslint(EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(version)); + } + + @Override + public TypescriptEslintFormatExtension eslint(Map devDependencies) { + TypescriptEslintFormatExtension eslint = new TypescriptEslintFormatExtension(devDependencies); + addStep(eslint.createStep()); + return eslint; + } + + public class TypescriptEslintFormatExtension extends EslintFormatExtension { + + public TypescriptEslintFormatExtension(Map devDependencies) { + super(devDependencies); + } + + @Override + public TypescriptEslintFormatExtension devDependencies(Map devDependencies) { + return (TypescriptEslintFormatExtension) super.devDependencies(devDependencies); + } + + @Override + public TypescriptEslintFormatExtension configJs(String configJs) { + return (TypescriptEslintFormatExtension) super.configJs(configJs); + } + + @Override + public TypescriptEslintFormatExtension configFile(Object configFilePath) { + return (TypescriptEslintFormatExtension) super.configFile(configFilePath); + } + + public TypescriptEslintFormatExtension styleGuide(String styleGuide) { + PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide); + if (popularStyleGuide == null) { + throw new IllegalArgumentException("Unknown style guide: " + styleGuide + ". Known style guides: " + + Arrays.stream(PopularStyleGuide.values()).map(PopularStyleGuide::getPopularStyleGuideName).collect(Collectors.joining(", "))); + } + devDependencies(popularStyleGuide.devDependencies()); + replaceStep(createStep()); + return this; + } + } + @Override protected void setupTask(SpotlessTask task) { // defaults to all typescript files diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index 100c4eb3b0..40da7e3536 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,4 +141,23 @@ void usePrettier() throws IOException { gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); assertFile("test.ts").sameAsResource("npm/prettier/filetypes/typescript/typescript.clean"); } + + @Test + void useEslint() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/filetypes/typescript/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " typescript {", + " target 'test.ts'", + " eslint().configFile('.eslintrc.js')", + " }", + "}"); + setFile("test.ts").toResource("npm/eslint/filetypes/typescript/typescript.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.ts").sameAsResource("npm/eslint/filetypes/typescript/typescript.clean"); + } } diff --git a/testlib/src/main/resources/npm/eslint/config/.eslintrc.js b/testlib/src/main/resources/npm/eslint/config/.eslintrc.js new file mode 100644 index 0000000000..6cb3070512 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/config/.eslintrc.js @@ -0,0 +1,38 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [ + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +}; diff --git a/testlib/src/main/resources/npm/eslint/config/typescript.configfile.clean b/testlib/src/main/resources/npm/eslint/config/typescript.configfile.clean new file mode 100644 index 0000000000..0ec76369be --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/config/typescript.configfile.clean @@ -0,0 +1,15 @@ +export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary + extends AbstractController + implements DisposeAware, CallbackAware { + public myValue: string[]; + + constructor( + private myService: Service, + name: string, + private field: any + ) { + super(name); + } + + //... +} diff --git a/testlib/src/main/resources/npm/eslint/config/typescript.defaults.clean b/testlib/src/main/resources/npm/eslint/config/typescript.defaults.clean new file mode 100644 index 0000000000..0155b905bd --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/config/typescript.defaults.clean @@ -0,0 +1,11 @@ +export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary + extends AbstractController + implements DisposeAware, CallbackAware { + public myValue: string[]; + + constructor(private myService: Service, name: string, private field: any) { + super(name); + } + + //... +} diff --git a/testlib/src/main/resources/npm/eslint/config/typescript.dirty b/testlib/src/main/resources/npm/eslint/config/typescript.dirty new file mode 100644 index 0000000000..a3a30bf49a --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/config/typescript.dirty @@ -0,0 +1,10 @@ +export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary extends AbstractController implements DisposeAware, CallbackAware { + + +public myValue:string[]; + +constructor(private myService:Service,name:string,private field:any){ super(name) ;} + + +//... +} diff --git a/testlib/src/main/resources/npm/eslint/config/typescript.override.clean b/testlib/src/main/resources/npm/eslint/config/typescript.override.clean new file mode 100644 index 0000000000..3f3a8c30af --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/config/typescript.override.clean @@ -0,0 +1,9 @@ +export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary extends AbstractController implements DisposeAware, CallbackAware { + public myValue: string[]; + + constructor(private myService: Service, name: string, private field: any) { + super(name); + } + + //... +} diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js b/testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js new file mode 100644 index 0000000000..e6ddff7d4c --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js @@ -0,0 +1,59 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [ + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ], + "curly": [ + "error" + ], + "max-statements-per-line": [ + "error", + { "max": 1 } + ], + "object-curly-newline": [ + "error", + "always" + ], + "comma-spacing": [ + "error", + { "before": false, "after": true } + ], + "object-property-newline": [ + "error", + ], + "no-trailing-spaces": [ + "error" + ], + } +}; diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean b/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean new file mode 100644 index 0000000000..526519cae9 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean @@ -0,0 +1,11 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + + + public myValue:string[]; + + constructor(private myService:Service, name:string, private field:any){ super(name) ;} + + + //... + +} diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty b/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty new file mode 100644 index 0000000000..0a9201c2e7 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty @@ -0,0 +1,11 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + + +public myValue:string[]; + +constructor(private myService:Service,name:string,private field:any){ super(name) ;} + + +//... + +} From 4f5e4d343d08831104abd7faec86d8ff820b3125 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 7 Dec 2022 20:01:32 +0100 Subject: [PATCH 05/37] eslint: add support for xo standard typescript ruleset --- .../diffplug/spotless/npm/EslintConfig.java | 19 ++++++++- .../spotless/npm/EslintFormatterStep.java | 42 ++++++++++++------- .../spotless/npm/EslintRestService.java | 2 +- .../npm/NpmFormatterStepStateBase.java | 6 ++- .../spotless/npm/NpmResourceHelper.java | 17 ++++++-- .../spotless/npm/PrettierFormatterStep.java | 7 ++-- .../spotless/npm/TsFmtFormatterStep.java | 7 ++-- .../com/diffplug/spotless/npm/eslint-serve.js | 14 +++++++ .../gradle/spotless/FormatExtension.java | 32 +++++++++++++- .../gradle/spotless/TypescriptExtension.java | 11 +++++ .../spotless/TypescriptExtensionTest.java | 26 ++++++++++-- .../custom_rules}/.eslintrc.js | 0 .../custom_rules}/typescript.clean | 0 .../custom_rules}/typescript.dirty | 0 .../typescript/standard_rules_xo/.eslintrc.js | 26 ++++++++++++ .../standard_rules_xo/tsconfig.json | 18 ++++++++ .../standard_rules_xo/typescript.clean | 9 ++++ .../standard_rules_xo/typescript.dirty | 10 +++++ 18 files changed, 216 insertions(+), 30 deletions(-) rename testlib/src/main/resources/npm/eslint/{filetypes/typescript => typescript/custom_rules}/.eslintrc.js (100%) rename testlib/src/main/resources/npm/eslint/{filetypes/typescript => typescript/custom_rules}/typescript.clean (100%) rename testlib/src/main/resources/npm/eslint/{filetypes/typescript => typescript/custom_rules}/typescript.dirty (100%) create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java index a6d41f9bc0..83f5ab1a73 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java @@ -18,7 +18,12 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.diffplug.spotless.FileSignature; @@ -39,11 +44,18 @@ public class EslintConfig implements Serializable { private final String eslintConfigJs; - public EslintConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs) { + @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") + private final transient Map> additionalConfigFiles; // key: source-file, value: target-remapping path relative to package.json (if needed) + + private final FileSignature additionalConfigFilesSignature; + + public EslintConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs, Map> additionalConfigFiles) { try { this.eslintConfigPath = eslintConfigPath; this.eslintConfigPathSignature = eslintConfigPath != null ? FileSignature.signAsList(this.eslintConfigPath) : FileSignature.signAsList(); this.eslintConfigJs = eslintConfigJs; + this.additionalConfigFiles = additionalConfigFiles != null ? new TreeMap<>(additionalConfigFiles) : Collections.emptyMap(); + this.additionalConfigFilesSignature = FileSignature.signAsList(this.additionalConfigFiles.keySet().toArray(new File[0])); } catch (IOException e) { throw ThrowingEx.asRuntime(e); } @@ -58,4 +70,9 @@ public File getEslintConfigPath() { public String getEslintConfigJs() { return eslintConfigJs; } + + @Nonnull + public Map> getAdditionalConfigFiles() { + return additionalConfigFiles; + } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 83d60a72c1..492cf0da90 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.TreeMap; import javax.annotation.Nonnull; @@ -113,12 +114,13 @@ public static Map defaultDevDependenciesWithEslint(String versio return Collections.singletonMap("eslint", version); } - public static FormatterStep create(Map devDependencies, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) { + public static FormatterStep create(Map devDependencies, Provisioner provisioner, File projectDir, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) { requireNonNull(devDependencies); requireNonNull(provisioner); + requireNonNull(projectDir); requireNonNull(buildDir); return FormatterStep.createLazy(NAME, - () -> new State(NAME, devDependencies, buildDir, npmPathResolver, eslintConfig), + () -> new State(NAME, devDependencies, projectDir, buildDir, npmPathResolver, eslintConfig), State::createFormatterFunc); } @@ -127,7 +129,7 @@ private static class State extends NpmFormatterStepStateBase implements Serializ private static final long serialVersionUID = -539537027004745812L; private final EslintConfig eslintConfig; - State(String stepName, Map devDependencies, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) throws IOException { + State(String stepName, Map devDependencies, File projectDir, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) throws IOException { super(stepName, new NpmConfig( replaceDevDependencies( @@ -138,20 +140,29 @@ private static class State extends NpmFormatterStepStateBase implements Serializ "/com/diffplug/spotless/npm/common-serve.js", "/com/diffplug/spotless/npm/eslint-serve.js"), npmPathResolver.resolveNpmrcContent()), + projectDir, buildDir, npmPathResolver.resolveNpmExecutable()); this.eslintConfig = localCopyFiles(requireNonNull(eslintConfig)); } private EslintConfig localCopyFiles(EslintConfig orig) { - if (orig.getEslintConfigPath() == null) { - return orig; - } - // If a config file is provided, we need to make sure it is at the same location as the node modules - // as eslint will try to resolve plugin/config names relatively to the config file location + // If any config files are provided, we need to make sure they are at the same location as the node modules + // as eslint will try to resolve plugin/config names relatively to the config file location and some + // eslint configs contain relative paths to additional config files (such as tsconfig.json e.g.) FormattedPrinter.SYSOUT.print("Copying config file <%s> to <%s> and using the copy", orig.getEslintConfigPath(), nodeModulesDir); File configFileCopy = NpmResourceHelper.copyFileToDir(orig.getEslintConfigPath(), nodeModulesDir); - return new EslintConfig(configFileCopy, orig.getEslintConfigJs()); + + for (Map.Entry> additionalConfigFile : orig.getAdditionalConfigFiles().entrySet()) { + FormattedPrinter.SYSOUT.print("Copying additional config file <%s> to <%s> at subpath <%s> and using the copy", additionalConfigFile.getKey(), nodeModulesDir, additionalConfigFile.getValue()); + + if (additionalConfigFile.getValue().isPresent()) { + NpmResourceHelper.copyFileToDirAtSubpath(additionalConfigFile.getKey(), nodeModulesDir, additionalConfigFile.getValue().get()); + } else { + NpmResourceHelper.copyFileToDir(additionalConfigFile.getKey(), nodeModulesDir); + } + } + return new EslintConfig(configFileCopy, orig.getEslintConfigJs(), orig.getAdditionalConfigFiles()); } @Override @@ -161,9 +172,7 @@ public FormatterFunc createFormatterFunc() { FormattedPrinter.SYSOUT.print("creating formatter function (starting server)"); ServerProcessInfo eslintRestServer = npmRunServer(); EslintRestService restService = new EslintRestService(eslintRestServer.getBaseUrl()); - - // String prettierConfigOptions = restService.resolveConfig(this.prettierConfig.getPrettierConfigPath(), this.prettierConfig.getOptions()); - return Closeable.ofDangerous(() -> endServer(restService, eslintRestServer), new EslintFilePathPassingFormatterFunc(nodeModulesDir, eslintConfig, restService)); + return Closeable.ofDangerous(() -> endServer(restService, eslintRestServer), new EslintFilePathPassingFormatterFunc(projectDir, nodeModulesDir, eslintConfig, restService)); } catch (IOException e) { throw ThrowingEx.asRuntime(e); } @@ -182,12 +191,14 @@ private void endServer(BaseNpmRestService restService, ServerProcessInfo restSer } private static class EslintFilePathPassingFormatterFunc implements FormatterFunc.NeedsFile { + private final File projectDir; private final File nodeModulesDir; private final EslintConfig eslintConfig; private final EslintRestService restService; - public EslintFilePathPassingFormatterFunc(File nodeModulesDir, EslintConfig eslintConfig, EslintRestService restService) { - this.nodeModulesDir = nodeModulesDir; + public EslintFilePathPassingFormatterFunc(File projectDir, File nodeModulesDir, EslintConfig eslintConfig, EslintRestService restService) { + this.projectDir = requireNonNull(projectDir); + this.nodeModulesDir = requireNonNull(nodeModulesDir); this.eslintConfig = requireNonNull(eslintConfig); this.restService = requireNonNull(restService); } @@ -214,6 +225,9 @@ private void setConfigToCallOptions(Map eslintCallOptions) eslintCallOptions.put(FormatOption.ESLINT_OVERRIDE_CONFIG, eslintConfig.getEslintConfigJs()); } eslintCallOptions.put(FormatOption.NODE_MODULES_DIR, nodeModulesDir.getAbsolutePath()); + + // TODO (simschla, 09.12.22): maybe only add this if there is a typescript config active? (TBD: how to detect) + eslintCallOptions.put(FormatOption.TS_CONFIG_ROOT_DIR, nodeModulesDir.toPath().relativize(projectDir.toPath()).toString()); } } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java index 541b77bab8..d3a01621f1 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java @@ -35,7 +35,7 @@ public String format(String fileContent, Map formatOptions } enum FormatOption { - ESLINT_OVERRIDE_CONFIG("eslint_override_config"), ESLINT_OVERRIDE_CONFIG_FILE("eslint_override_config_file"), FILE_PATH("file_path"), NODE_MODULES_DIR("node_modules_dir"); + ESLINT_OVERRIDE_CONFIG("eslint_override_config"), ESLINT_OVERRIDE_CONFIG_FILE("eslint_override_config_file"), FILE_PATH("file_path"), NODE_MODULES_DIR("node_modules_dir"), TS_CONFIG_ROOT_DIR("ts_config_root_dir"); private final String backendName; diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java index 2e8d80e471..fc1543fd61 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java @@ -51,13 +51,17 @@ abstract class NpmFormatterStepStateBase implements Serializable { @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") private final transient File npmExecutable; + @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") + public final transient File projectDir; + private final NpmConfig npmConfig; private final String stepName; - protected NpmFormatterStepStateBase(String stepName, NpmConfig npmConfig, File buildDir, File npm) throws IOException { + protected NpmFormatterStepStateBase(String stepName, NpmConfig npmConfig, File projectDir, File buildDir, File npm) throws IOException { this.stepName = requireNonNull(stepName); this.npmConfig = requireNonNull(npmConfig); + this.projectDir = requireNonNull(projectDir); this.npmExecutable = npm; NodeServerLayout layout = prepareNodeServer(buildDir); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java index 4f97e7e732..48e974eec6 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java @@ -18,9 +18,12 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; @@ -103,10 +106,18 @@ static void awaitReadableFile(File file, Duration maxWaitTime) throws TimeoutExc } static File copyFileToDir(File file, File targetDir) { + return copyFileToDirAtSubpath(file, targetDir, file.getName()); + } + + static File copyFileToDirAtSubpath(File file, File targetDir, String relativePath) { + Objects.requireNonNull(relativePath); try { - File copiedFile = new File(targetDir, file.getName()); - Files.copy(file.toPath(), copiedFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - return copiedFile; + // create file pointing to relativePath in targetDir + final Path relativeTargetFile = Paths.get(targetDir.getAbsolutePath(), relativePath); + assertDirectoryExists(relativeTargetFile.getParent().toFile()); + + Files.copy(file.toPath(), relativeTargetFile, StandardCopyOption.REPLACE_EXISTING); + return relativeTargetFile.toFile(); } catch (IOException e) { throw ThrowingEx.asRuntime(e); } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java index 5a5662eee4..8489644c36 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java @@ -49,12 +49,12 @@ public static final Map defaultDevDependenciesWithPrettier(Strin return Collections.singletonMap("prettier", version); } - public static FormatterStep create(Map devDependencies, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) { + public static FormatterStep create(Map devDependencies, Provisioner provisioner, File projectDir, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) { requireNonNull(devDependencies); requireNonNull(provisioner); requireNonNull(buildDir); return FormatterStep.createLazy(NAME, - () -> new State(NAME, devDependencies, buildDir, npmPathResolver, prettierConfig), + () -> new State(NAME, devDependencies, projectDir, buildDir, npmPathResolver, prettierConfig), State::createFormatterFunc); } @@ -63,7 +63,7 @@ private static class State extends NpmFormatterStepStateBase implements Serializ private static final long serialVersionUID = -539537027004745812L; private final PrettierConfig prettierConfig; - State(String stepName, Map devDependencies, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) throws IOException { + State(String stepName, Map devDependencies, File projectDir, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) throws IOException { super(stepName, new NpmConfig( replaceDevDependencies( @@ -74,6 +74,7 @@ private static class State extends NpmFormatterStepStateBase implements Serializ "/com/diffplug/spotless/npm/common-serve.js", "/com/diffplug/spotless/npm/prettier-serve.js"), npmPathResolver.resolveNpmrcContent()), + projectDir, buildDir, npmPathResolver.resolveNpmExecutable()); this.prettierConfig = requireNonNull(prettierConfig); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java index e9c098d709..027af89e23 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java @@ -40,11 +40,11 @@ public class TsFmtFormatterStep { public static final String NAME = "tsfmt-format"; - public static FormatterStep create(Map versions, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) { + public static FormatterStep create(Map versions, Provisioner provisioner, File projectDir, File buildDir, NpmPathResolver npmPathResolver, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) { requireNonNull(provisioner); requireNonNull(buildDir); return FormatterStep.createLazy(NAME, - () -> new State(NAME, versions, buildDir, npmPathResolver, configFile, inlineTsFmtSettings), + () -> new State(NAME, versions, projectDir, buildDir, npmPathResolver, configFile, inlineTsFmtSettings), State::createFormatterFunc); } @@ -71,7 +71,7 @@ public static class State extends NpmFormatterStepStateBase implements Serializa @Nullable private final TypedTsFmtConfigFile configFile; - public State(String stepName, Map versions, File buildDir, NpmPathResolver npmPathResolver, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) throws IOException { + public State(String stepName, Map versions, File projectDir, File buildDir, NpmPathResolver npmPathResolver, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) throws IOException { super(stepName, new NpmConfig( replaceDevDependencies(NpmResourceHelper.readUtf8StringFromClasspath(TsFmtFormatterStep.class, "/com/diffplug/spotless/npm/tsfmt-package.json"), new TreeMap<>(versions)), @@ -80,6 +80,7 @@ public State(String stepName, Map versions, File buildDir, NpmPa "/com/diffplug/spotless/npm/common-serve.js", "/com/diffplug/spotless/npm/tsfmt-serve.js"), npmPathResolver.resolveNpmrcContent()), + projectDir, buildDir, npmPathResolver.resolveNpmExecutable()); this.buildDir = requireNonNull(buildDir); diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index 8c56b8ef08..15018e6cb0 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -24,8 +24,22 @@ app.post("/eslint/format", async (req, res) => { fix: true, useEslintrc: false, // would result in (gradle) cache issues resolvePluginsRelativeTo: format_data.node_modules_dir + // baseConfig: { + // parserOptions: { + // tsconfigRootDir: '../../', + // } + // } }; + if (format_data.ts_config_root_dir) { + ESLintOptions.baseConfig = { + parserOptions: { + tsconfigRootDir: format_data.ts_config_root_dir + } + }; + // res.status(501).send("Resolved ts config root dir: " + format_data.ts_config_root_dir); + } + if (ESLintOverrideConfigFile) { ESLintOptions.overrideConfigFile = ESLintOverrideConfigFile; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 99e21bd8c8..3f433afa92 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -27,8 +27,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.TreeMap; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; @@ -583,6 +585,7 @@ FormatterStep createStep() { return PrettierFormatterStep.create( devDependencies, provisioner(), + project.getProjectDir(), project.getBuildDir(), new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), new com.diffplug.spotless.npm.PrettierConfig( @@ -618,6 +621,8 @@ public class EslintFormatExtension extends NpmStepConfig @Nullable String configJs = null; + List additionalConfigs = new ArrayList<>(); + public EslintFormatExtension(Map devDependencies) { this.devDependencies.putAll(requireNonNull(devDependencies)); } @@ -640,19 +645,44 @@ public EslintFormatExtension configFile(Object configFilePath) { return this; } + protected void additionalConfigFilePath(Object sourceFile, String relativeTargetPath) { + this.additionalConfigs.add(new AdditionalEslintConfig(sourceFile, relativeTargetPath)); + } + public FormatterStep createStep() { final Project project = getProject(); return EslintFormatterStep.create( devDependencies, provisioner(), + project.getProjectDir(), project.getBuildDir(), new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), eslintConfig()); } private EslintConfig eslintConfig() { - return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); + return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs, additionalConfigs()); + } + + private Map> additionalConfigs() { + // convert additionalConfigs to a map explicitly allowing null values + + return additionalConfigs.stream() + .collect(Collectors.toMap( + config -> getProject().file(config.configFilePath), + config -> Optional.ofNullable(config.relativeTargetPath))); + } + } + + private static class AdditionalEslintConfig { + final Object configFilePath; + + final String relativeTargetPath; + + AdditionalEslintConfig(Object configFilePath, String relativeTargetPath) { + this.configFilePath = configFilePath; + this.relativeTargetPath = relativeTargetPath; } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index e80730d5ed..b126cc76c6 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -113,6 +113,7 @@ public FormatterStep createStep() { return TsFmtFormatterStep.create( devDependencies, provisioner(), + project.getProjectDir(), project.getBuildDir(), new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), typedConfigFile(), @@ -221,6 +222,16 @@ public TypescriptEslintFormatExtension styleGuide(String styleGuide) { replaceStep(createStep()); return this; } + + public TypescriptEslintFormatExtension tsconfigFile(Object path) { + return tsconfigFile(path, null); + } + + public TypescriptEslintFormatExtension tsconfigFile(Object path, String remapping) { + additionalConfigFilePath(path, remapping); + replaceStep(createStep()); + return this; + } } @Override diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index 40da7e3536..e7086e0c91 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -144,7 +144,7 @@ void usePrettier() throws IOException { @Test void useEslint() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/filetypes/typescript/.eslintrc.js"); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/custom_rules/.eslintrc.js"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -156,8 +156,28 @@ void useEslint() throws IOException { " eslint().configFile('.eslintrc.js')", " }", "}"); - setFile("test.ts").toResource("npm/eslint/filetypes/typescript/typescript.dirty"); + setFile("test.ts").toResource("npm/eslint/typescript/custom_rules/typescript.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); - assertFile("test.ts").sameAsResource("npm/eslint/filetypes/typescript/typescript.clean"); + assertFile("test.ts").sameAsResource("npm/eslint/typescript/custom_rules/typescript.clean"); + } + + @Test + void useXoStandardRules() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/typescript/standard_rules_xo/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_xo/tsconfig.json"); // needs to be copied to! + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " typescript {", + " target 'test.ts'", + " eslint().styleGuide('xo-typescript').configFile('.eslintrc.js')//.tsconfigFile('tsconfig.json')", // TODO TODO maybe can skip the additional config files alltogether, instead provide tsConfigRootDir to eslint-serve.js via call + " }", + "}"); + setFile("test.ts").toResource("npm/eslint/typescript/standard_rules_xo/typescript.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.ts").sameAsResource("npm/eslint/typescript/standard_rules_xo/typescript.clean"); } } diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js b/testlib/src/main/resources/npm/eslint/typescript/custom_rules/.eslintrc.js similarity index 100% rename from testlib/src/main/resources/npm/eslint/filetypes/typescript/.eslintrc.js rename to testlib/src/main/resources/npm/eslint/typescript/custom_rules/.eslintrc.js diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean b/testlib/src/main/resources/npm/eslint/typescript/custom_rules/typescript.clean similarity index 100% rename from testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.clean rename to testlib/src/main/resources/npm/eslint/typescript/custom_rules/typescript.clean diff --git a/testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty b/testlib/src/main/resources/npm/eslint/typescript/custom_rules/typescript.dirty similarity index 100% rename from testlib/src/main/resources/npm/eslint/filetypes/typescript/typescript.dirty rename to testlib/src/main/resources/npm/eslint/typescript/custom_rules/typescript.dirty diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js new file mode 100644 index 0000000000..e1ca03f732 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: 'xo/browser', + overrides: [ + { + extends: [ + 'xo-typescript', + ], + files: [ + '*.ts', + '*.tsx', + ], + }, + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + }, + rules: { + }, +}; diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json new file mode 100644 index 0000000000..629f834eb5 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "ES6", + "noImplicitAny": true, + "outDir": "build/ide/", + "sourceMap": true, + "skipLibCheck": true, + "target": "ES6", + "baseUrl": "./", + "paths": { + }, + "lib": ["es7", "dom", "es2017"] + }, + "include": [ + "**/*" + ] +} diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean new file mode 100644 index 0000000000..5c43d7a746 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean @@ -0,0 +1,9 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + public myValue: string[]; + + constructor(private readonly myService: Service, name: string, private readonly field: any) { + super(name); + } + + // ... +} diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty new file mode 100644 index 0000000000..a8f7447af1 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty @@ -0,0 +1,10 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + +public myValue:string[]; + +constructor(private myService:Service,name:string,private field:any){ super(name) ;} + + +//... + +} From 96339675402e79f0737f70733b2224cba4345c2e Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 9 Dec 2022 21:10:40 +0100 Subject: [PATCH 06/37] eslint: add support for standard-with-typescript ruleset --- .../com/diffplug/spotless/npm/eslint-serve.js | 71 +++++++++---------- .../spotless/TypescriptExtensionTest.java | 24 ++++++- .../.eslintrc.js | 16 +++++ .../tsconfig.json | 18 +++++ .../typescript.clean | 7 ++ .../typescript.dirty | 10 +++ 6 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean create mode 100644 testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index 15018e6cb0..2804eb86dd 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -1,57 +1,50 @@ const {ESLint} = require("eslint"); app.post("/eslint/format", async (req, res) => { + try { + const format_data = req.body; - const format_data = req.body; - - const ESLintOverrideConfig = format_data.eslint_override_config; + const ESLintOverrideConfig = format_data.eslint_override_config; - const ESLintOverrideConfigFile = format_data.eslint_override_config_file; + const ESLintOverrideConfigFile = format_data.eslint_override_config_file; - if (!ESLintOverrideConfig && !ESLintOverrideConfigFile) { - res.status(501).send("Error while formatting: No config provided"); - return; - } - - const filePath = format_data.file_path; + if (!ESLintOverrideConfig && !ESLintOverrideConfigFile) { + res.status(501).send("Error while formatting: No config provided"); + return; + } - if (!filePath) { - res.status(501).send("Error while formatting: No file path provided"); - return; - } + const filePath = format_data.file_path; - const ESLintOptions = { - fix: true, - useEslintrc: false, // would result in (gradle) cache issues - resolvePluginsRelativeTo: format_data.node_modules_dir - // baseConfig: { - // parserOptions: { - // tsconfigRootDir: '../../', - // } - // } - }; + if (!filePath) { + res.status(501).send("Error while formatting: No file path provided"); + return; + } - if (format_data.ts_config_root_dir) { - ESLintOptions.baseConfig = { - parserOptions: { - tsconfigRootDir: format_data.ts_config_root_dir - } + const ESLintOptions = { + fix: true, + useEslintrc: false, // would result in (gradle) cache issues + resolvePluginsRelativeTo: format_data.node_modules_dir }; - // res.status(501).send("Resolved ts config root dir: " + format_data.ts_config_root_dir); - } + if (format_data.ts_config_root_dir) { + ESLintOptions.baseConfig = { + parserOptions: { + tsconfigRootDir: format_data.ts_config_root_dir + } + }; + } - if (ESLintOverrideConfigFile) { - ESLintOptions.overrideConfigFile = ESLintOverrideConfigFile; - } - if (ESLintOverrideConfig) { - ESLintOptions.overrideConfig = ESLintOverrideConfig; - } - const eslint = new ESLint(ESLintOptions); + if (ESLintOverrideConfigFile) { + ESLintOptions.overrideConfigFile = ESLintOverrideConfigFile; + } + if (ESLintOverrideConfig) { + ESLintOptions.overrideConfig = ESLintOverrideConfig; + } + + const eslint = new ESLint(ESLintOptions); - try { console.log("using options: " + JSON.stringify(ESLintOptions)); console.log("format input", format_data.file_content); const lintTextOptions = { diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index e7086e0c91..0c3d10c89f 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -164,7 +164,7 @@ void useEslint() throws IOException { @Test void useXoStandardRules() throws IOException { setFile(".eslintrc.js").toResource("npm/eslint/typescript/standard_rules_xo/.eslintrc.js"); - setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_xo/tsconfig.json"); // needs to be copied to! + setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_xo/tsconfig.json"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -173,11 +173,31 @@ void useXoStandardRules() throws IOException { "spotless {", " typescript {", " target 'test.ts'", - " eslint().styleGuide('xo-typescript').configFile('.eslintrc.js')//.tsconfigFile('tsconfig.json')", // TODO TODO maybe can skip the additional config files alltogether, instead provide tsConfigRootDir to eslint-serve.js via call + " eslint().styleGuide('xo-typescript').configFile('.eslintrc.js')", " }", "}"); setFile("test.ts").toResource("npm/eslint/typescript/standard_rules_xo/typescript.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); assertFile("test.ts").sameAsResource("npm/eslint/typescript/standard_rules_xo/typescript.clean"); } + + @Test + void useStandardWithTypescriptRules() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " typescript {", + " target 'test.ts'", + " eslint().styleGuide('standard-with-typescript').configFile('.eslintrc.js')", + " }", + "}"); + setFile("test.ts").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.ts").sameAsResource("npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean"); + } } diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js new file mode 100644 index 0000000000..0a86d5db86 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: 'standard-with-typescript', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + }, + rules: { + } +} diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json new file mode 100644 index 0000000000..629f834eb5 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "ES6", + "noImplicitAny": true, + "outDir": "build/ide/", + "sourceMap": true, + "skipLibCheck": true, + "target": "ES6", + "baseUrl": "./", + "paths": { + }, + "lib": ["es7", "dom", "es2017"] + }, + "include": [ + "**/*" + ] +} diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean new file mode 100644 index 0000000000..cbe609b1bb --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean @@ -0,0 +1,7 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + public myValue: string[] + + constructor (private readonly myService: Service, name: string, private readonly field: any) { super(name) } + + // ... +} diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty new file mode 100644 index 0000000000..a8f7447af1 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty @@ -0,0 +1,10 @@ +export class MyController extends AbstractController implements DisposeAware, CallbackAware { + +public myValue:string[]; + +constructor(private myService:Service,name:string,private field:any){ super(name) ;} + + +//... + +} From 7022b7fa3b9337474951defff106746c280f4a58 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 15 Dec 2022 19:53:59 +0100 Subject: [PATCH 07/37] eslint: add unit tests, cleanup code --- .../diffplug/spotless/npm/EslintConfig.java | 23 +- .../spotless/npm/EslintFormatterStep.java | 21 +- .../spotless/npm/EslintTypescriptConfig.java | 41 ++++ .../gradle/spotless/FormatExtension.java | 30 +-- .../gradle/spotless/TypescriptExtension.java | 19 +- .../diffplug/spotless/ResourceHarness.java | 18 +- .../spotless/npm/EslintFormatterStepTest.java | 210 ++++++++++++++++++ .../npm/NpmFormatterStepCommonTests.java | 9 + .../npm/PrettierFormatterStepTest.java | 5 + .../spotless/npm/TsFmtFormatterStepTest.java | 2 + 10 files changed, 309 insertions(+), 69 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java create mode 100644 testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java index 83f5ab1a73..732111cc71 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java @@ -18,12 +18,7 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.TreeMap; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.diffplug.spotless.FileSignature; @@ -44,23 +39,20 @@ public class EslintConfig implements Serializable { private final String eslintConfigJs; - @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") - private final transient Map> additionalConfigFiles; // key: source-file, value: target-remapping path relative to package.json (if needed) - - private final FileSignature additionalConfigFilesSignature; - - public EslintConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs, Map> additionalConfigFiles) { + public EslintConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs) { try { this.eslintConfigPath = eslintConfigPath; this.eslintConfigPathSignature = eslintConfigPath != null ? FileSignature.signAsList(this.eslintConfigPath) : FileSignature.signAsList(); this.eslintConfigJs = eslintConfigJs; - this.additionalConfigFiles = additionalConfigFiles != null ? new TreeMap<>(additionalConfigFiles) : Collections.emptyMap(); - this.additionalConfigFilesSignature = FileSignature.signAsList(this.additionalConfigFiles.keySet().toArray(new File[0])); } catch (IOException e) { throw ThrowingEx.asRuntime(e); } } + public EslintConfig withEslintConfigPath(@Nullable File eslintConfigPath) { + return new EslintConfig(eslintConfigPath, this.eslintConfigJs); + } + @Nullable public File getEslintConfigPath() { return eslintConfigPath; @@ -70,9 +62,4 @@ public File getEslintConfigPath() { public String getEslintConfigJs() { return eslintConfigJs; } - - @Nonnull - public Map> getAdditionalConfigFiles() { - return additionalConfigFiles; - } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 492cf0da90..49bde50945 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -25,7 +25,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.TreeMap; import javax.annotation.Nonnull; @@ -152,17 +151,7 @@ private EslintConfig localCopyFiles(EslintConfig orig) { // eslint configs contain relative paths to additional config files (such as tsconfig.json e.g.) FormattedPrinter.SYSOUT.print("Copying config file <%s> to <%s> and using the copy", orig.getEslintConfigPath(), nodeModulesDir); File configFileCopy = NpmResourceHelper.copyFileToDir(orig.getEslintConfigPath(), nodeModulesDir); - - for (Map.Entry> additionalConfigFile : orig.getAdditionalConfigFiles().entrySet()) { - FormattedPrinter.SYSOUT.print("Copying additional config file <%s> to <%s> at subpath <%s> and using the copy", additionalConfigFile.getKey(), nodeModulesDir, additionalConfigFile.getValue()); - - if (additionalConfigFile.getValue().isPresent()) { - NpmResourceHelper.copyFileToDirAtSubpath(additionalConfigFile.getKey(), nodeModulesDir, additionalConfigFile.getValue().get()); - } else { - NpmResourceHelper.copyFileToDir(additionalConfigFile.getKey(), nodeModulesDir); - } - } - return new EslintConfig(configFileCopy, orig.getEslintConfigJs(), orig.getAdditionalConfigFiles()); + return orig.withEslintConfigPath(configFileCopy); } @Override @@ -226,8 +215,12 @@ private void setConfigToCallOptions(Map eslintCallOptions) } eslintCallOptions.put(FormatOption.NODE_MODULES_DIR, nodeModulesDir.getAbsolutePath()); - // TODO (simschla, 09.12.22): maybe only add this if there is a typescript config active? (TBD: how to detect) - eslintCallOptions.put(FormatOption.TS_CONFIG_ROOT_DIR, nodeModulesDir.toPath().relativize(projectDir.toPath()).toString()); + if (eslintConfig instanceof EslintTypescriptConfig) { + // if we are a ts config, see if we need to use specific paths or use default projectDir + File tsConfigFilePath = ((EslintTypescriptConfig) eslintConfig).getTypescriptConfigPath(); + File tsConfigRootDir = tsConfigFilePath != null ? tsConfigFilePath.getParentFile() : projectDir; + eslintCallOptions.put(FormatOption.TS_CONFIG_ROOT_DIR, nodeModulesDir.getAbsoluteFile().toPath().relativize(tsConfigRootDir.getAbsoluteFile().toPath()).toString()); + } } } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java new file mode 100644 index 0000000000..05c8209837 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java @@ -0,0 +1,41 @@ +package com.diffplug.spotless.npm; + +import com.diffplug.spotless.FileSignature; +import com.diffplug.spotless.ThrowingEx; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import javax.annotation.Nullable; + +import java.io.File; +import java.io.IOException; + +public class EslintTypescriptConfig extends EslintConfig { + + @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") + @Nullable + private final transient File typescriptConfigPath; + + @SuppressWarnings("unused") + private final FileSignature typescriptConfigPathSignature; + + public EslintTypescriptConfig(@Nullable File eslintConfigPath, @Nullable String eslintConfigJs, @Nullable File typescriptConfigPath) { + super(eslintConfigPath, eslintConfigJs); + try { + this.typescriptConfigPath = typescriptConfigPath; + this.typescriptConfigPathSignature = typescriptConfigPath != null ? FileSignature.signAsList(this.typescriptConfigPath) : FileSignature.signAsList(); + } catch (IOException e) { + throw ThrowingEx.asRuntime(e); + } + } + + @Override + public EslintConfig withEslintConfigPath(@Nullable File eslintConfigPath) { + return new EslintTypescriptConfig(eslintConfigPath, this.getEslintConfigJs(), this.typescriptConfigPath); + } + + @Nullable + public File getTypescriptConfigPath() { + return typescriptConfigPath; + } +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 3f433afa92..6152f9009d 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -621,8 +621,6 @@ public class EslintFormatExtension extends NpmStepConfig @Nullable String configJs = null; - List additionalConfigs = new ArrayList<>(); - public EslintFormatExtension(Map devDependencies) { this.devDependencies.putAll(requireNonNull(devDependencies)); } @@ -645,10 +643,6 @@ public EslintFormatExtension configFile(Object configFilePath) { return this; } - protected void additionalConfigFilePath(Object sourceFile, String relativeTargetPath) { - this.additionalConfigs.add(new AdditionalEslintConfig(sourceFile, relativeTargetPath)); - } - public FormatterStep createStep() { final Project project = getProject(); @@ -661,28 +655,8 @@ public FormatterStep createStep() { eslintConfig()); } - private EslintConfig eslintConfig() { - return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs, additionalConfigs()); - } - - private Map> additionalConfigs() { - // convert additionalConfigs to a map explicitly allowing null values - - return additionalConfigs.stream() - .collect(Collectors.toMap( - config -> getProject().file(config.configFilePath), - config -> Optional.ofNullable(config.relativeTargetPath))); - } - } - - private static class AdditionalEslintConfig { - final Object configFilePath; - - final String relativeTargetPath; - - AdditionalEslintConfig(Object configFilePath, String relativeTargetPath) { - this.configFilePath = configFilePath; - this.relativeTargetPath = relativeTargetPath; + protected EslintConfig eslintConfig() { + return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index b126cc76c6..d4dedc32eb 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -27,6 +27,10 @@ import javax.annotation.Nullable; import javax.inject.Inject; +import com.diffplug.spotless.npm.EslintConfig; + +import com.diffplug.spotless.npm.EslintTypescriptConfig; + import org.gradle.api.Project; import com.diffplug.spotless.FormatterStep; @@ -193,6 +197,9 @@ public TypescriptEslintFormatExtension eslint(Map devDependencie public class TypescriptEslintFormatExtension extends EslintFormatExtension { + @Nullable + Object typescriptConfigFilePath = null; + public TypescriptEslintFormatExtension(Map devDependencies) { super(devDependencies); } @@ -224,14 +231,16 @@ public TypescriptEslintFormatExtension styleGuide(String styleGuide) { } public TypescriptEslintFormatExtension tsconfigFile(Object path) { - return tsconfigFile(path, null); - } - - public TypescriptEslintFormatExtension tsconfigFile(Object path, String remapping) { - additionalConfigFilePath(path, remapping); + this.typescriptConfigFilePath = requireNonNull(path); replaceStep(createStep()); return this; } + + @Override + protected EslintConfig eslintConfig() { + EslintConfig config = super.eslintConfig(); + return new EslintTypescriptConfig(config.getEslintConfigPath(), config.getEslintConfigJs(), typescriptConfigFilePath != null ? getProject().file(typescriptConfigFilePath) : null); + } } @Override diff --git a/testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java b/testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java index d4a79fe417..0304889e1f 100644 --- a/testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java +++ b/testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.UnaryOperator; @@ -86,11 +87,20 @@ protected void replace(String path, String toReplace, String replaceWith) throws /** Returns the contents of the given file from the src/test/resources directory. */ protected static String getTestResource(String filename) { - URL url = ResourceHarness.class.getResource("/" + filename); - if (url == null) { - throw new IllegalArgumentException("No such resource " + filename); + Optional resourceUrl = getTestResourceUrl(filename); + if (resourceUrl.isPresent()) { + return ThrowingEx.get(() -> LineEnding.toUnix(Resources.toString(resourceUrl.get(), StandardCharsets.UTF_8))); } - return ThrowingEx.get(() -> LineEnding.toUnix(Resources.toString(url, StandardCharsets.UTF_8))); + throw new IllegalArgumentException("No such resource " + filename); + } + + protected static boolean existsTestResource(String filename) { + return getTestResourceUrl(filename).isPresent(); + } + + private static Optional getTestResourceUrl(String filename) { + URL url = ResourceHarness.class.getResource("/" + filename); + return Optional.ofNullable(url); } /** Returns Files (in a temporary folder) which has the contents of the given file from the src/test/resources directory. */ diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java new file mode 100644 index 0000000000..afe463c1aa --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -0,0 +1,210 @@ +/* + * Copyright 2016-2021 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +import com.diffplug.common.collect.ImmutableMap; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.StepHarnessWithFile; +import com.diffplug.spotless.TestProvisioner; +import com.diffplug.spotless.tag.NpmTest; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.File; +import java.util.Map; +import java.util.TreeMap; + +@NpmTest +class EslintFormatterStepTest { + + @NpmTest + @Nested + class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { + + private final Map> devDependenciesForRuleset = ImmutableMap.of( + "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), + "standard_rules_standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.STANDARD_WITH_TYPESCRIPT.devDependencies()), + "standard_rules_xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.XO_TYPESCRIPT.devDependencies()) + ); + + private final Map combine(Map m1, Map m2) { + Map combined = new TreeMap<>(m1); + combined.putAll(m2); + return combined; + } + + @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") + @ValueSource(strings = {"custom_rules", "standard_rules_standard_with_typescript", "standard_rules_xo"}) + void formattingUsingRulesetsFile(String ruleSetName) throws Exception { + String filedir = "npm/eslint/typescript/" + ruleSetName + "/"; + + String testDir = "formatting_ruleset_" + ruleSetName + "/"; +// File testDirFile = newFolder(testDir); + + final File eslintRc = createTestFile(filedir + ".eslintrc.js"); +// final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); + + //setFile(testDir + "/test.ts").toResource(filedir + "typescript.dirty"); + File tsconfigFile = null; + if (existsTestResource(filedir + "tsconfig.json")) { + tsconfigFile = setFile(testDir + "tsconfig.json").toResource(filedir + "tsconfig.json"); + } + final String dirtyFile = filedir + "typescript.dirty"; + File dirtyFileFile = setFile(testDir + "test.ts").toResource(dirtyFile); + final String cleanFile = filedir + "typescript.clean"; + + final FormatterStep formatterStep = EslintFormatterStep.create( + devDependenciesForRuleset.get(ruleSetName), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintTypescriptConfig(eslintRc, null, tsconfigFile)); + + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { + stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); + } + } + } +/* + @NpmTest + @Nested + class PrettierFormattingOfFileTypesIsWorking extends NpmFormatterStepCommonTests { + + @ParameterizedTest(name = "{index}: prettier can be applied to {0}") + @ValueSource(strings = {"html", "typescript", "json", "javascript-es5", "javascript-es6", "css", "scss", "markdown", "yaml"}) + void formattingUsingConfigFile(String fileType) throws Exception { + String filedir = "npm/prettier/filetypes/" + fileType + "/"; + + final File prettierRc = createTestFile(filedir + ".prettierrc.yml"); + final String dirtyFile = filedir + fileType + ".dirty"; + final String cleanFile = filedir + fileType + ".clean"; + + final FormatterStep formatterStep = PrettierFormatterStep.create( + PrettierFormatterStep.defaultDevDependencies(), + TestProvisioner.mavenCentral(), + buildDir(), + npmPathResolver(), + new PrettierConfig(prettierRc, null)); + + try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { + stepHarness.testResource(dirtyFile, cleanFile); + } + } + } + + @NpmTest + @Nested + class SpecificPrettierFormatterStepTests extends NpmFormatterStepCommonTests { + + @Test + void parserInferenceBasedOnExplicitFilepathIsWorking() throws Exception { + String filedir = "npm/prettier/filetypes/json/"; + + final String dirtyFile = filedir + "json.dirty"; + final String cleanFile = filedir + "json.clean"; + + final FormatterStep formatterStep = PrettierFormatterStep.create( + PrettierFormatterStep.defaultDevDependencies(), + TestProvisioner.mavenCentral(), + buildDir(), + npmPathResolver(), + new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"))); // should select parser based on this name + + try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { + stepHarness.testResource(dirtyFile, cleanFile); + } + } + + @Test + void parserInferenceBasedOnFilenameIsWorking() throws Exception { + String filedir = "npm/prettier/filename/"; + + final String dirtyFile = filedir + "dirty.json"; + final String cleanFile = filedir + "clean.json"; + + final FormatterStep formatterStep = PrettierFormatterStep.create( + PrettierFormatterStep.defaultDevDependencies(), + TestProvisioner.mavenCentral(), + buildDir(), + npmPathResolver(), + new PrettierConfig(null, Collections.emptyMap())); + + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { + stepHarness.testResource(new File("test.json"), dirtyFile, cleanFile); + } + } + + @Test + void verifyPrettierErrorMessageIsRelayed() throws Exception { + FormatterStep formatterStep = PrettierFormatterStep.create( + PrettierFormatterStep.defaultDevDependenciesWithPrettier("2.0.5"), + TestProvisioner.mavenCentral(), + buildDir(), + npmPathResolver(), + new PrettierConfig(null, ImmutableMap.of("parser", "postcss"))); + try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { + stepHarness.testResourceException("npm/prettier/filetypes/scss/scss.dirty", exception -> { + exception.hasMessageContaining("HTTP 501"); + exception.hasMessageContaining("Couldn't resolve parser \"postcss\""); + }); + } + } + } + + @NpmTest + @Nested + class PrettierFormattingOptionsAreWorking extends NpmFormatterStepCommonTests { + + private static final String FILEDIR = "npm/prettier/config/"; + + void runFormatTest(PrettierConfig config, String cleanFileNameSuffix) throws Exception { + + final String dirtyFile = FILEDIR + "typescript.dirty"; + final String cleanFile = FILEDIR + "typescript." + cleanFileNameSuffix + ".clean"; + + final FormatterStep formatterStep = PrettierFormatterStep.create( + PrettierFormatterStep.defaultDevDependencies(), + TestProvisioner.mavenCentral(), + buildDir(), + npmPathResolver(), + config); // should select parser based on this name + + try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { + stepHarness.testResource(dirtyFile, cleanFile); + } + } + + @Test + void defaultsAreApplied() throws Exception { + runFormatTest(new PrettierConfig(null, ImmutableMap.of("parser", "typescript")), "defaults"); + } + + @Test + void configFileOptionsAreApplied() throws Exception { + runFormatTest(new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), null), "configfile"); + } + + @Test + void configFileOptionsCanBeOverriden() throws Exception { + runFormatTest(new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), ImmutableMap.of("printWidth", 300)), "override"); + } + + }*/ +} diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java index 4da65cadc6..f1b5da59ff 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java @@ -42,4 +42,13 @@ protected File buildDir() throws IOException { } return this.buildDir; } + + private File projectDir = null; + + protected File projectDir() throws IOException { + if (this.projectDir == null) { + this.projectDir = newFolder("project-dir"); + } + return this.projectDir; + } } diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java index 5f14022ad8..9db218efb5 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java @@ -50,6 +50,7 @@ void formattingUsingConfigFile(String fileType) throws Exception { final FormatterStep formatterStep = PrettierFormatterStep.create( PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), new PrettierConfig(prettierRc, null)); @@ -74,6 +75,7 @@ void parserInferenceBasedOnExplicitFilepathIsWorking() throws Exception { final FormatterStep formatterStep = PrettierFormatterStep.create( PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"))); // should select parser based on this name @@ -93,6 +95,7 @@ void parserInferenceBasedOnFilenameIsWorking() throws Exception { final FormatterStep formatterStep = PrettierFormatterStep.create( PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), new PrettierConfig(null, Collections.emptyMap())); @@ -107,6 +110,7 @@ void verifyPrettierErrorMessageIsRelayed() throws Exception { FormatterStep formatterStep = PrettierFormatterStep.create( PrettierFormatterStep.defaultDevDependenciesWithPrettier("2.0.5"), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), new PrettierConfig(null, ImmutableMap.of("parser", "postcss"))); @@ -131,6 +135,7 @@ void runFormatTest(PrettierConfig config, String cleanFileNameSuffix) throws Exc final FormatterStep formatterStep = PrettierFormatterStep.create( PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), config); // should select parser based on this name diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java index 2812601726..d06b902b83 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java @@ -57,6 +57,7 @@ void formattingUsingConfigFile(String formattingConfigFile) throws Exception { final FormatterStep formatterStep = TsFmtFormatterStep.create( TsFmtFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), TypedTsFmtConfigFile.named(configFileNameWithoutExtension, configFile), @@ -79,6 +80,7 @@ void formattingUsingInlineConfigWorks() throws Exception { final FormatterStep formatterStep = TsFmtFormatterStep.create( TsFmtFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), + projectDir(), buildDir(), npmPathResolver(), null, From a2a5502b2ea696968c036dc89e21dedc95dd8633 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 15 Dec 2022 20:11:33 +0100 Subject: [PATCH 08/37] eslint: reduce required options --- .../resources/com/diffplug/spotless/npm/eslint-serve.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index 2804eb86dd..3c10b048c1 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -23,7 +23,6 @@ app.post("/eslint/format", async (req, res) => { const ESLintOptions = { fix: true, useEslintrc: false, // would result in (gradle) cache issues - resolvePluginsRelativeTo: format_data.node_modules_dir }; if (format_data.ts_config_root_dir) { @@ -42,15 +41,17 @@ app.post("/eslint/format", async (req, res) => { ESLintOptions.overrideConfig = ESLintOverrideConfig; } + console.log("using options: " + JSON.stringify(ESLintOptions)); + console.log("format input: ", format_data.file_content); + const eslint = new ESLint(ESLintOptions); - console.log("using options: " + JSON.stringify(ESLintOptions)); - console.log("format input", format_data.file_content); const lintTextOptions = { filePath: filePath, } console.log("lintTextOptions", lintTextOptions); + // LintResult[] // https://eslint.org/docs/latest/developer-guide/nodejs-api#-lintresult-type const results = await eslint.lintText(format_data.file_content, lintTextOptions); if (results.length !== 1) { From 35b2b7ced394c1bb5c512b47edd34604e451726d Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 16 Dec 2022 06:06:07 +0100 Subject: [PATCH 09/37] eslint: remove unneeded configuration --- .../diffplug/spotless/npm/EslintFormatterStep.java | 2 -- .../diffplug/spotless/npm/EslintRestService.java | 2 +- .../spotless/npm/EslintFormatterStepTest.java | 13 ++++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 49bde50945..55261350b3 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -213,8 +213,6 @@ private void setConfigToCallOptions(Map eslintCallOptions) if (eslintConfig.getEslintConfigJs() != null) { eslintCallOptions.put(FormatOption.ESLINT_OVERRIDE_CONFIG, eslintConfig.getEslintConfigJs()); } - eslintCallOptions.put(FormatOption.NODE_MODULES_DIR, nodeModulesDir.getAbsolutePath()); - if (eslintConfig instanceof EslintTypescriptConfig) { // if we are a ts config, see if we need to use specific paths or use default projectDir File tsConfigFilePath = ((EslintTypescriptConfig) eslintConfig).getTypescriptConfigPath(); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java index d3a01621f1..198ee5389c 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java @@ -35,7 +35,7 @@ public String format(String fileContent, Map formatOptions } enum FormatOption { - ESLINT_OVERRIDE_CONFIG("eslint_override_config"), ESLINT_OVERRIDE_CONFIG_FILE("eslint_override_config_file"), FILE_PATH("file_path"), NODE_MODULES_DIR("node_modules_dir"), TS_CONFIG_ROOT_DIR("ts_config_root_dir"); + ESLINT_OVERRIDE_CONFIG("eslint_override_config"), ESLINT_OVERRIDE_CONFIG_FILE("eslint_override_config_file"), FILE_PATH("file_path"), TS_CONFIG_ROOT_DIR("ts_config_root_dir"); private final String backendName; diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index afe463c1aa..45787e4217 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -23,6 +23,7 @@ import com.diffplug.spotless.tag.NpmTest; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -33,6 +34,12 @@ @NpmTest class EslintFormatterStepTest { + private final Map combine(Map m1, Map m2) { + Map combined = new TreeMap<>(m1); + combined.putAll(m2); + return combined; + } + @NpmTest @Nested class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { @@ -43,11 +50,7 @@ class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { "standard_rules_xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.XO_TYPESCRIPT.devDependencies()) ); - private final Map combine(Map m1, Map m2) { - Map combined = new TreeMap<>(m1); - combined.putAll(m2); - return combined; - } + @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") @ValueSource(strings = {"custom_rules", "standard_rules_standard_with_typescript", "standard_rules_xo"}) From 016dd3adc08cfb92ef37bccf2ea59d7c48c4edcd Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 16 Dec 2022 06:06:51 +0100 Subject: [PATCH 10/37] eslint: test supplying inline config --- .../spotless/npm/EslintFormatterStep.java | 3 + .../com/diffplug/spotless/npm/eslint-serve.js | 2 +- .../spotless/npm/EslintFormatterStepTest.java | 161 ++++++------------ 3 files changed, 52 insertions(+), 114 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 55261350b3..91d8ef62a5 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -146,6 +146,9 @@ private static class State extends NpmFormatterStepStateBase implements Serializ } private EslintConfig localCopyFiles(EslintConfig orig) { + if (orig.getEslintConfigPath() == null) { + return orig; + } // If any config files are provided, we need to make sure they are at the same location as the node modules // as eslint will try to resolve plugin/config names relatively to the config file location and some // eslint configs contain relative paths to additional config files (such as tsconfig.json e.g.) diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index 3c10b048c1..b9f3207d5f 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -38,7 +38,7 @@ app.post("/eslint/format", async (req, res) => { ESLintOptions.overrideConfigFile = ESLintOverrideConfigFile; } if (ESLintOverrideConfig) { - ESLintOptions.overrideConfig = ESLintOverrideConfig; + eval("ESLintOptions.overrideConfig = " + ESLintOverrideConfig); } console.log("using options: " + JSON.stringify(ESLintOptions)); diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 45787e4217..fdf17caae7 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -85,129 +85,64 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { } } } -/* - @NpmTest - @Nested - class PrettierFormattingOfFileTypesIsWorking extends NpmFormatterStepCommonTests { - - @ParameterizedTest(name = "{index}: prettier can be applied to {0}") - @ValueSource(strings = {"html", "typescript", "json", "javascript-es5", "javascript-es6", "css", "scss", "markdown", "yaml"}) - void formattingUsingConfigFile(String fileType) throws Exception { - String filedir = "npm/prettier/filetypes/" + fileType + "/"; - - final File prettierRc = createTestFile(filedir + ".prettierrc.yml"); - final String dirtyFile = filedir + fileType + ".dirty"; - final String cleanFile = filedir + fileType + ".clean"; - - final FormatterStep formatterStep = PrettierFormatterStep.create( - PrettierFormatterStep.defaultDevDependencies(), - TestProvisioner.mavenCentral(), - buildDir(), - npmPathResolver(), - new PrettierConfig(prettierRc, null)); - - try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { - stepHarness.testResource(dirtyFile, cleanFile); - } - } - } @NpmTest @Nested - class SpecificPrettierFormatterStepTests extends NpmFormatterStepCommonTests { + class EslintInlineConfigFormattingStepTest extends NpmFormatterStepCommonTests { - @Test - void parserInferenceBasedOnExplicitFilepathIsWorking() throws Exception { - String filedir = "npm/prettier/filetypes/json/"; - - final String dirtyFile = filedir + "json.dirty"; - final String cleanFile = filedir + "json.clean"; - - final FormatterStep formatterStep = PrettierFormatterStep.create( - PrettierFormatterStep.defaultDevDependencies(), - TestProvisioner.mavenCentral(), - buildDir(), - npmPathResolver(), - new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"))); // should select parser based on this name - - try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { - stepHarness.testResource(dirtyFile, cleanFile); - } - } @Test - void parserInferenceBasedOnFilenameIsWorking() throws Exception { - String filedir = "npm/prettier/filename/"; - - final String dirtyFile = filedir + "dirty.json"; - final String cleanFile = filedir + "clean.json"; + void formattingUsingInlineXoConfig() throws Exception { + String filedir = "npm/eslint/typescript/standard_rules_xo/"; + + String testDir = "formatting_ruleset_xo_inline_config/"; + + + final String esLintConfig = String.join("\n", + "{", + " env: {", + " browser: true,", + " es2021: true,", + " },", + " extends: 'xo/browser',", + " overrides: [", + " {", + " extends: [", + " 'xo-typescript',", + " ],", + " files: [", + " '*.ts',", + " '*.tsx',", + " ],", + " },", + " ],", + " parser: '@typescript-eslint/parser',", + " parserOptions: {", + " ecmaVersion: 'latest',", + " sourceType: 'module',", + " project: './tsconfig.json',", + " },", + " rules: {", + " },", + "}" + ); + + File tsconfigFile = setFile(testDir + "tsconfig.json").toResource(filedir + "tsconfig.json"); + final String dirtyFile = filedir + "typescript.dirty"; + File dirtyFileFile = setFile(testDir + "test.ts").toResource(dirtyFile); + final String cleanFile = filedir + "typescript.clean"; - final FormatterStep formatterStep = PrettierFormatterStep.create( - PrettierFormatterStep.defaultDevDependencies(), - TestProvisioner.mavenCentral(), - buildDir(), - npmPathResolver(), - new PrettierConfig(null, Collections.emptyMap())); + final FormatterStep formatterStep = EslintFormatterStep.create( + combine(EslintFormatterStep.PopularStyleGuide.XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintTypescriptConfig(null, esLintConfig, tsconfigFile)); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { - stepHarness.testResource(new File("test.json"), dirtyFile, cleanFile); - } - } - - @Test - void verifyPrettierErrorMessageIsRelayed() throws Exception { - FormatterStep formatterStep = PrettierFormatterStep.create( - PrettierFormatterStep.defaultDevDependenciesWithPrettier("2.0.5"), - TestProvisioner.mavenCentral(), - buildDir(), - npmPathResolver(), - new PrettierConfig(null, ImmutableMap.of("parser", "postcss"))); - try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { - stepHarness.testResourceException("npm/prettier/filetypes/scss/scss.dirty", exception -> { - exception.hasMessageContaining("HTTP 501"); - exception.hasMessageContaining("Couldn't resolve parser \"postcss\""); - }); + stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); } } } - - @NpmTest - @Nested - class PrettierFormattingOptionsAreWorking extends NpmFormatterStepCommonTests { - - private static final String FILEDIR = "npm/prettier/config/"; - - void runFormatTest(PrettierConfig config, String cleanFileNameSuffix) throws Exception { - - final String dirtyFile = FILEDIR + "typescript.dirty"; - final String cleanFile = FILEDIR + "typescript." + cleanFileNameSuffix + ".clean"; - - final FormatterStep formatterStep = PrettierFormatterStep.create( - PrettierFormatterStep.defaultDevDependencies(), - TestProvisioner.mavenCentral(), - buildDir(), - npmPathResolver(), - config); // should select parser based on this name - - try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { - stepHarness.testResource(dirtyFile, cleanFile); - } - } - - @Test - void defaultsAreApplied() throws Exception { - runFormatTest(new PrettierConfig(null, ImmutableMap.of("parser", "typescript")), "defaults"); - } - - @Test - void configFileOptionsAreApplied() throws Exception { - runFormatTest(new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), null), "configfile"); - } - - @Test - void configFileOptionsCanBeOverriden() throws Exception { - runFormatTest(new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), ImmutableMap.of("printWidth", 300)), "override"); - } - - }*/ } From a9f89d8e51e8c969a40f1bf78b668852ffa69ddc Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 16 Dec 2022 19:57:48 +0100 Subject: [PATCH 11/37] eslint: adding javascript formatting (part 1) --- .../spotless/npm/EslintFormatterStep.java | 40 ++++++++++++- .../spotless/TypescriptExtensionTest.java | 16 +++--- .../javascript/custom_rules/.eslintrc.js | 31 ++++++++++ .../custom_rules/javascript-es6.clean | 21 +++++++ .../custom_rules/javascript-es6.dirty | 21 +++++++ .../javascript/styleguide/airbnb/.eslintrc.js | 15 +++++ .../styleguide/airbnb/javascript-es6.clean | 17 ++++++ .../styleguide/airbnb/javascript-es6.dirty | 21 +++++++ .../javascript/styleguide/google/.eslintrc.js | 15 +++++ .../styleguide/google/javascript-es6.clean | 25 +++++++++ .../styleguide/google/javascript-es6.dirty | 21 +++++++ .../styleguide/standard/.eslintrc.js | 15 +++++ .../styleguide/standard/javascript-es6.clean | 19 +++++++ .../styleguide/standard/javascript-es6.dirty | 21 +++++++ .../javascript/styleguide/xo/.eslintrc.js | 15 +++++ .../styleguide/xo/javascript-es6.clean | 20 +++++++ .../styleguide/xo/javascript-es6.dirty | 21 +++++++ .../standard_with_typescript}/.eslintrc.js | 0 .../standard_with_typescript}/tsconfig.json | 0 .../typescript.clean | 0 .../typescript.dirty | 0 .../xo}/.eslintrc.js | 0 .../xo}/tsconfig.json | 0 .../xo}/typescript.clean | 0 .../xo}/typescript.dirty | 0 .../spotless/npm/EslintFormatterStepTest.java | 56 ++++++++++++++++--- 26 files changed, 393 insertions(+), 17 deletions(-) create mode 100644 testlib/src/main/resources/npm/eslint/javascript/custom_rules/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.clean create mode 100644 testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.dirty create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.clean create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/google/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.clean create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.dirty create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.clean create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.dirty create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/.eslintrc.js create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.clean create mode 100644 testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.dirty rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_standard_with_typescript => styleguide/standard_with_typescript}/.eslintrc.js (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_standard_with_typescript => styleguide/standard_with_typescript}/tsconfig.json (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_standard_with_typescript => styleguide/standard_with_typescript}/typescript.clean (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_standard_with_typescript => styleguide/standard_with_typescript}/typescript.dirty (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_xo => styleguide/xo}/.eslintrc.js (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_xo => styleguide/xo}/tsconfig.json (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_xo => styleguide/xo}/typescript.clean (100%) rename testlib/src/main/resources/npm/eslint/typescript/{standard_rules_xo => styleguide/xo}/typescript.dirty (100%) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 91d8ef62a5..14bc99a243 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -48,7 +48,7 @@ public class EslintFormatterStep { public static final String DEFAULT_ESLINT_VERSION = "8.28.0"; public enum PopularStyleGuide { - STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { + TS_STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); @@ -60,7 +60,7 @@ public Map devDependencies() { return dependencies; } }, - XO_TYPESCRIPT("xo-typescript") { + TS_XO_TYPESCRIPT("xo-typescript") { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); @@ -69,6 +69,42 @@ public Map devDependencies() { dependencies.put("typescript", "4.9.3"); return dependencies; } + }, + JS_AIRBNB("airbnb") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-airbnb-base", "15.0.0"); + dependencies.put("eslint-plugin-import", "2.26.0"); + return dependencies; + } + }, + JS_GOOGLE("google") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-google", "0.14.0"); + return dependencies; + } + }, + JS_STANDARD("standard") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-standard", "17.0.0"); + dependencies.put("eslint-plugin-import", "2.26.0"); + dependencies.put("eslint-plugin-n", "15.6.0"); + dependencies.put("eslint-plugin-promise", "6.1.1"); + return dependencies; + } + }, + JS_XO("xo") { + @Override + public Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-xo", "0.43.1"); + return dependencies; + } }; private final String popularStyleGuideName; diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index 0c3d10c89f..7d715998f6 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -163,8 +163,8 @@ void useEslint() throws IOException { @Test void useXoStandardRules() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/typescript/standard_rules_xo/.eslintrc.js"); - setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_xo/tsconfig.json"); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/xo/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/xo/tsconfig.json"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -176,15 +176,15 @@ void useXoStandardRules() throws IOException { " eslint().styleGuide('xo-typescript').configFile('.eslintrc.js')", " }", "}"); - setFile("test.ts").toResource("npm/eslint/typescript/standard_rules_xo/typescript.dirty"); + setFile("test.ts").toResource("npm/eslint/typescript/styleguide/xo/typescript.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); - assertFile("test.ts").sameAsResource("npm/eslint/typescript/standard_rules_xo/typescript.clean"); + assertFile("test.ts").sameAsResource("npm/eslint/typescript/styleguide/xo/typescript.clean"); } @Test void useStandardWithTypescriptRules() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js"); - setFile("tsconfig.json").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json"); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/tsconfig.json"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -196,8 +196,8 @@ void useStandardWithTypescriptRules() throws IOException { " eslint().styleGuide('standard-with-typescript').configFile('.eslintrc.js')", " }", "}"); - setFile("test.ts").toResource("npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty"); + setFile("test.ts").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); - assertFile("test.ts").sameAsResource("npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean"); + assertFile("test.ts").sameAsResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean"); } } diff --git a/testlib/src/main/resources/npm/eslint/javascript/custom_rules/.eslintrc.js b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/.eslintrc.js new file mode 100644 index 0000000000..60c5c19f5b --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/.eslintrc.js @@ -0,0 +1,31 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "overrides": [ + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +}; diff --git a/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.clean b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.clean new file mode 100644 index 0000000000..8bbeb50cf3 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.clean @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: "Peter", + last : "Pan", + get fullName() { return this.first + " " + this.last; } +}; + +const str = "Hello, world!" +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = "Hello \ +World" +; + +function test (a, b = "world") { let combined =a+ b; return combined;} + +test ("Hello"); diff --git a/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.dirty b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.dirty new file mode 100644 index 0000000000..36a514cc30 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/custom_rules/javascript-es6.dirty @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: 'Peter', + last : 'Pan', + get fullName() { return this.first + ' ' + this.last; } +}; + +const str = 'Hello, world!' +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = 'Hello \ +World' +; + +function test (a, b = 'world') { let combined =a+ b; return combined}; + +test ('Hello'); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/.eslintrc.js b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/.eslintrc.js new file mode 100644 index 0000000000..d93248ee88 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: 'airbnb-base', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + rules: { + }, +}; diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.clean b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.clean new file mode 100644 index 0000000000..c8dda3d33f --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.clean @@ -0,0 +1,17 @@ +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +]; + +const p = { + first: 'Peter', + last: 'Pan', + get fullName() { return `${this.first} ${this.last}`; }, +}; + +const str = 'Hello, world!'; +const str2 = str.charAt(3) + str[0]; + +const multilinestr = 'Hello \ +World'; +function test(a, b = 'world') { const combined = a + b; return combined; } + +test('Hello'); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty new file mode 100644 index 0000000000..08ebafc1a2 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: "Peter", + last : "Pan", + get fullName() { return this.first + " " + this.last; } +}; + +const str = "Hello, world!" +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = "Hello \ +World" +; + +function test (a, b = "world") { let combined =a+ b; return combined}; + +test ("Hello"); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/.eslintrc.js b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/.eslintrc.js new file mode 100644 index 0000000000..71b0c47016 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + 'env': { + 'browser': true, + 'es2021': true, + }, + 'extends': 'google', + 'overrides': [ + ], + 'parserOptions': { + 'ecmaVersion': 'latest', + 'sourceType': 'module', + }, + 'rules': { + }, +}; diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.clean b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.clean new file mode 100644 index 0000000000..f960a984c2 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.clean @@ -0,0 +1,25 @@ +const numbers=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +]; + +const p = { + first: 'Peter', + last: 'Pan', + get fullName() { + return this.first + ' ' + this.last; + }, +}; + +const str = 'Hello, world!' +; + +const str2=str.charAt(3)+str[0]; + +const multilinestr = 'Hello \ +World' +; + +function test(a, b = 'world') { + const combined =a+ b; return combined; +}; + +test('Hello'); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.dirty b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.dirty new file mode 100644 index 0000000000..08ebafc1a2 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/google/javascript-es6.dirty @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: "Peter", + last : "Pan", + get fullName() { return this.first + " " + this.last; } +}; + +const str = "Hello, world!" +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = "Hello \ +World" +; + +function test (a, b = "world") { let combined =a+ b; return combined}; + +test ("Hello"); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/.eslintrc.js b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/.eslintrc.js new file mode 100644 index 0000000000..cbed25aab9 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: 'standard', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, + rules: { + } +} diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.clean b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.clean new file mode 100644 index 0000000000..757d330a7f --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.clean @@ -0,0 +1,19 @@ +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +] + +const p = { + first: 'Peter', + last: 'Pan', + get fullName () { return this.first + ' ' + this.last } +} + +const str = 'Hello, world!' + +const str2 = str.charAt(3) + str[0] + +const multilinestr = 'Hello \ +World' + +function test (a, b = 'world') { const combined = a + b; return combined }; + +test('Hello') diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.dirty b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.dirty new file mode 100644 index 0000000000..08ebafc1a2 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/standard/javascript-es6.dirty @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: "Peter", + last : "Pan", + get fullName() { return this.first + " " + this.last; } +}; + +const str = "Hello, world!" +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = "Hello \ +World" +; + +function test (a, b = "world") { let combined =a+ b; return combined}; + +test ("Hello"); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/.eslintrc.js b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/.eslintrc.js new file mode 100644 index 0000000000..844e843c41 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: 'xo', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + rules: { + }, +}; diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.clean b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.clean new file mode 100644 index 0000000000..3cd1bd0865 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.clean @@ -0,0 +1,20 @@ +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + +const p = { + first: 'Peter', + last: 'Pan', + get fullName() { + return this.first + ' ' + this.last; + }, +}; + +const str = 'Hello, world!'; +const str2 = str.charAt(3) + str[0]; + +const multilinestr = 'Hello \ +World'; +function test(a, b = 'world') { + const combined = a + b; return combined; +} + +test('Hello'); diff --git a/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.dirty b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.dirty new file mode 100644 index 0000000000..08ebafc1a2 --- /dev/null +++ b/testlib/src/main/resources/npm/eslint/javascript/styleguide/xo/javascript-es6.dirty @@ -0,0 +1,21 @@ +var numbers=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, +]; + +const p = { + first: "Peter", + last : "Pan", + get fullName() { return this.first + " " + this.last; } +}; + +const str = "Hello, world!" +; + +var str2=str.charAt(3)+str[0]; + +var multilinestr = "Hello \ +World" +; + +function test (a, b = "world") { let combined =a+ b; return combined}; + +test ("Hello"); diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js b/testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/.eslintrc.js rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json b/testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/tsconfig.json similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/tsconfig.json rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/tsconfig.json diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean b/testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.clean rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty b/testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/typescript.dirty similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_standard_with_typescript/typescript.dirty rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/standard_with_typescript/typescript.dirty diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js b/testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/.eslintrc.js similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/.eslintrc.js rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/.eslintrc.js diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json b/testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/tsconfig.json similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/tsconfig.json rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/tsconfig.json diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean b/testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/typescript.clean similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.clean rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/typescript.clean diff --git a/testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty b/testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/typescript.dirty similarity index 100% rename from testlib/src/main/resources/npm/eslint/typescript/standard_rules_xo/typescript.dirty rename to testlib/src/main/resources/npm/eslint/typescript/styleguide/xo/typescript.dirty diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index fdf17caae7..3a8bfc7fd6 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -17,7 +17,6 @@ import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.StepHarness; import com.diffplug.spotless.StepHarnessWithFile; import com.diffplug.spotless.TestProvisioner; import com.diffplug.spotless.tag.NpmTest; @@ -40,24 +39,67 @@ private final Map combine(Map m1, Map> devDependenciesForRuleset = ImmutableMap.of( + "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), + "styleguide/airbnb", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_AIRBNB.devDependencies()), + "styleguide/google", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_GOOGLE.devDependencies()), + "styleguide/standard", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_STANDARD.devDependencies()), + "styleguide/xo", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_XO.devDependencies()) + ); + + @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") + @ValueSource(strings = {"custom_rules", "styleguide/airbnb", "styleguide/google", "styleguide/standard", "styleguide/xo"}) + void formattingUsingRulesetsFile(String ruleSetName) throws Exception { + String filedir = "npm/eslint/javascript/" + ruleSetName + "/"; + + String testDir = "formatting_ruleset_" + ruleSetName.replace('/', '_') + "/"; +// File testDirFile = newFolder(testDir); + + final File eslintRc = createTestFile(filedir + ".eslintrc.js"); +// final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); + + final String dirtyFile = filedir + "javascript-es6.dirty"; + File dirtyFileFile = setFile(testDir + "test.js").toResource(dirtyFile); + final String cleanFile = filedir + "javascript-es6.clean"; + + final FormatterStep formatterStep = EslintFormatterStep.create( + devDependenciesForRuleset.get(ruleSetName), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintConfig(eslintRc, null)); + + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { + stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); + } + } + } + + + @NpmTest @Nested class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { private final Map> devDependenciesForRuleset = ImmutableMap.of( "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), - "standard_rules_standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.STANDARD_WITH_TYPESCRIPT.devDependencies()), - "standard_rules_xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.XO_TYPESCRIPT.devDependencies()) + "styleguide/standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_STANDARD_WITH_TYPESCRIPT.devDependencies()), + "styleguide/xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies()) ); @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") - @ValueSource(strings = {"custom_rules", "standard_rules_standard_with_typescript", "standard_rules_xo"}) + @ValueSource(strings = {"custom_rules", "styleguide/standard_with_typescript", "styleguide/xo"}) void formattingUsingRulesetsFile(String ruleSetName) throws Exception { String filedir = "npm/eslint/typescript/" + ruleSetName + "/"; - String testDir = "formatting_ruleset_" + ruleSetName + "/"; + String testDir = "formatting_ruleset_" + ruleSetName.replace('/', '_') + "/"; // File testDirFile = newFolder(testDir); final File eslintRc = createTestFile(filedir + ".eslintrc.js"); @@ -88,7 +130,7 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { @NpmTest @Nested - class EslintInlineConfigFormattingStepTest extends NpmFormatterStepCommonTests { + class EslintInlineConfigTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { @Test @@ -133,7 +175,7 @@ void formattingUsingInlineXoConfig() throws Exception { final String cleanFile = filedir + "typescript.clean"; final FormatterStep formatterStep = EslintFormatterStep.create( - combine(EslintFormatterStep.PopularStyleGuide.XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), + combine(EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), TestProvisioner.mavenCentral(), projectDir(), buildDir(), From 4efc19b200face1697512e18ed906524df444177 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Sun, 18 Dec 2022 20:43:16 +0100 Subject: [PATCH 12/37] eslint: introduce separate javascript extension --- .../spotless/npm/EslintTypescriptConfig.java | 25 ++- .../gradle/spotless/FormatExtension.java | 100 +++-------- .../gradle/spotless/JavascriptExtension.java | 156 ++++++++++++++++++ .../gradle/spotless/SpotlessExtension.java | 5 + .../gradle/spotless/TypescriptExtension.java | 84 +++++----- .../spotless/npm/EslintFormatterStepTest.java | 139 ++++++++-------- .../npm/NpmFormatterStepCommonTests.java | 2 +- .../spotless/npm/TsFmtFormatterStepTest.java | 2 +- 8 files changed, 313 insertions(+), 200 deletions(-) create mode 100644 plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java index 05c8209837..f8c213a0a9 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java @@ -1,15 +1,30 @@ +/* + * Copyright 2022 DiffPlug + * + * 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. + */ package com.diffplug.spotless.npm; +import java.io.File; +import java.io.IOException; + +import javax.annotation.Nullable; + import com.diffplug.spotless.FileSignature; import com.diffplug.spotless.ThrowingEx; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import javax.annotation.Nullable; - -import java.io.File; -import java.io.IOException; - public class EslintTypescriptConfig extends EslintConfig { @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 6152f9009d..08e713a921 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -24,13 +24,11 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Random; import java.util.TreeMap; -import java.util.stream.Collectors; +import java.util.function.Consumer; import javax.annotation.Nullable; import javax.inject.Inject; @@ -62,8 +60,6 @@ import com.diffplug.spotless.generic.ReplaceRegexStep; import com.diffplug.spotless.generic.ReplaceStep; import com.diffplug.spotless.generic.TrimTrailingWhitespaceStep; -import com.diffplug.spotless.npm.EslintConfig; -import com.diffplug.spotless.npm.EslintFormatterStep; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; @@ -518,23 +514,32 @@ public LicenseHeaderConfig licenseHeaderFile(Object licenseHeaderFile, String de return config; } - public abstract class NpmStepConfig> { + public abstract static class NpmStepConfig> { @Nullable protected Object npmFile; @Nullable protected Object npmrcFile; + protected Project project; + + private Consumer replaceStep; + + public NpmStepConfig(Project project, Consumer replaceStep) { + this.project = requireNonNull(project); + this.replaceStep = requireNonNull(replaceStep); + } + @SuppressWarnings("unchecked") public T npmExecutable(final Object npmFile) { this.npmFile = npmFile; - replaceStep(createStep()); + replaceStep(); return (T) this; } public T npmrc(final Object npmrcFile) { this.npmrcFile = npmrcFile; - replaceStep(createStep()); + replaceStep(); return (T) this; } @@ -547,10 +552,14 @@ File npmrcFileOrNull() { } private File fileOrNull(Object npmFile) { - return npmFile != null ? getProject().file(npmFile) : null; + return npmFile != null ? project.file(npmFile) : null; } - abstract FormatterStep createStep(); + protected void replaceStep() { + replaceStep.accept(createStep()); + } + + abstract protected FormatterStep createStep(); } @@ -565,22 +574,24 @@ public class PrettierConfig extends NpmStepConfig { final Map devDependencies; PrettierConfig(Map devDependencies) { + super(getProject(), FormatExtension.this::replaceStep); this.devDependencies = requireNonNull(devDependencies); } public PrettierConfig configFile(final Object prettierConfigFile) { this.prettierConfigFile = prettierConfigFile; - replaceStep(createStep()); + replaceStep(); return this; } public PrettierConfig config(final Map prettierConfig) { this.prettierConfig = new TreeMap<>(prettierConfig); - replaceStep(createStep()); + replaceStep(); return this; } - FormatterStep createStep() { + @Override + protected FormatterStep createStep() { final Project project = getProject(); return PrettierFormatterStep.create( devDependencies, @@ -611,69 +622,6 @@ public PrettierConfig prettier(Map devDependencies) { return prettierConfig; } - public class EslintFormatExtension extends NpmStepConfig { - - Map devDependencies = new LinkedHashMap<>(); - - @Nullable - Object configFilePath = null; - - @Nullable - String configJs = null; - - public EslintFormatExtension(Map devDependencies) { - this.devDependencies.putAll(requireNonNull(devDependencies)); - } - - public EslintFormatExtension devDependencies(Map devDependencies) { - this.devDependencies.putAll(devDependencies); - replaceStep(createStep()); - return this; - } - - public EslintFormatExtension configJs(String configJs) { - this.configJs = requireNonNull(configJs); - replaceStep(createStep()); - return this; - } - - public EslintFormatExtension configFile(Object configFilePath) { - this.configFilePath = requireNonNull(configFilePath); - replaceStep(createStep()); - return this; - } - - public FormatterStep createStep() { - final Project project = getProject(); - - return EslintFormatterStep.create( - devDependencies, - provisioner(), - project.getProjectDir(), - project.getBuildDir(), - new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), - eslintConfig()); - } - - protected EslintConfig eslintConfig() { - return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); - } - } - - public EslintFormatExtension eslint() { - return eslint(EslintFormatterStep.defaultDevDependencies()); - } - - public EslintFormatExtension eslint(String version) { - return eslint(EslintFormatterStep.defaultDevDependenciesWithEslint(version)); - } - - public EslintFormatExtension eslint(Map devDependencies) { - EslintFormatExtension eslint = new EslintFormatExtension(devDependencies); - addStep(eslint.createStep()); - return eslint; - } - /** Uses the default version of clang-format. */ public ClangFormatConfig clangFormat() { return clangFormat(ClangFormatStep.defaultVersion()); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java new file mode 100644 index 0000000000..348ed5f37e --- /dev/null +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java @@ -0,0 +1,156 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.gradle.spotless; + +import static java.util.Objects.requireNonNull; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import org.gradle.api.Project; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.npm.EslintConfig; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; +import com.diffplug.spotless.npm.NpmPathResolver; + +public class JavascriptExtension extends FormatExtension { + + static final String NAME = "javascript"; + + @Inject + public JavascriptExtension(SpotlessExtension spotless) { + super(spotless); + } + + public JavascriptEslintConfig eslint() { + return eslint(EslintFormatterStep.defaultDevDependenciesForTypescript()); + } + + public JavascriptEslintConfig eslint(String version) { + return eslint(EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(version)); + } + + public JavascriptEslintConfig eslint(Map devDependencies) { + JavascriptEslintConfig eslint = new JavascriptEslintConfig(devDependencies); + addStep(eslint.createStep()); + return eslint; + } + + // TODO: make the configs static so that they do not need to have a hierarchy symmetric to the extensions + + public static abstract class EslintBaseConfig> extends NpmStepConfig> { + Map devDependencies = new LinkedHashMap<>(); + + @Nullable + Object configFilePath = null; + + @Nullable + String configJs = null; + + public EslintBaseConfig(Project project, Consumer replaceStep, Map devDependencies) { + super(project, replaceStep); + this.devDependencies.putAll(requireNonNull(devDependencies)); + } + + @SuppressWarnings("unchecked") + public T devDependencies(Map devDependencies) { + this.devDependencies.putAll(devDependencies); + replaceStep(); + return (T) this; + } + + @SuppressWarnings("unchecked") + public T configJs(String configJs) { + this.configJs = requireNonNull(configJs); + replaceStep(); + return (T) this; + } + + @SuppressWarnings("unchecked") + public T configFile(Object configFilePath) { + this.configFilePath = requireNonNull(configFilePath); + replaceStep(); + return (T) this; + } + + @SuppressWarnings("unchecked") + public T styleGuide(String styleGuide) { + PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide); + + verifyStyleGuideIsSupported(styleGuide, popularStyleGuide); + devDependencies(popularStyleGuide.devDependencies()); + replaceStep(); + return (T) this; + } + + protected abstract void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide); + } + + public class JavascriptEslintConfig extends EslintBaseConfig { + + public JavascriptEslintConfig(Map devDependencies) { + super(getProject(), JavascriptExtension.this::replaceStep, devDependencies); + } + + public FormatterStep createStep() { + final Project project = getProject(); + + return EslintFormatterStep.create( + devDependencies, + provisioner(), + project.getProjectDir(), + project.getBuildDir(), + new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), + eslintConfig()); + } + + @Override + protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { + if (!isJsStyleGuide(popularStyleGuide)) { + throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known javascript style guides: " + + Arrays.stream(PopularStyleGuide.values()) + .filter(this::isJsStyleGuide) + .map(PopularStyleGuide::getPopularStyleGuideName) + .sorted() + .collect(Collectors.joining(", "))); + } + } + + private boolean isJsStyleGuide(PopularStyleGuide popularStyleGuide) { + return popularStyleGuide != null && popularStyleGuide.name().startsWith("JS_"); + } + + protected EslintConfig eslintConfig() { + return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); + } + } + + @Override + protected void setupTask(SpotlessTask task) { + if (target == null) { + throw noDefaultTargetException(); + } + super.setupTask(task); + } +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index 57aa3b2d83..3fe5721b5d 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -167,6 +167,11 @@ public void cpp(Action closure) { format(CppExtension.NAME, CppExtension.class, closure); } + /** Configures the special javascript-specific extension for javascript files. */ + public void javascript(Action closure) { + format(JavascriptExtension.NAME, JavascriptExtension.class, closure); + } + /** Configures the special typescript-specific extension for typescript files. */ public void typescript(Action closure) { format(TypescriptExtension.NAME, TypescriptExtension.class, closure); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index d4dedc32eb..f318dd89c8 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -27,15 +27,13 @@ import javax.annotation.Nullable; import javax.inject.Inject; -import com.diffplug.spotless.npm.EslintConfig; - -import com.diffplug.spotless.npm.EslintTypescriptConfig; - import org.gradle.api.Project; import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; +import com.diffplug.spotless.npm.EslintTypescriptConfig; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; import com.diffplug.spotless.npm.TsConfigFileType; @@ -81,12 +79,13 @@ public class TypescriptFormatExtension extends NpmStepConfig devDependencies; TypescriptFormatExtension(Map devDependencies) { + super(getProject(), TypescriptExtension.this::replaceStep); this.devDependencies = Objects.requireNonNull(devDependencies); } public void config(final Map config) { this.config = new TreeMap<>(requireNonNull(config)); - replaceStep(createStep()); + replaceStep(); } public void tsconfigFile(final Object path) { @@ -108,7 +107,7 @@ public void tsfmtFile(final Object path) { private void configFile(TsConfigFileType filetype, Object path) { this.configFileType = requireNonNull(filetype); this.configFilePath = requireNonNull(path); - replaceStep(createStep()); + replaceStep(); } public FormatterStep createStep() { @@ -161,7 +160,7 @@ public class TypescriptPrettierConfig extends PrettierConfig { } @Override - FormatterStep createStep() { + protected FormatterStep createStep() { fixParserToTypescript(); return super.createStep(); } @@ -178,74 +177,73 @@ private void fixParserToTypescript() { } } - @Override - public TypescriptEslintFormatExtension eslint() { + public TypescriptEslintConfig eslint() { return eslint(EslintFormatterStep.defaultDevDependenciesForTypescript()); } - @Override - public TypescriptEslintFormatExtension eslint(String version) { + public TypescriptEslintConfig eslint(String version) { return eslint(EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(version)); } - @Override - public TypescriptEslintFormatExtension eslint(Map devDependencies) { - TypescriptEslintFormatExtension eslint = new TypescriptEslintFormatExtension(devDependencies); + public TypescriptEslintConfig eslint(Map devDependencies) { + TypescriptEslintConfig eslint = new TypescriptEslintConfig(devDependencies); addStep(eslint.createStep()); return eslint; } - public class TypescriptEslintFormatExtension extends EslintFormatExtension { + public class TypescriptEslintConfig extends JavascriptExtension.EslintBaseConfig { @Nullable Object typescriptConfigFilePath = null; - public TypescriptEslintFormatExtension(Map devDependencies) { - super(devDependencies); + public TypescriptEslintConfig(Map devDependencies) { + super(getProject(), TypescriptExtension.this::replaceStep, devDependencies); } - @Override - public TypescriptEslintFormatExtension devDependencies(Map devDependencies) { - return (TypescriptEslintFormatExtension) super.devDependencies(devDependencies); + public TypescriptEslintConfig tsconfigFile(Object path) { + this.typescriptConfigFilePath = requireNonNull(path); + replaceStep(); + return this; } @Override - public TypescriptEslintFormatExtension configJs(String configJs) { - return (TypescriptEslintFormatExtension) super.configJs(configJs); + protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { + if (!isTsStyleGuide(popularStyleGuide)) { + throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known typescript style guides: " + + Arrays.stream(EslintFormatterStep.PopularStyleGuide.values()) + .filter(this::isTsStyleGuide) + .map(PopularStyleGuide::getPopularStyleGuideName) + .sorted() + .collect(Collectors.joining(", "))); + } } - @Override - public TypescriptEslintFormatExtension configFile(Object configFilePath) { - return (TypescriptEslintFormatExtension) super.configFile(configFilePath); + private boolean isTsStyleGuide(PopularStyleGuide popularStyleGuide) { + return popularStyleGuide != null && popularStyleGuide.name().startsWith("TS_"); } - public TypescriptEslintFormatExtension styleGuide(String styleGuide) { - PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide); - if (popularStyleGuide == null) { - throw new IllegalArgumentException("Unknown style guide: " + styleGuide + ". Known style guides: " - + Arrays.stream(PopularStyleGuide.values()).map(PopularStyleGuide::getPopularStyleGuideName).collect(Collectors.joining(", "))); - } - devDependencies(popularStyleGuide.devDependencies()); - replaceStep(createStep()); - return this; - } + public FormatterStep createStep() { + final Project project = getProject(); - public TypescriptEslintFormatExtension tsconfigFile(Object path) { - this.typescriptConfigFilePath = requireNonNull(path); - replaceStep(createStep()); - return this; + return EslintFormatterStep.create( + devDependencies, + provisioner(), + project.getProjectDir(), + project.getBuildDir(), + new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), + eslintConfig()); } - @Override protected EslintConfig eslintConfig() { - EslintConfig config = super.eslintConfig(); - return new EslintTypescriptConfig(config.getEslintConfigPath(), config.getEslintConfigJs(), typescriptConfigFilePath != null ? getProject().file(typescriptConfigFilePath) : null); + return new EslintTypescriptConfig( + configFilePath != null ? getProject().file(configFilePath) : null, + configJs, + typescriptConfigFilePath != null ? getProject().file(typescriptConfigFilePath) : null); } } @Override protected void setupTask(SpotlessTask task) { - // defaults to all typescript files if (target == null) { throw noDefaultTargetException(); } diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 3a8bfc7fd6..95298db0ea 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +15,20 @@ */ package com.diffplug.spotless.npm; -import com.diffplug.common.collect.ImmutableMap; -import com.diffplug.spotless.FormatterStep; -import com.diffplug.spotless.StepHarnessWithFile; -import com.diffplug.spotless.TestProvisioner; -import com.diffplug.spotless.tag.NpmTest; +import java.io.File; +import java.util.Map; +import java.util.TreeMap; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.io.File; -import java.util.Map; -import java.util.TreeMap; +import com.diffplug.common.collect.ImmutableMap; +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.StepHarnessWithFile; +import com.diffplug.spotless.TestProvisioner; +import com.diffplug.spotless.tag.NpmTest; @NpmTest class EslintFormatterStepTest { @@ -44,12 +44,11 @@ private final Map combine(Map m1, Map> devDependenciesForRuleset = ImmutableMap.of( - "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), - "styleguide/airbnb", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_AIRBNB.devDependencies()), - "styleguide/google", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_GOOGLE.devDependencies()), - "styleguide/standard", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_STANDARD.devDependencies()), - "styleguide/xo", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_XO.devDependencies()) - ); + "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), + "styleguide/airbnb", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_AIRBNB.devDependencies()), + "styleguide/google", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_GOOGLE.devDependencies()), + "styleguide/standard", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_STANDARD.devDependencies()), + "styleguide/xo", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_XO.devDependencies())); @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") @ValueSource(strings = {"custom_rules", "styleguide/airbnb", "styleguide/google", "styleguide/standard", "styleguide/xo"}) @@ -57,22 +56,22 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { String filedir = "npm/eslint/javascript/" + ruleSetName + "/"; String testDir = "formatting_ruleset_" + ruleSetName.replace('/', '_') + "/"; -// File testDirFile = newFolder(testDir); + // File testDirFile = newFolder(testDir); final File eslintRc = createTestFile(filedir + ".eslintrc.js"); -// final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); + // final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); final String dirtyFile = filedir + "javascript-es6.dirty"; File dirtyFileFile = setFile(testDir + "test.js").toResource(dirtyFile); final String cleanFile = filedir + "javascript-es6.clean"; final FormatterStep formatterStep = EslintFormatterStep.create( - devDependenciesForRuleset.get(ruleSetName), - TestProvisioner.mavenCentral(), - projectDir(), - buildDir(), - npmPathResolver(), - new EslintConfig(eslintRc, null)); + devDependenciesForRuleset.get(ruleSetName), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintConfig(eslintRc, null)); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); @@ -80,19 +79,14 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { } } - - @NpmTest @Nested class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { private final Map> devDependenciesForRuleset = ImmutableMap.of( - "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), - "styleguide/standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_STANDARD_WITH_TYPESCRIPT.devDependencies()), - "styleguide/xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies()) - ); - - + "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), + "styleguide/standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_STANDARD_WITH_TYPESCRIPT.devDependencies()), + "styleguide/xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies())); @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") @ValueSource(strings = {"custom_rules", "styleguide/standard_with_typescript", "styleguide/xo"}) @@ -100,10 +94,10 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { String filedir = "npm/eslint/typescript/" + ruleSetName + "/"; String testDir = "formatting_ruleset_" + ruleSetName.replace('/', '_') + "/"; -// File testDirFile = newFolder(testDir); + // File testDirFile = newFolder(testDir); final File eslintRc = createTestFile(filedir + ".eslintrc.js"); -// final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); + // final File eslintRc = setFile(buildDir().getPath() + "/.eslintrc.js").toResource(filedir + ".eslintrc.js"); //setFile(testDir + "/test.ts").toResource(filedir + "typescript.dirty"); File tsconfigFile = null; @@ -115,12 +109,12 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { final String cleanFile = filedir + "typescript.clean"; final FormatterStep formatterStep = EslintFormatterStep.create( - devDependenciesForRuleset.get(ruleSetName), - TestProvisioner.mavenCentral(), - projectDir(), - buildDir(), - npmPathResolver(), - new EslintTypescriptConfig(eslintRc, null, tsconfigFile)); + devDependenciesForRuleset.get(ruleSetName), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintTypescriptConfig(eslintRc, null, tsconfigFile)); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); @@ -132,42 +126,39 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { @Nested class EslintInlineConfigTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { - @Test void formattingUsingInlineXoConfig() throws Exception { String filedir = "npm/eslint/typescript/standard_rules_xo/"; String testDir = "formatting_ruleset_xo_inline_config/"; - final String esLintConfig = String.join("\n", - "{", - " env: {", - " browser: true,", - " es2021: true,", - " },", - " extends: 'xo/browser',", - " overrides: [", - " {", - " extends: [", - " 'xo-typescript',", - " ],", - " files: [", - " '*.ts',", - " '*.tsx',", - " ],", - " },", - " ],", - " parser: '@typescript-eslint/parser',", - " parserOptions: {", - " ecmaVersion: 'latest',", - " sourceType: 'module',", - " project: './tsconfig.json',", - " },", - " rules: {", - " },", - "}" - ); + "{", + " env: {", + " browser: true,", + " es2021: true,", + " },", + " extends: 'xo/browser',", + " overrides: [", + " {", + " extends: [", + " 'xo-typescript',", + " ],", + " files: [", + " '*.ts',", + " '*.tsx',", + " ],", + " },", + " ],", + " parser: '@typescript-eslint/parser',", + " parserOptions: {", + " ecmaVersion: 'latest',", + " sourceType: 'module',", + " project: './tsconfig.json',", + " },", + " rules: {", + " },", + "}"); File tsconfigFile = setFile(testDir + "tsconfig.json").toResource(filedir + "tsconfig.json"); final String dirtyFile = filedir + "typescript.dirty"; @@ -175,12 +166,12 @@ void formattingUsingInlineXoConfig() throws Exception { final String cleanFile = filedir + "typescript.clean"; final FormatterStep formatterStep = EslintFormatterStep.create( - combine(EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), - TestProvisioner.mavenCentral(), - projectDir(), - buildDir(), - npmPathResolver(), - new EslintTypescriptConfig(null, esLintConfig, tsconfigFile)); + combine(EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), + TestProvisioner.mavenCentral(), + projectDir(), + buildDir(), + npmPathResolver(), + new EslintTypescriptConfig(null, esLintConfig, tsconfigFile)); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java index f1b5da59ff..1ed6e9bbe8 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java index d06b902b83..474c664486 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 0206246cee0de58aac3b3477da69f98d8b1c2426 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 20 Dec 2022 19:38:34 +0100 Subject: [PATCH 13/37] eslint: adding tests for js --- .../diffplug/spotless/npm/EslintConfig.java | 7 + .../spotless/npm/EslintFormatterStep.java | 4 +- .../gradle/spotless/JavascriptExtension.java | 61 ++++- .../spotless/JavascriptExtensionTest.java | 221 ++++++++++++++++++ .../spotless/TypescriptExtensionTest.java | 25 +- 5 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java index 732111cc71..4e1848856a 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java @@ -62,4 +62,11 @@ public File getEslintConfigPath() { public String getEslintConfigJs() { return eslintConfigJs; } + + public EslintConfig verify() { + if (eslintConfigPath == null && eslintConfigJs == null) { + throw new IllegalArgumentException("ESLint must be configured using either a configFile or a configJs - but both are null."); + } + return this; + } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 14bc99a243..0e5e618e1e 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -183,14 +183,14 @@ private static class State extends NpmFormatterStepStateBase implements Serializ private EslintConfig localCopyFiles(EslintConfig orig) { if (orig.getEslintConfigPath() == null) { - return orig; + return orig.verify(); } // If any config files are provided, we need to make sure they are at the same location as the node modules // as eslint will try to resolve plugin/config names relatively to the config file location and some // eslint configs contain relative paths to additional config files (such as tsconfig.json e.g.) FormattedPrinter.SYSOUT.print("Copying config file <%s> to <%s> and using the copy", orig.getEslintConfigPath(), nodeModulesDir); File configFileCopy = NpmResourceHelper.copyFileToDir(orig.getEslintConfigPath(), nodeModulesDir); - return orig.withEslintConfigPath(configFileCopy); + return orig.withEslintConfigPath(configFileCopy).verify(); } @Override diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java index 348ed5f37e..fb05387f49 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java @@ -18,6 +18,7 @@ import static java.util.Objects.requireNonNull; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; @@ -28,11 +29,13 @@ import org.gradle.api.Project; +import com.diffplug.common.collect.ImmutableList; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; import com.diffplug.spotless.npm.NpmPathResolver; +import com.diffplug.spotless.npm.PrettierFormatterStep; public class JavascriptExtension extends FormatExtension { @@ -57,8 +60,6 @@ public JavascriptEslintConfig eslint(Map devDependencies) { return eslint; } - // TODO: make the configs static so that they do not need to have a hierarchy symmetric to the extensions - public static abstract class EslintBaseConfig> extends NpmStepConfig> { Map devDependencies = new LinkedHashMap<>(); @@ -146,6 +147,62 @@ protected EslintConfig eslintConfig() { } } + /** Uses the default version of prettier. */ + @Override + public PrettierConfig prettier() { + return prettier(PrettierFormatterStep.defaultDevDependencies()); + } + + /** Uses the specified version of prettier. */ + @Override + public PrettierConfig prettier(String version) { + return prettier(PrettierFormatterStep.defaultDevDependenciesWithPrettier(version)); + } + + /** Uses exactly the npm packages specified in the map. */ + @Override + public PrettierConfig prettier(Map devDependencies) { + PrettierConfig prettierConfig = new JavascriptPrettierConfig(devDependencies); + addStep(prettierConfig.createStep()); + return prettierConfig; + } + + private static final String DEFAULT_PRETTIER_JS_PARSER = "babel"; + private static final ImmutableList PRETTIER_JS_PARSERS = ImmutableList.of(DEFAULT_PRETTIER_JS_PARSER, "babel-flow", "flow"); + + /** + * Overrides the parser to be set to a js parser. + */ + public class JavascriptPrettierConfig extends PrettierConfig { + + JavascriptPrettierConfig(Map devDependencies) { + super(devDependencies); + } + + @Override + protected FormatterStep createStep() { + fixParserToJavascript(); + return super.createStep(); + } + + private void fixParserToJavascript() { + if (this.prettierConfig == null) { + this.prettierConfig = Collections.singletonMap("parser", DEFAULT_PRETTIER_JS_PARSER); + } else { + final Object currentParser = this.prettierConfig.get("parser"); + if (PRETTIER_JS_PARSERS.contains(String.valueOf(currentParser))) { + getProject().getLogger().debug("Already javascript parser set, not overriding."); + } else { + this.prettierConfig.put("parser", DEFAULT_PRETTIER_JS_PARSER); + if (currentParser != null) { + getProject().getLogger().warn("Overriding parser option to '{}'. (Was set to '{}'.) Set it to another js parser if you have problems with '{}'.", DEFAULT_PRETTIER_JS_PARSER, currentParser, DEFAULT_PRETTIER_JS_PARSER); + } + } + + } + } + } + @Override protected void setupTask(SpotlessTask task) { if (target == null) { diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java new file mode 100644 index 0000000000..af203936f3 --- /dev/null +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java @@ -0,0 +1,221 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.gradle.spotless; + +import java.io.IOException; + +import org.assertj.core.api.Assertions; +import org.gradle.testkit.runner.BuildResult; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.diffplug.spotless.tag.NpmTest; + +@NpmTest +class JavascriptExtensionTest extends GradleIntegrationHarness { + + @NpmTest + @Nested + class EslintGeneralJavascriptTests extends GradleIntegrationHarness { + @Test + void supportsEslintFormattingForJavascript() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint().configFile('.eslintrc.js').styleGuide('standard')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); + } + + @Test + void eslintDoesNotAllowToUseTsStyleGuideForJavascript() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint().configFile('.eslintrc.js').styleGuide('xo-typescript')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); + Assertions.assertThat(spotlessApply.getOutput()).contains("Unknown style guide: xo-typescript"); + } + + @Test + void eslintAllowsToSpecifyEslintVersionForJavascript() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint('8.28.0').configFile('.eslintrc.js').styleGuide('standard')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); + } + + @Test + void esllintAllowsToSpecifyInlineConfig() throws IOException { + final String eslintConfigJs = String.join("\n", + "{", + " env: {", + " browser: true,", + " es2021: true", + " },", + " extends: 'standard',", + " overrides: [", + " ],", + " parserOptions: {", + " ecmaVersion: 'latest',", + " sourceType: 'module'", + " },", + " rules: {", + " }", + "}"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint().configJs('''" + eslintConfigJs + "''').styleGuide('standard')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); + } + + @Test + void eslintRequiresAnExplicitEslintConfig() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint().styleGuide('standard')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); + Assertions.assertThat(spotlessApply.getOutput()).contains("ESLint must be configured"); + } + + @Test + void eslintAllowsSpecifyingCustomLibraryVersions() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint([", + " 'eslint': '8.28.0',", + " 'eslint-config-standard': '17.0.0',", + " 'eslint-plugin-import': '2.26.0',", + " 'eslint-plugin-n': '15.6.0',", + " 'eslint-plugin-promise': '6.1.1'", + " ]).configFile('.eslintrc.js')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); + } + + } + + @NpmTest + @Nested + class EslintPopularJsStyleGuideTests extends GradleIntegrationHarness { + @ParameterizedTest(name = "{index}: eslint can be applied using styleguide {0}") + @ValueSource(strings = {"airbnb", "google", "standard", "xo"}) + void formattingUsingStyleguide(String styleguide) throws Exception { + + final String styleguidePath = "npm/eslint/javascript/styleguide/" + styleguide + "/"; + + setFile(".eslintrc.js").toResource(styleguidePath + ".eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " eslint().configFile('.eslintrc.js').styleGuide('" + styleguide + "')", + " }", + "}"); + setFile("test.js").toResource(styleguidePath + "javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource(styleguidePath + "javascript-es6.clean"); + } + } + + @NpmTest + @Nested + class JavascriptPrettierTests extends GradleIntegrationHarness { + @Test + void supportsPrettierFormattingForJavascript() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " javascript {", + " target 'test.js'", + " prettier()", + " }", + "}"); + setFile("test.js").toResource("npm/prettier/filetypes/javascript-es6/javascript-es6.dirty"); + gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + assertFile("test.js").sameAsResource("npm/prettier/filetypes/javascript-es6/javascript-es6.clean"); + } + } + +} diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index 7d715998f6..da9fa03905 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -17,6 +17,8 @@ import java.io.IOException; +import org.assertj.core.api.Assertions; +import org.gradle.testkit.runner.BuildResult; import org.junit.jupiter.api.Test; import com.diffplug.spotless.tag.NpmTest; @@ -162,7 +164,7 @@ void useEslint() throws IOException { } @Test - void useXoStandardRules() throws IOException { + void useEslintXoStandardRules() throws IOException { setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/xo/.eslintrc.js"); setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/xo/tsconfig.json"); setFile("build.gradle").toLines( @@ -182,7 +184,7 @@ void useXoStandardRules() throws IOException { } @Test - void useStandardWithTypescriptRules() throws IOException { + void useEslintStandardWithTypescriptRules() throws IOException { setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js"); setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/tsconfig.json"); setFile("build.gradle").toLines( @@ -200,4 +202,23 @@ void useStandardWithTypescriptRules() throws IOException { gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); assertFile("test.ts").sameAsResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean"); } + + @Test + void useEslintForTypescriptDoesNotAllowUsingJsStyleguide() throws IOException { + setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/airbnb/.eslintrc.js"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "spotless {", + " typescript {", + " target 'test.ts'", + " eslint().styleGuide('airbnb').configFile('.eslintrc.js')", + " }", + "}"); + setFile("test.js").toResource("npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty"); + BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); + Assertions.assertThat(spotlessApply.getOutput()).contains("Unknown style guide: airbnb"); + } } From 393a4c4424e66466cf1a698ab8716c331ec7e493 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 20 Dec 2022 19:37:54 +0100 Subject: [PATCH 14/37] eslint: bumping version numbers --- .../spotless/npm/EslintFormatterStep.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 0e5e618e1e..8589819540 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -45,18 +45,17 @@ public class EslintFormatterStep { public static final String NAME = "eslint-format"; - public static final String DEFAULT_ESLINT_VERSION = "8.28.0"; + public static final String DEFAULT_ESLINT_VERSION = "^8.30.0"; public enum PopularStyleGuide { TS_STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-standard-with-typescript", "23.0.0"); - dependencies.put("eslint-plugin-import", "2.26.0"); - dependencies.put("eslint-plugin-n", "15.5.1"); - dependencies.put("eslint-plugin-promise", "6.1.1"); - dependencies.put("typescript", "4.9.3"); + dependencies.put("eslint-config-standard-with-typescript", "^24.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); + dependencies.put("eslint-plugin-n", "^15.6.0"); + dependencies.put("eslint-plugin-promise", "^6.1.1"); return dependencies; } }, @@ -64,9 +63,8 @@ public Map devDependencies() { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-xo", "0.43.1"); - dependencies.put("eslint-config-xo-typescript", "0.55.1"); - dependencies.put("typescript", "4.9.3"); + dependencies.put("eslint-config-xo", "^0.43.1"); + dependencies.put("eslint-config-xo-typescript", "^0.55.1"); return dependencies; } }, @@ -74,8 +72,8 @@ public Map devDependencies() { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-airbnb-base", "15.0.0"); - dependencies.put("eslint-plugin-import", "2.26.0"); + dependencies.put("eslint-config-airbnb-base", "^15.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); return dependencies; } }, @@ -83,7 +81,7 @@ public Map devDependencies() { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-google", "0.14.0"); + dependencies.put("eslint-config-google", "^0.14.0"); return dependencies; } }, @@ -91,10 +89,10 @@ public Map devDependencies() { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-standard", "17.0.0"); - dependencies.put("eslint-plugin-import", "2.26.0"); - dependencies.put("eslint-plugin-n", "15.6.0"); - dependencies.put("eslint-plugin-promise", "6.1.1"); + dependencies.put("eslint-config-standard", "^17.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); + dependencies.put("eslint-plugin-n", "^15.6.0"); + dependencies.put("eslint-plugin-promise", "^6.1.1"); return dependencies; } }, @@ -102,7 +100,7 @@ public Map devDependencies() { @Override public Map devDependencies() { Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-xo", "0.43.1"); + dependencies.put("eslint-config-xo", "^0.43.1"); return dependencies; } }; @@ -135,8 +133,9 @@ public static Map defaultDevDependenciesForTypescript() { public static Map defaultDevDependenciesTypescriptWithEslint(String eslintVersion) { Map dependencies = new LinkedHashMap<>(); - dependencies.put("@typescript-eslint/eslint-plugin", "5.45.0"); - dependencies.put("@typescript-eslint/parser", "5.45.0"); + dependencies.put("@typescript-eslint/eslint-plugin", "^5.47.0"); + dependencies.put("@typescript-eslint/parser", "^5.47.0"); + dependencies.put("typescript", "^4.9.4"); dependencies.put("eslint", Objects.requireNonNull(eslintVersion)); return dependencies; } From 8dc740ef3723d38687222c2dfcd9b68bae3edce0 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 20 Dec 2022 19:53:57 +0100 Subject: [PATCH 15/37] eslint: adapt to extended api on maven side --- .../java/com/diffplug/spotless/maven/generic/Prettier.java | 7 ++++--- .../java/com/diffplug/spotless/maven/typescript/Tsfmt.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java index 85684e85cd..5bdce32c47 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,8 +105,9 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { // create the format step PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline); File buildDir = stepConfig.getFileLocator().getBuildDir(); - NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, stepConfig.getFileLocator().getBaseDir()); - return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npmPathResolver, prettierConfig); + File baseDir = stepConfig.getFileLocator().getBaseDir(); + NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, baseDir); + return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, prettierConfig); } private boolean moreThanOneNonNull(Object... objects) { diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java index 9ebb9ac503..e41231818c 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -120,8 +120,9 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { } File buildDir = stepConfig.getFileLocator().getBuildDir(); - NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, stepConfig.getFileLocator().getBaseDir()); - return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npmPathResolver, configFile, configInline); + File baseDir = stepConfig.getFileLocator().getBaseDir(); + NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, baseDir); + return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, configFile, configInline); } private static IllegalArgumentException onlyOneConfig() { From cb3dd5536febc8fccb0b71ee1103225d6fd9eafb Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 21 Dec 2022 16:28:32 +0100 Subject: [PATCH 16/37] eslint: speedup npm install --- lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java index e1bf8c8673..28db79b726 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ class NpmProcess { } void install() { - npmAwait("install", "--no-audit", "--no-package-lock"); + npmAwait("install", "--no-audit", "--no-package-lock", "--prefer-offline"); } Process start() { From e12c5efb5f64b4133228e7d102baebd67191da32 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 21 Dec 2022 16:31:18 +0100 Subject: [PATCH 17/37] eslint: speedup npmrc tests we intend them to timeout, but please fail quicker. --- .../gradle/spotless/PrettierIntegrationTest.java | 12 +++++++++--- .../maven/prettier/PrettierFormatStepTest.java | 14 +++++++++++--- .../maven/typescript/TypescriptFormatStepTest.java | 14 +++++++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java index 404ccf88c8..6f55312bd5 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -163,7 +163,10 @@ void usePhpCommunityPlugin() throws IOException { @Test void autodetectNpmrcFileConfig() throws IOException { setFile(".npmrc").toLines( - "registry=https://i.do.no.exist.com"); + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -186,7 +189,10 @@ void autodetectNpmrcFileConfig() throws IOException { @Test void pickupNpmrcFileConfig() throws IOException { setFile(".custom_npmrc").toLines( - "registry=https://i.do.no.exist.com"); + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java index 130bbde600..8fce61b5d8 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,7 +145,11 @@ void autodetect_parser_based_on_filename() throws Exception { @Test void autodetect_npmrc_file() throws Exception { - setFile(".npmrc").toLines("registry=https://i.do.no.exist.com"); + setFile(".npmrc").toLines( + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); String suffix = "ts"; writePomWithPrettierSteps("**/*." + suffix, "", @@ -158,7 +162,11 @@ void autodetect_npmrc_file() throws Exception { @Test void select_configured_npmrc_file() throws Exception { - setFile(".custom_npmrc").toLines("registry=https://i.do.no.exist.com"); + setFile(".custom_npmrc").toLines( + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); String suffix = "ts"; writePomWithPrettierSteps("**/*." + suffix, "", diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java index 600f9818d1..3b21f972ed 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,7 +114,11 @@ void testTypescript_2_Configs() throws Exception { @Test void testNpmrcIsAutoPickedUp() throws Exception { - setFile(".npmrc").toLines("registry=https://i.do.no.exist.com"); + setFile(".npmrc").toLines( + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); writePomWithTypescriptSteps( "", " ${basedir}/tslint.json", @@ -126,7 +130,11 @@ void testNpmrcIsAutoPickedUp() throws Exception { @Test void testNpmrcIsConfigurativelyPickedUp() throws Exception { - setFile(".custom_npmrc").toLines("registry=https://i.do.no.exist.com"); + setFile(".custom_npmrc").toLines( + "registry=https://i.do.not.exist.com", + "fetch-timeout=250", + "fetch-retry-mintimeout=250", + "fetch-retry-maxtimeout=250"); writePomWithTypescriptSteps( "", " ${basedir}/tslint.json", From 218db79b6ff8d0a1ea8c00a83cf38e21b8a19689 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 21 Dec 2022 16:31:51 +0100 Subject: [PATCH 18/37] eslint: extract base properties to a common base class --- .../spotless/maven/generic/Prettier.java | 20 ++----- .../npm/AbstractNpmFormatterStepFactory.java | 55 +++++++++++++++++++ .../spotless/maven/typescript/Tsfmt.java | 20 ++----- 3 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java index 5bdce32c47..b45f802653 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java @@ -23,12 +23,12 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; -import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.maven.npm.AbstractNpmFormatterStepFactory; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierConfig; import com.diffplug.spotless.npm.PrettierFormatterStep; -public class Prettier implements FormatterStepFactory { +public class Prettier extends AbstractNpmFormatterStepFactory { public static final String ERROR_MESSAGE_ONLY_ONE_CONFIG = "must specify exactly one prettierVersion, devDependencies or devDependencyProperties"; @@ -47,12 +47,6 @@ public class Prettier implements FormatterStepFactory { @Parameter private String configFile; - @Parameter - private String npmExecutable; - - @Parameter - private String npmrc; - @Override public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { @@ -70,10 +64,6 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { this.devDependencies = dependencyPropertiesAsMap(); } - File npm = npmExecutable != null ? stepConfig.getFileLocator().locateFile(npmExecutable) : null; - - File npmrcFile = npmrc != null ? stepConfig.getFileLocator().locateFile(npmrc) : null; - // process config file or inline config File configFileHandler; if (this.configFile != null) { @@ -103,10 +93,10 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { } // create the format step + File baseDir = baseDir(stepConfig); + File buildDir = buildDir(stepConfig); PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline); - File buildDir = stepConfig.getFileLocator().getBuildDir(); - File baseDir = stepConfig.getFileLocator().getBaseDir(); - NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, baseDir); + NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, prettierConfig); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java new file mode 100644 index 0000000000..8bc0f8f715 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.npm; + +import java.io.File; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.npm.NpmPathResolver; + +public abstract class AbstractNpmFormatterStepFactory implements FormatterStepFactory { + + @Parameter + private String npmExecutable; + + @Parameter + private String npmrc; + + protected File npm(FormatterStepConfig stepConfig) { + File npm = npmExecutable != null ? stepConfig.getFileLocator().locateFile(npmExecutable) : null; + return npm; + } + + protected File npmrc(FormatterStepConfig stepConfig) { + File npmrc = this.npmrc != null ? stepConfig.getFileLocator().locateFile(this.npmrc) : null; + return npmrc; + } + + protected File buildDir(FormatterStepConfig stepConfig) { + return stepConfig.getFileLocator().getBuildDir(); + } + + protected File baseDir(FormatterStepConfig stepConfig) { + return stepConfig.getFileLocator().getBaseDir(); + } + + protected NpmPathResolver npmPathResolver(FormatterStepConfig stepConfig) { + return new NpmPathResolver(npm(stepConfig), npmrc(stepConfig), baseDir(stepConfig)); + } +} diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java index e41231818c..6c60c02cc6 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java @@ -23,13 +23,13 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; -import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.maven.npm.AbstractNpmFormatterStepFactory; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.TsConfigFileType; import com.diffplug.spotless.npm.TsFmtFormatterStep; import com.diffplug.spotless.npm.TypedTsFmtConfigFile; -public class Tsfmt implements FormatterStepFactory { +public class Tsfmt extends AbstractNpmFormatterStepFactory { @Parameter private String tslintFile; @@ -52,12 +52,6 @@ public class Tsfmt implements FormatterStepFactory { @Parameter private String tslintVersion; - @Parameter - private String npmExecutable; - - @Parameter - private String npmrc; - @Parameter private Map config; @@ -74,10 +68,6 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { devDependencies.put("tslint", tslintVersion); } - File npm = npmExecutable != null ? stepConfig.getFileLocator().locateFile(npmExecutable) : null; - - File npmrcFile = npmrc != null ? stepConfig.getFileLocator().locateFile(npmrc) : null; - TypedTsFmtConfigFile configFile; Map configInline; // check that there is only 1 config file or inline config @@ -119,9 +109,9 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { throw onlyOneConfig(); } - File buildDir = stepConfig.getFileLocator().getBuildDir(); - File baseDir = stepConfig.getFileLocator().getBaseDir(); - NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, baseDir); + File buildDir = buildDir(stepConfig); + File baseDir = baseDir(stepConfig); + NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, configFile, configInline); } From 31462a224d9b54c21ce75366e5d40ce5ea59f540 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 22 Dec 2022 07:52:03 +0100 Subject: [PATCH 19/37] eslint: initial maven support --- .../spotless/npm/EslintFormatterStep.java | 25 +++-- .../gradle/spotless/JavascriptExtension.java | 14 +-- .../gradle/spotless/TypescriptExtension.java | 9 +- .../spotless/maven/AbstractSpotlessMojo.java | 4 + .../spotless/maven/javascript/EslintJs.java | 98 +++++++++++++++++++ .../spotless/maven/javascript/Javascript.java | 42 ++++++++ .../spotless/maven/typescript/EslintTs.java | 48 +++++++++ .../spotless/maven/typescript/Typescript.java | 8 +- .../spotless/npm/EslintFormatterStepTest.java | 2 +- 9 files changed, 221 insertions(+), 29 deletions(-) create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 8589819540..820283aae4 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -20,12 +20,14 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.function.Predicate; import javax.annotation.Nonnull; @@ -50,7 +52,7 @@ public class EslintFormatterStep { public enum PopularStyleGuide { TS_STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-standard-with-typescript", "^24.0.0"); dependencies.put("eslint-plugin-import", "^2.26.0"); @@ -61,7 +63,7 @@ public Map devDependencies() { }, TS_XO_TYPESCRIPT("xo-typescript") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-xo", "^0.43.1"); dependencies.put("eslint-config-xo-typescript", "^0.55.1"); @@ -70,7 +72,7 @@ public Map devDependencies() { }, JS_AIRBNB("airbnb") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-airbnb-base", "^15.0.0"); dependencies.put("eslint-plugin-import", "^2.26.0"); @@ -79,7 +81,7 @@ public Map devDependencies() { }, JS_GOOGLE("google") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-google", "^0.14.0"); return dependencies; @@ -87,7 +89,7 @@ public Map devDependencies() { }, JS_STANDARD("standard") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-standard", "^17.0.0"); dependencies.put("eslint-plugin-import", "^2.26.0"); @@ -98,7 +100,7 @@ public Map devDependencies() { }, JS_XO("xo") { @Override - public Map devDependencies() { + public @Nonnull Map devDependencies() { Map dependencies = new LinkedHashMap<>(); dependencies.put("eslint-config-xo", "^0.43.1"); return dependencies; @@ -115,7 +117,7 @@ public String getPopularStyleGuideName() { return popularStyleGuideName; } - public abstract Map devDependencies(); + public abstract @Nonnull Map devDependencies(); public static PopularStyleGuide fromNameOrNull(String popularStyleGuideName) { for (PopularStyleGuide popularStyleGuide : PopularStyleGuide.values()) { @@ -125,6 +127,15 @@ public static PopularStyleGuide fromNameOrNull(String popularStyleGuideName) { } return null; } + + public static String getPopularStyleGuideNames(Predicate filter) { + // collect matching style guide names using stream + return Arrays.stream(PopularStyleGuide.values()) + .filter(filter) + .map(PopularStyleGuide::getPopularStyleGuideName) + .sorted() + .collect(java.util.stream.Collectors.joining(", ")); + } } public static Map defaultDevDependenciesForTypescript() { diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java index fb05387f49..cbe0040a83 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java @@ -17,12 +17,10 @@ import static java.util.Objects.requireNonNull; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; @@ -100,9 +98,8 @@ public T styleGuide(String styleGuide) { PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide); verifyStyleGuideIsSupported(styleGuide, popularStyleGuide); - devDependencies(popularStyleGuide.devDependencies()); - replaceStep(); - return (T) this; + assert popularStyleGuide != null; + return devDependencies(popularStyleGuide.devDependencies()); } protected abstract void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide); @@ -129,12 +126,7 @@ public FormatterStep createStep() { @Override protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { if (!isJsStyleGuide(popularStyleGuide)) { - throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known javascript style guides: " - + Arrays.stream(PopularStyleGuide.values()) - .filter(this::isJsStyleGuide) - .map(PopularStyleGuide::getPopularStyleGuideName) - .sorted() - .collect(Collectors.joining(", "))); + throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known javascript style guides: " + PopularStyleGuide.getPopularStyleGuideNames(this::isJsStyleGuide)); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index f318dd89c8..79ae056bdc 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -17,12 +17,10 @@ import static java.util.Objects.requireNonNull; -import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.TreeMap; -import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; @@ -209,12 +207,7 @@ public TypescriptEslintConfig tsconfigFile(Object path) { @Override protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { if (!isTsStyleGuide(popularStyleGuide)) { - throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known typescript style guides: " - + Arrays.stream(EslintFormatterStep.PopularStyleGuide.values()) - .filter(this::isTsStyleGuide) - .map(PopularStyleGuide::getPopularStyleGuideName) - .sorted() - .collect(Collectors.joining(", "))); + throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known typescript style guides: " + PopularStyleGuide.getPopularStyleGuideNames(this::isTsStyleGuide)); } } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java index e4fdc7dcd0..f13f9b200d 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java @@ -64,6 +64,7 @@ import com.diffplug.spotless.maven.incremental.UpToDateChecker; import com.diffplug.spotless.maven.incremental.UpToDateChecking; import com.diffplug.spotless.maven.java.Java; +import com.diffplug.spotless.maven.javascript.Javascript; import com.diffplug.spotless.maven.kotlin.Kotlin; import com.diffplug.spotless.maven.markdown.Markdown; import com.diffplug.spotless.maven.pom.Pom; @@ -152,6 +153,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo { @Parameter private Typescript typescript; + @Parameter + private Javascript javascript; + @Parameter private Antlr4 antlr4; diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java new file mode 100644 index 0000000000..91de2136a1 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.javascript; + +import java.io.File; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.npm.AbstractNpmFormatterStepFactory; +import com.diffplug.spotless.npm.EslintConfig; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.NpmPathResolver; + +public class EslintJs extends AbstractNpmFormatterStepFactory { + + @Parameter + private String configFile; + + @Parameter + private String configJs; + + @Parameter + private String styleGuide; + + @Parameter + protected String eslintVersion; + + @Parameter + private Map devDependencies; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { + Map devDependencies = new TreeMap<>(); + if (this.devDependencies != null) { + devDependencies.putAll(this.devDependencies); + } else { + Map defaultDependencies = createDefaultDependencies(); + devDependencies.putAll(defaultDependencies); + } + + addStyleGuideDevDependencies(devDependencies); + + File buildDir = buildDir(stepConfig); + File baseDir = baseDir(stepConfig); + NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); + return EslintFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, eslintConfig(stepConfig)); + } + + protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { + return new EslintConfig(this.configFile != null ? stepConfig.getFileLocator().locateFile(this.configFile) : null, this.configJs); + } + + private void addStyleGuideDevDependencies(Map devDependencies) { + if (this.styleGuide != null) { + EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.valueOf(this.styleGuide); + validateStyleGuide(styleGuide); + devDependencies.putAll(styleGuide.devDependencies()); + } + } + + private void validateStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { + if (styleGuide == null) { + throw new IllegalArgumentException("StyleGuide '" + this.styleGuide + "' is not supported. Supported style guides: " + supportedStyleGuides()); + } + if (!isValidStyleGuide(styleGuide)) { + throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide + ". Use one of the following: " + supportedStyleGuides()); + } + } + + private String supportedStyleGuides() { + return EslintFormatterStep.PopularStyleGuide.getPopularStyleGuideNames(this::isValidStyleGuide); + } + + protected boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { + return styleGuide.name().startsWith("JS_"); + } + + protected Map createDefaultDependencies() { + return eslintVersion == null ? EslintFormatterStep.defaultDevDependencies() : EslintFormatterStep.defaultDevDependenciesWithEslint(eslintVersion); + } +} diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java new file mode 100644 index 0000000000..db7049ff0d --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.javascript; + +import java.util.Collections; +import java.util.Set; + +import com.diffplug.spotless.maven.FormatterFactory; + +/** + * A {@link FormatterFactory} implementation that corresponds to {@code ...} configuration element. + *

+ * It defines a formatter for typescript source files. + */ +public class Javascript extends FormatterFactory { + @Override + public Set defaultIncludes() { + return Collections.emptySet(); + } + + @Override + public String licenseHeaderDelimiter() { + return null; + } + + public void addEslint(EslintJs eslint) { + addStepFactory(eslint); + } +} diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java new file mode 100644 index 0000000000..0fe834ffde --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.typescript; + +import java.util.Map; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.javascript.EslintJs; +import com.diffplug.spotless.npm.EslintConfig; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintTypescriptConfig; + +public class EslintTs extends EslintJs { + + @Parameter + private String tsconfigFile; + + @Override + protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { + EslintConfig jsConfig = super.eslintConfig(stepConfig); + return new EslintTypescriptConfig(jsConfig.getEslintConfigPath(), jsConfig.getEslintConfigJs(), tsconfigFile != null ? stepConfig.getFileLocator().locateFile(tsconfigFile) : null); + } + + @Override + protected boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { + return styleGuide.name().startsWith("TS_"); + } + + @Override + protected Map createDefaultDependencies() { + return this.eslintVersion == null ? EslintFormatterStep.defaultDevDependenciesForTypescript() : EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(this.eslintVersion); + } +} diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java index da6e6ddd91..1320cac2d8 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2022 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * A {@link FormatterFactory} implementation that corresponds to {@code ...} configuration element. *

- * It defines a formatter for typescript source files. + * It defines formatters for typescript source files. */ public class Typescript extends FormatterFactory { @Override @@ -39,4 +39,8 @@ public String licenseHeaderDelimiter() { public void addTsfmt(Tsfmt tsfmt) { addStepFactory(tsfmt); } + + public void addEslint(EslintTs eslint) { + addStepFactory(eslint); + } } diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 95298db0ea..15ebb0cc0d 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -128,7 +128,7 @@ class EslintInlineConfigTypescriptFormattingStepTest extends NpmFormatterStepCom @Test void formattingUsingInlineXoConfig() throws Exception { - String filedir = "npm/eslint/typescript/standard_rules_xo/"; + String filedir = "npm/eslint/typescript/styleguide/xo/"; String testDir = "formatting_ruleset_xo_inline_config/"; From dd5ae78fcd05300f64a6a85a1823402fe1891045 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 22 Dec 2022 20:04:16 +0100 Subject: [PATCH 20/37] eslint: tests for maven integration into typescript --- .../com/diffplug/spotless/npm/eslint-serve.js | 2 +- .../spotless/maven/javascript/EslintJs.java | 4 +- .../maven/MavenIntegrationHarness.java | 4 +- .../typescript/TypescriptFormatStepTest.java | 107 +++++++++++++++--- 4 files changed, 96 insertions(+), 21 deletions(-) diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index b9f3207d5f..1f1b1fab5a 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -55,7 +55,7 @@ app.post("/eslint/format", async (req, res) => { // LintResult[] // https://eslint.org/docs/latest/developer-guide/nodejs-api#-lintresult-type const results = await eslint.lintText(format_data.file_content, lintTextOptions); if (results.length !== 1) { - res.status(501).send("Error while formatting: Unexpected number of results"); + res.status(501).send("Error while formatting: Unexpected number of results: " + JSON.stringify(results)); return; } const result = results[0]; diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java index 91de2136a1..589e2f3f0c 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java @@ -69,7 +69,7 @@ protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { private void addStyleGuideDevDependencies(Map devDependencies) { if (this.styleGuide != null) { - EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.valueOf(this.styleGuide); + EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.fromNameOrNull(this.styleGuide); validateStyleGuide(styleGuide); devDependencies.putAll(styleGuide.devDependencies()); } @@ -80,7 +80,7 @@ private void validateStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide throw new IllegalArgumentException("StyleGuide '" + this.styleGuide + "' is not supported. Supported style guides: " + supportedStyleGuides()); } if (!isValidStyleGuide(styleGuide)) { - throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide + ". Use one of the following: " + supportedStyleGuides()); + throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide.getPopularStyleGuideName() + ". Use one of the following: " + supportedStyleGuides()); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java index 939940a315..61d9c80a37 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java @@ -136,8 +136,8 @@ protected void writePomWithCppSteps(String... steps) throws IOException { writePom(groupWithSteps("cpp", steps)); } - protected void writePomWithTypescriptSteps(String... steps) throws IOException { - writePom(groupWithSteps("typescript", including("**/*.ts"), steps)); + protected void writePomWithTypescriptSteps(String includes, String... steps) throws IOException { + writePom(groupWithSteps("typescript", including(includes), steps)); } protected void writePomWithSqlSteps(String... steps) throws IOException { diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java index 3b21f972ed..b57a2a97a6 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java @@ -21,84 +21,95 @@ import org.junit.jupiter.api.Test; +import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.maven.MavenIntegrationHarness; import com.diffplug.spotless.maven.MavenRunner.Result; import com.diffplug.spotless.tag.NpmTest; @NpmTest class TypescriptFormatStepTest extends MavenIntegrationHarness { - private void run(String kind) throws IOException, InterruptedException { - String path = prepareRun(kind); + + private static final String TEST_FILE_PATH = "src/main/typescript/test.ts"; + + private void runTsfmt(String kind) throws IOException, InterruptedException { + String path = prepareRunTsfmt(kind); mavenRunner().withArguments("spotless:apply").runNoError(); assertFile(path).sameAsResource("npm/tsfmt/" + kind + "/" + kind + ".clean"); } - private String prepareRun(String kind) throws IOException { - String path = "src/main/typescript/test.ts"; - setFile(path).toResource("npm/tsfmt/" + kind + "/" + kind + ".dirty"); - return path; + private String prepareRunTsfmt(String kind) throws IOException { + setFile(TEST_FILE_PATH).toResource("npm/tsfmt/" + kind + "/" + kind + ".dirty"); + return TEST_FILE_PATH; } - private Result runExpectingError(String kind) throws IOException, InterruptedException { - prepareRun(kind); + private Result runExpectingErrorTsfmt(String kind) throws IOException, InterruptedException { + prepareRunTsfmt(kind); return mavenRunner().withArguments("spotless:apply").runHasError(); } @Test void tslint() throws Exception { writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${basedir}/tslint.json", ""); setFile("tslint.json").toResource("npm/tsfmt/tslint/tslint.json"); - run("tslint"); + runTsfmt("tslint"); } @Test void vscode() throws Exception { writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${basedir}/vscode.json", ""); setFile("vscode.json").toResource("npm/tsfmt/vscode/vscode.json"); - run("vscode"); + runTsfmt("vscode"); } @Test void tsfmt() throws Exception { writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${basedir}/tsfmt.json", ""); setFile("tsfmt.json").toResource("npm/tsfmt/tsfmt/tsfmt.json"); - run("tsfmt"); + runTsfmt("tsfmt"); } @Test void tsfmtInline() throws Exception { writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ", " 1", " true", " ", ""); - run("tsfmt"); + runTsfmt("tsfmt"); } @Test void tsconfig() throws Exception { writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${project.basedir}/tsconfig.json", ""); setFile("tsconfig.json").toResource("npm/tsfmt/tsconfig/tsconfig.json"); - run("tsconfig"); + runTsfmt("tsconfig"); } @Test void testTypescript_2_Configs() throws Exception { + String path = "src/main/typescript/test.ts"; + writePomWithTypescriptSteps( + path, "", " ${basedir}/tslint.json", " ${basedir}/tslint.json", @@ -106,7 +117,6 @@ void testTypescript_2_Configs() throws Exception { setFile("vscode.json").toResource("npm/tsfmt/vscode/vscode.json"); setFile("tsfmt.json").toResource("npm/tsfmt/tsfmt/tsfmt.json"); - String path = "src/main/typescript/test.ts"; setFile(path).toResource("npm/tsfmt/tsfmt/tsfmt.dirty"); Result result = mavenRunner().withArguments("spotless:apply").runHasError(); assertThat(result.output()).contains("must specify exactly one configFile or config"); @@ -120,11 +130,12 @@ void testNpmrcIsAutoPickedUp() throws Exception { "fetch-retry-mintimeout=250", "fetch-retry-maxtimeout=250"); writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${basedir}/tslint.json", ""); setFile("tslint.json").toResource("npm/tsfmt/tslint/tslint.json"); - Result result = runExpectingError("tslint"); + Result result = runExpectingErrorTsfmt("tslint"); assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); } @@ -136,12 +147,76 @@ void testNpmrcIsConfigurativelyPickedUp() throws Exception { "fetch-retry-mintimeout=250", "fetch-retry-maxtimeout=250"); writePomWithTypescriptSteps( + TEST_FILE_PATH, "", " ${basedir}/tslint.json", " ${basedir}/.custom_npmrc", ""); setFile("tslint.json").toResource("npm/tsfmt/tslint/tslint.json"); - Result result = runExpectingError("tslint"); + Result result = runExpectingErrorTsfmt("tslint"); assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); } + + @Test + void eslintConfigFile() throws Exception { + writePomWithTypescriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + ""); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/custom_rules/.eslintrc.js"); + setFile(TEST_FILE_PATH).toResource("npm/eslint/typescript/custom_rules/typescript.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/typescript/custom_rules/typescript.clean"); + } + + @Test + void eslintConfigJs() throws Exception { + final String configJs = ResourceHarness.getTestResource("npm/eslint/typescript/custom_rules/.eslintrc.js") + .replace("module.exports = ", ""); + writePomWithTypescriptSteps( + TEST_FILE_PATH, + "", + " " + configJs + "", + ""); + setFile(TEST_FILE_PATH).toResource("npm/eslint/typescript/custom_rules/typescript.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/typescript/custom_rules/typescript.clean"); + } + + @Test + void eslintStyleguideStandardWithTypescript() throws Exception { + writePomWithTypescriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + " standard-with-typescript", + " ${basedir}/tsconfig.json", + ""); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/tsconfig.json"); + setFile(TEST_FILE_PATH).toResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean"); + } + + @Test + void eslintStyleguideXo() throws Exception { + writePomWithTypescriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + " xo-typescript", + " ${basedir}/tsconfig.json", + ""); + setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/xo/.eslintrc.js"); + setFile("tsconfig.json").toResource("npm/eslint/typescript/styleguide/xo/tsconfig.json"); + setFile(TEST_FILE_PATH).toResource("npm/eslint/typescript/styleguide/xo/typescript.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/typescript/styleguide/xo/typescript.clean"); + } } From 7d2e5adc131e6458bb71e8e9cb694dffce1ae502 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 23 Dec 2022 07:53:57 +0100 Subject: [PATCH 21/37] eslint: adding maven javascript tests --- .../spotless/maven/AbstractSpotlessMojo.java | 2 +- .../maven/javascript/AbstractEslint.java | 92 +++++++++++++++ .../spotless/maven/javascript/EslintJs.java | 65 +--------- .../spotless/maven/typescript/EslintTs.java | 10 +- .../maven/MavenIntegrationHarness.java | 4 + .../javascript/JavascriptFormatStepTest.java | 111 ++++++++++++++++++ 6 files changed, 216 insertions(+), 68 deletions(-) create mode 100644 plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java create mode 100644 plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java index f13f9b200d..647535a590 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java @@ -335,7 +335,7 @@ private FileLocator getFileLocator() { } private List getFormatterFactories() { - return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, antlr4, pom, sql, python, markdown)) + return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, javascript, antlr4, pom, sql, python, markdown)) .filter(Objects::nonNull) .collect(toList()); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java new file mode 100644 index 0000000000..735a25c0c5 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java @@ -0,0 +1,92 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.javascript; + +import java.io.File; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.npm.AbstractNpmFormatterStepFactory; +import com.diffplug.spotless.npm.EslintConfig; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.NpmPathResolver; + +public abstract class AbstractEslint extends AbstractNpmFormatterStepFactory { + + @Parameter + protected String configFile; + + @Parameter + protected String configJs; + + @Parameter + protected String styleGuide; + + @Parameter + protected String eslintVersion; + + @Parameter + protected Map devDependencies; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { + Map devDependencies = new TreeMap<>(); + if (this.devDependencies != null) { + devDependencies.putAll(this.devDependencies); + } else { + Map defaultDependencies = createDefaultDependencies(); + devDependencies.putAll(defaultDependencies); + } + + addStyleGuideDevDependencies(devDependencies); + + File buildDir = buildDir(stepConfig); + File baseDir = baseDir(stepConfig); + NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); + return EslintFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, eslintConfig(stepConfig)); + } + + protected abstract EslintConfig eslintConfig(FormatterStepConfig stepConfig); + + private void addStyleGuideDevDependencies(Map devDependencies) { + if (this.styleGuide != null) { + EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.fromNameOrNull(this.styleGuide); + validateStyleGuide(styleGuide); + devDependencies.putAll(styleGuide.devDependencies()); + } + } + + private void validateStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { + if (styleGuide == null) { + throw new IllegalArgumentException("StyleGuide '" + this.styleGuide + "' is not supported. Supported style guides: " + supportedStyleGuides()); + } + if (!isValidStyleGuide(styleGuide)) { + throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide.getPopularStyleGuideName() + ". Use one of the following: " + supportedStyleGuides()); + } + } + + private String supportedStyleGuides() { + return EslintFormatterStep.PopularStyleGuide.getPopularStyleGuideNames(this::isValidStyleGuide); + } + + protected abstract boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide); + + protected abstract Map createDefaultDependencies(); +} diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java index 589e2f3f0c..f6c7beaff4 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java @@ -15,84 +15,23 @@ */ package com.diffplug.spotless.maven.javascript; -import java.io.File; import java.util.Map; -import java.util.TreeMap; -import org.apache.maven.plugins.annotations.Parameter; - -import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; -import com.diffplug.spotless.maven.npm.AbstractNpmFormatterStepFactory; import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; -import com.diffplug.spotless.npm.NpmPathResolver; - -public class EslintJs extends AbstractNpmFormatterStepFactory { - - @Parameter - private String configFile; - - @Parameter - private String configJs; - - @Parameter - private String styleGuide; - - @Parameter - protected String eslintVersion; - - @Parameter - private Map devDependencies; - - @Override - public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { - Map devDependencies = new TreeMap<>(); - if (this.devDependencies != null) { - devDependencies.putAll(this.devDependencies); - } else { - Map defaultDependencies = createDefaultDependencies(); - devDependencies.putAll(defaultDependencies); - } - addStyleGuideDevDependencies(devDependencies); - - File buildDir = buildDir(stepConfig); - File baseDir = baseDir(stepConfig); - NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); - return EslintFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, eslintConfig(stepConfig)); - } +public class EslintJs extends AbstractEslint { protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { return new EslintConfig(this.configFile != null ? stepConfig.getFileLocator().locateFile(this.configFile) : null, this.configJs); } - private void addStyleGuideDevDependencies(Map devDependencies) { - if (this.styleGuide != null) { - EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.fromNameOrNull(this.styleGuide); - validateStyleGuide(styleGuide); - devDependencies.putAll(styleGuide.devDependencies()); - } - } - - private void validateStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { - if (styleGuide == null) { - throw new IllegalArgumentException("StyleGuide '" + this.styleGuide + "' is not supported. Supported style guides: " + supportedStyleGuides()); - } - if (!isValidStyleGuide(styleGuide)) { - throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide.getPopularStyleGuideName() + ". Use one of the following: " + supportedStyleGuides()); - } - } - - private String supportedStyleGuides() { - return EslintFormatterStep.PopularStyleGuide.getPopularStyleGuideNames(this::isValidStyleGuide); - } - protected boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { return styleGuide.name().startsWith("JS_"); } protected Map createDefaultDependencies() { - return eslintVersion == null ? EslintFormatterStep.defaultDevDependencies() : EslintFormatterStep.defaultDevDependenciesWithEslint(eslintVersion); + return this.eslintVersion == null ? EslintFormatterStep.defaultDevDependencies() : EslintFormatterStep.defaultDevDependenciesWithEslint(this.eslintVersion); } } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java index 0fe834ffde..f69f17f41e 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java @@ -20,20 +20,22 @@ import org.apache.maven.plugins.annotations.Parameter; import com.diffplug.spotless.maven.FormatterStepConfig; -import com.diffplug.spotless.maven.javascript.EslintJs; +import com.diffplug.spotless.maven.javascript.AbstractEslint; import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; import com.diffplug.spotless.npm.EslintTypescriptConfig; -public class EslintTs extends EslintJs { +public class EslintTs extends AbstractEslint { @Parameter private String tsconfigFile; @Override protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { - EslintConfig jsConfig = super.eslintConfig(stepConfig); - return new EslintTypescriptConfig(jsConfig.getEslintConfigPath(), jsConfig.getEslintConfigJs(), tsconfigFile != null ? stepConfig.getFileLocator().locateFile(tsconfigFile) : null); + return new EslintTypescriptConfig( + configFile != null ? stepConfig.getFileLocator().locateFile(configFile) : null, + configJs, + tsconfigFile != null ? stepConfig.getFileLocator().locateFile(tsconfigFile) : null); } @Override diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java index 61d9c80a37..7a8073925d 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java @@ -136,6 +136,10 @@ protected void writePomWithCppSteps(String... steps) throws IOException { writePom(groupWithSteps("cpp", steps)); } + protected void writePomWithJavascriptSteps(String includes, String... steps) throws IOException { + writePom(groupWithSteps("javascript", including(includes), steps)); + } + protected void writePomWithTypescriptSteps(String includes, String... steps) throws IOException { writePom(groupWithSteps("typescript", including(includes), steps)); } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java new file mode 100644 index 0000000000..0f6d0d275f --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2016-2022 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.maven.javascript; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.diffplug.spotless.ResourceHarness; +import com.diffplug.spotless.maven.MavenIntegrationHarness; +import com.diffplug.spotless.maven.MavenRunner.Result; +import com.diffplug.spotless.tag.NpmTest; + +@NpmTest +class JavascriptFormatStepTest extends MavenIntegrationHarness { + + private static final String TEST_FILE_PATH = "src/main/javascript/test.js"; + + @NpmTest + @Nested + class EslintCustomRulesTest extends MavenIntegrationHarness { + + @Test + void eslintConfigFile() throws Exception { + writePomWithJavascriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + ""); + setFile(".eslintrc.js").toResource("npm/eslint/javascript/custom_rules/.eslintrc.js"); + setFile(TEST_FILE_PATH).toResource("npm/eslint/javascript/custom_rules/javascript-es6.dirty"); + + Result result = mavenRunner().withArguments("spotless:apply").runNoError(); + System.out.println(result.output()); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/javascript/custom_rules/javascript-es6.clean"); + } + + @Test + void eslintConfigJs() throws Exception { + final String configJs = ResourceHarness.getTestResource("npm/eslint/javascript/custom_rules/.eslintrc.js") + .replace("module.exports = ", ""); + writePomWithJavascriptSteps( + TEST_FILE_PATH, + "", + " " + configJs + "", + ""); + setFile(TEST_FILE_PATH).toResource("npm/eslint/javascript/custom_rules/javascript-es6.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource("npm/eslint/javascript/custom_rules/javascript-es6.clean"); + } + + } + + @NpmTest + @Nested + class EslintStyleguidesTest extends MavenIntegrationHarness { + + @ParameterizedTest(name = "{index}: eslint js formatting with configFile using styleguide {0}") + @ValueSource(strings = {"airbnb", "google", "standard", "xo"}) + void eslintJsStyleguideUsingConfigFile(String styleGuide) throws Exception { + final String styleGuidePath = "npm/eslint/javascript/styleguide/" + styleGuide; + + writePomWithJavascriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + " " + styleGuide + "", + ""); + setFile(".eslintrc.js").toResource(styleGuidePath + "/.eslintrc.js"); + setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource(styleGuidePath + "/javascript-es6.clean"); + } + + @ParameterizedTest(name = "{index}: eslint js formatting with inline config using styleguide {0}") + @ValueSource(strings = {"airbnb", "google", "standard", "xo"}) + void eslintJsStyleguideUsingInlineConfig(String styleGuide) throws Exception { + final String styleGuidePath = "npm/eslint/javascript/styleguide/" + styleGuide; + + final String escapedInlineConfig = ResourceHarness.getTestResource(styleGuidePath + "/.eslintrc.js") + .replace("<", "<") + .replace(">", ">"); + writePomWithJavascriptSteps( + TEST_FILE_PATH, + "", + " " + escapedInlineConfig + "", + " " + styleGuide + "", + ""); + setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource(styleGuidePath + "/javascript-es6.clean"); + } + } +} From 1d164d3528c8b9763217637665e9a84587a85002 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 23 Dec 2022 08:22:07 +0100 Subject: [PATCH 22/37] eslint: also test custom devDependencies case --- .../javascript/JavascriptFormatStepTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java index 0f6d0d275f..379208b9e4 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java @@ -107,5 +107,29 @@ void eslintJsStyleguideUsingInlineConfig(String styleGuide) throws Exception { mavenRunner().withArguments("spotless:apply").runNoError(); assertFile(TEST_FILE_PATH).sameAsResource(styleGuidePath + "/javascript-es6.clean"); } + + @Test + void provideCustomDependenciesForStyleguideStandard() throws Exception { + final String styleGuidePath = "npm/eslint/javascript/styleguide/standard"; + + writePomWithJavascriptSteps( + TEST_FILE_PATH, + "", + " .eslintrc.js", + " ", + " 8.28.0", + " 17.0.0", + " 2.26.0", + " 15.6.0", + " 6.1.1", + " ", + ""); + setFile(".eslintrc.js").toResource(styleGuidePath + "/.eslintrc.js"); + + setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); + + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(TEST_FILE_PATH).sameAsResource(styleGuidePath + "/javascript-es6.clean"); + } } } From 3f3705395fac64a7f083facd13896e5d26243e38 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 09:55:35 +0100 Subject: [PATCH 23/37] eslint: adding readme (common and plugin-gradle) --- CHANGES.md | 3 + README.md | 2 + plugin-gradle/CHANGES.md | 2 + plugin-gradle/README.md | 120 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a7e3516607..3efdec3b67 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,9 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) + ### Changes * Bump the dev version of Gradle from `7.5.1` to `7.6` ([#1409](https://github.com/diffplug/spotless/pull/1409)) * We also removed the no-longer-required dependency `org.codehaus.groovy:groovy-xml` diff --git a/README.md b/README.md index 605305bd29..ddc3c2391c 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ lib('kotlin.KtfmtStep') +'{{yes}} | {{yes}} lib('kotlin.DiktatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |', lib('markdown.FlexmarkStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |', +lib('npm.EslintFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('npm.PrettierFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |', lib('pom.SortPomStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |', @@ -113,6 +114,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | [`kotlin.DiktatStep`](lib/src/main/java/com/diffplug/spotless/kotlin/DiktatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`markdown.FreshMarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FreshMarkStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: | | [`markdown.FlexmarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FlexmarkStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: | +| [`npm.EslintFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`npm.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: | | [`pom.SortPomStep`](lib/src/main/java/com/diffplug/spotless/pom/SortPomStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: | diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index c5f464069f..d668f69483 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +* Added support for npm-based [ESLint](https://eslint.org/) formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) ### Fixed * Prevent tool configurations from being resolved outside project ([#1447](https://github.com/diffplug/spotless/pull/1447) fixes [#1215](https://github.com/diffplug/spotless/issues/1215)) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 755d21d056..6f498059b9 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -69,7 +69,8 @@ Spotless supports all of Gradle's built-in performance features (incremental bui - [FreshMark](#freshmark) aka markdown - [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter)) - [SQL](#sql) ([dbeaver](#dbeaver), [prettier](#prettier)) - - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier)) + - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [eslint](#eslint--typescript-)) + - [Javascript](#javascript) ([prettier](#prettier), [eslint](#eslint--javascript-)) - [JSON](#json) - Multiple languages - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) @@ -576,6 +577,7 @@ spotless { tsfmt() // has its own section below prettier() // has its own section below + eslint() // has its own section below licenseHeader '/* (C) $YEAR */', '(import|const|declare|export|var) ' // or licenseHeaderFile // note the '(import|const|...' argument - this is a regex which identifies the top @@ -608,6 +610,122 @@ spotless { For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to tsfmt. +### eslint (Typescript) + +[npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* +The auto-discovery of config files (up the file tree) will not work when using eslint within spotless, +hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. + +The configuration is very similar to the [eslint (Javascript)](#eslint--javascript-) configuration. It differs in supported +styleguides and the requirement for a tsconfigFile. + +```gradle +spotless { + typescript { + eslint('8.30.0') // version is optional + eslint(['my-eslint-fork': '1.2.3', 'my-eslint-plugin': '1.2.1']) // can specify exactly which npm packages to use + + eslint() + // optional: use a popular eslint styleguide for typescript + .styleGuide('standard-with-typescript') // or 'xo-typescript' + // configuration is mandatory. Provide inline config or a config file. + // a) inline-configuration + .configJs(''' + { + env: { + browser: true, + es2021: true + }, + extends: 'standard-with-typescript', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + }, + rules: { + } + } + ''') + // b) config file + .configFile('.eslintrc.js') + // recommended: provide a tsconfig.json - especially when using the styleguides + .tsconfigFile('tsconfig.json') + } +} +``` + +**Prerequisite: eslint requires a working NodeJS version** + +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to eslint. + +## Javascript + +- `com.diffplug.gradle.spotless.JavascriptExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.12.0/com/diffplug/gradle/spotless/JavascriptExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java) + +```gradle +spotless { + javascript { + target 'src/**/*.js' // you have to set the target manually + + prettier() // has its own section below + eslint() // has its own section below + + licenseHeader '/* (C) $YEAR */', 'REGEX_TO_DEFINE_TOP_OF_FILE' // or licenseHeaderFile + } +} +``` + +### eslint (Javascript) + +[npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* +The auto-discovery of config files (up the file tree) will not work when using eslint within spotless, +hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. + +The configuration is very similar to the [eslint (Typescript)](#eslint--typescript-) configuration. It differs in supported +styleguides and no requirement for a tsconfig (of course). + +```gradle + +```gradle +spotless { + javascript { + eslint('8.30.0') // version is optional + eslint(['my-eslint-fork': '1.2.3', 'my-eslint-plugin': '1.2.1']) // can specify exactly which npm packages to use + + eslint() + // optional: use a popular eslint styleguide for javascript + .styleGuide('standard') // or 'airbnb', 'google', 'xo' + // configuration is mandatory. Provide inline config or a config file. + // a) inline-configuration + .configJs(''' + { + env: { + browser: true, + es2021: true + }, + extends: 'standard', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, + rules: { + } + } + ''') + // b) config file + .configFile('.eslintrc.js') + } +} +``` + +**Prerequisite: eslint requires a working NodeJS version** + +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to eslint. + ## JSON - `com.diffplug.gradle.spotless.JsonExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.12.1/com/diffplug/gradle/spotless/JsonExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JsonExtension.java) From bb843ce0cf741b809ea8cf022ae5556464789ac5 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 09:56:22 +0100 Subject: [PATCH 24/37] eslint: apply spotless --- .../spotless/npm/BaseNpmRestService.java | 2 +- .../diffplug/spotless/npm/EslintConfig.java | 2 +- .../spotless/npm/EslintFormatterStep.java | 2 +- .../spotless/npm/EslintRestService.java | 2 +- .../spotless/npm/EslintTypescriptConfig.java | 2 +- .../npm/NpmFormatterStepStateBase.java | 2 +- .../com/diffplug/spotless/npm/NpmProcess.java | 2 +- .../spotless/npm/NpmResourceHelper.java | 2 +- .../spotless/npm/PrettierFormatterStep.java | 2 +- .../spotless/npm/PrettierRestService.java | 2 +- .../spotless/npm/TsFmtFormatterStep.java | 2 +- .../spotless/npm/TsFmtRestService.java | 2 +- .../gradle/spotless/FormatExtension.java | 2 +- .../gradle/spotless/JavascriptExtension.java | 2 +- .../gradle/spotless/SpotlessExtension.java | 2 +- .../gradle/spotless/TypescriptExtension.java | 2 +- .../spotless/JavascriptExtensionTest.java | 2 +- .../spotless/PrettierIntegrationTest.java | 2 +- .../spotless/TypescriptExtensionTest.java | 2 +- .../spotless/maven/AbstractSpotlessMojo.java | 2 +- .../spotless/maven/generic/Prettier.java | 2 +- .../maven/javascript/AbstractEslint.java | 2 +- .../spotless/maven/javascript/EslintJs.java | 2 +- .../spotless/maven/javascript/Javascript.java | 2 +- .../npm/AbstractNpmFormatterStepFactory.java | 2 +- .../spotless/maven/typescript/EslintTs.java | 2 +- .../spotless/maven/typescript/Tsfmt.java | 2 +- .../spotless/maven/typescript/Typescript.java | 2 +- .../maven/MavenIntegrationHarness.java | 2 +- .../javascript/JavascriptFormatStepTest.java | 24 +++++++++---------- .../prettier/PrettierFormatStepTest.java | 2 +- .../typescript/TypescriptFormatStepTest.java | 2 +- .../spotless/npm/EslintFormatterStepTest.java | 2 +- .../npm/NpmFormatterStepCommonTests.java | 2 +- .../spotless/npm/TsFmtFormatterStepTest.java | 2 +- 35 files changed, 46 insertions(+), 46 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java index e8582c15ec..a6d93182a1 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/BaseNpmRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java index 4e1848856a..3499b3face 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index 820283aae4..ac3ee05345 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java index 198ee5389c..e17237ecbf 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java index f8c213a0a9..fc45f8bd7b 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 DiffPlug + * Copyright 2022-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java index fc1543fd61..49a166e45c 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java index 28db79b726..e2fd07682c 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java index 48e974eec6..2acf3a180d 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java index 8489644c36..a83fc07674 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java index 2a62823aa0..ccdc189d27 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java index 027af89e23..98d694ec33 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java index f77510c3b6..704d2b47af 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 08e713a921..c72c9339a0 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java index cbe0040a83..862c7566ee 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index 3fe5721b5d..1c65fad310 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index 79ae056bdc..5de1bb1ac3 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java index af203936f3..331e17f28e 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java index 6f55312bd5..bc8ad1148e 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index da9fa03905..598ff7dc14 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java index 647535a590..5ad96ece46 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java index b45f802653..ffe384b4d9 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java index 735a25c0c5..b5fc156a57 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java index f6c7beaff4..f9954ab0c8 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java index db7049ff0d..7ca35dd258 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java index 8bc0f8f715..22ee5e0dba 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java index f69f17f41e..72f3735d97 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 DiffPlug + * Copyright 2022-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java index 6c60c02cc6..685d473072 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java index 1320cac2d8..6f0d7f91b2 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java index 7a8073925d..d9a23cdb74 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationHarness.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java index 379208b9e4..7ebc592add 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,17 +113,17 @@ void provideCustomDependenciesForStyleguideStandard() throws Exception { final String styleGuidePath = "npm/eslint/javascript/styleguide/standard"; writePomWithJavascriptSteps( - TEST_FILE_PATH, - "", - " .eslintrc.js", - " ", - " 8.28.0", - " 17.0.0", - " 2.26.0", - " 15.6.0", - " 6.1.1", - " ", - ""); + TEST_FILE_PATH, + "", + " .eslintrc.js", + " ", + " 8.28.0", + " 17.0.0", + " 2.26.0", + " 15.6.0", + " 6.1.1", + " ", + ""); setFile(".eslintrc.js").toResource(styleGuidePath + "/.eslintrc.js"); setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java index 8fce61b5d8..c0a9267b93 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java index b57a2a97a6..ac925cc9b6 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 15ebb0cc0d..5f53c9effa 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java index 1ed6e9bbe8..dff105108a 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/NpmFormatterStepCommonTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java index 474c664486..a774c7c1ee 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/TsFmtFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 508dd69b8c328cf537809025ca0134e65c7587b0 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 10:02:13 +0100 Subject: [PATCH 25/37] bumping default prettier version --- CHANGES.md | 3 ++- .../spotless/npm/PrettierFormatterStep.java | 2 +- .../config/typescript.configfile.clean | 3 ++- .../prettier/config/typescript.defaults.clean | 3 ++- .../javascript-es5/javascript-es5.clean | 24 +++---------------- .../javascript-es6/javascript-es6.clean | 24 +++---------------- 6 files changed, 13 insertions(+), 46 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3efdec3b67..bf1798d7c2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,8 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added * Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) - ### Changes +* Bump default Version for `prettier` from `2.0.5` to `2.8.1` * Bump the dev version of Gradle from `7.5.1` to `7.6` ([#1409](https://github.com/diffplug/spotless/pull/1409)) * We also removed the no-longer-required dependency `org.codehaus.groovy:groovy-xml` * Breaking changes to Spotless' internal testing infrastructure `testlib` ([#1443](https://github.com/diffplug/spotless/pull/1443)) @@ -22,6 +22,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * `StepHarnessWithFile` now takes a `ResourceHarness` in its constructor to handle the file manipulation parts * Standardized that we test exception *messages*, not types, which will ease the transition to linting later on + ## [2.31.1] - 2023-01-02 ### Fixed * Improve memory usage when using git ratchet ([#1426](https://github.com/diffplug/spotless/pull/1426)) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java index a83fc07674..22a162eb0b 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java @@ -42,7 +42,7 @@ public class PrettierFormatterStep { public static final String NAME = "prettier-format"; public static final Map defaultDevDependencies() { - return defaultDevDependenciesWithPrettier("2.0.5"); + return defaultDevDependenciesWithPrettier("2.8.1"); } public static final Map defaultDevDependenciesWithPrettier(String version) { diff --git a/testlib/src/main/resources/npm/prettier/config/typescript.configfile.clean b/testlib/src/main/resources/npm/prettier/config/typescript.configfile.clean index 0ec76369be..dfe71bf0a9 100644 --- a/testlib/src/main/resources/npm/prettier/config/typescript.configfile.clean +++ b/testlib/src/main/resources/npm/prettier/config/typescript.configfile.clean @@ -1,6 +1,7 @@ export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary extends AbstractController - implements DisposeAware, CallbackAware { + implements DisposeAware, CallbackAware +{ public myValue: string[]; constructor( diff --git a/testlib/src/main/resources/npm/prettier/config/typescript.defaults.clean b/testlib/src/main/resources/npm/prettier/config/typescript.defaults.clean index 0155b905bd..61d8e020d6 100644 --- a/testlib/src/main/resources/npm/prettier/config/typescript.defaults.clean +++ b/testlib/src/main/resources/npm/prettier/config/typescript.defaults.clean @@ -1,6 +1,7 @@ export class MyVeryOwnControllerWithARatherLongNameThatIsNotReallyNecessary extends AbstractController - implements DisposeAware, CallbackAware { + implements DisposeAware, CallbackAware +{ public myValue: string[]; constructor(private myService: Service, name: string, private field: any) { diff --git a/testlib/src/main/resources/npm/prettier/filetypes/javascript-es5/javascript-es5.clean b/testlib/src/main/resources/npm/prettier/filetypes/javascript-es5/javascript-es5.clean index 29002f6b05..0cfac613f1 100644 --- a/testlib/src/main/resources/npm/prettier/filetypes/javascript-es5/javascript-es5.clean +++ b/testlib/src/main/resources/npm/prettier/filetypes/javascript-es5/javascript-es5.clean @@ -1,24 +1,5 @@ var numbers = [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ]; var p = { @@ -32,7 +13,8 @@ var p = { var str = "Hello, world!"; var str2 = str.charAt(3) + str[0]; -var multilinestr = "Hello \ +var multilinestr = + "Hello \ World"; function test(a, b) { return a + b; diff --git a/testlib/src/main/resources/npm/prettier/filetypes/javascript-es6/javascript-es6.clean b/testlib/src/main/resources/npm/prettier/filetypes/javascript-es6/javascript-es6.clean index d4e982d69f..1935faa03c 100644 --- a/testlib/src/main/resources/npm/prettier/filetypes/javascript-es6/javascript-es6.clean +++ b/testlib/src/main/resources/npm/prettier/filetypes/javascript-es6/javascript-es6.clean @@ -1,24 +1,5 @@ var numbers = [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ]; const p = { @@ -32,7 +13,8 @@ const p = { const str = "Hello, world!"; var str2 = str.charAt(3) + str[0]; -var multilinestr = "Hello \ +var multilinestr = + "Hello \ World"; function test(a, b = "world") { let combined = a + b; From 67a0a0510e9f55c911c4bc7f519756c49767d88d Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 12:56:45 +0100 Subject: [PATCH 26/37] eslint: update readme for maven --- plugin-maven/CHANGES.md | 2 + plugin-maven/README.md | 140 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 84e06aa847..f57a8dbbd4 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) ## [2.29.0] - 2023-01-02 ### Added diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 80be3dbc37..b246bcb035 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -57,7 +57,8 @@ user@machine repo % mvn spotless:check - [Sql](#sql) ([dbeaver](#dbeaver)) - [Maven Pom](#maven-pom) ([sortPom](#sortpom)) - [Markdown](#markdown) ([flexmark](#flexmark)) - - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier)) + - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint--typescript-)) + - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint--javascript-)) - Multiple languages - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) - [eclipse web tools platform](#eclipse-web-tools-platform) @@ -660,6 +661,7 @@ Currently, none of the available options can be configured yet. It uses only the + /* (C)$YEAR */ @@ -700,8 +702,144 @@ The auto-discovery of config files (up the file tree) will not work when using t For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to tsfmt. +### ESLint (typescript) + +[npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* +The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, +hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. + +The configuration is very similar to the [ESLint (Javascript)](#eslint--javascript-) configuration. It differs in supported +styleguides and the requirement for a tsconfigFile. + +```xml + + + 8.30.0 + + 8.30.0 + 1.2.1 + + + + eslint + 8.30.0 + + + @eslint/my-plugin-typescript + 0.14.2 + + + + standard-with-typescript + + ${project.basedir}/.eslintrc.js + + { + env: { + browser: true, + es2021: true + }, + extends: 'standard-with-typescript', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + }, + rules: { + } + } + + + ${project.basedir}/tsconfig.json + +``` + +**Prerequisite: ESLint requires a working NodeJS version** + +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to ESLint. + +## Javascript + +[code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/Javascript.java). [available steps](https://github.com/diffplug/spotless/tree/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript). + +```xml + + + + src/**/*.js + + + + + + + /* (C)$YEAR */ + REGEX_TO_DEFINE_TOP_OF_FILE + + + +``` + + +### ESLint (Javascript) + +[npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* +The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, +hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. + +The configuration is very similar to the [ESLint (Typescript)](#eslint--typescript-) configuration. It differs in supported +styleguides and no requirement for a tsconfig (of course). + +```xml + + + 8.30.0 + + 8.30.0 + 1.2.1 + + + + eslint + 8.30.0 + + + @eslint/my-plugin-javascript + 0.14.2 + + + + standard + + ${project.basedir}/.eslintrc.js + + { + env: { + browser: true, + es2021: true + }, + extends: 'standard', + overrides: [ + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, + rules: { + } + } + + +``` + +**Prerequisite: ESLint requires a working NodeJS version** + +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to ESLint. + ## Prettier [homepage](https://prettier.io/). [changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md). [official plugins](https://prettier.io/docs/en/plugins.html#official-plugins). [community plugins](https://prettier.io/docs/en/plugins.html#community-plugins). Prettier is a formatter that can format almost every anything - JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, Less, SCSS, HTML, JSON, GraphQL, Markdown (including GFM and MDX), and YAML. It can format even more [using plugins](https://prettier.io/docs/en/plugins.html) (PHP, Ruby, Swift, XML, Apex, Elm, Java (!!), Kotlin, pgSQL, .properties, solidity, svelte, toml, shellscript, ...). From e07082a5526e370b5355606d90298d70b8d8d32f Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 12:56:56 +0100 Subject: [PATCH 27/37] eslint: unify naming in documentation --- plugin-gradle/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 6f498059b9..edd6c43bb6 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -69,8 +69,8 @@ Spotless supports all of Gradle's built-in performance features (incremental bui - [FreshMark](#freshmark) aka markdown - [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter)) - [SQL](#sql) ([dbeaver](#dbeaver), [prettier](#prettier)) - - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [eslint](#eslint--typescript-)) - - [Javascript](#javascript) ([prettier](#prettier), [eslint](#eslint--javascript-)) + - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint--typescript-)) + - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint--javascript-)) - [JSON](#json) - Multiple languages - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) @@ -610,13 +610,13 @@ spotless { For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to tsfmt. -### eslint (Typescript) +### ESLint (Typescript) [npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* -The auto-discovery of config files (up the file tree) will not work when using eslint within spotless, +The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [eslint (Javascript)](#eslint--javascript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Javascript)](#eslint--javascript-) configuration. It differs in supported styleguides and the requirement for a tsconfigFile. ```gradle @@ -656,9 +656,9 @@ spotless { } ``` -**Prerequisite: eslint requires a working NodeJS version** +**Prerequisite: ESLint requires a working NodeJS version** -For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to eslint. +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to ESLint. ## Javascript @@ -677,13 +677,13 @@ spotless { } ``` -### eslint (Javascript) +### ESLint (Javascript) [npm](https://www.npmjs.com/package/eslint). [changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md). *Please note:* -The auto-discovery of config files (up the file tree) will not work when using eslint within spotless, +The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [eslint (Typescript)](#eslint--typescript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Typescript)](#eslint--typescript-) configuration. It differs in supported styleguides and no requirement for a tsconfig (of course). ```gradle @@ -722,9 +722,9 @@ spotless { } ``` -**Prerequisite: eslint requires a working NodeJS version** +**Prerequisite: ESLint requires a working NodeJS version** -For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to eslint. +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to ESLint. ## JSON From 2943c661623dddfd34c8a532fbdad8f1fdf32469 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 13:41:44 +0100 Subject: [PATCH 28/37] eslint: adapt to api changes in TestResource --- .../spotless/npm/EslintFormatterStepTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 5f53c9effa..a993f43090 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -19,6 +19,8 @@ import java.util.Map; import java.util.TreeMap; +import com.diffplug.spotless.ResourceHarness; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -73,8 +75,8 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { npmPathResolver(), new EslintConfig(eslintRc, null)); - try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { - stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) { + stepHarness.test(dirtyFileFile, ResourceHarness.getTestResource(dirtyFile), ResourceHarness.getTestResource(cleanFile)); } } } @@ -116,8 +118,8 @@ void formattingUsingRulesetsFile(String ruleSetName) throws Exception { npmPathResolver(), new EslintTypescriptConfig(eslintRc, null, tsconfigFile)); - try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { - stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) { + stepHarness.test(dirtyFileFile, ResourceHarness.getTestResource(dirtyFile), ResourceHarness.getTestResource(cleanFile)); } } } @@ -173,8 +175,8 @@ void formattingUsingInlineXoConfig() throws Exception { npmPathResolver(), new EslintTypescriptConfig(null, esLintConfig, tsconfigFile)); - try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { - stepHarness.testResource(dirtyFileFile, dirtyFile, cleanFile); + try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) { + stepHarness.test(dirtyFileFile, ResourceHarness.getTestResource(dirtyFile), ResourceHarness.getTestResource(cleanFile)); } } } From ce21de7bbe2108a04b8b9b1c02a43cf01d889faf Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 13:44:21 +0100 Subject: [PATCH 29/37] eslint: adapt PR number --- CHANGES.md | 2 +- plugin-gradle/CHANGES.md | 2 +- plugin-maven/CHANGES.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bf1798d7c2..95a657e334 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) +* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453)) ### Changes * Bump default Version for `prettier` from `2.0.5` to `2.8.1` * Bump the dev version of Gradle from `7.5.1` to `7.6` ([#1409](https://github.com/diffplug/spotless/pull/1409)) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index d668f69483..7ed1bc7a20 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,7 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* Added support for npm-based [ESLint](https://eslint.org/) formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) +* Added support for npm-based [ESLint](https://eslint.org/) formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453)) ### Fixed * Prevent tool configurations from being resolved outside project ([#1447](https://github.com/diffplug/spotless/pull/1447) fixes [#1215](https://github.com/diffplug/spotless/issues/1215)) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index f57a8dbbd4..a58bb2a30f 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,7 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added -* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1433](https://github.com/diffplug/spotless/pull/1433)) +* Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453)) ## [2.29.0] - 2023-01-02 ### Added From 3a1d43bca5274dd83fe5f7667d85afe14c8d4400 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 13:53:53 +0100 Subject: [PATCH 30/37] eslint: add default version bump to CHANGES --- plugin-gradle/CHANGES.md | 2 ++ plugin-maven/CHANGES.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7ed1bc7a20..bb05d07554 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -7,6 +7,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * Added support for npm-based [ESLint](https://eslint.org/) formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453)) ### Fixed * Prevent tool configurations from being resolved outside project ([#1447](https://github.com/diffplug/spotless/pull/1447) fixes [#1215](https://github.com/diffplug/spotless/issues/1215)) +### Changes +* Bump default Version for `prettier` from `2.0.5` to `2.8.1` ## [6.12.1] - 2023-01-02 ### Fixed diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index a58bb2a30f..4f5d30cb74 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,6 +5,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added * Added support for npm-based [ESLint](https://eslint.org/)-formatter for javascript and typescript ([#1453](https://github.com/diffplug/spotless/pull/1453)) +### Changes +* Bump default Version for `prettier` from `2.0.5` to `2.8.1` ## [2.29.0] - 2023-01-02 ### Added From b4f76b4f8cca59803ef720b651c39d00e88e0617 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 14:04:21 +0100 Subject: [PATCH 31/37] eslint: adapt maven code to match documentation --- .../spotless/maven/generic/Prettier.java | 16 +-------------- .../maven/javascript/AbstractEslint.java | 17 ++++++++++++++++ .../npm/AbstractNpmFormatterStepFactory.java | 20 +++++++++++++++++++ .../spotless/npm/EslintFormatterStepTest.java | 3 +-- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java index ffe384b4d9..01ce2a5394 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java @@ -61,7 +61,7 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { if (prettierVersion != null && !prettierVersion.isEmpty()) { this.devDependencies = PrettierFormatterStep.defaultDevDependenciesWithPrettier(prettierVersion); } else if (devDependencyProperties != null) { - this.devDependencies = dependencyPropertiesAsMap(); + this.devDependencies = propertiesAsMap(this.devDependencyProperties); } // process config file or inline config @@ -100,20 +100,6 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, prettierConfig); } - private boolean moreThanOneNonNull(Object... objects) { - return Arrays.stream(objects) - .filter(Objects::nonNull) - .filter(o -> !(o instanceof String) || !((String) o).isEmpty()) // if it is a string, it should not be empty - .count() > 1; - } - - private Map dependencyPropertiesAsMap() { - return this.devDependencyProperties.stringPropertyNames() - .stream() - .map(name -> new AbstractMap.SimpleEntry<>(name, this.devDependencyProperties.getProperty(name))) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - private static IllegalArgumentException onlyOneConfig() { return new IllegalArgumentException(ERROR_MESSAGE_ONLY_ONE_CONFIG); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java index b5fc156a57..1829f91b4e 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java @@ -17,6 +17,7 @@ import java.io.File; import java.util.Map; +import java.util.Properties; import java.util.TreeMap; import org.apache.maven.plugins.annotations.Parameter; @@ -30,6 +31,8 @@ public abstract class AbstractEslint extends AbstractNpmFormatterStepFactory { + public static final String ERROR_MESSAGE_ONLY_ONE_CONFIG = "must specify exactly one eslintVersion, devDependencies or devDependencyProperties"; + @Parameter protected String configFile; @@ -45,11 +48,21 @@ public abstract class AbstractEslint extends AbstractNpmFormatterStepFactory { @Parameter protected Map devDependencies; + @Parameter + protected Properties devDependencyProperties; + @Override public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { + // check if config is only setup in one way + if (moreThanOneNonNull(this.eslintVersion, this.devDependencies, this.devDependencyProperties)) { + throw onlyOneConfig(); + } + Map devDependencies = new TreeMap<>(); if (this.devDependencies != null) { devDependencies.putAll(this.devDependencies); + } else if (this.devDependencyProperties != null) { + devDependencies.putAll(propertiesAsMap(this.devDependencyProperties)); } else { Map defaultDependencies = createDefaultDependencies(); devDependencies.putAll(defaultDependencies); @@ -63,6 +76,10 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { return EslintFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, npmPathResolver, eslintConfig(stepConfig)); } + private static IllegalArgumentException onlyOneConfig() { + return new IllegalArgumentException(ERROR_MESSAGE_ONLY_ONE_CONFIG); + } + protected abstract EslintConfig eslintConfig(FormatterStepConfig stepConfig); private void addStyleGuideDevDependencies(Map devDependencies) { diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java index 22ee5e0dba..5467f4c3bc 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/npm/AbstractNpmFormatterStepFactory.java @@ -16,6 +16,12 @@ package com.diffplug.spotless.maven.npm; import java.io.File; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.stream.Collectors; import org.apache.maven.plugins.annotations.Parameter; @@ -52,4 +58,18 @@ protected File baseDir(FormatterStepConfig stepConfig) { protected NpmPathResolver npmPathResolver(FormatterStepConfig stepConfig) { return new NpmPathResolver(npm(stepConfig), npmrc(stepConfig), baseDir(stepConfig)); } + + protected boolean moreThanOneNonNull(Object... objects) { + return Arrays.stream(objects) + .filter(Objects::nonNull) + .filter(o -> !(o instanceof String) || !((String) o).isEmpty()) // if it is a string, it should not be empty + .count() > 1; + } + + protected Map propertiesAsMap(Properties devDependencyProperties) { + return devDependencyProperties.stringPropertyNames() + .stream() + .map(name -> new AbstractMap.SimpleEntry<>(name, devDependencyProperties.getProperty(name))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } } diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index a993f43090..19f6e8c223 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -19,8 +19,6 @@ import java.util.Map; import java.util.TreeMap; -import com.diffplug.spotless.ResourceHarness; - import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -28,6 +26,7 @@ import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.StepHarnessWithFile; import com.diffplug.spotless.TestProvisioner; import com.diffplug.spotless.tag.NpmTest; From 45983197c3098f163dc6abf5c751684b21cbc6cc Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 4 Jan 2023 14:04:31 +0100 Subject: [PATCH 32/37] eslint: spotlessApply --- .../src/main/java/com/diffplug/spotless/extra/GitRatchet.java | 2 +- plugin-gradle/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java index 31f2f597f9..037d4d847b 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 DiffPlug + * Copyright 2020-2023 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index edd6c43bb6..1e34cdc88a 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -662,7 +662,7 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n ## Javascript -- `com.diffplug.gradle.spotless.JavascriptExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.12.0/com/diffplug/gradle/spotless/JavascriptExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java) +- `com.diffplug.gradle.spotless.JavascriptExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.12.1/com/diffplug/gradle/spotless/JavascriptExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java) ```gradle spotless { From 3540242e98591391c2e766656d5e9ed53f6ca746 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 5 Jan 2023 07:00:51 +0100 Subject: [PATCH 33/37] eslint: reduce log-noise, cleanup error codes --- .../com/diffplug/spotless/npm/NpmProcess.java | 6 +++++- .../com/diffplug/spotless/npm/common-serve.js | 13 ++++++++++--- .../com/diffplug/spotless/npm/eslint-serve.js | 18 +++++++++--------- .../diffplug/spotless/npm/prettier-serve.js | 2 +- .../com/diffplug/spotless/npm/tsfmt-serve.js | 2 +- .../npm/PrettierFormatterStepTest.java | 2 +- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java index e2fd07682c..8be94a9ea3 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmProcess.java @@ -34,7 +34,11 @@ class NpmProcess { } void install() { - npmAwait("install", "--no-audit", "--no-package-lock", "--prefer-offline"); + npmAwait("install", + "--no-audit", + "--no-package-lock", + "--no-fund", + "--prefer-offline"); } Process start() { diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js index 3e031cedea..c1c9d62757 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js @@ -1,4 +1,5 @@ -// this file will be glued to the top of the specific xy-server.js file +// this file will be glued to the top of the specific xy-serve.js file +const debug_serve = false; // set to true for debug log output in node process const GracefulShutdownManager = require("@moebius/http-graceful-shutdown").GracefulShutdownManager; const express = require("express"); const app = express(); @@ -7,8 +8,14 @@ app.use(express.json({ limit: "50mb" })); const fs = require("fs"); +function debugLog() { + if (debug_serve) { + console.log.apply(this, arguments) + } +} + var listener = app.listen(0, "127.0.0.1", () => { - console.log("Server running on port " + listener.address().port); + debugLog("Server running on port " + listener.address().port); fs.writeFile("server.port.tmp", "" + listener.address().port, function(err) { if (err) { return console.log(err); @@ -26,7 +33,7 @@ const shutdownManager = new GracefulShutdownManager(listener); app.post("/shutdown", (req, res) => { res.status(200).send("Shutting down"); setTimeout(function() { - shutdownManager.terminate(() => console.log("graceful shutdown finished.")); + shutdownManager.terminate(() => debugLog("graceful shutdown finished.")); }, 200); }); diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js index 1f1b1fab5a..8b60e56dc8 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/eslint-serve.js @@ -9,14 +9,14 @@ app.post("/eslint/format", async (req, res) => { const ESLintOverrideConfigFile = format_data.eslint_override_config_file; if (!ESLintOverrideConfig && !ESLintOverrideConfigFile) { - res.status(501).send("Error while formatting: No config provided"); + res.status(400).send("Error while formatting: No config provided"); return; } const filePath = format_data.file_path; if (!filePath) { - res.status(501).send("Error while formatting: No file path provided"); + res.status(400).send("Error while formatting: No file path provided"); return; } @@ -41,8 +41,8 @@ app.post("/eslint/format", async (req, res) => { eval("ESLintOptions.overrideConfig = " + ESLintOverrideConfig); } - console.log("using options: " + JSON.stringify(ESLintOptions)); - console.log("format input: ", format_data.file_content); + debugLog("using options: " + JSON.stringify(ESLintOptions)); + debugLog("format input: ", format_data.file_content); const eslint = new ESLint(ESLintOptions); @@ -50,18 +50,18 @@ app.post("/eslint/format", async (req, res) => { const lintTextOptions = { filePath: filePath, } - console.log("lintTextOptions", lintTextOptions); + debugLog("lintTextOptions", lintTextOptions); // LintResult[] // https://eslint.org/docs/latest/developer-guide/nodejs-api#-lintresult-type const results = await eslint.lintText(format_data.file_content, lintTextOptions); if (results.length !== 1) { - res.status(501).send("Error while formatting: Unexpected number of results: " + JSON.stringify(results)); + res.status(500).send("Error while formatting: Unexpected number of results: " + JSON.stringify(results)); return; } const result = results[0]; - console.log("result: " + JSON.stringify(result)); + debugLog("result: " + JSON.stringify(result)); if (result.fatalErrorCount && result.fatalErrorCount > 0) { - res.status(501).send("Fatal error while formatting: " + JSON.stringify(result.messages)); + res.status(500).send("Fatal error while formatting: " + JSON.stringify(result.messages)); return; } const formatted = result.output || result.source || format_data.file_content; @@ -69,6 +69,6 @@ app.post("/eslint/format", async (req, res) => { res.send(formatted); } catch (err) { console.log("error", err); - res.status(501).send("Error while formatting: " + err); + res.status(500).send("Error while formatting: " + err); } }); diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js index d4ce13bbbc..b60daaaa77 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js @@ -27,7 +27,7 @@ app.post("/prettier/format", (req, res) => { try { formatted_file_content = prettier.format(format_data.file_content, format_data.config_options); } catch(err) { - res.status(501).send("Error while formatting: " + err); + res.status(500).send("Error while formatting: " + err); return; } res.set("Content-Type", "text/plain"); diff --git a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js index 8ec25565ff..b9f20a1472 100644 --- a/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js +++ b/lib/src/main/resources/com/diffplug/spotless/npm/tsfmt-serve.js @@ -25,6 +25,6 @@ app.post("/tsfmt/format", (req, res) => { res.set("Content-Type", "text/plain"); res.send(resultMap.dest); }).catch(reason => { - res.status(501).send(reason); + res.status(500).send(reason); }); }); diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java index 9db218efb5..acc756fa31 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java @@ -116,7 +116,7 @@ void verifyPrettierErrorMessageIsRelayed() throws Exception { new PrettierConfig(null, ImmutableMap.of("parser", "postcss"))); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) { stepHarness.testResourceExceptionMsg("npm/prettier/filetypes/scss/scss.dirty").isEqualTo( - "Unexpected response status code at /prettier/format [HTTP 501] -- (Error while formatting: Error: Couldn't resolve parser \"postcss\")"); + "Unexpected response status code at /prettier/format [HTTP 500] -- (Error while formatting: Error: Couldn't resolve parser \"postcss\")"); } } } From 98c023b8f972ac37574f0bdae5d407bc502b50f0 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Thu, 5 Jan 2023 09:35:10 +0100 Subject: [PATCH 34/37] eslint: spotbugs fix --- .../java/com/diffplug/spotless/npm/EslintTypescriptConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java index fc45f8bd7b..ea3e2046b3 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java @@ -27,6 +27,8 @@ public class EslintTypescriptConfig extends EslintConfig { + private static final long serialVersionUID = -126864670181617006L; + @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") @Nullable private final transient File typescriptConfigPath; From d4d91cb97975a8a29e7a067a3c64af15460b7bef Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 10 Jan 2023 08:35:14 +0100 Subject: [PATCH 35/37] eslint: remove direct api support for styleguides --- .../spotless/npm/EslintFormatterStep.java | 93 +------------- plugin-gradle/README.md | 4 - .../gradle/spotless/JavascriptExtension.java | 25 +--- .../gradle/spotless/TypescriptExtension.java | 15 +-- .../spotless/JavascriptExtensionTest.java | 41 ++---- .../spotless/TypescriptExtensionTest.java | 32 ++--- plugin-maven/README.md | 4 - .../maven/javascript/AbstractEslint.java | 28 ---- .../spotless/maven/javascript/EslintJs.java | 4 - .../spotless/maven/typescript/EslintTs.java | 5 - .../javascript/JavascriptFormatStepTest.java | 10 +- .../typescript/TypescriptFormatStepTest.java | 10 +- .../spotless/npm/EslintStyleGuide.java | 120 ++++++++++++++++++ .../spotless/npm/EslintFormatterStepTest.java | 21 +-- 14 files changed, 170 insertions(+), 242 deletions(-) create mode 100644 testlib/src/main/java/com/diffplug/spotless/npm/EslintStyleGuide.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java index ac3ee05345..81c5b6ce78 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/EslintFormatterStep.java @@ -20,14 +20,12 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.TreeMap; -import java.util.function.Predicate; import javax.annotation.Nonnull; @@ -47,96 +45,7 @@ public class EslintFormatterStep { public static final String NAME = "eslint-format"; - public static final String DEFAULT_ESLINT_VERSION = "^8.30.0"; - - public enum PopularStyleGuide { - TS_STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-standard-with-typescript", "^24.0.0"); - dependencies.put("eslint-plugin-import", "^2.26.0"); - dependencies.put("eslint-plugin-n", "^15.6.0"); - dependencies.put("eslint-plugin-promise", "^6.1.1"); - return dependencies; - } - }, - TS_XO_TYPESCRIPT("xo-typescript") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-xo", "^0.43.1"); - dependencies.put("eslint-config-xo-typescript", "^0.55.1"); - return dependencies; - } - }, - JS_AIRBNB("airbnb") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-airbnb-base", "^15.0.0"); - dependencies.put("eslint-plugin-import", "^2.26.0"); - return dependencies; - } - }, - JS_GOOGLE("google") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-google", "^0.14.0"); - return dependencies; - } - }, - JS_STANDARD("standard") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-standard", "^17.0.0"); - dependencies.put("eslint-plugin-import", "^2.26.0"); - dependencies.put("eslint-plugin-n", "^15.6.0"); - dependencies.put("eslint-plugin-promise", "^6.1.1"); - return dependencies; - } - }, - JS_XO("xo") { - @Override - public @Nonnull Map devDependencies() { - Map dependencies = new LinkedHashMap<>(); - dependencies.put("eslint-config-xo", "^0.43.1"); - return dependencies; - } - }; - - private final String popularStyleGuideName; - - PopularStyleGuide(String popularStyleGuideName) { - this.popularStyleGuideName = popularStyleGuideName; - } - - public String getPopularStyleGuideName() { - return popularStyleGuideName; - } - - public abstract @Nonnull Map devDependencies(); - - public static PopularStyleGuide fromNameOrNull(String popularStyleGuideName) { - for (PopularStyleGuide popularStyleGuide : PopularStyleGuide.values()) { - if (popularStyleGuide.popularStyleGuideName.equals(popularStyleGuideName)) { - return popularStyleGuide; - } - } - return null; - } - - public static String getPopularStyleGuideNames(Predicate filter) { - // collect matching style guide names using stream - return Arrays.stream(PopularStyleGuide.values()) - .filter(filter) - .map(PopularStyleGuide::getPopularStyleGuideName) - .sorted() - .collect(java.util.stream.Collectors.joining(", ")); - } - } + public static final String DEFAULT_ESLINT_VERSION = "^8.31.0"; public static Map defaultDevDependenciesForTypescript() { return defaultDevDependenciesTypescriptWithEslint(DEFAULT_ESLINT_VERSION); diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 1ed2b942b5..8a06351bed 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -634,8 +634,6 @@ spotless { eslint(['my-eslint-fork': '1.2.3', 'my-eslint-plugin': '1.2.1']) // can specify exactly which npm packages to use eslint() - // optional: use a popular eslint styleguide for typescript - .styleGuide('standard-with-typescript') // or 'xo-typescript' // configuration is mandatory. Provide inline config or a config file. // a) inline-configuration .configJs(''' @@ -703,8 +701,6 @@ spotless { eslint(['my-eslint-fork': '1.2.3', 'my-eslint-plugin': '1.2.1']) // can specify exactly which npm packages to use eslint() - // optional: use a popular eslint styleguide for javascript - .styleGuide('standard') // or 'airbnb', 'google', 'xo' // configuration is mandatory. Provide inline config or a config file. // a) inline-configuration .configJs(''' diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java index 862c7566ee..dd476b6a69 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavascriptExtension.java @@ -31,7 +31,6 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; -import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; @@ -73,7 +72,7 @@ public EslintBaseConfig(Project project, Consumer replaceStep, Ma } @SuppressWarnings("unchecked") - public T devDependencies(Map devDependencies) { + protected T devDependencies(Map devDependencies) { this.devDependencies.putAll(devDependencies); replaceStep(); return (T) this; @@ -92,17 +91,6 @@ public T configFile(Object configFilePath) { replaceStep(); return (T) this; } - - @SuppressWarnings("unchecked") - public T styleGuide(String styleGuide) { - PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide); - - verifyStyleGuideIsSupported(styleGuide, popularStyleGuide); - assert popularStyleGuide != null; - return devDependencies(popularStyleGuide.devDependencies()); - } - - protected abstract void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide); } public class JavascriptEslintConfig extends EslintBaseConfig { @@ -123,17 +111,6 @@ public FormatterStep createStep() { eslintConfig()); } - @Override - protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { - if (!isJsStyleGuide(popularStyleGuide)) { - throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known javascript style guides: " + PopularStyleGuide.getPopularStyleGuideNames(this::isJsStyleGuide)); - } - } - - private boolean isJsStyleGuide(PopularStyleGuide popularStyleGuide) { - return popularStyleGuide != null && popularStyleGuide.name().startsWith("JS_"); - } - protected EslintConfig eslintConfig() { return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs); } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java index 5de1bb1ac3..0824caa769 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/TypescriptExtension.java @@ -27,10 +27,10 @@ import org.gradle.api.Project; +import com.diffplug.gradle.spotless.JavascriptExtension.EslintBaseConfig; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.npm.EslintConfig; import com.diffplug.spotless.npm.EslintFormatterStep; -import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide; import com.diffplug.spotless.npm.EslintTypescriptConfig; import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; @@ -189,7 +189,7 @@ public TypescriptEslintConfig eslint(Map devDependencies) { return eslint; } - public class TypescriptEslintConfig extends JavascriptExtension.EslintBaseConfig { + public class TypescriptEslintConfig extends EslintBaseConfig { @Nullable Object typescriptConfigFilePath = null; @@ -204,17 +204,6 @@ public TypescriptEslintConfig tsconfigFile(Object path) { return this; } - @Override - protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) { - if (!isTsStyleGuide(popularStyleGuide)) { - throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known typescript style guides: " + PopularStyleGuide.getPopularStyleGuideNames(this::isTsStyleGuide)); - } - } - - private boolean isTsStyleGuide(PopularStyleGuide popularStyleGuide) { - return popularStyleGuide != null && popularStyleGuide.name().startsWith("TS_"); - } - public FormatterStep createStep() { final Project project = getProject(); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java index 331e17f28e..26354b93be 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/JavascriptExtensionTest.java @@ -24,11 +24,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintStyleGuide; import com.diffplug.spotless.tag.NpmTest; @NpmTest class JavascriptExtensionTest extends GradleIntegrationHarness { + private static String styleGuideMapString(String styleGuideName) { + return EslintStyleGuide.fromNameOrNull(styleGuideName).asGradleMapStringMergedWith(EslintFormatterStep.defaultDevDependencies()); + } + @NpmTest @Nested class EslintGeneralJavascriptTests extends GradleIntegrationHarness { @@ -43,7 +49,7 @@ void supportsEslintFormattingForJavascript() throws IOException { "spotless {", " javascript {", " target 'test.js'", - " eslint().configFile('.eslintrc.js').styleGuide('standard')", + " eslint(" + styleGuideMapString("standard") + ").configFile('.eslintrc.js')", " }", "}"); setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); @@ -51,28 +57,9 @@ void supportsEslintFormattingForJavascript() throws IOException { assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); } - @Test - void eslintDoesNotAllowToUseTsStyleGuideForJavascript() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); - setFile("build.gradle").toLines( - "plugins {", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " javascript {", - " target 'test.js'", - " eslint().configFile('.eslintrc.js').styleGuide('xo-typescript')", - " }", - "}"); - setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); - final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); - Assertions.assertThat(spotlessApply.getOutput()).contains("Unknown style guide: xo-typescript"); - } - @Test void eslintAllowsToSpecifyEslintVersionForJavascript() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/standard/.eslintrc.js"); + setFile(".eslintrc.js").toResource("npm/eslint/javascript/custom_rules/.eslintrc.js"); setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -81,12 +68,12 @@ void eslintAllowsToSpecifyEslintVersionForJavascript() throws IOException { "spotless {", " javascript {", " target 'test.js'", - " eslint('8.28.0').configFile('.eslintrc.js').styleGuide('standard')", + " eslint('8.28.0').configFile('.eslintrc.js')", " }", "}"); - setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); + setFile("test.js").toResource("npm/eslint/javascript/custom_rules/javascript-es6.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); - assertFile("test.js").sameAsResource("npm/eslint/javascript/styleguide/standard/javascript-es6.clean"); + assertFile("test.js").sameAsResource("npm/eslint/javascript/custom_rules/javascript-es6.clean"); } @Test @@ -115,7 +102,7 @@ void esllintAllowsToSpecifyInlineConfig() throws IOException { "spotless {", " javascript {", " target 'test.js'", - " eslint().configJs('''" + eslintConfigJs + "''').styleGuide('standard')", + " eslint(" + styleGuideMapString("standard") + ").configJs('''" + eslintConfigJs + "''')", " }", "}"); setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); @@ -134,7 +121,7 @@ void eslintRequiresAnExplicitEslintConfig() throws IOException { "spotless {", " javascript {", " target 'test.js'", - " eslint().styleGuide('standard')", + " eslint(" + styleGuideMapString("standard") + ")", " }", "}"); setFile("test.js").toResource("npm/eslint/javascript/styleguide/standard/javascript-es6.dirty"); @@ -187,7 +174,7 @@ void formattingUsingStyleguide(String styleguide) throws Exception { "spotless {", " javascript {", " target 'test.js'", - " eslint().configFile('.eslintrc.js').styleGuide('" + styleguide + "')", + " eslint(" + styleGuideMapString(styleguide) + ").configFile('.eslintrc.js')", " }", "}"); setFile("test.js").toResource(styleguidePath + "javascript-es6.dirty"); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java index 598ff7dc14..9b4ec8372e 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/TypescriptExtensionTest.java @@ -17,14 +17,19 @@ import java.io.IOException; -import org.assertj.core.api.Assertions; -import org.gradle.testkit.runner.BuildResult; import org.junit.jupiter.api.Test; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintStyleGuide; import com.diffplug.spotless.tag.NpmTest; @NpmTest class TypescriptExtensionTest extends GradleIntegrationHarness { + + private static String styleGuideMapString(String styleGuideName) { + return EslintStyleGuide.fromNameOrNull(styleGuideName).asGradleMapStringMergedWith(EslintFormatterStep.defaultDevDependencies()); + } + @Test void allowToSpecifyFormatterVersion() throws IOException { setFile("build.gradle").toLines( @@ -175,7 +180,7 @@ void useEslintXoStandardRules() throws IOException { "spotless {", " typescript {", " target 'test.ts'", - " eslint().styleGuide('xo-typescript').configFile('.eslintrc.js')", + " eslint(" + styleGuideMapString("xo-typescript") + ").configFile('.eslintrc.js')", " }", "}"); setFile("test.ts").toResource("npm/eslint/typescript/styleguide/xo/typescript.dirty"); @@ -195,30 +200,11 @@ void useEslintStandardWithTypescriptRules() throws IOException { "spotless {", " typescript {", " target 'test.ts'", - " eslint().styleGuide('standard-with-typescript').configFile('.eslintrc.js')", + " eslint(" + styleGuideMapString("standard-with-typescript") + ").configFile('.eslintrc.js')", " }", "}"); setFile("test.ts").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.dirty"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); assertFile("test.ts").sameAsResource("npm/eslint/typescript/styleguide/standard_with_typescript/typescript.clean"); } - - @Test - void useEslintForTypescriptDoesNotAllowUsingJsStyleguide() throws IOException { - setFile(".eslintrc.js").toResource("npm/eslint/javascript/styleguide/airbnb/.eslintrc.js"); - setFile("build.gradle").toLines( - "plugins {", - " id 'com.diffplug.spotless'", - "}", - "repositories { mavenCentral() }", - "spotless {", - " typescript {", - " target 'test.ts'", - " eslint().styleGuide('airbnb').configFile('.eslintrc.js')", - " }", - "}"); - setFile("test.js").toResource("npm/eslint/javascript/styleguide/airbnb/javascript-es6.dirty"); - BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); - Assertions.assertThat(spotlessApply.getOutput()).contains("Unknown style guide: airbnb"); - } } diff --git a/plugin-maven/README.md b/plugin-maven/README.md index dcf9df45d9..2a2aa31010 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -745,8 +745,6 @@ styleguides and the requirement for a tsconfigFile. 0.14.2 - - standard-with-typescript ${project.basedir}/.eslintrc.js @@ -826,8 +824,6 @@ styleguides and no requirement for a tsconfig (of course). 0.14.2 - - standard ${project.basedir}/.eslintrc.js diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java index 1829f91b4e..b06d3079e7 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/AbstractEslint.java @@ -39,9 +39,6 @@ public abstract class AbstractEslint extends AbstractNpmFormatterStepFactory { @Parameter protected String configJs; - @Parameter - protected String styleGuide; - @Parameter protected String eslintVersion; @@ -68,8 +65,6 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { devDependencies.putAll(defaultDependencies); } - addStyleGuideDevDependencies(devDependencies); - File buildDir = buildDir(stepConfig); File baseDir = baseDir(stepConfig); NpmPathResolver npmPathResolver = npmPathResolver(stepConfig); @@ -82,28 +77,5 @@ private static IllegalArgumentException onlyOneConfig() { protected abstract EslintConfig eslintConfig(FormatterStepConfig stepConfig); - private void addStyleGuideDevDependencies(Map devDependencies) { - if (this.styleGuide != null) { - EslintFormatterStep.PopularStyleGuide styleGuide = EslintFormatterStep.PopularStyleGuide.fromNameOrNull(this.styleGuide); - validateStyleGuide(styleGuide); - devDependencies.putAll(styleGuide.devDependencies()); - } - } - - private void validateStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { - if (styleGuide == null) { - throw new IllegalArgumentException("StyleGuide '" + this.styleGuide + "' is not supported. Supported style guides: " + supportedStyleGuides()); - } - if (!isValidStyleGuide(styleGuide)) { - throw new IllegalArgumentException("StyleGuide must be of correct type but is: " + styleGuide.getPopularStyleGuideName() + ". Use one of the following: " + supportedStyleGuides()); - } - } - - private String supportedStyleGuides() { - return EslintFormatterStep.PopularStyleGuide.getPopularStyleGuideNames(this::isValidStyleGuide); - } - - protected abstract boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide); - protected abstract Map createDefaultDependencies(); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java index f9954ab0c8..483d38ae1e 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/javascript/EslintJs.java @@ -27,10 +27,6 @@ protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { return new EslintConfig(this.configFile != null ? stepConfig.getFileLocator().locateFile(this.configFile) : null, this.configJs); } - protected boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { - return styleGuide.name().startsWith("JS_"); - } - protected Map createDefaultDependencies() { return this.eslintVersion == null ? EslintFormatterStep.defaultDevDependencies() : EslintFormatterStep.defaultDevDependenciesWithEslint(this.eslintVersion); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java index 72f3735d97..dc43185dcd 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/EslintTs.java @@ -38,11 +38,6 @@ protected EslintConfig eslintConfig(FormatterStepConfig stepConfig) { tsconfigFile != null ? stepConfig.getFileLocator().locateFile(tsconfigFile) : null); } - @Override - protected boolean isValidStyleGuide(EslintFormatterStep.PopularStyleGuide styleGuide) { - return styleGuide.name().startsWith("TS_"); - } - @Override protected Map createDefaultDependencies() { return this.eslintVersion == null ? EslintFormatterStep.defaultDevDependenciesForTypescript() : EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(this.eslintVersion); diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java index 7ebc592add..5c8a5f04b3 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/javascript/JavascriptFormatStepTest.java @@ -23,6 +23,8 @@ import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.maven.MavenIntegrationHarness; import com.diffplug.spotless.maven.MavenRunner.Result; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintStyleGuide; import com.diffplug.spotless.tag.NpmTest; @NpmTest @@ -30,6 +32,10 @@ class JavascriptFormatStepTest extends MavenIntegrationHarness { private static final String TEST_FILE_PATH = "src/main/javascript/test.js"; + private static String styleGuideDevDependenciesString(String styleGuideName) { + return EslintStyleGuide.fromNameOrNull(styleGuideName).asMavenXmlStringMergedWith(EslintFormatterStep.defaultDevDependencies()); + } + @NpmTest @Nested class EslintCustomRulesTest extends MavenIntegrationHarness { @@ -79,7 +85,7 @@ void eslintJsStyleguideUsingConfigFile(String styleGuide) throws Exception { TEST_FILE_PATH, "", " .eslintrc.js", - " " + styleGuide + "", + " " + styleGuideDevDependenciesString(styleGuide), ""); setFile(".eslintrc.js").toResource(styleGuidePath + "/.eslintrc.js"); setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); @@ -100,7 +106,7 @@ void eslintJsStyleguideUsingInlineConfig(String styleGuide) throws Exception { TEST_FILE_PATH, "", " " + escapedInlineConfig + "", - " " + styleGuide + "", + " " + styleGuideDevDependenciesString(styleGuide), ""); setFile(TEST_FILE_PATH).toResource(styleGuidePath + "/javascript-es6.dirty"); diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java index ac925cc9b6..95097fe467 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java @@ -24,6 +24,8 @@ import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.maven.MavenIntegrationHarness; import com.diffplug.spotless.maven.MavenRunner.Result; +import com.diffplug.spotless.npm.EslintFormatterStep; +import com.diffplug.spotless.npm.EslintStyleGuide; import com.diffplug.spotless.tag.NpmTest; @NpmTest @@ -31,6 +33,10 @@ class TypescriptFormatStepTest extends MavenIntegrationHarness { private static final String TEST_FILE_PATH = "src/main/typescript/test.ts"; + private static String styleGuideDevDependenciesString(String styleGuideName) { + return EslintStyleGuide.fromNameOrNull(styleGuideName).asMavenXmlStringMergedWith(EslintFormatterStep.defaultDevDependencies()); + } + private void runTsfmt(String kind) throws IOException, InterruptedException { String path = prepareRunTsfmt(kind); mavenRunner().withArguments("spotless:apply").runNoError(); @@ -192,7 +198,7 @@ void eslintStyleguideStandardWithTypescript() throws Exception { TEST_FILE_PATH, "", " .eslintrc.js", - " standard-with-typescript", + " " + styleGuideDevDependenciesString("standard-with-typescript"), " ${basedir}/tsconfig.json", ""); setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/standard_with_typescript/.eslintrc.js"); @@ -209,7 +215,7 @@ void eslintStyleguideXo() throws Exception { TEST_FILE_PATH, "", " .eslintrc.js", - " xo-typescript", + " " + styleGuideDevDependenciesString("xo-typescript"), " ${basedir}/tsconfig.json", ""); setFile(".eslintrc.js").toResource("npm/eslint/typescript/styleguide/xo/.eslintrc.js"); diff --git a/testlib/src/main/java/com/diffplug/spotless/npm/EslintStyleGuide.java b/testlib/src/main/java/com/diffplug/spotless/npm/EslintStyleGuide.java new file mode 100644 index 0000000000..62d0b0aa00 --- /dev/null +++ b/testlib/src/main/java/com/diffplug/spotless/npm/EslintStyleGuide.java @@ -0,0 +1,120 @@ +/* + * Copyright 2023 DiffPlug + * + * 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. + */ +package com.diffplug.spotless.npm; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +/** + * A helper class to create dev dependencies for eslint when using one of the popular styleguides in testing. + */ +public enum EslintStyleGuide { + TS_STANDARD_WITH_TYPESCRIPT("standard-with-typescript") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-standard-with-typescript", "^24.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); + dependencies.put("eslint-plugin-n", "^15.6.0"); + dependencies.put("eslint-plugin-promise", "^6.1.1"); + return dependencies; + } + }, + TS_XO_TYPESCRIPT("xo-typescript") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-xo", "^0.43.1"); + dependencies.put("eslint-config-xo-typescript", "^0.55.1"); + return dependencies; + } + }, + JS_AIRBNB("airbnb") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-airbnb-base", "^15.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); + return dependencies; + } + }, + JS_GOOGLE("google") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-google", "^0.14.0"); + return dependencies; + } + }, + JS_STANDARD("standard") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-standard", "^17.0.0"); + dependencies.put("eslint-plugin-import", "^2.26.0"); + dependencies.put("eslint-plugin-n", "^15.6.0"); + dependencies.put("eslint-plugin-promise", "^6.1.1"); + return dependencies; + } + }, + JS_XO("xo") { + @Override + public @Nonnull Map devDependencies() { + Map dependencies = new LinkedHashMap<>(); + dependencies.put("eslint-config-xo", "^0.43.1"); + return dependencies; + } + }; + + private final String popularStyleGuideName; + + EslintStyleGuide(String popularStyleGuideName) { + this.popularStyleGuideName = popularStyleGuideName; + } + + public abstract @Nonnull Map devDependencies(); + + public static EslintStyleGuide fromNameOrNull(String popularStyleGuideName) { + for (EslintStyleGuide popularStyleGuide : EslintStyleGuide.values()) { + if (popularStyleGuide.popularStyleGuideName.equals(popularStyleGuideName)) { + return popularStyleGuide; + } + } + return null; + } + + public Map mergedWith(Map devDependencies) { + Map merged = new LinkedHashMap<>(devDependencies); + merged.putAll(devDependencies()); + return merged; + } + + public String asGradleMapStringMergedWith(Map devDependencies) { + return mergedWith(devDependencies).entrySet().stream() + .map(entry -> "'" + entry.getKey() + "': '" + entry.getValue() + "'") + .collect(Collectors.joining(", ", "[", "]")); + } + + public String asMavenXmlStringMergedWith(Map devDependencies) { + return mergedWith(devDependencies).entrySet().stream() + .map(entry -> "<" + entry.getKey() + ">" + entry.getValue() + "") + .collect(Collectors.joining("", "", "")); + } + +} diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java index 19f6e8c223..1bc0915ed3 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/EslintFormatterStepTest.java @@ -17,7 +17,6 @@ import java.io.File; import java.util.Map; -import java.util.TreeMap; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -34,22 +33,16 @@ @NpmTest class EslintFormatterStepTest { - private final Map combine(Map m1, Map m2) { - Map combined = new TreeMap<>(m1); - combined.putAll(m2); - return combined; - } - @NpmTest @Nested class EslintJavascriptFormattingStepTest extends NpmFormatterStepCommonTests { private final Map> devDependenciesForRuleset = ImmutableMap.of( "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), - "styleguide/airbnb", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_AIRBNB.devDependencies()), - "styleguide/google", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_GOOGLE.devDependencies()), - "styleguide/standard", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_STANDARD.devDependencies()), - "styleguide/xo", combine(EslintFormatterStep.defaultDevDependencies(), EslintFormatterStep.PopularStyleGuide.JS_XO.devDependencies())); + "styleguide/airbnb", EslintStyleGuide.JS_AIRBNB.mergedWith(EslintFormatterStep.defaultDevDependencies()), + "styleguide/google", EslintStyleGuide.JS_GOOGLE.mergedWith(EslintFormatterStep.defaultDevDependencies()), + "styleguide/standard", EslintStyleGuide.JS_STANDARD.mergedWith(EslintFormatterStep.defaultDevDependencies()), + "styleguide/xo", EslintStyleGuide.JS_XO.mergedWith(EslintFormatterStep.defaultDevDependencies())); @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") @ValueSource(strings = {"custom_rules", "styleguide/airbnb", "styleguide/google", "styleguide/standard", "styleguide/xo"}) @@ -86,8 +79,8 @@ class EslintTypescriptFormattingStepTest extends NpmFormatterStepCommonTests { private final Map> devDependenciesForRuleset = ImmutableMap.of( "custom_rules", EslintFormatterStep.defaultDevDependenciesForTypescript(), - "styleguide/standard_with_typescript", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_STANDARD_WITH_TYPESCRIPT.devDependencies()), - "styleguide/xo", combine(EslintFormatterStep.defaultDevDependenciesForTypescript(), EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies())); + "styleguide/standard_with_typescript", EslintStyleGuide.TS_STANDARD_WITH_TYPESCRIPT.mergedWith(EslintFormatterStep.defaultDevDependenciesForTypescript()), + "styleguide/xo", EslintStyleGuide.TS_XO_TYPESCRIPT.mergedWith(EslintFormatterStep.defaultDevDependenciesForTypescript())); @ParameterizedTest(name = "{index}: eslint can be applied using ruleset {0}") @ValueSource(strings = {"custom_rules", "styleguide/standard_with_typescript", "styleguide/xo"}) @@ -167,7 +160,7 @@ void formattingUsingInlineXoConfig() throws Exception { final String cleanFile = filedir + "typescript.clean"; final FormatterStep formatterStep = EslintFormatterStep.create( - combine(EslintFormatterStep.PopularStyleGuide.TS_XO_TYPESCRIPT.devDependencies(), EslintFormatterStep.defaultDevDependenciesForTypescript()), + EslintStyleGuide.TS_XO_TYPESCRIPT.mergedWith(EslintFormatterStep.defaultDevDependenciesForTypescript()), TestProvisioner.mavenCentral(), projectDir(), buildDir(), From 6577e29cdbd735b310143057372b1a0709c5219f Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 10 Jan 2023 09:43:04 +0100 Subject: [PATCH 36/37] eslint: fix anchor navigation --- plugin-gradle/README.md | 8 ++++---- plugin-maven/README.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 8a06351bed..878885f645 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -69,8 +69,8 @@ Spotless supports all of Gradle's built-in performance features (incremental bui - [FreshMark](#freshmark) aka markdown - [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter)) - [SQL](#sql) ([dbeaver](#dbeaver), [prettier](#prettier)) - - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint--typescript-)) - - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint--javascript-)) + - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint-typescript)) + - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint-javascript)) - [JSON](#json) - Multiple languages - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) @@ -624,7 +624,7 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Javascript)](#eslint--javascript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. It differs in supported styleguides and the requirement for a tsconfigFile. ```gradle @@ -689,7 +689,7 @@ spotless { The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Typescript)](#eslint--typescript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. It differs in supported styleguides and no requirement for a tsconfig (of course). ```gradle diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 2a2aa31010..e74e1b27cf 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -57,8 +57,8 @@ user@machine repo % mvn spotless:check - [Sql](#sql) ([dbeaver](#dbeaver)) - [Maven Pom](#maven-pom) ([sortPom](#sortpom)) - [Markdown](#markdown) ([flexmark](#flexmark)) - - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint--typescript-)) - - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint--javascript-)) + - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint-typescript)) + - [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint-javascript)) - [JSON](#json) - Multiple languages - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) @@ -724,7 +724,7 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Javascript)](#eslint--javascript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. It differs in supported styleguides and the requirement for a tsconfigFile. ```xml @@ -803,7 +803,7 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Typescript)](#eslint--typescript-) configuration. It differs in supported +The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. It differs in supported styleguides and no requirement for a tsconfig (of course). ```xml From 0227e89d641ab85dc8f2e48f715867c0992add06 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 10 Jan 2023 15:09:03 +0100 Subject: [PATCH 37/37] eslint: cleanup doc --- plugin-gradle/README.md | 8 ++++---- plugin-maven/README.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 878885f645..b44d79ef3d 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -624,8 +624,8 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. It differs in supported -styleguides and the requirement for a tsconfigFile. +The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. In typescript, a +reference to a `tsconfig.json` is required. ```gradle spotless { @@ -689,8 +689,8 @@ spotless { The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. It differs in supported -styleguides and no requirement for a tsconfig (of course). +The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. In javascript, *no* +`tsconfig.json` is supported. ```gradle diff --git a/plugin-maven/README.md b/plugin-maven/README.md index e74e1b27cf..3ec338bf10 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -724,8 +724,8 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. It differs in supported -styleguides and the requirement for a tsconfigFile. +The configuration is very similar to the [ESLint (Javascript)](#eslint-javascript) configuration. In typescript, a +reference to a `tsconfig.json` is required. ```xml @@ -803,8 +803,8 @@ For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#n The auto-discovery of config files (up the file tree) will not work when using ESLint within spotless, hence you are required to provide resolvable file paths for config files, or alternatively provide the configuration inline. -The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. It differs in supported -styleguides and no requirement for a tsconfig (of course). +The configuration is very similar to the [ESLint (Typescript)](#eslint-typescript) configuration. In javascript, *no* +`tsconfig.json` is supported. ```xml