Skip to content
Closed
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
14 changes: 10 additions & 4 deletions lib/src/main/java/com/diffplug/spotless/npm/PrettierConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 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 @@ -32,17 +32,23 @@ public class PrettierConfig implements Serializable {

private final TreeMap<String, Object> options;

public PrettierConfig(@Nullable File prettierConfigPath, @Nullable Map<String, Object> options) {
private final Boolean editorconfig;

public PrettierConfig(@Nullable File prettierConfigPath, @Nullable Map<String, Object> options, @Nullable Boolean editorconfig) {
this.prettierConfigPathSignature = prettierConfigPath == null ? null : FileSignature.promise(prettierConfigPath);
this.options = options == null ? new TreeMap<>() : new TreeMap<>(options);
this.editorconfig = editorconfig;
}

@Nullable
public File getPrettierConfigPath() {
@Nullable public File getPrettierConfigPath() {
return prettierConfigPathSignature == null ? null : prettierConfigPathSignature.get().getOnlyFile();
}

public Map<String, Object> getOptions() {
return new TreeMap<>(this.options);
}

@Nullable public Boolean getEditorconfig() {
return editorconfig;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 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 @@ -90,7 +90,7 @@ public FormatterFunc createFormatterFunc() {
logger.info("creating formatter function (starting server)");
ServerProcessInfo prettierRestServer = toRuntime().npmRunServer();
PrettierRestService restService = new PrettierRestService(prettierRestServer.getBaseUrl());
String prettierConfigOptions = restService.resolveConfig(this.prettierConfig.getPrettierConfigPath(), this.prettierConfig.getOptions());
String prettierConfigOptions = restService.resolveConfig(this.prettierConfig.getPrettierConfigPath(), this.prettierConfig.getOptions(), prettierConfig.getEditorconfig());
return Closeable.ofDangerous(() -> endServer(restService, prettierRestServer), new PrettierFilePathPassingFormatterFunc(prettierConfigOptions, restService));
} catch (IOException e) {
throw ThrowingEx.asRuntime(e);
Expand Down
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 All @@ -25,14 +25,17 @@ public class PrettierRestService extends BaseNpmRestService {
super(baseUrl);
}

public String resolveConfig(File prettierConfigPath, Map<String, Object> prettierConfigOptions) {
public String resolveConfig(File prettierConfigPath, Map<String, Object> prettierConfigOptions, Boolean editorconfig) {
Map<String, Object> jsonProperties = new LinkedHashMap<>();
if (prettierConfigPath != null) {
jsonProperties.put("prettier_config_path", prettierConfigPath.getAbsolutePath());
}
if (prettierConfigOptions != null) {
jsonProperties.put("prettier_config_options", JsonWriter.of(prettierConfigOptions).toJsonRawValue());
}
if (editorconfig != null) {
jsonProperties.put("editorconfig", editorconfig);
}
return restClient.postJson("/prettier/config-options", jsonProperties);
}

Expand Down
21 changes: 8 additions & 13 deletions lib/src/main/resources/com/diffplug/spotless/npm/prettier-serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,14 @@ app.post("/prettier/config-options", (req, res) => {
const prettier_config_path = config_data.prettier_config_path;
const prettier_config_options = config_data.prettier_config_options || {};

if (prettier_config_path) {
prettier
.resolveConfig(undefined, { config: prettier_config_path })
.then(options => {
const mergedConfigOptions = mergeConfigOptions(options, prettier_config_options);
res.set("Content-Type", "application/json")
res.json(mergedConfigOptions);
})
.catch(reason => res.status(501).send("Exception while resolving config_file_path: " + reason));
return;
}
res.set("Content-Type", "application/json")
res.json(prettier_config_options);
prettier
.resolveConfig(prettier_config_path, { editorconfig: config_data.editorconfig })
.then(options => {
const mergedConfigOptions = mergeConfigOptions(options, prettier_config_options);
res.set("Content-Type", "application/json")
res.json(mergedConfigOptions);
})
.catch(reason => res.status(501).send("Exception while resolving config_file_path: " + reason));
});

app.post("/prettier/format", async (req, res) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ public class PrettierConfig extends NpmStepConfig<PrettierConfig> {

@Nullable Map<String, Object> prettierConfig;

@Nullable Boolean editorconfig;

final Map<String, String> devDependencies;

PrettierConfig(Map<String, String> devDependencies) {
Expand All @@ -788,6 +790,12 @@ public PrettierConfig config(final Map<String, Object> prettierConfig) {
return this;
}

public PrettierConfig editorconfig(Boolean editorconfig) {
this.editorconfig = editorconfig;
replaceStep();
return this;
}

@Override
protected FormatterStep createStep() {
final Project project = getProject();
Expand All @@ -797,7 +805,7 @@ protected FormatterStep createStep() {
Arrays.asList(project.getProjectDir(), project.getRootDir())),
new com.diffplug.spotless.npm.PrettierConfig(
this.prettierConfigFile != null ? project.file(this.prettierConfigFile) : null,
this.prettierConfig));
this.prettierConfig, this.editorconfig));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 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 @@ -115,6 +115,35 @@ void useFileConfig(String prettierVersion) throws IOException {
}
}

@ParameterizedTest(name = "{index}: useEditorconfig with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void useEditorconfig(String prettierVersion) throws IOException {
setFile(".prettierrc.yml").toResource("npm/prettier/config/.prettierrc_noop.yml");
setFile(".editorconfig").toResource("npm/prettier/config/.editorconfig_20");
setFile("build.gradle").toLines(
"plugins {",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" format 'mytypescript', {",
" target 'test.ts'",
" prettier('" + prettierVersion + "').configFile('.prettierrc.yml').editorconfig(true)",
" }",
"}");
setFile("test.ts").toResource("npm/prettier/config/typescript.dirty");
final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "spotlessApply").build();
Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL");
switch (prettierVersion) {
case PRETTIER_VERSION_2:
assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_2.clean");
break;
case PRETTIER_VERSION_3:
assertFile("test.ts").sameAsResource("npm/prettier/config/typescript.configfile_prettier_3.clean");
break;
}
}

@ParameterizedTest(name = "{index}: chooseParserBasedOnFilename with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void chooseParserBasedOnFilename(String prettierVersion) throws IOException {
Expand Down
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 @@ -47,6 +47,9 @@ public class Prettier extends AbstractNpmFormatterStepFactory {
@Parameter
private String configFile;

@Parameter
private Boolean editorconfig;

@Override
public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) {

Expand Down Expand Up @@ -101,7 +104,7 @@ public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) {
File baseDir = baseDir(stepConfig);
File buildDir = buildDir(stepConfig);
File cacheDir = cacheDir(stepConfig);
PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline);
PrettierConfig prettierConfig = new PrettierConfig(configFileHandler, configInline, editorconfig);
NpmPathResolver npmPathResolver = npmPathResolver(stepConfig);
return PrettierFormatterStep.create(devDependencies, stepConfig.getProvisioner(), baseDir, buildDir, cacheDir, npmPathResolver, prettierConfig);
}
Expand Down
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 @@ -158,6 +158,25 @@ void multiple_prettier_configs() throws Exception {

}

@Test
void prettier_editorconfig() throws Exception {
String suffix = "ts";
writePomWithPrettierSteps("**/*." + suffix,
"<prettier>",
" <prettierVersion>1.16.4</prettierVersion>",
" <configFile>.prettierrc.yml</configFile>",
" <editorconfig>true</editorconfig>",
"</prettier>");

String kind = "typescript";
setFile(".prettierrc.yml").toResource("npm/prettier/config/.prettierrc_noop.yml");
setFile(".editorconfig").toResource("npm/prettier/config/.editorconfig_300");
String path = "src/main/" + kind + "/test." + suffix;
setFile(path).toResource("npm/prettier/filetypes/" + kind + "/" + kind + ".dirty");
mavenRunner().withArguments("spotless:apply").runNoError();
assertFile(path).sameAsResource("npm/prettier/filetypes/" + kind + "/" + kind + ".clean");
}

@Test
void custom_plugin() throws Exception {
writePomWithFormatSteps(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*]
max_line_length = 20
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*]
max_line_length = 300
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parser: typescript
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 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 @@ -69,7 +69,7 @@ private void runTestUsingPrettier(String fileType, Map<String, String> dependenc
buildDir(),
null,
npmPathResolver(),
new PrettierConfig(prettierRc, null));
new PrettierConfig(prettierRc, null, null));

try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) {
stepHarness.testResource(dirtyFile, cleanFile);
Expand All @@ -96,7 +96,7 @@ void parserInferenceBasedOnExplicitFilepathIsWorking(String prettierVersion) thr
buildDir(),
null,
npmPathResolver(),
new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"))); // should select parser based on this name
new PrettierConfig(null, ImmutableMap.of("filepath", "anyname.json"), null)); // should select parser based on this name

try (StepHarness stepHarness = StepHarness.forStep(formatterStep)) {
stepHarness.testResource(dirtyFile, cleanFile);
Expand All @@ -118,7 +118,7 @@ void parserInferenceBasedOnFilenameIsWorking(String prettierVersion) throws Exce
buildDir(),
null,
npmPathResolver(),
new PrettierConfig(null, Collections.emptyMap()));
new PrettierConfig(null, Collections.emptyMap(), null));

try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) {
stepHarness.testResource("test.json", dirtyFile, cleanFile);
Expand All @@ -134,7 +134,7 @@ void verifyPrettierErrorMessageIsRelayed() throws Exception {
buildDir(),
null,
npmPathResolver(),
new PrettierConfig(null, ImmutableMap.of("parser", "postcss")));
new PrettierConfig(null, ImmutableMap.of("parser", "postcss"), null));
try (StepHarnessWithFile stepHarness = StepHarnessWithFile.forStep(this, formatterStep)) {
stepHarness.expectLintsOfResource("npm/prettier/filetypes/scss/scss.dirty")
.toBe("LINE_UNDEFINED prettier-format(com.diffplug.spotless.npm.SimpleRestClient$SimpleRestResponseException) Unexpected response status code at /prettier/format [HTTP 500] -- (Error while formatting: Error: Couldn't resolve parser \"postcss\") (...)");
Expand Down Expand Up @@ -170,19 +170,27 @@ void runFormatTest(String prettierVersion, PrettierConfig config, String cleanFi
@ParameterizedTest(name = "{index}: defaults are applied with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void defaultsAreApplied(String prettierVersion) throws Exception {
runFormatTest(prettierVersion, new PrettierConfig(null, ImmutableMap.of("parser", "typescript")), "defaults_prettier_" + major(prettierVersion));
runFormatTest(prettierVersion, new PrettierConfig(null, ImmutableMap.of("parser", "typescript"), null), "defaults_prettier_" + major(prettierVersion));
}

@ParameterizedTest(name = "{index}: config file options are applied with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void configFileOptionsAreApplied(String prettierVersion) throws Exception {
runFormatTest(prettierVersion, new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), null), "configfile_prettier_" + major(prettierVersion));
runFormatTest(prettierVersion, new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), null, null), "configfile_prettier_" + major(prettierVersion));
}

