Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/documentation/docs/pages/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A projects UI5 CLI configuration is typically located in a [YAML](https://yaml.org/) file named `ui5.yaml`, located in the root directory.

::: info Info
This document describes the configuration of UI5 CLI-based projects and extensions. It represents **[Specification Version 3.0](#specification-versions)**.
This document describes the configuration of UI5 CLI-based projects and extensions. It represents **[Specification Version 5.0](#specification-versions)**.

:::

Expand Down
74 changes: 49 additions & 25 deletions packages/documentation/docs/pages/Project.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,33 @@ Types define how a project can be configured and how it is built. A type orchest

Also see [UI5 Project: Configuration](./Configuration.md#general-configuration)

### component
*Available since [Specification Version 5.0](./Configuration.md#specification-version-50)*

> **Note:** The UI5 CLI project type `component` differs from the `sap.app/type` runtime property with the same name defined in the `manifest.json` file. In most cases, a CLI project of type `component` is still a runtime `application`. For more information, see the [manifest documentation](https://ui5.sap.com/#/topic/be0cf40f61184b358b5faedaec98b2da.html#loiobe0cf40f61184b358b5faedaec98b2da/section_sap_app).

Projects of the `component` type cover a range of use cases beyond typical standalone UI5 applications:

- **Application components:** These are typical UI5 applications designed to run in container-like applications such as the SAP Fiori launchpad (FLP). They generally inherit from `sap.ui.core.UIComponent` (or a subclass) and define the `manifest.json` property `sap.app/type: application`.
- **Reusable UI components:** These provide UI elements or features that you can embed in different contexts. They typically inherit from `sap.ui.core.UIComponent` and define the `manifest.json` property `sap.app/type: component`.
- **Faceless components:** These provide functionality without a user interface. They are defined with `manifest.json` property `sap.app/type: component` and inherit from `sap.ui.core.Component` (not `UIComponent`).

For more information, see [Components](https://ui5.sap.com/#/topic/958ead51e2e94ab8bcdc90fb7e9d53d0).

To allow multiple component projects to coexist in the same environment, each project is served under its own namespace, for example `/resources/my/bookstore/admin`. In contrast, `application`-type projects act as root projects and are served at `/`, without a namespace.

By default, component projects use the same directory structure as library projects: they include `src` and `test` directories in the root. Both directories can have either a flat or a namespace structure. If you use a flat structure, the project namespace derives from the `sap.app/id` property in the `manifest.json`.

A component project must contain both, a `Component.js` and a `manifest.json` file.

Unlike `application`-type projects, component projects typically don't have dedicated `index.html` files in their regular resources (`src/`). However, you can still run them standalone. You can do this by using a dedicated HTML file located in their test resources or by declaring a development dependency to an application-type project that can serve the component, such as the FLP sandbox.

Component projects support all [output styles](#build-output-style) that library projects currently support. This allows a deployment where you can omit the namespace from the final directory structure using the output style: `flat`.

For more details, see also [RFC 0018 Component Type](https://github.com/UI5/cli/blob/rfc-component-type/rfcs/0018-component-type.md#rfc-0018-component-type).

### application
Projects of type `application` are typically the main or root project. In a projects dependency tree, there should only be one project of type `application`. If multiple are found, those further away from the root are ignored.
Projects of the `application` type typically serve as the main or root project. In a project's dependency tree, there shouldn't be more than one project of this type. If the system detects additional application projects, it ignores those that are further away from the root.

The source directory of an application (typically named `webapp`) is mapped to the virtual root path `/`.

Expand All @@ -21,12 +46,12 @@ An applications source directory may or may not contain a `Component.js` file. I
### library
UI5 libraries are often referred to as reuse-, custom- or [control libraries](https://github.com/SAP/openui5/blob/-/docs/controllibraries.md). They are a key component in sharing code across multiple projects in UI5.

A project of type `library` must have a source directory (typically named `src`). It may also feature a "test" directory. These directories are mapped to the virtual directories `/resources` for the sources and `/test-resources` for the test resources.
A project of the `library` type must have a source directory (typically named `src`). It may also feature a "test" directory. These directories are mapped to the virtual directories `/resources` for the sources and `/test-resources` for the test resources.

These directories should contain a directory structure representing the namespace of the library (e.g. `src/my/first/library`) to prevent name clashes between the resources of different libraries.

### theme-library
*Available since [Specification Version](./Configuration.md#specification-versions) 1.1*
*Available since [Specification Version 1.1](./Configuration.md#specification-version-11)*

UI5 theme libraries provide theming resources for the controls of one or multiple libraries.

Expand All @@ -44,28 +69,27 @@ The _Output Style_ offers you control over your project's build output folder. N

In the table below you can find the available combinations of project type & output style.

| Project Type / Requested Output Style | Resulting Style |
|---|---|
| **application** | |
| `Default` | Root project is written `Flat`-style. ^1^ |
| `Flat` | Same as `Default`. |
| `Namespace` | Root project is written `Namespace`-style (resources are prefixed with the project's namespace). ^1^ |
| **library** | |
| `Default` | Root project is written `Namespace`-style. ^1^ |
| `Flat` | Root project is written `Flat`-style (without its namespace, logging warnings for resources outside of it). ^1^ |
| `Namespace` | Same as `Default`. |
| **theme-library** | |
| `Default` | Root project is written in the style of the sources (multiple namespaces). ^1^ |
| `Flat` | **Unsupported** ^2^ |
| `Namespace` | **Unsupported** ^2^ |
| **module** | |
| `Default` | Root project is written with the [configured paths](https://ui5.github.io/cli/v5/pages/Configuration/#available-path-mappings). ^1^ |
| `Flat` | **Unsupported** ^3^ |
| `Namespace` | **Unsupported** ^3^ |

^1^ The Output Style is only applied to the root project's output folder structure. Any dependencies included in the build would retain their `Default` output style.
^2^ Theme libraries in most cases have more than one namespace.
^3^ Modules have explicit path mappings configured and no namespace concept.
| Project Type | Requested Output Style | Resulting Style |
| :--- | :--- | :--- |
| **component** | `Default` | Root project is written `Namespace`-style.¹ |
| | `Flat` | Root project is written `Flat`-style (without its namespace, logging warnings for resources outside of it).¹ |
| | `Namespace` | Same as `Default`. |
| **application** | `Default` | Root project is written `Flat`-style.¹ |
| | `Flat` | Same as `Default`. |
| | `Namespace` | Root project is written `Namespace`-style (resources are prefixed with the project's namespace).¹ |
| **library** | `Default` | Root project is written `Namespace`-style.¹ |
| | `Flat` | Root project is written `Flat`-style (without its namespace, logging warnings for resources outside of it).¹ |
| | `Namespace` | Same as `Default`. |
| **theme-library** | `Default` | Root project is written in the style of the sources (multiple namespaces).¹ |
| | `Flat` | **Unsupported** ² |
| | `Namespace` | **Unsupported** ² |
| **module** | `Default` | Root project is written with the [configured paths](https://ui5.github.io/cli/v5/pages/Configuration/#available-path-mappings).¹ |
| | `Flat` | **Unsupported** ³ |
| | `Namespace` | **Unsupported** ³ |

¹ The output style is only applied to the root project's output folder structure. Any dependencies included in the build would retain their `Default` output style.
² Theme libraries in most cases have more than one namespace.
³ Modules have explicit path mappings configured and no namespace concept.


<div style="margin: 1rem 0;">
Expand Down
3 changes: 3 additions & 0 deletions packages/project/lib/build/TaskRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class TaskRunner {
case "library":
buildDefinition = "./definitions/library.js";
break;
case "component":
buildDefinition = "./definitions/component.js";
break;
case "module":
buildDefinition = "./definitions/module.js";
break;
Expand Down
128 changes: 128 additions & 0 deletions packages/project/lib/build/definitions/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {enhancePatternWithExcludes} from "./_utils.js";
import {enhanceBundlesWithDefaults} from "../../validation/validator.js";

/**
* Get tasks and their configuration for a given component project
*
* @private
* @param {object} parameters
* @param {object} parameters.project
* @param {object} parameters.taskUtil
* @param {Function} parameters.getTask
*/
export default function({project, taskUtil, getTask}) {
const tasks = new Map();
tasks.set("escapeNonAsciiCharacters", {
options: {
encoding: project.getPropertiesFileSourceEncoding(),
pattern: "/**/*.properties"
}
});

tasks.set("replaceCopyright", {
options: {
copyright: project.getCopyright(),
pattern: "/**/*.{js,json}"
}
});

tasks.set("replaceVersion", {
options: {
version: project.getVersion(),
pattern: "/**/*.{js,json}"
}
});

// Support rules should not be minified to have readable code in the Support Assistant
const minificationPattern = ["/**/*.js", "!**/*.support.js"];
const minificationExcludes = project.getMinificationExcludes();
if (minificationExcludes.length) {
enhancePatternWithExcludes(minificationPattern, minificationExcludes, "/resources/");
}

tasks.set("minify", {
options: {
pattern: minificationPattern
}
});

tasks.set("enhanceManifest", {});

tasks.set("generateFlexChangesBundle", {});

const bundles = project.getBundles();
const existingBundleDefinitionNames =
bundles.map(({bundleDefinition}) => bundleDefinition.name).filter(Boolean);

const componentPreloadPaths = project.getComponentPreloadPaths();
const componentPreloadNamespaces = project.getComponentPreloadNamespaces();
const componentPreloadExcludes = project.getComponentPreloadExcludes();
if (componentPreloadPaths.length || componentPreloadNamespaces.length) {
tasks.set("generateComponentPreload", {
options: {
paths: componentPreloadPaths,
namespaces: componentPreloadNamespaces,
excludes: componentPreloadExcludes,
skipBundles: existingBundleDefinitionNames
}
});
} else {
// Default component preload
tasks.set("generateComponentPreload", {
options: {
namespaces: [project.getNamespace()],
excludes: componentPreloadExcludes,
skipBundles: existingBundleDefinitionNames
}
});
}

if (bundles.length) {
tasks.set("generateBundle", {
requiresDependencies: true,
taskFunction: async ({workspace, dependencies, taskUtil, options}) => {
const generateBundleTask = await getTask("generateBundle");
// Async resolve default values for bundle definitions and options
const bundlesDefaults = await enhanceBundlesWithDefaults(bundles, taskUtil.getProject());

return bundlesDefaults.reduce(async function(sequence, bundle) {
return sequence.then(function() {
return generateBundleTask.task({
workspace,
dependencies,
taskUtil,
options: {
projectName: options.projectName,
bundleDefinition: bundle.bundleDefinition,
bundleOptions: bundle.bundleOptions
}
});
});
}, Promise.resolve());
}
});
} else {
// No bundles defined. Just set task so that it can be referenced by custom tasks
tasks.set("generateBundle", {
taskFunction: null
});
}

tasks.set("generateVersionInfo", {
requiresDependencies: true,
options: {
rootProject: project,
pattern: "/resources/**/.library"
}
});

tasks.set("generateCachebusterInfo", {
options: {
signatureType: project.getCachebusterSignatureType(),
}
});

tasks.set("generateResourcesJson", {requiresDependencies: true});

return tasks;
}
3 changes: 3 additions & 0 deletions packages/project/lib/specifications/Specification.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class Specification {
case "application": {
return createAndInitializeSpec("types/Application.js", parameters);
}
case "component": {
return createAndInitializeSpec("types/Component.js", parameters);
}
case "library": {
return createAndInitializeSpec("types/Library.js", parameters);
}
Expand Down
Loading
Loading