cds-plugin-ui5 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +11 -0
- package/cds-plugin.js +15 -11
- package/lib/applyUI5Middleware.js +101 -55
- package/lib/createPatchedRouter.js +3 -3
- package/lib/findUI5Modules.js +3 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [0.13.0](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/cds-plugin-ui5@0.12.1...cds-plugin-ui5@0.13.0) (2025-05-20)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **cds-plugin-ui5:** add lazy module loading ([#1216](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/1216)) ([58c88cf](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/58c88cfede10db2ac4aa59f8447edb02e12aa883))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [0.12.1](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/cds-plugin-ui5@0.12.0...cds-plugin-ui5@0.12.1) (2025-05-18)
|
|
7
18
|
|
|
8
19
|
|
package/README.md
CHANGED
|
@@ -75,6 +75,15 @@ The configuration can also be injected with the environment variable `CDS_PLUGIN
|
|
|
75
75
|
CDS_PLUGIN_UI5_MODULES="{ \"ui5-bookshop\": { \"mountPath\": \"/the-bookshop\" } }" cds-serve
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
#### Lazy Loading
|
|
79
|
+
|
|
80
|
+
The plugin supports lazy loading of UI5 applications which means that the UI5 middlewares will not be applied by default. The first time a UI5 application will be accessed in the CDS server triggers the load and apply of the UI5 middlewares. This feature is not active by default and needs to be activated with the environment variable `CDS_PLUGIN_UI5_LAZY_LOADING`.
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
# enable lazy loading for UI5 applications
|
|
84
|
+
CDS_PLUGIN_UI5_LAZY_LOADING=true cds watch
|
|
85
|
+
```
|
|
86
|
+
|
|
78
87
|
#### Logger
|
|
79
88
|
|
|
80
89
|
The `cds-plugin-ui5` uses the logger from CDS. By default, it adds coloring to the logs from CDS. This can be disabled in general by using the environment variable `NO_COLOR` for the logger overall or specifically for the `cds-plugin-ui5` by setting the environment variable `CDS_PLUGIN_UI5_NO_CUSTOM_LOGGER`.
|
|
@@ -155,6 +164,8 @@ module.exports = async ({ log, resources, options }) => {
|
|
|
155
164
|
|
|
156
165
|
The returned app pages will be added to the welcome page within the respective mount path.
|
|
157
166
|
|
|
167
|
+
> :warning: The app pages cannot be retrieved and injected when using the lazy loading option.
|
|
168
|
+
|
|
158
169
|
## Hints
|
|
159
170
|
|
|
160
171
|
This section includes hints for the usage of the `cds-plugin-ui5` with other tools.
|
package/cds-plugin.js
CHANGED
|
@@ -171,6 +171,9 @@ if (!skip) {
|
|
|
171
171
|
|
|
172
172
|
const links = [];
|
|
173
173
|
|
|
174
|
+
// is lazy loading enabled?
|
|
175
|
+
const isLazyLoadingEnabled = process.env["CDS_PLUGIN_UI5_LAZY_LOADING"] === "true";
|
|
176
|
+
|
|
174
177
|
// log the version of the cds-plugin-ui5
|
|
175
178
|
logVersion();
|
|
176
179
|
|
|
@@ -182,32 +185,33 @@ if (!skip) {
|
|
|
182
185
|
LOG.info(`Mounting ${mountPath} to UI5 app ${modulePath} (id=${moduleId})${config[moduleId] ? ` using config=${JSON.stringify(config[moduleId])}` : ""}`);
|
|
183
186
|
|
|
184
187
|
// create a patched router
|
|
185
|
-
const router =
|
|
188
|
+
const router = createPatchedRouter();
|
|
186
189
|
|
|
187
|
-
// apply the UI5 middlewares
|
|
188
|
-
|
|
189
|
-
const appInfo = await applyUI5Middleware(router, {
|
|
190
|
+
// determine the application info and apply the UI5 middlewares (maybe lazy)
|
|
191
|
+
applyUI5Middleware(router, {
|
|
190
192
|
cwd,
|
|
191
193
|
basePath: modulePath,
|
|
192
194
|
...(config[moduleId] || {}),
|
|
193
195
|
LOG,
|
|
196
|
+
lazy: isLazyLoadingEnabled,
|
|
197
|
+
//logPerformance: true,
|
|
198
|
+
}).then(({ pages }) => {
|
|
199
|
+
// append the HTML pages to the links
|
|
200
|
+
pages.forEach((page) => {
|
|
201
|
+
const prefix = mountPath !== "/" ? mountPath : "";
|
|
202
|
+
links.push(`${prefix}${page}`);
|
|
203
|
+
});
|
|
194
204
|
});
|
|
195
205
|
|
|
196
206
|
// register the router to the specified mount path
|
|
197
207
|
app.use(mountPath, router);
|
|
198
|
-
|
|
199
|
-
// append the HTML pages to the links
|
|
200
|
-
appInfo.pages.forEach((page) => {
|
|
201
|
-
const prefix = mountPath !== "/" ? mountPath : "";
|
|
202
|
-
links.push(`${prefix}${page}`);
|
|
203
|
-
});
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
// identify whether the welcome page should be rewritten
|
|
207
211
|
let rewrite = links.length > 0;
|
|
208
212
|
|
|
209
213
|
// rewrite the welcome page
|
|
210
|
-
if (rewrite) {
|
|
214
|
+
if (rewrite || isLazyLoadingEnabled) {
|
|
211
215
|
// register the custom middleware (similar like in @sap/cds/server.js)
|
|
212
216
|
app.get("/", function appendLinksToIndex(req, res, next) {
|
|
213
217
|
req._cds_plugin_ui5 = true; // marker for patched router to ignore
|
|
@@ -28,14 +28,14 @@ const fs = require("fs");
|
|
|
28
28
|
* root directory to the given router.
|
|
29
29
|
* @param {import("express").Router} router Express Router instance
|
|
30
30
|
* @param {applyUI5MiddlewareOptions} options configuration options
|
|
31
|
-
* @returns {UI5AppInfo} UI5 application information object
|
|
31
|
+
* @returns {Promise<UI5AppInfo>} UI5 application information object
|
|
32
32
|
*/
|
|
33
33
|
module.exports = async function applyUI5Middleware(router, options) {
|
|
34
|
-
const
|
|
35
|
-
const { createReaderCollection } = await import("@ui5/fs/resourceFactory");
|
|
34
|
+
const millis = Date.now();
|
|
36
35
|
|
|
37
36
|
options.cwd = options.cwd || process.cwd();
|
|
38
37
|
options.basePath = options.basePath || process.cwd();
|
|
38
|
+
options.lazy = options.lazy || false;
|
|
39
39
|
|
|
40
40
|
const log = options.log || console;
|
|
41
41
|
|
|
@@ -44,6 +44,12 @@ module.exports = async function applyUI5Middleware(router, options) {
|
|
|
44
44
|
const workspaceConfigPath = options.workspaceConfigPath || options.basePath;
|
|
45
45
|
const workspaceConfigFile = options.workspaceConfigFile || "ui5-workspace.yaml";
|
|
46
46
|
|
|
47
|
+
const logPerformance = options.logPerformance || false;
|
|
48
|
+
|
|
49
|
+
const millisImport = logPerformance && Date.now();
|
|
50
|
+
const { graphFromPackageDependencies } = await import("@ui5/project/graph");
|
|
51
|
+
logPerformance && log.info(`[PERF] Import took ${Date.now() - millisImport}ms`);
|
|
52
|
+
|
|
47
53
|
const determineConfigPath = function (configPath, configFile) {
|
|
48
54
|
// ensure that the config path is absolute
|
|
49
55
|
if (!path.isAbsolute(configPath)) {
|
|
@@ -66,6 +72,8 @@ module.exports = async function applyUI5Middleware(router, options) {
|
|
|
66
72
|
return undefined;
|
|
67
73
|
};
|
|
68
74
|
|
|
75
|
+
// determine the project graph from the given options
|
|
76
|
+
const millisGraph = logPerformance && Date.now();
|
|
69
77
|
const graph = await graphFromPackageDependencies({
|
|
70
78
|
cwd: options.basePath,
|
|
71
79
|
rootConfigPath: determineConfigPath(configPath, configFile),
|
|
@@ -74,72 +82,110 @@ module.exports = async function applyUI5Middleware(router, options) {
|
|
|
74
82
|
versionOverride: options.versionOverride,
|
|
75
83
|
cacheMode: options.cacheMode,
|
|
76
84
|
});
|
|
85
|
+
logPerformance && log.info(`[PERF] Graph took ${Date.now() - millisGraph}ms`);
|
|
77
86
|
|
|
87
|
+
const millisRoot = logPerformance && Date.now();
|
|
88
|
+
// detect the root project
|
|
78
89
|
const rootProject = graph.getRoot();
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
await graph.traverseBreadthFirst(async function ({ project: dep }) {
|
|
82
|
-
if (dep.getName() === rootProject.getName()) {
|
|
83
|
-
// Ignore root project
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
readers.push(dep.getReader({ style: "runtime" }));
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const dependencies = createReaderCollection({
|
|
90
|
-
name: `Dependency reader collection for project ${rootProject.getName()}`,
|
|
91
|
-
readers,
|
|
92
|
-
});
|
|
93
|
-
|
|
91
|
+
// create a reader for the root project
|
|
94
92
|
const rootReader = rootProject.getReader({ style: "runtime" });
|
|
95
|
-
|
|
96
|
-
// TODO change to ReaderCollection once duplicates are sorted out
|
|
97
|
-
const combo = createReaderCollection({
|
|
98
|
-
name: "server - prioritize workspace over dependencies",
|
|
99
|
-
readers: [rootReader, dependencies],
|
|
100
|
-
});
|
|
101
|
-
const resources = {
|
|
102
|
-
rootProject: rootReader,
|
|
103
|
-
dependencies: dependencies,
|
|
104
|
-
all: combo,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// TODO: rework ui5-server API and make public
|
|
108
|
-
const { default: MiddlewareManager } = await import("@ui5/server/internal/MiddlewareManager");
|
|
109
|
-
const middlewareManager = new MiddlewareManager({
|
|
110
|
-
graph,
|
|
111
|
-
rootProject,
|
|
112
|
-
resources,
|
|
113
|
-
options: {
|
|
114
|
-
//sendSAPTargetCSP,
|
|
115
|
-
//serveCSPReports,
|
|
116
|
-
//simpleIndex: true
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
await middlewareManager.applyMiddleware(router);
|
|
93
|
+
logPerformance && log.info(`[PERF] Root project took ${Date.now() - millisRoot}ms`);
|
|
120
94
|
|
|
121
95
|
// for Fiori elements based applications we need to invalidate the view cache
|
|
96
|
+
// so we need to append the query parameter to the HTML files (sap-ui-xx-viewCache=false)
|
|
122
97
|
const isFioriElementsBased = rootProject.getFrameworkDependencies().find((lib) => lib.name.startsWith("sap.fe"));
|
|
123
98
|
|
|
124
99
|
// collect app pages from workspace (glob testing: https://globster.xyz/ and https://codepen.io/mrmlnc/pen/OXQjMe)
|
|
125
100
|
// -> but exclude the HTML fragments from the list of app pages!
|
|
101
|
+
const millisPages = logPerformance && Date.now();
|
|
126
102
|
const pages = (await rootReader.byGlob("**/!(*.fragment).{html,htm}")).map((resource) => `${resource.getPath()}${isFioriElementsBased ? "?sap-ui-xx-viewCache=false" : ""}`);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
103
|
+
logPerformance && log.info(`[PERF] Pages took ${Date.now() - millisPages}ms`);
|
|
104
|
+
|
|
105
|
+
// method to create the middleware manager and to apply the middlewares
|
|
106
|
+
// to the express router provided as a parameter to this function
|
|
107
|
+
const apply = async () => {
|
|
108
|
+
// find the relevant readers for the dependencies
|
|
109
|
+
const readers = [];
|
|
110
|
+
await graph.traverseBreadthFirst(async function ({ project: dep }) {
|
|
111
|
+
if (dep.getName() === rootProject.getName()) {
|
|
112
|
+
// Ignore root project
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
readers.push(dep.getReader({ style: "runtime" }));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const { createReaderCollection } = await import("@ui5/fs/resourceFactory");
|
|
119
|
+
|
|
120
|
+
// create a reader collection for the dependencies
|
|
121
|
+
const dependencies = createReaderCollection({
|
|
122
|
+
name: `Dependency reader collection for project ${rootProject.getName()}`,
|
|
123
|
+
readers,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// TODO change to ReaderCollection once duplicates are sorted out
|
|
127
|
+
const combo = createReaderCollection({
|
|
128
|
+
name: "server - prioritize workspace over dependencies",
|
|
129
|
+
readers: [rootReader, dependencies],
|
|
130
|
+
});
|
|
131
|
+
const resources = {
|
|
132
|
+
rootProject: rootReader,
|
|
133
|
+
dependencies: dependencies,
|
|
134
|
+
all: combo,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// TODO: rework ui5-server API and make public
|
|
138
|
+
const { default: MiddlewareManager } = await import("@ui5/server/internal/MiddlewareManager");
|
|
139
|
+
const middlewareManager = new MiddlewareManager({
|
|
140
|
+
graph,
|
|
141
|
+
rootProject,
|
|
142
|
+
resources,
|
|
143
|
+
options: {
|
|
144
|
+
//sendSAPTargetCSP,
|
|
145
|
+
//serveCSPReports,
|
|
146
|
+
//simpleIndex: true
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
await middlewareManager.applyMiddleware(router);
|
|
150
|
+
|
|
151
|
+
// collect app pages from middlewares implementing the getAppPages
|
|
152
|
+
// which will only work if the middleware is executed synchronously
|
|
153
|
+
middlewareManager.middlewareExecutionOrder?.map((name) => {
|
|
154
|
+
const { middleware } = middlewareManager.middleware?.[name] || {};
|
|
155
|
+
if (typeof middleware?.getAppPages === "function") {
|
|
156
|
+
if (!options.lazy) {
|
|
157
|
+
const customAppPages = middleware.getAppPages();
|
|
158
|
+
if (Array.isArray(customAppPages)) {
|
|
159
|
+
pages.push(...customAppPages);
|
|
160
|
+
} else {
|
|
161
|
+
if (customAppPages) {
|
|
162
|
+
log.warn(`The middleware ${name} returns an unexpected value for "getAppPages". The value must be either undefined or string[]! Ignoring app pages from middleware!`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
log.warn(`The middleware ${name} returns a function for "getAppPages" but the lazy option is enabled. The function will not be executed!`);
|
|
138
167
|
}
|
|
139
168
|
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// install a callback in the router to apply the middlewares
|
|
173
|
+
if (options.lazy) {
|
|
174
|
+
let isApplied = false;
|
|
175
|
+
router.use(async (req, res, next) => {
|
|
176
|
+
if (!isApplied) {
|
|
177
|
+
await apply();
|
|
178
|
+
isApplied = true;
|
|
179
|
+
}
|
|
180
|
+
next();
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
await apply();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
logPerformance && log.info(`[PERF] applyUI5Middleware took ${Date.now() - millis}ms`);
|
|
142
187
|
|
|
188
|
+
// return the UI5 application information
|
|
143
189
|
return {
|
|
144
190
|
pages,
|
|
145
191
|
};
|
|
@@ -6,10 +6,10 @@ const rewriteHTML = require("./rewriteHTML");
|
|
|
6
6
|
* from urls and disabling the encoding
|
|
7
7
|
* @returns {Router} patched router
|
|
8
8
|
*/
|
|
9
|
-
module.exports =
|
|
9
|
+
module.exports = function createPatchedRouter() {
|
|
10
10
|
// create the router and get rid of the mount path
|
|
11
11
|
const router = new Router();
|
|
12
|
-
router.use(function (req, res, next) {
|
|
12
|
+
router.use(async function (req, res, next) {
|
|
13
13
|
// store the original request information
|
|
14
14
|
const { url, originalUrl, baseUrl } = req;
|
|
15
15
|
req["ui5-patched-router"] = req["ui5-patched-router"] || {
|
|
@@ -64,7 +64,7 @@ module.exports = async function createPatchedRouter() {
|
|
|
64
64
|
});
|
|
65
65
|
h1?.insertAdjacentHTML("afterbegin", `<a href="/">🏡</a> / `);
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
},
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
// next one!
|
package/lib/findUI5Modules.js
CHANGED
|
@@ -32,7 +32,7 @@ module.exports = async function findUI5Modules({ cwd, cds, skipLocalApps, skipDe
|
|
|
32
32
|
try {
|
|
33
33
|
modulesConfig = JSON.parse(process.env.CDS_PLUGIN_UI5_MODULES);
|
|
34
34
|
log.info(`Using modules configuration from env`);
|
|
35
|
-
} catch
|
|
35
|
+
} catch {
|
|
36
36
|
modulesConfig = pkgJson.cds?.["cds-plugin-ui5"]?.modules;
|
|
37
37
|
}
|
|
38
38
|
if (modulesConfig) {
|
|
@@ -101,10 +101,10 @@ module.exports = async function findUI5Modules({ cwd, cds, skipLocalApps, skipDe
|
|
|
101
101
|
paths: [cwd],
|
|
102
102
|
});
|
|
103
103
|
return true;
|
|
104
|
-
} catch
|
|
104
|
+
} catch {
|
|
105
105
|
return false;
|
|
106
106
|
}
|
|
107
|
-
})
|
|
107
|
+
}),
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cds-plugin-ui5",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A CDS server plugin to inject the middlewares of all related UI5 tooling based projects.",
|
|
5
5
|
"author": "Peter Muessig",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
"@sap/cds": ">=6.8.2",
|
|
27
27
|
"express": ">=4.18.2"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "0eb73f5043c510e48377e943d27f817d46178e00"
|
|
30
30
|
}
|