Skip to content

Commit

Permalink
feat: opt-out web components from package.json (#20392)
Browse files Browse the repository at this point in the history
Adds new property `npm.excludeWebComponents` (or `npmExcludeWebComponents` in Maven configurations). By default, it's `false` and everything works as before. `true` will exclude all web component dependencies from `package.json` for development mode (Vite/dev bundle) and production bundle build. Excluded dependencies are all Vaadin core components (e.g. button, grid, login, etc.) and commercial components (e.g. charts, rich-text-editor, etc.), but not lumo/material themes.

RelatedTo: #19948

---------

Co-authored-by: caalador <[email protected]>
  • Loading branch information
tltv and caalador authored Nov 7, 2024
1 parent 01a6eec commit fd16ca6
Show file tree
Hide file tree
Showing 20 changed files with 394 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ public class BuildDevBundleMojo extends AbstractMojo
@Parameter(defaultValue = "${project.basedir}/src/main/" + FRONTEND)
private File frontendDirectory;

@Parameter(property = InitParameters.NPM_EXCLUDE_WEB_COMPONENTS, defaultValue = "false")
private boolean npmExcludeWebComponents;

@Override
public void execute() throws MojoFailureException {
long start = System.nanoTime();
Expand Down Expand Up @@ -469,4 +472,9 @@ public boolean checkRuntimeDependency(String groupId, String artifactId,
Consumer<String> missingDependencyMessageConsumer) {
return false;
}

@Override
public boolean isNpmExcludeWebComponents() {
return npmExcludeWebComponents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ internal class GradlePluginAdapter(

override fun applicationIdentifier(): String = config.applicationIdentifier.get()

override fun isNpmExcludeWebComponents(): Boolean = config.npmExcludeWebComponents.get()

override fun checkRuntimeDependency(
groupId: String,
artifactId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ internal class PrepareFrontendInputProperties(private val config: PluginEffectiv
@Input
public fun getApplicationIdentifier(): Provider<String> = config.applicationIdentifier

@Input
public fun getNpmExcludeWebComponents(): Provider<Boolean> = config.npmExcludeWebComponents

@Input
@Optional
public fun getNodeExecutablePath(): Provider<String> = tools
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ public abstract class VaadinFlowPluginExtension @Inject constructor(private val

public abstract val applicationIdentifier: Property<String>

public abstract val npmExcludeWebComponents: Property<Boolean>

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

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

/**
* Finds the value of a boolean property. It searches in gradle and system properties.
*
Expand Down Expand Up @@ -499,7 +504,8 @@ public class PluginEffectiveConfiguration(
"alwaysExecutePrepareFrontend=${alwaysExecutePrepareFrontend.get()}, " +
"frontendHotdeploy=${frontendHotdeploy.get()}," +
"reactEnable=${reactEnable.get()}," +
"cleanFrontendFiles=${cleanFrontendFiles.get()}" +
"cleanFrontendFiles=${cleanFrontendFiles.get()}," +
"npmExcludeWebComponents=${npmExcludeWebComponents.get()}" +
")"
public companion object {
public fun get(project: Project): PluginEffectiveConfiguration =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ public abstract class FlowModeAbstractMojo extends AbstractMojo
@Parameter(property = InitParameters.REACT_ENABLE, defaultValue = "${null}")
private Boolean reactEnable;

@Parameter(property = InitParameters.NPM_EXCLUDE_WEB_COMPONENTS, defaultValue = "false")
private boolean npmExcludeWebComponents;

/**
* Identifier for the application.
* <p>
Expand Down Expand Up @@ -570,4 +573,9 @@ public String applicationIdentifier() {
project.getGroupId() + ":" + project.getArtifactId(),
StandardCharsets.UTF_8);
}

@Override
public boolean isNpmExcludeWebComponents() {
return npmExcludeWebComponents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ public void execute() throws MojoExecutionException, MojoFailureException {
.withHomeNodeExecRequired(requireHomeNodeExec())
.setJavaResourceFolder(javaResourceFolder())
.withProductionMode(productionMode)
.withReact(isReactEnabled());
.withReact(isReactEnabled())
.withNpmExcludeWebComponents(
isNpmExcludeWebComponents());
new NodeTasks(options).execute();
logInfo("SBOM generation created node_modules and all needed metadata. "
+ "If you don't need it, please run mvn vaadin:clean-frontend");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
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;
import static com.vaadin.flow.server.InitParameters.NPM_EXCLUDE_WEB_COMPONENTS;
import static com.vaadin.flow.server.InitParameters.REACT_ENABLE;
import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_INITIAL_UIDL;
import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_PRODUCTION_MODE;
Expand Down Expand Up @@ -165,7 +166,9 @@ public static void prepareFrontend(PluginAdapterBase adapter)
.setNodeAutoUpdate(adapter.nodeAutoUpdate())
.withHomeNodeExecRequired(adapter.requireHomeNodeExec())
.setJavaResourceFolder(adapter.javaResourceFolder())
.withProductionMode(false).withReact(adapter.isReactEnabled());
.withProductionMode(false).withReact(adapter.isReactEnabled())
.withNpmExcludeWebComponents(
adapter.isNpmExcludeWebComponents());

// Copy jar artifact contents in TaskCopyFrontendFiles
options.copyResources(adapter.getJarFiles());
Expand Down Expand Up @@ -263,6 +266,10 @@ public static File propagateBuildInfo(PluginAdapterBase adapter) {
}

buildInfo.put(REACT_ENABLE, adapter.isReactEnabled());
if (adapter.isNpmExcludeWebComponents()) {
buildInfo.put(NPM_EXCLUDE_WEB_COMPONENTS,
adapter.isNpmExcludeWebComponents());
}

try {
FileUtils.forceMkdir(token.getParentFile());
Expand Down Expand Up @@ -339,7 +346,9 @@ public static void runNodeUpdater(PluginAdapterBuild adapter)
.withPostinstallPackages(adapter.postinstallPackages())
.withCiBuild(adapter.ciBuild())
.withForceProductionBuild(adapter.forceProductionBuild())
.withReact(adapter.isReactEnabled());
.withReact(adapter.isReactEnabled())
.withNpmExcludeWebComponents(
adapter.isNpmExcludeWebComponents());
new NodeTasks(options).execute();
} catch (ExecutionFailedException exception) {
throw exception;
Expand Down Expand Up @@ -405,7 +414,9 @@ public static void runDevBuildNodeUpdater(PluginAdapterBuild adapter)
.withBundleBuild(true)
.skipDevBundleBuild(adapter.skipDevBundleBuild())
.withCompressBundle(adapter.compressBundle())
.withReact(adapter.isReactEnabled());
.withReact(adapter.isReactEnabled())
.withNpmExcludeWebComponents(
adapter.isNpmExcludeWebComponents());
new NodeTasks(options).execute();
} catch (ExecutionFailedException exception) {
throw exception;
Expand Down Expand Up @@ -751,6 +762,7 @@ public static void updateBuildFile(PluginAdapterBuild adapter,
buildInfo.remove(Constants.CONNECT_OPEN_API_FILE_TOKEN);
buildInfo.remove(Constants.PROJECT_FRONTEND_GENERATED_DIR_TOKEN);
buildInfo.remove(InitParameters.BUILD_FOLDER);
buildInfo.remove(InitParameters.NPM_EXCLUDE_WEB_COMPONENTS);
// Premium features flag is always true, because Vaadin CI server
// uses Enterprise sub, thus it's always true.
// Thus, resets the premium feature flag and DAU flag before asking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,11 @@ default Lookup createLookup(ClassFinder classFinder) {
* {@literal blank}.
*/
String applicationIdentifier();

/**
* Whether to include web component npm packages in packages.json.
*
* @return {@code true} to include web component npm packages.
*/
boolean isNpmExcludeWebComponents();
}
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,9 @@ public class InitParameters implements Serializable {
*/
public static final String APPLICATION_IDENTIFIER = "applicationIdentifier";

/**
* Configuration name for excluding npm packages for web components.
*/
public static final String NPM_EXCLUDE_WEB_COMPONENTS = "npm.excludeWebComponents";

}
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ public void execute() {
Map<String, String> filteredApplicationDependencies = new ExclusionFilter(
options.getClassFinder(),
options.isReactEnabled()
&& FrontendUtils.isReactModuleAvailable(options))
&& FrontendUtils.isReactModuleAvailable(options),
options.isNpmExcludeWebComponents())
.exclude(applicationDependencies);

// Add application dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class ExclusionFilter implements Serializable {

private final boolean reactEnabled;

private final boolean excludeWebComponentNpmPackages;

/**
* Create a new exclusion filter.
*
Expand All @@ -52,8 +54,24 @@ public class ExclusionFilter implements Serializable {
* whether React is enabled
*/
public ExclusionFilter(ClassFinder finder, boolean reactEnabled) {
this(finder, reactEnabled, false);
}

/**
* Create a new exclusion filter.
*
* @param finder
* the class finder to use
* @param reactEnabled
* whether React is enabled
* @param excludeWebComponentNpmPackages
* whether to exclude web component npm packages
*/
public ExclusionFilter(ClassFinder finder, boolean reactEnabled,
boolean excludeWebComponentNpmPackages) {
this.finder = finder;
this.reactEnabled = reactEnabled;
this.excludeWebComponentNpmPackages = excludeWebComponentNpmPackages;
}

/**
Expand Down Expand Up @@ -95,7 +113,7 @@ private Set<String> getExclusions(URL versionsResource) throws IOException {
VersionsJsonConverter convert = new VersionsJsonConverter(
Json.parse(
IOUtils.toString(content, StandardCharsets.UTF_8)),
reactEnabled);
reactEnabled, excludeWebComponentNpmPackages);
return convert.getExclusions();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ private JsonObject getFilteredVersionsFromResource(URL versionsResource,
Json.parse(
IOUtils.toString(content, StandardCharsets.UTF_8)),
options.isReactEnabled()
&& FrontendUtils.isReactModuleAvailable(options));
&& FrontendUtils.isReactModuleAvailable(options),
options.isNpmExcludeWebComponents());
versionsJson = convert.getConvertedJson();
versionsJson = new VersionsJsonFilter(getPackageJson(),
DEPENDENCIES)
Expand Down Expand Up @@ -617,6 +618,10 @@ private void putHillaComponentsDependencies(
if (options.isReactEnabled()) {
dependencies.putAll(readDependenciesIfAvailable(
"hilla/components/react", packageJsonKey));
if (options.isNpmExcludeWebComponents()) {
// remove dependencies that depends on web components
dependencies.remove("@vaadin/hilla-react-crud");
}
} else {
dependencies.putAll(readDependenciesIfAvailable(
"hilla/components/lit", packageJsonKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ public class Options implements Serializable {

private boolean reactEnable = true;

private boolean npmExcludeWebComponents = false;

/**
* Removes generated files from a previous execution that are no more
* created.
Expand Down Expand Up @@ -967,4 +969,25 @@ public Options withCleanOldGeneratedFiles(boolean clean) {
public boolean isCleanOldGeneratedFiles() {
return cleanOldGeneratedFiles;
}

/**
* Sets whether to exclude web component npm packages in packages.json.
*
* @return this builder
*/
public boolean isNpmExcludeWebComponents() {
return npmExcludeWebComponents;
}

/**
* Sets whether to exclude web component npm packages in packages.json.
*
* @param exclude
* whether to exclude web component npm packages
* @return this builder
*/
public Options withNpmExcludeWebComponents(boolean exclude) {
this.npmExcludeWebComponents = exclude;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ private boolean updatePackageJsonDependencies(JsonObject packageJson,
Map<String, String> filteredApplicationDependencies = new ExclusionFilter(
finder,
options.isReactEnabled()
&& FrontendUtils.isReactModuleAvailable(options))
&& FrontendUtils.isReactModuleAvailable(options),
options.isNpmExcludeWebComponents())
.exclude(applicationDependencies);

// Add application dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
class VersionsJsonConverter {

static final String VAADIN_CORE_NPM_PACKAGE = "@vaadin/vaadin-core";
static final String VAADIN_BUNDLES = "@vaadin/bundles";
private static final String JS_VERSION = "jsVersion";
private static final String NPM_NAME = "npmName";
private static final String NPM_VERSION = "npmVersion";
Expand Down Expand Up @@ -74,15 +75,18 @@ class VersionsJsonConverter {

private boolean reactEnabled;

private boolean excludeWebComponents;

private Set<String> exclusions;

private static Logger getLogger() {
return LoggerFactory.getLogger(VersionsJsonConverter.class);
}

VersionsJsonConverter(JsonObject platformVersions,
boolean collectReactComponents) {
this.reactEnabled = collectReactComponents;
VersionsJsonConverter(JsonObject platformVersions, boolean reactEnabled,
boolean excludeWebComponents) {
this.reactEnabled = reactEnabled;
this.excludeWebComponents = excludeWebComponents;
exclusions = new HashSet<>();
convertedObject = Json.createObject();

Expand Down Expand Up @@ -135,6 +139,8 @@ private void excludeDependencies() {
private boolean isIncludedByMode(String mode) {
if (mode == null || mode.isBlank() || MODE_ALL.equalsIgnoreCase(mode)) {
return true;
} else if (excludeWebComponents) {
return false;
} else if (reactEnabled) {
return MODE_REACT.equalsIgnoreCase(mode);
} else {
Expand All @@ -151,7 +157,19 @@ private void addDependency(JsonObject obj) {
if (Objects.equals(npmName, VAADIN_CORE_NPM_PACKAGE)) {
return;
}
if (excludeWebComponents && Objects.equals(npmName, VAADIN_BUNDLES)) {
exclusions.add(npmName);
return;
}
if (!isIncludedByMode(mode)) {
if (excludeWebComponents) {
// collecting exclusions also from non-included dependencies
// with a mode (react), when web components are not wanted.
if (MODE_REACT.equalsIgnoreCase(mode)) {
exclusions.add(npmName);
}
collectExclusions(obj);
}
return;
}
if (obj.hasKey(NPM_VERSION)) {
Expand All @@ -166,15 +184,19 @@ private void addDependency(JsonObject obj) {
}
convertedObject.put(npmName, version);

collectExclusions(obj);
getLogger().debug("versions.json adds dependency {} with version {}{}",
npmName, version, (mode != null ? " for mode " + mode : ""));
}

private void collectExclusions(JsonObject obj) {
if (obj.hasKey(EXCLUSIONS)) {
JsonArray array = obj.getArray(EXCLUSIONS);
if (array != null) {
IntStream.range(0, array.length())
.forEach(i -> exclusions.add(array.getString(i)));
}
}
getLogger().debug("versions.json adds dependency {} with version {}{}",
npmName, version, (mode != null ? " for mode " + mode : ""));
}

}
Loading

0 comments on commit fd16ca6

Please sign in to comment.