From df9a4179f4ce62446bf3088882af8a3a4d239d5e Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Tue, 15 Sep 2020 20:51:10 +0200 Subject: [PATCH 1/9] extract FileFinder into its own class for reuse --- .../com/diffplug/spotless/npm/FileFinder.java | 190 ++++++++++++++++++ .../spotless/npm/NpmExecutableResolver.java | 69 ++----- 2 files changed, 210 insertions(+), 49 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java b/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java new file mode 100644 index 0000000000..49c892a87b --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java @@ -0,0 +1,190 @@ +/* + * Copyright 2020 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.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +class FileFinder { + + private final String fileName; + + private final List>> fileCandidateFinders; + + private FileFinder(Builder builder) { + this.fileName = builder.fileName; + this.fileCandidateFinders = Collections.unmodifiableList(new ArrayList<>(builder.candidateFinders)); + } + + static Builder finderForFilename(String fileName) { + return new Builder(fileName, null); + } + + static Builder finderForExecutableFilename(String fileName) { + return new Builder(fileName, true); + } + + Optional tryFind() { + return fileCandidateFinders + .stream() + .map(Supplier::get) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); + } + + static class Builder { + + private final String fileName; + + private final Boolean executable; + + private final List>> candidateFinders = new ArrayList<>(); + + Builder(String fileName, Boolean executable) { + this.fileName = requireNonNull(fileName); + this.executable = executable; + } + + public Builder candidateEnvironmentPath(String environmentVar) { + candidateFinders.add(new CandidateOnSinglePathEnvironmentVar(environmentVar, fileName, FileIsExecutableFilter.executable(this.executable))); + return this; + } + + public Builder candidateEnvironmentPathList(String environmentVar, Function fileTransformer) { + candidateFinders.add(new CandidateOnPathListEnvironmentVar(environmentVar, fileName, fileTransformer, FileIsExecutableFilter.executable(this.executable))); + return this; + } + + public Builder candidateSystemProperty(String systemProperty) { + candidateFinders.add(new CandidateOnSystemPropertyVar(systemProperty, FileIsExecutableFilter.executable(this.executable))); + return this; + } + + public FileFinder build() { + return new FileFinder(this); + } + } + + private static class FileIsExecutableFilter implements Predicate { + @Override + public boolean test(File file) { + return file.canExecute(); + } + + static Predicate executable() { + return new FileIsExecutableFilter(); + } + + static Predicate executable(Boolean executable) { + if (executable == null) { + return AnyFileFilter.any(); + } + if (executable) { + return executable(); + } + // !executable + return executable().negate(); + } + } + + private static class AnyFileFilter implements Predicate { + + @Override + public boolean test(File file) { + return true; + } + + static AnyFileFilter any() { + return new AnyFileFilter(); + } + } + + private static class CandidateOnSinglePathEnvironmentVar implements Supplier> { + private final String environmentVar; + private final String fileName; + private final Predicate additionalFilters; + + public CandidateOnSinglePathEnvironmentVar(String environmentVar, String fileName, Predicate additionalFilter) { + this.environmentVar = environmentVar; + this.fileName = fileName; + this.additionalFilters = additionalFilter == null ? AnyFileFilter.any() : additionalFilter; + } + + @Override + public Optional get() { + return Optional.ofNullable(environmentVar) + .map(File::new) + .map(file -> new File(file, fileName)) + .filter(File::exists) + .filter(additionalFilters); + } + } + + private static class CandidateOnPathListEnvironmentVar implements Supplier> { + private final String environmentVar; + private final String fileName; + private final Function fileTransformer; + private final Predicate additionalFilter; + + public CandidateOnPathListEnvironmentVar(String environmentVar, String fileName, Function fileTransformer, Predicate additionalFilter) { + this.environmentVar = environmentVar; + this.fileName = fileName; + this.fileTransformer = fileTransformer; + this.additionalFilter = additionalFilter; + } + + @Override + public Optional get() { + String pathList = System.getenv(environmentVar); + if (pathList != null) { + return Arrays.stream(pathList.split(System.getProperty("path.separator", ":"))) + .map(File::new) + .filter(File::exists) + .map(fileTransformer) + .map(dir -> new File(dir, fileName)) + .filter(File::exists) + .filter(additionalFilter) + .findFirst(); + } + return Optional.empty(); + } + } + + private static class CandidateOnSystemPropertyVar implements Supplier> { + private final String systemProperty; + private final Predicate additionalFilter; + + public CandidateOnSystemPropertyVar(String systemProperty, Predicate additionalFilter) { + this.systemProperty = systemProperty; + this.additionalFilter = additionalFilter == null ? AnyFileFilter.any() : additionalFilter; + + } + + @Override + public Optional get() { + return Optional.ofNullable(System.getProperty(this.systemProperty)) + .map(File::new) + .filter(File::exists) + .filter(additionalFilter); + } + } +} diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java index 5be273621e..24492b08e9 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java @@ -18,10 +18,8 @@ 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; +import java.util.function.Function; /** * Utility class to resolve an npm binary to be used by npm-based steps. @@ -39,6 +37,14 @@ */ class NpmExecutableResolver { + private static final FileFinder NPM_EXECUTABLE_FINDER = FileFinder.finderForExecutableFilename(npmExecutableName()) + .candidateSystemProperty("npm.exec") + .candidateEnvironmentPath("NVM_BIN") + .candidateEnvironmentPathList("NVM_SYMLINK", resolveParentOfNodeModulesDir()) + .candidateEnvironmentPathList("NODE_PATH", resolveParentOfNodeModulesDir()) + .candidateEnvironmentPathList("PATH", resolveParentOfNodeModulesDir()) + .build(); + private NpmExecutableResolver() { // no instance } @@ -51,58 +57,23 @@ static String npmExecutableName() { return npmName; } - static Supplier> systemProperty() { - return () -> Optional.ofNullable(System.getProperty("npm.exec")) - .map(File::new); - } - - static Supplier> environmentNvmBin() { - return () -> Optional.ofNullable(System.getenv("NVM_BIN")) - .map(File::new) - .map(binDir -> new File(binDir, npmExecutableName())) - .filter(File::exists) - .filter(File::canExecute); + private static ParentOfNodeModulesDirResolver resolveParentOfNodeModulesDir() { + return new ParentOfNodeModulesDirResolver(); } - static Supplier> environmentNvmSymlink() { - return pathListFromEnvironment("NVM_SYMLINK"); - } - - static Supplier> environmentNodepath() { - return pathListFromEnvironment("NODE_PATH"); - } + static class ParentOfNodeModulesDirResolver implements Function { - static Supplier> environmentPath() { - return pathListFromEnvironment("PATH"); + @Override + public File apply(File file) { + if (file != null && file.isDirectory() && file.getName().equalsIgnoreCase("node_modules")) { + return file.getParentFile(); + } + return file; + } } static Optional tryFind() { - return Stream.of(systemProperty(), - environmentNvmBin(), - environmentNvmSymlink(), - environmentNodepath(), - environmentPath()) - .map(Supplier::get) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); - } - - private static Supplier> 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(); - }; + return NPM_EXECUTABLE_FINDER.tryFind(); } static String explainMessage() { From 2a1a6b92c1b61375dfba9b9f76ff360266ce4351 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 16 Sep 2020 22:28:08 +0200 Subject: [PATCH 2/9] introduce finder for finding .npmrc files --- .../com/diffplug/spotless/npm/FileFinder.java | 31 +++++++++-- .../diffplug/spotless/npm/NpmrcResolver.java | 53 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/NpmrcResolver.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java b/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java index 49c892a87b..76016d68db 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/FileFinder.java @@ -25,12 +25,9 @@ class FileFinder { - private final String fileName; - private final List>> fileCandidateFinders; private FileFinder(Builder builder) { - this.fileName = builder.fileName; this.fileCandidateFinders = Collections.unmodifiableList(new ArrayList<>(builder.candidateFinders)); } @@ -79,6 +76,11 @@ public Builder candidateSystemProperty(String systemProperty) { return this; } + public Builder candidateFileInFolder(File folder) { + candidateFinders.add(new CandidateInFolder(folder, fileName, FileIsExecutableFilter.executable(this.executable))); + return this; + } + public FileFinder build() { return new FileFinder(this); } @@ -187,4 +189,27 @@ public Optional get() { .filter(additionalFilter); } } + + private static class CandidateInFolder implements Supplier> { + + private final File folder; + private final String fileName; + private final Predicate additionalFilter; + + public CandidateInFolder(File folder, String fileName, Predicate additionalFilter) { + this.folder = folder; + this.fileName = fileName; + this.additionalFilter = additionalFilter == null ? AnyFileFilter.any() : additionalFilter; + } + + @Override + public Optional get() { + return Optional.of(folder) + .filter(File::exists) + .filter(File::isDirectory) + .map(folder -> new File(folder, fileName)) + .filter(File::exists) + .filter(additionalFilter); + } + } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmrcResolver.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmrcResolver.java new file mode 100644 index 0000000000..64e2bb7d41 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmrcResolver.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2020 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.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * Utility class to resolve a {@code .npmrc} file to use in our node config. + * Tries to find a {@code .npmrc} config file in the following order: + *
    + *
  1. from System-Property {@code npm.npmrc}
  2. + *
  3. from 0..n projectLocations (specified by arguments)
  4. + *
  5. from the user home directory, resolved via environment variable{@code $HOME}
  6. + *
+ */ +class NpmrcResolver { + + private final FileFinder npmrcFileFinder; + + NpmrcResolver(File... projectLocations) { + this(Arrays.asList(projectLocations)); + } + + NpmrcResolver(List projectLocations) { + // no instance + final FileFinder.Builder finderBuilder = FileFinder.finderForFilename(".npmrc") + .candidateSystemProperty("npm.npmrc"); + projectLocations.forEach(finderBuilder::candidateFileInFolder); + npmrcFileFinder = finderBuilder + .candidateEnvironmentPath("HOME") + .build(); + } + + Optional tryFind() { + return npmrcFileFinder.tryFind(); + } +} From bc5023b8800b0c41ef37d3b203ea644f62d1469a Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Wed, 16 Sep 2020 22:34:24 +0200 Subject: [PATCH 3/9] use dedicated npmPathResolver to find npm exec and .npmrc --- .../spotless/npm/NodeServerLayout.java | 6 +++ .../com/diffplug/spotless/npm/NpmConfig.java | 11 +++- .../spotless/npm/NpmExecutableResolver.java | 2 +- .../npm/NpmFormatterStepStateBase.java | 19 +++---- .../spotless/npm/NpmPathResolver.java | 53 +++++++++++++++++++ .../spotless/npm/PrettierFormatterStep.java | 12 ++--- .../spotless/npm/TsFmtFormatterStep.java | 11 ++-- .../gradle/spotless/FormatExtension.java | 20 ++++++- .../gradle/spotless/TypescriptExtension.java | 3 +- .../spotless/maven/generic/Prettier.java | 9 +++- .../spotless/maven/typescript/Tsfmt.java | 11 +++- .../npm/NpmFormatterStepCommonTests.java | 12 ++++- .../npm/PrettierFormatterStepTest.java | 10 ++-- .../spotless/npm/TsFmtFormatterStepTest.java | 6 +-- 14 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/npm/NpmPathResolver.java diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NodeServerLayout.java b/lib/src/main/java/com/diffplug/spotless/npm/NodeServerLayout.java index ef873ed2af..f34520f3e4 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NodeServerLayout.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NodeServerLayout.java @@ -22,11 +22,13 @@ class NodeServerLayout { private final File nodeModulesDir; private final File packageJsonFile; private final File serveJsFile; + private final File npmrcFile; NodeServerLayout(File buildDir, String stepName) { this.nodeModulesDir = new File(buildDir, "spotless-node-modules-" + stepName); this.packageJsonFile = new File(nodeModulesDir, "package.json"); this.serveJsFile = new File(nodeModulesDir, "serve.js"); + this.npmrcFile = new File(nodeModulesDir, ".npmrc"); } File nodeModulesDir() { @@ -40,4 +42,8 @@ File packageJsonFile() { File serveJsFile() { return serveJsFile; } + + public File npmrcFile() { + return npmrcFile; + } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmConfig.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmConfig.java index 7fe3daf0b7..1492fe7a99 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmConfig.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmConfig.java @@ -21,7 +21,7 @@ class NpmConfig implements Serializable { - private static final long serialVersionUID = -7660089232952131272L; + private static final long serialVersionUID = 684264546497914877L; private final String packageJsonContent; @@ -29,10 +29,13 @@ class NpmConfig implements Serializable { private final String serveScriptContent; - public NpmConfig(String packageJsonContent, String npmModule, String serveScriptContent) { + private final String npmrcContent; + + public NpmConfig(String packageJsonContent, String npmModule, String serveScriptContent, String npmrcContent) { this.packageJsonContent = packageJsonContent; this.npmModule = npmModule; this.serveScriptContent = serveScriptContent; + this.npmrcContent = npmrcContent; } public String getPackageJsonContent() { @@ -47,4 +50,8 @@ public String getNpmModule() { public String getServeScriptContent() { return serveScriptContent; } + + public String getNpmrcContent() { + return npmrcContent; + } } diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java index 24492b08e9..8c233abc8c 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmExecutableResolver.java @@ -25,7 +25,7 @@ * Utility class to resolve an npm binary to be used by npm-based steps. * Tries to find an npm executable in the following order: *
    - *
  1. from System-Property {@code npm.exec} (unverified)
  2. + *
  3. from System-Property {@code npm.exec}
  4. *
  5. from Environment-Properties in the following order:
  6. *
      *
    1. from NVM_BIN environment variable, if available
    2. 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 22752aae31..01c5d82138 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmFormatterStepStateBase.java @@ -25,13 +25,10 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; -import javax.annotation.Nullable; - import com.diffplug.spotless.FileSignature; import com.diffplug.spotless.FormatterFunc; @@ -56,11 +53,10 @@ abstract class NpmFormatterStepStateBase implements Serializable { private final String stepName; - protected NpmFormatterStepStateBase(String stepName, NpmConfig npmConfig, File buildDir, - @Nullable File npm) throws IOException { + protected NpmFormatterStepStateBase(String stepName, NpmConfig npmConfig, File buildDir, File npm) throws IOException { this.stepName = requireNonNull(stepName); this.npmConfig = requireNonNull(npmConfig); - this.npmExecutable = resolveNpm(npm); + this.npmExecutable = npm; NodeServerLayout layout = prepareNodeServer(buildDir); this.nodeModulesDir = layout.nodeModulesDir(); @@ -74,6 +70,11 @@ private NodeServerLayout prepareNodeServer(File buildDir) throws IOException { this.npmConfig.getPackageJsonContent()); NpmResourceHelper .writeUtf8StringToFile(layout.serveJsFile(), this.npmConfig.getServeScriptContent()); + if (this.npmConfig.getNpmrcContent() != null) { + NpmResourceHelper.writeUtf8StringToFile(layout.npmrcFile(), this.npmConfig.getNpmrcContent()); + } else { + NpmResourceHelper.deleteFileIfExists(layout.npmrcFile()); + } FormattedPrinter.SYSOUT.print("running npm install"); runNpmInstall(layout.nodeModulesDir()); FormattedPrinter.SYSOUT.print("npm install finished"); @@ -116,12 +117,6 @@ protected ServerProcessInfo npmRunServer() throws ServerStartException { } } - private static File resolveNpm(@Nullable File npm) { - return Optional.ofNullable(npm) - .orElseGet(() -> NpmExecutableResolver.tryFind() - .orElseThrow(() -> new IllegalStateException("Can't automatically determine npm executable and none was specifically supplied!\n\n" + NpmExecutableResolver.explainMessage()))); - } - protected static String replaceDevDependencies(String template, Map devDependencies) { StringBuilder builder = new StringBuilder(); Iterator> entryIter = devDependencies.entrySet().iterator(); diff --git a/lib/src/main/java/com/diffplug/spotless/npm/NpmPathResolver.java b/lib/src/main/java/com/diffplug/spotless/npm/NpmPathResolver.java new file mode 100644 index 0000000000..a9a15bd49d --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/npm/NpmPathResolver.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 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.Arrays; +import java.util.List; +import java.util.Optional; + +public class NpmPathResolver { + + private final File explicitNpmExecutable; + + private final File explicitNpmrcFile; + + private final List additionalNpmrcLocations; + + public NpmPathResolver(File explicitNpmExecutable, File explicitNpmrcFile, File... additionalNpmrcLocations) { + this.explicitNpmExecutable = explicitNpmExecutable; + this.explicitNpmrcFile = explicitNpmrcFile; + this.additionalNpmrcLocations = Arrays.asList(additionalNpmrcLocations); + } + + public File resolveNpmExecutable() { + return Optional.ofNullable(this.explicitNpmExecutable) + .orElseGet(() -> NpmExecutableResolver.tryFind() + .orElseThrow(() -> new IllegalStateException("Can't automatically determine npm executable and none was specifically supplied!\n\n" + NpmExecutableResolver.explainMessage()))); + } + + public String resolveNpmrcContent() { + File npmrcFile = Optional.ofNullable(this.explicitNpmrcFile) + .orElseGet(() -> new NpmrcResolver(additionalNpmrcLocations).tryFind() + .orElse(null)); + if (npmrcFile != null) { + return NpmResourceHelper.readUtf8StringFromFile(npmrcFile); + } + return null; + } + +} 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 5613f2e2d9..c65fcf8b8c 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java @@ -27,7 +27,6 @@ import java.util.logging.Logger; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.FormatterFunc.Closeable; @@ -49,12 +48,12 @@ public static final Map defaultDevDependenciesWithPrettier(Strin return Collections.singletonMap("prettier", version); } - public static FormatterStep create(Map devDependencies, Provisioner provisioner, File buildDir, @Nullable File npm, PrettierConfig prettierConfig) { + public static FormatterStep create(Map devDependencies, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) { requireNonNull(devDependencies); requireNonNull(provisioner); requireNonNull(buildDir); return FormatterStep.createLazy(NAME, - () -> new State(NAME, devDependencies, buildDir, npm, prettierConfig), + () -> new State(NAME, devDependencies, buildDir, npmPathResolver, prettierConfig), State::createFormatterFunc); } @@ -63,16 +62,17 @@ 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, @Nullable File npm, PrettierConfig prettierConfig) throws IOException { + State(String stepName, Map devDependencies, File buildDir, NpmPathResolver npmPathResolver, PrettierConfig prettierConfig) throws IOException { super(stepName, new NpmConfig( replaceDevDependencies( 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/prettier-serve.js"), + npmPathResolver.resolveNpmrcContent()), buildDir, - npm); + 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 3d2b1fcbc6..457e4dcaf7 100644 --- a/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java @@ -39,11 +39,11 @@ public class TsFmtFormatterStep { public static final String NAME = "tsfmt-format"; - public static FormatterStep create(Map versions, Provisioner provisioner, File buildDir, @Nullable File npm, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) { + public static FormatterStep create(Map versions, Provisioner provisioner, File buildDir, NpmPathResolver npmPathResolver, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) { requireNonNull(provisioner); requireNonNull(buildDir); return FormatterStep.createLazy(NAME, - () -> new State(NAME, versions, buildDir, npm, configFile, inlineTsFmtSettings), + () -> new State(NAME, versions, buildDir, npmPathResolver, configFile, inlineTsFmtSettings), State::createFormatterFunc); } @@ -70,14 +70,15 @@ public static class State extends NpmFormatterStepStateBase implements Serializa @Nullable private final TypedTsFmtConfigFile configFile; - public State(String stepName, Map versions, File buildDir, @Nullable File npm, @Nullable TypedTsFmtConfigFile configFile, @Nullable Map inlineTsFmtSettings) throws IOException { + public State(String stepName, Map versions, 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)), "typescript-formatter", - NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, "/com/diffplug/spotless/npm/tsfmt-serve.js")), + NpmResourceHelper.readUtf8StringFromClasspath(PrettierFormatterStep.class, "/com/diffplug/spotless/npm/tsfmt-serve.js"), + npmPathResolver.resolveNpmrcContent()), buildDir, - npm); + npmPathResolver.resolveNpmExecutable()); this.buildDir = requireNonNull(buildDir); this.configFile = configFile; this.inlineTsFmtSettings = inlineTsFmtSettings == null ? new TreeMap<>() : new TreeMap<>(inlineTsFmtSettings); 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 1f4f90352d..88c3755508 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 @@ -58,6 +58,7 @@ import com.diffplug.spotless.generic.ReplaceRegexStep; import com.diffplug.spotless.generic.ReplaceStep; import com.diffplug.spotless.generic.TrimTrailingWhitespaceStep; +import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; import groovy.lang.Closure; @@ -494,6 +495,9 @@ public abstract class NpmStepConfig> { @Nullable protected Object npmFile; + @Nullable + protected Object npmrcFile; + @SuppressWarnings("unchecked") public T npmExecutable(final Object npmFile) { this.npmFile = npmFile; @@ -501,7 +505,21 @@ public T npmExecutable(final Object npmFile) { return (T) this; } + public T npmrc(final Object npmrcFile) { + this.npmrcFile = npmrcFile; + replaceStep(createStep()); + return (T) this; + } + File npmFileOrNull() { + return fileOrNull(npmFile); + } + + File npmrcFileOrNull() { + return fileOrNull(npmrcFile); + } + + private File fileOrNull(Object npmFile) { return npmFile != null ? getProject().file(npmFile) : null; } @@ -541,7 +559,7 @@ FormatterStep createStep() { devDependencies, provisioner(), project.getBuildDir(), - npmFileOrNull(), + new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), new com.diffplug.spotless.npm.PrettierConfig( this.prettierConfigFile != null ? project.file(this.prettierConfigFile) : null, this.prettierConfig)); 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 08b2b463e6..58b9d4acbb 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 @@ -28,6 +28,7 @@ import org.gradle.api.Project; import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierFormatterStep; import com.diffplug.spotless.npm.TsConfigFileType; import com.diffplug.spotless.npm.TsFmtFormatterStep; @@ -109,7 +110,7 @@ public FormatterStep createStep() { devDependencies, provisioner(), project.getBuildDir(), - npmFileOrNull(), + new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()), typedConfigFile(), config); } 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 cfd31d9294..85684e85cd 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 @@ -24,6 +24,7 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.PrettierConfig; import com.diffplug.spotless.npm.PrettierFormatterStep; @@ -49,6 +50,9 @@ public class Prettier implements FormatterStepFactory { @Parameter private String npmExecutable; + @Parameter + private String npmrc; + @Override public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { @@ -68,6 +72,8 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { 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) { @@ -99,7 +105,8 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { // create the format step PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline); File buildDir = stepConfig.getFileLocator().getBuildDir(); - return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npm, prettierConfig); + NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, stepConfig.getFileLocator().getBaseDir()); + return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), 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 faea03f2a0..9ebb9ac503 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 DiffPlug + * Copyright 2016-2020 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.maven.FormatterStepConfig; import com.diffplug.spotless.maven.FormatterStepFactory; +import com.diffplug.spotless.npm.NpmPathResolver; import com.diffplug.spotless.npm.TsConfigFileType; import com.diffplug.spotless.npm.TsFmtFormatterStep; import com.diffplug.spotless.npm.TypedTsFmtConfigFile; @@ -54,6 +55,9 @@ public class Tsfmt implements FormatterStepFactory { @Parameter private String npmExecutable; + @Parameter + private String npmrc; + @Parameter private Map config; @@ -72,6 +76,8 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { 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 @@ -114,7 +120,8 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) { } File buildDir = stepConfig.getFileLocator().getBuildDir(); - return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npm, configFile, configInline); + NpmPathResolver npmPathResolver = new NpmPathResolver(npm, npmrcFile, stepConfig.getFileLocator().getBaseDir()); + return TsFmtFormatterStep.create(devDependencies, stepConfig.getProvisioner(), buildDir, npmPathResolver, configFile, configInline); } private static IllegalArgumentException onlyOneConfig() { 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 79b58ab5a9..16b1b29477 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 DiffPlug + * Copyright 2016-2020 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,18 @@ public abstract class NpmFormatterStepCommonTests extends ResourceHarness { - protected File npmExecutable() { + protected NpmPathResolver npmPathResolver() { + return new NpmPathResolver(npmExecutable(), npmrc()); + } + + private File npmExecutable() { return NpmExecutableResolver.tryFind().orElseThrow(() -> new IllegalStateException("cannot detect node binary")); } + private File npmrc() { + return new NpmrcResolver().tryFind().orElse(null); + } + private File buildDir = null; protected File buildDir() throws IOException { 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 1ac24e8422..b11971364a 100644 --- a/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/npm/PrettierFormatterStepTest.java @@ -57,7 +57,7 @@ public void formattingUsingConfigFile() throws Exception { PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), new PrettierConfig(prettierRc, null)); try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { @@ -80,7 +80,7 @@ public void parserInferenceBasedOnExplicitFilepathIsWorking() throws Exception { PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"))); // should select parser based on this name try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { @@ -99,7 +99,7 @@ public void parserInferenceBasedOnFilenameIsWorking() throws Exception { PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), new PrettierConfig(null, Collections.emptyMap())); try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(formatterStep)) { @@ -113,7 +113,7 @@ public void verifyPrettierErrorMessageIsRelayed() throws Exception { PrettierFormatterStep.defaultDevDependenciesWithPrettier("2.0.5"), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), new PrettierConfig(null, ImmutableMap.of("parser", "postcss"))); try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { stepHarness.testResourceException("npm/prettier/filetypes/scss/scss.dirty", exception -> { @@ -138,7 +138,7 @@ public void runFormatTest(PrettierConfig config, String cleanFileNameSuffix) thr PrettierFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), config); // should select parser based on this name try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) { 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 41858d2faa..8ec4ce747b 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 DiffPlug + * Copyright 2016-2020 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,7 +65,7 @@ public void formattingUsingConfigFile() throws Exception { TsFmtFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), TypedTsFmtConfigFile.named(configFileNameWithoutExtension, configFile), Collections.emptyMap()); @@ -86,7 +86,7 @@ public void formattingUsingInlineConfigWorks() throws Exception { TsFmtFormatterStep.defaultDevDependencies(), TestProvisioner.mavenCentral(), buildDir(), - npmExecutable(), + npmPathResolver(), null, inlineConfig); From 7482ea3e630e4337823790ec5247fcf6e6b66696 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 23 Oct 2020 11:03:05 +0200 Subject: [PATCH 4/9] adding int test to verify .npmrc is picked up --- .../spotless/PrettierIntegrationTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 1e1b00c6b0..cd5e943e07 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 @@ -160,4 +160,27 @@ public void usePhpCommunityPlugin() throws IOException { Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL"); assertFile("php-example.php").sameAsResource("npm/prettier/plugins/php.clean"); } + + @Test + public void useNpmrcFileConfig() throws IOException { + setFile(".npmrc").toLines( + "registry=https://i.do.no.exist.com"); + setFile("build.gradle").toLines( + "buildscript { repositories { mavenCentral() } }", + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "def prettierConfig = [:]", + "prettierConfig['printWidth'] = 50", + "prettierConfig['parser'] = 'typescript'", + "spotless {", + " format 'mytypescript', {", + " target 'test.ts'", + " prettier().config(prettierConfig)", + " }", + "}"); + setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); + final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); + Assertions.assertThat(spotlessApply.getOutput()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } } From 8f5093692dce970603cb0d9baf449d8b859eda51 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Mon, 26 Oct 2020 20:57:43 +0100 Subject: [PATCH 5/9] add documentation for configuration of .npmrc file location --- plugin-gradle/README.md | 24 ++++++++++++++++++++++++ plugin-maven/README.md | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index e62d9086d5..660e9e1542 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -524,6 +524,19 @@ spotless { tsfmt().npmExecutable('/usr/bin/npm').config(...) ``` +**Configuring npm using `.npmrc` file** + +Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. +Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) + +```gradle +spotless { + typescript { + tsfmt().npmrc("$projectDir/config/.npmrc").config(...) +``` + + + ## Prettier @@ -591,6 +604,17 @@ spotless { prettier().npmExecutable('/usr/bin/npm').config(...) ``` +### Configuring npm using `.npmrc` file + +Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. +Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) + +```gradle +spotless { + typescript { + prettier().npmrc("$projectDir/config/.npmrc").config(...) +``` + ## clang-format [homepage](https://clang.llvm.org/docs/ClangFormat.html). [changelog](https://releases.llvm.org/download.html). `clang-format` is a formatter for c, c++, c#, objective-c, protobuf, javascript, and java. You can use clang-format in any language-specific format, but usually you will be creating a generic format. diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 7e03dde430..e5a77ed7b0 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -490,6 +490,17 @@ Spotless will try to auto-discover an npm installation. If that is not working f ``` +**Configuring npm using `.npmrc` file** + +Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. +Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) + +```xml + + /usr/local/shared/.npmrc + +``` + ## Prettier @@ -614,6 +625,16 @@ Spotless will try to auto-discover an npm installation. If that is not working f /usr/bin/npm ``` +### Configuring npm using `.npmrc` file + +Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. +Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) + +```xml + + /usr/local/shared/.npmrc +``` + ## Eclipse web tools platform From 823d6016e5467da15333718f78a3a3c6b08b72e1 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Fri, 30 Oct 2020 20:43:08 +0100 Subject: [PATCH 6/9] adding more integration tests for checking npmrc config option --- .../spotless/PrettierIntegrationTest.java | 25 ++++++++++- .../prettier/PrettierFormatStepTest.java | 44 +++++++++++++++++-- .../typescript/TypescriptFormatStepTest.java | 43 ++++++++++++++++-- 3 files changed, 103 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 cd5e943e07..4025cf5288 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 @@ -162,7 +162,7 @@ public void usePhpCommunityPlugin() throws IOException { } @Test - public void useNpmrcFileConfig() throws IOException { + public void autodetectNpmrcFileConfig() throws IOException { setFile(".npmrc").toLines( "registry=https://i.do.no.exist.com"); setFile("build.gradle").toLines( @@ -183,4 +183,27 @@ public void useNpmrcFileConfig() throws IOException { final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); Assertions.assertThat(spotlessApply.getOutput()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); } + + @Test + public void pickupNpmrcFileConfig() throws IOException { + setFile(".custom_npmrc").toLines( + "registry=https://i.do.no.exist.com"); + setFile("build.gradle").toLines( + "buildscript { repositories { mavenCentral() } }", + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "def prettierConfig = [:]", + "prettierConfig['printWidth'] = 50", + "prettierConfig['parser'] = 'typescript'", + "spotless {", + " format 'mytypescript', {", + " target 'test.ts'", + " prettier().npmrc('.custom_npmrc').config(prettierConfig)", + " }", + "}"); + setFile("test.ts").toResource("npm/prettier/config/typescript.dirty"); + final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").buildAndFail(); + Assertions.assertThat(spotlessApply.getOutput()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } } 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 3286972002..9dd285204c 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 @@ -24,19 +24,29 @@ import com.diffplug.spotless.category.NpmTest; import com.diffplug.spotless.maven.MavenIntegrationHarness; -import com.diffplug.spotless.maven.MavenRunner; +import com.diffplug.spotless.maven.MavenRunner.Result; import com.diffplug.spotless.maven.generic.Prettier; @Category(NpmTest.class) public class PrettierFormatStepTest extends MavenIntegrationHarness { private void run(String kind, String suffix) throws IOException, InterruptedException { + String path = prepareRun(kind, suffix); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource("npm/prettier/filetypes/" + kind + "/" + kind + ".clean"); + } + + private String prepareRun(String kind, String suffix) throws IOException { 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"); + return path; + } + + private Result runExpectingError(String kind, String suffix) throws IOException, InterruptedException { + String path = prepareRun(kind, suffix); + return mavenRunner().withArguments("spotless:apply").runHasError(); } @Test @@ -93,7 +103,7 @@ public void unique_dependency_config() throws Exception { " 1.16.4", ""); - MavenRunner.Result result = mavenRunner().withArguments("spotless:apply").runHasError(); + Result result = mavenRunner().withArguments("spotless:apply").runHasError(); assertThat(result.output()).contains(Prettier.ERROR_MESSAGE_ONLY_ONE_CONFIG); } @@ -134,4 +144,30 @@ public void autodetect_parser_based_on_filename() throws Exception { assertFile("dirty.json").sameAsResource("npm/prettier/filename/clean.json"); } + @Test + public void autodetect_npmrc_file() throws Exception { + setFile(".npmrc").toLines("registry=https://i.do.no.exist.com"); + String suffix = "ts"; + writePomWithPrettierSteps("**/*." + suffix, + "", + " 1.16.4", + " .prettierrc.yml", + ""); + Result result = runExpectingError("typescript", suffix); + assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } + + @Test + public void select_configured_npmrc_file() throws Exception { + setFile(".custom_npmrc").toLines("registry=https://i.do.no.exist.com"); + String suffix = "ts"; + writePomWithPrettierSteps("**/*." + suffix, + "", + " 1.16.4", + " .prettierrc.yml", + " ${basedir}/.custom_npmrc", + ""); + Result result = runExpectingError("typescript", suffix); + assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } } 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 a8823b81fb..9521962a74 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,17 +24,27 @@ import com.diffplug.spotless.category.NpmTest; import com.diffplug.spotless.maven.MavenIntegrationHarness; -import com.diffplug.spotless.maven.MavenRunner; +import com.diffplug.spotless.maven.MavenRunner.Result; @Category(NpmTest.class) public class TypescriptFormatStepTest extends MavenIntegrationHarness { private void run(String kind) throws IOException, InterruptedException { - String path = "src/main/typescript/test.ts"; - setFile(path).toResource("npm/tsfmt/" + kind + "/" + kind + ".dirty"); + String path = prepareRun(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 Result runExpectingError(String kind) throws IOException, InterruptedException { + prepareRun(kind); + return mavenRunner().withArguments("spotless:apply").runHasError(); + } + @Test public void tslint() throws Exception { writePomWithTypescriptSteps( @@ -99,7 +109,32 @@ public void testTypescript_2_Configs() throws Exception { String path = "src/main/typescript/test.ts"; setFile(path).toResource("npm/tsfmt/tsfmt/tsfmt.dirty"); - MavenRunner.Result result = mavenRunner().withArguments("spotless:apply").runHasError(); + Result result = mavenRunner().withArguments("spotless:apply").runHasError(); assertThat(result.output()).contains("must specify exactly one configFile or config"); } + + @Test + public void testNpmrcIsAutoPickedUp() throws Exception { + setFile(".npmrc").toLines("registry=https://i.do.no.exist.com"); + writePomWithTypescriptSteps( + "", + " ${basedir}/tslint.json", + ""); + setFile("tslint.json").toResource("npm/tsfmt/tslint/tslint.json"); + Result result = runExpectingError("tslint"); + assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } + + @Test + public void testNpmrcIsConfigurativelyPickedUp() throws Exception { + setFile(".custom_npmrc").toLines("registry=https://i.do.no.exist.com"); + writePomWithTypescriptSteps( + "", + " ${basedir}/tslint.json", + " ${basedir}/.custom_npmrc", + ""); + setFile("tslint.json").toResource("npm/tsfmt/tslint/tslint.json"); + Result result = runExpectingError("tslint"); + assertThat(result.output()).containsPattern("Running npm command.*npm install.* failed with exit code: 1"); + } } From dc1c7d29d31aad708493fc73800fc5b158417990 Mon Sep 17 00:00:00 2001 From: Simon Gamma Date: Sat, 31 Oct 2020 19:58:34 +0100 Subject: [PATCH 7/9] added .npmrc change to changelog --- CHANGES.md | 2 ++ plugin-gradle/CHANGES.md | 2 ++ plugin-maven/CHANGES.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ea7e686842..d00be461ad 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 +* Added support to npm-based steps for picking up `.npmrc` files ([#727](https://github.com/diffplug/spotless/pull/727)) ## [2.9.0] - 2020-10-20 ### Added diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 3a75564a30..b2f8e1fb47 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 to npm-based steps for picking up `.npmrc` files ([#727](https://github.com/diffplug/spotless/pull/727)) ## [5.7.0] - 2020-10-20 ### Added diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 984c238e0f..e9c1c55020 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 to npm-based steps for picking up `.npmrc` files ([#727](https://github.com/diffplug/spotless/pull/727)) ## [2.5.0] - 2020-10-20 ### Added From 0c56866d14168e376d186de559c47b8aebb3cfee Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 1 Nov 2020 21:54:05 -0800 Subject: [PATCH 8/9] Fix JDK 15 build. --- gradle/spotless-freshmark.gradle | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gradle/spotless-freshmark.gradle b/gradle/spotless-freshmark.gradle index dc7888adc4..d444740a33 100644 --- a/gradle/spotless-freshmark.gradle +++ b/gradle/spotless-freshmark.gradle @@ -1,4 +1,30 @@ +import java.util.regex.Matcher +import java.util.regex.Pattern + + +def thisVm() { + String jre = System.getProperty("java.version") + if (jre.startsWith("1.8")) { + return 8 + } else { + Matcher matcher = Pattern.compile("(\\d+)").matcher(jre) + if (!matcher.find()) { + throw new IllegalArgumentException("Expected " + jre + " to start with an integer") + } + int version = Integer.parseInt(matcher.group(1)) + if (version <= 8) { + throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8") + } + return version + } +} + +if (thisVm() >= 15) { + // freshmark doesn't run on JRE 15+ + return +} + apply plugin: 'com.diffplug.spotless' import com.diffplug.gradle.spotless.FreshMarkExtension From 4eb8ef10c998e3abc10b2d6867952221e0142ac3 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 1 Nov 2020 22:01:54 -0800 Subject: [PATCH 9/9] Add `.npmrc detection` to the README TOC, and remove duplicate info from tsfmt. --- plugin-gradle/README.md | 26 +++----------------------- plugin-maven/README.md | 24 +++--------------------- 2 files changed, 6 insertions(+), 44 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 660e9e1542..797e223de2 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -69,7 +69,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui - [SQL](#sql) ([dbeaver](#dbeaver), [prettier](#prettier)) - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier)) - Multiple languages - - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection)) + - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) - javascript, jsx, angular, vue, flow, typescript, css, less, scss, html, json, graphql, markdown, ymaml - [clang-format](#clang-format) - c, c++, c#, objective-c, protobuf, javascript, java @@ -515,27 +515,7 @@ spotless { **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. - -```gradle -spotless { - typescript { - tsfmt().npmExecutable('/usr/bin/npm').config(...) -``` - -**Configuring npm using `.npmrc` file** - -Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. -Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) - -```gradle -spotless { - typescript { - tsfmt().npmrc("$projectDir/config/.npmrc").config(...) -``` - - +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to tsfmt. @@ -604,7 +584,7 @@ spotless { prettier().npmExecutable('/usr/bin/npm').config(...) ``` -### Configuring npm using `.npmrc` file +### `.npmrc` detection Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) diff --git a/plugin-maven/README.md b/plugin-maven/README.md index e5a77ed7b0..135291f93f 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -55,7 +55,7 @@ user@machine repo % mvn spotless:check - [Sql](#sql) ([dbeaver](#dbeaver)) - [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier)) - Multiple languages - - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection)) + - [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection)) - [eclipse web tools platform](#eclipse-web-tools-platform) - **Language independent** - [Generic steps](#generic-steps) @@ -481,25 +481,7 @@ The auto-discovery of config files (up the file tree) will not work when using t **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 - -``` - -**Configuring npm using `.npmrc` file** - -Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. -Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.) - -```xml - - /usr/local/shared/.npmrc - -``` +For details, see the [npm detection](#npm-detection) and [`.npmrc` detection](#npmrc-detection) sections of prettier, which apply also to tsfmt. @@ -625,7 +607,7 @@ Spotless will try to auto-discover an npm installation. If that is not working f /usr/bin/npm ``` -### Configuring npm using `.npmrc` file +### `.npmrc` detection Spotless picks up npm configuration stored in a `.npmrc` file either in the project directory or in your user home. Alternatively you can supply spotless with a location of the `.npmrc` file to use. (This can be combined with `npmExecutable`, of course.)