Skip to content
Draft
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
4 changes: 3 additions & 1 deletion internal/documentation/docs/pages/Builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ All available standard tasks are documented under **API -> @ui5/builder -> tasks
<sup>3</sup> Enabled in `self-contained` build, which disables `generateComponentPreload` and `generateLibraryPreload`
<sup>4</sup> Enabled for projects defining a [bundle configuration](./Configuration.md#custom-bundling)
<sup>5</sup> Can be enabled for framework projects via the `includeTask` option. For other projects, this task is skipped
<sup>6</sup> Disabled for the server due to a corresponding middleware producing the same output
<sup>6</sup> Disabled for the server (`ui5 serve` and `ui5 build for-server`) due to a corresponding middleware producing the same output
<sup>7</sup> Enabled for Specification Version 4.0 and lower, and for framework projects. For other projects using Specification Version 5.0 and higher, this task is skipped

### minify
Expand Down Expand Up @@ -227,6 +227,8 @@ The cache may grow over time. It can be deleted at any time to reclaim disk spac

::: info
By default, build caches created by `ui5 build` and `ui5 serve` are **separate and cannot be mixed**. Each command executes a distinct set of tasks, resulting in separate caches tailored to its specific use case. For more details on server caching, see the [UI5 Server documentation](./Server.md).

To pre-populate the cache that `ui5 serve` reuses, run `ui5 build for-server`. It executes the same task set as the server (for example, it skips `generateVersionInfo`, which the server generates on the fly) and only warms the cache without writing a build result to the destination directory. A subsequent `ui5 serve` then reuses those cached results.
:::


10 changes: 10 additions & 0 deletions packages/cli/lib/cli/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ build.builder = function(cli) {
builder: noop,
middlewares: [baseMiddleware]
})
.command("for-server",
"Build the project into the shared build cache for reuse by 'ui5 serve'. " +
"Aligns the task set with the server (e.g. skips 'generateVersionInfo') and does not " +
"write the build result to the destination directory.", {
handler: handleBuild,
builder: noop,
middlewares: [baseMiddleware]
})
.option("include-all-dependencies", {
describe: "Include all dependencies in the build result. " +
"This is equivalent to '--include-dependency \"*\"'",
Expand Down Expand Up @@ -215,6 +223,8 @@ async function handleBuild(argv) {
},
selfContained: command === "self-contained",
jsdoc: command === "jsdoc",
server: command === "for-server",
cacheOnly: command === "for-server",
includedTasks: argv["include-task"],
excludedTasks: argv["exclude-task"],
outputStyle: argv["output-style"],
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/test/lib/cli/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ function getDefaultBuilderArgs() {
createBuildManifest: false,
selfContained: false,
jsdoc: false,
server: false,
cacheOnly: false,
includedTasks: undefined,
excludedTasks: undefined,
outputStyle: "Default"
Expand Down Expand Up @@ -120,6 +122,19 @@ test.serial("ui5 build jsdoc", async (t) => {
t.deepEqual(builder.getCall(0).args[0], expectedBuilderArgs, "JSDoc build called with expected arguments");
});

test.serial("ui5 build for-server", async (t) => {
const {build, argv, builder, expectedBuilderArgs} = t.context;

argv._.push("for-server");

await build.handler(argv);

expectedBuilderArgs.server = true;
expectedBuilderArgs.cacheOnly = true;
t.deepEqual(builder.getCall(0).args[0], expectedBuilderArgs,
"for-server build called with expected arguments");
});

test.serial("ui5 build --framework-version", async (t) => {
const {build, argv, graphFromPackageDependenciesStub} = t.context;

Expand Down
12 changes: 11 additions & 1 deletion packages/project/lib/build/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class ProjectBuilder {
* @typedef {object} @ui5/project/build/ProjectBuilder~BuildConfiguration
* @property {boolean} [selfContained=false] Flag to activate self contained build
* @property {boolean} [jsdoc=false] Flag to activate JSDoc build
* @property {boolean} [server=false]
* Flag to activate a server-aligned build. Disables tasks whose output the server generates
* on the fly (currently <code>generateVersionInfo</code>) by default, matching the task set
* used by <code>@ui5/server</code>. The disabled tasks can still be re-enabled via
* <code>includedTasks</code>.
* @property {boolean} [createBuildManifest=false]
* Whether to create a build manifest file for the root project.
* This is currently only supported for projects of type 'library' and 'theme-library'
Expand Down Expand Up @@ -195,17 +200,22 @@ class ProjectBuilder {
* @param {boolean} [parameters.includeRootProject=true] Whether to include the root project
* @param {Array.<string|RegExp>} [parameters.includedDependencies=[]] List of dependencies to include
* @param {Array.<string|RegExp>} [parameters.excludedDependencies=[]] List of dependencies to exclude
* @param {@ui5/project/build/ProjectBuilder~DependencyIncludes} [parameters.dependencyIncludes]
* Alternative to the <code>includedDependencies</code> and <code>excludedDependencies</code> parameters.
* Allows for a more sophisticated configuration for defining which dependencies should be built.
* If this is provided, the other mentioned parameters are ignored.
* @param {AbortSignal} [parameters.signal] Signal to abort the build
* @param {Function} [projectBuiltCallback] Callback invoked after each project is built
* @returns {Promise<string[]>} Promise resolving with array of processed project names
*/
async build({
includeRootProject = true,
includedDependencies = [], excludedDependencies = [],
dependencyIncludes,
signal,
}, projectBuiltCallback) {
const requestedProjects = this._determineRequestedProjects(
includeRootProject, includedDependencies, excludedDependencies);
includeRootProject, includedDependencies, excludedDependencies, dependencyIncludes);
return await this.#build(requestedProjects, projectBuiltCallback, signal);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/project/lib/build/helpers/BuildContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class BuildContext {
constructor(graph, taskRepository, { // buildConfig
selfContained = false,
jsdoc = false,
server = false,
createBuildManifest = false,
outputStyle = OutputStyleEnum.Default,
includedTasks = [], excludedTasks = [],
Expand Down Expand Up @@ -69,6 +70,7 @@ class BuildContext {
this._buildConfig = {
selfContained,
jsdoc,
server,
createBuildManifest,
outputStyle,
includedTasks,
Expand Down
11 changes: 10 additions & 1 deletion packages/project/lib/build/helpers/composeTaskList.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* Sets specific tasks to be disabled by default, these tasks need to be included explicitly.
* Based on the selected build mode (selfContained|preload), different tasks are enabled.
* When the <code>server</code> flag is set, tasks whose output the server generates on the fly
* (currently <code>generateVersionInfo</code>) are disabled by default.
* Tasks can be enabled or disabled. The wildcard <code>*</code> is also supported and affects all tasks.
*
* @private
Expand All @@ -11,7 +13,7 @@
* Build configuration
* @returns {Array} List of tasks to be executed
*/
export default function composeTaskList(allTasks, {selfContained, jsdoc, includedTasks, excludedTasks}) {
export default function composeTaskList(allTasks, {selfContained, jsdoc, server, includedTasks, excludedTasks}) {
let selectedTasks = allTasks.reduce((list, key) => {
list[key] = true;
return list;
Expand Down Expand Up @@ -59,6 +61,13 @@ export default function composeTaskList(allTasks, {selfContained, jsdoc, include
selectedTasks.generateFlexChangesBundle = false;
}

if (server) {
// The 'versionInfo' server middleware generates the version info on the fly, so a server
// build does not run generateVersionInfo. Keeping it out of the default set here lets
// 'ui5 serve' and 'ui5 build for-server' share one build-cache entry.
selectedTasks.generateVersionInfo = false;
}

// Exclude tasks
for (let i = 0; i < excludedTasks.length; i++) {
const taskName = excludedTasks[i];
Expand Down
31 changes: 29 additions & 2 deletions packages/project/lib/graph/ProjectGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,14 @@ class ProjectGraph {
* part of the build result. If this is provided, the other mentioned parameters will be ignored.
* @param {boolean} [parameters.selfContained=false] Flag to activate self contained build
* @param {boolean} [parameters.jsdoc=false] Flag to activate JSDoc build
* @param {boolean} [parameters.server=false]
* Flag to activate a server-aligned build. Disables tasks whose output the server generates
* on the fly (currently <code>generateVersionInfo</code>) by default, so the build result
* matches what <code>@ui5/server</code> produces.
* @param {boolean} [parameters.cacheOnly=false]
* Only populate the build cache without writing the build result to <code>destPath</code>.
* Intended to warm the shared build cache that a subsequent <code>ui5 serve</code> reuses.
* When set, <code>destPath</code> and <code>cleanDest</code> are ignored.
* @param {boolean} [parameters.createBuildManifest=false]
* Whether to create a build manifest file for the root project.
* This is currently only supported for projects of type 'library' and 'theme-library'
Expand All @@ -725,7 +733,8 @@ class ProjectGraph {
destPath, cleanDest = false,
includedDependencies = [], excludedDependencies = [],
dependencyIncludes,
selfContained = false, jsdoc = false, createBuildManifest = false,
selfContained = false, jsdoc = false, server = false, createBuildManifest = false,
cacheOnly = false,
includedTasks = [], excludedTasks = [],
outputStyle = OutputStyleEnum.Default,
cache = Cache.Default,
Expand All @@ -745,13 +754,28 @@ class ProjectGraph {
graph: this,
taskRepository: await this._getTaskRepository(),
buildConfig: {
selfContained, jsdoc,
selfContained, jsdoc, server,
createBuildManifest,
includedTasks, excludedTasks, outputStyle,
cache
},
ui5DataDir,
});
if (cacheOnly) {
// Build to populate the cache only, without writing the result to a target directory.
// buildToTarget closes the CacheManager itself; the reader-based build() does not, so
// close it explicitly once the build has finished.
try {
await builder.build({
includeRootProject: true,
includedDependencies, excludedDependencies,
dependencyIncludes,
});
} finally {
builder.closeCacheManager();
}
return;
}
return await builder.buildToTarget({
destPath, cleanDest,
includedDependencies, excludedDependencies,
Expand Down Expand Up @@ -803,6 +827,9 @@ class ProjectGraph {
taskRepository: await this._getTaskRepository(),
buildConfig: {
selfContained, jsdoc,
// A serve is always a server-aligned build: disable tasks the server generates
// on the fly (e.g. generateVersionInfo). See composeTaskList.
server: true,
createBuildManifest,
includedTasks, excludedTasks,
outputStyle: OutputStyleEnum.Default,
Expand Down
Loading
Loading