Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding npm-based formatters typescript-formatter (tsfmt) and prettier #283

Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
75ce9aa
initial implementation of nodebased steps
simschla Sep 7, 2018
dfe881d
Move tests for into lib-extra.
nedtwigg Aug 20, 2018
c0bb96f
Separate the `NpmTest` tests from the rest of the test suite.
nedtwigg Aug 20, 2018
991457c
renaming package nodebased -> npm
simschla Sep 7, 2018
164f047
move subpackages to npm top-level package
simschla Sep 7, 2018
37fff43
restrict visibility
simschla Sep 7, 2018
ddfc882
revert the from-file feature for JarState as it is not used right now
simschla Sep 7, 2018
ef4d244
adapt to package renaming
simschla Sep 7, 2018
4f16998
using map as config api for npm formatter steps
simschla Sep 7, 2018
74dfaee
clearing up resources after prettier/tsfmt run
simschla Sep 7, 2018
e706531
spotbugs cleanup
simschla Sep 7, 2018
b8fb907
cleanup autorelease-code
simschla Sep 7, 2018
11b32f4
restrict engine versions according to the npm packages used
simschla Sep 7, 2018
c02b3ef
dynamically select j2v8 lib based on platform
simschla Sep 7, 2018
27dc6f9
add support for closing FormatterFunc
simschla Sep 7, 2018
0b7f6a9
implement behavioral tests
simschla Sep 7, 2018
4b25b7c
validate working config options for tsfmt
simschla Sep 7, 2018
48430c8
normalized arch name (matching arch for for ci)
simschla Sep 7, 2018
ff1d3d3
validate multiple formatting filetypes are working
simschla Sep 7, 2018
d832ccd
test prettier configuration behavior
simschla Sep 7, 2018
ff9a3d2
auto-resolve npm binary (at least for tests very useful)
simschla Sep 7, 2018
3dfe3c9
adding gradle-integration for prettier
simschla Sep 7, 2018
d21f2bc
adding gradle-integration for tsfmt
simschla Sep 7, 2018
8b1cf97
Fix minor warnings in eclipse.
nedtwigg Sep 4, 2018
18c7748
Made as much of the npm implementation package-private as possible.
nedtwigg Sep 4, 2018
5550174
Fixed the gradle typescript extension.
nedtwigg Sep 4, 2018
092a488
Make the npm tests run on CI.
nedtwigg Sep 4, 2018
83178d1
Moved all of npm's test files into testlib so that they can be reused…
nedtwigg Sep 4, 2018
ded5885
readFileFromClasspath is now compatible with the gradle classpath
nedtwigg Sep 4, 2018
4ee647e
Added an integration test for TypescriptExtension, but it doesn't pas…
nedtwigg Sep 4, 2018
0b604ab
A super incomplete version of the docs, to plant the seed.
nedtwigg Sep 4, 2018
2197c27
first documentation draft
simschla Sep 7, 2018
732e27a
unify naming
simschla Sep 7, 2018
e400fa5
provide default basedir
simschla Sep 7, 2018
1fb5838
step 1 on supporting inline-config for typescript-formatter
simschla Sep 7, 2018
b5e57e7
introduce inline-config for tsfmt step
simschla Sep 7, 2018
2a21254
adding more integration tests for typescript extension
simschla Sep 7, 2018
3af839f
integration tests for prettier formatter
simschla Sep 7, 2018
c3c8ae1
unify usage of NpmTest category
simschla Sep 7, 2018
f817309
PR feedback: extract into sub-methods
simschla Sep 8, 2018
bf16cab
update documentation for tsfmt config file part
simschla Sep 9, 2018
3d3f304
smaller api for tsfmt config files
simschla Sep 11, 2018
6e07578
moving npm based formatters from lib-extra to lib
simschla Sep 12, 2018
ade884a
fixes for running on windows
simschla Sep 12, 2018
807bf7b
integrating PR feedback
simschla Sep 13, 2018
60f2bcd
Merge branch 'master' into feature/typescript-formatting-using-nodejs…
simschla Sep 16, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# Do the Gradle build
./gradlew build || exit 1
./gradlew npmTest || exit 1

