Skip to content
Draft
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
85 changes: 85 additions & 0 deletions flow-frontend-tools/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vaadin</groupId>
<artifactId>flow-project</artifactId>
<version>25.0-SNAPSHOT</version>
</parent>
<artifactId>flow-frontend-tools</artifactId>
<packaging>jar</packaging>
<name>Flow Frontend Tools</name>
<description>Build-time frontend tooling for Flow applications (Node.js installation, dependency scanning, task framework)</description>

<dependencies>

<!-- Project dependencies -->

<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-server</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Library dependencies -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>license-checker</artifactId>
</dependency>

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.28.0</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<!-- Byte code analysis for scanner -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>

<!-- Jsoup for HTML parsing -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>

<!-- Jackson for JSON processing -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<!-- TESTING DEPENDENCIES -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-test-util</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private Map<File, List<String>> process(Map<ChunkInfo, List<CssData>> css,
List<CssData> eagerCssData = new ArrayList<>();
List<CssData> appShellCssData = new ArrayList<>();
List<CssData> webComponentCssData = new ArrayList<>();
if (FrontendUtils.isTailwindCssEnabled(options)) {
if (options.getFeatureFlags().isEnabled("tailwindCss")) {
appShellCssData.add(new CssData(TAILWIND_IMPORT, null, null, null));
}
for (Entry<ChunkInfo, List<String>> entry : javascript.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2000-2025 Vaadin Ltd.
*
* 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.vaadin.flow.server.frontend;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.flow.server.Constants;

import static com.vaadin.flow.server.Constants.DEV_BUNDLE_JAR_PATH;

/**
* Build-time utility class for bundle operations.
* <p>
* For internal use only. May be renamed or removed in a future release.
*/
public final class BuildBundleUtils {

private BuildBundleUtils() {
// Util methods only
}

/**
* Copy package-lock.json/.yaml file from existing dev-bundle for building
* new bundle.
*
* @param options
* task options
*/
public static void copyPackageLockFromBundle(Options options) {
String lockFile;
if (options.isEnablePnpm()) {
lockFile = Constants.PACKAGE_LOCK_YAML;
} else {
lockFile = Constants.PACKAGE_LOCK_JSON;
}
File packageLock = new File(options.getNpmFolder(), lockFile);
if (packageLock.exists()) {
// NO-OP due to existing package-lock
return;
}

try {
copyAppropriatePackageLock(options, packageLock);
} catch (IOException ioe) {
getLogger().error(
"Failed to copy existing `" + lockFile + "` to use", ioe);
}

}

private static void copyAppropriatePackageLock(Options options,
File packageLock) throws IOException {
File devBundleFolder = new File(
new File(options.getNpmFolder(),
options.getBuildDirectoryName()),
Constants.DEV_BUNDLE_LOCATION);
String packageLockFile = options.isEnablePnpm()
? Constants.PACKAGE_LOCK_YAML
: Constants.PACKAGE_LOCK_JSON;
if (devBundleFolder.exists()) {
File devPackageLock = new File(devBundleFolder, packageLockFile);
if (devPackageLock.exists()) {
FileUtils.copyFile(devPackageLock, packageLock);
return;
}
}
boolean hillaUsed = FrontendUtils.isHillaUsed(
options.getFrontendDirectory(), options.getClassFinder());
URL resource = null;
if (hillaUsed) {
resource = options.getClassFinder().getResource(
DEV_BUNDLE_JAR_PATH + "hybrid-" + packageLockFile);
}
if (resource == null) {
// If Hilla is in used but the hybrid lock file is not found in the
// dev-bundle, fallback to the standard.
// Could happen if Flow, dev-bundle and Vaadin maven plugin are not
// in sync because of project configuration.
if (hillaUsed) {
getLogger().debug(
"The '{}' template for hybrid application could not be found in dev-bundle JAR. Fallback to standard template.",
packageLockFile);
}
resource = options.getClassFinder()
.getResource(DEV_BUNDLE_JAR_PATH + packageLockFile);
}
if (resource != null) {
FileUtils.write(packageLock,
IOUtils.toString(resource, StandardCharsets.UTF_8),
StandardCharsets.UTF_8);
} else {
getLogger().debug(
"The '{}' file cannot be created because the dev-bundle JAR does not contain a suitable template.",
packageLockFile);
}
}

private static Logger getLogger() {
return LoggerFactory.getLogger(BuildBundleUtils.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
* renamed or removed in a future release.
*
* @author Vaadin Ltd
*
*
* TODO figure out why this is in flow-server?!? flow-server should not need front-end tooling!!
*/
public class FrontendTools {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public NodeTasks(Options options) {
}
} else {
commands.add(new TaskGenerateCommercialBanner(options));
BundleUtils.copyPackageLockFromBundle(options);
BuildBundleUtils.copyPackageLockFromBundle(options);
}
} else if (options.isBundleBuild()) {
// The dev bundle check needs the frontendDependencies to be
Expand All @@ -156,7 +156,7 @@ public NodeTasks(Options options) {
commands.add(new TaskCleanFrontendFiles(options));
options.withRunNpmInstall(true);
options.withCopyTemplates(true);
BundleUtils.copyPackageLockFromBundle(options);
BuildBundleUtils.copyPackageLockFromBundle(options);
UsageStatistics.markAsUsed("flow/app-dev-bundle", null);
} else {
// A dev bundle build is not needed after all, skip it
Expand All @@ -171,7 +171,7 @@ public NodeTasks(Options options) {
}
}
} else if (options.isFrontendHotdeploy()) {
BundleUtils.copyPackageLockFromBundle(options);
BuildBundleUtils.copyPackageLockFromBundle(options);
}

if (options.isGenerateEmbeddableWebComponents()) {
Expand Down Expand Up @@ -295,7 +295,7 @@ private void addBootstrapTasks(Options options) {
|| options.isBundleBuild()) {
commands.add(new TaskGenerateIndexTs(options));
commands.add(new TaskGenerateReactFiles(options));
if (FrontendUtils.isTailwindCssEnabled(options)) {
if (options.getFeatureFlags().isEnabled("tailwindCss")) {
commands.add(new TaskGenerateTailwindCss(options));
}
if (!options.isProductionMode()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ Map<String, String> getDefaultDependencies() {
dependencies
.putAll(readDependencies("vaadin-router", "dependencies"));
}
if (FrontendUtils.isTailwindCssEnabled(options)) {
if (options.getFeatureFlags().isEnabled("tailwindCss")) {
dependencies
.putAll(readDependencies("tailwindcss", "dependencies"));
}
Expand Down Expand Up @@ -377,7 +377,7 @@ Map<String, String> getDefaultDevDependencies() {
defaults.putAll(
readDependencies("react-router", "devDependencies"));
}
if (FrontendUtils.isTailwindCssEnabled(options)) {
if (options.getFeatureFlags().isEnabled("tailwindCss")) {
defaults.putAll(readDependencies("tailwindcss", "devDependencies"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private String updateFileSystemRouterVitePlugin(String template) {
}

private String updateTailwindCssVitePlugin(String template) {
if (FrontendUtils.isTailwindCssEnabled(options)) {
if (options.getFeatureFlags().isEnabled("tailwindCss")) {
return template
.replace("//#tailwindcssVitePluginImport#",
"import tailwindcss from '@tailwindcss/vite';")
Expand Down
Loading
Loading