Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Support for `idea` ([#2020](https://github.com/diffplug/spotless/pull/2020), [#2535](https://github.com/diffplug/spotless/pull/2535))
* Add support for removing wildcard imports via `removeWildcardImports` step. ([#2517](https://github.com/diffplug/spotless/pull/2517))

### Fixed
* Make sure npm-based formatters use the correct `node_modules` directory when running in parallel. ([#2542](https://github.com/diffplug/spotless/pull/2542))

### Changed
* Bump internal dependencies for npm-based formatters ([#2542](https://github.com/diffplug/spotless/pull/2542))

## [3.1.2] - 2025-05-27
### Fixed
* Fix `UnsupportedOperationException` in the Gradle plugin when using `targetExcludeContent[Pattern]` ([#2487](https://github.com/diffplug/spotless/pull/2487))
Expand Down
1 change: 1 addition & 0 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies {
testCommonImplementation "org.junit.jupiter:junit-jupiter:$VER_JUNIT"
testCommonImplementation "org.assertj:assertj-core:$VER_ASSERTJ"
testCommonImplementation "com.diffplug.durian:durian-testlib:$VER_DURIAN"
testCommonImplementation projects.testlib
testCommonRuntimeOnly "org.junit.platform:junit-platform-launcher"

// GLUE CODE (alphabetic order please)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 DiffPlug
* Copyright 2020-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -37,16 +37,16 @@ class NodeServerLayout {
private final File serveJsFile;
private final File npmrcFile;

NodeServerLayout(File buildDir, String packageJsonContent) {
this.nodeModulesDir = new File(buildDir, nodeModulesDirName(packageJsonContent));
NodeServerLayout(File buildDir, String packageJsonContent, String serveJsContent) {
this.nodeModulesDir = new File(buildDir, nodeModulesDirName(packageJsonContent, serveJsContent));
this.packageJsonFile = new File(nodeModulesDir, "package.json");
this.packageLockJsonFile = new File(nodeModulesDir, "package-lock.json");
this.serveJsFile = new File(nodeModulesDir, "serve.js");
this.npmrcFile = new File(nodeModulesDir, ".npmrc");
}

private static String nodeModulesDirName(String packageJsonContent) {
String md5Hash = NpmResourceHelper.md5(packageJsonContent);
private static String nodeModulesDirName(String packageJsonContent, String serveJsContent) {
String md5Hash = NpmResourceHelper.md5(packageJsonContent, serveJsContent);
Matcher matcher = PACKAGE_JSON_NAME_PATTERN.matcher(packageJsonContent);
if (!matcher.find()) {
throw new IllegalArgumentException("package.json must contain a name property");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static class Runtime {

Runtime(NpmFormatterStepStateBase parent) {
this.parent = parent;
this.nodeServerLayout = new NodeServerLayout(parent.locations.buildDir(), parent.npmConfig.getPackageJsonContent());
this.nodeServerLayout = new NodeServerLayout(parent.locations.buildDir(), parent.npmConfig.getPackageJsonContent(), parent.npmConfig.getServeScriptContent());
this.nodeServeApp = new NodeServeApp(nodeServerLayout, parent.npmConfig, parent.locations);
}

Expand Down
16 changes: 13 additions & 3 deletions lib/src/main/java/com/diffplug/spotless/npm/NpmResourceHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 DiffPlug
* Copyright 2016-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,10 +27,14 @@
import java.util.Objects;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.diffplug.spotless.ThrowingEx;

final class NpmResourceHelper {

public static final String MD5_STRING_DELIMITER = "@@@";

private NpmResourceHelper() {
// no instance required
}
Expand Down Expand Up @@ -140,9 +144,15 @@ static String md5(File file) {
return md5(readUtf8StringFromFile(file));
}

static String md5(String fileContent) {
static String md5(String fileContent, String... additionalFileContents) {
Objects.requireNonNull(fileContent, "fileContent must not be null");
Stream<String> additionalFileContentStream = Stream.concat(
Stream.of(fileContent),
Stream.of(additionalFileContents));
MessageDigest md = ThrowingEx.get(() -> MessageDigest.getInstance("MD5"));
md.update(fileContent.getBytes(StandardCharsets.UTF_8));
String stringToHash = additionalFileContentStream.collect(Collectors.joining(MD5_STRING_DELIMITER));
md.update(stringToHash.getBytes(StandardCharsets.UTF_8));

byte[] digest = md.digest();
// convert byte array digest to hex string
StringBuilder sb = new StringBuilder();
Expand Down
16 changes: 11 additions & 5 deletions lib/src/main/resources/com/diffplug/spotless/npm/common-serve.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// this file will be glued to the top of the specific xy-serve.js file
const debug_serve = false; // set to true for debug log output in node process
const GracefulShutdownManager = require("@moebius/http-graceful-shutdown").GracefulShutdownManager;
const shutdownServer = require("http-graceful-shutdown");
const express = require("express");
const app = express();

Expand Down Expand Up @@ -48,12 +48,18 @@ var listener = app.listen(0, "127.0.0.1", () => {
}
});
});
const shutdownManager = new GracefulShutdownManager(listener);
const shutdown = shutdownServer(listener, {
forceExit: false, // let the event loop clear
finally: () => debugLog("graceful shutdown finished."),
});

app.post("/shutdown", (req, res) => {
res.status(200).send("Shutting down");
setTimeout(function () {
shutdownManager.terminate(() => debugLog("graceful shutdown finished."));
setTimeout(async () => {
try {
await shutdown();
} catch (err) {
console.error("Error during shutdown:", err);
}
}, 200);
});

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spotless-eslint",
"version": "2.0.0",
"version": "4.0.0",
"description": "Spotless formatter step for running eslint as a rest service.",
"repository": "https://github.com/diffplug/spotless",
"license": "Apache-2.0",
Expand All @@ -9,11 +9,11 @@
},
"devDependencies": {
${devDependencies},
"express": "4.18.2",
"@moebius/http-graceful-shutdown": "1.1.0"
"express": "5.1.0",
"http-graceful-shutdown": "3.1.14"
},
"dependencies": {},
"engines": {
"node": ">=6"
"node": ">= 18"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spotless-prettier",
"version": "2.0.0",
"version": "4.0.0",
"description": "Spotless formatter step for running prettier as a rest service.",
"repository": "https://github.com/diffplug/spotless",
"license": "Apache-2.0",
Expand All @@ -9,11 +9,11 @@
},
"devDependencies": {
${devDependencies},
"express": "4.18.2",
"@moebius/http-graceful-shutdown": "1.1.0"
"express": "5.1.0",
"http-graceful-shutdown": "3.1.14"
},
"dependencies": {},
"engines": {
"node": ">=6"
"node": ">= 18"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spotless-tsfmt",
"version": "2.0.0",
"version": "4.0.0",
"description": "Spotless formatter step for running tsfmt as a rest service.",
"repository": "https://github.com/diffplug/spotless",
"license": "Apache-2.0",
Expand All @@ -9,11 +9,11 @@
},
"devDependencies": {
${devDependencies},
"express": "4.18.2",
"@moebius/http-graceful-shutdown": "1.1.0"
"express": "5.1.0",
"http-graceful-shutdown": "3.1.14"
},
"dependencies": {},
"engines": {
"node": ">= 4.2.0"
"node": ">= 18"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ app.post("/tsfmt/format", (req, res) => {
*/
// result contains 'message' (String), 'error' (boolean), 'dest' (String) => formatted
if (resultMap.error !== undefined && resultMap.error) {
res.status(400).send(resultmap.message);
res.status(400).send(resultMap.message);
return;
}
res.set("Content-Type", "text/plain");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2025 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 org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;

import org.junit.jupiter.api.Test;

import com.diffplug.spotless.ResourceHarness;

class NodeServerLayoutTest extends ResourceHarness {

@Test
void itCalculatesSameNodeModulesDirForSameContent() throws IOException {
File testDir = newFolder("build");
String packageJsonContent = prettierPackageJson(Collections.emptyMap());
String serveJsContent = "fun main() { console.log('Hello, world!'); }";
NodeServerLayout layout1 = new NodeServerLayout(testDir, packageJsonContent, serveJsContent);
NodeServerLayout layout2 = new NodeServerLayout(testDir, packageJsonContent, serveJsContent);

assertThat(layout1.nodeModulesDir()).isEqualTo(layout2.nodeModulesDir());
}

@Test
void itCalculatesDifferentNodeModulesDirForDifferentPackageJson() throws IOException {
File testDir = newFolder("build");
String packageJsonContent1 = prettierPackageJson(Collections.singletonMap("prettier-plugin-xy", "^2.0.0"));
String packageJsonContent2 = prettierPackageJson(Collections.singletonMap("prettier-plugin-xy", "^2.1.0"));
String serveJsContent = "fun main() { console.log('Hello, world!'); }";

NodeServerLayout layout1 = new NodeServerLayout(testDir, packageJsonContent1, serveJsContent);
NodeServerLayout layout2 = new NodeServerLayout(testDir, packageJsonContent2, serveJsContent);

assertThat(layout1.nodeModulesDir()).isNotEqualTo(layout2.nodeModulesDir());
}

@Test
void itCalculatesDifferentNodeModulesDirForDifferentServeJs() throws IOException {
File testDir = newFolder("build");
String packageJsonContent = prettierPackageJson(Collections.emptyMap());
String serveJsContent1 = "fun main() { console.log('Hello, world!'); }";
String serveJsContent2 = "fun main() { console.log('Goodbye, world!'); }";

NodeServerLayout layout1 = new NodeServerLayout(testDir, packageJsonContent, serveJsContent1);
NodeServerLayout layout2 = new NodeServerLayout(testDir, packageJsonContent, serveJsContent2);

assertThat(layout1.nodeModulesDir()).isNotEqualTo(layout2.nodeModulesDir());
}

static String prettierPackageJson(Map<String, String> dependencies) {
String templateContent = NpmResourceHelper.readUtf8StringFromClasspath(NodeServerLayoutTest.class, "/com/diffplug/spotless/npm/prettier-package.json");
String dependenciesList = dependencies.entrySet().stream()
.map(entry -> String.format("\"%s\": \"%s\"", entry.getKey(), entry.getValue()))
.reduce((a, b) -> a + ",\n " + b)
.orElse("");

return templateContent.replace("${devDependencies}", dependenciesList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 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.selfie.Selfie.expectSelfie;

import org.junit.jupiter.api.Test;

class NpmResourceHelperTest {

@Test
void itCalculatesMd5ForSingleString() {
String input = "Hello, World!";

expectSelfie(NpmResourceHelper.md5(input)).toBe("65a8e27d8879283831b664bd8b7f0ad4");
}

@Test
void itCalculatesMd5ForMultipleStrings() {
String input1 = "Hello, World!";
String input2 = "Hello, Spencer!";

expectSelfie(NpmResourceHelper.md5(input1, input2)).toBe("371ba0fbf3d73b33e71b4af8dc6afe00");
}
}
6 changes: 6 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Support for `idea` ([#2020](https://github.com/diffplug/spotless/pull/2020), [#2535](https://github.com/diffplug/spotless/pull/2535))
* Add support for removing wildcard imports via `removeWildcardImports` step. ([#2517](https://github.com/diffplug/spotless/pull/2517))

### Fixed
* Make sure npm-based formatters use the correct `node_modules` directory when running in parallel. ([#2542](https://github.com/diffplug/spotless/pull/2542))

### Changed
* Bump internal dependencies for npm-based formatters ([#2542](https://github.com/diffplug/spotless/pull/2542))\

## [7.0.4] - 2025-05-27
### Fixed
* Fix `UnsupportedOperationException` in the Gradle plugin when using `targetExcludeContent[Pattern]` ([#2487](https://github.com/diffplug/spotless/pull/2487))
Expand Down
6 changes: 6 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Support for `idea` ([#2020](https://github.com/diffplug/spotless/pull/2020), [#2535](https://github.com/diffplug/spotless/pull/2535))
* Add support for removing wildcard imports via `removeWildcardImports` step. ([#2517](https://github.com/diffplug/spotless/pull/2517))

### Fixed
* Make sure npm-based formatters use the correct `node_modules` directory when running in parallel. ([#2542](https://github.com/diffplug/spotless/pull/2542))

### Changed
* Bump internal dependencies for npm-based formatters ([#2542](https://github.com/diffplug/spotless/pull/2542))

## [2.44.5] - 2025-05-27
### Changed
* Bump default `eclipse` version to latest `4.34` -> `4.35`. ([#2458](https://github.com/diffplug/spotless/pull/2458))
Expand Down
Loading