Skip to content
Closed
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
35 changes: 24 additions & 11 deletions packages/builder/lib/tasks/bundlers/generateLibraryPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import moduleBundler from "../../processors/bundlers/moduleBundler.js";
import {applyDefaultsToBundleDefinition} from "./utils/applyDefaultsToBundleDefinition.js";
import {negateFilters} from "../../lbt/resources/ResourceFilterList.js";
import createModuleNameMapping from "./utils/createModuleNameMapping.js";
import {getDebugName, getNonDebugName} from "../../lbt/utils/ModuleName.js";

function getDefaultLibraryPreloadFilters(namespace, excludes) {
const filters = [
Expand Down Expand Up @@ -352,16 +353,6 @@ export default function defineTask(options) {
* @returns {Promise<undefined>} Promise resolving once the library preload bundles have been written
*/
async function task({workspace, taskUtil, options: {skipBundles = [], excludes = [], projectName}}) {
let nonDbgWorkspace = workspace;
if (taskUtil) {
nonDbgWorkspace = taskUtil.resourceFactory.createFilterReader({
reader: workspace,
callback: function(resource) {
// Remove any debug variants
return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
}
});
}
const coreVersion = taskUtil?.getProject("sap.ui.core")?.getVersion();
const allowStringBundling = taskUtil?.getProject().getSpecVersion().lt("4.0");
// Read the experimental flag through taskUtil.getEnv so that its usage is tracked as a task
Expand All @@ -383,9 +374,31 @@ async function task({workspace, taskUtil, options: {skipBundles = [], excludes =
return moduleBundler({options, resources});
};

return nonDbgWorkspace.byGlob(
return workspace.byGlob(
"/resources/**/*.{js,json,xml,html,properties,library,js.map}"
).then(async (resources) => {
if (taskUtil) {
const paths = new Set(resources.map((resource) => resource.getPath()));
resources = resources.filter((resource) => {
if (!taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant) &&
!taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant)) {
// Check for -dbg.js/.js pairs and tag them accordingly
const resourcePath = resource.getPath();
const debugPath = getDebugName(resourcePath);
if (debugPath && paths.has(debugPath)) {
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
}
const nonDebugPath = getNonDebugName(resourcePath);
if (nonDebugPath && paths.has(nonDebugPath)) {
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
}
}

// Remove these debug variants (+ those already tagged by earlier tasks)
return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
});
}

// Find all libraries and create a library-preload.js bundle

let p = Promise.resolve();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import test from "ava";
import path from "node:path";
import {createAdapter, createResource} from "@ui5/fs/resourceFactory";
import {createAdapter, createResource, createFilterReader} from "@ui5/fs/resourceFactory";
import DuplexCollection from "@ui5/fs/DuplexCollection";
import ResourceTagCollection from "@ui5/fs/internal/ResourceTagCollection";
import {graphFromObject} from "@ui5/project/graph";
import {directoryDeepEqual, fileEqual, findFiles} from "../../../utils/fshelper.js";
import generateLibraryPreload from "../../../../lib/tasks/bundlers/generateLibraryPreload.js";
Expand Down Expand Up @@ -417,3 +418,113 @@ const libraryNTreeLegacy = {
}
}
};

test.serial("integration: generateLibraryPreload excludes a -dbg.js with a .js sibling", async (t) => {
// This test covers a scenario where a -dbg.js file exists WITH a corresponding normal .js sibling.
// This could happen e.g. if a build step already has already been executed manually (before the UI5 CLI ran).
// (-> bad pattern)
const tags = new ResourceTagCollection({allowedNamespaces: ["ui5"]});
const taskUtil = {
STANDARD_TAGS: {
IsDebugVariant: "ui5:IsDebugVariant",
HasDebugVariant: "ui5:HasDebugVariant",
IsBundle: "ui5:IsBundle",
OmitFromBuildResult: "ui5:OmitFromBuildResult"
},
getTag: tags.getTag.bind(tags),
setTag: tags.setTag.bind(tags),
clearTag: tags.clearTag.bind(tags),
getProject: (name) => name ? undefined : {getSpecVersion: () => ({lt: () => false})},
getEnv: () => undefined,
resourceFactory: {createFilterReader}
};

const reader = createAdapter({
virBasePath: "/"
});
await reader.write(createResource({
path: "/resources/my/test/lib/library.js",
string: ""
}));
await reader.write(createResource({
path: "/resources/my/test/lib/Example.js",
string: "sap.ui.define([], function() {});"
}));
await reader.write(createResource({
path: "/resources/my/test/lib/Example-dbg.js",
string: "sap.ui.define([], function() {});"
}));

const writer = createAdapter({
virBasePath: "/"
});
const duplexCollection = new DuplexCollection({reader, writer});

await generateLibraryPreload().task({
workspace: duplexCollection,
taskUtil,
options: {
projectName: "my.test.lib"
}
});

const libraryPreload = await writer.byPath("/resources/my/test/lib/library-preload.js");
t.truthy(libraryPreload, "library-preload.js should have been created");
const libraryPreloadContent = await libraryPreload.getString();
t.regex(libraryPreloadContent, /"my\/test\/lib\/Example"/,
"library-preload should include the normal .js sibling");
t.notRegex(libraryPreloadContent, /"my\/test\/lib\/Example-dbg"/,
"library-preload should exclude the -dbg.js sibling");
});

test.serial("integration: generateLibraryPreload includes a -dbg.js without a .js sibling", async (t) => {
// This test covers a scenario where a -dbg.js file exists WITHOUT a corresponding normal .js sibling.
// This could happen e.g. if (for whatever reason) the resource was simply named "..-dbg.js"
// (-> valid case)
const tags = new ResourceTagCollection({allowedNamespaces: ["ui5"]});
const taskUtil = {
STANDARD_TAGS: {
IsDebugVariant: "ui5:IsDebugVariant",
HasDebugVariant: "ui5:HasDebugVariant",
IsBundle: "ui5:IsBundle",
OmitFromBuildResult: "ui5:OmitFromBuildResult"
},
getTag: tags.getTag.bind(tags),
setTag: tags.setTag.bind(tags),
clearTag: tags.clearTag.bind(tags),
getProject: (name) => name ? undefined : {getSpecVersion: () => ({lt: () => false})},
getEnv: () => undefined,
resourceFactory: {createFilterReader}
};

const reader = createAdapter({
virBasePath: "/"
});
await reader.write(createResource({
path: "/resources/my/test/lib/library.js",
string: ""
}));
await reader.write(createResource({
path: "/resources/my/test/lib/Example-dbg.js",
string: "sap.ui.define([], function() {});"
}));

const writer = createAdapter({
virBasePath: "/"
});
const duplexCollection = new DuplexCollection({reader, writer});

await generateLibraryPreload().task({
workspace: duplexCollection,
taskUtil,
options: {
projectName: "my.test.lib"
}
});

const libraryPreload = await writer.byPath("/resources/my/test/lib/library-preload.js");
t.truthy(libraryPreload, "library-preload.js should have been created");
const libraryPreloadContent = await libraryPreload.getString();
t.regex(libraryPreloadContent, /"my\/test\/lib\/Example-dbg"/,
"library-preload should include the standalone -dbg.js");
});
120 changes: 102 additions & 18 deletions packages/builder/test/lib/tasks/bundlers/generateLibraryPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,90 @@ test.afterEach.always(() => {
sinon.restore();
});

test.serial("generateLibraryPreload tags a -dbg.js with a .js sibling", async (t) => {
// This test covers a scenario where a -dbg.js file exists WITH a corresponding normal .js sibling.
// This could happen e.g. if a build step already has already been executed manually (before the UI5 CLI ran).
// (-> bad pattern)

const {generateLibraryPreload, workspace, dependencies, firstByGlob} = t.context;
const resources = [
{getPath: sinon.stub().returns("/resources/my/lib/Example.js")},
{getPath: sinon.stub().returns("/resources/my/lib/Example-dbg.js")}
];
firstByGlob.resolves(resources);
workspace.byGlob.resolves([
{getPath: sinon.stub().returns("/resources/my/lib/.library")}
]);

const taskUtil = {
getTag: sinon.stub().returns(false),
setTag: sinon.stub(),
getProject: (name) => name ? undefined : {getSpecVersion: () => ({lt: () => false})},
STANDARD_TAGS: {
IsDebugVariant: "<IsDebugVariant>",
HasDebugVariant: "<HasDebugVariant>"
},
resourceFactory: {
createFilterReader: ({reader, callback}) => ({
byGlob: async (pattern) => (await reader.byGlob(pattern)).filter(callback)
})
}
};
await generateLibraryPreload().task({
workspace,
dependencies,
taskUtil,
options: {projectName: "Test Library"}
});

t.true(taskUtil.setTag.calledWithExactly(resources[1], taskUtil.STANDARD_TAGS.IsDebugVariant),
"The -dbg.js sibling is tagged as a debug variant");
t.true(taskUtil.setTag.calledWithExactly(resources[0], taskUtil.STANDARD_TAGS.HasDebugVariant),
"The normal .js sibling is tagged as having a debug variant");
t.false(taskUtil.setTag.calledWith(resources[0], taskUtil.STANDARD_TAGS.IsDebugVariant),
"The normal .js sibling is not tagged as a debug variant");
});

test.serial("generateLibraryPreload does not tag a -dbg.js without a .js sibling", async (t) => {
// This test covers a scenario where a -dbg.js file exists WITHOUT a corresponding normal .js sibling.
// This could happen e.g. if (for whatever reason) the resource was simply named "..-dbg.js"
// (-> valid case)

const {generateLibraryPreload, workspace, dependencies, firstByGlob} = t.context;
const resources = [
{getPath: sinon.stub().returns("/resources/my/lib/Example-dbg.js")}
// WITHOUT the normal .js sibling
];
firstByGlob.resolves(resources);
workspace.byGlob.resolves([
{getPath: sinon.stub().returns("/resources/my/lib/.library")}
]);

const taskUtil = {
getTag: sinon.stub().returns(false),
setTag: sinon.stub(),
getProject: (name) => name ? undefined : {getSpecVersion: () => ({lt: () => false})},
STANDARD_TAGS: {
IsDebugVariant: "<IsDebugVariant>",
HasDebugVariant: "<HasDebugVariant>"
},
resourceFactory: {
createFilterReader: ({reader, callback}) => ({
byGlob: async (pattern) => (await reader.byGlob(pattern)).filter(callback)
})
}
};
await generateLibraryPreload().task({
workspace,
dependencies,
taskUtil,
options: {projectName: "Test Library"}
});

t.false(taskUtil.setTag.calledWith(resources[0], taskUtil.STANDARD_TAGS.IsDebugVariant),
"A standalone -dbg.js is not tagged as a debug variant");
});

test.serial("generateLibraryPreload", async (t) => {
const {
generateLibraryPreload, moduleBundlerStub,
Expand Down Expand Up @@ -528,15 +612,15 @@ test.serial("generateLibraryPreload for sap.ui.core (/w ui5loader.js)", async (t
t.deepEqual(workspace.byGlob.getCall(2).args, ["/resources/**/.library"],
"workspace.byGlob should have been called with expected pattern");

t.is(taskUtil.getTag.callCount, 2, "TaskUtil#getTag got called two times");
t.is(taskUtil.getTag.getCall(0).args[0], resources[2],
"TaskUtil#getTag got called with expected resource on first call");
t.is(taskUtil.getTag.getCall(0).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on first call");
t.is(taskUtil.getTag.getCall(1).args[0], resources[1],
"TaskUtil#getTag got called with expected resource on second call");
t.is(taskUtil.getTag.getCall(1).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on second call");
t.is(taskUtil.getTag.callCount, 11, "TaskUtil#getTag got called eleven times");
t.is(taskUtil.getTag.getCall(9).args[0], resources[2],
"TaskUtil#getTag got called with expected resource on tenth call");
t.is(taskUtil.getTag.getCall(9).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on tenth call");
t.is(taskUtil.getTag.getCall(10).args[0], resources[1],
"TaskUtil#getTag got called with expected resource on eleventh call");
t.is(taskUtil.getTag.getCall(10).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on eleventh call");

t.is(moduleBundlerStub.callCount, 7, "moduleBundler should have been called 7 times");
t.deepEqual(moduleBundlerStub.getCall(0).args, [{
Expand Down Expand Up @@ -1946,15 +2030,15 @@ test.serial("generateLibraryPreload for sap.ui.core (/w ui5loader.js), UI5 Versi
t.deepEqual(workspace.byGlob.getCall(2).args, ["/resources/**/.library"],
"workspace.byGlob should have been called with expected pattern");

t.is(taskUtil.getTag.callCount, 2, "TaskUtil#getTag got called two times");
t.is(taskUtil.getTag.getCall(0).args[0], resources[2],
"TaskUtil#getTag got called with expected resource on first call");
t.is(taskUtil.getTag.getCall(0).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on first call");
t.is(taskUtil.getTag.getCall(1).args[0], resources[1],
"TaskUtil#getTag got called with expected resource on second call");
t.is(taskUtil.getTag.getCall(1).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on second call");
t.is(taskUtil.getTag.callCount, 11, "TaskUtil#getTag got called eleven times");
t.is(taskUtil.getTag.getCall(9).args[0], resources[2],
"TaskUtil#getTag got called with expected resource on tenth call");
t.is(taskUtil.getTag.getCall(9).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on tenth call");
t.is(taskUtil.getTag.getCall(10).args[0], resources[1],
"TaskUtil#getTag got called with expected resource on eleventh call");
t.is(taskUtil.getTag.getCall(10).args[1], "<IsDebugVariant>",
"TaskUtil#getTag got called with expected tag on eleventh call");

t.is(moduleBundlerStub.callCount, 7, "moduleBundler should have been called 7 times");
t.deepEqual(moduleBundlerStub.getCall(0).args, [{
Expand Down
Loading