Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b619c9e
default flp.libs to true and enhance servestatic
heimwege Oct 27, 2025
62b6b51
ignore serve static paths that match '/resources'
heimwege Oct 27, 2025
a233c77
add unit test
heimwege Oct 27, 2025
f122a23
adjust snaps
heimwege Oct 27, 2025
1a6a2df
fix lint errors
heimwege Oct 27, 2025
25abd36
Linting auto fix commit
github-actions[bot] Oct 27, 2025
9eb65a5
use manifest type from @sap-ux/project-access instead of defining own…
heimwege Oct 27, 2025
b9b3f05
Merge remote-tracking branch 'origin/fix/preview-middleware/default-f…
heimwege Oct 27, 2025
d2a30ef
fix sonar errors
heimwege Oct 27, 2025
e65e6cc
remove todo
heimwege Oct 27, 2025
ab82a76
Linting auto fix commit
github-actions[bot] Oct 27, 2025
e2b6550
refactoring
heimwege Oct 27, 2025
156600c
Merge remote-tracking branch 'origin/fix/preview-middleware/default-f…
heimwege Oct 27, 2025
2d841c2
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Oct 27, 2025
0220607
refactoring (part 2)
heimwege Oct 27, 2025
cbfea67
fix log message only when flp.libs = true for non edmx projects
heimwege Oct 27, 2025
76c0631
add fallback libs for offline use case
heimwege Oct 28, 2025
c522d23
Linting auto fix commit
github-actions[bot] Oct 28, 2025
f0453ab
add changesets
heimwege Oct 28, 2025
513077e
Merge remote-tracking branch 'origin/fix/preview-middleware/default-f…
heimwege Oct 28, 2025
46d58d1
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Oct 28, 2025
265a24b
fix + add unit test
heimwege Oct 28, 2025
a6a43a8
fix sonar issue
heimwege Oct 28, 2025
60fa13c
refactoring
heimwege Oct 29, 2025
e36c52e
fix sonar issues
heimwege Oct 29, 2025
78a5265
add unit tests for error cases
heimwege Oct 29, 2025
d740dd0
harmonize usage of UI5 version info types
heimwege Oct 29, 2025
448e14d
one day I'll find out how to share types between preview-middleware-c…
heimwege Oct 29, 2025
c0784ab
disable @typescript-eslint/no-duplicate-type-constituents
heimwege Oct 29, 2025
2e22ee6
disable @typescript-eslint/no-duplicate-type-constituents
heimwege Oct 29, 2025
1a2aa80
fix lint errors
heimwege Oct 30, 2025
3fbee06
Linting auto fix commit
github-actions[bot] Oct 30, 2025
2aae505
fix lint error
heimwege Oct 30, 2025
8e90b60
fix lint error
heimwege Oct 30, 2025
8a18f49
Merge branch 'fix/preview-middleware/default-flp.libs-to-true-for-edm…
heimwege Oct 30, 2025
93114f4
fix lint error
heimwege Oct 30, 2025
36fab8b
Merge branch 'main' of github.com:SAP/open-ux-tools into fix/preview-…
heimwege Nov 7, 2025
bb32801
rephrase backward compatibility comment in servestatic
heimwege Nov 7, 2025
514c52d
use type LibraryInfo from sap.ui.core
heimwege Nov 10, 2025
47b1a0f
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Nov 11, 2025
123e541
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Nov 11, 2025
c735e59
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Nov 12, 2025
c1f2a24
add unit test
heimwege Nov 12, 2025
e80e373
Linting auto fix commit
github-actions[bot] Nov 12, 2025
cf88b83
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
heimwege Nov 12, 2025
b190a3b
Merge branch 'main' into fix/preview-middleware/default-flp.libs-to-t…
longieirl Nov 28, 2025
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
6 changes: 6 additions & 0 deletions .changeset/big-trees-feel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sap-ux-private/preview-middleware-client': patch
'@sap-ux/preview-middleware': patch
---

