Skip to content

Commit

Permalink
feat(cds-plugin-ui5): use CAP logging, custom formatter using colors,…
Browse files Browse the repository at this point in the history
… allow log levels (#1067)

* Use CAP logging to allow for log levels

* feat(cds-plugin-ui5): reuse CDS logger + coloring

---------

Co-authored-by: Marten Schiwek <[email protected]>
  • Loading branch information
petermuessig and schiwekM authored Aug 29, 2024
1 parent 8a720d1 commit 4be3b08
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 52 deletions.
9 changes: 9 additions & 0 deletions packages/cds-plugin-ui5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ The configuration can also be injected with the environment variable `CDS_PLUGIN
CDS_PLUGIN_UI5_MODULES="{ \"ui5-bookshop\": { \"mountPath\": \"/the-bookshop\" } }" cds-serve
```

#### Logger

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`.

```sh
# disable the colored logging extension of the cds-plugin-ui5
CDS_PLUGIN_UI5_NO_CUSTOM_LOGGER=1 cds watch
```

#### UI5 Server Middlewares

If you require to configure the UI5 server middlewares, you can do so by adding some selected configuration options in the `package.json` of the CDS server in the section: `cds/cds-plugin-ui5/modules/%moduleId%`
Expand Down
64 changes: 43 additions & 21 deletions packages/cds-plugin-ui5/cds-plugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const log = require("./lib/log");

// >> IMPORTANT <<
//
// JEST has issues with dynamic imports and will fail when they are used,
Expand All @@ -16,17 +14,40 @@ const log = require("./lib/log");
//
// To disable JEST we rely on env variables (see https://jestjs.io/docs/environment-variables)
if (process.env.NODE_ENV === "test" && process.env.JEST_WORKER_ID && process.env.CDS_PLUGIN_UI5_ACTIVE !== "true") {
log.info("Skip execution of plugin because JEST is running tests! To force the execution of the plugin set env var CDS_PLUGIN_UI5_ACTIVE=true...");
console.log(
process.env.NO_COLOR ? "[%s] %s" : "\x1b[36m[%s]\x1b[0m \x1b[31m%s\x1b[0m",
"cds-plugin-ui5",
"Skip execution because JEST is running tests! To force the execution of the plugin set env var CDS_PLUGIN_UI5_ACTIVE=true..."
);
return;
}
if (process.env.CDS_PLUGIN_UI5_ACTIVE === "false") {
log.info("Skip execution of plugin because it has been disabled by env var CDS_PLUGIN_UI5_ACTIVE!");
console.log(process.env.NO_COLOR ? "[%s] %s" : "\x1b[36m[%s]\x1b[0m \x1b[31m%s\x1b[0m", "cds-plugin-ui5", "Skip execution because it has been disabled by env var CDS_PLUGIN_UI5_ACTIVE!");
return;
}

// @sap/cds/lib/index.js#138: global.cds = cds // REVISIT: using global.cds seems wrong
const cds = global.cds || require("@sap/cds"); // reuse already loaded cds!

// add color support to the logger
if (!(process.env.NO_COLOR || process.env.CDS_PLUGIN_UI5_NO_CUSTOM_LOGGER)) {
const LOG_COLORS = {
TRACE: "\x1b[0m", // default
DEBUG: "\x1b[34m", // blue
INFO: "\x1b[32m", // green
WARN: "\x1b[33m", // yellow
ERROR: "\x1b[31m", // red
};
const LOG_LEVEL_TO_COLOR = Object.fromEntries(Object.keys(LOG_COLORS).map((level) => [cds.log.levels[level], LOG_COLORS[level]]));
const LOG_LEVEL_TO_TEXT = Object.fromEntries(Object.keys(LOG_COLORS).map((level) => [cds.log.levels[level], level]));
cds.log.format = (label, level, ...args) => {
return ["\x1b[36m[%s]\x1b[0m %s[%s]\x1b[0m %s", label, LOG_LEVEL_TO_COLOR[level], LOG_LEVEL_TO_TEXT[level], ...args];
};
}

// create a logger for the cds-plugin-ui5
const LOG = cds.log("cds-plugin-ui5");

const findUI5Modules = require("./lib/findUI5Modules");
const createPatchedRouter = require("./lib/createPatchedRouter");
const applyUI5Middleware = require("./lib/applyUI5Middleware");
Expand All @@ -35,10 +56,10 @@ const rewriteHTML = require("./lib/rewriteHTML");
// identify whether the execution should be skipped
let skip = false;
if (process.env["ui5-middleware-cap"]) {
log.info("Skip execution of plugin because is has been started via ui5-middleware-cap!");
LOG.info("Skip execution of plugin because is has been started via ui5-middleware-cap!");
skip = true;
} else if (process.env["dev-approuter"]) {
log.info("Skip execution of plugin because is has been started via dev-approuter!");
LOG.info("Skip execution of plugin because is has been started via dev-approuter!");
skip = true;
}

Expand Down Expand Up @@ -108,11 +129,11 @@ if (!skip) {
const cdsdkVersion = getCDSDKVersion();

// logging the version of the cds-plugin-ui5
log.info(`Running cds-plugin-ui5@${cdsPluginUI5Version} (@sap/cds-dk@${cdsdkVersion}, @sap/cds@${cds.version})`);
LOG.info(`Running cds-plugin-ui5@${cdsPluginUI5Version} (@sap/cds-dk@${cdsdkVersion}, @sap/cds@${cds.version})`);
if (global.__cds_loaded_from?.size > 1) {
log.warn(" !! Multiple versions of @sap/cds loaded !!");
LOG.warn(" !! Multiple versions of @sap/cds loaded !!");
global.__cds_loaded_from.forEach((cdsPath) => {
log.warn(` => ${cdsPath}`);
LOG.warn(` => ${cdsPath}`);
});
}

Expand All @@ -127,19 +148,19 @@ if (!skip) {
// CDS server until the bootstrap is completed and the UI5
// middlewares for the UI5 applications are properly available
cds.on("served", async function served(/* cdsServices */) {
log.debug("served");
LOG.debug("served");
await bootstrapCompleted;
});

// hook into the "bootstrap" event to startup the UI5 middlewares
// for the available UI5 applications in the CDS server and its deps
cds.on("bootstrap", async function bootstrap(app) {
log.debug("bootstrap");
LOG.debug("bootstrap");

// only for cds serve or serving all services the cds-plugin-ui5 is active
if (cds.cli?.command === "serve" || cds.options?.service === "all") {
const cwd = cds.env?._home || process.cwd();
const ui5Modules = await findUI5Modules({ cwd, cds });
const ui5Modules = await findUI5Modules({ cwd, cds, LOG });
const { localApps, config } = ui5Modules;

const links = [];
Expand All @@ -149,7 +170,7 @@ if (!skip) {
const { moduleId, mountPath, modulePath } = ui5Module;

// mounting the Router for the UI5 application to the CDS server
log.info(`Mounting ${mountPath} to UI5 app ${modulePath} (id=${moduleId})${config[moduleId] ? ` using config=${JSON.stringify(config[moduleId])}` : ""}`);
LOG.info(`Mounting ${mountPath} to UI5 app ${modulePath} (id=${moduleId})${config[moduleId] ? ` using config=${JSON.stringify(config[moduleId])}` : ""}`);

// create a patched router
const router = await createPatchedRouter();
Expand All @@ -160,6 +181,7 @@ if (!skip) {
cwd,
basePath: modulePath,
...(config[moduleId] || {}),
LOG,
});

// register the router to the specified mount path
Expand Down Expand Up @@ -228,7 +250,7 @@ if (!skip) {
);
ul.innerHTML = newLis.join("\n");
} else {
log.warn(`Failed to inject application links into CDS index page!`);
LOG.warn(`Failed to inject application links into CDS index page!`);
}
}
);
Expand All @@ -245,14 +267,14 @@ if (!skip) {
if (idxOfServeStatic !== -1) {
middlewareStack.splice(idxOfServeStatic, 0, cmw);
} else {
log.error(`Failed to determine welcome page middleware! The links of the application pages may not work properly!`);
LOG.error(`Failed to determine welcome page middleware! The links of the application pages may not work properly!`);
}
} else {
log.error(`Failed to inject application pages to welcome page! The links of the application pages may not work properly!`);
LOG.error(`Failed to inject application pages to welcome page! The links of the application pages may not work properly!`);
}
}
} else {
log.info("Skip execution of plugin! The plugin is only active for 'cds serve'!");
LOG.info("Skip execution of plugin! The plugin is only active for 'cds serve'!");
}

// bootstrap completed, unlock the "served" event
Expand Down Expand Up @@ -313,7 +335,7 @@ if (!skip) {
// sanitize the package.json if it exists
const packageJsonPath = join(this.task.dest, "package.json");
if (existsSync(packageJsonPath)) {
log.info(`Sanitizing the package.json for "${namespace}"...`);
LOG.info(`Sanitizing the package.json for "${namespace}"...`);
const packageJson = JSON.parse(await readFile(packageJsonPath), "utf-8");
let modified = false;
// remove the workspace configuration
Expand All @@ -338,13 +360,13 @@ if (!skip) {
}
// update the package-lock.json if it exists
if (existsSync(join(this.task.dest, "package-lock.json"))) {
log.info(`Updating the package-lock.json for "${namespace}"...`);
LOG.info(`Updating the package-lock.json for "${namespace}"...`);
// run the npm install --package-lock-only to only update the package-lock.json
// without installing the dependencies to node_modules
try {
/* const { stdout, stderr } = */ await exec("npm install --package-lock-only", { cwd: this.task.dest });
} catch (e) {
log.error(`Failed to update the package-lock.json for "${namespace}"! Error: ${e.code} - ${e.message}`);
LOG.error(`Failed to update the package-lock.json for "${namespace}"! Error: ${e.code} - ${e.message}`);
}
}
}
Expand All @@ -354,7 +376,7 @@ if (!skip) {
if (!satisfies(cdsdkVersion, ">=7.5.0")) {
// TODO: add error message to inform the user that the cds build task is not available
// and that the @sap/cds-dk version is too old to support the cds build task
log.warn("The cds build task requires @sap/cds-dk version >= 7.5.0! Skipping execution as your @sap/cds-dk version is too old...");
LOG.warn("The cds build task requires @sap/cds-dk version >= 7.5.0! Skipping execution as your @sap/cds-dk version is too old...");
}
}
}
5 changes: 3 additions & 2 deletions packages/cds-plugin-ui5/lib/applyUI5Middleware.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
const path = require("path");
const fs = require("fs");

const log = require("./log");

/**
* @typedef UI5AppInfo
* @type {object}
Expand All @@ -21,6 +19,7 @@ const log = require("./log");
* @property {string} [workspaceConfigPath] /!\ RESTRICTED /!\ - path to the ui5-workspace.yaml (defaults to "${basePath}/${workspaceConfigFile}")
* @property {string} [versionOverride] Framework version to use instead of the one defined in the root project
* @property {string} [cacheMode] /!\ RESTRICTED /!\ - Cache mode to use when consuming SNAPSHOT versions of a framework (one of: Default|False|Off)
* @property {string} [log] the logger (defaults to console)
*/

// inspired by https://github.com/SAP/karma-ui5/blob/main/lib/framework.js#L466-L522
Expand All @@ -38,6 +37,8 @@ module.exports = async function applyUI5Middleware(router, options) {
options.cwd = options.cwd || process.cwd();
options.basePath = options.basePath || process.cwd();

const log = options.log || console;

const configPath = options.configPath || options.basePath;
const configFile = options.configFile || "ui5.yaml";
const workspaceConfigPath = options.workspaceConfigPath || options.basePath;
Expand Down
5 changes: 2 additions & 3 deletions packages/cds-plugin-ui5/lib/findUI5Modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ const path = require("path");
const fs = require("fs");
const yaml = require("js-yaml");

const log = require("./log");

/**
* @typedef UI5Module
* @type {object}
Expand All @@ -19,9 +17,10 @@ const log = require("./log");
* @param {string} options.cds reference to cds
* @param {string} options.skipLocalApps skip local apps
* @param {string} options.skipDeps skip dependencies
* @param {string} options.log the logger (defaults to console)
* @returns {Array<UI5Module>} array of UI5 module
*/
module.exports = async function findUI5Modules({ cwd, cds, skipLocalApps, skipDeps }) {
module.exports = async function findUI5Modules({ cwd, cds, skipLocalApps, skipDeps, log = console }) {
// extract the modules configuration from the package.json
const pkgJson = require(path.join(cwd, "package.json"));

Expand Down
26 changes: 0 additions & 26 deletions packages/cds-plugin-ui5/lib/log.js

This file was deleted.

0 comments on commit 4be3b08

Please sign in to comment.