|
| 1 | +import semver from "semver"; |
1 | 2 | import {getLogger} from "@ui5/logger"; |
2 | 3 | const log = getLogger("builder:tasks:bundlers:generateLibraryPreload"); |
3 | 4 | import moduleBundler from "../../processors/bundlers/moduleBundler.js"; |
@@ -32,6 +33,32 @@ function getDefaultLibraryPreloadFilters(namespace, excludes) { |
32 | 33 | return filters; |
33 | 34 | } |
34 | 35 |
|
| 36 | +function getExperimentalDefaultLibraryPreloadFilters(namespace, excludes) { |
| 37 | + const filters = [ |
| 38 | + `${namespace}/library.js`, |
| 39 | + `!${namespace}/**/*-preload.js`, // exclude all bundles |
| 40 | + `!${namespace}/designtime/`, |
| 41 | + `!${namespace}/**/*.designtime.js`, |
| 42 | + `!${namespace}/**/*.support.js` |
| 43 | + ]; |
| 44 | + |
| 45 | + if (Array.isArray(excludes)) { |
| 46 | + const allFilterExcludes = negateFilters(excludes); |
| 47 | + // Add configured excludes at the end of filter list |
| 48 | + allFilterExcludes.forEach((filterExclude) => { |
| 49 | + // Allow all excludes (!) and limit re-includes (+) to the library namespace |
| 50 | + if (filterExclude.startsWith("!") || filterExclude.startsWith(`+${namespace}/`)) { |
| 51 | + filters.push(filterExclude); |
| 52 | + } else { |
| 53 | + log.warn(`Configured preload exclude contains invalid re-include: !${filterExclude.substr(1)}. ` + |
| 54 | + `Re-includes must start with the library's namespace ${namespace}`); |
| 55 | + } |
| 56 | + }); |
| 57 | + } |
| 58 | + |
| 59 | + return filters; |
| 60 | +} |
| 61 | + |
35 | 62 | function getBundleDefinition(namespace, excludes) { |
36 | 63 | // Note: This configuration is only used when no bundle definition in ui5.yaml exists (see "skipBundles" parameter) |
37 | 64 |
|
@@ -106,6 +133,69 @@ function getBundleDefinition(namespace, excludes) { |
106 | 133 | }; |
107 | 134 | } |
108 | 135 |
|
| 136 | +function getBundleInfoPreloadDefinition(namespace, excludes, coreVersion) { |
| 137 | + const sections = [{ |
| 138 | + mode: "preload", |
| 139 | + filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes), |
| 140 | + resolve: true |
| 141 | + }, |
| 142 | + { |
| 143 | + mode: "bundleInfo", |
| 144 | + name: `${namespace}/library-content.js`, |
| 145 | + filters: getDefaultLibraryPreloadFilters(namespace, excludes), |
| 146 | + resolve: false, |
| 147 | + resolveConditional: false, |
| 148 | + renderer: true |
| 149 | + }]; |
| 150 | + |
| 151 | + if (coreVersion) { |
| 152 | + const parsedVersion = semver.parse(coreVersion); |
| 153 | + let targetUi5CoreVersionMajor = parsedVersion.major; |
| 154 | + |
| 155 | + // legacy-free versions include changes of the upcoming major version |
| 156 | + // so we should treat them the same as the next major version |
| 157 | + if ( |
| 158 | + parsedVersion.prerelease.includes("legacy-free") || |
| 159 | + parsedVersion.prerelease.includes("legacy-free-SNAPSHOT") // Maven snapshot version |
| 160 | + ) { |
| 161 | + targetUi5CoreVersionMajor += 1; |
| 162 | + } |
| 163 | + if (parsedVersion) { |
| 164 | + if (targetUi5CoreVersionMajor >= 2) { |
| 165 | + // Do not include manifest.json in UI5 2.x and higher to allow for loading it upfront for all libraries |
| 166 | + sections.unshift({ |
| 167 | + mode: "provided", |
| 168 | + filters: [ |
| 169 | + `${namespace}/manifest.json`, |
| 170 | + ] |
| 171 | + }); |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + return { |
| 177 | + name: `${namespace}/library-preload.js`, |
| 178 | + sections, |
| 179 | + }; |
| 180 | +} |
| 181 | + |
| 182 | +function getContentBundleDefinition(namespace, excludes) { |
| 183 | + return { |
| 184 | + name: `${namespace}/library-content.js`, |
| 185 | + sections: [{ |
| 186 | + mode: "provided", |
| 187 | + filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes), |
| 188 | + resolve: true |
| 189 | + }, { |
| 190 | + mode: "preload", |
| 191 | + filters: getDefaultLibraryPreloadFilters(namespace, excludes), |
| 192 | + resolve: false, |
| 193 | + resolveConditional: false, |
| 194 | + renderer: true |
| 195 | + }] |
| 196 | + }; |
| 197 | +} |
| 198 | + |
109 | 199 | function getDesigntimeBundleDefinition(namespace) { |
110 | 200 | return { |
111 | 201 | name: `${namespace}/designtime/library-preload.designtime.js`, |
@@ -258,6 +348,7 @@ export default async function({workspace, taskUtil, options: {skipBundles = [], |
258 | 348 | } |
259 | 349 | const coreVersion = taskUtil?.getProject("sap.ui.core")?.getVersion(); |
260 | 350 | const allowStringBundling = taskUtil?.getProject().getSpecVersion().lt("4.0"); |
| 351 | + const createBundleInfoPreload = !!process.env.UI5_CLI_EXPERIMENTAL_BUNDLE_INFO_PRELOAD; |
261 | 352 | const execModuleBundlerIfNeeded = ({options, resources}) => { |
262 | 353 | if (skipBundles.includes(options.bundleDefinition.name)) { |
263 | 354 | log.verbose(`Skipping generation of bundle ${options.bundleDefinition.name}`); |
@@ -390,42 +481,99 @@ export default async function({workspace, taskUtil, options: {skipBundles = [], |
390 | 481 | const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern); |
391 | 482 | if (libraryNamespaceMatch && libraryNamespaceMatch[1]) { |
392 | 483 | const libraryNamespace = libraryNamespaceMatch[1]; |
393 | | - const results = await Promise.all([ |
394 | | - execModuleBundlerIfNeeded({ |
395 | | - options: { |
396 | | - bundleDefinition: getBundleDefinition(libraryNamespace, excludes), |
397 | | - bundleOptions: { |
398 | | - optimize: true, |
399 | | - ignoreMissingModules: true |
400 | | - } |
401 | | - }, |
402 | | - resources |
403 | | - }), |
404 | | - execModuleBundlerIfNeeded({ |
405 | | - options: { |
406 | | - bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace), |
407 | | - bundleOptions: { |
408 | | - optimize: true, |
409 | | - ignoreMissingModules: true, |
410 | | - skipIfEmpty: true |
411 | | - } |
412 | | - }, |
413 | | - resources |
414 | | - }), |
415 | | - execModuleBundlerIfNeeded({ |
416 | | - options: { |
417 | | - bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace), |
418 | | - bundleOptions: { |
419 | | - optimize: false, |
420 | | - ignoreMissingModules: true, |
421 | | - skipIfEmpty: true |
422 | | - } |
423 | | - // Note: Although the bundle uses optimize=false, there is |
424 | | - // no moduleNameMapping needed, as support files are excluded from minification. |
425 | | - }, |
426 | | - resources |
427 | | - }) |
428 | | - ]); |
| 484 | + let results; |
| 485 | + if (!createBundleInfoPreload) { |
| 486 | + // Regular bundling |
| 487 | + results = await Promise.all([ |
| 488 | + execModuleBundlerIfNeeded({ |
| 489 | + options: { |
| 490 | + bundleDefinition: getBundleDefinition(libraryNamespace, excludes), |
| 491 | + bundleOptions: { |
| 492 | + optimize: true, |
| 493 | + ignoreMissingModules: true |
| 494 | + } |
| 495 | + }, |
| 496 | + resources |
| 497 | + }), |
| 498 | + execModuleBundlerIfNeeded({ |
| 499 | + options: { |
| 500 | + bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace), |
| 501 | + bundleOptions: { |
| 502 | + optimize: true, |
| 503 | + ignoreMissingModules: true, |
| 504 | + skipIfEmpty: true |
| 505 | + } |
| 506 | + }, |
| 507 | + resources |
| 508 | + }), |
| 509 | + execModuleBundlerIfNeeded({ |
| 510 | + options: { |
| 511 | + bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace), |
| 512 | + bundleOptions: { |
| 513 | + optimize: false, |
| 514 | + ignoreMissingModules: true, |
| 515 | + skipIfEmpty: true |
| 516 | + } |
| 517 | + // Note: Although the bundle uses optimize=false, there is |
| 518 | + // no moduleNameMapping needed, as support files are excluded from minification. |
| 519 | + }, |
| 520 | + resources |
| 521 | + }) |
| 522 | + ]); |
| 523 | + } else { |
| 524 | + log.info( |
| 525 | + `Using experimental bundling with bundle info preload ` + |
| 526 | + `for library ${libraryNamespace} in project ${projectName}`); |
| 527 | + log.info(`Detected sap.ui.core version is ${coreVersion || "unknown"}`); |
| 528 | + // Experimental bundling with bundle info preload |
| 529 | + results = await Promise.all([ |
| 530 | + execModuleBundlerIfNeeded({ |
| 531 | + options: { |
| 532 | + bundleDefinition: |
| 533 | + getBundleInfoPreloadDefinition(libraryNamespace, excludes, coreVersion), |
| 534 | + bundleOptions: { |
| 535 | + optimize: true, |
| 536 | + ignoreMissingModules: true |
| 537 | + } |
| 538 | + }, |
| 539 | + resources |
| 540 | + }), |
| 541 | + execModuleBundlerIfNeeded({ |
| 542 | + options: { |
| 543 | + bundleDefinition: getContentBundleDefinition(libraryNamespace, excludes), |
| 544 | + bundleOptions: { |
| 545 | + optimize: true, |
| 546 | + ignoreMissingModules: true |
| 547 | + } |
| 548 | + }, |
| 549 | + resources |
| 550 | + }), |
| 551 | + execModuleBundlerIfNeeded({ |
| 552 | + options: { |
| 553 | + bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace), |
| 554 | + bundleOptions: { |
| 555 | + optimize: true, |
| 556 | + ignoreMissingModules: true, |
| 557 | + skipIfEmpty: true |
| 558 | + } |
| 559 | + }, |
| 560 | + resources |
| 561 | + }), |
| 562 | + execModuleBundlerIfNeeded({ |
| 563 | + options: { |
| 564 | + bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace), |
| 565 | + bundleOptions: { |
| 566 | + optimize: false, |
| 567 | + ignoreMissingModules: true, |
| 568 | + skipIfEmpty: true |
| 569 | + } |
| 570 | + // Note: Although the bundle uses optimize=false, there is |
| 571 | + // no moduleNameMapping needed, as support files are excluded from minification. |
| 572 | + }, |
| 573 | + resources |
| 574 | + }) |
| 575 | + ]); |
| 576 | + } |
429 | 577 | const bundles = Array.prototype.concat.apply([], results).filter(Boolean); |
430 | 578 | return Promise.all(bundles.map(({bundle, sourceMap} = {}) => { |
431 | 579 | if (bundle) { |
|
0 commit comments