diff --git a/packages/cds-plugin-ui5/cds-plugin.js b/packages/cds-plugin-ui5/cds-plugin.js index df1bf04a..78ce4dbf 100644 --- a/packages/cds-plugin-ui5/cds-plugin.js +++ b/packages/cds-plugin-ui5/cds-plugin.js @@ -13,370 +13,383 @@ // - https://github.com/ui5-community/ui5-ecosystem-showcase/issues/901 // // To disable JEST we rely on env variables (see https://jestjs.io/docs/environment-variables) +let skip = false; if (process.env.NODE_ENV === "test" && process.env.JEST_WORKER_ID && process.env.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..." + "Skip execution because JEST is running tests! To force the execution of the plugin set env var CDS_PLUGIN_UI5_ACTIVE=true...", ); - return; + skip = true; } if (process.env.CDS_PLUGIN_UI5_ACTIVE === "false") { 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; + skip = true; } -// @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]; - }; -} +// only execute the plugin if it should not be skipped +if (!skip) { + // @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! -// create a logger for the cds-plugin-ui5 -const LOG = cds.log("cds-plugin-ui5"); + // 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]; + }; + } -const findUI5Modules = require("./lib/findUI5Modules"); -const createPatchedRouter = require("./lib/createPatchedRouter"); -const applyUI5Middleware = require("./lib/applyUI5Middleware"); -const rewriteHTML = require("./lib/rewriteHTML"); + // create a logger for the cds-plugin-ui5 + const LOG = cds.log("cds-plugin-ui5"); -// 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!"); - skip = true; -} else if (process.env["dev-approuter"]) { - LOG.info("Skip execution of plugin because is has been started via dev-approuter!"); - skip = true; -} + const findUI5Modules = require("./lib/findUI5Modules"); + const createPatchedRouter = require("./lib/createPatchedRouter"); + const applyUI5Middleware = require("./lib/applyUI5Middleware"); + const rewriteHTML = require("./lib/rewriteHTML"); -// only hook into lifecycle if the plugin should not be skipped -if (!skip) { - // marker that the cds-plugin-ui5 plugin is running - // to disable the ui5-middleware-cap if used in apps - process.env["cds-plugin-ui5"] = true; - - const { dirname, join, resolve } = require("path"); - const { readFileSync, existsSync, realpathSync } = require("fs"); - const { execSync } = require("child_process"); - - const { version: cdsPluginUI5Version } = require(`${__dirname}/package.json`); - - // function to resolve a module with the given paths without throwing an error - const resolveModule = function resolveModule(moduleName, paths) { - try { - return require.resolve(moduleName, { paths }); - } catch (error) { - return null; - } - }; - - // helper to find the package.json in the current dir or upwards - const findPackageJson = function findPackageJson(dir) { - let currentDir = dir; - while (currentDir !== resolve(currentDir, "..")) { - const packageJsonPath = join(currentDir, "package.json"); - if (existsSync(packageJsonPath)) { - return packageJsonPath; + // identify whether the execution should be skipped + if (process.env["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!"); + skip = true; + } + + // only hook into lifecycle if the plugin should not be skipped + if (!skip) { + // marker that the cds-plugin-ui5 plugin is running + // to disable the ui5-middleware-cap if used in apps + process.env["cds-plugin-ui5"] = true; + + const { dirname, join, resolve } = require("path"); + const { readFileSync, existsSync, realpathSync } = require("fs"); + const { execSync } = require("child_process"); + + const { version: cdsPluginUI5Version } = require(`${__dirname}/package.json`); + + // function to resolve a module with the given paths without throwing an error + const resolveModule = function resolveModule(moduleName, paths) { + try { + return require.resolve(moduleName, { paths }); + // eslint-disable-next-line no-unused-vars + } catch (err) { + return null; } - currentDir = resolve(currentDir, ".."); - } - return undefined; - }; - - // find out the CDS-DK version to control the behavior of the plugin - const getCDSDKVersion = function getCDSDKVersion() { - let cdsDkPath = process.argv[1]; - try { - cdsDkPath = realpathSync(cdsDkPath); - } catch (err) { - // ignore - } - const cdsDkDir = dirname(cdsDkPath); - const packageJsonPath = findPackageJson(cdsDkDir); - if (packageJsonPath) { - const packageJson = JSON.parse(readFileSync(packageJsonPath, { encoding: "utf-8" })); - return packageJson.version; - } else { - const moduleName = "@sap/cds-dk"; - let resolvedPath = resolveModule(`${moduleName}/package.json`); - if (!resolvedPath) { - const globalModulesPath = execSync("npm root -g").toString().trim(); - resolvedPath = resolveModule(`${moduleName}/package.json`, [globalModulesPath]); + }; + + // helper to find the package.json in the current dir or upwards + const findPackageJson = function findPackageJson(dir) { + let currentDir = dir; + while (currentDir !== resolve(currentDir, "..")) { + const packageJsonPath = join(currentDir, "package.json"); + if (existsSync(packageJsonPath)) { + return packageJsonPath; + } + currentDir = resolve(currentDir, ".."); } - if (resolvedPath) { - const packageJson = JSON.parse(readFileSync(resolvedPath, { encoding: "utf-8" })); + return undefined; + }; + + // find out the CDS-DK version to control the behavior of the plugin + const getCDSDKVersion = function getCDSDKVersion() { + let cdsDkPath = process.argv[1]; + try { + cdsDkPath = realpathSync(cdsDkPath); + // eslint-disable-next-line no-unused-vars + } catch (err) { + // ignore + } + const cdsDkDir = dirname(cdsDkPath); + const packageJsonPath = findPackageJson(cdsDkDir); + if (packageJsonPath) { + const packageJson = JSON.parse(readFileSync(packageJsonPath, { encoding: "utf-8" })); return packageJson.version; + } else { + const moduleName = "@sap/cds-dk"; + let resolvedPath = resolveModule(`${moduleName}/package.json`); + if (!resolvedPath) { + const globalModulesPath = execSync("npm root -g").toString().trim(); + resolvedPath = resolveModule(`${moduleName}/package.json`, [globalModulesPath]); + } + if (resolvedPath) { + const packageJson = JSON.parse(readFileSync(resolvedPath, { encoding: "utf-8" })); + return packageJson.version; + } } - } - return undefined; - }; - - // get the CDS-DK version to control the behavior of the plugin - 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})`); - if (global.__cds_loaded_from?.size > 1) { - LOG.warn(" !! Multiple versions of @sap/cds loaded !!"); - global.__cds_loaded_from.forEach((cdsPath) => { - LOG.warn(` => ${cdsPath}`); - }); - } + return undefined; + }; - // promise to await the bootstrap and lock the - // served event to delay the startup a bit - let bootstrapped; - const bootstrapCompleted = new Promise((r) => { - bootstrapped = r; - }); - - // hook into the "served" event to delay the startup of the - // 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"); - 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"); - - // 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, LOG }); - const { localApps, config } = ui5Modules; - - const links = []; - - // register the UI5 modules via their own router/middlewares - for await (const ui5Module of ui5Modules) { - 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])}` : ""}`); - - // create a patched router - const router = await createPatchedRouter(); - - // apply the UI5 middlewares to the router and - // retrieve the available HTML pages - const appInfo = await applyUI5Middleware(router, { - cwd, - basePath: modulePath, - ...(config[moduleId] || {}), - LOG, + // get the CDS-DK version to control the behavior of the plugin + const cdsdkVersion = getCDSDKVersion(); + const logVersion = function () { + // logging the version of the cds-plugin-ui5 + 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 !!"); + global.__cds_loaded_from.forEach((cdsPath) => { + LOG.warn(` => ${cdsPath}`); }); + } + }; - // register the router to the specified mount path - app.use(mountPath, router); + // promise to await the bootstrap and lock the + // served event to delay the startup a bit + let bootstrapped; + const bootstrapCompleted = new Promise((r) => { + bootstrapped = r; + }); - // append the HTML pages to the links - appInfo.pages.forEach((page) => { - const prefix = mountPath !== "/" ? mountPath : ""; - links.push(`${prefix}${page}`); - }); - } + // hook into the "served" event to delay the startup of the + // 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"); + await bootstrapCompleted; + }); - // identify whether the welcome page should be rewritten - let rewrite = links.length > 0; - - // rewrite the welcome page - if (rewrite) { - // register the custom middleware (similar like in @sap/cds/server.js) - app.get("/", function appendLinksToIndex(req, res, next) { - req._cds_plugin_ui5 = true; // marker for patched router to ignore - rewriteHTML( - req, - res, - () => true, - (doc) => { - // the first