Skip to content
Open
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
8 changes: 7 additions & 1 deletion packages/builder/lib/tasks/minify.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function defineTask({pattern, omitSourceMapResources = false, use
name: "minify",
// One key per matched resource, so a delta build re-minifies only the resources whose inputs
// changed. The input source map is read through the step's workspace and thus tracked per step.
keys: async ({workspace}) => {
keys: async ({workspace, taskUtil}) => {
const resources = await workspace.byGlob(pattern);

// A -dbg.js next to its .js in the sources is unexpected: the minify task owns the -dbg.js
Expand All @@ -92,11 +92,17 @@ export default function defineTask({pattern, omitSourceMapResources = false, use
`ECMAScript syntax the minify task does not support yet), list both ${resourcePath} ` +
`and ${dbgPath} in the 'builder.minification.excludes' configuration of the ` +
`project's ui5.yaml.`);
if (taskUtil) {
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
}
return false;
}
const nonDbgPath = getNonDebugName(resourcePath);
if (nonDbgPath && paths.has(nonDbgPath)) {
// Skip the -dbg.js itself when its non-debug .js sibling is also present
if (taskUtil) {
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
}
return false;
}
return true;
Expand Down
89 changes: 87 additions & 2 deletions packages/builder/test/lib/tasks/minify.integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,27 @@ test.serial("integration: minify with taskUtil and resources tagged with OmitFro
// the -dbg.js, so a pre-existing one must not be there. The glob then matches both the .js and the -dbg.js
// as separate map-step keys, and the .js key would write its generated debug variant to the -dbg.js path
// that the -dbg.js key also writes - two concurrent keys writing one path, which crashes the build. To
// stay robust, the .js and that -dbg.js are both skipped. Other .js files are minified as usual.
test.serial("integration: minify skips a .js with an existing -dbg.js sibling and that -dbg.js",
// stay robust, the .js and that -dbg.js are both skipped, but must still receive their debug tags.
// Other .js files are minified as usual.
test.serial("integration: minify skips and tags a .js with an existing -dbg.js sibling and that -dbg.js",
async (t) => {
const {reader, writer, workspace} = createWorkspace();
const taskUtil = {
getEnv: (name) => process.env[name],
setTag: t.context.sinon.stub(),
getTag: t.context.sinon.stub().returns(false),
STANDARD_TAGS: {
HasDebugVariant: "has debug variant",
IsDebugVariant: "is debug variant",
OmitFromBuildResult: "omit from build result"
},
registerCleanupTask: t.context.sinon.stub()
};
t.teardown(async () => {
for (const call of taskUtil.registerCleanupTask.getCalls()) {
await call.firstArg();
}
});
const base = "/resources/my/namespace";
const content = `function a(x){return x;}\n`;

Expand All @@ -466,11 +483,20 @@ test.serial("integration: minify skips a .js with an existing -dbg.js sibling an

await runTask(minify, {
workspace,
taskUtil,
options: {
pattern: "/resources/**/*.js"
}
});

const tagsByPath = new Map(taskUtil.setTag.getCalls().map(({args: [resource, tag]}) => [
resource.getPath(), tag
]));
t.is(tagsByPath.get(base + "/withDbg.js"), taskUtil.STANDARD_TAGS.HasDebugVariant,
"Skipped .js is tagged as having a debug variant");
t.is(tagsByPath.get(base + "/withDbg-dbg.js"), taskUtil.STANDARD_TAGS.IsDebugVariant,
"Existing -dbg.js is tagged as a debug variant");

// The skipped .js and its -dbg.js are left untouched - the task writes neither them nor any derived
// resource (no source map, no overwrite of the existing -dbg.js).
t.is(await writer.byPath(`${base}/withDbg.js`), null, "Skipped .js is not written");
Expand All @@ -487,3 +513,62 @@ test.serial("integration: minify skips a .js with an existing -dbg.js sibling an
t.is(await dbg.getString(), content, "Debug variant keeps the original content");
t.truthy(await writer.byPath(`${base}/plain.js.map`), "Source map for the plain .js is written");
});

test.serial("integration: minify processes a standalone -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 {reader, writer, workspace} = createWorkspace();
const taskUtil = {
getEnv: (name) => process.env[name],
setTag: t.context.sinon.stub(),
getTag: t.context.sinon.stub().returns(false),
STANDARD_TAGS: {
HasDebugVariant: "has debug variant",
IsDebugVariant: "is debug variant",
OmitFromBuildResult: "omit from build result"
},
registerCleanupTask: t.context.sinon.stub()
};
t.teardown(async () => {
for (const call of taskUtil.registerCleanupTask.getCalls()) {
await call.firstArg();
}
});
const base = "/resources/my/namespace";
const content = `function a(x){return x;}\n`;

// A standalone -dbg.js without a normal .js sibling must be minified as usual.
await reader.write(resourceFactory.createResource({
path: `${base}/standalone-dbg.js`,
string: content
}));

await runTask(minify, {
workspace,
taskUtil,
options: {
pattern: "/resources/**/*.js"
}
});

const tagsByPath = new Map(taskUtil.setTag.getCalls().map(({args: [resource, tag]}) => [
resource.getPath(), tag
]));
t.is(tagsByPath.get(base + "/standalone-dbg.js"), taskUtil.STANDARD_TAGS.HasDebugVariant,
"The standalone file is tagged as having a generated debug variant");
t.is(tagsByPath.get(base + "/standalone-dbg-dbg.js"), taskUtil.STANDARD_TAGS.IsDebugVariant,
"The generated debug variant receives its debug tag");

t.is(await workspace.byPath(`${base}/standalone.js`), null, "No normal .js sibling exists");

// The standalone file produces a minified result, a debug variant and a source map.
const minified = await writer.byPath(`${base}/standalone-dbg.js`);
t.truthy(minified, "Standalone -dbg.js is written");
t.is(await minified.getString(), `function a(n){return n}\n${SOURCE_MAPPING_URL}=standalone-dbg.js.map`,
"Standalone -dbg.js is minified");
const dbg = await writer.byPath(`${base}/standalone-dbg-dbg.js`);
t.truthy(dbg, "Debug variant for the standalone file is written");
t.is(await dbg.getString(), content, "Debug variant keeps the original content");
t.truthy(await writer.byPath(`${base}/standalone-dbg.js.map`), "Source map for the standalone file is written");
});
51 changes: 50 additions & 1 deletion packages/builder/test/lib/tasks/minify.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ test("minify: No taskUtil", async (t) => {
});
});

test("minify: Skips and warns for an existing -dbg.js next to its .js", async (t) => {
test("minify: Skips, tags and warns for an existing -dbg.js next to its .js", async (t) => {
Comment thread
maxreichmann marked this conversation as resolved.
Resolved
const {minify, workspace, taskUtil, minifierStub, log} = t.context;
// A .js with an existing -dbg.js sibling (broken input state), plus a plain .js without a sibling.
const normal = {getPath: () => "/resources/normal.js"};
Expand Down Expand Up @@ -278,9 +278,58 @@ test("minify: Skips and warns for an existing -dbg.js next to its .js", async (t
t.true(log.warn.firstCall.firstArg.includes("builder.minification.excludes"),
"Warning hints at the minification excludes configuration");

t.true(taskUtil.setTag.calledWithExactly(withDbg, taskUtil.STANDARD_TAGS.HasDebugVariant),
"Skipped .js is tagged as having a debug variant");
t.true(taskUtil.setTag.calledWithExactly(existingDbg, taskUtil.STANDARD_TAGS.IsDebugVariant),
Comment on lines +281 to +283

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matz3 is testing setTag() enough? (testing getTag() would be cleaner?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for a unit test, I think checking for an expected setTag call is sufficient.
But the question here would rather be, how does the setTag behave from within the keys callback (mainly regarding build cache behavior). I can't answer it from the top of my head, but I assume this is not something we have really thought about before.
So it would be worth checking the details and how it behaves, e.g. via an integration test of such a scenario, where the whole build incl. caching and differential build is involved.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time :)

Yeah good idea regarding tests with caching. I'll look into this once I'm initially done with these PRs.

"Existing -dbg.js is tagged as a debug variant");

// Neither the skipped .js nor its -dbg.js got written back.
t.is(workspace.write.callCount, 3, "workspace#write got called only for the plain .js and its derived resources");
["resource normal", "dbgResource normal", "sourceMapResource normal"].forEach((resName, idx) => {
t.is(workspace.write.getCall(idx).firstArg, resName, "workspace#write got called for expected resource");
});
});

test("minify: Processes a standalone -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 {minify, workspace, taskUtil, minifierStub, log} = t.context;
const standalone = {getPath: () => "/resources/standalone-dbg.js"};
workspace.byGlob.resolves([standalone]);

const processedByInput = new Map([
[standalone, {
resource: "resource standalone",
dbgResource: "dbgResource standalone",
sourceMapResource: "sourceMapResource standalone",
}]
]);
minifierStub.callsFake(({resources}) => Promise.resolve([processedByInput.get(resources[0])]));

await runTask(minify, {
workspace,
taskUtil,
options: {
pattern: "**"
}
});

t.is(minifierStub.callCount, 1, "minifier got called once - for the standalone -dbg.js");
t.deepEqual(minifierStub.firstCall.firstArg.resources, [standalone],
"minifier processed the standalone -dbg.js");

t.is(log.warn.callCount, 0, "No warning got logged for the standalone -dbg.js");

t.true(taskUtil.setTag.calledWithExactly("resource standalone", taskUtil.STANDARD_TAGS.HasDebugVariant),
"The standalone file is tagged as having a generated debug variant");
t.false(taskUtil.setTag.calledWith(standalone, taskUtil.STANDARD_TAGS.IsDebugVariant),
"The standalone file is not tagged as a debug variant based on its name");
t.true(taskUtil.setTag.calledWithExactly("dbgResource standalone", taskUtil.STANDARD_TAGS.IsDebugVariant),
"The generated debug variant receives its debug tag");

t.is(workspace.write.callCount, 3, "workspace#write got called for the standalone file and its derived resources");
["resource standalone", "dbgResource standalone", "sourceMapResource standalone"].forEach((resName, idx) => {
t.is(workspace.write.getCall(idx).firstArg, resName, "workspace#write got called for expected resource");
});
});
Loading