cds-plugin-ui5 0.1.5 → 0.1.7

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 CHANGED
@@ -3,6 +3,29 @@
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.1.7](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/cds-plugin-ui5@0.1.6...cds-plugin-ui5@0.1.7) (2023-07-28)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **cds-plugin-ui5:** allow to select apps or deps ([#775](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/775)) ([aeefb3f](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/aeefb3f5aa70129489f90d684965ade6fc20ef53))
12
+ * **cds-plugin-ui5:** proper dependency handling / decoupling ([#773](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/773)) ([9ef8bf3](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/9ef8bf3da69721a2d599a119407e53c1272fb884))
13
+
14
+
15
+
16
+
17
+
18
+ ## [0.1.6](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/cds-plugin-ui5@0.1.5...cds-plugin-ui5@0.1.6) (2023-07-11)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * support single app in ./app ([#763](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/763)) ([8642de6](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/8642de6695efd49bc0e1b4e907497a82241bc90d))
24
+
25
+
26
+
27
+
28
+
6
29
  ## [0.1.5](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/cds-plugin-ui5@0.1.4...cds-plugin-ui5@0.1.5) (2023-07-01)
7
30
 
8
31
 
package/cds-plugin.js CHANGED
@@ -1,139 +1,55 @@
1
- const path = require("path");
2
- const fs = require("fs");
3
- const yaml = require("js-yaml");
4
-
5
- const cds = require("@sap/cds");
6
- const { Router } = require("express");
1
+ // @sap/cds/lib/index.js#138: global.cds = cds // REVISIT: using global.cds seems wrong
2
+ const cds = global.cds || require("@sap/cds"); // reuse already loaded cds!
7
3
 
4
+ const log = require("./lib/log");
5
+ const findUI5Modules = require("./lib/findUI5Modules");
6
+ const createPatchedRouter = require("./lib/createPatchedRouter");
8
7
  const applyUI5Middleware = require("./lib/applyUI5Middleware");
9
8
 
10
9
  // marker that the cds-plugin-ui5 plugin is running
11
10
  // to disable the ui5-middleware-cap if used in apps
12
11
  process.env["cds-plugin-ui5"] = true;
13
12
 
14
- /**
15
- * helper to log colorful messages
16
- * @param {string} type the type of the message
17
- * @param {string} message the message text
18
- */
19
- function log(type, message) {
20
- const colors = {
21
- log: "\x1b[0m", // default
22
- info: "\x1b[32m", // green
23
- debug: "\x1b[34m", // blue
24
- warn: "\x1b[33m", // yellow
25
- error: "\x1b[31m", // red
26
- };
27
- if (!console[type]) {
28
- type = "log";
29
- }
30
- console[type](`\x1b[36m[cds-ui5-plugin]\x1b[0m %s[%s]\x1b[0m %s`, colors[type], type, message);
31
- }
32
-
33
13
  cds.on("bootstrap", async function bootstrap(app) {
34
- log("debug", "bootstrap");
14
+ log.debug("bootstrap");
35
15
 
36
- // lookup the app folder to determine local apps and ui5 app directories
37
- const localApps = new Set(),
38
- appDirs = [];
39
- fs.readdirSync(path.join(process.cwd(), "app"), { withFileTypes: true })
40
- .filter((f) => f.isDirectory())
41
- .forEach((d) => localApps.add(d.name));
42
- localApps.forEach((e) => {
43
- const d = path.join(process.cwd(), "app", e);
44
- if (fs.existsSync(path.join(d, "ui5.yaml"))) {
45
- localApps.delete(e);
46
- appDirs.push(d);
47
- }
48
- });
16
+ const cwd = process.cwd();
17
+ const ui5Modules = await findUI5Modules({ cwd });
18
+ const localApps = ui5Modules.localApps;
49
19
 
50
- // lookup the UI5 dependencies
51
- const pkgJson = require(path.join(process.cwd(), "package.json"));
52
- const deps = [];
53
- deps.push(...Object.keys(pkgJson.dependencies || {}));
54
- deps.push(...Object.keys(pkgJson.devDependencies || {}));
55
- //deps.push(...Object.keys(pkgJson.peerDependencies || {}));
56
- //deps.push(...Object.keys(pkgJson.optionalDependencies || {}));
57
- appDirs.push(
58
- ...deps.filter((dep) => {
59
- try {
60
- require.resolve(`${dep}/ui5.yaml`, {
61
- paths: [process.cwd()],
62
- });
63
- return true;
64
- } catch (e) {
65
- return false;
66
- }
67
- })
68
- );
69
-
70
- // if apps are available, attach the middlewares of the UI5 apps
71
- // to the express of the CAP server via a express router
72
- if (appDirs) {
73
- const links = [];
74
- for await (const appDir of appDirs) {
75
- // read the ui5.yaml file to extract the configuration
76
- const ui5YamlPath = require.resolve(path.join(appDir, "ui5.yaml"), {
77
- paths: [process.cwd()],
78
- });
79
- let ui5Configs;
80
- try {
81
- const content = fs.readFileSync(ui5YamlPath, "utf-8");
82
- ui5Configs = yaml.loadAll(content);
83
- } catch (err) {
84
- if (err.name === "YAMLException") {
85
- log("error", `Failed to read ${ui5YamlPath}!`);
86
- }
87
- throw err;
88
- }
20
+ const links = [];
89
21
 
90
- // by default the mount path is derived from the metadata/name
91
- // and can be overridden by customConfiguration/mountPath
92
- const ui5Config = ui5Configs?.[0];
93
- let mountPath = ui5Config?.customConfiguration?.mountPath || ui5Config?.metadata?.name;
94
- if (!/^\//.test(mountPath)) {
95
- mountPath = `/${mountPath}`; // always start with /
96
- }
22
+ // register the UI5 modules via their own router/middlewares
23
+ for await (const ui5Module of ui5Modules) {
24
+ const { mountPath, modulePath } = ui5Module;
97
25
 
98
- // mounting the Router for the application to the CAP server
99
- log("info", `Mounting ${mountPath} to UI5 app ${appDir}`);
100
- const modulePath = path.dirname(ui5YamlPath);
26
+ // mounting the Router for the UI5 application to the CAP server
27
+ log.info(`Mounting ${mountPath} to UI5 app ${modulePath}`);
101
28
 
102
- // create the router and get rid of the mount path
103
- const router = new Router();
104
- router.use(function (req, res, next) {
105
- // disable the compression when livereload is used
106
- // for loading html-related content (via accept header)
107
- const accept = req.headers["accept"]?.indexOf("html");
108
- if (accept && res._livereload) {
109
- req.headers["accept-encoding"] = "identity";
110
- }
111
- // remove the mount path from the url
112
- req.originalUrl = req.url;
113
- req.baseUrl = "/";
114
- next();
115
- });
29
+ // create a patched router
30
+ const router = await createPatchedRouter();
116
31
 
117
- // apply the UI5 middlewares to the router and
118
- // retrieve the available HTML pages
119
- const pages = await applyUI5Middleware(router, {
120
- basePath: modulePath,
121
- configPath: modulePath,
122
- });
32
+ // apply the UI5 middlewares to the router and
33
+ // retrieve the available HTML pages
34
+ const appInfo = await applyUI5Middleware(router, {
35
+ basePath: modulePath,
36
+ configPath: modulePath,
37
+ });
123
38
 
124
- // append the HTML pages to the links
125
- pages.forEach((page) => {
126
- const prefix = mountPath !== "/" ? mountPath : "";
127
- links.push(`${prefix}${page.getPath()}`);
128
- });
39
+ // register the router to the specified mount path
40
+ app.use(mountPath, router);
129
41
 
130
- // mount the router to the determined mount path
131
- app.use(`${mountPath}`, router);
132
- }
42
+ // append the HTML pages to the links
43
+ appInfo.pages.forEach((page) => {
44
+ const prefix = mountPath !== "/" ? mountPath : "";
45
+ links.push(`${prefix}${page.getPath()}`);
46
+ });
47
+ }
133
48
 
49
+ if (links.length > 0) {
134
50
  // register the custom middleware (similar like in @sap/cds/server.js)
135
51
  app.get("/", function appendLinksToIndex(req, res, next) {
136
- var send = res.send;
52
+ const send = res.send;
137
53
  res.send = function (content) {
138
54
  // the first <ul> element contains the links to the
139
55
  // application pages which is fully under control of
@@ -175,11 +91,11 @@ cds.on("bootstrap", async function bootstrap(app) {
175
91
  ul.innerHTML = newLis.join("\n");
176
92
  content = doc.toString();
177
93
  } else {
178
- log("warn", `Failed to inject application links into CAP index page!`);
94
+ log.warn(`Failed to inject application links into CAP index page!`);
179
95
  }
180
96
  send.apply(this, arguments);
181
97
  };
182
- //log("debug", req.url);
98
+ //log.debug(req.url);
183
99
  next();
184
100
  });
185
101
 
@@ -193,10 +109,10 @@ cds.on("bootstrap", async function bootstrap(app) {
193
109
  if (idxOfServeStatic !== -1) {
194
110
  middlewareStack.splice(idxOfServeStatic, 0, cmw);
195
111
  } else {
196
- log("error", `Failed to determine CAP overview page middleware! You need to manually open the application pages!`);
112
+ log.error(`Failed to determine CAP overview page middleware! You need to manually open the application pages!`);
197
113
  }
198
114
  } else {
199
- log("error", `Failed to inject application pages to CAP overview page! You need to manually open the application pages!`);
115
+ log.error(`Failed to inject application pages to CAP overview page! You need to manually open the application pages!`);
200
116
  }
201
117
  }
202
118
  });
@@ -204,6 +120,6 @@ cds.on("bootstrap", async function bootstrap(app) {
204
120
  // return callback for plugin activation
205
121
  module.exports = {
206
122
  activate: function activate(conf) {
207
- log("debug", "activate", conf);
123
+ log.debug("activate", conf);
208
124
  },
209
125
  };
@@ -1,6 +1,21 @@
1
1
  const path = require("path");
2
2
 
3
+ /**
4
+ * @typedef UI5AppInfo
5
+ * @type {object}
6
+ * @property {Array<string>} pages root path of the module
7
+ */
8
+
3
9
  // inspired by https://github.com/SAP/karma-ui5/blob/main/lib/framework.js#L466-L522
10
+ /**
11
+ * Applies the middlewares for the UI5 application located in the given
12
+ * root directory to the given router.
13
+ * @param {import("express").Router} router Express Router instance
14
+ * @param {object} options configuration options
15
+ * @param {string} options.basePath base path of the UI5 application
16
+ * @param {string} [options.configPath] path to the ui5.yaml (defaults to "${basePath}/ui5.yaml")
17
+ * @returns {UI5AppInfo} UI5 application information object
18
+ */
4
19
  module.exports = async function applyUI5Middleware(router, { basePath, configPath }) {
5
20
  const { graphFromPackageDependencies } = await import("@ui5/project/graph");
6
21
  const { createReaderCollection } = await import("@ui5/fs/resourceFactory");
@@ -54,5 +69,7 @@ module.exports = async function applyUI5Middleware(router, { basePath, configPat
54
69
  });
55
70
  await middlewareManager.applyMiddleware(router);
56
71
 
57
- return await rootReader.byGlob("**/*.html");
72
+ return {
73
+ pages: await rootReader.byGlob("**/*.html"),
74
+ };
58
75
  };
@@ -0,0 +1,24 @@
1
+ const { Router } = require("express");
2
+
3
+ /**
4
+ * Creates a patched router removing the mount path
5
+ * from urls and disabling the encoding
6
+ * @returns {Router} patched router
7
+ */
8
+ module.exports = async function createPatchedRouter() {
9
+ // create the router and get rid of the mount path
10
+ const router = new Router();
11
+ router.use(function (req, res, next) {
12
+ // disable the compression when livereload is used
13
+ // for loading html-related content (via accept header)
14
+ const accept = req.headers["accept"]?.indexOf("html");
15
+ if (accept && res._livereload) {
16
+ req.headers["accept-encoding"] = "identity";
17
+ }
18
+ // remove the mount path from the url
19
+ req.originalUrl = req.url;
20
+ req.baseUrl = "/";
21
+ next();
22
+ });
23
+ return router;
24
+ };
@@ -0,0 +1,109 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const yaml = require("js-yaml");
4
+
5
+ const log = require("./log");
6
+
7
+ /**
8
+ * @typedef UI5Module
9
+ * @type {object}
10
+ * @property {string} modulePath root path of the module
11
+ * @property {string} mountPath path to mount the module to
12
+ */
13
+
14
+ /**
15
+ * Returns all UI5 modules from local apps and the project dependencies.
16
+ * @param {object} options configuration options
17
+ * @param {string} options.cwd current working directory
18
+ * @param {string} options.skipLocalApps skip local apps
19
+ * @param {string} options.skipDeps skip dependencies
20
+ * @returns {Array<UI5Module>} array of UI5 module
21
+ */
22
+ module.exports = async function findUI5Modules({ cwd, skipLocalApps, skipDeps }) {
23
+ // lookup the app folder to determine local apps and UI5 apps
24
+ const localApps = new Set();
25
+ const appDirs = [];
26
+ if (!skipLocalApps) {
27
+ const appDir = path.join(cwd, "app");
28
+ if (fs.existsSync(appDir)) {
29
+ fs.readdirSync(appDir, { withFileTypes: true })
30
+ .filter((f) => f.isDirectory())
31
+ .forEach((d) => localApps.add(d.name));
32
+ localApps.forEach((e) => {
33
+ const d = path.join(appDir, e);
34
+ if (fs.existsSync(path.join(d, "ui5.yaml"))) {
35
+ localApps.delete(e);
36
+ appDirs.push(d);
37
+ }
38
+ });
39
+ }
40
+
41
+ // look for a single app if no apps were found in the app directories
42
+ if (appDirs.length === 0) {
43
+ if (fs.existsSync(path.join(appDir, "ui5.yaml"))) {
44
+ appDirs.push(appDir);
45
+ }
46
+ }
47
+ }
48
+
49
+ // lookup the UI5 modules in the project dependencies
50
+ if (!skipDeps) {
51
+ const pkgJson = require(path.join(cwd, "package.json"));
52
+ const deps = [];
53
+ deps.push(...Object.keys(pkgJson.dependencies || {}));
54
+ deps.push(...Object.keys(pkgJson.devDependencies || {}));
55
+ //deps.push(...Object.keys(pkgJson.peerDependencies || {}));
56
+ //deps.push(...Object.keys(pkgJson.optionalDependencies || {}));
57
+ appDirs.push(
58
+ ...deps.filter((dep) => {
59
+ try {
60
+ require.resolve(`${dep}/ui5.yaml`, {
61
+ paths: [cwd],
62
+ });
63
+ return true;
64
+ } catch (e) {
65
+ return false;
66
+ }
67
+ })
68
+ );
69
+ }
70
+
71
+ // if apps are available, attach the middlewares of the UI5 apps
72
+ // to the express of the CAP server via a express router
73
+ const apps = [];
74
+ if (appDirs) {
75
+ for await (const appDir of appDirs) {
76
+ // read the ui5.yaml file to extract the configuration
77
+ const ui5YamlPath = require.resolve(path.join(appDir, "ui5.yaml"), {
78
+ paths: [cwd],
79
+ });
80
+ let ui5Configs;
81
+ try {
82
+ const content = fs.readFileSync(ui5YamlPath, "utf-8");
83
+ ui5Configs = yaml.loadAll(content);
84
+ } catch (err) {
85
+ if (err.name === "YAMLException") {
86
+ log("error", `Failed to read ${ui5YamlPath}!`);
87
+ }
88
+ throw err;
89
+ }
90
+
91
+ // by default the mount path is derived from the metadata/name
92
+ // and can be overridden by customConfiguration/mountPath
93
+ const ui5Config = ui5Configs?.[0];
94
+ const isApplication = ui5Config?.type === "application";
95
+ if (isApplication) {
96
+ let mountPath = ui5Config?.customConfiguration?.mountPath || ui5Config?.metadata?.name;
97
+ if (!/^\//.test(mountPath)) {
98
+ mountPath = `/${mountPath}`; // always start with /
99
+ }
100
+
101
+ // determine the module path based on the location of the ui5.yaml
102
+ const modulePath = path.dirname(ui5YamlPath);
103
+ apps.push({ modulePath, mountPath });
104
+ }
105
+ }
106
+ }
107
+ apps.localApps = localApps; // necessary for CAP index.html rewrite
108
+ return apps;
109
+ };
package/lib/log.js ADDED
@@ -0,0 +1,26 @@
1
+ const colors = {
2
+ log: "\x1b[0m", // default
3
+ debug: "\x1b[34m", // blue
4
+ info: "\x1b[32m", // green
5
+ warn: "\x1b[33m", // yellow
6
+ error: "\x1b[31m", // red
7
+ };
8
+
9
+ /**
10
+ * helper to log colorful messages
11
+ * @param {string} type the type of the message
12
+ * @param {...string} message the message text
13
+ */
14
+ function log(type, ...message) {
15
+ if (!console[type]) {
16
+ type = "log";
17
+ }
18
+ const args = [`\x1b[36m[cds-ui5-plugin]\x1b[0m %s[%s]\x1b[0m %s`, colors[type], type];
19
+ args.push(message);
20
+ console[type].apply(console[type], args);
21
+ }
22
+
23
+ module.exports = log;
24
+ Object.keys(colors).forEach((level) => {
25
+ module.exports[level] = log.bind(this, level);
26
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cds-plugin-ui5",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A CAP server cds-plugin to inject the middlewares of all related UI5 tooling based projects.",
5
5
  "author": "Peter Muessig",
6
6
  "license": "Apache-2.0",
@@ -24,5 +24,5 @@
24
24
  "@sap/cds": ">=6.8.2",
25
25
  "express": ">=4.18.2"
26
26
  },
27
- "gitHead": "45cf9d0a37a56836cc4771ecf1f8ef7bff03f9c5"
27
+ "gitHead": "03bdac05dfa37252b4df03a865810ab807cb91b5"
28
28
  }