diff --git a/CHANGES.md b/CHANGES.md index 1d78e8d932..652e4ce38c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Fixed +* Added support for plugins when using Prettier version `3.0.0` and newer. ([#1802](https://github.com/diffplug/spotless/pull/1802)) + ## [2.41.0] - 2023-08-29 ### Added * Add a `jsonPatch` step to `json` formatter configurations. This allows patching of JSON documents using [JSON Patches](https://jsonpatch.com). ([#1753](https://github.com/diffplug/spotless/pull/1753)) diff --git a/lib/src/main/java/com/diffplug/spotless/npm/JsonEscaper.java b/lib/src/main/java/com/diffplug/spotless/npm/JsonEscaper.java index 163818d0e7..3ac20d892b 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/JsonEscaper.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/JsonEscaper.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 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. @@ -34,6 +34,22 @@ public static String jsonEscape(Object val) { if (val instanceof String) { return jsonEscape((String) val); } + if (ListableAdapter.canAdapt(val)) { + // create an array + StringBuilder sb = new StringBuilder(); + sb.append('['); + boolean first = true; + for (Object o : ListableAdapter.adapt(val)) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(jsonEscape(o)); + } + sb.append(']'); + return sb.toString(); + } return val.toString(); } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/SimpleJsonWriter.java b/lib/src/main/java/com/diffplug/spotless/npm/JsonWriter.java similarity index 77% rename from lib/src/main/java/com/diffplug/spotless/npm/SimpleJsonWriter.java rename to lib/src/main/java/com/diffplug/spotless/npm/JsonWriter.java index 846f7f1cf3..bbdf9b63d4 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/SimpleJsonWriter.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/JsonWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 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. @@ -28,33 +28,46 @@ import com.diffplug.spotless.ThrowingEx; -public class SimpleJsonWriter { +class JsonWriter { private final LinkedHashMap valueMap = new LinkedHashMap<>(); - public static SimpleJsonWriter of(Map values) { - SimpleJsonWriter writer = new SimpleJsonWriter(); + public static JsonWriter of(Map values) { + JsonWriter writer = new JsonWriter(); writer.putAll(values); return writer; } - SimpleJsonWriter putAll(Map values) { + JsonWriter putAll(Map values) { verifyValues(values); this.valueMap.putAll(values); return this; } - SimpleJsonWriter put(String name, Object value) { + JsonWriter put(String name, Object value) { verifyValues(Collections.singletonMap(name, value)); this.valueMap.put(name, value); return this; } private void verifyValues(Map values) { - if (values.values() - .stream() - .anyMatch(val -> !(val instanceof String || val instanceof JsonRawValue || val instanceof Number || val instanceof Boolean))) { - throw new IllegalArgumentException("Only values of type 'String', 'JsonRawValue', 'Number' and 'Boolean' are supported. You provided: " + values.values()); + for (Object value : values.values()) { + verifyValue(value); + } + } + + private void verifyValue(Object val) { + if (val == null) { + return; + } + if (ListableAdapter.canAdapt(val)) { + for (Object o : ListableAdapter.adapt(val)) { + verifyValue(o); + } + return; + } + if (!(val instanceof String || val instanceof JsonRawValue || val instanceof Number || val instanceof Boolean)) { + throw new IllegalArgumentException("Only values of type 'String', 'JsonRawValue', 'Number' and 'Boolean' are supported. You provided: " + val); } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/ListableAdapter.java b/lib/src/main/java/com/diffplug/spotless/npm/ListableAdapter.java new file mode 100644 index 0000000000..24ae62fde6 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/ListableAdapter.java @@ -0,0 +1,58 @@ +/* + * 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.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nonnull; + +class ListableAdapter implements Iterable { + + private final List delegate; + + @SuppressWarnings("unchecked") + private ListableAdapter(Object delegate) { + Objects.requireNonNull(delegate); + if (!canAdapt(delegate)) { + throw new IllegalArgumentException("Cannot create ListableAdapter from " + delegate.getClass() + ". Use canAdapt() to check first."); + } + if (delegate instanceof List) { + this.delegate = (List) delegate; + } else if (delegate.getClass().isArray()) { + this.delegate = Arrays.asList((T[]) delegate); + } else { + throw new IllegalArgumentException("Cannot create IterableAdapter from " + delegate.getClass()); + } + } + + static Iterable adapt(Object delegate) { + return new ListableAdapter<>(delegate); + } + + @Override + @Nonnull + public Iterator iterator() { + return delegate.iterator(); + } + + static boolean canAdapt(Object delegate) { + Objects.requireNonNull(delegate); + return delegate instanceof List || delegate.getClass().isArray(); + } +} 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 ccdc189d27..11fd29c68a 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierRestService.java @@ -31,8 +31,7 @@ public String resolveConfig(File prettierConfigPath, Map prettie jsonProperties.put("prettier_config_path", prettierConfigPath.getAbsolutePath()); } if (prettierConfigOptions != null) { - jsonProperties.put("prettier_config_options", SimpleJsonWriter.of(prettierConfigOptions).toJsonRawValue()); - + jsonProperties.put("prettier_config_options", JsonWriter.of(prettierConfigOptions).toJsonRawValue()); } return restClient.postJson("/prettier/config-options", jsonProperties); } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/SimpleRestClient.java b/lib/src/main/java/com/diffplug/spotless/npm/SimpleRestClient.java index 561839c757..2f413e4b3a 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/SimpleRestClient.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/SimpleRestClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 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. @@ -40,7 +40,7 @@ static SimpleRestClient forBaseUrl(String baseUrl) { } String postJson(String endpoint, Map jsonParams) throws SimpleRestException { - final SimpleJsonWriter jsonWriter = SimpleJsonWriter.of(jsonParams); + final JsonWriter jsonWriter = JsonWriter.of(jsonParams); final String jsonString = jsonWriter.toJsonString(); return postJson(endpoint, jsonString); 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 a83c3e202a..6b55610be0 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java @@ -107,7 +107,7 @@ private Map unifyOptions() { Map unified = new HashMap<>(); if (!this.inlineTsFmtSettings.isEmpty()) { File targetFile = new File(this.buildDir, "inline-tsfmt.json"); - SimpleJsonWriter.of(this.inlineTsFmtSettings).toJsonFile(targetFile); + JsonWriter.of(this.inlineTsFmtSettings).toJsonFile(targetFile); unified.put("tsfmt", true); unified.put("tsfmtFile", targetFile.getAbsolutePath()); } else if (this.configFile != null) { 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 704d2b47af..61a53b4637 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtRestService.java @@ -28,7 +28,7 @@ public String format(String fileContent, Map configOptions) { Map jsonProperties = new LinkedHashMap<>(); jsonProperties.put("file_content", fileContent); if (configOptions != null && !configOptions.isEmpty()) { - jsonProperties.put("config_options", SimpleJsonWriter.of(configOptions).toJsonRawValue()); + jsonProperties.put("config_options", JsonWriter.of(configOptions).toJsonRawValue()); } return restClient.postJson("/tsfmt/format", jsonProperties); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index eab7e32b2d..989b4ece12 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,6 +4,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Fixed +* Added support for plugins when using Prettier version `3.0.0` and newer. ([#1802](https://github.com/diffplug/spotless/pull/1802)) + ## [6.21.0] - 2023-08-29 ### Added * Add a `jsonPatch` step to `json` formatter configurations. This allows patching of JSON documents using [JSON Patches](https://jsonpatch.com). ([#1753](https://github.com/diffplug/spotless/pull/1753)) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 944c07bb6e..fcdcfc213a 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -1024,10 +1024,12 @@ Since spotless uses the actual npm prettier package behind the scenes, it is pos spotless { java { prettier(['prettier': '2.8.8', 'prettier-plugin-java': '2.2.0']).config(['parser': 'java', 'tabWidth': 4]) + // prettier(['prettier': '3.0.3', 'prettier-plugin-java': '2.3.0']).config(['parser': 'java', 'tabWidth': 4, 'plugins': ['prettier-plugin-java']]) // Prettier v3 requires additional 'plugins' config } format 'php', { target 'src/**/*.php' prettier(['prettier': '2.8.8', '@prettier/plugin-php': '0.19.6']).config(['parser': 'php', 'tabWidth': 3]) + // prettier(['prettier': '3.0.3', '@prettier/plugin-php': '0.20.1']).config(['parser': 'php', 'tabWidth': 3, 'plugins': ['@prettier/plugin-php']]) // Prettier v3 requires additional 'plugins' config } } ``` 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 4d5069c1b5..74b6db9167 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 @@ -19,14 +19,22 @@ import org.assertj.core.api.Assertions; import org.gradle.testkit.runner.BuildResult; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import com.diffplug.spotless.npm.PrettierFormatterStep; import com.diffplug.spotless.tag.NpmTest; @NpmTest class PrettierIntegrationTest extends GradleIntegrationHarness { - @Test - void useInlineConfig() throws IOException { + + private static final String PRETTIER_VERSION_2 = PrettierFormatterStep.DEFAULT_VERSION; + + private static final String PRETTIER_VERSION_3 = "3.0.3"; + + @ParameterizedTest(name = "{index}: useInlineConfig with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void useInlineConfig(String prettierVersion) throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -38,17 +46,25 @@ void useInlineConfig() throws IOException { "spotless {", " format 'mytypescript', {", " target 'test.ts'", - " prettier().config(prettierConfig)", + " prettier('" + prettierVersion + "').config(prettierConfig)", " }", "}"); setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL"); - assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_2.clean"); + switch (prettierVersion) { + case PRETTIER_VERSION_2: + assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_2.clean"); + break; + case PRETTIER_VERSION_3: + assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_3.clean"); + break; + } } - @Test - void verifyCleanSpotlessCheckWorks() throws IOException { + @ParameterizedTest(name = "{index}: verifyCleanSpotlessCheckWorks with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void verifyCleanSpotlessCheckWorks(String prettierVersion) throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -60,19 +76,20 @@ void verifyCleanSpotlessCheckWorks() throws IOException { "spotless {", " format 'mytypescript', {", " target 'test.ts'", - " prettier().config(prettierConfig)", + " prettier('" + prettierVersion + "').config(prettierConfig)", " }", "}"); setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); - BuildResult spotlessCheckFailsGracefully = gradleRunner().withArguments("--stacktrace", "spotlessCheck").buildAndFail(); + final BuildResult spotlessCheckFailsGracefully = gradleRunner().withArguments("--stacktrace", "spotlessCheck").buildAndFail(); Assertions.assertThat(spotlessCheckFailsGracefully.getOutput()).contains("> The following files had format violations:"); gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); gradleRunner().withArguments("--stacktrace", "spotlessCheck").build(); } - @Test - void useFileConfig() throws IOException { + @ParameterizedTest(name = "{index}: useFileConfig with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void useFileConfig(String prettierVersion) throws IOException { setFile(".prettierrc.yml").toResource("npm/prettier/config/.prettierrc.yml"); setFile("build.gradle").toLines( "plugins {", @@ -82,17 +99,25 @@ void useFileConfig() throws IOException { "spotless {", " format 'mytypescript', {", " target 'test.ts'", - " prettier().configFile('.prettierrc.yml')", + " prettier('" + prettierVersion + "').configFile('.prettierrc.yml')", " }", "}"); setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL"); - assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_2.clean"); + switch (prettierVersion) { + case PRETTIER_VERSION_2: + assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_2.clean"); + break; + case PRETTIER_VERSION_3: + assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_3.clean"); + break; + } } - @Test - void chooseParserBasedOnFilename() throws IOException { + @ParameterizedTest(name = "{index}: chooseParserBasedOnFilename with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void chooseParserBasedOnFilename(String prettierVersion) throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -101,7 +126,7 @@ void chooseParserBasedOnFilename() throws IOException { "spotless {", " format 'webResources', {", " target 'dirty.*'", - " prettier()", + " prettier('" + prettierVersion + "')", " }", "}"); setFile("dirty.json").toResource("npm/prettier/filename/dirty.json"); @@ -110,8 +135,20 @@ void chooseParserBasedOnFilename() throws IOException { assertFile("dirty.json").sameAsResource("npm/prettier/filename/clean.json"); } - @Test - void useJavaCommunityPlugin() throws IOException { + @ParameterizedTest(name = "{index}: useJavaCommunityPlugin with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void useJavaCommunityPlugin(String prettierVersion) throws IOException { + var prettierPluginJava = ""; + var prettierConfigPluginsStr = ""; + switch (prettierVersion) { + case PRETTIER_VERSION_2: + prettierPluginJava = "2.1.0"; // last version to support v2 + break; + case PRETTIER_VERSION_3: + prettierPluginJava = "2.3.0"; // latest to support v3 + prettierConfigPluginsStr = "prettierConfig['plugins'] = ['prettier-plugin-java']"; + break; + } setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -120,9 +157,10 @@ void useJavaCommunityPlugin() throws IOException { "def prettierConfig = [:]", "prettierConfig['tabWidth'] = 4", "prettierConfig['parser'] = 'java'", + prettierConfigPluginsStr, "def prettierPackages = [:]", - "prettierPackages['prettier'] = '2.8.8'", - "prettierPackages['prettier-plugin-java'] = '2.2.0'", + "prettierPackages['prettier'] = '" + prettierVersion + "'", + "prettierPackages['prettier-plugin-java'] = '" + prettierPluginJava + "'", "spotless {", " format 'java', {", " target 'JavaTest.java'", @@ -135,8 +173,42 @@ void useJavaCommunityPlugin() throws IOException { assertFile("JavaTest.java").sameAsResource("npm/prettier/plugins/java-test.clean"); } - @Test - void suggestsMissingJavaCommunityPlugin() throws IOException { + @ParameterizedTest(name = "{index}: useJavaCommunityPluginFileConfig with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void useJavaCommunityPluginFileConfig(String prettierVersion) throws IOException { + var prettierPluginJava = ""; + switch (prettierVersion) { + case PRETTIER_VERSION_2: + prettierPluginJava = "2.1.0"; // last version to support v2 + break; + case PRETTIER_VERSION_3: + prettierPluginJava = "2.3.0"; // latest to support v3 + break; + } + setFile(".prettierrc.yml").toResource("npm/prettier/config/.prettierrc_java_plugin.yml"); + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "def prettierPackages = [:]", + "prettierPackages['prettier'] = '" + prettierVersion + "'", + "prettierPackages['prettier-plugin-java'] = '" + prettierPluginJava + "'", + "spotless {", + " format 'java', {", + " target 'JavaTest.java'", + " prettier(prettierPackages).configFile('.prettierrc.yml')", + " }", + "}"); + setFile("JavaTest.java").toResource("npm/prettier/plugins/java-test.dirty"); + final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").build(); + Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL"); + assertFile("JavaTest.java").sameAsResource("npm/prettier/plugins/java-test.clean"); + } + + @ParameterizedTest(name = "{index}: suggestsMissingJavaCommunityPlugin with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void suggestsMissingJavaCommunityPlugin(String prettierVersion) throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -145,7 +217,7 @@ void suggestsMissingJavaCommunityPlugin() throws IOException { "def prettierConfig = [:]", "prettierConfig['tabWidth'] = 4", "def prettierPackages = [:]", - "prettierPackages['prettier'] = '2.8.8'", + "prettierPackages['prettier'] = '" + prettierVersion + "'", "spotless {", " format 'java', {", " target 'JavaTest.java'", @@ -158,8 +230,20 @@ void suggestsMissingJavaCommunityPlugin() throws IOException { Assertions.assertThat(spotlessApply.getOutput()).contains("prettier-plugin-java"); } - @Test - void usePhpCommunityPlugin() throws IOException { + @ParameterizedTest(name = "{index}: usePhpCommunityPlugin with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void usePhpCommunityPlugin(String prettierVersion) throws IOException { + var prettierPluginPhp = ""; + var prettierConfigPluginsStr = ""; + switch (prettierVersion) { + case PRETTIER_VERSION_2: + prettierPluginPhp = "0.19.7"; // last version to support v2 + break; + case PRETTIER_VERSION_3: + prettierPluginPhp = "0.20.1"; // latest to support v3 + prettierConfigPluginsStr = "prettierConfig['plugins'] = ['@prettier/plugin-php']"; + break; + } setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -168,9 +252,10 @@ void usePhpCommunityPlugin() throws IOException { "def prettierConfig = [:]", "prettierConfig['tabWidth'] = 3", "prettierConfig['parser'] = 'php'", + prettierConfigPluginsStr, "def prettierPackages = [:]", - "prettierPackages['prettier'] = '2.8.8'", - "prettierPackages['@prettier/plugin-php'] = '0.19.6'", + "prettierPackages['prettier'] = '" + prettierVersion + "'", + "prettierPackages['@prettier/plugin-php'] = '" + prettierPluginPhp + "'", "spotless {", " format 'php', {", " target 'php-example.php'", @@ -188,8 +273,25 @@ void usePhpCommunityPlugin() throws IOException { * * @see Issue #1162 on github */ - @Test - void usePhpAndJavaCommunityPlugin() throws IOException { + @ParameterizedTest(name = "{index}: usePhpAndJavaCommunityPlugin with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void usePhpAndJavaCommunityPlugin(String prettierVersion) throws IOException { + var prettierPluginJava = ""; + var prettierPluginPhp = ""; + var prettierConfigPluginsJavaStr = ""; + var prettierConfigPluginsPhpStr = ""; + switch (prettierVersion) { + case PRETTIER_VERSION_2: + prettierPluginJava = "2.1.0"; // last version to support v2 + prettierPluginPhp = "0.19.7"; // last version to support v2 + break; + case PRETTIER_VERSION_3: + prettierPluginJava = "2.3.0"; // latest to support v3 + prettierPluginPhp = "0.20.1"; // latest to support v3 + prettierConfigPluginsJavaStr = "prettierConfigJava['plugins'] = ['prettier-plugin-java']"; + prettierConfigPluginsPhpStr = "prettierConfigPhp['plugins'] = ['@prettier/plugin-php']"; + break; + } setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -198,15 +300,17 @@ void usePhpAndJavaCommunityPlugin() throws IOException { "def prettierConfigPhp = [:]", "prettierConfigPhp['tabWidth'] = 3", "prettierConfigPhp['parser'] = 'php'", + prettierConfigPluginsPhpStr, "def prettierPackagesPhp = [:]", - "prettierPackagesPhp['prettier'] = '2.8.8'", - "prettierPackagesPhp['@prettier/plugin-php'] = '0.19.6'", + "prettierPackagesPhp['prettier'] = '" + prettierVersion + "'", + "prettierPackagesPhp['@prettier/plugin-php'] = '" + prettierPluginPhp + "'", "def prettierConfigJava = [:]", "prettierConfigJava['tabWidth'] = 4", "prettierConfigJava['parser'] = 'java'", + prettierConfigPluginsJavaStr, "def prettierPackagesJava = [:]", - "prettierPackagesJava['prettier'] = '2.8.8'", - "prettierPackagesJava['prettier-plugin-java'] = '2.2.0'", + "prettierPackagesJava['prettier'] = '" + prettierVersion + "'", + "prettierPackagesJava['prettier-plugin-java'] = '" + prettierPluginJava + "'", "spotless {", " format 'php', {", " target 'php-example.php'", @@ -227,8 +331,9 @@ void usePhpAndJavaCommunityPlugin() throws IOException { assertFile("JavaTest.java").sameAsResource("npm/prettier/plugins/java-test.clean"); } - @Test - void autodetectNpmrcFileConfig() throws IOException { + @ParameterizedTest(name = "{index}: autodetectNpmrcFileConfig with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void autodetectNpmrcFileConfig(String prettierVersion) throws IOException { setFile(".npmrc").toLines( "registry=https://i.do.not.exist.com", "fetch-timeout=250", @@ -245,7 +350,7 @@ void autodetectNpmrcFileConfig() throws IOException { "spotless {", " format 'mytypescript', {", " target 'test.ts'", - " prettier().config(prettierConfig)", + " prettier('" + prettierVersion + "').config(prettierConfig)", " }", "}"); setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); @@ -253,8 +358,9 @@ void autodetectNpmrcFileConfig() throws IOException { Assertions.assertThat(spotlessApply.getOutput()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); } - @Test - void pickupNpmrcFileConfig() throws IOException { + @ParameterizedTest(name = "{index}: autodetectNpmrcFileConfig with prettier {0}") + @ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3}) + void pickupNpmrcFileConfig(String prettierVersion) throws IOException { setFile(".custom_npmrc").toLines( "registry=https://i.do.not.exist.com", "fetch-timeout=250", @@ -271,7 +377,7 @@ void pickupNpmrcFileConfig() throws IOException { "spotless {", " format 'mytypescript', {", " target 'test.ts'", - " prettier().npmrc('.custom_npmrc').config(prettierConfig)", + " prettier('" + prettierVersion + "').npmrc('.custom_npmrc').config(prettierConfig)", " }", "}"); setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index f932806fcc..b624299e71 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,6 +4,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Fixed +* Added support for plugins when using Prettier version `3.0.0` and newer. ([#1802](https://github.com/diffplug/spotless/pull/1802)) + ## [2.39.0] - 2023-08-29 ### Added * Add a `jsonPatch` step to `json` formatter configurations. This allows patching of JSON documents using [JSON Patches](https://jsonpatch.com). ([#1753](https://github.com/diffplug/spotless/pull/1753)) diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 74872ef61f..8f91d8b08b 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -1074,6 +1074,8 @@ You can use prettier in any language-specific format, but usually you will be cr ${project.basedir}/path/to/configfile true + + @prettier/plugin-php @@ -1119,6 +1121,7 @@ Since spotless uses the actual npm prettier package behind the scenes, it is pos 4 java + prettier-plugin-java @@ -1144,6 +1147,7 @@ Since spotless uses the actual npm prettier package behind the scenes, it is pos 3 php + @prettier/plugin-php 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 e92b2814bd..c6b3e46e3c 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 @@ -85,6 +85,11 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { if (Boolean.TRUE.toString().equalsIgnoreCase(entry.getValue()) || Boolean.FALSE.toString().equalsIgnoreCase(entry.getValue())) { return new AbstractMap.SimpleEntry<>(entry.getKey(), Boolean.parseBoolean(entry.getValue())); } + // Prettier v3 - plugins config will be a comma delimited list of plugins + if (entry.getKey().equals("plugins")) { + List values = entry.getValue().isEmpty() ? List.of() : Arrays.asList(entry.getValue().split(",")); + return new AbstractMap.SimpleEntry<>(entry.getKey(), values); + } return entry; }) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new)); 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 9cd1a313eb..abba35e72c 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 @@ -184,6 +184,56 @@ void custom_plugin() throws Exception { assertFile("php-example.php").sameAsResource("npm/prettier/plugins/php.clean"); } + @Test + void custom_plugin_prettier3() throws Exception { + writePomWithFormatSteps( + "php-example.php", + "", + " ", + " ", + " prettier", + " 3.0.3", + " ", + " ", + " @prettier/plugin-php", + " 0.20.1", + " ", + " ", + " ", + " 3", + " php", + " @prettier/plugin-php", + " ", + ""); + + setFile("php-example.php").toResource("npm/prettier/plugins/php.dirty"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile("php-example.php").sameAsResource("npm/prettier/plugins/php.clean"); + } + + @Test + void custom_plugin_prettier3_file_config() throws Exception { + writePomWithFormatSteps( + "JavaTest.java", + "", + " ", + " ", + " prettier", + " 3.0.3", + " ", + " ", + " prettier-plugin-java", + " 2.3.0", + " ", + " ", + " .prettierrc.yml", + ""); + setFile(".prettierrc.yml").toResource("npm/prettier/config/.prettierrc_java_plugin.yml"); + setFile("JavaTest.java").toResource("npm/prettier/plugins/java-test.dirty"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile("JavaTest.java").sameAsResource("npm/prettier/plugins/java-test.clean"); + } + @Test void autodetect_parser_based_on_filename() throws Exception { writePomWithFormatSteps( diff --git a/testlib/src/main/resources/npm/prettier/config/.prettierrc_java_plugin.yml b/testlib/src/main/resources/npm/prettier/config/.prettierrc_java_plugin.yml new file mode 100644 index 0000000000..3f7a801335 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/config/.prettierrc_java_plugin.yml @@ -0,0 +1,3 @@ +parser: java +tabWidth: 4 +plugins: ['prettier-plugin-java'] diff --git a/testlib/src/test/java/com/diffplug/spotless/npm/SimpleJsonWriterTest.java b/testlib/src/test/java/com/diffplug/spotless/npm/JsonWriterTest.java similarity index 93% rename from testlib/src/test/java/com/diffplug/spotless/npm/SimpleJsonWriterTest.java rename to testlib/src/test/java/com/diffplug/spotless/npm/JsonWriterTest.java index 34d7966709..cb1719c0f3 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/SimpleJsonWriterTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/JsonWriterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 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. @@ -26,9 +26,9 @@ import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.ResourceHarness; -class SimpleJsonWriterTest extends ResourceHarness { +class JsonWriterTest extends ResourceHarness { - private SimpleJsonWriter jsonWriter = new SimpleJsonWriter(); + private JsonWriter jsonWriter = new JsonWriter(); @Test void itWritesAValidEmptyObject() {