Skip to content
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: 2 additions & 1 deletion lib/lbt/bundle/Resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class BundleResolver {
* in the resource pool.
*/
const missingModules = Object.create(null);
const ignoreMissingModules = pool.getIgnoreMissingModules();
/**
* Names of modules that are included in non-decomposable bundles.
* If they occur in the missingModules, then this is not an error.
Expand Down Expand Up @@ -123,7 +124,7 @@ class BundleResolver {
done = pool.findResourceWithInfo(resourceName)
.catch( (err) => {
// if the caller provided an error message, log it
if ( msg ) {
if ( msg && !ignoreMissingModules ) {
missingModules[resourceName] ??= [];
missingModules[resourceName].push(msg);
}
Expand Down
220 changes: 184 additions & 36 deletions lib/tasks/bundlers/generateLibraryPreload.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import semver from "semver";
import {getLogger} from "@ui5/logger";
const log = getLogger("builder:tasks:bundlers:generateLibraryPreload");
import moduleBundler from "../../processors/bundlers/moduleBundler.js";
Expand Down Expand Up @@ -32,6 +33,32 @@ function getDefaultLibraryPreloadFilters(namespace, excludes) {
return filters;
}

function getExperimentalDefaultLibraryPreloadFilters(namespace, excludes) {
const filters = [
`${namespace}/library.js`,
`!${namespace}/**/*-preload.js`, // exclude all bundles
`!${namespace}/designtime/`,
`!${namespace}/**/*.designtime.js`,
`!${namespace}/**/*.support.js`
];

if (Array.isArray(excludes)) {
const allFilterExcludes = negateFilters(excludes);
// Add configured excludes at the end of filter list
allFilterExcludes.forEach((filterExclude) => {
// Allow all excludes (!) and limit re-includes (+) to the library namespace
if (filterExclude.startsWith("!") || filterExclude.startsWith(`+${namespace}/`)) {
filters.push(filterExclude);
} else {
log.warn(`Configured preload exclude contains invalid re-include: !${filterExclude.substr(1)}. ` +
`Re-includes must start with the library's namespace ${namespace}`);
}
});
}

return filters;
}

function getBundleDefinition(namespace, excludes) {
// Note: This configuration is only used when no bundle definition in ui5.yaml exists (see "skipBundles" parameter)

Expand Down Expand Up @@ -106,6 +133,69 @@ function getBundleDefinition(namespace, excludes) {
};
}

function getBundleInfoPreloadDefinition(namespace, excludes, coreVersion) {
const sections = [{
mode: "preload",
filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes),
resolve: true
},
{
mode: "bundleInfo",
name: `${namespace}/library-content.js`,
filters: getDefaultLibraryPreloadFilters(namespace, excludes),
resolve: false,
resolveConditional: false,
renderer: true
}];

if (coreVersion) {
const parsedVersion = semver.parse(coreVersion);
let targetUi5CoreVersionMajor = parsedVersion.major;

// legacy-free versions include changes of the upcoming major version
// so we should treat them the same as the next major version
if (
parsedVersion.prerelease.includes("legacy-free") ||
parsedVersion.prerelease.includes("legacy-free-SNAPSHOT") // Maven snapshot version
) {
targetUi5CoreVersionMajor += 1;
}
if (parsedVersion) {
if (targetUi5CoreVersionMajor >= 2) {
// Do not include manifest.json in UI5 2.x and higher to allow for loading it upfront for all libraries
sections.unshift({
mode: "provided",
filters: [
`${namespace}/manifest.json`,
]
});
}
}
}

return {
name: `${namespace}/library-preload.js`,
sections,
};
}

function getContentBundleDefinition(namespace, excludes) {
return {
name: `${namespace}/library-content.js`,
sections: [{
mode: "provided",
filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes),
resolve: true
}, {
mode: "preload",
filters: getDefaultLibraryPreloadFilters(namespace, excludes),
resolve: false,
resolveConditional: false,
renderer: true
}]
};
}