@ParameterizedTest(name = "{index}: config file options can be overriden with prettier {0}")
@ParameterizedTest(name = "{index}: config file options can be overridden with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void configFileOptionsCanBeOverriden(String prettierVersion) throws Exception {
runFormatTest(prettierVersion, new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), ImmutableMap.of("printWidth", 300)), "override_prettier_" + major(prettierVersion));
void configFileOptionsCanBeOverridden(String prettierVersion) throws Exception {
runFormatTest(prettierVersion, new PrettierConfig(createTestFile(FILEDIR + ".prettierrc.yml"), ImmutableMap.of("printWidth", 300), null), "override_prettier_" + major(prettierVersion));
}

@ParameterizedTest(name = "{index}: config file options can be extended with editorconfig with prettier {0}")
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
void configFileOptionsCanBeExtendedWithEditorconfig(String prettierVersion) throws Exception {
setFile(".editorconfig").toResource(FILEDIR + ".editorconfig_300");
File prettierConfigFile = setFile(".prettierrc.yml").toResource(FILEDIR + ".prettierrc_noop.yml");
runFormatTest(prettierVersion, new PrettierConfig(prettierConfigFile, null, true), "override_prettier_" + major(prettierVersion));
}

private String major(String semVer) {
Expand All @@ -194,7 +202,7 @@ private String major(String semVer) {
void equality() {
new SerializableEqualityTester() {
String prettierVersion = "3.0.0";
PrettierConfig config = new PrettierConfig(null, Map.of("parser", "typescript"));
PrettierConfig config = new PrettierConfig(null, Map.of("parser", "typescript"), null);

@Override
protected void setupTest(API api) {
Expand All @@ -203,7 +211,7 @@ protected void setupTest(API api) {
// change the groupArtifact, and it's different
prettierVersion = "2.8.8";
api.areDifferentThan();
config = new PrettierConfig(null, Map.of("parser", "css"));
config = new PrettierConfig(null, Map.of("parser", "css"), null);
api.areDifferentThan();
}

Expand Down
Loading