Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] ProjectBuilder: Add outputStyle option to request flat build output #624

Merged
merged 29 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f1c61c9
[FEATURE] ProjectBuilder: Add option to request flat build output
RandomByte Jun 14, 2023
eaa70e6
[INTERNAL] BuildContext: Do not allow flat build output for theme-lib…
RandomByte Nov 16, 2023
0413d30
Add flatOutput to the ProjectGraph's build dependencies
d3xter666 Nov 27, 2023
3961054
Handle flatOutput default value and errors
d3xter666 Nov 28, 2023
3005524
Add JSDoc for application type for flatOutput
d3xter666 Nov 28, 2023
92cb9c9
Enhance tests for type=application
d3xter666 Nov 28, 2023
48e1877
Log skipped files when doing flat output
d3xter666 Nov 29, 2023
3d8133a
Refactor
d3xter666 Nov 29, 2023
5d352cc
Fix tests
d3xter666 Nov 29, 2023
525113d
Skip checks for flat project types
d3xter666 Nov 29, 2023
eb9fb41
Adjust docs
d3xter666 Nov 30, 2023
e4f542c
Migrate boolean flag for flat output to enum
d3xter666 Nov 30, 2023
aa1d4be
Fix jsdoc issues
d3xter666 Nov 30, 2023
2607525
JSDoc adjustments
d3xter666 Nov 30, 2023
8008da1
Start using "real" Enum for output style
d3xter666 Nov 30, 2023
0f2bf86
Enable Namespaced builds for applications
d3xter666 Dec 4, 2023
57b3513
Enhance tests to support application's namespaced build
d3xter666 Dec 4, 2023
d8fb285
Apply output styles only for the root project
d3xter666 Dec 7, 2023
4f1e754
Validate unsupported combinations of project type & output style
d3xter666 Dec 7, 2023
7487618
Enhance tests for the new behaviour
d3xter666 Dec 7, 2023
51e22f1
Update lib/build/ProjectBuilder.js
d3xter666 Dec 8, 2023
76635e3
Update lib/build/ProjectBuilder.js
d3xter666 Dec 8, 2023
e844df3
Update lib/build/helpers/BuildContext.js
d3xter666 Dec 8, 2023
f1e6ed4
Update lib/build/helpers/BuildContext.js
d3xter666 Dec 8, 2023
74f5204
Update lib/build/helpers/ProjectBuilderOutputStyle.js
d3xter666 Dec 8, 2023
ac1877d
Update lib/build/helpers/ProjectBuilderOutputStyle.js
d3xter666 Dec 8, 2023
f7f1862
JSDoc adjustments
d3xter666 Dec 8, 2023
5890943
Fix JSDocs & Tests
d3xter666 Dec 8, 2023
577696f
Update lib/build/helpers/ProjectBuilderOutputStyle.js
d3xter666 Dec 11, 2023
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
73 changes: 66 additions & 7 deletions lib/build/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,6 +25,12 @@ 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.
* "Flat" omits the project namespace and the "resources" directory. This
* is currently only supported for projects of type 'library'.
* Projects of type 'application' always result in a flat output.
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* 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 @@ -165,10 +172,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);
Expand Down Expand Up @@ -360,13 +370,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 = this._graph.getRoot().getName() === project.getName();
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved

let readerStyle = "dist";
if (createBuildManifest ||
(isRootProject && outputStyle === OutputStyleEnum.Namespace && project.getType() === "application")) {
// Ensure buildtime (=namespaced) style when writing with a build manifest
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -386,6 +408,43 @@ class ProjectBuilder {
}
return target.write(resource);
}));

if (isRootProject &&
outputStyle === OutputStyleEnum.Flat &&
project.getType() !== "application" /* application type is with a flat source 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) {
Expand Down
33 changes: 31 additions & 2 deletions lib/build/helpers/BuildContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ProjectBuildContext from "./ProjectBuildContext.js";
import OutputStyleEnum from "./ProjectBuilderOutputStyle.js";

/**
* Context of a build process
Expand All @@ -12,6 +13,7 @@ class BuildContext {
cssVariables = false,
jsdoc = false,
createBuildManifest = false,
outputStyle = OutputStyleEnum.Default,
includedTasks = [], excludedTasks = [],
} = {}) {
if (!graph) {
Expand All @@ -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) {
Expand All @@ -33,12 +37,37 @@ 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 is currently not supported` +
` since a theme-library almost always has more than one namespace.` +
`You can use only the Default build output for this project type.`
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
);
}
if (rootProjectType === "module") {
throw new Error(
`${outputStyle} build output is currently not supported` +
` since modules have explicit path mappings configured and no namespace concept.` +
`You can use only the Default build output for this project type.`
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
);
}
}


// Enforce boolean and default value, but do it here as we're also interested
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
// in the initial value above
this._graph = graph;
this._buildConfig = {
selfContained,
cssVariables,
jsdoc,
createBuildManifest,
outputStyle,
includedTasks,
excludedTasks,
};
Expand Down
18 changes: 18 additions & 0 deletions lib/build/helpers/ProjectBuilderOutputStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Processes build results into a specific directory structure.
*
* @public
* @readonly
* @enum {string}
* @property {string} Default Default directory structure for every project type.
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* For applications: "Flat", for libraries: "Namespace", for theme-libraries: "Namespace", etc.
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* @property {string} Flat Omits the project namespace and the "resources" directory.
Copy link
Contributor

Choose a reason for hiding this comment

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

These string variable names are in uppercase - do we have any guidelines which case to use?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's the same as the following module we already have: https://github.com/SAP/ui5-project/blob/main/lib/ui5Framework/maven/CacheMode.js

Basically, those are enums and they are expected to start with a capital letter. It's actually a workaround we use as JavaScript does not have native Enum type.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ack

* @property {string} Namespace Respects project namespace and the "resources" directory.
* Not applicable for projects of type 'application'.
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* @module @ui5/project/build/ProjectBuilderOutputStyle
*/
export default {
Default: "Default",
Flat: "Flat",
Namespace: "Namespace"
};
14 changes: 11 additions & 3 deletions lib/graph/ProjectGraph.js
Original file line number Diff line number Diff line change
@@ -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.
* <br><br>
Expand Down Expand Up @@ -540,15 +542,21 @@ class ProjectGraph {
* This is currently only supported for projects of type 'library' and 'theme-library'
* @param {Array.<string>} [parameters.includedTasks=[]] List of tasks to be included
* @param {Array.<string>} [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.
* "Flat" omits the project namespace and the "resources" directory. This
* is currently only supported for projects of type 'library'.
* Projects of type 'application' always result in a flat output.
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* No other dependencies can be included in the build result.
* @returns {Promise} Promise resolving to <code>undefined</code> once build has finished
*/
async build({
destPath, cleanDest = false,
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) {
Expand All @@ -566,7 +574,7 @@ class ProjectGraph {
buildConfig: {
selfContained, cssVariables, jsdoc,
createBuildManifest,
includedTasks, excludedTasks,
includedTasks, excludedTasks, outputStyle,
}
});
await builder.build({
Expand Down
Loading
Loading