From 00ee973fb63e8e307214e7567a6d085377145418 Mon Sep 17 00:00:00 2001 From: Frank Weigel Date: Mon, 18 Jun 2018 11:29:19 +0200 Subject: [PATCH] [FEATURE] Build DepCache files (*-h2-preload.js) for HTTP/2 support - implement a new bundle section type 'depcache' in the lbt builder - ignore pseudo-dependencies 'require', 'module' and 'exports' in the dependency analyzer (so that they don't appear in the dependency cache) - add configuration in the library and component preload bundlers to create bundles with the new section types in parallel to the existing preload bundles (using an '-h2-preload' suffix) - configure fixture applications g,h,i,j and fixture libraries e,h,i,j to use evo bundle format by adding a dependency to the sap.ui.core substitute fixture (sap.ui.core-evo) - add the newly introduced h2-preload bundles to the set of expected files in all fixtures that are used with one of the module bundling tasks --- lib/lbt/bundle/Builder.js | 54 +++++++ lib/lbt/bundle/BundleDefinition.js | 6 + .../bundlers/generateComponentPreload.js | 66 ++++++-- lib/tasks/bundlers/generateLibraryPreload.js | 109 ++++++++----- .../cachebuster/Component-h2-preload.js | 9 ++ .../cachebuster/Component-preload.js | 6 +- .../cachebuster/sap-ui-cachebuster-info.json | 1 + .../dest/Component-h2-preload.js | 7 + .../application.g/dest/Component-preload.js | 6 +- .../subcomponentA/Component-h2-preload.js | 7 + .../dest/subcomponentA/Component-preload.js | 6 +- .../subcomponentB/Component-h2-preload.js | 7 + .../dest/subcomponentB/Component-preload.js | 6 +- .../dest/sectionsA/customBundle.js | 6 +- .../dest/sectionsB/customBundle.js | 6 +- .../dest/Component-h2-preload.js | 7 + .../application.i/dest/Component-preload.js | 6 +- .../dest/Component-h2-preload.js | 7 + .../application.j/dest/Component-preload.js | 6 +- .../resources/library/d/library-h2-preload.js | 3 + .../h/components/Component-h2-preload.js | 4 + .../subcomponent1/Component-h2-preload.js | 4 + .../subcomponent2/Component-h2-preload.js | 4 + .../subcomponent3/Component-h2-preload.js | 4 + .../dest/resources/library/i/library.js | 6 +- .../sap/ui/core/library-h2-preload.js | 3 + .../library.i/main/src/library/i/library.js | 6 +- test/lib/builder/builder.js | 153 ++++++------------ .../tasks/bundlers/generateLibraryPreload.js | 4 +- test/lib/tasks/generateCachebusterInfo.js | 47 +++++- 30 files changed, 365 insertions(+), 201 deletions(-) create mode 100644 test/expected/build/application.g/cachebuster/Component-h2-preload.js create mode 100644 test/expected/build/application.g/dest/Component-h2-preload.js create mode 100644 test/expected/build/application.g/dest/subcomponentA/Component-h2-preload.js create mode 100644 test/expected/build/application.g/dest/subcomponentB/Component-h2-preload.js create mode 100644 test/expected/build/application.i/dest/Component-h2-preload.js create mode 100644 test/expected/build/application.j/dest/Component-h2-preload.js create mode 100644 test/expected/build/library.d/preload/resources/library/d/library-h2-preload.js create mode 100644 test/expected/build/library.h/dest/resources/library/h/components/Component-h2-preload.js create mode 100644 test/expected/build/library.h/dest/resources/library/h/components/subcomponent1/Component-h2-preload.js create mode 100644 test/expected/build/library.h/dest/resources/library/h/components/subcomponent2/Component-h2-preload.js create mode 100644 test/expected/build/library.h/dest/resources/library/h/components/subcomponent3/Component-h2-preload.js create mode 100644 test/expected/build/sap.ui.core/preload/resources/sap/ui/core/library-h2-preload.js diff --git a/lib/lbt/bundle/Builder.js b/lib/lbt/bundle/Builder.js index 7a010c2b9..88e24dc03 100644 --- a/lib/lbt/bundle/Builder.js +++ b/lib/lbt/bundle/Builder.js @@ -52,6 +52,14 @@ const UI5BundleFormat = { outW.writeln(`}});`); }, + beforeH2Preloads(outW) { + outW.writeln(`"unsupported"; /* Bundle format 'h2' not supported (requires ui5loader)`); + }, + + afterH2Preloads(outW) { + outW.writeln(`*/`); + }, + requireSync(outW, moduleName) { outW.writeln(`sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");`); }, @@ -78,6 +86,14 @@ const EVOBundleFormat = { outW.writeln(`);`); }, + beforeH2Preloads(outW) { + outW.writeln(`sap.ui.loader.config({depCacheUI5:{`); + }, + + afterH2Preloads(outW) { + outW.writeln(`}});`); + }, + requireSync(outW, moduleName) { outW.writeln(`sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");`); }, @@ -206,6 +222,8 @@ class BundleBuilder { return this.writeRaw(section); case SectionType.Preload: return this.writePreloadFunction(section); + case SectionType.DepCache: + return this.writeDepCache(section); case SectionType.Require: return this.writeRequires(section); default: @@ -449,6 +467,42 @@ class BundleBuilder { }); } + async writeDepCache(section) { + const outW = this.outW; + + outW.ensureNewLine(); + + const sequence = section.modules.slice().sort(); + + if ( sequence.length > 0 ) { + this.targetBundleFormat.beforeH2Preloads(outW, section); + let i = 0; + for (const module of sequence) { + const resource = await this.pool.findResourceWithInfo(module); + if ( resource != null ) { + const deps = resource.info.dependencies.filter( (dep) => + !resource.info.isConditionalDependency(dep) && !resource.info.isImplicitDependency(dep) ); + if ( deps.length > 0 ) { + if ( i > 0 ) { + outW.writeln(","); + } + outW.write(`"${module}": [${deps.map((dep) => "\"" + dep + "\"").join(",")}]`); + i++; + } else { + log.verbose(" skipped %s, no dependencies", module); + } + } else { + log.error(" couldn't find %s", module); + } + } + + if ( i > 0 ) { + outW.writeln(); + } + this.targetBundleFormat.afterH2Preloads(outW, section); + } + } + writeRequires(section) { this.outW.ensureNewLine(); section.modules.forEach( (module) => { diff --git a/lib/lbt/bundle/BundleDefinition.js b/lib/lbt/bundle/BundleDefinition.js index 1e7909dd9..260a2d214 100644 --- a/lib/lbt/bundle/BundleDefinition.js +++ b/lib/lbt/bundle/BundleDefinition.js @@ -17,6 +17,12 @@ const SectionType = { */ Preload: "preload", + /** + * Only the dependencies of the modules are stored as 'depCache' configuration. + * Requires UI5 Evolution runtime. + */ + DepCache: "depcache", + /** * For each module, a jQuery.sap.require call will be created. * Usually used as the last section in a merged module to enforce loading and diff --git a/lib/tasks/bundlers/generateComponentPreload.js b/lib/tasks/bundlers/generateComponentPreload.js index 226a59fcc..bdb082331 100644 --- a/lib/tasks/bundlers/generateComponentPreload.js +++ b/lib/tasks/bundlers/generateComponentPreload.js @@ -59,27 +59,59 @@ module.exports = function({workspace, dependencies, options}) { } }); - return moduleBundler({ - resources, - options: { - bundleDefinition: { - name: `${namespace}/Component-preload.js`, - defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], - sections: [ - { - mode: "preload", - filters: filters, - resolve: false, - resolveConditional: false, - renderer: false - } - ] + return Promise.all([ + moduleBundler({ + resources, + options: { + bundleDefinition: { + name: `${namespace}/Component-preload.js`, + defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], + sections: [ + { + mode: "preload", + filters: filters, + resolve: false, + resolveConditional: false, + renderer: false + } + ] + } } - } - }); + }), + moduleBundler({ + resources, + options: { + bundleDefinition: { + name: `${namespace}/Component-h2-preload.js`, + defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], + sections: [ + { + mode: "preload", + filters: [ + `${namespace}/library.js`, + `${namespace}/manifest.json` + ], + resolve: false, + resolveConditional: false, + renderer: false + }, + { + mode: "depcache", + filters: filters, + resolve: false, + resolveConditional: false, + renderer: false + } + ] + } + } + }) + ]); })); }) .then((processedResources) => { + return Array.prototype.concat.apply([], processedResources); + }).then((processedResources) => { return Promise.all(processedResources.map((resource) => { return workspace.write(resource[0]); })); diff --git a/lib/tasks/bundlers/generateLibraryPreload.js b/lib/tasks/bundlers/generateLibraryPreload.js index e940617a1..a24d84724 100644 --- a/lib/tasks/bundlers/generateLibraryPreload.js +++ b/lib/tasks/bundlers/generateLibraryPreload.js @@ -2,11 +2,13 @@ const log = require("@ui5/logger").getLogger("builder:tasks:bundlers:generateLib const moduleBundler = require("../../processors/bundlers/moduleBundler"); const ReaderCollectionPrioritized = require("@ui5/fs").ReaderCollectionPrioritized; -function getBundleDefinition(namespace) { +function getBundleDefinition(namespace, h2) { + const h2infix = h2 ? "h2-" : ""; + let bundleDef; // TODO: move to config of actual core project if (namespace === "sap/ui/core") { - return { - name: `${namespace}/library-preload.js`, + bundleDef = { + name: `${namespace}/library-${h2infix}preload.js`, defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], sections: [ { @@ -19,7 +21,7 @@ function getBundleDefinition(namespace) { resolve: true }, { - mode: "preload", + mode: h2 ? "depcache" : "preload", filters: [ `${namespace}/`, `!${namespace}/.library`, @@ -62,28 +64,40 @@ function getBundleDefinition(namespace) { } ] }; + } else { + bundleDef = { + name: `${namespace}/library-${h2infix}preload.js`, + defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], + sections: [ + { + mode: h2 ? "depcache" : "preload", + filters: [ + `${namespace}/`, + `!${namespace}/.library`, + `!${namespace}/themes/`, + `!${namespace}/messagebundle*` + ], + resolve: false, + resolveConditional: false, + renderer: true + } + ] + }; } - return { - name: `${namespace}/library-preload.js`, - defaultFileTypes: [".js", ".fragment.xml", ".view.xml", ".properties", ".json"], - sections: [ - { - mode: "preload", - filters: [ - `${namespace}/`, - `!${namespace}/.library`, - `!${namespace}/designtime/`, - `!${namespace}/**/*.designtime.js`, - `!${namespace}/**/*.support.js`, - `!${namespace}/themes/`, - `!${namespace}/messagebundle*` - ], - resolve: false, - resolveConditional: false, - renderer: true - } - ] - }; + + if ( h2 ) { + bundleDef.sections.unshift({ + mode: "preload", + filters: [ + `${namespace}/library.js`, + `${namespace}/manifest.json` + ], + resolve: false, + resolveConditional: false, + renderer: false + }); + } + return bundleDef; } function getModuleBundlerOptions(config) { @@ -249,21 +263,38 @@ module.exports = function({workspace, dependencies, options}) { const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern); if (libraryNamespaceMatch && libraryNamespaceMatch[1]) { const libraryNamespace = libraryNamespaceMatch[1]; - return moduleBundler({ - options: { - bundleDefinition: getBundleDefinition(libraryNamespace), - bundleOptions: { - optimize: true, - usePredefineCalls: true + return Promise.all([ + moduleBundler({ + options: { + bundleDefinition: getBundleDefinition(libraryNamespace), + bundleOptions: { + optimize: true, + usePredefineCalls: true + } + }, + resources + }).then(([bundle]) => { + if (bundle) { + // console.log(`${libraryNamespace}/library-preload.js bundle created`); + return workspace.write(bundle); + } + }), + moduleBundler({ + options: { + bundleDefinition: getBundleDefinition(libraryNamespace, /* h2 */ true), + bundleOptions: { + optimize: true, + usePredefineCalls: true + } + }, + resources + }).then(([bundle]) => { + if (bundle) { + // console.log(`${libraryNamespace}/library-h2-preload.js bundle created`); + return workspace.write(bundle); } - }, - resources - }).then(([bundle]) => { - if (bundle) { - // console.log(`${libraryNamespace}/library-preload.js bundle created`); - return workspace.write(bundle); - } - }); + }) + ]); } else { log.verbose(`Could not determine library namespace from file "${libraryIndicatorPath}" for project ${options.projectName}. Skipping library preload bundling.`); return Promise.resolve(); diff --git a/test/expected/build/application.g/cachebuster/Component-h2-preload.js b/test/expected/build/application.g/cachebuster/Component-h2-preload.js new file mode 100644 index 000000000..bb796ab06 --- /dev/null +++ b/test/expected/build/application.g/cachebuster/Component-h2-preload.js @@ -0,0 +1,9 @@ +//@ui5-bundle application/g/Component-h2-preload.js +sap.ui.require.preload({ + "application/g/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g","type":"application","applicationVersion":{"version":"1.0.0"},"embeds":["embedded"],"title":"{{title}}"},"customCopyrightString":"Some fancy copyright"}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/g/Component.js": ["sap/ui/core/UIComponent.js"], +"application/g/subcomponentA/Component.js": ["sap/ui/core/UIComponent.js"], +"application/g/subcomponentB/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/application.g/cachebuster/Component-preload.js b/test/expected/build/application.g/cachebuster/Component-preload.js index 504dbcbe0..6bd4694ad 100644 --- a/test/expected/build/application.g/cachebuster/Component-preload.js +++ b/test/expected/build/application.g/cachebuster/Component-preload.js @@ -1,7 +1,5 @@ //@ui5-bundle application/g/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/g/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.Component",{metadata:{manifest:"json"}})}); }, "application/g/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g","type":"application","applicationVersion":{"version":"1.0.0"},"embeds":["embedded"],"title":"{{title}}"},"customCopyrightString":"Some fancy copyright"}', @@ -11,4 +9,4 @@ jQuery.sap.registerPreloadedModules({ "application/g/subcomponentB/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.subcomponentB.Component",{metadata:{manifest:"json"}})}); }, "application/g/subcomponentB/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentB","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}' -}}); +}); diff --git a/test/expected/build/application.g/cachebuster/sap-ui-cachebuster-info.json b/test/expected/build/application.g/cachebuster/sap-ui-cachebuster-info.json index b7b2814c7..9f401e31d 100644 --- a/test/expected/build/application.g/cachebuster/sap-ui-cachebuster-info.json +++ b/test/expected/build/application.g/cachebuster/sap-ui-cachebuster-info.json @@ -5,6 +5,7 @@ "subcomponentB/Component.js": 1540570258000, "subcomponentA/Component.js": 1540570258000, "Component.js": 1540570258000, + "Component-h2-preload.js": 1554823987259, "Component-preload.js": 1554823987259, "subcomponentB/Component-dbg.js": 1540570258000, "subcomponentA/Component-dbg.js": 1540570258000, diff --git a/test/expected/build/application.g/dest/Component-h2-preload.js b/test/expected/build/application.g/dest/Component-h2-preload.js new file mode 100644 index 000000000..6cdb95289 --- /dev/null +++ b/test/expected/build/application.g/dest/Component-h2-preload.js @@ -0,0 +1,7 @@ +//@ui5-bundle application/g/Component-h2-preload.js +sap.ui.require.preload({ + "application/g/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g","type":"application","applicationVersion":{"version":"1.0.0"},"embeds":["embedded"],"title":"{{title}}"},"customCopyrightString":"Some fancy copyright"}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/g/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/application.g/dest/Component-preload.js b/test/expected/build/application.g/dest/Component-preload.js index bb4d7436f..6d6ba91c1 100644 --- a/test/expected/build/application.g/dest/Component-preload.js +++ b/test/expected/build/application.g/dest/Component-preload.js @@ -1,8 +1,6 @@ //@ui5-bundle application/g/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/g/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.Component",{metadata:{manifest:"json"}})}); }, "application/g/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g","type":"application","applicationVersion":{"version":"1.0.0"},"embeds":["embedded"],"title":"{{title}}"},"customCopyrightString":"Some fancy copyright"}' -}}); +}); diff --git a/test/expected/build/application.g/dest/subcomponentA/Component-h2-preload.js b/test/expected/build/application.g/dest/subcomponentA/Component-h2-preload.js new file mode 100644 index 000000000..ba65e3d0f --- /dev/null +++ b/test/expected/build/application.g/dest/subcomponentA/Component-h2-preload.js @@ -0,0 +1,7 @@ +//@ui5-bundle application/g/subcomponentA/Component-h2-preload.js +sap.ui.require.preload({ + "application/g/subcomponentA/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentA","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/g/subcomponentA/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/application.g/dest/subcomponentA/Component-preload.js b/test/expected/build/application.g/dest/subcomponentA/Component-preload.js index 4c103c261..4b73c3d3b 100644 --- a/test/expected/build/application.g/dest/subcomponentA/Component-preload.js +++ b/test/expected/build/application.g/dest/subcomponentA/Component-preload.js @@ -1,8 +1,6 @@ //@ui5-bundle application/g/subcomponentA/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/g/subcomponentA/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.subcomponentA.Component",{metadata:{manifest:"json"}})}); }, "application/g/subcomponentA/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentA","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}' -}}); +}); diff --git a/test/expected/build/application.g/dest/subcomponentB/Component-h2-preload.js b/test/expected/build/application.g/dest/subcomponentB/Component-h2-preload.js new file mode 100644 index 000000000..8a25d1286 --- /dev/null +++ b/test/expected/build/application.g/dest/subcomponentB/Component-h2-preload.js @@ -0,0 +1,7 @@ +//@ui5-bundle application/g/subcomponentB/Component-h2-preload.js +sap.ui.require.preload({ + "application/g/subcomponentB/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentB","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/g/subcomponentB/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/application.g/dest/subcomponentB/Component-preload.js b/test/expected/build/application.g/dest/subcomponentB/Component-preload.js index 14667fe94..eae25681a 100644 --- a/test/expected/build/application.g/dest/subcomponentB/Component-preload.js +++ b/test/expected/build/application.g/dest/subcomponentB/Component-preload.js @@ -1,8 +1,6 @@ //@ui5-bundle application/g/subcomponentB/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/g/subcomponentB/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.g.subcomponentB.Component",{metadata:{manifest:"json"}})}); }, "application/g/subcomponentB/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.g.subcomponentB","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"}}' -}}); +}); diff --git a/test/expected/build/application.h/dest/sectionsA/customBundle.js b/test/expected/build/application.h/dest/sectionsA/customBundle.js index 4acc4444d..eec7db48d 100644 --- a/test/expected/build/application.h/dest/sectionsA/customBundle.js +++ b/test/expected/build/application.h/dest/sectionsA/customBundle.js @@ -1,9 +1,7 @@ //@ui5-bundle application/h/sectionsA/customBundle.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/h/sectionsA/section1.js":function(){sap.ui.define(["sap/m/Button"],function(n){console.log("Section 1 included")}); }, "application/h/sectionsA/section3.js":function(){sap.ui.define(["sap/m/Button"],function(n){console.log("Section 3 included")}); } -}}); +}); diff --git a/test/expected/build/application.h/dest/sectionsB/customBundle.js b/test/expected/build/application.h/dest/sectionsB/customBundle.js index 0e66f011b..3d4f55316 100644 --- a/test/expected/build/application.h/dest/sectionsB/customBundle.js +++ b/test/expected/build/application.h/dest/sectionsB/customBundle.js @@ -1,11 +1,9 @@ //@ui5-bundle application/h/sectionsB/customBundle.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/h/sectionsB/section1.js":function(){sap.ui.define(["sap/m/Button"],function(n){console.log("Section 1 included")}); }, "application/h/sectionsB/section2.js":function(){sap.ui.define(["sap/m/Button"],function(n){console.log("Section 2 included")}); }, "application/h/sectionsB/section3.js":function(){sap.ui.define(["sap/m/Button"],function(n){console.log("Section 3 included")}); } -}}); +}); diff --git a/test/expected/build/application.i/dest/Component-h2-preload.js b/test/expected/build/application.i/dest/Component-h2-preload.js new file mode 100644 index 000000000..5a46a45de --- /dev/null +++ b/test/expected/build/application.i/dest/Component-h2-preload.js @@ -0,0 +1,7 @@ +//@ui5-bundle application/i/Component-h2-preload.js +sap.ui.require.preload({ + "application/i/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.i","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"},"sap.ui5":{"dependencies":{"libs":{"sap.ui.layout":{},"sap.ui.core":{},"sap.m":{},"sap.ui.fl":{}}}}}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/i/Component.js": ["sap/ui/core/UIComponent.js","sap/ui/layout/library.js","sap/ui/core/library.js","sap/m/library.js","sap/ui/fl/library.js"] +}}); diff --git a/test/expected/build/application.i/dest/Component-preload.js b/test/expected/build/application.i/dest/Component-preload.js index 0c2c24167..af9fb92f0 100644 --- a/test/expected/build/application.i/dest/Component-preload.js +++ b/test/expected/build/application.i/dest/Component-preload.js @@ -1,7 +1,5 @@ //@ui5-bundle application/i/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/i/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.i.Component",{metadata:{manifest:"json"}})}); }, "application/i/changes/changes-bundle.json":'[{"fileName":"id_456_addField","fileType":"change","changeType":"hideControl","component":"application.i.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.i.Component/changes","creation":"2023-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}},{"fileName":"id_123_addField","fileType":"change","changeType":"hideControl","component":"application.i.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.i.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}}]', @@ -9,4 +7,4 @@ jQuery.sap.registerPreloadedModules({ }, "application/i/changes/fragments/MyFragment.fragment.xml":'', "application/i/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.i","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"},"sap.ui5":{"dependencies":{"libs":{"sap.ui.layout":{},"sap.ui.core":{},"sap.m":{},"sap.ui.fl":{}}}}}' -}}); +}); diff --git a/test/expected/build/application.j/dest/Component-h2-preload.js b/test/expected/build/application.j/dest/Component-h2-preload.js new file mode 100644 index 000000000..6b87cf6ae --- /dev/null +++ b/test/expected/build/application.j/dest/Component-h2-preload.js @@ -0,0 +1,7 @@ +//@ui5-bundle application/j/Component-h2-preload.js +sap.ui.require.preload({ + "application/j/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.j","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"},"sap.ui5":{"dependencies":{"minUI5Version":"1.73.2","libs":{"sap.ui.layout":{},"sap.ui.core":{},"sap.m":{},"sap.ui.fl":{"lazy":false}}}}}' +}); +sap.ui.loader.config({depCacheUI5:{ +"application/j/Component.js": ["sap/ui/core/UIComponent.js","sap/ui/layout/library.js","sap/ui/core/library.js","sap/m/library.js","sap/ui/fl/library.js"] +}}); diff --git a/test/expected/build/application.j/dest/Component-preload.js b/test/expected/build/application.j/dest/Component-preload.js index 4e58fdf09..4d345b369 100644 --- a/test/expected/build/application.j/dest/Component-preload.js +++ b/test/expected/build/application.j/dest/Component-preload.js @@ -1,7 +1,5 @@ //@ui5-bundle application/j/Component-preload.js -jQuery.sap.registerPreloadedModules({ -"version":"2.0", -"modules":{ +sap.ui.require.preload({ "application/j/Component.js":function(){sap.ui.define(["sap/ui/core/UIComponent"],function(n){"use strict";return n.extend("application.j.Component",{metadata:{manifest:"json"}})}); }, "application/j/changes/coding/MyExtension.js":function(){sap.ui.define([],function(){return{}}); @@ -9,4 +7,4 @@ jQuery.sap.registerPreloadedModules({ "application/j/changes/flexibility-bundle.json":'{"changes":[{"fileName":"id_456_addField","fileType":"change","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2023-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}},{"fileName":"id_123_addField","fileType":"change","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}}],"compVariants":[{"fileName":"id_111_compVariants","fileType":"variant","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"},"appDescriptorChange":false}],"variants":[{"fileName":"id_111_test","fileType":"ctrl_variant","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}}],"variantChanges":[{"fileName":"id_111_test","fileType":"ctrl_variant_change","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}}],"variantDependentControlChanges":[{"fileName":"id_111_variantDependentControlChange","fileType":"change","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"},"variantReference":"someting here"}],"variantManagementChanges":[{"fileName":"id_111_test","fileType":"ctrl_variant_management_change","changeType":"hideControl","component":"application.j.Component","content":{},"selector":{"id":"control1"},"layer":"VENDOR","texts":{},"namespace":"apps/application.j.Component/changes","creation":"2025-10-30T13:52:40.4754350Z","originalLanguage":"","conditions":{},"support":{"generator":"did it","user":"Max Mustermann"}}]}', "application/j/changes/fragments/MyFragment.fragment.xml":'', "application/j/manifest.json":'{"_version":"1.1.0","sap.app":{"_version":"1.1.0","id":"application.j","type":"application","applicationVersion":{"version":"1.2.2"},"embeds":["embedded"],"title":"{{title}}"},"sap.ui5":{"dependencies":{"minUI5Version":"1.73.2","libs":{"sap.ui.layout":{},"sap.ui.core":{},"sap.m":{},"sap.ui.fl":{"lazy":false}}}}}' -}}); +}); diff --git a/test/expected/build/library.d/preload/resources/library/d/library-h2-preload.js b/test/expected/build/library.d/preload/resources/library/d/library-h2-preload.js new file mode 100644 index 000000000..2e4921411 --- /dev/null +++ b/test/expected/build/library.d/preload/resources/library/d/library-h2-preload.js @@ -0,0 +1,3 @@ +//@ui5-bundle library/d/library-h2-preload.js +"unsupported"; /* Bundle format 'h2' not supported (requires ui5loader) +*/ diff --git a/test/expected/build/library.h/dest/resources/library/h/components/Component-h2-preload.js b/test/expected/build/library.h/dest/resources/library/h/components/Component-h2-preload.js new file mode 100644 index 000000000..148630f8e --- /dev/null +++ b/test/expected/build/library.h/dest/resources/library/h/components/Component-h2-preload.js @@ -0,0 +1,4 @@ +//@ui5-bundle library/h/components/Component-h2-preload.js +sap.ui.loader.config({depCacheUI5:{ +"library/h/components/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/library.h/dest/resources/library/h/components/subcomponent1/Component-h2-preload.js b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent1/Component-h2-preload.js new file mode 100644 index 000000000..92e779c82 --- /dev/null +++ b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent1/Component-h2-preload.js @@ -0,0 +1,4 @@ +//@ui5-bundle library/h/components/subcomponent1/Component-h2-preload.js +sap.ui.loader.config({depCacheUI5:{ +"library/h/components/subcomponent1/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/library.h/dest/resources/library/h/components/subcomponent2/Component-h2-preload.js b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent2/Component-h2-preload.js new file mode 100644 index 000000000..dded428bb --- /dev/null +++ b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent2/Component-h2-preload.js @@ -0,0 +1,4 @@ +//@ui5-bundle library/h/components/subcomponent2/Component-h2-preload.js +sap.ui.loader.config({depCacheUI5:{ +"library/h/components/subcomponent2/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/library.h/dest/resources/library/h/components/subcomponent3/Component-h2-preload.js b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent3/Component-h2-preload.js new file mode 100644 index 000000000..e1f2929ab --- /dev/null +++ b/test/expected/build/library.h/dest/resources/library/h/components/subcomponent3/Component-h2-preload.js @@ -0,0 +1,4 @@ +//@ui5-bundle library/h/components/subcomponent3/Component-h2-preload.js +sap.ui.loader.config({depCacheUI5:{ +"library/h/components/subcomponent3/Component.js": ["sap/ui/core/UIComponent.js"] +}}); diff --git a/test/expected/build/library.i/dest/resources/library/i/library.js b/test/expected/build/library.i/dest/resources/library/i/library.js index 48a19acb7..e2764c75a 100644 --- a/test/expected/build/library.i/dest/resources/library/i/library.js +++ b/test/expected/build/library.i/dest/resources/library/i/library.js @@ -2,7 +2,7 @@ * Some fancy copyright */ sap.ui.define([ - 'sap/ui/core/Core', + 'sap/ui/core/Core' ], function(Core) { "use strict"; @@ -13,10 +13,10 @@ sap.ui.define([ dependencies : ["sap.ui.core"], types: [ "library.i.ButtonType", - "library.i.DialogType", + "library.i.DialogType" ], interfaces: [ - "library.i.IContent", + "library.i.IContent" ], controls: [ "library.i.Button", diff --git a/test/expected/build/sap.ui.core/preload/resources/sap/ui/core/library-h2-preload.js b/test/expected/build/sap.ui.core/preload/resources/sap/ui/core/library-h2-preload.js new file mode 100644 index 000000000..ea8c6a90c --- /dev/null +++ b/test/expected/build/sap.ui.core/preload/resources/sap/ui/core/library-h2-preload.js @@ -0,0 +1,3 @@ +//@ui5-bundle sap/ui/core/library-h2-preload.js +"unsupported"; /* Bundle format 'h2' not supported (requires ui5loader) +*/ diff --git a/test/fixtures/library.i/main/src/library/i/library.js b/test/fixtures/library.i/main/src/library/i/library.js index e1c0e2cb0..f70247c43 100644 --- a/test/fixtures/library.i/main/src/library/i/library.js +++ b/test/fixtures/library.i/main/src/library/i/library.js @@ -2,7 +2,7 @@ * ${copyright} */ sap.ui.define([ - 'sap/ui/core/Core', + 'sap/ui/core/Core' ], function(Core) { "use strict"; @@ -13,10 +13,10 @@ sap.ui.define([ dependencies : ["sap.ui.core"], types: [ "library.i.ButtonType", - "library.i.DialogType", + "library.i.DialogType" ], interfaces: [ - "library.i.IContent", + "library.i.IContent" ], controls: [ "library.i.Button", diff --git a/test/lib/builder/builder.js b/test/lib/builder/builder.js index 8b1bcf5d4..c4bd73b2c 100644 --- a/test/lib/builder/builder.js +++ b/test/lib/builder/builder.js @@ -569,35 +569,37 @@ test.serial("Cleanup", async (t) => { }); +const coreLibraryTree = { + "id": "sap.ui.core-evo", + "version": "1.0.0", + "path": libraryCore, + "dependencies": [], + "_level": 0, + "specVersion": "0.1", + "type": "library", + "metadata": { + "name": "sap.ui.core", + "namespace": "sap/ui/core", + "copyright": "Some fancy copyright" + }, + "resources": { + "configuration": { + "paths": { + "src": "main/src" + } + }, + "pathMappings": { + "/resources/": "main/src" + } + } +}; + const libraryDTree = { "id": "library.d", "version": "1.0.0", "path": libraryDPath, "dependencies": [ - { - "id": "sap.ui.core-evo", - "version": "1.0.0", - "path": libraryCore, - "dependencies": [], - "_level": 1, - "specVersion": "0.1", - "type": "library", - "metadata": { - "name": "sap.ui.core", - "namespace": "sap/ui/core", - "copyright": "Some fancy copyright" - }, - "resources": { - "configuration": { - "paths": { - "src": "main/src" - } - }, - "pathMappings": { - "/resources/": "main/src" - } - } - } + cloneProjectTree(coreLibraryTree) ], "_level": 0, "specVersion": "0.1", @@ -762,7 +764,9 @@ const applicationGTree = { "namespace": "application/g", "copyright": "Some fancy copyright" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -797,7 +801,9 @@ const applicationGTreeWithExcludes = { "namespace": "application/g", "copyright": "Some fancy copyright" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -833,7 +839,9 @@ const applicationGTreeComponentPreloadPaths = { "namespace": "application/g", "copyright": "Some fancy copyright" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -865,7 +873,9 @@ const applicationHTree = { "name": "application.h", "namespace": "application/h" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -926,7 +936,9 @@ const applicationITree = { "name": "application.i", "namespace": "application/i" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -954,7 +966,9 @@ const applicationJTree = { "name": "application.j", "namespace": "application/j" }, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "resources": { "configuration": { "paths": { @@ -976,30 +990,7 @@ const libraryETree = { "version": "1.0.0", "path": libraryEPath, "dependencies": [ - { - "id": "sap.ui.core-evo", - "version": "1.0.0", - "path": libraryCore, - "dependencies": [], - "_level": 1, - "specVersion": "0.1", - "type": "library", - "metadata": { - "name": "sap.ui.core", - "namespace": "sap/ui/core", - "copyright": "Some fancy copyright" - }, - "resources": { - "configuration": { - "paths": { - "src": "main/src" - } - }, - "pathMappings": { - "/resources/": "main/src" - } - } - } + cloneProjectTree(coreLibraryTree) ], "_level": 0, "specVersion": "0.1", @@ -1029,30 +1020,7 @@ const libraryHTree = { "version": "1.0.0", "path": libraryHPath, "dependencies": [ - { - "id": "sap.ui.core-evo", - "version": "1.0.0", - "path": libraryCore, - "dependencies": [], - "_level": 1, - "specVersion": "0.1", - "type": "library", - "metadata": { - "name": "sap.ui.core", - "namespace": "sap/ui/core", - "copyright": "Some fancy copyright" - }, - "resources": { - "configuration": { - "paths": { - "src": "main/src" - } - }, - "pathMappings": { - "/resources/": "main/src" - } - } - } + cloneProjectTree(coreLibraryTree) ], "_level": 0, "specVersion": "0.1", @@ -1114,30 +1082,7 @@ const libraryITree = { "version": "1.0.0", "path": libraryIPath, "dependencies": [ - { - "id": "sap.ui.core-evo", - "version": "1.0.0", - "path": libraryCore, - "dependencies": [], - "_level": 1, - "specVersion": "0.1", - "type": "library", - "metadata": { - "name": "sap.ui.core", - "namespace": "sap/ui/core", - "copyright": "Some fancy copyright" - }, - "resources": { - "configuration": { - "paths": { - "src": "main/src" - } - }, - "pathMappings": { - "/resources/": "main/src" - } - } - }, + cloneProjectTree(coreLibraryTree), cloneProjectTree(libraryDTree) ], "_level": 0, @@ -1166,7 +1111,9 @@ const libraryJTree = { "id": "library.j", "version": "1.0.0", "path": libraryJPath, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "_level": 0, "specVersion": "0.1", "type": "library", diff --git a/test/lib/tasks/bundlers/generateLibraryPreload.js b/test/lib/tasks/bundlers/generateLibraryPreload.js index 11790e88d..c0e890d91 100644 --- a/test/lib/tasks/bundlers/generateLibraryPreload.js +++ b/test/lib/tasks/bundlers/generateLibraryPreload.js @@ -42,7 +42,7 @@ test("integration: build library.d with library preload", async (t) => { assert.directoryDeepEqual(destPath, expectedPath); // Check for all file contents - t.deepEqual(expectedFiles.length, 4, "4 files are expected"); + t.deepEqual(expectedFiles.length, 5, "5 files are expected"); expectedFiles.forEach((expectedFile) => { const relativeFile = path.relative(expectedPath, expectedFile); const destFile = path.join(destPath, relativeFile); @@ -95,7 +95,7 @@ test("integration: build sap.ui.core with library preload", async (t) => { assert.directoryDeepEqual(destPath, expectedPath); // Check for all file contents - t.deepEqual(expectedFiles.length, 9, "9 files are expected"); + t.deepEqual(expectedFiles.length, 10, "10 files are expected"); expectedFiles.forEach((expectedFile) => { const relativeFile = path.relative(expectedPath, expectedFile); const destFile = path.join(destPath, relativeFile); diff --git a/test/lib/tasks/generateCachebusterInfo.js b/test/lib/tasks/generateCachebusterInfo.js index d577641dc..9a0d3b0fe 100644 --- a/test/lib/tasks/generateCachebusterInfo.js +++ b/test/lib/tasks/generateCachebusterInfo.js @@ -9,6 +9,7 @@ const assert = chai.assert; const ui5Builder = require("../../../"); const builder = ui5Builder.builder; const applicationGPath = path.join(__dirname, "..", "..", "fixtures", "application.g"); +const libraryCore = path.join(__dirname, "..", "..", "fixtures", "sap.ui.core-evo"); const recursive = require("recursive-readdir"); @@ -24,6 +25,20 @@ const findFiles = (folder) => { }); }; +function cloneProjectTree(tree) { + const clone = JSON.parse(JSON.stringify(tree)); + + function increaseDepth(node) { + node._level++; + if (Array.isArray(node.dependencies)) { + node.dependencies.forEach(increaseDepth); + } + } + + increaseDepth(clone); + return clone; +} + test("integration: Build application.g with manifestBundler", (t) => { const destPath = path.join("test", "tmp", "build", "application.g", "cachebuster"); const expectedPath = path.join("test", "expected", "build", "application.g", "cachebuster"); @@ -90,11 +105,37 @@ test("integration: Build application.g with manifestBundler and cachebuster usin }); }); +const coreLibraryTree = { + "id": "sap.ui.core-evo", + "version": "1.0.0", + "path": libraryCore, + "dependencies": [], + "_level": 0, + "specVersion": "0.1", + "type": "library", + "metadata": { + "name": "sap.ui.core", + "copyright": "Some fancy copyright" + }, + "resources": { + "configuration": { + "paths": { + "src": "main/src" + } + }, + "pathMappings": { + "/resources/": "main/src" + } + } +}; + const applicationGTree = { "id": "application.g", "version": "1.0.0", "path": applicationGPath, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "builder": {}, "_level": 0, "specVersion": "0.1", @@ -120,7 +161,9 @@ const applicationGTreeWithCachebusterHash = { "id": "application.g", "version": "1.0.0", "path": applicationGPath, - "dependencies": [], + "dependencies": [ + cloneProjectTree(coreLibraryTree) + ], "builder": { "cachebuster": { "signatureType": "hash"