Skip to content

Commit 3c327df

Browse files
committed
fix: write pnpm overrides to pnpm-workspace.yaml
Recent pnpm versions no longer read the "pnpm" field from package.json, so the dependency overrides Flow generated there were silently ignored and pnpm logged a warning about it. As a result Flow could no longer lock transitive dependencies to the platform versions when using pnpm. Flow now writes these overrides to pnpm-workspace.yaml, the location pnpm actually reads, and moves any overrides left in package.json over to it. Existing user content in pnpm-workspace.yaml is preserved, and a Flow-generated file is cleaned up like other generated frontend files.
1 parent 64f93be commit 3c327df

12 files changed

Lines changed: 540 additions & 166 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ flow-tests/**/vite.config.ts
6262
flow-tests/**/generated/**
6363
flow-tests/**/pnpmfile.js
6464
flow-tests/**/.pnpmfile.cjs
65+
flow-tests/**/pnpm-workspace.yaml
6566
flow-tests/**/*.npmrc
6667

6768
*.xml.versionsBackup

flow-build-tools/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
<scope>provided</scope>
4848
</dependency>
4949

50+
<dependency>
51+
<groupId>tools.jackson.dataformat</groupId>
52+
<artifactId>jackson-dataformat-yaml</artifactId>
53+
</dependency>
54+
5055
<!-- Test dependencies -->
5156

5257
<dependency>

flow-build-tools/src/main/java/com/vaadin/flow/server/frontend/BundleValidationUtil.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,12 @@ public void execute() {
372372
dep.getKey(), dep.getValue());
373373
}
374374

375+
final Map<String, String> pnpmOverrides = options.isEnablePnpm()
376+
? new PnpmWorkspaceFile(options.getNpmFolder())
377+
.getOverrides()
378+
: Map.of();
375379
final String hash = TaskUpdatePackages
376-
.generatePackageJsonHash(packageJson);
380+
.generatePackageJsonHash(packageJson, pnpmOverrides);
377381
((ObjectNode) packageJson.get(NodeUpdater.VAADIN_DEP_KEY))
378382
.put(NodeUpdater.HASH_KEY, hash);
379383

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2000-2026 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* 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, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.server.frontend;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
import java.nio.file.Files;
22+
import java.util.LinkedHashMap;
23+
import java.util.Map;
24+
25+
import tools.jackson.databind.JsonNode;
26+
import tools.jackson.databind.node.ObjectNode;
27+
import tools.jackson.dataformat.yaml.YAMLMapper;
28+
29+
import com.vaadin.flow.internal.JacksonUtils;
30+
31+
/**
32+
* Reads and writes the {@code overrides} block of a project's
33+
* {@code pnpm-workspace.yaml}, the location pnpm 10+ uses for dependency
34+
* overrides. All other content in the file is preserved untouched. YAML I/O
35+
* goes through Jackson ({@link YAMLMapper}) so the whole module uses a single
36+
* JSON/YAML tool.
37+
* <p>
38+
* For internal use only. May be renamed or removed in a future release.
39+
*/
40+
class PnpmWorkspaceFile {
41+
42+
static final String WORKSPACE_FILE = "pnpm-workspace.yaml";
43+
private static final String OVERRIDES = "overrides";
44+
private static final YAMLMapper YAML = YAMLMapper.builder().build();
45+
46+
private final File file;
47+
private final ObjectNode document;
48+
49+
PnpmWorkspaceFile(File projectRoot) throws IOException {
50+
this.file = new File(projectRoot, WORKSPACE_FILE);
51+
this.document = load();
52+
}
53+
54+
private ObjectNode load() throws IOException {
55+
if (!file.isFile()) {
56+
return JacksonUtils.createObjectNode();
57+
}
58+
JsonNode parsed = YAML.readTree(file);
59+
return parsed instanceof ObjectNode object ? object
60+
: JacksonUtils.createObjectNode();
61+
}
62+
63+
/**
64+
* Returns the current overrides as a flat {@code key -> version} map.
65+
*/
66+
Map<String, String> getOverrides() {
67+
Map<String, String> result = new LinkedHashMap<>();
68+
JsonNode overrides = document.get(OVERRIDES);
69+
if (overrides instanceof ObjectNode object) {
70+
for (String key : JacksonUtils.getKeys(object)) {
71+
result.put(key, object.get(key).asString());
72+
}
73+
}
74+
return result;
75+
}
76+
77+
/**
78+
* Replaces the overrides block with the given entries, removing the block
79+
* entirely when the map is empty.
80+
*/
81+
void setOverrides(Map<String, String> overrides) {
82+
if (overrides.isEmpty()) {
83+
document.remove(OVERRIDES);
84+
} else {
85+
ObjectNode node = JacksonUtils.createObjectNode();
86+
overrides.forEach(node::put);
87+
document.set(OVERRIDES, node);
88+
}
89+
}
90+
91+
/**
92+
* Persists the file when its serialized content changed. When the whole
93+
* document is empty the file is deleted, because an empty
94+
* {@code pnpm-workspace.yaml} carries no configuration; user-authored
95+
* override keys and other sections keep the document non-empty and thus
96+
* keep the file alive.
97+
*
98+
* @return {@code true} if the file was written or deleted
99+
*/
100+
boolean save() throws IOException {
101+
if (document.isEmpty()) {
102+
if (file.isFile()) {
103+
Files.delete(file.toPath());
104+
return true;
105+
}
106+
return false;
107+
}
108+
String yaml = YAML.writeValueAsString(document);
109+
String current = file.isFile()
110+
? Files.readString(file.toPath(), StandardCharsets.UTF_8)
111+
: null;
112+
if (yaml.equals(current)) {
113+
return false;
114+
}
115+
Files.writeString(file.toPath(), yaml, StandardCharsets.UTF_8);
116+
return true;
117+
}
118+
}

flow-build-tools/src/main/java/com/vaadin/flow/server/frontend/TaskCleanFrontendFiles.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class TaskCleanFrontendFiles implements FallibleCommand {
5151
Constants.PACKAGE_LOCK_YAML, Constants.PACKAGE_LOCK_BUN,
5252
Constants.PACKAGE_LOCK_BUN_1_2, TaskGenerateTsConfig.TSCONFIG_JSON,
5353
TaskGenerateTsDefinitions.TS_DEFINITIONS, ".pnpmfile.cjs", ".npmrc",
54+
PnpmWorkspaceFile.WORKSPACE_FILE,
5455
FrontendUtils.VITE_GENERATED_CONFIG, FrontendUtils.VITE_CONFIG);
5556
private Set<File> existingFiles = new HashSet<>();
5657

0 commit comments

Comments
 (0)