Skip to content

Commit 4efc19b

Browse files
committed
eslint: introduce separate javascript extension
1 parent a9f89d8 commit 4efc19b

File tree

8 files changed

+313
-200
lines changed

8 files changed

+313
-200
lines changed

lib/src/main/java/com/diffplug/spotless/npm/EslintTypescriptConfig.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package com.diffplug.spotless.npm;
217

18+
import java.io.File;
19+
import java.io.IOException;
20+
21+
import javax.annotation.Nullable;
22+
323
import com.diffplug.spotless.FileSignature;
424
import com.diffplug.spotless.ThrowingEx;
525

626
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
727

8-
import javax.annotation.Nullable;
9-
10-
import java.io.File;
11-
import java.io.IOException;
12-
1328
public class EslintTypescriptConfig extends EslintConfig {
1429

1530
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 24 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,11 @@
2424
import java.nio.file.Files;
2525
import java.util.ArrayList;
2626
import java.util.Collection;
27-
import java.util.LinkedHashMap;
2827
import java.util.List;
2928
import java.util.Map;
30-
import java.util.Optional;
3129
import java.util.Random;
3230
import java.util.TreeMap;
33-
import java.util.stream.Collectors;
31+
import java.util.function.Consumer;
3432

3533
import javax.annotation.Nullable;
3634
import javax.inject.Inject;
@@ -62,8 +60,6 @@
6260
import com.diffplug.spotless.generic.ReplaceRegexStep;
6361
import com.diffplug.spotless.generic.ReplaceStep;
6462
import com.diffplug.spotless.generic.TrimTrailingWhitespaceStep;
65-
import com.diffplug.spotless.npm.EslintConfig;
66-
import com.diffplug.spotless.npm.EslintFormatterStep;
6763
import com.diffplug.spotless.npm.NpmPathResolver;
6864
import com.diffplug.spotless.npm.PrettierFormatterStep;
6965

@@ -518,23 +514,32 @@ public LicenseHeaderConfig licenseHeaderFile(Object licenseHeaderFile, String de
518514
return config;
519515
}
520516

521-
public abstract class NpmStepConfig<T extends NpmStepConfig<?>> {
517+
public abstract static class NpmStepConfig<T extends NpmStepConfig<?>> {
522518
@Nullable
523519
protected Object npmFile;
524520

525521
@Nullable
526522
protected Object npmrcFile;
527523

524+
protected Project project;
525+
526+
private Consumer<FormatterStep> replaceStep;
527+
528+
public NpmStepConfig(Project project, Consumer<FormatterStep> replaceStep) {
529+
this.project = requireNonNull(project);
530+
this.replaceStep = requireNonNull(replaceStep);
531+
}
532+
528533
@SuppressWarnings("unchecked")
529534
public T npmExecutable(final Object npmFile) {
530535
this.npmFile = npmFile;
531-
replaceStep(createStep());
536+
replaceStep();
532537
return (T) this;
533538
}
534539

535540
public T npmrc(final Object npmrcFile) {
536541
this.npmrcFile = npmrcFile;
537-
replaceStep(createStep());
542+
replaceStep();
538543
return (T) this;
539544
}
540545

@@ -547,10 +552,14 @@ File npmrcFileOrNull() {
547552
}
548553

549554
private File fileOrNull(Object npmFile) {
550-
return npmFile != null ? getProject().file(npmFile) : null;
555+
return npmFile != null ? project.file(npmFile) : null;
551556
}
552557

553-
abstract FormatterStep createStep();
558+
protected void replaceStep() {
559+
replaceStep.accept(createStep());
560+
}
561+
562+
abstract protected FormatterStep createStep();
554563

555564
}
556565

@@ -565,22 +574,24 @@ public class PrettierConfig extends NpmStepConfig<PrettierConfig> {
565574
final Map<String, String> devDependencies;
566575

567576
PrettierConfig(Map<String, String> devDependencies) {
577+
super(getProject(), FormatExtension.this::replaceStep);
568578
this.devDependencies = requireNonNull(devDependencies);
569579
}
570580

571581
public PrettierConfig configFile(final Object prettierConfigFile) {
572582
this.prettierConfigFile = prettierConfigFile;
573-
replaceStep(createStep());
583+
replaceStep();
574584
return this;
575585
}
576586

577587
public PrettierConfig config(final Map<String, Object> prettierConfig) {
578588
this.prettierConfig = new TreeMap<>(prettierConfig);
579-
replaceStep(createStep());
589+
replaceStep();
580590
return this;
581591
}
582592

583-
FormatterStep createStep() {
593+
@Override
594+
protected FormatterStep createStep() {
584595
final Project project = getProject();
585596
return PrettierFormatterStep.create(
586597
devDependencies,
@@ -611,69 +622,6 @@ public PrettierConfig prettier(Map<String, String> devDependencies) {
611622
return prettierConfig;
612623
}
613624

614-
public class EslintFormatExtension extends NpmStepConfig<EslintFormatExtension> {
615-
616-
Map<String, String> devDependencies = new LinkedHashMap<>();
617-
618-
@Nullable
619-
Object configFilePath = null;
620-
621-
@Nullable
622-
String configJs = null;
623-
624-
public EslintFormatExtension(Map<String, String> devDependencies) {
625-
this.devDependencies.putAll(requireNonNull(devDependencies));
626-
}
627-
628-
public EslintFormatExtension devDependencies(Map<String, String> devDependencies) {
629-
this.devDependencies.putAll(devDependencies);
630-
replaceStep(createStep());
631-
return this;
632-
}
633-
634-
public EslintFormatExtension configJs(String configJs) {
635-
this.configJs = requireNonNull(configJs);
636-
replaceStep(createStep());
637-
return this;
638-
}
639-
640-
public EslintFormatExtension configFile(Object configFilePath) {
641-
this.configFilePath = requireNonNull(configFilePath);
642-
replaceStep(createStep());
643-
return this;
644-
}
645-
646-
public FormatterStep createStep() {
647-
final Project project = getProject();
648-
649-
return EslintFormatterStep.create(
650-
devDependencies,
651-
provisioner(),
652-
project.getProjectDir(),
653-
project.getBuildDir(),
654-
new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()),
655-
eslintConfig());
656-
}
657-
658-
protected EslintConfig eslintConfig() {
659-
return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs);
660-
}
661-
}
662-
663-
public EslintFormatExtension eslint() {
664-
return eslint(EslintFormatterStep.defaultDevDependencies());
665-
}
666-
667-
public EslintFormatExtension eslint(String version) {
668-
return eslint(EslintFormatterStep.defaultDevDependenciesWithEslint(version));
669-
}
670-
671-
public EslintFormatExtension eslint(Map<String, String> devDependencies) {
672-
EslintFormatExtension eslint = new EslintFormatExtension(devDependencies);
673-
addStep(eslint.createStep());
674-
return eslint;
675-
}
676-
677625
/** Uses the default version of clang-format. */
678626
public ClangFormatConfig clangFormat() {
679627
return clangFormat(ClangFormatStep.defaultVersion());
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2016-2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.gradle.spotless;
17+
18+
import static java.util.Objects.requireNonNull;
19+
20+
import java.util.Arrays;
21+
import java.util.LinkedHashMap;
22+
import java.util.Map;
23+
import java.util.function.Consumer;
24+
import java.util.stream.Collectors;
25+
26+
import javax.annotation.Nullable;
27+
import javax.inject.Inject;
28+
29+
import org.gradle.api.Project;
30+
31+
import com.diffplug.spotless.FormatterStep;
32+
import com.diffplug.spotless.npm.EslintConfig;
33+
import com.diffplug.spotless.npm.EslintFormatterStep;
34+
import com.diffplug.spotless.npm.EslintFormatterStep.PopularStyleGuide;
35+
import com.diffplug.spotless.npm.NpmPathResolver;
36+
37+
public class JavascriptExtension extends FormatExtension {
38+
39+
static final String NAME = "javascript";
40+
41+
@Inject
42+
public JavascriptExtension(SpotlessExtension spotless) {
43+
super(spotless);
44+
}
45+
46+
public JavascriptEslintConfig eslint() {
47+
return eslint(EslintFormatterStep.defaultDevDependenciesForTypescript());
48+
}
49+
50+
public JavascriptEslintConfig eslint(String version) {
51+
return eslint(EslintFormatterStep.defaultDevDependenciesTypescriptWithEslint(version));
52+
}
53+
54+
public JavascriptEslintConfig eslint(Map<String, String> devDependencies) {
55+
JavascriptEslintConfig eslint = new JavascriptEslintConfig(devDependencies);
56+
addStep(eslint.createStep());
57+
return eslint;
58+
}
59+
60+
// TODO: make the configs static so that they do not need to have a hierarchy symmetric to the extensions
61+
62+
public static abstract class EslintBaseConfig<T extends EslintBaseConfig<?>> extends NpmStepConfig<EslintBaseConfig<T>> {
63+
Map<String, String> devDependencies = new LinkedHashMap<>();
64+
65+
@Nullable
66+
Object configFilePath = null;
67+
68+
@Nullable
69+
String configJs = null;
70+
71+
public EslintBaseConfig(Project project, Consumer<FormatterStep> replaceStep, Map<String, String> devDependencies) {
72+
super(project, replaceStep);
73+
this.devDependencies.putAll(requireNonNull(devDependencies));
74+
}
75+
76+
@SuppressWarnings("unchecked")
77+
public T devDependencies(Map<String, String> devDependencies) {
78+
this.devDependencies.putAll(devDependencies);
79+
replaceStep();
80+
return (T) this;
81+
}
82+
83+
@SuppressWarnings("unchecked")
84+
public T configJs(String configJs) {
85+
this.configJs = requireNonNull(configJs);
86+
replaceStep();
87+
return (T) this;
88+
}
89+
90+
@SuppressWarnings("unchecked")
91+
public T configFile(Object configFilePath) {
92+
this.configFilePath = requireNonNull(configFilePath);
93+
replaceStep();
94+
return (T) this;
95+
}
96+
97+
@SuppressWarnings("unchecked")
98+
public T styleGuide(String styleGuide) {
99+
PopularStyleGuide popularStyleGuide = PopularStyleGuide.fromNameOrNull(styleGuide);
100+
101+
verifyStyleGuideIsSupported(styleGuide, popularStyleGuide);
102+
devDependencies(popularStyleGuide.devDependencies());
103+
replaceStep();
104+
return (T) this;
105+
}
106+
107+
protected abstract void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide);
108+
}
109+
110+
public class JavascriptEslintConfig extends EslintBaseConfig<JavascriptEslintConfig> {
111+
112+
public JavascriptEslintConfig(Map<String, String> devDependencies) {
113+
super(getProject(), JavascriptExtension.this::replaceStep, devDependencies);
114+
}
115+
116+
public FormatterStep createStep() {
117+
final Project project = getProject();
118+
119+
return EslintFormatterStep.create(
120+
devDependencies,
121+
provisioner(),
122+
project.getProjectDir(),
123+
project.getBuildDir(),
124+
new NpmPathResolver(npmFileOrNull(), npmrcFileOrNull(), project.getProjectDir(), project.getRootDir()),
125+
eslintConfig());
126+
}
127+
128+
@Override
129+
protected void verifyStyleGuideIsSupported(String styleGuideName, PopularStyleGuide popularStyleGuide) {
130+
if (!isJsStyleGuide(popularStyleGuide)) {
131+
throw new IllegalArgumentException("Unknown style guide: " + styleGuideName + ". Known javascript style guides: "
132+
+ Arrays.stream(PopularStyleGuide.values())
133+
.filter(this::isJsStyleGuide)
134+
.map(PopularStyleGuide::getPopularStyleGuideName)
135+
.sorted()
136+
.collect(Collectors.joining(", ")));
137+
}
138+
}
139+
140+
private boolean isJsStyleGuide(PopularStyleGuide popularStyleGuide) {
141+
return popularStyleGuide != null && popularStyleGuide.name().startsWith("JS_");
142+
}
143+
144+
protected EslintConfig eslintConfig() {
145+
return new EslintConfig(configFilePath != null ? getProject().file(configFilePath) : null, configJs);
146+
}
147+
}
148+
149+
@Override
150+
protected void setupTask(SpotlessTask task) {
151+
if (target == null) {
152+
throw noDefaultTargetException();
153+
}
154+
super.setupTask(task);
155+
}
156+
}

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ public void cpp(Action<CppExtension> closure) {
167167
format(CppExtension.NAME, CppExtension.class, closure);
168168
}
169169

170+
/** Configures the special javascript-specific extension for javascript files. */
171+
public void javascript(Action<JavascriptExtension> closure) {
172+
format(JavascriptExtension.NAME, JavascriptExtension.class, closure);
173+
}
174+
170175
/** Configures the special typescript-specific extension for typescript files. */
171176
public void typescript(Action<TypescriptExtension> closure) {
172177
format(TypescriptExtension.NAME, TypescriptExtension.class, closure);

0 commit comments

Comments
 (0)