From 79312fcefea1ea97c1f3d403ac4470f890069809 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Tue, 12 Dec 2023 14:46:41 +0100 Subject: [PATCH] [FEATURE] ProjectBuilder: Add `outputStyle` option to request flat build output (#624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New option `outputStyle`. Whether to omit the `/resources/` directory structure in the build output. Resolves https://github.com/SAP/ui5-tooling/issues/507 --------- Co-authored-by: Yavor Ivanov Co-authored-by: Yavor Ivanov Co-authored-by: Günter Klatt <57760635+KlattG@users.noreply.github.com> --- lib/build/ProjectBuilder.js | 69 +++++++++-- lib/build/helpers/BuildContext.js | 30 ++++- .../helpers/ProjectBuilderOutputStyle.js | 18 +++ lib/graph/ProjectGraph.js | 10 +- test/lib/build/ProjectBuilder.js | 74 +++++++---- test/lib/build/helpers/BuildContext.js | 116 ++++++++++++++++-- test/lib/build/helpers/ProjectBuildContext.js | 5 +- 7 files changed, 270 insertions(+), 52 deletions(-) create mode 100644 lib/build/helpers/ProjectBuilderOutputStyle.js diff --git a/lib/build/ProjectBuilder.js b/lib/build/ProjectBuilder.js index 010a9cbbb..fe39870d9 100644 --- a/lib/build/ProjectBuilder.js +++ b/lib/build/ProjectBuilder.js @@ -4,6 +4,7 @@ import BuildLogger from "@ui5/logger/internal/loggers/Build"; import composeProjectList from "./helpers/composeProjectList.js"; import BuildContext from "./helpers/BuildContext.js"; import prettyHrtime from "pretty-hrtime"; +import OutputStyleEnum from "./helpers/ProjectBuilderOutputStyle.js"; /** * @public @@ -24,6 +25,8 @@ 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 {module:@ui5/project/build/ProjectBuilderOutputStyle} [outputStyle=Default] + * Processes build results into a specific directory structure. * @property {Array.} [includedTasks=[]] List of tasks to be included * @property {Array.} [excludedTasks=[]] List of tasks to be excluded. * If the wildcard '*' is provided, only the included tasks will be executed. @@ -165,10 +168,13 @@ 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} = 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`); + } } const projectBuildContexts = await this._createRequiredBuildContexts(requestedProjects); @@ -360,13 +366,25 @@ class ProjectBuilder { const project = projectBuildContext.getProject(); const taskUtil = projectBuildContext.getTaskUtil(); const buildConfig = this._buildContext.getBuildConfig(); + const {createBuildManifest, outputStyle} = buildConfig; + // Output styles are applied only for the root project + const isRootProject = taskUtil.isRootProject(); + + let readerStyle = "dist"; + if (createBuildManifest || + (isRootProject && outputStyle === OutputStyleEnum.Namespace && project.getType() === "application")) { + // Ensure buildtime (=namespaced) style when writing with a build manifest or when explicitly requested + readerStyle = "buildtime"; + } else if (isRootProject && outputStyle === OutputStyleEnum.Flat) { + 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 @@ -386,6 +404,43 @@ class ProjectBuilder { } return target.write(resource); })); + + if (isRootProject && + outputStyle === OutputStyleEnum.Flat && + project.getType() !== "application" /* application type is with a default flat build output structure */) { + const namespace = project.getNamespace(); + const libraryResourcesPrefix = `/resources/${namespace}/`; + const testResourcesPrefix = "/test-resources/"; + const namespacedRegex = new RegExp(`/(resources|test-resources)/${namespace}`); + const processedResourcesSet = resources.reduce((acc, resource) => acc.add(resource.getPath()), new Set()); + + // If outputStyle === "Flat", then the FlatReader would have filtered + // some resources. We now need to get all of the available resources and + // do an intersection with the processed/bundled ones. + const defaultReader = project.getReader(); + const defaultResources = await defaultReader.byGlob("/**/*"); + const flatDefaultResources = defaultResources.map((resource) => ({ + flatResource: resource.getPath().replace(namespacedRegex, ""), + originalPath: resource.getPath(), + })); + + const skippedResources = flatDefaultResources.filter((resource) => { + return processedResourcesSet.has(resource.flatResource) === false; + }); + + skippedResources.forEach((resource) => { + if (resource.originalPath.startsWith(testResourcesPrefix)) { + this.#log.verbose( + `Omitting ${resource.originalPath} from build result. File is part of ${testResourcesPrefix}.` + ); + } else if (!resource.originalPath.startsWith(libraryResourcesPrefix)) { + this.#log.warn( + `Omitting ${resource.originalPath} from build result. ` + + `File is not within project namespace '${namespace}'.` + ); + } + }); + } } async _executeCleanupTasks(force) { diff --git a/lib/build/helpers/BuildContext.js b/lib/build/helpers/BuildContext.js index f10aa072d..8d8d1e1a3 100644 --- a/lib/build/helpers/BuildContext.js +++ b/lib/build/helpers/BuildContext.js @@ -1,4 +1,5 @@ import ProjectBuildContext from "./ProjectBuildContext.js"; +import OutputStyleEnum from "./ProjectBuilderOutputStyle.js"; /** * Context of a build process @@ -12,6 +13,7 @@ class BuildContext { cssVariables = false, jsdoc = false, createBuildManifest = false, + outputStyle = OutputStyleEnum.Default, includedTasks = [], excludedTasks = [], } = {}) { if (!graph) { @@ -21,10 +23,12 @@ class BuildContext { throw new Error(`Missing parameter 'taskRepository'`); } - if (createBuildManifest && !["library", "theme-library"].includes(graph.getRoot().getType())) { + const rootProjectType = graph.getRoot().getType(); + + if (createBuildManifest && !["library", "theme-library"].includes(rootProjectType)) { throw new Error( `Build manifest creation is currently not supported for projects of type ` + - graph.getRoot().getType()); + rootProjectType); } if (createBuildManifest && selfContained) { @@ -33,12 +37,34 @@ class BuildContext { `self-contained builds`); } + if (createBuildManifest && outputStyle === OutputStyleEnum.Flat) { + throw new Error( + `Build manifest creation is not supported in conjunction with flat build output`); + } + if (outputStyle !== OutputStyleEnum.Default) { + if (rootProjectType === "theme-library") { + throw new Error( + `${outputStyle} build output style is currently not supported for projects of type` + + `theme-library since they commonly have more than one namespace. ` + + `Currently only the Default output style is supported for this project type.` + ); + } + if (rootProjectType === "module") { + throw new Error( + `${outputStyle} build output style is currently not supported for projects of type` + + `module. Their path mappings configuration can't be mapped to any namespace.` + + `Currently only the Default output style is supported for this project type.` + ); + } + } + this._graph = graph; this._buildConfig = { selfContained, cssVariables, jsdoc, createBuildManifest, + outputStyle, includedTasks, excludedTasks, }; diff --git a/lib/build/helpers/ProjectBuilderOutputStyle.js b/lib/build/helpers/ProjectBuilderOutputStyle.js new file mode 100644 index 000000000..62d0bbb67 --- /dev/null +++ b/lib/build/helpers/ProjectBuilderOutputStyle.js @@ -0,0 +1,18 @@ +/** + * Processes build results into a specific directory structure. + * + * @public + * @readonly + * @enum {string} + * @property {string} Default The default directory structure for every project type. + * For applications this is identical to "Flat" and for libraries to "Namespace". + * Other types have a more distinct default output style. + * @property {string} Flat Omits the project namespace and the "resources" directory. + * @property {string} Namespace Respects project namespace and the "resources" directory. + * @module @ui5/project/build/ProjectBuilderOutputStyle + */ +export default { + Default: "Default", + Flat: "Flat", + Namespace: "Namespace" +}; diff --git a/lib/graph/ProjectGraph.js b/lib/graph/ProjectGraph.js index af9861e0d..38ff3afff 100644 --- a/lib/graph/ProjectGraph.js +++ b/lib/graph/ProjectGraph.js @@ -1,6 +1,8 @@ +import OutputStyleEnum from "../build/helpers/ProjectBuilderOutputStyle.js"; import {getLogger} from "@ui5/logger"; const log = getLogger("graph:ProjectGraph"); + /** * A rooted, directed graph representing a UI5 project, its dependencies and available extensions. *

@@ -540,7 +542,8 @@ class ProjectGraph { * This is currently only supported for projects of type 'library' and 'theme-library' * @param {Array.} [parameters.includedTasks=[]] List of tasks to be included * @param {Array.} [parameters.excludedTasks=[]] List of tasks to be excluded. - * If the wildcard '*' is provided, only the included tasks will be executed. + * @param {module:@ui5/project/build/ProjectBuilderOutputStyle} [parameters.outputStyle=Default] + * Processes build results into a specific directory structure. * @returns {Promise} Promise resolving to undefined once build has finished */ async build({ @@ -548,7 +551,8 @@ class ProjectGraph { includedDependencies = [], excludedDependencies = [], dependencyIncludes, selfContained = false, cssVariables = false, jsdoc = false, createBuildManifest = false, - includedTasks = [], excludedTasks = [] + includedTasks = [], excludedTasks = [], + outputStyle = OutputStyleEnum.Default }) { this.seal(); // Do not allow further changes to the graph if (this._built) { @@ -566,7 +570,7 @@ class ProjectGraph { buildConfig: { selfContained, cssVariables, jsdoc, createBuildManifest, - includedTasks, excludedTasks, + includedTasks, excludedTasks, outputStyle, } }); await builder.build({ diff --git a/test/lib/build/ProjectBuilder.js b/test/lib/build/ProjectBuilder.js index 7c4177d8e..64b36ab85 100644 --- a/test/lib/build/ProjectBuilder.js +++ b/test/lib/build/ProjectBuilder.js @@ -3,6 +3,7 @@ import sinonGlobal from "sinon"; import path from "node:path"; import esmock from "esmock"; import {setLogLevel} from "@ui5/logger"; +import OutputStyleEnum from "../../../lib/build/helpers/ProjectBuilderOutputStyle.js"; function noop() {} @@ -508,7 +509,7 @@ test("_writeResults", async (t) => { } }); - const dummyResources = [{ + const mockResources = [{ _resourceName: "resource.a", getPath: () => "resource.a" }, { @@ -518,7 +519,7 @@ test("_writeResults", async (t) => { _resourceName: "resource.c", getPath: () => "resource.c" }]; - const byGlobStub = sinon.stub().resolves(dummyResources); + const byGlobStub = sinon.stub().resolves(mockResources); const getReaderStub = sinon.stub().returns({ byGlob: byGlobStub }); @@ -553,16 +554,16 @@ test("_writeResults", async (t) => { t.is(byGlobStub.getCall(0).args[0], "/**/*", "byGlob called with expected pattern"); 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[0], mockResources[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[0], mockResources[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[0], mockResources[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"); + t.is(writerMock.write.getCall(0).args[0], mockResources[1], "Write got called with second resource"); + t.is(writerMock.write.getCall(1).args[0], mockResources[2], "Write got called with third resource"); }); test.serial("_writeResults: Create build manifest", async (t) => { @@ -587,7 +588,7 @@ test.serial("_writeResults: Create build manifest", async (t) => { } }); - const dummyResources = [{ + const mockResources = [{ _resourceName: "resource.a", getPath: () => "resource.a" }, { @@ -597,7 +598,7 @@ test.serial("_writeResults: Create build manifest", async (t) => { _resourceName: "resource.c", getPath: () => "resource.c" }]; - const byGlobStub = sinon.stub().resolves(dummyResources); + const byGlobStub = sinon.stub().resolves(mockResources); const getReaderStub = sinon.stub().returns({ byGlob: byGlobStub }); @@ -636,6 +637,7 @@ test.serial("_writeResults: Create build manifest", async (t) => { "createBuildManifest got called with correct project"); t.deepEqual(createBuildManifestStub.getCall(0).args[1], { createBuildManifest: true, + outputStyle: OutputStyleEnum.Default, cssVariables: false, excludedTasks: [], includedTasks: [], @@ -652,50 +654,57 @@ test.serial("_writeResults: Create build manifest", async (t) => { }, "Build manifest resource has been created with correct arguments"); 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[0], mockResources[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[0], mockResources[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[0], mockResources[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, 3, "Write got called three times"); t.is(writerMock.write.getCall(0).args[0], "build manifest resource", "Write got called with build manifest"); - t.is(writerMock.write.getCall(1).args[0], dummyResources[1], "Write got called with second resource"); - t.is(writerMock.write.getCall(2).args[0], dummyResources[2], "Write got called with third resource"); + t.is(writerMock.write.getCall(1).args[0], mockResources[1], "Write got called with second resource"); + t.is(writerMock.write.getCall(2).args[0], mockResources[2], "Write got called with third resource"); 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 + outputStyle: OutputStyleEnum.Flat, + otherBuildConfig: "yes" } }); - const dummyResources = [{ + const mockResources = [{ _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 byGlobStub = sinon.stub().resolves(mockResources); const getReaderStub = sinon.stub().returns({ byGlob: byGlobStub }); - const mockProject = getMockProject("library", "c"); + const mockProject = getMockProject("library", "a"); 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" @@ -709,17 +718,28 @@ test("_writeResults: Do not create build manifest for non-root project", async ( await builder._writeResults(projectBuildContextMock, writerMock); - t.is(getReaderStub.callCount, 1, "One reader requested"); + t.is(getReaderStub.callCount, 2, "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, 2, "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], mockResources[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], mockResources[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], mockResources[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], mockResources[1], "Write got called with second resource"); + t.is(writerMock.write.getCall(1).args[0], mockResources[2], "Write got called with third resource"); }); + test("_executeCleanupTasks", async (t) => { const {graph, taskRepository, ProjectBuilder, sinon} = t.context; const builder = new ProjectBuilder({graph, taskRepository}); diff --git a/test/lib/build/helpers/BuildContext.js b/test/lib/build/helpers/BuildContext.js index 34b9d2f47..cc09a7cd8 100644 --- a/test/lib/build/helpers/BuildContext.js +++ b/test/lib/build/helpers/BuildContext.js @@ -1,5 +1,6 @@ import test from "ava"; import sinon from "sinon"; +import OutputStyleEnum from "../../../../lib/build/helpers/ProjectBuilderOutputStyle.js"; test.afterEach.always((t) => { sinon.restore(); @@ -22,30 +23,42 @@ test("Missing parameters", (t) => { }); test("getRootProject", (t) => { - const buildContext = new BuildContext({ - getRoot: () => "pony" - }, "taskRepository"); + const rootProjectStub = sinon.stub() + .onFirstCall().returns({getType: () => "library"}) + .returns("pony"); + const graph = {getRoot: rootProjectStub}; + const buildContext = new BuildContext(graph, "taskRepository"); t.is(buildContext.getRootProject(), "pony", "Returned correct value"); }); test("getGraph", (t) => { - const buildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository"); - t.is(buildContext.getGraph(), "graph", "Returned correct value"); + t.deepEqual(buildContext.getGraph(), graph, "Returned correct value"); }); test("getTaskRepository", (t) => { - const buildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository"); t.is(buildContext.getTaskRepository(), "taskRepository", "Returned correct value"); }); test("getBuildConfig: Default values", (t) => { - const buildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository"); t.deepEqual(buildContext.getBuildConfig(), { selfContained: false, + outputStyle: OutputStyleEnum.Default, cssVariables: false, jsdoc: false, createBuildManifest: false, @@ -63,6 +76,7 @@ test("getBuildConfig: Custom values", (t) => { } }, "taskRepository", { selfContained: true, + outputStyle: OutputStyleEnum.Namespace, cssVariables: true, jsdoc: true, createBuildManifest: false, @@ -72,6 +86,7 @@ test("getBuildConfig: Custom values", (t) => { t.deepEqual(buildContext.getBuildConfig(), { selfContained: true, + outputStyle: OutputStyleEnum.Namespace, cssVariables: true, jsdoc: true, createBuildManifest: false, @@ -162,8 +177,79 @@ test("createBuildManifest supported for jsdoc build", (t) => { }); }); -test("getBuildOption", (t) => { - const buildContext = new BuildContext("graph", "taskRepository", { +test("outputStyle='Namespace' supported for type application", (t) => { + t.notThrows(() => { + new BuildContext({ + getRoot: () => { + return { + getType: () => "application" + }; + } + }, "taskRepository", { + outputStyle: OutputStyleEnum.Namespace + }); + }); +}); + +test("outputStyle='Flat' not supported for type theme-library", (t) => { + const err = t.throws(() => { + new BuildContext({ + getRoot: () => { + return { + getType: () => "theme-library" + }; + } + }, "taskRepository", { + outputStyle: OutputStyleEnum.Flat + }); + }); + t.is(err.message, + "Flat build output style is currently not supported for projects of typetheme-library since they" + + " commonly have more than one namespace. Currently only the Default output style is supported" + + " for this project type."); +}); + +test("outputStyle='Flat' not supported for type module", (t) => { + const err = t.throws(() => { + new BuildContext({ + getRoot: () => { + return { + getType: () => "module" + }; + } + }, "taskRepository", { + outputStyle: OutputStyleEnum.Flat + }); + }); + t.is(err.message, + "Flat build output style is currently not supported for projects of typemodule. " + + "Their path mappings configuration can't be mapped to any namespace.Currently only the " + + "Default output style is supported for this project type."); +}); + +test("outputStyle='Flat' not supported for createBuildManifest build", (t) => { + const err = t.throws(() => { + new BuildContext({ + getRoot: () => { + return { + getType: () => "library" + }; + } + }, "taskRepository", { + createBuildManifest: true, + outputStyle: OutputStyleEnum.Flat + }); + }); + 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 graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository", { cssVariables: "value", }); @@ -171,11 +257,14 @@ 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) => { - const buildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository"); const projectBuildContext = await buildContext.createProjectContext({ project: { getName: () => "project", @@ -188,7 +277,10 @@ test("createProjectContext", async (t) => { }); test("executeCleanupTasks", async (t) => { - const buildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const buildContext = new BuildContext(graph, "taskRepository"); const executeCleanupTasks = sinon.stub().resolves(); diff --git a/test/lib/build/helpers/ProjectBuildContext.js b/test/lib/build/helpers/ProjectBuildContext.js index 5cf8bcf24..03f9a5683 100644 --- a/test/lib/build/helpers/ProjectBuildContext.js +++ b/test/lib/build/helpers/ProjectBuildContext.js @@ -381,7 +381,10 @@ test.serial("createProjectContext", async (t) => { create: sinon.stub().resolves(taskRunner) } }); - const testBuildContext = new BuildContext("graph", "taskRepository"); + const graph = { + getRoot: () => ({getType: () => "library"}), + }; + const testBuildContext = new BuildContext(graph, "taskRepository"); const projectContext = await testBuildContext.createProjectContext({ project