Skip to content

Commit

Permalink
[FEATURE] ProjectBuilder: Add option to request flat build output
Browse files Browse the repository at this point in the history
New option 'flatOutput'. If set to true, libraries and theme-libraries
will omit the "/resources/<namespace>" directory structure in the build
output.
  • Loading branch information
RandomByte committed Jun 14, 2023
1 parent 78d4775 commit 4a8f2f0
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 18 deletions.
36 changes: 29 additions & 7 deletions lib/build/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class ProjectBuilder {
* Whether to create a build manifest file for the root project.
* This is currently only supported for projects of type 'library' and 'theme-library'
* No other dependencies can be included in the build result.
* @property {boolean} [flatOutput=false]
* Whether to write the build results into a flat directory structure, omitting the project
* namespace and the "resources" directory.
* This is currently only supported for projects of type 'library' and 'theme-library'.
* No other dependencies can be included in the build result.
* @property {Array.<string>} [includedTasks=[]] List of tasks to be included
* @property {Array.<string>} [excludedTasks=[]] List of tasks to be excluded.
* If the wildcard '*' is provided, only the included tasks will be executed.
Expand Down Expand Up @@ -164,10 +169,18 @@ class ProjectBuilder {
return filterProject(projectName);
});

if (this._buildContext.getBuildConfig().createBuildManifest && requestedProjects.length > 1) {
throw new Error(
`It is currently not supported to request the creation of a build manifest ` +
`while including any dependencies into the build result`);
if (requestedProjects.length > 1) {
const {createBuildManifest, flatOutput} = this._buildContext.getBuildConfig();
if (createBuildManifest) {
throw new Error(
`It is currently not supported to request the creation of a build manifest ` +
`while including any dependencies into the build result`);
}
if (flatOutput) {
throw new Error(
`It is currently not supported to request flat build output ` +
`while including any dependencies into the build result`);
}
}

const projectBuildContexts = await this._createRequiredBuildContexts(requestedProjects);
Expand Down Expand Up @@ -359,13 +372,22 @@ class ProjectBuilder {
const project = projectBuildContext.getProject();
const taskUtil = projectBuildContext.getTaskUtil();
const buildConfig = this._buildContext.getBuildConfig();
const {createBuildManifest, flatOutput} = buildConfig;

let readerStyle = "dist";
if (createBuildManifest) {
// Ensure buildtime (=namespaced) style when writing with a build manifest
readerStyle = "buildtime";
} else if (flatOutput) {
readerStyle = "flat";
}

const reader = project.getReader({
// Force buildtime (=namespaced) style when writing with a build manifest
style: taskUtil.isRootProject() && buildConfig.createBuildManifest ? "buildtime" : "dist"
style: readerStyle
});
const resources = await reader.byGlob("/**/*");

if (taskUtil.isRootProject() && buildConfig.createBuildManifest) {
if (createBuildManifest) {
// Create and write a build manifest metadata file
const {
default: createBuildManifest
Expand Down
12 changes: 12 additions & 0 deletions lib/build/helpers/BuildContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class BuildContext {
cssVariables = false,
jsdoc = false,
createBuildManifest = false,
flatOutput = false,
includedTasks = [], excludedTasks = [],
} = {}) {
if (!graph) {
Expand All @@ -33,12 +34,23 @@ class BuildContext {
`self-contained builds`);
}

if (flatOutput && !["library", "theme-library"].includes(graph.getRoot().getType())) {
throw new Error(
`Flat build output is currently not supported for projects of type ` +
graph.getRoot().getType());
}
if (createBuildManifest && flatOutput) {
throw new Error(
`Build manifest creation is not supported in conjunction with flat build output`);
}

this._graph = graph;
this._buildConfig = {
selfContained,
cssVariables,
jsdoc,
createBuildManifest,
flatOutput,
includedTasks,
excludedTasks,
};
Expand Down
37 changes: 28 additions & 9 deletions test/lib/build/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ test.serial("_writeResults: Create build manifest", async (t) => {
"createBuildManifest got called with correct project");
t.deepEqual(createBuildManifestStub.getCall(0).args[1], {
createBuildManifest: true,
flatOutput: false,
cssVariables: false,
excludedTasks: [],
includedTasks: [],
Expand Down Expand Up @@ -667,21 +668,28 @@ test.serial("_writeResults: Create build manifest", async (t) => {
esmock.purge(ProjectBuilder);
});

test("_writeResults: Do not create build manifest for non-root project", async (t) => {
const {ProjectBuilder, sinon} = t.context;
test.serial("_writeResults: Flat build output", async (t) => {
const {sinon, ProjectBuilder} = t.context;
t.context.getRootTypeStub = sinon.stub().returns("library");
const {graph, taskRepository} = t.context;

const builder = new ProjectBuilder({
graph, taskRepository,
buildConfig: {
createBuildManifest: true
flatOutput: true,
otherBuildConfig: "yes"
}
});

const dummyResources = [{
_resourceName: "resource.a",
getPath: () => "resource.a"
}, {
_resourceName: "resource.b",
getPath: () => "resource.b"
}, {
_resourceName: "resource.c",
getPath: () => "resource.c"
}];
const byGlobStub = sinon.stub().resolves(dummyResources);
const getReaderStub = sinon.stub().returns({
Expand All @@ -690,12 +698,12 @@ test("_writeResults: Do not create build manifest for non-root project", async (
const mockProject = getMockProject("library", "c");
mockProject.getReader = getReaderStub;

const getTagStub = sinon.stub().returns(false);
const getTagStub = sinon.stub().returns(false).onFirstCall().returns(true);
const projectBuildContextMock = {
getProject: () => mockProject,
getTaskUtil: () => {
return {
isRootProject: () => false,
isRootProject: () => true,
getTag: getTagStub,
STANDARD_TAGS: {
OmitFromBuildResult: "OmitFromBuildResultTag"
Expand All @@ -711,15 +719,26 @@ test("_writeResults: Do not create build manifest for non-root project", async (

t.is(getReaderStub.callCount, 1, "One reader requested");
t.deepEqual(getReaderStub.getCall(0).args[0], {
style: "dist"
style: "flat"
}, "Reader requested expected style");

t.is(getTagStub.callCount, 1, "TaskUtil#getTag got called once");
t.is(byGlobStub.callCount, 1, "One byGlob call");
t.is(byGlobStub.getCall(0).args[0], "/**/*", "byGlob called with expected pattern");

t.is(writerMock.write.callCount, 1, "Write got called once");
t.is(writerMock.write.getCall(0).args[0], dummyResources[0], "Write got called with only resource");
t.is(getTagStub.callCount, 3, "TaskUtil#getTag got called three times");
t.is(getTagStub.getCall(0).args[0], dummyResources[0], "TaskUtil#getTag called with first resource");
t.is(getTagStub.getCall(0).args[1], "OmitFromBuildResultTag", "TaskUtil#getTag called with correct tag value");
t.is(getTagStub.getCall(1).args[0], dummyResources[1], "TaskUtil#getTag called with second resource");
t.is(getTagStub.getCall(1).args[1], "OmitFromBuildResultTag", "TaskUtil#getTag called with correct tag value");
t.is(getTagStub.getCall(2).args[0], dummyResources[2], "TaskUtil#getTag called with third resource");
t.is(getTagStub.getCall(2).args[1], "OmitFromBuildResultTag", "TaskUtil#getTag called with correct tag value");

t.is(writerMock.write.callCount, 2, "Write got called twice");
t.is(writerMock.write.getCall(0).args[0], dummyResources[1], "Write got called with second resource");
t.is(writerMock.write.getCall(1).args[0], dummyResources[2], "Write got called with third resource");
});


test("_executeCleanupTasks", async (t) => {
const {graph, taskRepository, ProjectBuilder, sinon} = t.context;
const builder = new ProjectBuilder({graph, taskRepository});
Expand Down
59 changes: 57 additions & 2 deletions test/lib/build/helpers/BuildContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ test("getBuildConfig: Default values", (t) => {

t.deepEqual(buildContext.getBuildConfig(), {
selfContained: false,
flatOutput: false,
cssVariables: false,
jsdoc: false,
createBuildManifest: false,
Expand All @@ -63,6 +64,7 @@ test("getBuildConfig: Custom values", (t) => {
}
}, "taskRepository", {
selfContained: true,
flatOutput: false,
cssVariables: true,
jsdoc: true,
createBuildManifest: false,
Expand All @@ -72,6 +74,7 @@ test("getBuildConfig: Custom values", (t) => {

t.deepEqual(buildContext.getBuildConfig(), {
selfContained: true,
flatOutput: false,
cssVariables: true,
jsdoc: true,
createBuildManifest: false,
Expand Down Expand Up @@ -162,7 +165,59 @@ test("createBuildManifest supported for jsdoc build", (t) => {
});
});

test("getBuildOption", (t) => {
test("flatOutput not supported for type application", (t) => {
const err = t.throws(() => {
new BuildContext({
getRoot: () => {
return {
getType: () => "application"
};
}
}, "taskRepository", {
flatOutput: true
});
});
t.is(err.message,
"Flat build output is currently not supported for projects of type application",
"Threw with expected error message");
});

test("flatOutput not supported for type module", (t) => {
const err = t.throws(() => {
new BuildContext({
getRoot: () => {
return {
getType: () => "module"
};
}
}, "taskRepository", {
flatOutput: true
});
});
t.is(err.message,
"Flat build output is currently not supported for projects of type module",
"Threw with expected error message");
});

test("flatOutput not supported for createBuildManifest build", (t) => {
const err = t.throws(() => {
new BuildContext({
getRoot: () => {
return {
getType: () => "library"
};
}
}, "taskRepository", {
createBuildManifest: true,
flatOutput: true
});
});
t.is(err.message,
"Build manifest creation is not supported in conjunction with flat build output",
"Threw with expected error message");
});

test("getOption", (t) => {
const buildContext = new BuildContext("graph", "taskRepository", {
cssVariables: "value",
});
Expand All @@ -171,7 +226,7 @@ test("getBuildOption", (t) => {
"Returned correct value for build configuration 'cssVariables'");
t.is(buildContext.getOption("selfContained"), undefined,
"Returned undefined for build configuration 'selfContained' " +
"(not exposed as buold option)");
"(not exposed as build option)");
});

test("createProjectContext", async (t) => {
Expand Down

0 comments on commit 4a8f2f0

Please sign in to comment.