diff --git a/.changeset/big-trees-feel.md b/.changeset/big-trees-feel.md new file mode 100644 index 00000000000..a4b9cfb5fc3 --- /dev/null +++ b/.changeset/big-trees-feel.md @@ -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 diff --git a/.changeset/cold-stars-raise.md b/.changeset/cold-stars-raise.md new file mode 100644 index 00000000000..c46bd7b1eae --- /dev/null +++ b/.changeset/cold-stars-raise.md @@ -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 + diff --git a/packages/preview-middleware-client/package.json b/packages/preview-middleware-client/package.json index a4fa0412134..878a8b4a96b 100644 --- a/packages/preview-middleware-client/package.json +++ b/packages/preview-middleware-client/package.json @@ -34,6 +34,7 @@ "@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", @@ -41,5 +42,5 @@ "vscode-languageserver-textdocument": "1.0.11", "@ui5/manifest": "1.81.0" }, - "browserslist": "defaults" + "browserslist": ["defaults"] } diff --git a/packages/preview-middleware-client/src/flp/init.ts b/packages/preview-middleware-client/src/flp/init.ts index a554da9547f..5fff54569e1 100644 --- a/packages/preview-middleware-client/src/flp/init.ts +++ b/packages/preview-middleware-client/src/flp/init.ts @@ -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; - components: Record; - }; - componentUsages?: Record; - }; -} +import type { Manifest } from '@sap-ux/project-access'; type AppIndexData = Record< string, @@ -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, customLibs: Record): 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, customLibs: Record): 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> { + const ui5LibSet = await getUI5Libs(); + return new Set(keys.filter(key => !ui5LibSet.has(key))); } /** @@ -113,29 +46,38 @@ function getComponentUsageNames(compUsages: Record, cu * @returns Promise of a comma separated list of all required libraries. */ async function getManifestLibs(appUrls: string[]): Promise { - const result = {} as Record; - 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(); + + 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 | 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(','); } /** @@ -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 @@ -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) { diff --git a/packages/preview-middleware-client/src/utils/version.ts b/packages/preview-middleware-client/src/utils/version.ts index 0449633b9e4..629922306c0 100644 --- a/packages/preview-middleware-client/src/utils/version.ts +++ b/packages/preview-middleware-client/src/utils/version.ts @@ -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; @@ -54,8 +68,8 @@ function checkVersionInfo(versionInfo: Ui5VersionInfo): void { * @returns Ui5VersionInfo */ export async function getUi5Version(library: string = 'sap.ui.core'): Promise { - 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.'); @@ -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 | undefined; + return async function (): Promise> { + 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' +]); \ No newline at end of file diff --git a/packages/preview-middleware-client/test/unit/flp/init.test.ts b/packages/preview-middleware-client/test/unit/flp/init.test.ts index 289e6d9c2c0..b926e7e7114 100644 --- a/packages/preview-middleware-client/test/unit/flp/init.test.ts +++ b/packages/preview-middleware-client/test/unit/flp/init.test.ts @@ -104,16 +104,40 @@ describe('flp/init', () => { }); test('single app, no reuse libs', async () => { - fetchMock.mockResolvedValueOnce({ json: () => testManifest }); + fetchMock.mockResolvedValueOnce({ ok: true, json: () => testManifest }); await registerComponentDependencyPaths(['/'], new URLSearchParams()); expect(loaderMock).not.toHaveBeenCalled(); }); test('single app, one reuse lib', async () => { + VersionInfo.load.mockResolvedValue({ + name: 'SAPUI5 Distribution', + libraries: [{ name: 'sap.ui.core', version: '1.118.1' }] + }); + const manifest = JSON.parse(JSON.stringify(testManifest)) as typeof testManifest; + manifest['sap.ui5'].dependencies.libs['test.lib'] = {}; + manifest['sap.ui5'].dependencies.libs['sap.m'] = {}; + fetchMock.mockResolvedValueOnce({ ok: true, json: () => manifest }); + fetchMock.mockResolvedValueOnce({ + ok: true, + json: () => ({ + 'test.lib': { + dependencies: [{ url: '~url', type: 'UI5LIB', componentId: 'test.lib.component' }] + } + }) + }); + await registerComponentDependencyPaths(['/'], new URLSearchParams()); + expect(loaderMock).toHaveBeenCalledWith({ paths: { 'test/lib/component': '~url' } }); + }); + + test('single app, one reuse lib, no VersionInfo', async () => { + VersionInfo.load.mockResolvedValue(undefined); const manifest = JSON.parse(JSON.stringify(testManifest)) as typeof testManifest; manifest['sap.ui5'].dependencies.libs['test.lib'] = {}; - fetchMock.mockResolvedValueOnce({ json: () => manifest }); + manifest['sap.ui5'].dependencies.libs['sap.m'] = {}; + fetchMock.mockResolvedValueOnce({ ok: true, json: () => manifest }); fetchMock.mockResolvedValueOnce({ + ok: true, json: () => ({ 'test.lib': { dependencies: [{ url: '~url', type: 'UI5LIB', componentId: 'test.lib.component' }] @@ -133,8 +157,9 @@ describe('flp/init', () => { 'lazy': true } }; - fetchMock.mockResolvedValueOnce({ json: () => manifest }); + fetchMock.mockResolvedValueOnce({ ok: true, json: () => manifest }); fetchMock.mockResolvedValueOnce({ + ok: true, json: () => ({ 'test.lib': { dependencies: [{ url: '~url', type: 'UI5LIB', componentId: 'test.lib.component' }] @@ -148,11 +173,12 @@ describe('flp/init', () => { expect(loaderMock).toHaveBeenCalledWith({ paths: { 'test/lib/component': '~url' } }); }); - test('registerComponentDependencyPaths: error case', async () => { + test('registerComponentDependencyPaths: error case 1 (invalid manifest json)', async () => { const manifest = JSON.parse(JSON.stringify(testManifest)) as typeof testManifest; manifest['sap.ui5'].dependencies.libs['test.lib'] = {}; - fetchMock.mockResolvedValueOnce({ json: () => manifest }); + fetchMock.mockResolvedValueOnce({ ok: true, json: () => manifest }); fetchMock.mockResolvedValueOnce({ + ok: true, json: () => { throw new Error('Error'); } @@ -165,6 +191,34 @@ describe('flp/init', () => { expect(error).toEqual('Error'); } }); + + test('registerComponentDependencyPaths: error case 2 (no app index data)', async () => { + const manifest = JSON.parse(JSON.stringify(testManifest)) as typeof testManifest; + manifest['sap.ui5'].dependencies.libs['test.lib'] = {}; + fetchMock.mockResolvedValueOnce({ ok: true, json: () => manifest }); + fetchMock.mockResolvedValueOnce({ + ok: false, + json: () => { + throw new Error('Error'); + } + }); + CommunicationService.sendAction = jest.fn(); + + await registerComponentDependencyPaths(['/'], new URLSearchParams()); + + expect(loaderMock).not.toHaveBeenCalled(); + }); + + test('registerComponentDependencyPaths: error case 3 (no manifest json)', async () => { + const manifest = JSON.parse(JSON.stringify(testManifest)) as typeof testManifest; + manifest['sap.ui5'].dependencies.libs['test.lib'] = {}; + fetchMock.mockResolvedValueOnce({ ok: false }); + CommunicationService.sendAction = jest.fn(); + + await registerComponentDependencyPaths(['/'], new URLSearchParams()); + + expect(loaderMock).not.toHaveBeenCalled(); + }); }); describe('resetAppState', () => { diff --git a/packages/preview-middleware-client/tsconfig.json b/packages/preview-middleware-client/tsconfig.json index 01aca5de220..6dc751ed50e 100644 --- a/packages/preview-middleware-client/tsconfig.json +++ b/packages/preview-middleware-client/tsconfig.json @@ -42,6 +42,9 @@ }, { "path": "../i18n" + }, + { + "path": "../project-access" } ] } diff --git a/packages/preview-middleware/README.md b/packages/preview-middleware/README.md index da1615a3353..14eb3ab98f8 100644 --- a/packages/preview-middleware/README.md +++ b/packages/preview-middleware/README.md @@ -15,29 +15,29 @@ When this middleware is used together with the `reload-middleware`, then the ord ``` ## [Configuration Options](#configuration-options) -| Option | Value Type | Requirement Type | Default Value | Description | -|-------------------------|------------|------------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `flp` | --- | optional | --- | Configuration object for the local SAP Fiori launchpad | -| `flp.path` | `string` | optional | `/test/flp.html` | The mount point of the local SAP Fiori launchpad. In case no file is found at the given path, a virtual endpoint will be instantiated. | -| `flp.init` | `string` | optional | `undefined` | UI5 module/script to be executed after the standard initialization | -| `flp.intent` | --- | optional | --- | Intent object to be used for the application | -| `flp.intent.object` | `string` | optional | `app` | Name of the semantic object | -| `flp.intent.action` | `string` | optional | `preview` | Name of the action | -| `flp.apps` | `array` | optional | `undefined` | Additional local apps that are available in the local SAP Fiori launchpad | -| `flp.libs` | `boolean` | optional | `false` | Flag to add a generic script fetching the paths of used libraries not available in UI5. To disable it, set it to `false`. If not set, then the project is checked for a `load-reuse-libs` script and, if available, the libraries are fetched as well | -| `flp.theme` | `string` | optional | `(calculated)` | Name of the UI5 theme to be used (default is `sap_horizon` or the first entry in the sap.ui.supportedThemes list provided in the manifest.json file if `sap_horizon` is not contained in the list) | -| `flp.enhancedHomePage` | `boolean` | optional | `false` | Flag for enabling enhanced FLP homepage, available only from UI5 version 1.123.0 onwards | -| `adp.target` | --- | mandatory for adaptation projects | --- | Configuration object defining the connected back end | -| `adp.ignoreCertErrors` | `boolean` | optional | `false` | Flag to ignore certification validation errors when working with development systems with self-signed certificates, for example | -| `rta` | --- | 🚫 deprecated
*use `editors.rta` instead* | --- | Configuration allowing to add mount points for runtime adaptation | -| `editors` | `array` | optional | `undefined` | List of configurations allowing to add mount points for additional editors | -| `editors.rta` | `array` | optional | `undefined` | Configuration allowing to add mount points for runtime adaptation | -| `editors.rta.layer` | `string` | optional | `(calculated)` | Property for defining the runtime adaptation layer for changes (default is `CUSTOMER_BASE` or read from the project for adaptation projects) | -| `editors.rta.endpoints` | `array` | optional | `undefined` | List of mount points for editing | -| `editors.cardGenerator` | --- | optional | `undefined` | Configuration object that enables card generation for an application. The generated cards are only available when the application is deployed to an ABAP-based SAP Fiori launchpad, such as SAP Fiori launchpad in SAP S/4HANA Cloud Public Edition. | -| `editors.cardGenerator.path` | `string` | optional | `test/flpGeneratorSandbox.html` | The mount point of the local SAP Fiori launchpad which will be considered for card generation. | -| `test` | `array` | optional | `undefined` | List of configurations for automated testing. | -| `debug` | `boolean` | optional | `false` | Enables the debug output | +| Option | Value Type | Requirement Type | Default Value | Description | +|-------------------------|------------|------------------------------------------------|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `flp` | --- | optional | --- | Configuration object for the local SAP Fiori launchpad | +| `flp.path` | `string` | optional | `/test/flp.html` | The mount point of the local SAP Fiori launchpad. In case no file is found at the given path, a virtual endpoint will be instantiated. | +| `flp.init` | `string` | optional | `undefined` | UI5 module/script to be executed after the standard initialization | +| `flp.intent` | --- | optional | --- | Intent object to be used for the application | +| `flp.intent.object` | `string` | optional | `app` | Name of the semantic object | +| `flp.intent.action` | `string` | optional | `preview` | Name of the action | +| `flp.apps` | `array` | optional | `undefined` | Additional local apps that are available in the local SAP Fiori launchpad | +| `flp.libs` | `boolean` | optional | `true` (for EDMX backends) | Flag to automatically fetch paths of libraries and components (from EDMX backend) that are not available in the UI5 sources. This avoids trying to load libraries and components from the `/resources` path although they are not part of UI5. | +| `flp.theme` | `string` | optional | `(calculated)` | Name of the UI5 theme to be used (default is `sap_horizon` or the first entry in the sap.ui.supportedThemes list provided in the manifest.json file if `sap_horizon` is not contained in the list) | +| `flp.enhancedHomePage` | `boolean` | optional | `false` | Flag for enabling enhanced FLP homepage, available only from UI5 version 1.123.0 onwards | +| `adp.target` | --- | mandatory for adaptation projects | --- | Configuration object defining the connected back end | +| `adp.ignoreCertErrors` | `boolean` | optional | `false` | Flag to ignore certification validation errors when working with development systems with self-signed certificates, for example | +| `rta` | --- | 🚫 deprecated
*use `editors.rta` instead* | --- | Configuration allowing to add mount points for runtime adaptation | +| `editors` | `array` | optional | `undefined` | List of configurations allowing to add mount points for additional editors | +| `editors.rta` | `array` | optional | `undefined` | Configuration allowing to add mount points for runtime adaptation | +| `editors.rta.layer` | `string` | optional | `(calculated)` | Property for defining the runtime adaptation layer for changes (default is `CUSTOMER_BASE` or read from the project for adaptation projects) | +| `editors.rta.endpoints` | `array` | optional | `undefined` | List of mount points for editing | +| `editors.cardGenerator` | --- | optional | `undefined` | Configuration object to enable card generation for an application (only supported for non-CAP apps). | +| `editors.cardGenerator.path` | `string` | optional | `test/flpGeneratorSandbox.html` | The mount point of the local SAP Fiori launchpad which will be considered for card generation. | +| `test` | `array` | optional | `undefined` | List of configurations for automated testing. | +| `debug` | `boolean` | optional | `false` | Enables the debug output | ### [`flp.apps`](#configuration-option-flpapps) Array of additional application configurations: diff --git a/packages/preview-middleware/src/base/flp.ts b/packages/preview-middleware/src/base/flp.ts index 8a6fed129c0..ec64134e9ab 100644 --- a/packages/preview-middleware/src/base/flp.ts +++ b/packages/preview-middleware/src/base/flp.ts @@ -60,6 +60,38 @@ import { readFileSync } from 'node:fs'; import { getIntegrationCard } from './utils/cards'; import { createPropertiesI18nEntries } from '@sap-ux/i18n'; +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. + */ + 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. + */ + version: string | Manifest['sap.app']['applicationVersion']['version']; + libraries: UI5LibraryVersionInfo[]; +}; + +type UI5LibraryVersionInfo = { + name: string; + version: string; +}; + +export type Ui5VersionInfo = { + major: number; + minor: number; + patch: number; + label?: string; + /** + * Indicates if the UI5 version is served from CDN. + */ + isCdn: boolean; +}; + const DEFAULT_LIVERELOAD_PORT = 35729; /** @@ -82,17 +114,6 @@ type OnChangeRequestHandler = ( extendedChange?: CommonAdditionalChangeInfoProperties ) => Promise; -type Ui5Version = { - major: number; - minor: number; - patch: number; - label?: string; - /** - * Indicates if the UI5 version is served from CDN. - */ - isCdn: boolean; -}; - /** * Class handling preview of a sandbox FLP. */ @@ -157,7 +178,16 @@ export class FlpSandbox { ): Promise { this.projectType = await getProjectType(await findProjectRoot(process.cwd(), true, true)); this.createFlexHandler(); - this.flpConfig.libs ??= await this.hasLocateReuseLibsScript(); + if (this.projectType === 'EDMXBackend') { + this.flpConfig.libs ??= true; + } else { + if (this.flpConfig.libs === true) { + this.logger.warn( + `'flp.libs' disabled because the current project type is not EDMX. 'flp.libs' only works for EDMX backends.` + ); + } + this.flpConfig.libs = false; + } const id = manifest['sap.app']?.id ?? ''; this.templateConfig = createFlpTemplateConfig(this.flpConfig, manifest, resources); this.adp = adp; @@ -541,7 +571,7 @@ export class FlpSandbox { protocol: Request['protocol'], host: Request['headers']['host'], baseUrl: string = '' - ): Promise { + ): Promise { let version: string | undefined; let isCdn = false; if (!host) { @@ -550,7 +580,7 @@ export class FlpSandbox { try { const versionUrl = `${protocol}://${host}${baseUrl}/resources/sap-ui-version.json`; const responseJson = (await fetch(versionUrl).then((res) => res.json())) as - | { name: string; libraries: { name: string; version: string }[] } + | UI5VersionDetails | undefined; version = responseJson?.libraries?.find((lib) => lib.name === 'sap.ui.core')?.version; isCdn = responseJson?.name === 'SAPUI5 Distribution'; @@ -589,7 +619,7 @@ export class FlpSandbox { * @param ui5Version - the UI5 version * @returns the template for the sandbox HTML file */ - private getSandboxTemplate(ui5Version: Ui5Version): string { + private getSandboxTemplate(ui5Version: Ui5VersionInfo): string { this.logger.info( `Using sandbox template for UI5 version: ${ui5Version.major}.${ui5Version.minor}.${ui5Version.patch}${ ui5Version.label ? `-${ui5Version.label}` : '' @@ -614,16 +644,6 @@ export class FlpSandbox { } } - /** - * Try finding a locate-reuse-libs script in the project. - * - * @returns the location of the locate-reuse-libs script or undefined. - */ - private async hasLocateReuseLibsScript(): Promise { - const files = await this.project.byGlob('**/locate-reuse-libs.js'); - return files.length > 0; - } - /** * Add additional routes for apps also to be shown in the local FLP. */ @@ -1143,7 +1163,7 @@ function serializeDataAttributes(attributes: Map, indent = '', p } /** - * Creates an attribute string that can be added to bootstrap script in a HTML file. + * Creates an attribute string that can be added to bootstrap script in an HTML file. * * @param config ui5 configuration options * @returns attribute string diff --git a/packages/preview-middleware/test/unit/base/__snapshots__/flp.test.ts.snap b/packages/preview-middleware/test/unit/base/__snapshots__/flp.test.ts.snap index f09e2103da2..1dff449d026 100644 --- a/packages/preview-middleware/test/unit/base/__snapshots__/flp.test.ts.snap +++ b/packages/preview-middleware/test/unit/base/__snapshots__/flp.test.ts.snap @@ -15,7 +15,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -85,7 +85,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -149,7 +149,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -196,7 +196,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -243,7 +243,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -290,7 +290,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -323,6 +323,53 @@ Object { `; exports[`FlpSandbox init minimal manifest 1`] = ` +Object { + "apps": Object { + "app-preview": Object { + "additionalInformation": "SAPUI5.Component=my.id", + "applicationType": "URL", + "description": "", + "title": "my.id", + "url": "..", + }, + }, + "basePath": "..", + "enableCardGenerator": false, + "enhancedHomePage": false, + "init": undefined, + "locateReuseLibsScript": true, + "ui5": Object { + "bootstrapOptions": "", + "flex": Array [ + Object { + "connector": "LrepConnector", + "layers": Array [], + "url": "/sap/bc/lrep", + }, + Object { + "applyConnector": "open/ux/preview/client/flp/WorkspaceConnector", + "custom": true, + "writeConnector": "open/ux/preview/client/flp/WorkspaceConnector", + }, + Object { + "connector": "LocalStorageConnector", + "layers": Array [ + "CUSTOMER", + "USER", + ], + }, + ], + "libs": "sap.m,sap.ui.core,sap.ushell", + "resources": Object { + "my.id": "..", + "open.ux.preview.client": "../preview/client", + }, + "theme": "sap_horizon", + }, +} +`; + +exports[`FlpSandbox init minimal manifest with CAPNodejs project type 1`] = ` Object { "apps": Object { "app-preview": Object { @@ -384,7 +431,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -433,7 +480,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -527,7 +574,8 @@ exports[`FlpSandbox router - connect API GET default routes with connect API (us data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -692,7 +740,8 @@ exports[`FlpSandbox router - connect API GET default routes with connect API (us data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -751,6 +800,7 @@ exports[`FlpSandbox router - connect API GET default routes with connect API whe data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]' data-open-ux-preview-enhanced-homepage='true'> @@ -920,7 +970,8 @@ exports[`FlpSandbox router enhanced homepage disabled editor with config 1`] = ` data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -995,7 +1046,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html 1`] = ` data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1070,7 +1122,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html UI5 1.76.0 f data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1143,7 +1196,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html UI5 2.x 1`] data-sap-ui-resource-roots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frame-options=\\"allow\\" data-sap-ui-xx-component-preload=\\"off\\" - data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1216,7 +1270,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html UI5 legacy-f data-sap-ui-resource-roots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frame-options=\\"allow\\" data-sap-ui-xx-component-preload=\\"off\\" - data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1291,7 +1346,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html UI5 snapshot data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1368,7 +1424,8 @@ exports[`FlpSandbox router enhanced homepage disabled test/flp.html sap-ui-xx-vi data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1427,6 +1484,7 @@ exports[`FlpSandbox router enhanced homepage enabled editor with config 1`] = ` data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]' data-open-ux-preview-enhanced-homepage='true'> @@ -1488,6 +1546,7 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html 1`] = ` data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]' data-open-ux-preview-enhanced-homepage='true'> @@ -1563,7 +1622,8 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html UI5 1.76.0 fr data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1636,7 +1696,8 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html UI5 2.x 1`] = data-sap-ui-resource-roots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frame-options=\\"allow\\" data-sap-ui-xx-component-preload=\\"off\\" - data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1709,7 +1770,8 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html UI5 legacy-fr data-sap-ui-resource-roots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frame-options=\\"allow\\" data-sap-ui-xx-component-preload=\\"off\\" - data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1768,6 +1830,7 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html UI5 snapshot data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]' data-open-ux-preview-enhanced-homepage='true'> @@ -1829,6 +1892,7 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html sap-ui-xx-vie data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]' data-open-ux-preview-enhanced-homepage='true'> @@ -1904,7 +1968,8 @@ exports[`FlpSandbox router enhanced homepage enabled test/flp.html should fallba data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -1941,7 +2006,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -2038,7 +2103,8 @@ exports[`FlpSandbox router rta with adp instance - preview 2`] = ` data-sap-ui-resourceroots='{\\"myResources1\\":\\"myResourcesUrl1\\",\\"myResources2\\":\\"myResourcesUrl2\\",\\"open.ux.preview.client\\":\\"../preview/client\\",\\"my.id\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}' data-sap-ui-frameOptions=\\"allow\\" data-sap-ui-xx-componentPreload=\\"off\\" - data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\"> + data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2073,7 +2139,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -2175,7 +2241,8 @@ exports[`FlpSandbox router rta with adp instance 2`] = ` data-sap-ui-xx-viewCache=\\"false\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"myComponent\\",\\"appName\\":\\"my.id\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"myComponent\\",\\"appName\\":\\"my.id\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2333,7 +2400,8 @@ exports[`FlpSandbox router rta with developerMode=true UI5 version 2.x 2`] = ` data-sap-ui-xx-view-cache=\\"false\\" data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2427,7 +2495,8 @@ exports[`FlpSandbox router rta with developerMode=true and plugin 1`] = ` data-sap-ui-xx-viewCache=\\"false\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/tools/plugin\\"}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/tools/plugin\\"}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2520,7 +2589,8 @@ exports[`FlpSandbox router rta with url parameters 1`] = ` data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2556,7 +2626,7 @@ Object { "enableCardGenerator": false, "enhancedHomePage": false, "init": undefined, - "locateReuseLibsScript": false, + "locateReuseLibsScript": true, "ui5": Object { "bootstrapOptions": "", "flex": Array [ @@ -2658,7 +2728,8 @@ exports[`FlpSandbox router test/flp.html UI5 1.71 with asyncHints.requests 2`] = data-sap-ui-xx-viewCache=\\"false\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"myComponent\\",\\"appName\\":\\"my.id\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"myComponent\\",\\"appName\\":\\"my.id\\",\\"generator\\":\\"@sap-ux/control-property-editor\\",\\"developerMode\\":true,\\"pluginScript\\":\\"open/ux/preview/client/cpe/init\\"}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2777,7 +2848,8 @@ exports[`FlpSandbox rta with new config rta w/o layer 1`] = ` data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> @@ -2889,7 +2961,8 @@ exports[`FlpSandbox rta with new config rta with url parameters 1`] = ` data-sap-ui-xx-componentPreload=\\"off\\" data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\" data-open-ux-preview-features='[]' - data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}'> + data-open-ux-preview-flex-settings='{\\"layer\\":\\"CUSTOMER_BASE\\",\\"baseId\\":\\"test.fe.v2.app\\",\\"appName\\":\\"test.fe.v2.app\\",\\"generator\\":\\"@sap-ux/preview-middleware\\",\\"developerMode\\":false}' + data-open-ux-preview-libs-manifests='[\\"..\\",\\"/yet/another/app\\"]'> diff --git a/packages/preview-middleware/test/unit/base/flp.test.ts b/packages/preview-middleware/test/unit/base/flp.test.ts index 6ac4b14a720..fcfd885166a 100644 --- a/packages/preview-middleware/test/unit/base/flp.test.ts +++ b/packages/preview-middleware/test/unit/base/flp.test.ts @@ -112,6 +112,10 @@ describe('FlpSandbox', () => { }); describe('init', () => { + beforeEach(() => { + jest.spyOn(projectAccess, 'getProjectType').mockImplementation(() => Promise.resolve('EDMXBackend')); + }); + test('minimal manifest', async () => { const flp = new FlpSandbox({}, mockProject, mockUtils, logger); const manifest = { @@ -121,6 +125,26 @@ describe('FlpSandbox', () => { expect(flp.templateConfig).toMatchSnapshot(); }); + test('minimal manifest with CAPNodejs project type', async () => { + jest.spyOn(projectAccess, 'getProjectType').mockImplementation(() => Promise.resolve('CAPNodejs')); + const flp = new FlpSandbox( + { + flp: { + libs: true + } + }, + mockProject, + mockUtils, + logger + ); + const manifest = { + 'sap.app': { id: 'my.id' } + } as Manifest; + await flp.init(manifest); + expect(flp.templateConfig.locateReuseLibsScript).toBe(false); + expect(flp.templateConfig).toMatchSnapshot(); + }); + test('Card generator is enabled for the application', async () => { const flp = new FlpSandbox( { diff --git a/packages/serve-static-middleware/src/base/servestatic.ts b/packages/serve-static-middleware/src/base/servestatic.ts index d3a04714eaf..294970cb507 100644 --- a/packages/serve-static-middleware/src/base/servestatic.ts +++ b/packages/serve-static-middleware/src/base/servestatic.ts @@ -22,6 +22,20 @@ export const serveStaticMiddleware = ( const globalOptions: ServeStaticOptions = resolveServeStaticOptions(config); const router = Router(); + // Maintain backward compatibility by creating additional route mappings for paths beginning with /resources, + // making them accessible without the /resources prefix (due to flp.libs parameter default value changes in preview-middleware) + const compatibilityPaths: typeof paths = []; + for (const pathConfig of paths) { + if (pathConfig.path !== '/resources' && pathConfig.path.startsWith('/resources')) { + const compatibilityPath = { + ...pathConfig, + path: pathConfig.path.replace('/resources', '') + }; + compatibilityPaths.push(compatibilityPath); + } + } + paths.push(...compatibilityPaths); + for (const pathConfig of paths) { const localOptions = resolveServeStaticOptions(pathConfig); const serveStaticOptions = { ...globalOptions, ...localOptions }; diff --git a/packages/serve-static-middleware/test/base/__snapshots__/servestatic.test.ts.snap b/packages/serve-static-middleware/test/base/__snapshots__/servestatic.test.ts.snap index 5b0f6afa66c..e42ef959d67 100644 --- a/packages/serve-static-middleware/test/base/__snapshots__/servestatic.test.ts.snap +++ b/packages/serve-static-middleware/test/base/__snapshots__/servestatic.test.ts.snap @@ -19,6 +19,32 @@ Array [ ] `; +exports[`serve-static-middleware serveStaticMiddleware: call with custom library from /resources 1`] = ` +Array [ + Array [ + "/path/to/my/custom/lib", + Object { + "fallthrough": false, + "maxAge": 123, + }, + ], + Array [ + "/path/to/test-resources", + Object { + "fallthrough": true, + "maxAge": 456, + }, + ], + Array [ + "/path/to/my/custom/lib", + Object { + "fallthrough": false, + "maxAge": 123, + }, + ], +] +`; + exports[`serve-static-middleware serveStaticMiddleware: call with global options 1`] = ` Array [ Array [ diff --git a/packages/serve-static-middleware/test/base/servestatic.test.ts b/packages/serve-static-middleware/test/base/servestatic.test.ts index 3ef7640a174..b6dfcf6aa8e 100644 --- a/packages/serve-static-middleware/test/base/servestatic.test.ts +++ b/packages/serve-static-middleware/test/base/servestatic.test.ts @@ -47,6 +47,18 @@ describe('serve-static-middleware', () => { expect(serveStaticMock.mock.calls).toMatchSnapshot(); }); + test('serveStaticMiddleware: call with custom library from /resources', () => { + const config: ServeStaticConfig = { + paths: [ + { path: '/resources/my/custom/lib', src: '/path/to/my/custom/lib', maxAge: 123, fallthrough: false }, + { path: '/test-resources', src: '/path/to/test-resources', maxAge: 456, fallthrough: true } + ] + }; + serveStaticMiddleware('/root', config, logger); + expect(serveStaticMock).toHaveBeenCalledTimes(3); + expect(serveStaticMock.mock.calls).toMatchSnapshot(); + }); + test('serveStaticMiddleware: call with src path resolution', () => { const config: ServeStaticConfig = { paths: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be37f68afd4..c8b9560a216 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3296,6 +3296,9 @@ importers: '@sap-ux/i18n': specifier: workspace:* version: link:../i18n + '@sap-ux/project-access': + specifier: workspace:* + version: link:../project-access '@sapui5/types': specifier: 1.120.5 version: 1.120.5