feat: default 'flp.libs' parameter to 'true' to automatically fetch paths of libraries and components (from EDMX backend) that are not available in the UI5 sources
6 changes: 6 additions & 0 deletions .changeset/cold-stars-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sap-ux/serve-static-middleware': patch
---

fix: additionally serve paths that start with '/resources' w/o the '/resources' part (e.g. /resources/my/lib -> /my/lib) to ensure backward compatibility for the changed default value of 'flp.libs' parameter (was 'false' now is 'true') of preview-middleware

3 changes: 2 additions & 1 deletion packages/preview-middleware-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@
"@sapui5/types": "1.120.5",
"ui5-tooling-modules": "3.33.0",
"@sap-ux/eslint-plugin-fiori-tools": "workspace:*",
"@sap-ux/project-access": "workspace:*",
"@sap-ux/i18n": "workspace:*",
"@ui5/cli": "4.0.33",
"npm-run-all2": "6.2.0",
"ui5-tooling-transpile": "3.9.2",
"vscode-languageserver-textdocument": "1.0.11",
"@ui5/manifest": "1.81.0"
},
"browserslist": "defaults"
"browserslist": ["defaults"]
}
146 changes: 47 additions & 99 deletions packages/preview-middleware-client/src/flp/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,12 @@ import { getManifestAppdescr } from '../adp/api-handler';
import { getError } from '../utils/error';
import initCdm from './initCdm';
import initConnectors from './initConnectors';
import { getUi5Version, isLowerThanMinimalUi5Version, Ui5VersionInfo } from '../utils/version';
import { getUi5Version, isLowerThanMinimalUi5Version, type Ui5VersionInfo, getUI5Libs } from '../utils/version';
import type Component from 'sap/ui/core/Component';
import type Extension from 'sap/ushell/services/Extension';
import type { CardGeneratorType } from 'sap/cards/ap/generator';
import { sendInfoCenterMessage } from '../utils/info-center-message';

/**
* SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
*/
const UI5_LIBS = [
'sap.apf',
'sap.base',
'sap.chart',
'sap.collaboration',
'sap.f',
'sap.fe',
'sap.fileviewer',
'sap.gantt',
'sap.landvisz',
'sap.m',
'sap.ndc',
'sap.ovp',
'sap.rules',
'sap.suite',
'sap.tnt',
'sap.ui',
'sap.uiext',
'sap.ushell',
'sap.uxap',
'sap.viz',
'sap.webanalytics',
'sap.zen'
];

interface Manifest {
['sap.ui5']?: {
dependencies?: {
libs: Record<string, unknown>;
components: Record<string, unknown>;
};
componentUsages?: Record<string, { name: string }>;
};
}
import type { Manifest } from '@sap-ux/project-access';

type AppIndexData = Record<
string,
Expand All @@ -66,44 +29,14 @@ type AppIndexData = Record<
>;

/**
* Check whether a specific dependency is a custom library, and if yes, add it to the map.
* Check whether the given keys are custom libraries, and if yes, add them to the map.
*
* @param dependency dependency from the manifest
* @param customLibs map containing the required custom libraries
* @param keys array of library or component names
* @returns Promise of a set of custom library or component names.
*/
function addKeys(dependency: Record<string, unknown>, customLibs: Record<string, true>): void {
Object.keys(dependency).forEach(function (key) {
// ignore libs or Components that start with SAPUI5 delivered namespaces
if (
!UI5_LIBS.some(function (substring) {
return key === substring || key.startsWith(substring + '.');
})
) {
customLibs[key] = true;
}
});
}

