From 2e1f045a89a4b9523b1de38246596cb249870b46 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Thu, 21 Apr 2022 07:09:22 +0200 Subject: [PATCH 1/3] [FIX] generateResourcesJson: Add raw-module info for debug bundles The raw-module info within .library is only maintained using the non-debug name. Therefore, the analysis must use that name when running with debug bundles. Furthermore the handling of the "module" property for debug resources has been changed to only take place within the ResourceCollector. JIRA: CPOUI5FOUNDATION-480 --- lib/lbt/resources/ResourceCollector.js | 32 ++++---- lib/lbt/resources/ResourceInfoList.js | 1 - test/lib/lbt/resources/ResourceCollector.js | 83 +++++++++++++++++++++ test/lib/lbt/resources/ResourceInfoList.js | 3 +- 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/lib/lbt/resources/ResourceCollector.js b/lib/lbt/resources/ResourceCollector.js index a715f28cb..59872c056 100644 --- a/lib/lbt/resources/ResourceCollector.js +++ b/lib/lbt/resources/ResourceCollector.js @@ -279,11 +279,9 @@ class ResourceCollector { await Promise.all(promises); - const debugBundlePromises = []; - - for (let i = debugResourcesInfo.length - 1; i >= 0; i--) { - const dbgInfo = debugResourcesInfo[i]; - const nonDebugName = ResourceInfoList.getNonDebugName(dbgInfo.name); + await Promise.all(debugResourcesInfo.map(async (dbgInfo) => { + const debugName = dbgInfo.name; + const nonDebugName = ResourceInfoList.getNonDebugName(debugName); const nonDbgInfo = this._resources.get(nonDebugName); // FIXME: "merged" property is only calculated in ResourceInfo#copyFrom @@ -291,25 +289,33 @@ class ResourceCollector { if (!nonDbgInfo || (nonDbgInfo.included != null && nonDbgInfo.included.size > 0)) { // We need to analyze the dbg resource if there is no non-dbg variant or // it is a bundle because we will (usually) have different content. - debugBundlePromises.push( - this.enrichWithDependencyInfo(dbgInfo) - ); + + // In order to retrieve the correct raw module info, the name must be set + // to the non-debug name. The same happens during bundling, where also only + // the non-debug names are used. + dbgInfo.name = nonDebugName; + + await this.enrichWithDependencyInfo(dbgInfo); + + // After the analysis, the name needs to be set back + // The "module" property is now already set to the non-debug name, as expected + dbgInfo.name = debugName; } else { // If the non-dbg resource is not a bundle, we can just copy over the info and skip // analyzing the dbg variant as both should have the same info. - const newDbgInfo = new ResourceInfo(dbgInfo.name); + const newDbgInfo = new ResourceInfo(debugName); // First copy info of analysis from non-dbg file (included, required, condRequired, ...) newDbgInfo.copyFrom(null, nonDbgInfo); // Then copy over info from dbg file to properly set name, isDebug, etc. newDbgInfo.copyFrom(null, dbgInfo); + // Finally, set the module name to the non-dbg name + newDbgInfo.module = nonDbgInfo.module; - this._resources.set(dbgInfo.name, newDbgInfo); + this._resources.set(debugName, newDbgInfo); } - } - - await Promise.all(debugBundlePromises); + })); } createOrphanFilters() { diff --git a/lib/lbt/resources/ResourceInfoList.js b/lib/lbt/resources/ResourceInfoList.js index 1a87b2f8e..0501269aa 100644 --- a/lib/lbt/resources/ResourceInfoList.js +++ b/lib/lbt/resources/ResourceInfoList.js @@ -48,7 +48,6 @@ class ResourceInfoList { if ( myInfo == null ) { myInfo = new ResourceInfo(relativeName); myInfo.size = info.size; - myInfo.module = ResourceInfoList.getNonDebugName(info.name); this.resources.push(myInfo); this.resourcesByName.set(relativeName, myInfo); } diff --git a/test/lib/lbt/resources/ResourceCollector.js b/test/lib/lbt/resources/ResourceCollector.js index 5f0bd6204..721f70e60 100644 --- a/test/lib/lbt/resources/ResourceCollector.js +++ b/test/lib/lbt/resources/ResourceCollector.js @@ -1,6 +1,8 @@ const test = require("ava"); const sinon = require("sinon"); const mock = require("mock-require"); +const {Resource} = require("@ui5/fs"); +const LocatorResourcePool = require("../../../../lib/lbt/resources/LocatorResourcePool"); let ResourceCollector = require("../../../../lib/lbt/resources/ResourceCollector"); @@ -286,3 +288,84 @@ test.serial("enrichWithDependencyInfo: add infos to resourceinfo", async (t) => requiresTopLevelScope: true }, "all information gets used for the resourceInfo"); }); + +test.serial("integration: Raw Module Info for debug variant", async (t) => { + const resources = [ + new Resource({ + path: "/resources/mylib/myRawModuleBundle.js", + string: `define('a', () => 'a');define('b', ['a'], (a) => a + 'b');` + }), + new Resource({ + path: "/resources/mylib/externalDependency.js", + string: `console.log('Foo');` + }), + new Resource({ + path: "/resources/mylib/myRawModuleBundle-dbg.js", + string: ` +define('a', () => 'a'); +define('b', ['a'], (a) => a + 'b'); +` + }), + new Resource({ + path: "/resources/mylib/.library", + string: ` + + + mylib + Me + mylib + 1.0.0 + mylib + + + sap.ui.core + + + + + + + + + + ` + }), + ]; + + const pool = new LocatorResourcePool(); + await pool.prepare( resources ); + + const collector = new ResourceCollector(pool); + await Promise.all(resources.map((resource) => collector.visitResource(resource))); + + await collector.determineResourceDetails({ + debugResources: ["**/*-dbg.js"] + }); + + collector.groupResourcesByComponents(); + + const resourceInfoList = collector.components.get("mylib/"); + + const myRawModuleBundle = resourceInfoList.resourcesByName.get("myRawModuleBundle.js"); + t.is(myRawModuleBundle.name, "myRawModuleBundle.js"); + t.is(myRawModuleBundle.module, "mylib/myRawModuleBundle.js"); + t.is(myRawModuleBundle.format, "raw"); + t.is(myRawModuleBundle.requiresTopLevelScope, false); + t.deepEqual(myRawModuleBundle.included, + new Set(["a.js", "b.js"])); + t.deepEqual(myRawModuleBundle.required, + new Set(["mylib/externalDependency.js"])); + + const myRawModuleBundleDbg = resourceInfoList.resourcesByName.get("myRawModuleBundle-dbg.js"); + t.is(myRawModuleBundleDbg.name, "myRawModuleBundle-dbg.js"); + t.is(myRawModuleBundleDbg.module, "mylib/myRawModuleBundle.js"); + t.is(myRawModuleBundleDbg.format, "raw"); + t.is(myRawModuleBundleDbg.requiresTopLevelScope, false); + t.deepEqual(myRawModuleBundleDbg.included, + new Set(["a.js", "b.js"])); + t.deepEqual(myRawModuleBundleDbg.required, + new Set(["mylib/externalDependency.js"])); +}); diff --git a/test/lib/lbt/resources/ResourceInfoList.js b/test/lib/lbt/resources/ResourceInfoList.js index a11d19ac1..8521859b7 100644 --- a/test/lib/lbt/resources/ResourceInfoList.js +++ b/test/lib/lbt/resources/ResourceInfoList.js @@ -36,7 +36,8 @@ test("add: add debug resources", (t) => { t.is(resourceInfoList.resources.length, 1, "one resource added"); const resultDbg = resourceInfoList.resourcesByName.get("../myfile-dbg.js"); - t.is(resultDbg.module, "myfile.js", "module is set"); + // Note: "module" will be set properly for debug resources within ResourceCollector#determineResourceDetails + t.is(resultDbg.module, undefined, "module is not set"); t.deepEqual(resultDbg.required, new Set(["some-dep.js"]), "module is set"); }); From 831ff2f6c9bdb2ee2900e8ddd88d7cc1baabe33c Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Thu, 21 Apr 2022 09:21:46 +0200 Subject: [PATCH 2/3] WIP --- lib/lbt/resources/ResourceCollector.js | 5 +++ test/lib/lbt/resources/ResourceCollector.js | 47 +++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/lbt/resources/ResourceCollector.js b/lib/lbt/resources/ResourceCollector.js index 59872c056..b8366ca49 100644 --- a/lib/lbt/resources/ResourceCollector.js +++ b/lib/lbt/resources/ResourceCollector.js @@ -286,6 +286,11 @@ class ResourceCollector { // FIXME: "merged" property is only calculated in ResourceInfo#copyFrom // Therefore using the same logic here to compute it. + + // TODO: Idea: Use IsDebugVariant tag to decide whether to analyze the resource + // If the tag is set, we don't expect different analysis results so we can copy the info (else-path) + // Only when the tag is not set, we analyze the resource with its name (incl. -dbg) + if (!nonDbgInfo || (nonDbgInfo.included != null && nonDbgInfo.included.size > 0)) { // We need to analyze the dbg resource if there is no non-dbg variant or // it is a bundle because we will (usually) have different content. diff --git a/test/lib/lbt/resources/ResourceCollector.js b/test/lib/lbt/resources/ResourceCollector.js index 721f70e60..0b0eec57e 100644 --- a/test/lib/lbt/resources/ResourceCollector.js +++ b/test/lib/lbt/resources/ResourceCollector.js @@ -369,3 +369,50 @@ define('b', ['a'], (a) => a + 'b'); t.deepEqual(myRawModuleBundleDbg.required, new Set(["mylib/externalDependency.js"])); }); + +test.serial.only("integration: Analyze debug bundle", async (t) => { + const resources = [ + new Resource({ + path: "/resources/mylib/myBundle.js", + string: `sap.ui.define('a', () => 'a');sap.ui.define('b', ['a'], (a) => a + 'b');` + }), + new Resource({ + path: "/resources/mylib/myBundle-dbg.js", + string: `sap.ui.define('a', () => 'a');` + }), + ]; + + const pool = new LocatorResourcePool(); + await pool.prepare( resources ); + + const collector = new ResourceCollector(pool); + await Promise.all(resources.map((resource) => collector.visitResource(resource))); + + await collector.determineResourceDetails({ + debugResources: ["**/*-dbg.js"] + }); + + collector.groupResourcesByComponents(); + + const resourceInfoList = collector.components.get("mylib/"); + + const myRawModuleBundle = resourceInfoList.resourcesByName.get("myBundle.js"); + t.is(myRawModuleBundle.name, "myBundle.js"); + t.is(myRawModuleBundle.module, "mylib/myBundle.js"); + t.is(myRawModuleBundle.format, "raw"); + t.is(myRawModuleBundle.requiresTopLevelScope, false); + t.deepEqual(myRawModuleBundle.included, + new Set(["a.js", "b.js"])); + t.deepEqual(myRawModuleBundle.required, + new Set(["mylib/externalDependency.js"])); + + const myRawModuleBundleDbg = resourceInfoList.resourcesByName.get("myBundle-dbg.js"); + t.is(myRawModuleBundleDbg.name, "myBundle-dbg.js"); + t.is(myRawModuleBundleDbg.module, "mylib/myBundle.js"); + t.is(myRawModuleBundleDbg.format, "raw"); + t.is(myRawModuleBundleDbg.requiresTopLevelScope, false); + t.deepEqual(myRawModuleBundleDbg.included, + new Set(["a.js", "b.js"])); + t.deepEqual(myRawModuleBundleDbg.required, + new Set(["mylib/externalDependency.js"])); +}); From 32c95fc013191ea88d1d0cc7a844a52ba905e226 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Thu, 21 Apr 2022 13:35:38 +0200 Subject: [PATCH 3/3] Fix reading raw-module info --- lib/lbt/resources/ResourceCollector.js | 18 +++++------ lib/lbt/resources/ResourcePool.js | 13 ++++---- test/lib/lbt/resources/ResourceCollector.js | 33 ++++++++++++++++----- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/lib/lbt/resources/ResourceCollector.js b/lib/lbt/resources/ResourceCollector.js index b8366ca49..5534ed4df 100644 --- a/lib/lbt/resources/ResourceCollector.js +++ b/lib/lbt/resources/ResourceCollector.js @@ -110,10 +110,11 @@ class ResourceCollector { } async enrichWithDependencyInfo(resourceInfo) { - return this._pool.getModuleInfo(resourceInfo.name).then(async (moduleInfo) => { - if ( moduleInfo.name ) { + return this._pool.getModuleInfo(resourceInfo.name, resourceInfo.module).then(async (moduleInfo) => { + if ( !resourceInfo.module && moduleInfo.name ) { resourceInfo.module = moduleInfo.name; } + if ( moduleInfo.dynamicDependencies ) { resourceInfo.dynRequired = true; } @@ -295,16 +296,11 @@ class ResourceCollector { // We need to analyze the dbg resource if there is no non-dbg variant or // it is a bundle because we will (usually) have different content. - // In order to retrieve the correct raw module info, the name must be set - // to the non-debug name. The same happens during bundling, where also only - // the non-debug names are used. - dbgInfo.name = nonDebugName; - + if (nonDbgInfo) { + // Always use the non-debug module name, if available + dbgInfo.module = nonDbgInfo.module; + } await this.enrichWithDependencyInfo(dbgInfo); - - // After the analysis, the name needs to be set back - // The "module" property is now already set to the non-debug name, as expected - dbgInfo.name = debugName; } else { // If the non-dbg resource is not a bundle, we can just copy over the info and skip // analyzing the dbg variant as both should have the same info. diff --git a/lib/lbt/resources/ResourcePool.js b/lib/lbt/resources/ResourcePool.js index 5b92bb931..af3c8e7ac 100644 --- a/lib/lbt/resources/ResourcePool.js +++ b/lib/lbt/resources/ResourcePool.js @@ -176,17 +176,18 @@ class ResourcePool { /** * Retrieves the module info * - * @param {string} name module name + * @param {string} resourceName resource/module name + * @param {string} [moduleName] module name, in case it differs from the resource name (e.g. for -dbg resources) * @returns {Promise} */ - async getModuleInfo(name) { - let info = this._dependencyInfos.get(name); + async getModuleInfo(resourceName, moduleName) { + let info = this._dependencyInfos.get(resourceName); if ( info == null ) { info = Promise.resolve().then(async () => { - const resource = await this.findResource(name); - return determineDependencyInfo( resource, this._rawModuleInfos.get(name), this ); + const resource = await this.findResource(resourceName); + return determineDependencyInfo( resource, this._rawModuleInfos.get(moduleName || resourceName), this ); }); - this._dependencyInfos.set(name, info); + this._dependencyInfos.set(resourceName, info); } return info; } diff --git a/test/lib/lbt/resources/ResourceCollector.js b/test/lib/lbt/resources/ResourceCollector.js index 0b0eec57e..12633136f 100644 --- a/test/lib/lbt/resources/ResourceCollector.js +++ b/test/lib/lbt/resources/ResourceCollector.js @@ -370,15 +370,32 @@ define('b', ['a'], (a) => a + 'b'); new Set(["mylib/externalDependency.js"])); }); -test.serial.only("integration: Analyze debug bundle", async (t) => { +test.serial("integration: Analyze debug bundle", async (t) => { const resources = [ new Resource({ path: "/resources/mylib/myBundle.js", - string: `sap.ui.define('a', () => 'a');sap.ui.define('b', ['a'], (a) => a + 'b');` + string: `sap.ui.predefine('a', () => 'a');sap.ui.predefine('b', ['a'], (a) => a + 'b');` }), new Resource({ path: "/resources/mylib/myBundle-dbg.js", - string: `sap.ui.define('a', () => 'a');` + string: `sap.ui.predefine('a', () => 'a');` + }), + new Resource({ + path: "/resources/mylib/.library", + string: ` + + + mylib + Me + mylib + 1.0.0 + mylib + + + sap.ui.core + + + ` }), ]; @@ -399,20 +416,20 @@ test.serial.only("integration: Analyze debug bundle", async (t) => { const myRawModuleBundle = resourceInfoList.resourcesByName.get("myBundle.js"); t.is(myRawModuleBundle.name, "myBundle.js"); t.is(myRawModuleBundle.module, "mylib/myBundle.js"); - t.is(myRawModuleBundle.format, "raw"); + t.is(myRawModuleBundle.format, null); t.is(myRawModuleBundle.requiresTopLevelScope, false); t.deepEqual(myRawModuleBundle.included, new Set(["a.js", "b.js"])); t.deepEqual(myRawModuleBundle.required, - new Set(["mylib/externalDependency.js"])); + new Set([])); const myRawModuleBundleDbg = resourceInfoList.resourcesByName.get("myBundle-dbg.js"); t.is(myRawModuleBundleDbg.name, "myBundle-dbg.js"); t.is(myRawModuleBundleDbg.module, "mylib/myBundle.js"); - t.is(myRawModuleBundleDbg.format, "raw"); + t.is(myRawModuleBundleDbg.format, null); t.is(myRawModuleBundleDbg.requiresTopLevelScope, false); t.deepEqual(myRawModuleBundleDbg.included, - new Set(["a.js", "b.js"])); + new Set(["a.js"])); t.deepEqual(myRawModuleBundleDbg.required, - new Set(["mylib/externalDependency.js"])); + new Set()); });