function getDesigntimeBundleDefinition(namespace) {
return {
name: `${namespace}/designtime/library-preload.designtime.js`,
Expand Down Expand Up @@ -258,6 +348,7 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
}
const coreVersion = taskUtil?.getProject("sap.ui.core")?.getVersion();
const allowStringBundling = taskUtil?.getProject().getSpecVersion().lt("4.0");
const createBundleInfoPreload = !!process.env.UI5_CLI_EXPERIMENTAL_BUNDLE_INFO_PRELOAD;
const execModuleBundlerIfNeeded = ({options, resources}) => {
if (skipBundles.includes(options.bundleDefinition.name)) {
log.verbose(`Skipping generation of bundle ${options.bundleDefinition.name}`);
Expand Down Expand Up @@ -390,42 +481,99 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern);
if (libraryNamespaceMatch && libraryNamespaceMatch[1]) {
const libraryNamespace = libraryNamespaceMatch[1];
const results = await Promise.all([
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
bundleOptions: {
optimize: true,
ignoreMissingModules: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: true,
ignoreMissingModules: true,
skipIfEmpty: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: false,
ignoreMissingModules: true,
skipIfEmpty: true
}
// Note: Although the bundle uses optimize=false, there is
// no moduleNameMapping needed, as support files are excluded from minification.
},
resources
})
]);
let results;
if (!createBundleInfoPreload) {
// Regular bundling
results = await Promise.all([
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
bundleOptions: {
optimize: true,
ignoreMissingModules: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: true,
ignoreMissingModules: true,
skipIfEmpty: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: false,
ignoreMissingModules: true,
skipIfEmpty: true
}
// Note: Although the bundle uses optimize=false, there is
// no moduleNameMapping needed, as support files are excluded from minification.
},
resources
})
]);
} else {
log.info(
`Using experimental bundling with bundle info preload ` +
`for library ${libraryNamespace} in project ${projectName}`);
log.info(`Detected sap.ui.core version is ${coreVersion || "unknown"}`);
// Experimental bundling with bundle info preload
results = await Promise.all([
execModuleBundlerIfNeeded({
options: {
bundleDefinition:
getBundleInfoPreloadDefinition(libraryNamespace, excludes, coreVersion),
bundleOptions: {
optimize: true,
ignoreMissingModules: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getContentBundleDefinition(libraryNamespace, excludes),
bundleOptions: {
optimize: true,
ignoreMissingModules: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: true,
ignoreMissingModules: true,
skipIfEmpty: true
}
},
resources
}),
execModuleBundlerIfNeeded({
options: {
bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
bundleOptions: {
optimize: false,
ignoreMissingModules: true,
skipIfEmpty: true
}
// Note: Although the bundle uses optimize=false, there is
// no moduleNameMapping needed, as support files are excluded from minification.
},
resources
})
]);
}
const bundles = Array.prototype.concat.apply([], results).filter(Boolean);
return Promise.all(bundles.map(({bundle, sourceMap} = {}) => {
if (bundle) {
Expand Down
9 changes: 6 additions & 3 deletions test/lib/lbt/bundle/AutoSplitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ function createMockPool(dependencies) {
name: "x.view.xml"
}, {
name: "c.properties"
}]
}],
getIgnoreMissingModules: () => false,
};
}

Expand Down Expand Up @@ -165,7 +166,8 @@ test("integration: Extreme AutoSplitter with numberOfParts 50", async (t) => {
});
return {info};
},
resources: modules.map((res) => ({name: res}))
resources: modules.map((res) => ({name: res})),
getIgnoreMissingModules: () => false,
};
const autoSplitter = new AutoSplitter(pool, new BundleResolver(pool));
const bundleDefinition = {
Expand Down Expand Up @@ -208,7 +210,8 @@ test("integration: AutoSplitter with bundleInfo", async (t) => {
const info = new ModuleInfo(name);
return {info};
},
resources: modules.map((res) => ({name: res}))
resources: modules.map((res) => ({name: res})),
getIgnoreMissingModules: () => false,
};
const autoSplitter = new AutoSplitter(pool, new BundleResolver(pool));
const bundleDefinition = {
Expand Down