diff --git a/CHANGES.md b/CHANGES.md index 9a9ba22e6d..fded6016fe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ 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 +* Tsfmt Maven Plugin ([#548](https://github.com/diffplug/spotless/pull/548)) ### Fixed * Javadoc for the `ext/eclipse-*` projects. * Replace the deprecated `compile` with `implementation` for the `ext/eclipse-*` projects. diff --git a/README.md b/README.md index 5c311133da..c5a1de180d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ extra('java.EclipseFormatterStep') +'{{yes}} | {{yes}} lib('kotlin.KtLintStep') +'{{yes}} | {{yes}} | {{no}} |', lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} |', lib('npm.PrettierFormatterStep') +'{{yes}} | {{no}} | {{no}} |', -lib('npm.TsFmtFormatterStep') +'{{yes}} | {{no}} | {{no}} |', +lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} |', lib('scala.ScalaFmtStep') +'{{yes}} | {{yes}} | {{no}} |', lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{no}} | {{no}} |', extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | {{no}} |', @@ -74,7 +74,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}} | [`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.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :white_large_square: | :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: | | [`wtp.EclipseWtpFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/wtp/EclipseWtpFormatterStep.java) | :+1: | :+1: | :white_large_square: | diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 5cdbb26d77..c2e4a10981 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 +* Tsfmt Maven Plugin ([#548](https://github.com/diffplug/spotless/pull/548)) ### Fixed * Eclipse-WTP formatter (web tools platform, not java) handles some character encodings incorrectly on OS with non-unicode default file encoding [#545](https://github.com/diffplug/spotless/issues/545). Fixed for Eclipse-WTP formatter Eclipse version 4.13.0 (default version). diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 7e03e07eee..1e1948dea9 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -193,6 +193,56 @@ By default, all files matching `src/main/cpp/**/*.` and `src/test/cpp/**/*. ``` Use the Eclipse to define the *Code Style preferences* (see [Eclipse documentation](https://www.eclipse.org/documentation/)). Within the preferences *Edit...* dialog, you can export your configuration as XML file, which can be used as a configuration ``. If no `` is provided, the CDT default configuration is used. + + +## Applying to Typescript source + +```xml + + + + + + src/**/*.ts + + + ${basedir}/path/to/repo/tslint.json + ${basedir}/path/to/repo/tsfmt.json + ${basedir}/path/to/repo/tsconfig.json + ${basedir}/path/to/repo/vscode.json + + 1 + true + + + 7.2.2 + 3.3.3 + 5.12.1 + + + +``` + +Supported config file types are `tsconfigFile`, `tslintFile`, `vscodeFile` and `tsfmtFile`. They are corresponding to the respective +[tsfmt-parameters](https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/index.ts#L27L34). See [tsfmt's default config settings](https://github.com/vvakame/typescript-formatter/blob/7764258ad42ac65071399840d1b8701868510ca7/lib/utils.ts#L11L32) for what is available. + +*Please note:* +The auto-discovery of config files (up the file tree) will not work when using tsfmt within spotless, + hence you are required to provide resolvable file paths for config files. + +### Prerequisite: tsfmt requires a working NodeJS version + +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 tsfmt using [J2V8](https://github.com/eclipsesource/J2V8) internally after that. + ## Applying to custom sources 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 f045348117..195614a768 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 @@ -45,6 +45,7 @@ import com.diffplug.spotless.maven.java.Java; import com.diffplug.spotless.maven.kotlin.Kotlin; import com.diffplug.spotless.maven.scala.Scala; +import com.diffplug.spotless.maven.typescript.Typescript; public abstract class AbstractSpotlessMojo extends AbstractMojo { @@ -98,6 +99,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo { @Parameter private Cpp cpp; + @Parameter + private Typescript typescript; + /** The CSS extension is discontinued. */ @Parameter @Deprecated @@ -179,7 +183,7 @@ private FileLocator getFileLocator() { } private List getFormatterFactories() { - return Stream.concat(formats.stream(), Stream.of(java, scala, kotlin, cpp, css, xml)) + return Stream.concat(formats.stream(), Stream.of(java, scala, kotlin, cpp, typescript, css, xml)) .filter(Objects::nonNull) .collect(toList()); } 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 new file mode 100644 index 0000000000..1eb2fbd079 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Tsfmt.java @@ -0,0 +1,105 @@ +/* + * 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.typescript; + +import java.io.File; +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.TsConfigFileType; +import com.diffplug.spotless.npm.TsFmtFormatterStep; +import com.diffplug.spotless.npm.TypedTsFmtConfigFile; + +public class Tsfmt implements FormatterStepFactory { + + @Parameter + private String tslintFile; + + @Parameter + private String tsconfigFile; + + @Parameter + private String vscodeFile; + + @Parameter + private String tsfmtFile; + + @Parameter + private String typescriptFormatterVersion; + + @Parameter + private String typescriptVersion; + + @Parameter + private String tslintVersion; + + @Parameter + private String npmExecutable; + + @Parameter + private Map config; + + @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) + private File buildDir; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { + + Map devDependencies = TsFmtFormatterStep.defaultDevDependencies(); + if (typescriptFormatterVersion != null) { + devDependencies.put("typescript-formatter", typescriptFormatterVersion); + } + if (typescriptVersion != null) { + devDependencies.put("typescript", typescriptVersion); + } + if (tslintVersion != null) { + devDependencies.put("tslint", tslintVersion); + } + + File npm = npmExecutable != null ? stepConfig.getFileLocator().locateFile(npmExecutable) : null; + + TypedTsFmtConfigFile configFile = null; + + // check that there is only 1 config file or inline config + if (this.tsconfigFile != null + ^ this.tsfmtFile != null + ^ this.tslintFile != null + ^ this.vscodeFile != null) { + if (this.tsconfigFile != null) { + configFile = new TypedTsFmtConfigFile(TsConfigFileType.TSCONFIG, stepConfig.getFileLocator().locateFile(tsconfigFile)); + } else if (this.tsfmtFile != null) { + configFile = new TypedTsFmtConfigFile(TsConfigFileType.TSFMT, stepConfig.getFileLocator().locateFile(tsfmtFile)); + } else if (this.tslintFile != null) { + configFile = new TypedTsFmtConfigFile(TsConfigFileType.TSLINT, stepConfig.getFileLocator().locateFile(tslintFile)); + } else if (this.vscodeFile != null) { + configFile = new TypedTsFmtConfigFile(TsConfigFileType.VSCODE, stepConfig.getFileLocator().locateFile(vscodeFile)); + } + } else { + if (config == null) { + throw new IllegalArgumentException("must specify exactly one configFile or config"); + } + } + + if (buildDir == null) { + buildDir = new File("."); + } + return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npm, configFile, config); + } +} 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 new file mode 100644 index 0000000000..e2cd953268 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/typescript/Typescript.java @@ -0,0 +1,48 @@ +/* + * 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.typescript; + +import java.util.Set; + +import com.diffplug.common.collect.ImmutableSet; +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 Typescript extends FormatterFactory { + + private static final Set DEFAULT_INCLUDES = ImmutableSet.of("src/**/*.ts"); + + private static final String LICENSE_HEADER_DELIMITER = null; + + @Override + public Set defaultIncludes() { + return DEFAULT_INCLUDES; + } + + @Override + public String licenseHeaderDelimiter() { + return LICENSE_HEADER_DELIMITER; + } + + public void addTsfmt(Tsfmt tsfmt) { + addStepFactory(tsfmt); + } + +} 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 8036b1899e..09da08e2e5 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 @@ -118,6 +118,10 @@ protected void writePomWithCssSteps(String... steps) throws IOException { writePom(groupWithSteps("css", steps)); } + protected void writePomWithTypescriptSteps(String... steps) throws IOException { + writePom(groupWithSteps("typescript", steps)); + } + protected void writePom(String... configuration) throws IOException { writePom(null, configuration); } 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 new file mode 100644 index 0000000000..3ef3d734da --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/typescript/TypescriptFormatStepTest.java @@ -0,0 +1,125 @@ +/* + * 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.typescript; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Ignore; +import org.junit.Test; + +import com.diffplug.spotless.maven.MavenIntegrationTest; +import com.diffplug.spotless.maven.MavenRunner; + +public class TypescriptFormatStepTest extends MavenIntegrationTest { + + @Test + public void testTypescriptTsfmtTslintFile() throws Exception { + writePomWithTypescriptSteps( + "", + " ${basedir}/tslint.json", + " 7.2.2", + ""); + setFile("tslint.json").toResource("typescript/tsfmt/tslint.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("typescript/tsfmt/TypescriptCodeFormatted.test"); + } + + @Test + @Ignore + public void testTypescriptTsfmtTsConfigFile() throws Exception { + writePomWithTypescriptSteps( + "", + " ${basedir}/", + " ${basedir}/tsconfig.json", + " ${basedir}/tsfmt.json", + " 7.2.2", + ""); + setFile("tsconfig.json").toResource("typescript/tsfmt/tsconfig.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("typescript/tsfmt/TypescriptCodeFormatted.test"); + } + + @Test + public void testTypescriptTsfmtTsFmtFile() throws Exception { + writePomWithTypescriptSteps( + "", + " ${basedir}/tsfmt.json", + " 7.2.2", + ""); + setFile("tsfmt.json").toResource("typescript/tsfmt/tsfmt.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("typescript/tsfmt/TypescriptCodeFormatted.test"); + } + + @Test + public void testTypescriptTsfmtVsCodeFile() throws Exception { + writePomWithTypescriptSteps( + "", + " ${basedir}/vscode.json", + " 7.2.2", + ""); + setFile("vscode.json").toResource("typescript/tsfmt/vscode.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("typescript/tsfmt/TypescriptCodeFormatted.test"); + } + + @Test + public void testTypescriptTsfmtInlineConfig() throws Exception { + writePomWithTypescriptSteps( + "", + " ", + " 1", + " true", + " ", + " 7.2.2", + ""); + setFile("vscode.json").toResource("typescript/tsfmt/vscode.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("typescript/tsfmt/TypescriptCodeFormatted.test"); + } + + @Test + public void testTypescript_2_Configs() throws Exception { + writePomWithTypescriptSteps( + "", + " ${basedir}/tslint.json", + " ${basedir}/tslint.json", + " 7.2.2", + ""); + setFile("vscode.json").toResource("typescript/tsfmt/vscode.json"); + setFile("tsfmt.json").toResource("typescript/tsfmt/tsfmt.json"); + + String path = "src/main/typescript/test.ts"; + setFile(path).toResource("typescript/tsfmt/TypescriptCodeUnformatted.test"); + 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/typescript/tsfmt/TypescriptCodeFormatted.test b/testlib/src/main/resources/typescript/tsfmt/TypescriptCodeFormatted.test new file mode 100644 index 0000000000..63d84d87ca --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/TypescriptCodeFormatted.test @@ -0,0 +1,4 @@ +import '@stencil/router'; + +class Sample { hello(word = "world") { return "Hello, " + word; } } +new Sample().hello("TypeScript"); \ No newline at end of file diff --git a/testlib/src/main/resources/typescript/tsfmt/TypescriptCodeUnformatted.test b/testlib/src/main/resources/typescript/tsfmt/TypescriptCodeUnformatted.test new file mode 100644 index 0000000000..e366b51e0d --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/TypescriptCodeUnformatted.test @@ -0,0 +1,4 @@ +import '@stencil/router'; + +class Sample{hello(word="world"){return "Hello, "+word;}} +new Sample().hello("TypeScript"); \ No newline at end of file diff --git a/testlib/src/main/resources/typescript/tsfmt/tsconfig.json b/testlib/src/main/resources/typescript/tsfmt/tsconfig.json new file mode 100644 index 0000000000..4b87261319 --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["src/main/typescript/*.ts"] +} \ No newline at end of file diff --git a/testlib/src/main/resources/typescript/tsfmt/tsfmt.json b/testlib/src/main/resources/typescript/tsfmt/tsfmt.json new file mode 100644 index 0000000000..86f08eced6 --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/tsfmt.json @@ -0,0 +1,24 @@ +{ + "baseIndentSize": 0, + "indentSize": 4, + "tabSize": 4, + "indentStyle": 2, + "newLineCharacter": "\r\n", + "convertTabsToSpaces": true, + "insertSpaceAfterCommaDelimiter": true, + "insertSpaceAfterSemicolonInForStatements": true, + "insertSpaceBeforeAndAfterBinaryOperators": true, + "insertSpaceAfterConstructor": false, + "insertSpaceAfterKeywordsInControlFlowStatements": true, + "insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true, + "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false, + "insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false, + "insertSpaceAfterTypeAssertion": false, + "insertSpaceBeforeFunctionParenthesis": false, + "insertSpaceBeforeTypeAnnotation": true, + "placeOpenBraceOnNewLineForFunctions": false, + "placeOpenBraceOnNewLineForControlBlocks": false +} \ No newline at end of file diff --git a/testlib/src/main/resources/typescript/tsfmt/tslint.json b/testlib/src/main/resources/typescript/tsfmt/tslint.json new file mode 100644 index 0000000000..3c77774f17 --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/tslint.json @@ -0,0 +1,11 @@ +{ + "rules": { + "indent": [true, 4], + "whitespace": [true, + "check-branch", + "check-operator", + "check-separator", + "check-typecast" + ] + } +} \ No newline at end of file diff --git a/testlib/src/main/resources/typescript/tsfmt/vscode.json b/testlib/src/main/resources/typescript/tsfmt/vscode.json new file mode 100644 index 0000000000..9f3ee12bf0 --- /dev/null +++ b/testlib/src/main/resources/typescript/tsfmt/vscode.json @@ -0,0 +1,15 @@ +{ + // Place your settings in this file to overwrite default and user settings. + "typescript.format.enable": true, + "typescript.format.insertSpaceAfterCommaDelimiter": true, + "typescript.format.insertSpaceAfterSemicolonInForStatements": true, + "typescript.format.insertSpaceBeforeAndAfterBinaryOperators": true, + "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": true, + "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false, + "typescript.format.placeOpenBraceOnNewLineForFunctions": false, + "typescript.format.placeOpenBraceOnNewLineForControlBlocks": false +} \ No newline at end of file