if [ "$TRAVIS_REPO_SLUG" == "diffplug/spotless" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
# Make sure that all pom are up-to-date
Expand Down
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
language: java
jdk:
- oraclejdk8
- oraclejdk8
env:
- NODE_VERSION="6.10.2"
before_install:
- nvm install $NODE_VERSION
install: true
script:
- ./.ci/ci.sh
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ output = [
[![License Apache](https://img.shields.io/badge/license-apache-brightgreen.svg)](https://tldrlegal.com/license/apache-license-2.0-(apache-2.0))
<!---freshmark /shields -->

Spotless can format &lt;java | kotlin | scala | sql | groovy | markdown | license headers | anything> using &lt;gradle | maven | anything>.
Spotless can format &lt;java | kotlin | scala | sql | groovy | javascript | flow | typeScript | css | scss | less | jsx | vue | graphql | json | yaml | markdown | license headers | anything> using &lt;gradle | maven | anything>.

- [Spotless for Gradle](plugin-gradle)
- [Spotless for Maven](plugin-maven)
Expand Down Expand Up @@ -45,6 +45,8 @@ 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.TsFmtFormatterStep') +'{{yes}} | {{no}} | {{no}} |',
lib('scala.ScalaFmtStep') +'{{yes}} | {{yes}} | {{no}} |',
lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{no}} | {{no}} |',
'| [(Your FormatterStep here)](CONTRIBUTING.md#how-to-add-a-new-formatterstep) | {{no}} | {{no}} | {{no}} |',
Expand All @@ -68,6 +70,8 @@ lib('sql.DBeaverSQLFormatterStep') +'{{yes}} | {{no}}
| [`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.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :white_large_square: | :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: |
| [(Your FormatterStep here)](CONTRIBUTING.md#how-to-add-a-new-formatterstep) | :white_large_square: | :white_large_square: | :white_large_square: |
Expand Down
1 change: 0 additions & 1 deletion lib-extra/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ dependencies {

// we'll hold the core lib to a high standard
spotbugs { reportLevel = 'low' } // low|medium|high (low = sensitive to even minor mistakes)

12 changes: 3 additions & 9 deletions lib/src/main/java/com/diffplug/spotless/JarState.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@
import java.io.Serializable;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
import java.util.stream.Collectors;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
Expand All @@ -35,7 +29,7 @@
* Grabs a jar and its dependencies from maven,
* and makes it easy to access the collection in
* a classloader.
*
* <p>
simschla marked this conversation as resolved.
Show resolved Hide resolved
* Serializes the full state of the jar, so it can
* catch changes in a SNAPSHOT version.
*/
Expand Down Expand Up @@ -85,7 +79,7 @@ URL[] jarUrls() {

/**
* Returns a classloader containing only the jars in this JarState.
*
* <p>
* The lifetime of the underlying cacheloader is controlled by {@link SpotlessCache}.
*/
public ClassLoader getClassLoader() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.npm;

class BlacklistedOptionException extends RuntimeException {
private static final long serialVersionUID = -5876348893394153811L;

public BlacklistedOptionException(String blacklistedOption) {
super("The config option '" + blacklistedOption + "' is not supported.");
}
}
106 changes: 106 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/npm/NodeJSWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.npm;

import java.io.File;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

class NodeJSWrapper extends ReflectiveObjectWrapper {

public static final String V8_RUNTIME_CLASS = "com.eclipsesource.v8.V8";
public static final String V8_VALUE_CLASS = "com.eclipsesource.v8.V8Value";

public static final String WRAPPED_CLASS = "com.eclipsesource.v8.NodeJS";

private static final AtomicBoolean flagsSet = new AtomicBoolean(false);

public NodeJSWrapper(ClassLoader classLoader) {
super(Reflective.withClassLoader(classLoader),
reflective -> {
final boolean firstRun = flagsSet.compareAndSet(false, true);
if (firstRun) {
reflective.invokeStaticMethod(V8_RUNTIME_CLASS, "setFlags", "-color=false"); // required to run prettier on windows
simschla marked this conversation as resolved.
Show resolved Hide resolved
}
return reflective.invokeStaticMethod(WRAPPED_CLASS, "createNodeJS");
});
}

public V8ObjectWrapper require(File npmModulePath) {
Objects.requireNonNull(npmModulePath);
Object v8Object = invoke("require", npmModulePath);
return new V8ObjectWrapper(reflective(), v8Object);
}

public V8ObjectWrapper createNewObject() {
Object v8Object = reflective().invokeConstructor(V8ObjectWrapper.WRAPPED_CLASS, nodeJsRuntime());
V8ObjectWrapper objectWrapper = new V8ObjectWrapper(reflective(), v8Object);
return objectWrapper;
}

public V8ObjectWrapper createNewObject(Map<String, Object> values) {
Objects.requireNonNull(values);
V8ObjectWrapper obj = createNewObject();
values.forEach(obj::add);
return obj;
}

public V8ArrayWrapper createNewArray(Object... elements) {
final V8ArrayWrapper v8ArrayWrapper = this.createNewArray();
for (Object element : elements) {
v8ArrayWrapper.push(element);
}
return v8ArrayWrapper;
}

public V8ArrayWrapper createNewArray() {
Object v8Array = reflective().invokeConstructor(V8ArrayWrapper.WRAPPED_CLASS, nodeJsRuntime());
V8ArrayWrapper arrayWrapper = new V8ArrayWrapper(reflective(), v8Array);
return arrayWrapper;
}

public V8FunctionWrapper createNewFunction(V8FunctionWrapper.WrappedJavaCallback callback) {
Object v8Function = reflective().invokeConstructor(V8FunctionWrapper.WRAPPED_CLASS,
reflective().typed(
V8_RUNTIME_CLASS,
nodeJsRuntime()),
reflective().typed(
V8FunctionWrapper.CALLBACK_WRAPPED_CLASS,
V8FunctionWrapper.proxiedCallback(callback, reflective())));
V8FunctionWrapper functionWrapper = new V8FunctionWrapper(reflective(), v8Function);
return functionWrapper;
}

public void handleMessage() {
invoke("handleMessage");
}

private Object nodeJsRuntime() {
return invoke("getRuntime");
}

public Object v8NullValue(Object value) {
if (value == null) {
return reflective().staticField(V8_VALUE_CLASS, "NULL");
}
return value;
}

public boolean isV8NullValue(Object v8Object) {
return reflective().staticField(V8_VALUE_CLASS, "NULL") == v8Object;
}
}
40 changes: 40 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/npm/NpmConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.npm;

import java.io.Serializable;

class NpmConfig implements Serializable {

private static final long serialVersionUID = -1866722789779160491L;

private final String packageJsonContent;

private final String npmModule;

public NpmConfig(String packageJsonContent, String npmModule) {
this.packageJsonContent = packageJsonContent;
this.npmModule = npmModule;
}

public String getPackageJsonContent() {
return packageJsonContent;
}

public String getNpmModule() {
return npmModule;
}
}
108 changes: 108 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.npm;

import static com.diffplug.spotless.npm.PlatformInfo.OS.WINDOWS;

import java.io.File;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
* Utility class to resolve an npm binary to be used by npm-based steps.
* Tries to find an npm executable in the following order:
* <ol>
* <li>from System-Property {@code npm.exec} (unverified)</li>
* <li>from Environment-Properties in the following order:</li>
* <ol>
* <li> from NVM_BIN environment variable, if available </li>
* <li> from NVM_SYMLINK environment variable, if available </li>
* <li> from NODE_PATH environment variable, if available </li>
* <li>fallback: PATH environment variable</li>
* </ol>
* </ol>
*/
class NpmExecutableResolver {

private NpmExecutableResolver() {
// no instance
}

static String npmExecutableName() {
String npmName = "npm";
if (PlatformInfo.normalizedOS() == WINDOWS) {
npmName += ".cmd";
}
return npmName;
}

static Supplier<Optional<File>> systemProperty() {
return () -> Optional.ofNullable(System.getProperty("npm.exec"))
.map(File::new);
}

static Supplier<Optional<File>> environmentNvmBin() {
return () -> Optional.ofNullable(System.getenv("NVM_BIN"))
.map(File::new)
.map(binDir -> new File(binDir, npmExecutableName()))
.filter(File::exists)
.filter(File::canExecute);
}

static Supplier<Optional<File>> environmentNvmSymlink() {
return pathListFromEnvironment("NVM_SYMLINK");
}

static Supplier<Optional<File>> environmentNodepath() {
return pathListFromEnvironment("NODE_PATH");
}

static Supplier<Optional<File>> environmentPath() {
return pathListFromEnvironment("PATH");
}

static Optional<File> tryFind() {
return Stream.of(systemProperty(),
environmentNvmBin(),
environmentNvmSymlink(),
environmentNodepath(),
environmentPath())
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}

private static Supplier<Optional<File>> pathListFromEnvironment(String environmentPathListName) {
return () -> {
String pathList = System.getenv(environmentPathListName);
if (pathList != null) {
return Arrays.stream(pathList.split(System.getProperty("path.separator", ":")))
.map(File::new)
.map(dir -> dir.getName().equalsIgnoreCase("node_modules") ? dir.getParentFile() : dir)
.map(dir -> new File(dir, npmExecutableName()))
.filter(File::exists)
.filter(File::canExecute)
.findFirst();

}
return Optional.empty();
};
}

}
Loading