/**
* Check whether a specific ComponentUsage is a custom component, and if yes, add it to the map.
*
* @param compUsages ComponentUsage from the manifest
* @param customLibs map containing the required custom libraries
*/
function getComponentUsageNames(compUsages: Record<string, { name: string }>, customLibs: Record<string, true>): void {
const compNames = Object.keys(compUsages).map(function (compUsageKey: string) {
return compUsages[compUsageKey].name;
});
compNames.forEach(function (key) {
// ignore libs or Components that start with SAPUI5 delivered namespaces
if (
!UI5_LIBS.some(function (substring) {
return key === substring || key.startsWith(substring + '.');
})
) {
customLibs[key] = true;
}
});
async function getCustomKeys(keys: string[]): Promise<Set<string>> {
const ui5LibSet = await getUI5Libs();
return new Set(keys.filter(key => !ui5LibSet.has(key)));
}

/**
Expand All @@ -113,29 +46,38 @@ function getComponentUsageNames(compUsages: Record<string, { name: string }>, cu
* @returns Promise of a comma separated list of all required libraries.
*/
async function getManifestLibs(appUrls: string[]): Promise<string> {
const result = {} as Record<string, true>;
const promises = [];
for (const url of appUrls) {
promises.push(
fetch(`${url}/manifest.json`).then(async (resp) => {
const manifest = (await resp.json()) as Manifest;
if (manifest) {
if (manifest['sap.ui5']?.dependencies) {
if (manifest['sap.ui5'].dependencies.libs) {
addKeys(manifest['sap.ui5'].dependencies.libs, result);
}
if (manifest['sap.ui5'].dependencies.components) {
addKeys(manifest['sap.ui5'].dependencies.components, result);
}
}
if (manifest['sap.ui5']?.componentUsages) {
getComponentUsageNames(manifest['sap.ui5'].componentUsages, result);
}
const result = new Set<string>();

await Promise.all(appUrls.map(async (url) => {
const response = await fetch(`${url}/manifest.json`);
if (!response.ok) {
Log.error(`Failed to fetch app manifest. Status: ${response.status} ${response.statusText}`);
return;
}
const manifest = (await response.json()) as Manifest;
const sapUi5 = manifest['sap.ui5'];

// Collect custom keys for libs and components
for (const key of ['libs', 'components'] as const) {
const dependencies = sapUi5?.dependencies?.[key];
if (dependencies) {
for (const key of await getCustomKeys(Object.keys(dependencies))) {
result.add(key);
}
})
);
}
return Promise.all(promises).then(() => Object.keys(result).join(','));
}
}

// Collect custom keys for componentUsages
const componentUsages = sapUi5?.componentUsages as Record<string, { name: string }> | undefined;
if (componentUsages) {
const compNames = Object.values(componentUsages).map((componentUsage) => componentUsage.name);
for (const key of await getCustomKeys(compNames)) {
result.add(key);
}
}
}));

return Array.from(result).join(',');
}

/**
Expand Down Expand Up @@ -177,7 +119,9 @@ export async function resetAppState(container: typeof sap.ushell.Container): Pro
}

/**
* Fetch the manifest from the given application urls, then parse them for custom libs, and finally request their urls.
* Fetch the manifest from the given application urls,
* then parse them for custom libs,
* and finally request their urls from the ABAP backend.
*
* @param appUrls application urls
* @param urlParams URLSearchParams object
Expand All @@ -188,10 +132,14 @@ export async function registerComponentDependencyPaths(appUrls: string[], urlPar
if (libs && libs.length > 0) {
let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
const sapClient = urlParams.get('sap-client');
if (sapClient && sapClient.length === 3) {
if (sapClient?.length === 3) {
url = url + '&sap-client=' + sapClient;
}
const response = await fetch(url);
if (!response.ok) {
Log.error(`Failed to fetch app index data. Status: ${response.status} ${response.statusText}`);
return;
}
try {
registerModules((await response.json()) as AppIndexData);
} catch (error) {
Expand Down
81 changes: 74 additions & 7 deletions packages/preview-middleware-client/src/utils/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@ import VersionInfo from 'sap/ui/VersionInfo';
import Log from 'sap/base/Log';
import { sendInfoCenterMessage } from './info-center-message';
import { MessageBarType } from '@sap-ux-private/control-property-editor-common';
import type { Manifest } from '@sap-ux/project-access';
import type { LibraryInfo } from 'sap/ui/core/Core';

type SingleVersionInfo =
| {
name: string;
version: string;
};
type UI5VersionDetails = {
/**
* Contains either
* - the name of the distribution or
* - the id of the application in case the UI5 sources have beeng loaded from npmjs.
*/
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents, @typescript-eslint/no-redundant-type-constituents
name: 'SAPUI5 Distribution' | Manifest['sap.app']['id'];
/**
* Contains either
* - the version of the UI5 framework or
* - the version of the application in case the UI5 sources have been loaded from npmjs.
*/
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents, @typescript-eslint/no-redundant-type-constituents
version: string | Manifest['sap.app']['applicationVersion']['version'];
libraries: LibraryInfo[]
};

export type Ui5VersionInfo = {
major: number;
Expand Down Expand Up @@ -54,8 +68,8 @@ function checkVersionInfo(versionInfo: Ui5VersionInfo): void {
* @returns Ui5VersionInfo
*/
export async function getUi5Version(library: string = 'sap.ui.core'): Promise<Ui5VersionInfo> {
const versionInfo = await VersionInfo.load() as { name: string; libraries: SingleVersionInfo[] } | undefined;
let version = versionInfo?.libraries?.find((lib) => lib.name === library)?.version;
const versionInfo = await VersionInfo.load() as UI5VersionDetails | undefined;
let version = versionInfo?.libraries.find((lib) => lib.name === library)?.version;
const isCdn = versionInfo?.name === 'SAPUI5 Distribution';
if (!version) {
Log.error('Could not get UI5 version of application. Using version: 1.130.9 as fallback.');
Expand Down Expand Up @@ -137,3 +151,56 @@ export function isVersionEqualOrHasNewerPatch(
export function getFullyQualifiedUi5Version(ui5VersionInfo: Ui5VersionInfo): string {
return `${ui5VersionInfo.major}.${ui5VersionInfo.minor}`;
}

/** Retrieve the SAPUI5 delivered namespaces for the current UI5 version.
*
* @returns Promise of a Set of SAPUI5 delivered namespaces
*/
export const getUI5Libs = (() => {
let cachedLibs: Set<string> | undefined;
return async function (): Promise<Set<string>> {
if (!cachedLibs) {
const versionInfo = await VersionInfo.load() as UI5VersionDetails | undefined;
const libNames = versionInfo?.libraries.map(lib => lib.name).filter((name): name is string => !!name);
if (libNames) {
cachedLibs = new Set(libNames);
}
}
if (!cachedLibs) {
Log.error('Could not get UI5 libraries of application. Using fallback libraries from UI5 version 1.130.9.');
cachedLibs = UI5_LIBS_1_130_9;
}
return cachedLibs;
};
})();

const UI5_LIBS_1_130_9 = new Set([
'sap.f',
'sap.fileviewer',
'sap.gantt',
'sap.m',
'sap.ndc',
'sap.suite.ui.commons',
'sap.tnt',
'sap.ui.comp',
'sap.ui.core',
'sap.ui.documentation',
'sap.ui.dt',
'sap.ui.fl',
'sap.ui.integration',
'sap.ui.layout',
'sap.ui.mdc',
'sap.ui.rta',
'sap.ui.suite',
'sap.ui.table',
'sap.ui.unified',
'sap.ui.ux3',
'sap.uxap',
'themelib_sap_belize',
'themelib_sap_bluecrystal',
'themelib_sap_fiori_3',
'themelib_sap_horizon',
'themelib_sap_hcb',
'themelib_sap_hcw',
'themelib_sap_quartz'
]);
Loading
Loading