diff --git a/lib/lbt/resources/ResourceCollector.js b/lib/lbt/resources/ResourceCollector.js index 3acc2f88d..a715f28cb 100644 --- a/lib/lbt/resources/ResourceCollector.js +++ b/lib/lbt/resources/ResourceCollector.js @@ -193,17 +193,16 @@ class ResourceCollector { } async determineResourceDetails({ - debugResources, mergedResources, designtimeResources, supportResources, debugBundles + debugResources, mergedResources, designtimeResources, supportResources }) { const baseNames = new Set(); const debugFilter = new ResourceFilterList(debugResources); const mergeFilter = new ResourceFilterList(mergedResources); const designtimeFilter = new ResourceFilterList(designtimeResources); const supportFilter = new ResourceFilterList(supportResources); - const debugBundleFilter = new ResourceFilterList(debugBundles); const promises = []; - const nonBundledDebugResources = []; + const debugResourcesInfo = []; for (const [name, info] of this._resources.entries()) { if ( debugFilter.matches(name) ) { @@ -231,13 +230,14 @@ class ResourceCollector { } if ( /(?:\.js|\.view\.xml|\.control\.xml|\.fragment\.xml)$/.test(name) ) { - if ( (!info.isDebug || debugBundleFilter.matches(name)) ) { - // Only analyze non-debug files and special debug bundles (like sap-ui-core-dbg.js) + if ( !info.isDebug ) { + // Only analyze non-dbg files in first run promises.push( this.enrichWithDependencyInfo(info) ); } else { - nonBundledDebugResources.push(info); + // Collect dbg files to be handled in a second step (see below) + debugResourcesInfo.push(info); } } @@ -279,19 +279,37 @@ class ResourceCollector { await Promise.all(promises); - for (let i = nonBundledDebugResources.length - 1; i >= 0; i--) { - const dbgInfo = nonBundledDebugResources[i]; + const debugBundlePromises = []; + + for (let i = debugResourcesInfo.length - 1; i >= 0; i--) { + const dbgInfo = debugResourcesInfo[i]; const nonDebugName = ResourceInfoList.getNonDebugName(dbgInfo.name); const nonDbgInfo = this._resources.get(nonDebugName); - const newDbgInfo = new ResourceInfo(dbgInfo.name); - - // 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); - this._resources.set(dbgInfo.name, newDbgInfo); + // FIXME: "merged" property is only calculated in ResourceInfo#copyFrom + // Therefore using the same logic here to compute it. + 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) + ); + } 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); + + // 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); + + this._resources.set(dbgInfo.name, newDbgInfo); + } } + + await Promise.all(debugBundlePromises); } createOrphanFilters() { diff --git a/lib/processors/resourceListCreator.js b/lib/processors/resourceListCreator.js index ba012488a..cf4d811b3 100644 --- a/lib/processors/resourceListCreator.js +++ b/lib/processors/resourceListCreator.js @@ -66,17 +66,6 @@ const DEFAULT_SUPPORT_RESOURCES_FILTER = [ "**/*.support.js" ]; -/** - * Hard coded debug bundle, to trigger separate analysis for this filename - * because sap-ui-core.js and sap-ui-core-dbg.js have different includes - * - * @type {string[]} - */ -const DEBUG_BUNDLES = [ - "sap-ui-core-dbg.js", - "sap-ui-core-nojQuery-dbg.js" -]; - /** * Creates and adds resources.json entry (itself) to the list. * @@ -138,8 +127,7 @@ module.exports = async function({resources, dependencyResources = [], options}) debugResources: DEFAULT_DEBUG_RESOURCES_FILTER, mergedResources: DEFAULT_BUNDLE_RESOURCES_FILTER, designtimeResources: DEFAULT_DESIGNTIME_RESOURCES_FILTER, - supportResources: DEFAULT_SUPPORT_RESOURCES_FILTER, - debugBundles: DEBUG_BUNDLES + supportResources: DEFAULT_SUPPORT_RESOURCES_FILTER }, options); const pool = new LocatorResourcePool(); @@ -158,12 +146,10 @@ module.exports = async function({resources, dependencyResources = [], options}) } await collector.determineResourceDetails({ - pool, debugResources: options.debugResources, mergedResources: options.mergedResources, designtimeResources: options.designtimeResources, - supportResources: options.supportResources, - debugBundles: options.debugBundles + supportResources: options.supportResources }); // group resources by components and create ResourceInfoLists diff --git a/test/lib/lbt/resources/ResourceCollector.js b/test/lib/lbt/resources/ResourceCollector.js index 102377612..5f0bd6204 100644 --- a/test/lib/lbt/resources/ResourceCollector.js +++ b/test/lib/lbt/resources/ResourceCollector.js @@ -95,7 +95,7 @@ test.serial("visitResource: ensure proper matching of indicator files", async (t t.is(resourceCollector.components.size, 0, "No prefixes should be added"); }); -test.serial("groupResourcesByComponents: debugBundles", async (t) => { +test.serial("groupResourcesByComponents: external resources", async (t) => { const resourceCollector = new ResourceCollector(); resourceCollector.setExternalResources({ "testcomp": ["my/file.js"] @@ -149,25 +149,60 @@ test.serial("determineResourceDetails: view.xml", async (t) => { t.is(enrichWithDependencyInfoStub.getCall(0).args[0].name, "mylib/my.view.xml", "is called with view"); }); -test.serial("determineResourceDetails: Debug bundle", async (t) => { +test.serial("determineResourceDetails: Debug bundle (without non-debug variant)", async (t) => { const resourceCollector = new ResourceCollector(); const enrichWithDependencyInfoStub = sinon.stub(resourceCollector, "enrichWithDependencyInfo").resolves(); await resourceCollector.visitResource({getPath: () => "/resources/MyBundle-dbg.js", getSize: async () => 13}); await resourceCollector.determineResourceDetails({ - debugBundles: ["MyBundle-dbg.js"] + debugResources: ["**/*-dbg.js"], // MyBundle-dbg.js should be marked as "isDebug" }); + t.is(enrichWithDependencyInfoStub.callCount, 1, "enrichWithDependencyInfo is called once"); t.is(enrichWithDependencyInfoStub.getCall(0).args[0].name, "MyBundle-dbg.js", "enrichWithDependencyInfo is called with debug bundle"); }); +test.serial("determineResourceDetails: Debug bundle (with non-debug variant)", async (t) => { + const resourceCollector = new ResourceCollector(); + + const enrichWithDependencyInfoStub = sinon.stub(resourceCollector, "enrichWithDependencyInfo") + .onFirstCall().callsFake(async (resourceInfo) => { + resourceInfo.included = new Set(["SomeModule.js"]); + resourceInfo.required = new Set(["Boot.js"]); + }) + .onSecondCall().callsFake(async (resourceInfo) => { + resourceInfo.required = new Set(["Boot.js"]); + }); + await resourceCollector.visitResource({getPath: () => "/resources/MyBundle-dbg.js", getSize: async () => 13}); + await resourceCollector.visitResource({getPath: () => "/resources/MyBundle.js", getSize: async () => 13}); + + await resourceCollector.determineResourceDetails({ + debugResources: ["**/*-dbg.js"], // MyBundle-dbg.js should be marked as "isDebug" + }); + t.is(enrichWithDependencyInfoStub.callCount, 2, "enrichWithDependencyInfo is called twice"); + t.is(enrichWithDependencyInfoStub.getCall(0).args[0].name, "MyBundle.js", + "enrichWithDependencyInfo is called with non-debug bundle first"); + t.is(enrichWithDependencyInfoStub.getCall(1).args[0].name, "MyBundle-dbg.js", + "enrichWithDependencyInfo is called with debug bundle on second run"); + + const bundleInfo = resourceCollector._resources.get("MyBundle.js"); + t.deepEqual(bundleInfo.included, new Set(["SomeModule.js"])); + t.deepEqual(bundleInfo.required, new Set(["Boot.js"])); + t.is(bundleInfo.isDebug, false); + + const debugBundleInfo = resourceCollector._resources.get("MyBundle-dbg.js"); + t.is(debugBundleInfo.included, null); + t.deepEqual(debugBundleInfo.required, new Set(["Boot.js"])); + t.is(debugBundleInfo.isDebug, true); +}); + test.serial("determineResourceDetails: Debug files and non-debug files", async (t) => { const resourceCollector = new ResourceCollector(); const enrichWithDependencyInfoStub = sinon.stub(resourceCollector, "enrichWithDependencyInfo") - .callsFake((resourceInfo) => { + .callsFake(async (resourceInfo) => { // Simulate enriching resource info with dependency info to test whether it gets shared // with the dbg resource later on resourceInfo.dynRequired = true; @@ -183,16 +218,15 @@ test.serial("determineResourceDetails: Debug files and non-debug files", async ( })); await resourceCollector.determineResourceDetails({ - debugResources: ["**/*-dbg.js"], - debugBundles: ["MyBundle-dbg.js"] + debugResources: ["**/*-dbg.js"] }); t.is(enrichWithDependencyInfoStub.callCount, 3, "enrichWithDependencyInfo is called three times"); - t.is(enrichWithDependencyInfoStub.getCall(0).args[0].name, "MyBundle-dbg.js", - "enrichWithDependencyInfo called with debug bundle"); - t.is(enrichWithDependencyInfoStub.getCall(1).args[0].name, "mylib/MyControlA.js", + t.is(enrichWithDependencyInfoStub.getCall(0).args[0].name, "mylib/MyControlA.js", "enrichWithDependencyInfo called with non-debug control A"); - t.is(enrichWithDependencyInfoStub.getCall(2).args[0].name, "mylib/MyControlB.js", + t.is(enrichWithDependencyInfoStub.getCall(1).args[0].name, "mylib/MyControlB.js", "enrichWithDependencyInfo called with non-debug control B"); + t.is(enrichWithDependencyInfoStub.getCall(2).args[0].name, "MyBundle-dbg.js", + "enrichWithDependencyInfo called with debug bundle"); t.is(resourceCollector._resources.get("MyBundle-dbg.js").isDebug, true, "MyBundle-dbg is a debug file"); t.is(resourceCollector._resources.get("MyBundle-dbg.js").dynRequired, true,