diff --git a/README.md b/README.md index f5fd141020..b70a1ed42d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} extra('java.EclipseFormatterStep') +'{{yes}} | {{yes}} | {{no}} |', lib('kotlin.KtLintStep') +'{{yes}} | {{yes}} | {{no}} |', lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} |', -lib('npm.PrettierFormatterStep') +'{{yes}} | {{no}} | {{no}} |', +lib('npm.PrettierFormatterStep') +'{{yes}} | {{yes}} | {{no}} |', lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} |', lib('scala.ScalaFmtStep') +'{{yes}} | {{yes}} | {{no}} |', lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{no}} | {{no}} |', @@ -73,7 +73,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | [`java.EclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseFormatterStep.java) | :+1: | :+1: | :white_large_square: | | [`kotlin.KtLintStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java) | :+1: | :+1: | :white_large_square: | | [`markdown.FreshMarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FreshMarkStep.java) | :+1: | :white_large_square: | :white_large_square: | -| [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :white_large_square: | :white_large_square: | +| [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :+1: | :white_large_square: | | [`npm.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :+1: | :white_large_square: | | [`scala.ScalaFmtStep`](lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java) | :+1: | :+1: | :white_large_square: | | [`sql.DBeaverSQLFormatterStep`](lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java) | :+1: | :white_large_square: | :white_large_square: | diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 4945b4af43..c623f7aed9 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] +### +* Support for prettier ([#555](https://github.com/diffplug/spotless/pull/555)). ## [1.29.0] - 2020-04-02 ### Added diff --git a/plugin-maven/README.md b/plugin-maven/README.md index d4354bff20..5e455e4fb3 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -76,6 +76,8 @@ Spotless supports the following powerful formatters: * Eclipse's [CDT](https://www.eclipse.org/cdt/) C/C++ code formatter * Eclipse's [WTP](https://www.eclipse.org/webtools/) Web-Tools code formatters * Google's [google-java-format](https://github.com/google/google-java-format) +* [Typescript Tsfmt formatter](https://github.com/vvakame/typescript-formatter) +* [Prettier formatter](https://prettier.io) * User-defined license enforcement, regex replacement, etc. Contributions are welcome, see [the contributing guide](../CONTRIBUTING.md) for development info. @@ -200,11 +202,13 @@ Use the Eclipse to define the *Code Style preferences* (see [Eclipse documentati ```xml - + src/**/*.ts + + ${basedir}/path/to/repo/tslint.json ${basedir}/path/to/repo/tsfmt.json @@ -243,6 +247,67 @@ Spotless will try to auto-discover an npm installation. If that is not working f Spotless uses npm to install necessary packages locally. It runs tsfmt using [J2V8](https://github.com/eclipsesource/J2V8) internally after that. + + +## Applying [Prettier](https://prettier.io) to javascript | flow | typeScript | css | scss | less | jsx | graphQL | yaml | etc. + +Prettier is a formatter that can format [multiple file types](https://prettier.io/docs/en/language-support.html). + +To use prettier, you first have to specify the files that you want it to apply to. Then you specify prettier, and how you want to apply it. + +```xml + + + + + + src/**/typescript/**/*.ts + + + + + 1.19.0 + + 1.19.0 + + + + ${basedir}/path/to/configfile + + true + + + + + + +``` + +Supported config options are documented on [prettier.io](https://prettier.io/docs/en/options.html). +Supported config file variants are documented on [prettier.io](https://prettier.io/docs/en/configuration.html). + +*Please note:* +- The auto-discovery of config files (up the file tree) will not work when using prettier within spotless. +- Prettier's override syntax is not supported when using prettier within spotless. + +To apply prettier to more kinds of files, just add more formats. + +### Prerequisite: prettier requires a working NodeJS version + +Prettier, like tsfmt, is based on NodeJS, so to use it, a working NodeJS installation (especially npm) is required on the host running spotless. +Spotless will try to auto-discover an npm installation. If that is not working for you, it is possible to directly configure the npm binary to use. + +```xml + + /usr/bin/npm + ... +``` + +Spotless uses npm to install necessary packages locally. It runs prettier using [J2V8](https://github.com/eclipsesource/J2V8) internally after that. +Development for J2V8 for non android envs is stopped (for Windows since J2V8 4.6.0 and Unix 4.8.0), therefore Prettier is limited to <= v1.19.0 as newer versions +use ES6 feature and that needs a newer J2V8 version. + ## Applying to custom sources diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/FormatterFactory.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/FormatterFactory.java index 8796fa255c..d3ad7062ca 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/FormatterFactory.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/FormatterFactory.java @@ -111,6 +111,10 @@ public final void addEclipseWtp(EclipseWtp eclipseWtp) { addStepFactory(eclipseWtp); } + public final void addPrettier(Prettier prettier) { + addStepFactory(prettier); + } + protected final void addStepFactory(FormatterStepFactory stepFactory) { Objects.requireNonNull(stepFactory); stepFactories.add(stepFactory); 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 new file mode 100644 index 0000000000..a82fcb8b83 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/generic/Prettier.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 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.generic; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.npm.PrettierConfig; +import com.diffplug.spotless.npm.PrettierFormatterStep; + +public class Prettier implements FormatterStepFactory { + + @Parameter + private String prettierVersion; + + @Parameter + private Map devDependencies; + + @Parameter + private Map config; + + @Parameter + private String configFile; + + @Parameter + private String npmExecutable; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { + + // check if config is only setup in one way + if (this.prettierVersion != null && this.devDependencies != null) { + throw onlyOneConfig(); + } + + // set dev dependencies + if (devDependencies == null) { + if (prettierVersion == null || prettierVersion.isEmpty()) { + devDependencies = PrettierFormatterStep.defaultDevDependencies(); + } else { + devDependencies = PrettierFormatterStep.defaultDevDependenciesWithPrettier(prettierVersion); + } + } + + File npm = npmExecutable != null ? stepConfig.getFileLocator().locateLocal(npmExecutable) : null; + + // process config file or inline config + File configFileHandler; + if (this.configFile != null) { + configFileHandler = stepConfig.getFileLocator().locateLocal(this.configFile); + } else { + configFileHandler = null; + } + + Map configInline; + if (config != null) { + configInline = new LinkedHashMap<>(); + // try to parse string values as integers or booleans + for (Map.Entry e : config.entrySet()) { + try { + configInline.put(e.getKey(), Integer.parseInt(e.getValue())); + } catch (NumberFormatException ignore) { + try { + configInline.put(e.getKey(), Boolean.parseBoolean(e.getValue())); + } catch (IllegalArgumentException ignore2) { + configInline.put(e.getKey(), e.getValue()); + } + } + } + } else { + configInline = null; + } + + // create the format step + PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline); + File buildDir = stepConfig.getFileLocator().getBuildDir(); + return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npm, prettierConfig); + } + + private static IllegalArgumentException onlyOneConfig() { + return new IllegalArgumentException("must specify exactly one configFile or config"); + } +} diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationTest.java index 13302a41a1..4a741a37c4 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenIntegrationTest.java @@ -130,6 +130,10 @@ protected void writePomWithTypescriptSteps(String... steps) throws IOException { writePom(groupWithSteps("typescript", steps)); } + protected void writePomWithPrettierSteps(String includes, String... steps) throws IOException { + writePom(formats(groupWithSteps("format", including(includes), steps))); + } + protected void writePom(String... configuration) throws IOException { writePom(null, configuration); } 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 new file mode 100644 index 0000000000..4db0ed8ead --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/prettier/PrettierFormatStepTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 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.prettier; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.Test; + +import com.diffplug.spotless.maven.MavenIntegrationTest; +import com.diffplug.spotless.maven.MavenRunner; + +public class PrettierFormatStepTest extends MavenIntegrationTest { + + private void run(String kind, String suffix) throws IOException, InterruptedException { + String configPath = ".prettierrc.yml"; + setFile(configPath).toResource("npm/prettier/filetypes/" + kind + "/" + ".prettierrc.yml"); + String path = "src/main/" + kind + "/test." + suffix; + setFile(path).toResource("npm/prettier/filetypes/" + kind + "/" + kind + ".dirty"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("npm/prettier/filetypes/" + kind + "/" + kind + ".clean"); + } + + @Test + public void prettier_typescript() throws Exception { + String suffix = "ts"; + writePomWithPrettierSteps("**/*." + suffix, + "", + " 1.16.4", + " .prettierrc.yml", + ""); + run("typescript", suffix); + } + + @Test + public void prettier_html() throws Exception { + String suffix = "html"; + writePomWithPrettierSteps("**/*." + suffix, + "", + " 1.16.4", + " .prettierrc.yml", + ""); + run("html", suffix); + } + + @Test + public void prettier_tsx() throws Exception { + String suffix = "tsx"; + writePomWithPrettierSteps("src/main/**/*." + suffix, + "src/**/*.tsx", + "", + " 1.16.4", + " .prettierrc.yml", + ""); + run("tsx", suffix); + } + + @Test + public void prettier_tsx_inline_config() throws Exception { + String suffix = "tsx"; + writePomWithPrettierSteps("src/main/**/*." + suffix, + "", + " 1.16.4", + " typescript", + ""); + run("tsx", suffix); + } + + @Test + public void unique_dependency_config() throws Exception { + writePomWithFormatSteps( + "**/*.ts", + "", + " 1.16.4", + " 1.16.4", + ""); + + MavenRunner.Result result = mavenRunner().withArguments("spotless:apply").runHasError(); + assertThat(result.output()).contains("must specify exactly one configFile or config"); + } +} diff --git a/testlib/src/main/resources/npm/prettier/filetypes/html/.prettierrc.yml b/testlib/src/main/resources/npm/prettier/filetypes/html/.prettierrc.yml new file mode 100644 index 0000000000..f1185441f8 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/html/.prettierrc.yml @@ -0,0 +1 @@ +parser: html diff --git a/testlib/src/main/resources/npm/prettier/filetypes/html/html.clean b/testlib/src/main/resources/npm/prettier/filetypes/html/html.clean new file mode 100644 index 0000000000..306b11ebb9 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/html/html.clean @@ -0,0 +1,13 @@ + + + + + Test + + + + + + + + diff --git a/testlib/src/main/resources/npm/prettier/filetypes/html/html.dirty b/testlib/src/main/resources/npm/prettier/filetypes/html/html.dirty new file mode 100644 index 0000000000..482abaadd7 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/html/html.dirty @@ -0,0 +1,17 @@ + + + + + + Test + + + + + + + + + diff --git a/testlib/src/main/resources/npm/prettier/filetypes/tsx/.prettierrc.yml b/testlib/src/main/resources/npm/prettier/filetypes/tsx/.prettierrc.yml new file mode 100644 index 0000000000..afddefc276 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/tsx/.prettierrc.yml @@ -0,0 +1,3 @@ +parser: typescript +files: "scr/**/*.tsx" +excludeFiles: "target/**" diff --git a/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.clean b/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.clean new file mode 100644 index 0000000000..9f13a1dca2 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.clean @@ -0,0 +1,31 @@ +import { Component } from "@stencil/core"; + +@Component({ + tag: "testtag", + shadow: false +}) +export class Sample { + @Prop() error: error; + + hello(word = "world") { + return "Hello, " + word; + } + + render() { + return [ +
+ + + {this.error.messages + .sort((n1, n2) => this.hello(n1)) + .map(message => ( + + ))} + +
{message}
+
+ ]; + } +} + +var s = new Sample(); diff --git a/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.dirty b/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.dirty new file mode 100644 index 0000000000..38035897f1 --- /dev/null +++ b/testlib/src/main/resources/npm/prettier/filetypes/tsx/tsx.dirty @@ -0,0 +1,28 @@ +import {Component} from "@stencil/core"; + +@Component({ +tag: 'testtag', +shadow: false +}) +export class Sample { + +@Prop()error:error; + + hello(word = "world") { + return "Hello, " + word; + } + +render(){ +return[ +
+ + { this.error.messages + .sort((n1,n2) =>this.hello(n1)).map((message) => )} + +
{message}
+]} + +} + + +var s = new Sample();