Skip to content

Commit

Permalink
feat: Add support for customizable projectFileExtensions (#20397)
Browse files Browse the repository at this point in the history
* feat: Add support for customizable `projectFileExtensions`.

Fixes #19527

* Made `projectFileExtensions` into `extraProjectFileExtensions`, which add to existing ones.

* Ran Maven formatter.

* Rename, add javadoc examples, add test.

* Add plugin data to runtime through build-info

Fix initParam name, update javadocs

* remove debug comment

* trim extension to not have spaces

fix gradle javadoc

* Add extra file extensions as input property

* format

---------

Co-authored-by: Oliver Yasuna <[email protected]>
  • Loading branch information
caalador and oliveryasuna authored Nov 11, 2024
1 parent 6f2ab8d commit 717d482
Show file tree
Hide file tree
Showing 20 changed files with 226 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
Expand Down Expand Up @@ -467,6 +468,11 @@ public String applicationIdentifier() {
return project.getGroupId() + ":" + project.getArtifactId();
}

@Override
public List<String> frontendExtraFileExtensions() {
return Collections.emptyList();
}

@Override
public boolean checkRuntimeDependency(String groupId, String artifactId,
Consumer<String> missingDependencyMessageConsumer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,7 @@ internal class GradlePluginAdapter(
}
return dependencyAbsent
}

override fun frontendExtraFileExtensions(): List<String> =
config.frontendExtraFileExtensions.get()
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ internal class PrepareFrontendInputProperties(private val config: PluginEffectiv
@Input
public fun getReactEnable(): Provider<Boolean> = config.reactEnable

@Input
public fun getFrontendExtraFileExtensions(): ListProperty<String> = config.frontendExtraFileExtensions

@Input
public fun getApplicationIdentifier(): Provider<String> = config.applicationIdentifier

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,16 @@ public abstract class VaadinFlowPluginExtension @Inject constructor(private val

public abstract val applicationIdentifier: Property<String>

/**
* The list of extra file extensions that are considered project files.
* Hashes are calculated for these files as part of detecting if a new
* bundle should be generated.
*/
public abstract val frontendExtraFileExtensions: ListProperty<String>

/**
* Whether to include web component npm packages in packages.json
*/
public abstract val npmExcludeWebComponents: Property<Boolean>

public fun filterClasspath(@DelegatesTo(value = ClasspathFilter::class, strategy = Closure.DELEGATE_FIRST) block: Closure<*>) {
Expand Down Expand Up @@ -441,6 +451,10 @@ public class PluginEffectiveConfiguration(
))
.overrideWithSystemProperty("vaadin.${InitParameters.APPLICATION_IDENTIFIER}")

// TODO: Possibly get value from system param InitParameters.FRONTEND_EXTRA_EXTENSIONS
public val frontendExtraFileExtensions: ListProperty<String> = extension.frontendExtraFileExtensions
.convention(listOf())

public val npmExcludeWebComponents: Provider<Boolean> = extension
.npmExcludeWebComponents.convention(false)

Expand Down Expand Up @@ -505,6 +519,7 @@ public class PluginEffectiveConfiguration(
"frontendHotdeploy=${frontendHotdeploy.get()}," +
"reactEnable=${reactEnable.get()}," +
"cleanFrontendFiles=${cleanFrontendFiles.get()}," +
"frontendExtraFileExtensions=${frontendExtraFileExtensions.get()}," +
"npmExcludeWebComponents=${npmExcludeWebComponents.get()}" +
")"
public companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
Expand Down Expand Up @@ -237,6 +238,22 @@ public abstract class FlowModeAbstractMojo extends AbstractMojo
@Parameter(property = InitParameters.NPM_EXCLUDE_WEB_COMPONENTS, defaultValue = "false")
private boolean npmExcludeWebComponents;

/**
* Parameter for adding file extensions to handle during frontend tasks.
* <p>
* From the commandline use comma separated list
* {@code -Ddevmode.frontendExtraFileExtensions="svg,ico"}
* <p>
* In plugin configuration use comma separated values
*
* <configuration>
* <frontendExtraFileExtensions>svg,ico</frontendExtraFileExtensions>
* </configuration>
*
*/
@Parameter(property = InitParameters.FRONTEND_EXTRA_EXTENSIONS, defaultValue = "${null}")
private List<String> frontendExtraFileExtensions;

/**
* Identifier for the application.
* <p>
Expand Down Expand Up @@ -574,6 +591,15 @@ public String applicationIdentifier() {
StandardCharsets.UTF_8);
}

@Override
public List<String> frontendExtraFileExtensions() {
if (frontendExtraFileExtensions != null) {
return frontendExtraFileExtensions;
}

return Collections.emptyList();
}

@Override
public boolean isNpmExcludeWebComponents() {
return npmExcludeWebComponents;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
Expand Down Expand Up @@ -74,6 +75,7 @@
import static com.vaadin.flow.server.Constants.NPM_TOKEN;
import static com.vaadin.flow.server.Constants.PROJECT_FRONTEND_GENERATED_DIR_TOKEN;
import static com.vaadin.flow.server.InitParameters.APPLICATION_IDENTIFIER;
import static com.vaadin.flow.server.InitParameters.FRONTEND_EXTRA_EXTENSIONS;
import static com.vaadin.flow.server.InitParameters.FRONTEND_HOTDEPLOY;
import static com.vaadin.flow.server.InitParameters.NODE_DOWNLOAD_ROOT;
import static com.vaadin.flow.server.InitParameters.NODE_VERSION;
Expand Down Expand Up @@ -167,6 +169,8 @@ public static void prepareFrontend(PluginAdapterBase adapter)
.withHomeNodeExecRequired(adapter.requireHomeNodeExec())
.setJavaResourceFolder(adapter.javaResourceFolder())
.withProductionMode(false).withReact(adapter.isReactEnabled())
.withFrontendExtraFileExtensions(
adapter.frontendExtraFileExtensions())
.withNpmExcludeWebComponents(
adapter.isNpmExcludeWebComponents());

Expand Down Expand Up @@ -271,6 +275,12 @@ public static File propagateBuildInfo(PluginAdapterBase adapter) {
adapter.isNpmExcludeWebComponents());
}

if (!adapter.frontendExtraFileExtensions().isEmpty()) {
buildInfo.put(FRONTEND_EXTRA_EXTENSIONS,
adapter.frontendExtraFileExtensions().stream()
.collect(Collectors.joining(",")));
}

try {
FileUtils.forceMkdir(token.getParentFile());
FileIOUtils.writeIfChanged(token,
Expand Down Expand Up @@ -415,6 +425,8 @@ public static void runDevBuildNodeUpdater(PluginAdapterBuild adapter)
.skipDevBundleBuild(adapter.skipDevBundleBuild())
.withCompressBundle(adapter.compressBundle())
.withReact(adapter.isReactEnabled())
.withFrontendExtraFileExtensions(
adapter.frontendExtraFileExtensions())
.withNpmExcludeWebComponents(
adapter.isNpmExcludeWebComponents());
new NodeTasks(options).execute();
Expand Down Expand Up @@ -750,6 +762,7 @@ public static void updateBuildFile(PluginAdapterBuild adapter,
buildInfo.remove(NODE_DOWNLOAD_ROOT);
buildInfo.remove(FRONTEND_TOKEN);
buildInfo.remove(FRONTEND_HOTDEPLOY);
buildInfo.remove(FRONTEND_EXTRA_EXTENSIONS);
buildInfo.remove(InitParameters.SERVLET_PARAMETER_ENABLE_PNPM);
buildInfo.remove(InitParameters.SERVLET_PARAMETER_ENABLE_BUN);
buildInfo.remove(InitParameters.CI_BUILD);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,16 @@ default Lookup createLookup(ClassFinder classFinder) {
*/
String applicationIdentifier();

/**
* Get the list of project file extensions.
* <p>
* File extensions are given with or without . prefix eg "png" and ".png"
* are both accepted.
*
* @return list of project file extensions
*/
List<String> frontendExtraFileExtensions();

/**
* Whether to include web component npm packages in packages.json.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ public class InitParameters implements Serializable {
*/
public static final String APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER = "devmode.componentTracker.enabled";

/**
* Configuration parameter name for adding extra file extensions for stats
* bundle to generate hashes for.
*/
public static final String FRONTEND_EXTRA_EXTENSIONS = "devmode.frontendExtraFileExtensions";

/**
* I18N provider property.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -84,6 +85,8 @@ public class Options implements Serializable {

private boolean compressBundle = true;

private List<String> frontendExtraFileExtensions = null;

/**
* The node.js version to be used when node.js is installed automatically by
* Vaadin, for example <code>"v16.0.0"</code>. Defaults to
Expand Down Expand Up @@ -970,6 +973,28 @@ public boolean isCleanOldGeneratedFiles() {
return cleanOldGeneratedFiles;
}

/**
* Sets the extra file extensions used in the project.
*
* @param frontendExtraFileExtensions
* the file extensions to add for the project
* @return this builder
*/
public Options withFrontendExtraFileExtensions(
List<String> frontendExtraFileExtensions) {
this.frontendExtraFileExtensions = frontendExtraFileExtensions;
return this;
}

/**
* Gets the project file extensions.
*
* @return the project file extensions
*/
public List<String> getFrontendExtraFileExtensions() {
return frontendExtraFileExtensions;
}

/**
* Sets whether to exclude web component npm packages in packages.json.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
Expand Down Expand Up @@ -127,14 +131,29 @@ private void createGeneratedConfig() throws IOException {
.replace("#webComponentTags#",
webComponentTags == null || webComponentTags.isEmpty()
? ""
: String.join(";", webComponentTags));
: String.join(";", webComponentTags))
.replace("#frontendExtraFileExtensions#",
getFrontendExtraFileExtensions());
template = updateFileSystemRouterVitePlugin(template);

FileIOUtils.writeIfChanged(generatedConfigFile, template);
log().debug("Created vite generated configuration file: '{}'",
generatedConfigFile);
}

private String getFrontendExtraFileExtensions() {
Optional<List<String>> frontendExtraFileExtensions = Optional
.ofNullable(options.getFrontendExtraFileExtensions());
if (frontendExtraFileExtensions.isPresent()
&& frontendExtraFileExtensions.get().size() > 0) {
return frontendExtraFileExtensions.get().stream()
.map(ext -> ext.replace("'", "\\'")).map(ext -> ext.trim())
.map(ext -> ext.startsWith(".") ? ext : "." + ext)
.collect(Collectors.joining("', '", ", '", "'"));
}
return "";
}

private String updateFileSystemRouterVitePlugin(String template) {
if (options.isReactEnabled() && FrontendUtils.isHillaUsed(
options.getFrontendDirectory(), options.getClassFinder())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ protected Map<String, String> getConfigParametersUsingTokenData(
String.valueOf(buildInfo.getBoolean(PREMIUM_FEATURES)));
}

if (buildInfo.hasKey(InitParameters.FRONTEND_EXTRA_EXTENSIONS)) {
params.put(InitParameters.FRONTEND_EXTRA_EXTENSIONS, buildInfo
.getString(InitParameters.FRONTEND_EXTRA_EXTENSIONS));
}

if (buildInfo.hasKey(NPM_EXCLUDE_WEB_COMPONENTS)) {
params.put(NPM_EXCLUDE_WEB_COMPONENTS, String
.valueOf(buildInfo.getBoolean(NPM_EXCLUDE_WEB_COMPONENTS)));
Expand Down
2 changes: 1 addition & 1 deletion flow-server/src/main/resources/vite.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ function statsExtracterPlugin(): PluginOption {

const frontendFiles: Record<string, string> = {};

const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'];
const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'#frontendExtraFileExtensions#];

const isThemeComponentsResource = (id: string) =>
id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ public void createInitParameters_valuesAreTakenFromservletConfigAndTokenFile_val
InitParameters.COMPILED_WEB_COMPONENTS_PATH,
InitParameters.NODE_VERSION, InitParameters.NODE_DOWNLOAD_ROOT,
InitParameters.BUILD_FOLDER,
InitParameters.APPLICATION_IDENTIFIER));
InitParameters.APPLICATION_IDENTIFIER,
InitParameters.FRONTEND_EXTRA_EXTENSIONS));
Field[] initParamFields = InitParameters.class.getDeclaredFields();
String mockTokenJsonString = generateJsonStringFromFields(
initParamFields, stringParams);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -174,4 +175,48 @@ public void generatedTemplate_reactDisabled_correctFileRouterImport()
template.contains(", vitePluginFileSystemRouter()"));

}

@Test
public void generatedTemplate_extraFrontendExtension_addedToViteConfiguration()
throws IOException {
options.withFrontendExtraFileExtensions(
Arrays.asList(".svg", ".ico", "png"));
TaskUpdateVite task = new TaskUpdateVite(options, null);
task.execute();

File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_GENERATED_CONFIG);

String template = IOUtils.toString(configFile.toURI(),
StandardCharsets.UTF_8);
Pattern matchSelection = Pattern
.compile("const projectFileExtensions = \\[(.*)];");
Matcher matcher = matchSelection.matcher(template);
Assert.assertTrue("No projectFileExtensions found", matcher.find());
Assert.assertEquals(
"Extra frontend extensions should be added to vite configuration, but was not.",
"'.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map', '.svg', '.ico', '.png'",
matcher.group(1));
}

@Test
public void generatedTemplate_noEraFrontendExtension_viteConfigurationWithoutExtraSelections()
throws IOException {
TaskUpdateVite task = new TaskUpdateVite(options, null);
task.execute();

File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_GENERATED_CONFIG);

String template = IOUtils.toString(configFile.toURI(),
StandardCharsets.UTF_8);
Pattern matchSelection = Pattern
.compile("const projectFileExtensions = \\[(.*)];");
Matcher matcher = matchSelection.matcher(template);
Assert.assertTrue("No projectFileExtensions found", matcher.find());
Assert.assertEquals(
"Extra frontend extensions should be added to vite configuration, but was not.",
"'.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'",
matcher.group(1));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@
</goals>
</execution>
</executions>
<configuration>
<frontendExtraFileExtensions>scss</frontendExtraFileExtensions>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
p {
border: 3px solid orange;
}
Loading

0 comments on commit 717d482

Please sign in to comment.