- Start Date: 2018-08-28
- RFC PR: #54
- Issue: -
- Affected components
Add a feature for basic customization of how a single UI5 project is being built.
Currently the UI5 build is only capable of building UI5 projects of types "application" and "library" with a fixed set of tasks to be executed.
A UI5 project (for example a library) may want to add build steps. For this, an extensibility mechanism is needed.
While multiple UI5 projects may require the same kind of "customized" build, easy reuse capabilities are not in focus of this RFC. RFC 0001 focuses more on that.
This shall be a preliminary solution to allow for basic extensibility and to learn about the different needs and use cases in that area before proceeding with RFC 0001 or similar concepts.
In a projects ui5.yaml, a new configuration option should be added to define additional tasks that shall be executed at a specific time during the build process of a project. This configuration shall only affect the project it belongs to. The build process of any of the other projects (e.g. project dependencies) shall be unaffected by this configuration.
A task may require certain tasks to be executed before and after it. This shall be configurable in a simple but less generic way. See RFC 0001 for a concept of a more generic handling.
A project configuration might look like this:
specVersion: "0.1"
type: library
metadata:
name: my.library
builder:
customTasks:
- name: babel
beforeTask: generateComponentPreload
- name: generateMarkdownFiles
afterTask: uglify
configuration:
color: blueWhen building "my.library", this will execute the custom task babel before the "standard" task generateComponentPreload and generateMarkdownFiles after uglify. This means that for example generateComponentPreload and all following tasks can work with the resources created or modified by the babel task.
This section is partially equal to what is outlined in RFC 0001.
Custom task implementations have similar characteristics than other possible "extensions" of the UI5 Build and Development Tooling. Examples for other extensions include "Shims" (see RFC 0002), server middlewares and translators.
Therefore a somewhat generic concept for dealing with extensions is needed.
To separate "UI5 Projects" (i.e. things that represent UI5-artifacts for the browser) from tooling specific things like "extensions", an additional attribute "kind" is added to the ui5.yaml.
A custom task (a.k.a. "task extension") will consist of at least a ui5.yaml defining it as an extension and a JavaScript implementation.
ui5.yaml:
specVersion: "0.1"
kind: extension
type: task
metadata:
name: generateMarkdownFiles
task:
path: lib/tasks/generateMarkdownFiles.jslib/tasks/generateMarkdownFiles.js:
const markdownGenerator = require("./markdownGenerator");
module.exports = function({workspace, options}) {
return workspace.byGlob("**/*.txt")
.then((textResources) => {
return markdownGenerator({
resources: textResources
});
})
.then((markdownResources) => {
return Promise.all(markdownResources.map((resource) => {
return workspace.write(resource);
}));
});
};A task extension might be a standalone module or part of a project.
If the extension is part of a project, the single ui5.yaml for the above example might look like this:
specVersion: "0.1"
kind: project
type: library
metadata:
name: my.library
builder:
customTasks:
- name: generateMarkdownFiles
afterTask: uglify
configuration:
color: blue
---
specVersion: "0.1"
kind: extension
type: task
metadata:
name: generateMarkdownFiles
task:
path: lib/tasks/generateMarkdownFiles.jsIn this case the extension is no dependency of any kind but automatically collected and processed with the processing of the project.
The AbstractBuilder will detect the custom task configuration of the project my.library and inject the tasks into the build execution.
A custom task implementation needs to return a function with the following signature (written in JSDoc):
/**
* Custom task example
*
* @param {Object} parameters Parameters
* @param {DuplexCollection} parameters.workspace DuplexCollection to read and write files
* @param {AbstractReader} parameters.dependencies Reader or Collection to read dependency files
* @param {Object} parameters.options Options
* @param {string} parameters.options.projectName Project name
* @param {string} [parameters.options.configuration] Task configuration if given in ui5.yaml
* @returns {Promise<undefined>} Promise resolving with undefined once data has been written
*/
module.exports = function({workspace, options}) {
// [...]
};- Documentation about how to implement custom tasks
- Explanation of the task/processor concept
Custom task configurations might break with future changes to the Application- and LibraryBuilder due to renaming or reordering of the standard tasks.
There are ways to consume (and thereby possibly adapt) the existing tooling through its API via taskrunners such as grunt or gulp, or using a custom node.js script. But this offers only limited possibilities, especially when it comes to building transient dependencies.
RFC 0001 may offer a more generic way to tackle this but requires additional concept and evaluation work. This RFC (0004) should not prevent the implementation of RFC 0001 in the future.
- Detailed task signature
- Should the whole
projectobject be handed over to custom tasks?
- Should the whole