Skip to content

Commit 66d87d5

Browse files
authored
feat: Development mode only scripts using @jsmodule(developmentOnly=true) (#17002)
Goal is to enable to load dev tools related scripts
1 parent ae8e652 commit 66d87d5

File tree

11 files changed

+283
-60
lines changed

11 files changed

+283
-60
lines changed

flow-server/src/main/java/com/vaadin/flow/component/dependency/JavaScript.java

+9
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@
105105
*/
106106
String value();
107107

108+
/**
109+
* Defines if the JavaScript should be loaded only when running in
110+
* development mode (for development tooling etc.) or if it should always be
111+
* loaded.
112+
* <p>
113+
* By default, scripts are always loaded.
114+
*/
115+
boolean developmentOnly() default false;
116+
108117
/**
109118
* Determines the dependency load mode. Refer to {@link LoadMode} for the
110119
* details.

flow-server/src/main/java/com/vaadin/flow/component/dependency/JsModule.java

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@
7979
*/
8080
String value();
8181

82+
/**
83+
* Defines if the JavaScript should be loaded only when running in
84+
* development mode (for development tooling etc.) or if it should always be
85+
* loaded.
86+
* <p>
87+
* By default, scripts are always loaded.
88+
*/
89+
boolean developmentOnly() default false;
90+
8291
/**
8392
* Internal annotation to enable use of multiple {@link JsModule}
8493
* annotations.

flow-server/src/main/java/com/vaadin/flow/server/frontend/AbstractUpdateImports.java

+18-11
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,15 @@ abstract class AbstractUpdateImports implements Runnable {
113113
@Override
114114
public void run() {
115115
Map<ChunkInfo, List<CssData>> css = scanner.getCss();
116-
Map<ChunkInfo, List<String>> javascript = mergeJavascript(
117-
scanner.getModules(), scanner.getScripts());
116+
Map<ChunkInfo, List<String>> javascript;
117+
if (options.isProductionMode()) {
118+
javascript = mergeJavascript(scanner.getModules(),
119+
scanner.getScripts());
120+
} else {
121+
javascript = mergeJavascript(scanner.getModules(),
122+
scanner.getModulesDevelopment(), scanner.getScripts(),
123+
scanner.getScriptsDevelopment());
124+
}
118125
Map<File, List<String>> output = process(css, javascript);
119126
writeOutput(output);
120127
}
@@ -371,20 +378,20 @@ protected String getThemeIdPrefix() {
371378

372379
protected abstract String getImportsNotFoundMessage();
373380

381+
@SafeVarargs
374382
private Map<ChunkInfo, List<String>> mergeJavascript(
375-
Map<ChunkInfo, List<String>> modules,
376-
Map<ChunkInfo, List<String>> scripts) {
383+
Map<ChunkInfo, List<String>>... javascripts) {
377384
Map<ChunkInfo, List<String>> result = new LinkedHashMap<>();
378385
Collection<? extends String> generated = resolveGeneratedModules(
379386
getGeneratedModules());
380-
for (Entry<ChunkInfo, List<String>> entry : modules.entrySet()) {
381-
result.computeIfAbsent(entry.getKey(), e -> new ArrayList<>())
382-
.addAll(resolveModules(entry.getValue()));
383-
}
384-
for (Entry<ChunkInfo, List<String>> entry : scripts.entrySet()) {
385-
result.computeIfAbsent(entry.getKey(), e -> new ArrayList<>())
386-
.addAll(resolveModules(entry.getValue()));
387+
for (Map<ChunkInfo, List<String>> javascript : javascripts) {
388+
389+
for (Entry<ChunkInfo, List<String>> entry : javascript.entrySet()) {
390+
result.computeIfAbsent(entry.getKey(), e -> new ArrayList<>())
391+
.addAll(resolveModules(entry.getValue()));
392+
}
387393
}
394+
388395
result.computeIfAbsent(ChunkInfo.GLOBAL, e -> new ArrayList<>())
389396
.addAll(generated);
390397
return result;

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/ClassInfo.java

+2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
public class ClassInfo {
1010
String className;
1111
final LinkedHashSet<String> modules = new LinkedHashSet<>();
12+
final LinkedHashSet<String> modulesDevelopmentOnly = new LinkedHashSet<>();
1213
final LinkedHashSet<String> scripts = new LinkedHashSet<>();
14+
final LinkedHashSet<String> scriptsDevelopmentOnly = new LinkedHashSet<>();
1315
final transient List<CssData> css = new ArrayList<>();
1416
String route = "";
1517
String layout;

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/EntryPointData.java

+10
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ public final class EntryPointData implements Serializable {
4141

4242
Set<String> reachableClasses;
4343
private LinkedHashSet<String> modules = new LinkedHashSet<>();
44+
private LinkedHashSet<String> modulesDevelopmentOnly = new LinkedHashSet<>();
4445
private LinkedHashSet<String> scripts = new LinkedHashSet<>();
46+
private LinkedHashSet<String> scriptsDevelopmentOnly = new LinkedHashSet<>();
4547
private LinkedHashSet<CssData> css = new LinkedHashSet<>();
4648

4749
EntryPointData(Class<?> clazz, EntryPointType type,
@@ -64,10 +66,18 @@ public LinkedHashSet<String> getModules() {
6466
return modules;
6567
}
6668

69+
public LinkedHashSet<String> getModulesDevelopmentOnly() {
70+
return modulesDevelopmentOnly;
71+
}
72+
6773
public Collection<String> getScripts() {
6874
return scripts;
6975
}
7076

77+
public Collection<String> getScriptsDevelopmentOnly() {
78+
return scriptsDevelopmentOnly;
79+
}
80+
7181
public Collection<CssData> getCss() {
7282
return css;
7383
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendClassVisitor.java

+49-14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.vaadin.flow.server.frontend.scanner;
1717

18+
import java.util.LinkedHashSet;
1819
import java.util.Set;
1920

2021
import org.objectweb.asm.AnnotationVisitor;
@@ -36,8 +37,6 @@
3637
* A class visitor for Flow components.
3738
* <p>
3839
* For internal use only. May be renamed or removed in a future release.
39-
* <p>
40-
* For internal use only. May be renamed or removed in a future release.
4140
*
4241
* @since 2.0
4342
*/
@@ -60,6 +59,50 @@ final class FrontendClassVisitor extends ClassVisitor {
6059
private final AnnotationVisitor jScriptVisitor;
6160
private ClassInfo classInfo;
6261

62+
private static final class JSAnnotationVisitor
63+
extends RepeatedAnnotationVisitor {
64+
65+
boolean currentDevOnly = false;
66+
private String currentModule;
67+
68+
private LinkedHashSet<String> target;
69+
private LinkedHashSet<String> targetDevelopmentOnly;
70+
71+
public JSAnnotationVisitor(LinkedHashSet<String> target,
72+
LinkedHashSet<String> targetDevelopmentOnly) {
73+
this.target = target;
74+
this.targetDevelopmentOnly = targetDevelopmentOnly;
75+
}
76+
77+
@Override
78+
public void visit(String name, Object value) {
79+
if (name.equals("developmentOnly")) {
80+
Boolean developmentOnly = (Boolean) value;
81+
if (developmentOnly != null && developmentOnly) {
82+
currentDevOnly = true;
83+
}
84+
} else if (name.equals("value")) {
85+
currentModule = value.toString();
86+
}
87+
}
88+
89+
@Override
90+
public void visitEnd() {
91+
super.visitEnd();
92+
if (currentModule != null) {
93+
// This visitor is called also for the $Container annotation
94+
if (currentDevOnly) {
95+
targetDevelopmentOnly.add(currentModule);
96+
} else {
97+
target.add(currentModule);
98+
}
99+
}
100+
currentModule = null;
101+
currentDevOnly = false;
102+
}
103+
104+
}
105+
63106
private final class FrontendMethodVisitor extends MethodVisitor {
64107
public FrontendMethodVisitor() {
65108
super(Opcodes.ASM9);
@@ -177,19 +220,11 @@ public void visit(String name, Object value) {
177220
}
178221
};
179222
// Visitor for @JsModule annotations
180-
jsModuleVisitor = new RepeatedAnnotationVisitor() {
181-
@Override
182-
public void visit(String name, Object value) {
183-
classInfo.modules.add(value.toString());
184-
}
185-
};
223+
jsModuleVisitor = new JSAnnotationVisitor(classInfo.modules,
224+
classInfo.modulesDevelopmentOnly);
186225
// Visitor for @JavaScript annotations
187-
jScriptVisitor = new RepeatedAnnotationVisitor() {
188-
@Override
189-
public void visit(String name, Object value) {
190-
classInfo.scripts.add(value.toString());
191-
}
192-
};
226+
jScriptVisitor = new JSAnnotationVisitor(classInfo.scripts,
227+
classInfo.scriptsDevelopmentOnly);
193228
// Visitor all other annotations
194229
annotationVisitor = new RepeatedAnnotationVisitor() {
195230
@Override

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependencies.java

+34
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,12 @@ private void aggregateEntryPointInformation() {
165165
for (String className : entryPoint.reachableClasses) {
166166
ClassInfo classInfo = visitedClasses.get(className);
167167
entryPoint.getModules().addAll(classInfo.modules);
168+
entryPoint.getModulesDevelopmentOnly()
169+
.addAll(classInfo.modulesDevelopmentOnly);
168170
entryPoint.getCss().addAll(classInfo.css);
169171
entryPoint.getScripts().addAll(classInfo.scripts);
172+
entryPoint.getScriptsDevelopmentOnly()
173+
.addAll(classInfo.scriptsDevelopmentOnly);
170174
}
171175
}
172176

@@ -256,6 +260,21 @@ public Map<ChunkInfo, List<String>> getModules() {
256260
return all;
257261
}
258262

263+
/**
264+
* Get all JS modules needed in development mode.
265+
*
266+
* @return list of JS modules
267+
*/
268+
@Override
269+
public Map<ChunkInfo, List<String>> getModulesDevelopment() {
270+
LinkedHashMap<ChunkInfo, List<String>> all = new LinkedHashMap<>();
271+
for (EntryPointData data : entryPoints.values()) {
272+
all.computeIfAbsent(getChunkInfo(data), k -> new ArrayList<>())
273+
.addAll(data.getModulesDevelopmentOnly());
274+
}
275+
return all;
276+
}
277+
259278
private ChunkInfo getChunkInfo(EntryPointData data) {
260279
if (data.getType() == EntryPointType.INTERNAL) {
261280
return ChunkInfo.GLOBAL;
@@ -279,6 +298,21 @@ public Map<ChunkInfo, List<String>> getScripts() {
279298
return all;
280299
}
281300

301+
/**
302+
* Get all the JS files needed in development mode.
303+
*
304+
* @return the set of JS files
305+
*/
306+
@Override
307+
public Map<ChunkInfo, List<String>> getScriptsDevelopment() {
308+
Map<ChunkInfo, List<String>> all = new LinkedHashMap<>();
309+
for (EntryPointData data : entryPoints.values()) {
310+
all.computeIfAbsent(getChunkInfo(data), k -> new ArrayList<>())
311+
.addAll(data.getScriptsDevelopmentOnly());
312+
}
313+
return all;
314+
}
315+
282316
/**
283317
* Get all the CSS files used by the application.
284318
*

flow-server/src/main/java/com/vaadin/flow/server/frontend/scanner/FrontendDependenciesScanner.java

+19-4
Original file line numberDiff line numberDiff line change
@@ -101,29 +101,44 @@ public FrontendDependenciesScanner createScanner(
101101
/**
102102
* Get all npm packages the application depends on.
103103
*
104-
* @return the set of npm packages
104+
* @return the npm packages
105105
*/
106106
Map<String, String> getPackages();
107107

108108
/**
109109
* Get all ES6 modules needed for run the application. Modules that are
110110
* theme dependencies are guaranteed to precede other modules in the result.
111111
*
112-
* @return list of JS modules
112+
* @return the JS modules
113113
*/
114114
Map<ChunkInfo, List<String>> getModules();
115115

116+
/**
117+
* Get all ES6 modules needed only in development mode. Modules that are
118+
* theme dependencies are guaranteed to precede other modules in the result.
119+
*
120+
* @return the JS modules
121+
*/
122+
Map<ChunkInfo, List<String>> getModulesDevelopment();
123+
116124
/**
117125
* Get all the JS files used by the application.
118126
*
119-
* @return the set of JS files
127+
* @return the JS files
120128
*/
121129
Map<ChunkInfo, List<String>> getScripts();
122130

131+
/**
132+
* Get all the JS files needed only in development mode.
133+
*
134+
* @return the JS files
135+
*/
136+
Map<ChunkInfo, List<String>> getScriptsDevelopment();
137+
123138
/**
124139
* Get all the CSS files used by the application.
125140
*
126-
* @return the set of CSS files
141+
* @return the CSS files
127142
*/
128143
Map<ChunkInfo, List<CssData>> getCss();
129144

0 commit comments

Comments
 (0)