Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-tooling-modules): performance opt + persistent bundle info cache #1059

Merged
merged 1 commit into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package-lock.json
yarn.lock

# ignore ui5-tooling-modules cache
showcases/*/.ui5-tooling-modules

# wdi5 screenshots
packages/ui5-app/webapp/test/e2e/report/*

Expand Down
6 changes: 5 additions & 1 deletion packages/ui5-tooling-modules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ The following configuration options are relevant for the `task` and the `middlew
Enables debug logging (defaults to `false`), by setting value to `"verbose"` the extension will log even more detailed
 

- *skipCache*: `boolean` *experimental feature*
- *skipCache*: `boolean`
For development scenarios, the module cache can be disabled by setting this option to true. Normally, if a module changes (e.g. bundledefs), this change is detected and the bundle is recreated. This just forces the regeneration always (defaults to `false`)
 

- *persistentCache*: `boolean` *experimental feature*
With this option, the bundle information will be persistent and will be available again after the restart of the development server or for the next execution of your build task. The bundle information is stored in the working directory in the folder `.ui5-tooling-modules` folder. It's recommended to put this folder into `.gitignore` (defaults to `false`)
 

- *keepDynamicImports*: `boolean|String[]` *experimental feature*
An arrays of NPM package names for which the dynamic imports with a generic target module (e.g. `import(anyUrl)`) will not be converted into a `require` based polyfill. If the value is a boolean, it activates/deactivates the feature for all modules (by default it is active!). This experimental feature is useful in scenarios loading ES modules dynamically from generic URLs. All dynamic imports resolving a concrete module (e.g. `import("jspdf")`) will be converted into chunks and loaded with a `require` call.
 
Expand Down
35 changes: 14 additions & 21 deletions packages/ui5-tooling-modules/lib/middleware.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable no-unused-vars */
const path = require("path");
const { createReadStream } = require("fs");
const { createHash } = require("crypto");
const chokidar = require("chokidar");

/**
Expand All @@ -21,6 +20,7 @@ const chokidar = require("chokidar");
* @param {object} parameters.options Options
* @param {object} [parameters.options.configuration] Custom server middleware configuration if given in ui5.yaml
* @param {boolean} [parameters.options.configuration.skipCache] Flag whether the module cache for the bundles should be skipped
* @param {boolean} [parameters.options.configuration.persistentCache] Flag whether the module cache for the bundles should be persistent
* @param {boolean|string[]} [parameters.options.configuration.keepDynamicImports] List of NPM packages for which the dynamic imports should be kept or boolean (defaults to true)
* @param {boolean|string} [parameters.options.configuration.chunksPath] the relative path for the chunks to be stored (defaults to "chunks", if value is true, chunks are put into the closest modules folder)
* @returns {Function} Middleware function to use
Expand All @@ -46,6 +46,7 @@ module.exports = async function ({ log, resources, options, middlewareUtil }) {
const config = Object.assign(
{
debug: false,
persistentCache: true,
skipTransform: false,
watch: true,
},
Expand Down Expand Up @@ -74,39 +75,31 @@ module.exports = async function ({ log, resources, options, middlewareUtil }) {
// logic which bundles and watches the modules coming from the
// node_modules or dependencies via NPM package names
const requestedModules = new Set();
let whenBundled, bundleWatcher, scanTime, bundleTime, bundleHash;
let whenBundled, bundleWatcher, scanTime, bundleTime;
const bundleAndWatch = async ({ moduleName, force }) => {
if (moduleName && !requestedModules.has(moduleName)) {
requestedModules.add(moduleName);
}
if (force || !whenBundled) {
// first, we need to scan for all unique dependencies
scanTime = Date.now();
const oldWhenBundled = whenBundled;
whenBundled = scan(depReaderCollection, config, { cwd, depPaths })
.then(({ uniqueModules }) => {
// second, we trigger the bundling of the unique dependencies
debug && log.info(`Scanning took ${Date.now() - scanTime} millis`);
bundleTime = Date.now();
const modules = Array.from(uniqueModules);
const hash = createHash("md5").update(modules.sort().join(",")).digest("hex");
if (bundleHash === hash) {
debug && log.info(`BundleInfo is up-to-date! Skipping bundle generation...`);
return oldWhenBundled;
} else {
bundleHash = hash;
// TODO: check whether we should really include the requested modules into the bundle
// because this could also be a negative side-effect when running the build task
// which wouldn't include the requested modules into the build - but in this case
// we need it since new modules are added dynamically during development
Array.from(requestedModules)
.filter((mod) => !uniqueModules.has(mod))
.forEach((mod) => {
log.warn(`Including module "${mod}" to bundle which has been requested dynamically! This module may not be packaged during the build!`);
modules.push(mod);
});
return getBundleInfo(modules, config, { cwd, projectNamespace, projectType, depPaths, isMiddleware: true });
}
// TODO: check whether we should really include the requested modules into the bundle
// because this could also be a negative side-effect when running the build task
// which wouldn't include the requested modules into the build - but in this case
// we need it since new modules are added dynamically during development
Array.from(requestedModules)
.filter((mod) => !uniqueModules.has(mod))
.forEach((mod) => {
log.warn(`Including module "${mod}" to bundle which has been requested dynamically! This module may not be packaged during the build!`);
modules.push(mod);
});
return getBundleInfo(modules, config, { cwd, projectNamespace, projectType, depPaths, isMiddleware: true });
})
.then((bundleInfo) => {
// finally, we watch the entries of the bundle
Expand Down
Loading
Loading