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
145 changes: 145 additions & 0 deletions docs/09-migration-guides/02-to-version-3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
sidebar_label: To UI5 Web Components 3.0
---

# Migration to UI5 Web Components 3.0

This guide will assist you in transitioning from UI5 Web Components version 2.x to UI5 Web Components 3.0.

---

## @ui5/webcomponents-tools

### WebdriverIO (WDIO) test support removed

WDIO-based testing has been removed from `@ui5/webcomponents-tools`. The following are no longer provided:

| Removed item | Details |
|---|---|
| NPS script `test` | Invoked the WDIO test runner |
| `lib/test-runner/test-runner.js` | WDIO runner wrapper |
| `components-package/wdio.js` | WDIO configuration |
| `@wdio/cli`, `@wdio/local-runner`, `@wdio/mocha-framework`, `@wdio/spec-reporter`, `@wdio/dot-reporter`, `@wdio/static-server-service` | WDIO npm dependencies |
| `wdio-chromedriver-service` | WDIO ChromeDriver service |
| `chromedriver` | Peer dependency |

> **What to do:** Migrate your tests to [Cypress Component Testing](https://docs.cypress.io/guides/component-testing/overview).
>
> The `@ui5/webcomponents-tools` package already provides Cypress-based NPS scripts as the replacement:
>
> | Old | New |
> |---|---|
> | `ui5nps test` | `ui5nps test-cy-ci` (CI, all specs) |
> | — | `ui5nps test-cy-ci-suite-1` … `suite-4` (CI, split by spec name) |
> | — | `ui5nps test-cy-open` (interactive browser) |
>
> If your project has a `config/wdio.conf.cjs` or `config/wdio.conf.js` file, remove it — it is no longer read by the tooling.
>
> For a complete Cypress setup example see any component package in the monorepo (e.g. `packages/main`).

---

### HBS templates, LitRenderer, and hbs2lit/hbs2ui5 removed

Handlebars-based template compilation and the LitRenderer rendering path have been removed. The following are no longer provided:

| Removed item | Details |
|---|---|
| `lib/hbs2lit/` | Handlebars → Lit-HTML compiler |
| `lib/hbs2ui5/` | CLI wrapper and LitRenderer template generator |
| NPS script `build.templates` | Compiled `.hbs` files to `src/generated/templates/` |
| NPS script `watch.templates` | Watched `.hbs` files for changes |
| `options.legacy` | `getScripts()` option that enabled the HBS build path |
| `options.jsx` | `getScripts()` option that forced TypeScript on in legacy mode |
| `handlebars`, `escodegen`, `esprima` | npm dependencies used by the HBS compiler |

> **What to do:** Migrate any remaining `.hbs` templates to JSX/TSX using `jsxRenderer`.
>
> Replace `litRender` + a generated `.lit.ts` template with a `render()` method in your component class:
>
> ```ts
> // Before
> import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
> import MyTemplate from "./generated/templates/MyComponentTemplate.lit.js";
>
> @customElement({ renderer: litRender, template: MyTemplate })
> class MyComponent extends UI5Element { }
> ```
>
> ```tsx
> // After
> import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
>
> @customElement({ renderer: jsxRenderer })
> class MyComponent extends UI5Element {
> render() {
> return <div>{this.text}</div>;
> }
> }
> ```
>
> If you were passing `legacy: true` or `jsx: true` to `getScripts()` in your `package-scripts.cjs`, remove those options. TypeScript support is now controlled via the new `typescript` option (defaults to `true`). Pass `typescript: false` only for pure JavaScript projects.

---

### CSS variable version-scoping removed

CSS custom properties (`--ui5-*`) are no longer scoped with the package version at build time. Previously, variables were transformed from `--ui5-button-color` to `--ui5-v2-19-0-button-color`. This is no longer done.

| Removed item | Details |
|---|---|
| `lib/css-processors/scope-variables.mjs` | Version-based CSS variable scoping logic |
| `CSS_VARIABLES_TARGET` env var | Controlled scoping mode (`root` vs `host`) |
| `options.cssVariablesTarget` | `getScripts()` option that set `CSS_VARIABLES_TARGET` |

> **What to do:** No action needed for most projects — CSS variables in your built output will simply use their plain names (e.g. `--ui5-button-color`) instead of versioned names.
>
> If you were passing `cssVariablesTarget` to `getScripts()` in your `package-scripts.cjs`, remove it — the option is no longer read.
>
> If you relied on versioned variable names for multi-version isolation, you will need to implement an alternative strategy (e.g. CSS layer scoping or shadow DOM containment).

---

### ESLint support removed

The built-in ESLint runner and shared ESLint configuration have been removed from `@ui5/webcomponents-tools`. The following are no longer provided:

| Removed item | Details |
|---|---|
| `lib/eslint/eslint.cjs` | ESLint runner script |
| `components-package/eslint.cjs` | Shared ESLint configuration (airbnb-base + TypeScript overrides) |
| NPS script `lint` | Ran ESLint on the package source |
| NPS script `lintfix` | Ran ESLint with `--fix` |
| `eslint`, `eslint-config-airbnb-base`, `eslint-plugin-import`, `eslint-plugin-jsx-no-leaked-values` | ESLint npm dependencies |
| `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser` | TypeScript ESLint dependencies |

> **What to do:** Set up ESLint directly in your project. Remove any `.eslintrc.cjs` file that extended `@ui5/webcomponents-tools/components-package/eslint.js` — it is no longer available.
>
> For guidance on setting up ESLint with TypeScript support in a web components project, refer to the [ESLint documentation](https://eslint.org/docs/latest/) and the [@typescript-eslint getting started guide](https://typescript-eslint.io/getting-started/).

---

### Package converted to native ESM (`"type": "module"`)

`@ui5/webcomponents-tools` is now a native ES module package. All files use ESM syntax (`import`/`export`) and the package has `"type": "module"` in its `package.json`.

**`package-scripts.cjs` must be renamed and converted to ESM**

If your package has a `package-scripts.cjs` file, rename it to `package-scripts.js` and convert it to ESM syntax:

```js
// Before (package-scripts.cjs — CommonJS)
const getScripts = require("@ui5/webcomponents-tools/components-package/nps.js");
module.exports = { scripts: getScripts({ ... }) };
```

```js
// After (package-scripts.js — ESM)
import getScripts from "@ui5/webcomponents-tools/components-package/nps.js";
export default { scripts: getScripts({ ... }) };
```

**`.mjs` entry points renamed to `.js`**

All files that were previously published with a `.mjs` extension are now `.js`. If you imported any of these directly (e.g. from `lib/css-processors/` or `lib/dev-server/`), update the extension in your imports.

10 changes: 0 additions & 10 deletions packages/ai/.eslintrc.cjs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
const getScripts = require("@ui5/webcomponents-tools/components-package/nps.js");
import getScripts from "@ui5/webcomponents-tools/components-package/nps.js";

const options = {
port: 8082,
portStep: 2,
aiPackage: true,
noWatchTS: true,
cssVariablesTarget: "host",
dev: true,
internal: {
cypress_code_coverage: false,
Expand All @@ -14,6 +13,6 @@ const options = {

const scripts = getScripts(options);

module.exports = {
export default {
scripts,
};
10 changes: 0 additions & 10 deletions packages/base/.eslintrc.cjs

This file was deleted.

32 changes: 0 additions & 32 deletions packages/base/cypress/specs/WithComplexTemplate.cy.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createRequire } from "module";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const resolve = require("resolve");
const path = require("path");

const assetParametersScript = resolve.sync("@ui5/webcomponents-base/lib/generate-asset-parameters/index.js");
const stylesScript = resolve.sync("@ui5/webcomponents-base/lib/generate-styles/index.js");
Expand All @@ -24,7 +29,7 @@ const scripts = {
"generated": `ui5nps-script "${LIB}/rimraf/rimraf.js src/generated`,
"dist": `ui5nps-script "${LIB}/rimraf/rimraf.js dist`,
},
lint: `ui5nps-script "${LIB}/eslint/eslint.js"`,
lint: ``,
generate: "ui5nps clean build.i18n integrate copy generateAssetParameters generateVersionInfo generateStyles generateFontFace build.jsonImports",
prepare: "ui5nps clean build.i18n integrate copy generateAssetParameters generateVersionInfo generateStyles generateFontFace typescript integrate.no-remaining-require build.jsonImports",
typescript: "tsc -b",
Expand All @@ -41,7 +46,7 @@ const scripts = {
},
build: {
default: `ui5nps prepare`,
bundle: `ui5nps-script "${LIB}/vite-bundler/vite-bundler.mjs" ${viteConfig}`,
bundle: `ui5nps-script "${LIB}/vite-bundler/vite-bundler.js" ${viteConfig}`,
i18n: {
default: "ui5nps build.i18n.defaultsjs build.i18n.json",
defaultsjs: `ui5nps-script "${LIB}/i18n/defaults.js" src/i18n src/generated/i18n`,
Expand All @@ -61,10 +66,9 @@ const scripts = {
generateVersionInfo: `ui5nps-script "${versionScript}"`,
generateStyles: `ui5nps-script "${stylesScript}"`,
generateFontFace: `ui5nps-script "${fontFaceScript}"`,
generateTestTemplates: `node "${LIB}/hbs2ui5/index.js" -d test/test-elements -o test/test-elements/generated/templates`,
generateProd: {
"default": "ui5nps generateProd.remove-dev-mode generateProd.copy-prod",
"remove-dev-mode": `ui5nps-script "${LIB}/remove-dev-mode/remove-dev-mode.mjs"`,
"remove-dev-mode": `ui5nps-script "${LIB}/remove-dev-mode/remove-dev-mode.js"`,
"copy-prod": {
default: "ui5nps-p generateProd.copy-prod.ui5 generateProd.copy-prod.preact generateProd.copy-prod.assets",
"ui5": `ui5nps-script "${LIB}copy-and-watch/index.js" "dist/sap/**/*" dist/prod/sap/`,
Expand All @@ -73,33 +77,33 @@ const scripts = {
}
},
generateAPI: {
generateCEM: `ui5nps-script "${LIB}/cem/cem.js" analyze --config "${LIB}cem/custom-elements-manifest.config.mjs"`,
generateCEM: `ui5nps-script "${LIB}/cem/cem.js" analyze --config "${LIB}cem/custom-elements-manifest.config.js"`,
validateCEM: `ui5nps-script "${LIB}/cem/validate.js"`,
mergeCEM: `ui5nps-script "${LIB}cem/merge.mjs"`,
mergeCEM: `ui5nps-script "${LIB}cem/merge.js"`,
},
watch: {
default: 'ui5nps-p watch.src watch.styles', // concurently
withBundle: 'ui5nps-p watch.src watch.bundle watch.styles', // concurently
src: 'ui5nps copy.srcWithWatch',
bundle: `ui5nps-script ${LIB}/dev-server/dev-server.mjs ${viteConfig}`,
bundle: `ui5nps-script ${LIB}/dev-server/dev-server.js ${viteConfig}`,
styles: `ui5nps-script "${LIB}/chokidar/chokidar.js" "src/css/*.css" "ui5nps generateStyles"`
},
test: {
default: 'ui5nps-p test.ssr test.ssr2 test.test-cy-ci', // concurently
ssr: `mocha test/ssr`,
ssr2: "node -e \"import('./dist/Device.js')\"",
"test-cy-ci": {
default: "ui5nps generateTestTemplates test.test-cy-ci.cypress",
default: "ui5nps test.test-cy-ci.cypress",
cypress: ` yarn cypress run --component --browser chrome`
},
"test-cy-open": {
default: "ui5nps generateTestTemplates test.test-cy-open.cypress",
default: "ui5nps test.test-cy-open.cypress",
cypress: ` yarn cypress open --component --browser chrome`
}
},
};


module.exports = {
export default {
scripts,
};
1 change: 1 addition & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@sap-theming/theming-base-content": "11.35.0",
"@ui5/cypress-internal": "0.1.0",
"@ui5/webcomponents-tools": "2.22.0-rc.4",
"chai": "^4.3.4",
"clean-css": "^5.2.2",
"cypress": "15.9.0",
"mocha": "^11.7.2",
Expand Down
8 changes: 3 additions & 5 deletions packages/base/src/asset-registries/Themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import { fireThemeRegistered } from "../theming/ThemeRegistered.js";
type ThemeData = string;
type ThemeLoader = (themeName: string) => Promise<string>;

type CSSVariablesTarget = "root" | "host";

const themeStyles = new Map<string, string>();
const loaders = new Map<string, ThemeLoader>();
const customLoaders = new Map<string, ThemeLoader>();
const registeredPackages = new Map<string, { cssVariablesTarget: CSSVariablesTarget }>();
const registeredPackages = new Set<string>();
const registeredThemes = new Set<string>();

const registerThemePropertiesLoader = (packageName: string, themeName: string, loader: ThemeLoader, cssVariablesTarget: CSSVariablesTarget = "root") => {
const registerThemePropertiesLoader = (packageName: string, themeName: string, loader: ThemeLoader) => {
loaders.set(`${packageName}/${themeName}`, loader);
registeredPackages.set(packageName, { cssVariablesTarget });
registeredPackages.add(packageName);
registeredThemes.add(themeName);
fireThemeRegistered(themeName);
};
Expand Down
9 changes: 2 additions & 7 deletions packages/base/src/theming/applyTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { attachCustomThemeStylesToHead, getThemeRoot } from "../config/ThemeRoot
import { setBaseTheme } from "../config/Theme.js";
import type OpenUI5Support from "../features/OpenUI5Support.js";
import { DEFAULT_THEME } from "../generated/AssetParameters.js";
import { getCurrentRuntimeIndex } from "../Runtimes.js";
import { updateComponentStyles } from "./componentStyles.js";

// eslint-disable-next-line
Expand Down Expand Up @@ -40,18 +39,14 @@ const deleteThemeBase = () => {
const loadComponentPackages = async (theme: string, externalThemeName?: string) => {
const registeredPackages = getRegisteredPackages();

const packagesStylesPromises = [...registeredPackages.entries()].map(async ([packageName, { cssVariablesTarget }]) => {
const packagesStylesPromises = [...registeredPackages].map(async packageName => {
if (packageName === BASE_THEME_PACKAGE) {
return;
}

const cssData = await getThemeProperties(packageName, theme, externalThemeName);
if (cssData) {
if (cssVariablesTarget === "root") {
createOrUpdateStyle(cssData, `data-ui5-component-properties-${getCurrentRuntimeIndex()}`, packageName);
} else if (cssVariablesTarget === "host") {
updateComponentStyles(packageName, cssData);
}
updateComponentStyles(packageName, cssData);
}
});

Expand Down
21 changes: 0 additions & 21 deletions packages/base/test/pages/WithComplexTemplate.html

This file was deleted.

Loading
Loading