diff --git a/examples/odata-cli/CHANGELOG.md b/examples/odata-cli/CHANGELOG.md index 16acdc3e30..f2bcfdb001 100644 --- a/examples/odata-cli/CHANGELOG.md +++ b/examples/odata-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @sap-ux/odata-cli +## 0.16.0 + +### Minor Changes + +- 2e3c15e: Proper check for cloud ABAP systems + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + ## 0.15.16 ### Patch Changes diff --git a/examples/odata-cli/package.json b/examples/odata-cli/package.json index ad18dfa3b4..48df85813b 100644 --- a/examples/odata-cli/package.json +++ b/examples/odata-cli/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/odata-cli", - "version": "0.15.16", + "version": "0.16.0", "description": "Simple example CLI uing the @sap-ux/axios-extension module to fetch metadata and annotations from an SAP system.", "license": "Apache-2.0", "private": true, diff --git a/examples/odata-cli/src/activities.ts b/examples/odata-cli/src/activities.ts index 5920a3ec7b..ee98bbd5d8 100644 --- a/examples/odata-cli/src/activities.ts +++ b/examples/odata-cli/src/activities.ts @@ -200,7 +200,7 @@ export async function testUiServiceGenerator( TEST_TRANSPORT: string; } ): Promise { - const s4Cloud = await provider.isS4Cloud(); + const s4Cloud = await provider.isAbapCloud(); if (!s4Cloud) { logger.warn('Not an S/4 Cloud system. UI service generation might not be supported.'); } diff --git a/examples/simple-generator/CHANGELOG.md b/examples/simple-generator/CHANGELOG.md index 8f7058cf01..f3601bc440 100644 --- a/examples/simple-generator/CHANGELOG.md +++ b/examples/simple-generator/CHANGELOG.md @@ -1,5 +1,20 @@ # @sap-ux/generator-simple-fe +## 1.0.103 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + - @sap-ux/system-access@0.5.25 + +## 1.0.102 + +### Patch Changes + +- @sap-ux/fiori-elements-writer@1.3.42 +- @sap-ux/fiori-freestyle-writer@1.2.38 + ## 1.0.101 ### Patch Changes diff --git a/examples/simple-generator/package.json b/examples/simple-generator/package.json index c833d71b74..5aa51f42b4 100644 --- a/examples/simple-generator/package.json +++ b/examples/simple-generator/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/generator-simple-fe", - "version": "1.0.101", + "version": "1.0.103", "description": "Simple example of a yeoman generator for Fiori elements.", "license": "Apache-2.0", "private": true, diff --git a/packages/abap-deploy-config-inquirer/CHANGELOG.md b/packages/abap-deploy-config-inquirer/CHANGELOG.md index 9e519dabde..af2eaffe44 100644 --- a/packages/abap-deploy-config-inquirer/CHANGELOG.md +++ b/packages/abap-deploy-config-inquirer/CHANGELOG.md @@ -1,5 +1,28 @@ # @sap-ux/abap-deploy-config-inquirer +## 1.1.19 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 1.1.18 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/guided-answers-helper@0.2.0 + - @sap-ux/inquirer-common@0.6.2 + +## 1.1.17 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + - @sap-ux/system-access@0.5.25 + ## 1.1.16 ### Patch Changes diff --git a/packages/abap-deploy-config-inquirer/package.json b/packages/abap-deploy-config-inquirer/package.json index 4e0ca90b64..57f1edaa2c 100644 --- a/packages/abap-deploy-config-inquirer/package.json +++ b/packages/abap-deploy-config-inquirer/package.json @@ -6,7 +6,7 @@ "url": "https://github.com/SAP/open-ux-tools.git", "directory": "packages/abap-deploy-config-inquirer" }, - "version": "1.1.16", + "version": "1.1.19", "license": "Apache-2.0", "main": "dist/index.js", "scripts": { diff --git a/packages/abap-deploy-config-sub-generator/CHANGELOG.md b/packages/abap-deploy-config-sub-generator/CHANGELOG.md index 8900bf9a61..8fb02afea4 100644 --- a/packages/abap-deploy-config-sub-generator/CHANGELOG.md +++ b/packages/abap-deploy-config-sub-generator/CHANGELOG.md @@ -1,5 +1,25 @@ # @sap-ux/abap-deploy-config-sub-generator +## 0.0.11 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.19 + +## 0.0.10 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.18 + +## 0.0.9 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.17 +- @sap-ux/deploy-config-generator-shared@0.0.9 +- @sap-ux/abap-deploy-config-writer@0.0.72 + ## 0.0.8 ### Patch Changes diff --git a/packages/abap-deploy-config-sub-generator/package.json b/packages/abap-deploy-config-sub-generator/package.json index eb3feda3ba..1fc7d3e51d 100644 --- a/packages/abap-deploy-config-sub-generator/package.json +++ b/packages/abap-deploy-config-sub-generator/package.json @@ -6,7 +6,7 @@ "url": "https://github.com/SAP/open-ux-tools.git", "directory": "packages/abap-deploy-config-sub-generator" }, - "version": "0.0.8", + "version": "0.0.11", "license": "Apache-2.0", "main": "generators/app/index.js", "scripts": { diff --git a/packages/abap-deploy-config-writer/CHANGELOG.md b/packages/abap-deploy-config-writer/CHANGELOG.md index a594beb742..e8d1491fd4 100644 --- a/packages/abap-deploy-config-writer/CHANGELOG.md +++ b/packages/abap-deploy-config-writer/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/abap-deploy-config-writer +## 0.0.72 + +### Patch Changes + +- @sap-ux/system-access@0.5.25 + ## 0.0.71 ### Patch Changes diff --git a/packages/abap-deploy-config-writer/package.json b/packages/abap-deploy-config-writer/package.json index 8ca56d8f15..5127f2c537 100644 --- a/packages/abap-deploy-config-writer/package.json +++ b/packages/abap-deploy-config-writer/package.json @@ -6,7 +6,7 @@ "url": "https://github.com/SAP/open-ux-tools.git", "directory": "packages/abap-deploy-config-writer" }, - "version": "0.0.71", + "version": "0.0.72", "license": "Apache-2.0", "main": "dist/index.js", "scripts": { diff --git a/packages/adp-tooling/CHANGELOG.md b/packages/adp-tooling/CHANGELOG.md index 46f0bfeebf..9334053cde 100644 --- a/packages/adp-tooling/CHANGELOG.md +++ b/packages/adp-tooling/CHANGELOG.md @@ -1,5 +1,33 @@ # @sap-ux/adp-tooling +## 0.12.105 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File + +## 0.12.104 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.12.103 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + +## 0.12.102 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + - @sap-ux/system-access@0.5.25 + ## 0.12.101 ### Patch Changes diff --git a/packages/adp-tooling/package.json b/packages/adp-tooling/package.json index 6cd62546c0..93eb2c3270 100644 --- a/packages/adp-tooling/package.json +++ b/packages/adp-tooling/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aadp-tooling" }, - "version": "0.12.101", + "version": "0.12.105", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", @@ -43,6 +43,7 @@ "@sap-ux/project-input-validator": "workspace:*", "@sap-ux/system-access": "workspace:*", "@sap-ux/ui5-config": "workspace:*", + "@sap-ux/odata-service-writer": "workspace:*", "@sap-ux/i18n": "workspace:*", "adm-zip": "0.5.10", "ejs": "3.1.10", diff --git a/packages/adp-tooling/src/base/abap/manifest-service.ts b/packages/adp-tooling/src/base/abap/manifest-service.ts index 30b80d59d3..145e8a47db 100644 --- a/packages/adp-tooling/src/base/abap/manifest-service.ts +++ b/packages/adp-tooling/src/base/abap/manifest-service.ts @@ -7,7 +7,7 @@ import { isAxiosError, type AbapServiceProvider, type Ui5AppInfoContent } from ' import { getWebappFiles } from '../helper'; import type { DescriptorVariant } from '../../types'; -type DataSources = Record; +export type DataSources = Record; /** * Retrieves the inbound navigation configurations from the project's manifest. diff --git a/packages/adp-tooling/src/base/change-utils.ts b/packages/adp-tooling/src/base/change-utils.ts index eb12ea5047..656756a788 100644 --- a/packages/adp-tooling/src/base/change-utils.ts +++ b/packages/adp-tooling/src/base/change-utils.ts @@ -36,16 +36,18 @@ export function writeAnnotationChange( projectPath: string, timestamp: number, annotation: AnnotationsData['annotation'], - change: ManifestChangeProperties, + change: ManifestChangeProperties | undefined, fs: Editor ): void { try { - const changeFileName = `id_${timestamp}_addAnnotationsToOData.change`; const changesFolderPath = path.join(projectPath, DirName.Webapp, DirName.Changes); - const changeFilePath = path.join(changesFolderPath, changeFileName); const annotationsFolderPath = path.join(changesFolderPath, DirName.Annotations); - - writeChangeToFile(changeFilePath, change, fs); + if (change) { + const changeFileName = `id_${timestamp}_addAnnotationsToOData.change`; + const changeFilePath = path.join(changesFolderPath, changeFileName); + change.fileName = `${change.fileName}_addAnnotationsToOData`; + writeChangeToFile(changeFilePath, change, fs); + } if (!annotation.filePath) { const annotationsTemplate = path.join( diff --git a/packages/adp-tooling/src/preview/adp-preview.ts b/packages/adp-tooling/src/preview/adp-preview.ts index 9713ccf755..f71f0a1fee 100644 --- a/packages/adp-tooling/src/preview/adp-preview.ts +++ b/packages/adp-tooling/src/preview/adp-preview.ts @@ -11,7 +11,14 @@ import type { LayeredRepositoryService, MergedAppDescriptor } from '@sap-ux/axio import RoutesHandler from './routes-handler'; import type { AdpPreviewConfig, CommonChangeProperties, DescriptorVariant, OperationType } from '../types'; import type { Editor } from 'mem-fs-editor'; -import { addXmlFragment, isAddXMLChange, moduleNameContentMap, tryFixChange } from './change-handler'; +import { + addAnnotationFile, + addXmlFragment, + isAddAnnotationChange, + isAddXMLChange, + moduleNameContentMap, + tryFixChange +} from './change-handler'; declare global { // false positive, const can't be used here https://github.com/eslint/eslint/issues/15896 // eslint-disable-next-line no-var @@ -21,7 +28,8 @@ declare global { export const enum ApiRoutes { FRAGMENT = '/adp/api/fragment', CONTROLLER = '/adp/api/controller', - CODE_EXT = '/adp/api/code_ext/:controllerName' + CODE_EXT = '/adp/api/code_ext/:controllerName', + ANNOTATION = '/adp/api/annotation' } /** @@ -198,6 +206,11 @@ export class AdpPreview { router.post(ApiRoutes.CONTROLLER, this.routesHandler.handleWriteControllerExt as RequestHandler); router.get(ApiRoutes.CODE_EXT, this.routesHandler.handleGetControllerExtensionData as RequestHandler); + router.post(ApiRoutes.ANNOTATION, this.routesHandler.handleCreateAnnotationFile as RequestHandler); + router.get( + ApiRoutes.ANNOTATION, + this.routesHandler.handleGetAllAnnotationFilesMappedByDataSource as RequestHandler + ); } /** @@ -225,6 +238,15 @@ export class AdpPreview { if (isAddXMLChange(change)) { addXmlFragment(this.util.getProject().getSourcePath(), change, fs, logger); } + if (isAddAnnotationChange(change)) { + await addAnnotationFile( + this.util.getProject().getSourcePath(), + this.util.getProject().getRootPath(), + change, + fs, + logger + ); + } break; default: // no need to handle delete changes diff --git a/packages/adp-tooling/src/preview/change-handler.ts b/packages/adp-tooling/src/preview/change-handler.ts index 977138ee70..1700af5402 100644 --- a/packages/adp-tooling/src/preview/change-handler.ts +++ b/packages/adp-tooling/src/preview/change-handler.ts @@ -1,10 +1,16 @@ import type { Editor } from 'mem-fs-editor'; -import type { AddXMLChange, CommonChangeProperties, CodeExtChange } from '../types'; -import { join } from 'path'; -import { DirName } from '@sap-ux/project-access'; -import type { Logger } from '@sap-ux/logger'; +import type { AddXMLChange, CommonChangeProperties, CodeExtChange, AnnotationFileChange } from '../types'; +import { ChangeType } from '../types'; +import { basename, join } from 'path'; +import { DirName, FileName } from '@sap-ux/project-access'; +import type { Logger, ToolsLogger } from '@sap-ux/logger'; import { render } from 'ejs'; import { randomBytes } from 'crypto'; +import { ManifestService } from '../base/abap/manifest-service'; +import { getAdpConfig, getVariant } from '../base/helper'; +import { createAbapServiceProvider } from '@sap-ux/system-access'; +import { getAnnotationNamespaces } from '@sap-ux/odata-service-writer'; +import { generateChange } from '../writer/editors'; const OBJECT_PAGE_CUSTOM_SECTION = 'OBJECT_PAGE_CUSTOM_SECTION'; const CUSTOM_ACTION = 'CUSTOM_ACTION'; @@ -167,6 +173,17 @@ export function isAddXMLChange(change: CommonChangeProperties): change is AddXML return change.changeType === 'addXML' || change.changeType === 'addXMLAtExtensionPoint'; } +/** + * Determines whether a given change is of type `AnnotationFileChange`. + * + * @param {CommonChangeProperties} change - The change object to check. + * @returns {boolean} `true` if the `changeType` is either 'appdescr_app_addAnnotationsToOData', + * indicating the change is of type `AnnotationFileChange`. + */ +export function isAddAnnotationChange(change: CommonChangeProperties): change is AnnotationFileChange { + return change.changeType === 'appdescr_app_addAnnotationsToOData'; +} + /** * Asynchronously adds an XML fragment to the project if it doesn't already exist. * @@ -196,3 +213,63 @@ export function addXmlFragment(basePath: string, change: AddXMLChange, fs: Edito logger.error(`Failed to create XML Fragment "${fragmentPath}": ${error}`); } } + +/** + * Asynchronously adds an XML fragment to the project if it doesn't already exist. + * + * @param {string} basePath - The base path of the project. + * @param {string} projectRoot - The root path of the project. + * @param {AddXMLChange} change - The change data, including the fragment path. + * @param {Editor} fs - The mem-fs-editor instance. + * @param {Logger} logger - The logging instance. + */ +export async function addAnnotationFile( + basePath: string, + projectRoot: string, + change: AnnotationFileChange, + fs: Editor, + logger: Logger +): Promise { + const { dataSourceId, annotations, dataSource } = change.content; + const annotationDataSourceKey = annotations[0]; + const annotationUriSegments = dataSource[annotationDataSourceKey].uri.split('/'); + annotationUriSegments.shift(); + const fullPath = join(basePath, DirName.Changes, ...annotationUriSegments); + try { + const manifestService = await getManifestService(projectRoot, logger); + const metadata = await manifestService.getDataSourceMetadata(dataSourceId); + const datasoruces = await manifestService.getManifestDataSources(); + const namespaces = getAnnotationNamespaces({ metadata }); + await generateChange( + projectRoot, + ChangeType.ADD_ANNOTATIONS_TO_ODATA, + { + annotation: { + dataSource: dataSourceId, + namespaces, + serviceUrl: datasoruces[dataSourceId].uri, + fileName: basename(dataSource[annotationDataSourceKey].uri) + }, + variant: getVariant(projectRoot), + isCommand: false + }, + fs + ); + } catch (error) { + logger.error(`Failed to create Local Annotation File "${fullPath}": ${error}`); + } +} + +/** + * Returns manifest service. + * + * @param {string} basePath - The base path of the project. + * @param {Logger} logger - The logging instance. + * @returns Promise + */ +async function getManifestService(basePath: string, logger: Logger): Promise { + const variant = getVariant(basePath); + const { target, ignoreCertErrors = false } = await getAdpConfig(basePath, join(basePath, FileName.Ui5Yaml)); + const provider = await createAbapServiceProvider(target, { ignoreCertErrors }, true, logger); + return await ManifestService.initMergedManifest(provider, basePath, variant, logger as unknown as ToolsLogger); +} diff --git a/packages/adp-tooling/src/preview/routes-handler.ts b/packages/adp-tooling/src/preview/routes-handler.ts index c3286fcf34..4798fbf121 100644 --- a/packages/adp-tooling/src/preview/routes-handler.ts +++ b/packages/adp-tooling/src/preview/routes-handler.ts @@ -10,14 +10,35 @@ import type { ReaderCollection, Resource } from '@ui5/fs'; import type { NextFunction, Request, Response } from 'express'; import { TemplateFileName, HttpStatusCodes } from '../types'; -import { DirName } from '@sap-ux/project-access'; -import type { CodeExtChange } from '../types'; +import { DirName, FileName } from '@sap-ux/project-access'; +import { ChangeType, type CodeExtChange } from '../types'; +import { generateChange } from '../writer/editors'; +import { ManifestService } from '../base/abap/manifest-service'; +import type { DataSources } from '../base/abap/manifest-service'; +import { getAdpConfig, getVariant } from '../base/helper'; +import { getAnnotationNamespaces } from '@sap-ux/odata-service-writer'; +import { createAbapServiceProvider } from '@sap-ux/system-access'; interface WriteControllerBody { controllerName: string; projectId: string; } +interface AnnotationFileDetails { + fileName?: string; + annotationPath?: string; + annotationPathFromRoot?: string; + annotationExistsInWS: boolean; +} + +interface AnnotationDataSourceMap { + [key: string]: { + serviceUrl: string; + isRunningInBAS: boolean; + annotationDetails: AnnotationFileDetails; + }; +} + /** * @description Handles API Routes */ @@ -244,4 +265,140 @@ export default class RoutesHandler { next(e); } }; + /** + * Handler for writing an annotation file to the workspace. + * + * @param req Request + * @param res Response + * @param next Next Function + */ + public handleCreateAnnotationFile = async (req: Request, res: Response, next: NextFunction) => { + try { + const { dataSource, serviceUrl } = req.body as { dataSource: string; serviceUrl: string }; + + if (!dataSource) { + res.status(HttpStatusCodes.BAD_REQUEST).send('No datasource found in manifest!'); + this.logger.debug('Bad request. Could not find a datasource in manifest!'); + return; + } + const project = this.util.getProject(); + const projectRoot = project.getRootPath(); + const manifestService = await this.getManifestService(); + const metadata = await manifestService.getDataSourceMetadata(dataSource); + const namespaces = getAnnotationNamespaces({ metadata }); + const fsEditor = await generateChange( + projectRoot, + ChangeType.ADD_ANNOTATIONS_TO_ODATA, + { + annotation: { + dataSource, + namespaces, + serviceUrl: serviceUrl + }, + variant: getVariant(projectRoot), + isCommand: false + } + ); + fsEditor.commit((err) => this.logger.error(err)); + + const message = 'Annotation file created!'; + res.status(HttpStatusCodes.CREATED).send(message); + } catch (e) { + const sanitizedMsg = sanitize(e.message); + this.logger.error(sanitizedMsg); + res.status(HttpStatusCodes.INTERNAL_SERVER_ERROR).send(sanitizedMsg); + next(e); + } + }; + + /** + * Handler for mapping annotation files with datasoruce. + * + * @param _req Request + * @param res Response + * @param next Next Function + */ + public handleGetAllAnnotationFilesMappedByDataSource = async (_req: Request, res: Response, next: NextFunction) => { + try { + const isRunningInBAS = isAppStudio(); + + const manifestService = await this.getManifestService(); + const dataSources = manifestService.getManifestDataSources(); + const apiResponse: AnnotationDataSourceMap = {}; + + for (const dataSourceId in dataSources) { + if (dataSources[dataSourceId].type === 'OData') { + apiResponse[dataSourceId] = { + annotationDetails: { + annotationExistsInWS: false + }, + serviceUrl: dataSources[dataSourceId].uri, + isRunningInBAS: isRunningInBAS + }; + } + this.fillAnnotationDataSourceMap(dataSources, dataSourceId, apiResponse); + } + this.sendFilesResponse(res, apiResponse); + } catch (e) { + this.handleErrorMessage(res, next, e); + } + }; + + /** + * Add local annotation details to api response. + * + * @param dataSources DataSources + * @param dataSourceId string + * @param apiResponse AnnotationDataSourceMap + */ + private fillAnnotationDataSourceMap( + dataSources: DataSources, + dataSourceId: string, + apiResponse: AnnotationDataSourceMap + ): void { + const project = this.util.getProject(); + const getPath = (projectPath: string, relativePath: string): string => + path.join(projectPath, DirName.Changes, relativePath).split(path.sep).join(path.posix.sep); + const annotations = dataSources[dataSourceId].settings?.annotations + ? [...dataSources[dataSourceId].settings.annotations].reverse() + : []; + for (const annotation of annotations) { + const annotationSetting = dataSources[annotation]; + if (annotationSetting.type === 'ODataAnnotation') { + const ui5NamespaceUri = `ui5://${project.getNamespace()}`; + if (annotationSetting.uri.startsWith(ui5NamespaceUri)) { + const localAnnotationUri = annotationSetting.uri.replace(ui5NamespaceUri, ''); + const annotationPath = getPath(project.getSourcePath(), localAnnotationUri); + const annotationPathFromRoot = getPath(project.getName(), localAnnotationUri); + const annotationExists = fs.existsSync(annotationPath); + apiResponse[dataSourceId].annotationDetails = { + fileName: path.parse(localAnnotationUri).base, + annotationPath: os.platform() === 'win32' ? `/${annotationPath}` : annotationPath, + annotationPathFromRoot, + annotationExistsInWS: annotationExists + }; + } + if (apiResponse[dataSourceId].annotationDetails.annotationExistsInWS) { + break; + } + } + } + } + + /** + * Returns manifest service. + * + * @returns Promise + */ + private async getManifestService(): Promise { + const project = this.util.getProject(); + const basePath = project.getRootPath(); + const variant = getVariant(basePath); + const { target, ignoreCertErrors = false } = await getAdpConfig( + basePath, + path.join(basePath, FileName.Ui5Yaml) + ); + const provider = await createAbapServiceProvider(target, { ignoreCertErrors }, true, this.logger); + return await ManifestService.initMergedManifest(provider, basePath, variant, this.logger); + } } diff --git a/packages/adp-tooling/src/types.ts b/packages/adp-tooling/src/types.ts index c42d028878..311979601b 100644 --- a/packages/adp-tooling/src/types.ts +++ b/packages/adp-tooling/src/types.ts @@ -200,6 +200,22 @@ export interface CodeExtChange extends CommonChangeProperties { }; } +export interface AnnotationFileChange extends CommonChangeProperties { + changeType: 'appdescr_app_addAnnotationsToOData'; + creation: string; + content: { + dataSourceId: string; + annotations: string[]; + annotationsInsertPosition: 'END'; + dataSource: { + [fileName: string]: { + uri: string; + type: 'ODataAnnotation'; + }; + }; + }; +} + export interface ParamCheck { shouldApply: boolean; value: string | undefined; @@ -357,6 +373,8 @@ export type GeneratorData = T extends ChangeType.ADD_ANNOT export interface AnnotationsData { variant: DescriptorVariant; + /** Flag for differentiating the annotation creation call from CLI and from CPE */ + isCommand: boolean; annotation: { /** Optional name of the annotation file. */ fileName?: string; diff --git a/packages/adp-tooling/src/writer/changes/writers/annotations-writer.ts b/packages/adp-tooling/src/writer/changes/writers/annotations-writer.ts index 8a086bd520..91ced76724 100644 --- a/packages/adp-tooling/src/writer/changes/writers/annotations-writer.ts +++ b/packages/adp-tooling/src/writer/changes/writers/annotations-writer.ts @@ -70,7 +70,11 @@ export class AnnotationsWriter implements IWriter { } const content = this.constructContent(data); const timestamp = Date.now(); - const change = getChange(variant, timestamp, content, ChangeType.ADD_ANNOTATIONS_TO_ODATA); + let change; + // When created via command change need to be created else change is created via RTA. + if (data.isCommand) { + change = getChange(variant, timestamp, content, ChangeType.ADD_ANNOTATIONS_TO_ODATA); + } writeAnnotationChange(this.projectPath, timestamp, data.annotation, change, this.fs); } } diff --git a/packages/adp-tooling/test/unit/preview/adp-preview.test.ts b/packages/adp-tooling/test/unit/preview/adp-preview.test.ts index 282f3e7316..386e905773 100644 --- a/packages/adp-tooling/test/unit/preview/adp-preview.test.ts +++ b/packages/adp-tooling/test/unit/preview/adp-preview.test.ts @@ -6,11 +6,16 @@ import type { Editor } from 'mem-fs-editor'; import { type Logger, ToolsLogger } from '@sap-ux/logger'; import type { ReaderCollection } from '@ui5/fs'; import type { SuperTest, Test } from 'supertest'; -import { readFileSync, existsSync, writeFileSync } from 'fs'; +import * as fs from 'fs'; import { AdpPreview } from '../../../src/preview/adp-preview'; import type { AdpPreviewConfig, CommonChangeProperties } from '../../../src'; +import * as helper from '../../../src/base/helper'; +import * as editors from '../../../src/writer/editors'; +import * as manifestService from '../../../src/base/abap/manifest-service'; import { addXmlFragment, tryFixChange } from '../../../src/preview/change-handler'; +import * as systemAccess from '@sap-ux/system-access/dist/base/connect'; +import * as serviceWriter from '@sap-ux/odata-service-writer/dist/data/annotations'; interface GetFragmentsResponse { fragments: { fragmentName: string }[]; @@ -65,12 +70,12 @@ jest.mock('fs', () => ({ copyFileSync: jest.fn() })); -const mockWriteFileSync = writeFileSync as jest.Mock; -const mockExistsSync = existsSync as jest.Mock; +const mockWriteFileSync = fs.writeFileSync as jest.Mock; +const mockExistsSync = fs.existsSync as jest.Mock; describe('AdaptationProject', () => { const backend = 'https://sap.example'; - const descriptorVariant = readFileSync( + const descriptorVariant = fs.readFileSync( join(__dirname, '../../fixtures/adaptation-project/webapp', 'manifest.appdescr_variant'), 'utf-8' ); @@ -115,6 +120,9 @@ describe('AdaptationProject', () => { }, getName() { return 'adp.project'; + }, + getNamespace() { + return 'adp/project'; } }; } @@ -468,7 +476,61 @@ describe('AdaptationProject', () => { middlewareUtil, logger ); + jest.spyOn(helper, 'getVariant').mockReturnValue({ + content: [], + id: 'adp/project', + layer: 'VENDOR', + namespace: 'test', + reference: 'adp/project' + }); + jest.spyOn(helper, 'getAdpConfig').mockResolvedValue({ + target: { + destination: 'testDestination' + }, + ignoreCertErrors: false + }); + jest.spyOn(systemAccess, 'createAbapServiceProvider').mockResolvedValue({} as any); + jest.spyOn(manifestService.ManifestService, 'initMergedManifest').mockResolvedValue({ + getDataSourceMetadata: jest.fn().mockResolvedValue(` + + + + + + +`), + getManifestDataSources: jest.fn().mockReturnValue({ + mainService: { + type: 'OData', + uri: 'main/service/uri', + settings: { + annotations: ['annotation0'] + } + }, + annotation0: { + type: 'ODataAnnotation', + uri: `ui5://adp/project/annotation0.xml` + }, + secondaryService: { + type: 'OData', + uri: 'secondary/service/uri', + settings: { + annotations: [] + } + } + }) + } as any); + jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true).mockReturnValue(false); + jest.spyOn(serviceWriter, 'getAnnotationNamespaces').mockReturnValue([ + { + namespace: 'com.sap.gateway.srvd.c_salesordermanage_sd.v0001', + alias: 'test' + } + ]); + jest.spyOn(editors, 'generateChange').mockResolvedValue({ + commit: jest.fn().mockResolvedValue('commited') + } as any); const app = express(); app.use(express.json()); adp.addApis(app); @@ -647,5 +709,27 @@ describe('AdaptationProject', () => { expect(e.message).toEqual(errorMsg); } }); + test('POST /adp/api/annotation - throws error when controller name is undefined', async () => { + const response = await server.post('/adp/api/annotation').send({}).expect(400); + const message = response.text; + expect(message).toBe('No datasource found in manifest!'); + }); + test('POST /adp/api/annotation', async () => { + const response = await server + .post('/adp/api/annotation') + .send({ dataSource: 'exampleDataSource', serviceUrl: 'sap/opu/data' }) + .expect(201); + + const message = response.text; + expect(message).toBe('Annotation file created!'); + }); + test('GET /adp/api/annotation', async () => { + const response = await server.get('/adp/api/annotation').send().expect(200); + + const message = response.text; + expect(message).toMatchInlineSnapshot( + `"{\\"mainService\\":{\\"annotationDetails\\":{\\"fileName\\":\\"annotation0.xml\\",\\"annotationPath\\":\\"//adp.project/webapp/changes/annotation0.xml\\",\\"annotationPathFromRoot\\":\\"adp.project/changes/annotation0.xml\\"},\\"serviceUrl\\":\\"main/service/uri\\",\\"isRunningInBAS\\":false},\\"secondaryService\\":{\\"annotationDetails\\":{\\"annotationExistsInWS\\":false},\\"serviceUrl\\":\\"secondary/service/uri\\",\\"isRunningInBAS\\":false}}"` + ); + }); }); }); diff --git a/packages/adp-tooling/test/unit/preview/change-handler.test.ts b/packages/adp-tooling/test/unit/preview/change-handler.test.ts index d3e53eefd1..c56c6a3514 100644 --- a/packages/adp-tooling/test/unit/preview/change-handler.test.ts +++ b/packages/adp-tooling/test/unit/preview/change-handler.test.ts @@ -7,12 +7,19 @@ import type { Editor } from 'mem-fs-editor'; import * as crypto from 'crypto'; import { + addAnnotationFile, addXmlFragment, + isAddAnnotationChange, isAddXMLChange, moduleNameContentMap, tryFixChange } from '../../../src/preview/change-handler'; -import type { AddXMLChange, CommonChangeProperties } from '../../../src'; +import type { AddXMLChange, CommonChangeProperties, AnnotationFileChange } from '../../../src'; +import * as manifestService from '../../../src/base/abap/manifest-service'; +import * as helper from '../../../src/base/helper'; +import * as editors from '../../../src/writer/editors'; +import * as systemAccess from '@sap-ux/system-access/dist/base/connect'; +import * as serviceWriter from '@sap-ux/odata-service-writer/dist/data/annotations'; describe('change-handler', () => { describe('moduleNameContentMap', () => { @@ -467,4 +474,142 @@ id=\\"btn-30303030\\"" }); }); }); + + describe('isAddAnnotationChange', () => { + it('should return true for change objects with changeType "addXML"', () => { + const addAnnotationChange = { + changeType: 'appdescr_app_addAnnotationsToOData', + content: { + serviceUrl: 'test/service/mainService' + } + } as unknown as CommonChangeProperties; + + expect(isAddAnnotationChange(addAnnotationChange)).toBe(true); + }); + + it('should return false for change objects with a different changeType', () => { + const addXMLChange = { + changeType: 'addXML', + content: { + fragmentPath: 'fragments/share.fragment.xml' + } + } as unknown as CommonChangeProperties; + + expect(isAddAnnotationChange(addXMLChange)).toBe(false); + }); + + it('should return false for change objects without a changeType', () => { + const unknownChange = { + content: {} + } as unknown as CommonChangeProperties; + + expect(isAddAnnotationChange(unknownChange as any)).toBe(false); + }); + }); + + describe('addAnnotationFile', () => { + jest.spyOn(serviceWriter, 'getAnnotationNamespaces').mockReturnValue([ + { + namespace: 'com.sap.test.serviceorder.v0001', + alias: 'test' + } + ]); + jest.spyOn(systemAccess, 'createAbapServiceProvider').mockResolvedValue({} as any); + jest.spyOn(manifestService.ManifestService, 'initMergedManifest').mockResolvedValue({ + getDataSourceMetadata: jest.fn().mockResolvedValue(` + + + + + + +`), + getManifestDataSources: jest.fn().mockReturnValue({ + mainService: { + type: 'OData', + uri: 'main/service/uri', + settings: { + annotations: ['annotation0'] + } + }, + annotation0: { + type: 'ODataAnnotation', + uri: `ui5://adp/project/annotation0.xml` + }, + secondaryService: { + type: 'OData', + uri: 'secondary/service/uri', + settings: { + annotations: [] + } + } + }) + } as any); + jest.spyOn(helper, 'getVariant').mockReturnValue({ + content: [], + id: 'adp/project', + layer: 'VENDOR', + namespace: 'test', + reference: 'adp/project' + }); + jest.spyOn(helper, 'getAdpConfig').mockResolvedValue({ + target: { + destination: 'testDestination' + }, + ignoreCertErrors: false + }); + const generateChangeSpy = jest.spyOn(editors, 'generateChange').mockResolvedValue({ + commit: jest.fn().mockResolvedValue('commited') + } as any); + const mockFs = { + exists: jest.fn(), + copy: jest.fn(), + read: jest.fn(), + write: jest.fn() + }; + + const mockLogger = { + info: jest.fn(), + error: jest.fn() + }; + + const fragmentName = 'Share'; + const change = { + changeType: 'appdescr_app_addAnnotationsToOData', + content: { + annotationsInsertPosition: `END`, + annotations: ['annotations.annotation13434343'], + dataSource: { + 'annotations.annotation13434343': { + type: 'ODataAnnotation', + uri: 'test/mainService/$metadata' + } + }, + dataSourceId: 'mainService' + } + } as unknown as AnnotationFileChange; + + beforeEach(() => { + mockFs.exists.mockClear(); + mockFs.copy.mockClear(); + mockFs.read.mockClear(); + mockFs.write.mockClear(); + mockLogger.info.mockClear(); + mockLogger.error.mockClear(); + }); + + it('should call the geneate change', async () => { + mockFs.exists.mockReturnValue(false); + + await addAnnotationFile( + 'projectRoot/webapp', + 'projectRoot', + change, + mockFs as unknown as Editor, + mockLogger as unknown as Logger + ); + + expect(generateChangeSpy).toHaveBeenCalled(); + }); + }); }); diff --git a/packages/adp-tooling/test/unit/writer/changes/writers/index.test.ts b/packages/adp-tooling/test/unit/writer/changes/writers/index.test.ts index 2406681628..11f31772f2 100644 --- a/packages/adp-tooling/test/unit/writer/changes/writers/index.test.ts +++ b/packages/adp-tooling/test/unit/writer/changes/writers/index.test.ts @@ -59,7 +59,8 @@ describe('AnnotationsWriter', () => { fileName: '', dataSource: '/sap/opu/odata/source', filePath: '/mock/path/to/annotation/file.xml' - } + }, + isCommand: true }; const writer = new AnnotationsWriter({} as Editor, mockProjectPath); @@ -87,7 +88,8 @@ describe('AnnotationsWriter', () => { fileName: '', dataSource: '/sap/opu/odata/source', filePath: '' - } + }, + isCommand: true }; const writer = new AnnotationsWriter({} as Editor, mockProjectPath); @@ -115,7 +117,8 @@ describe('AnnotationsWriter', () => { fileName: '', dataSource: '/sap/opu/odata/source', filePath: 'file.xml' - } + }, + isCommand: true }; const writer = new AnnotationsWriter({} as Editor, mockProjectPath); diff --git a/packages/adp-tooling/tsconfig.json b/packages/adp-tooling/tsconfig.json index f3a4da0f1b..a3cd92f5ba 100644 --- a/packages/adp-tooling/tsconfig.json +++ b/packages/adp-tooling/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../logger" }, + { + "path": "../odata-service-writer" + }, { "path": "../project-access" }, diff --git a/packages/app-config-writer/CHANGELOG.md b/packages/app-config-writer/CHANGELOG.md index 0665a80cdb..fb700bd5de 100644 --- a/packages/app-config-writer/CHANGELOG.md +++ b/packages/app-config-writer/CHANGELOG.md @@ -1,5 +1,30 @@ # @sap-ux/app-config-writer +## 0.5.13 + +### Patch Changes + +- 04f0547: fix: preview config update default configs + +## 0.5.12 + +### Patch Changes + +- 20ab455: fix: prerequisites check for preview conversion + +## 0.5.11 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + +## 0.5.10 + +### Patch Changes + +- d964a24: feat: add option to convert test runners to preview-config command + ## 0.5.9 ### Patch Changes diff --git a/packages/app-config-writer/package.json b/packages/app-config-writer/package.json index b9c8913673..4e369efdd8 100644 --- a/packages/app-config-writer/package.json +++ b/packages/app-config-writer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/app-config-writer", "description": "Add or update configuration for SAP Fiori tools application", - "version": "0.5.9", + "version": "0.5.13", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", @@ -40,7 +40,8 @@ "i18next": "20.6.1", "mem-fs": "2.1.0", "mem-fs-editor": "9.4.0", - "prompts": "2.4.2" + "prompts": "2.4.2", + "semver": "7.6.3" }, "devDependencies": { "@sap-ux/preview-middleware": "workspace:*", @@ -48,6 +49,7 @@ "@types/mem-fs": "1.1.2", "@types/mem-fs-editor": "7.0.1", "@types/prompts": "2.4.4", + "@types/semver": "7.5.8", "axios": "1.7.4", "nock": "13.4.0" }, diff --git a/packages/app-config-writer/src/preview-config/index.ts b/packages/app-config-writer/src/preview-config/index.ts index 540f343942..f32f2062f1 100644 --- a/packages/app-config-writer/src/preview-config/index.ts +++ b/packages/app-config-writer/src/preview-config/index.ts @@ -1,8 +1,8 @@ import { checkPrerequisites, getExplicitApprovalToAdjustFiles } from './prerequisites'; import { create as createStorage } from 'mem-fs'; import { create, type Editor } from 'mem-fs-editor'; -import { deleteNoLongerUsedFiles, renameDefaultSandboxes } from './preview-files'; -import { updatePreviewMiddlewareConfigs } from './ui5-yaml'; +import { deleteNoLongerUsedFiles, renameDefaultSandboxes, renameDefaultTestFiles } from './preview-files'; +import { updatePreviewMiddlewareConfigs, updateDefaultTestConfig } from './ui5-yaml'; import { updateVariantsCreationScript } from './package-json'; import { type ToolsLogger } from '@sap-ux/logger'; @@ -14,14 +14,19 @@ import { type ToolsLogger } from '@sap-ux/logger'; * Corresponding files which are used for the preview are renamed or deleted. * * @param basePath - base path to be used for the conversion - * @param logger logger to report info to the user - * @param fs - file system reference + * @param options - options for the conversion + * @param options.convertTests - if set to true, then test suite and test runners fill be included in the conversion + * @param options.logger - logger to report info to the user + * @param options.fs - file system reference * @returns file system reference */ -export async function convertToVirtualPreview(basePath: string, logger?: ToolsLogger, fs?: Editor): Promise { - if (!fs) { - fs = create(createStorage()); - } +export async function convertToVirtualPreview( + basePath: string, + options: { convertTests?: boolean; logger?: ToolsLogger; fs?: Editor } +): Promise { + const fs = options.fs ?? create(createStorage()); + const logger = options.logger; + const convertTests = options.convertTests ?? false; if (!(await checkPrerequisites(basePath, fs, logger))) { throw Error('The prerequisites are not met. For more information, see the log messages above.'); @@ -32,8 +37,12 @@ export async function convertToVirtualPreview(basePath: string, logger?: ToolsLo return fs; } - await updatePreviewMiddlewareConfigs(fs, basePath, logger); + await updatePreviewMiddlewareConfigs(fs, basePath, convertTests, logger); await renameDefaultSandboxes(fs, basePath, logger); + if (convertTests) { + await renameDefaultTestFiles(fs, basePath, logger); + await updateDefaultTestConfig(fs, basePath, logger); + } await deleteNoLongerUsedFiles(fs, basePath, logger); await updateVariantsCreationScript(fs, basePath, logger); diff --git a/packages/app-config-writer/src/preview-config/package-json.ts b/packages/app-config-writer/src/preview-config/package-json.ts index fdbb0d4160..1b01ad5e2d 100644 --- a/packages/app-config-writer/src/preview-config/package-json.ts +++ b/packages/app-config-writer/src/preview-config/package-json.ts @@ -1,5 +1,5 @@ import { basename, join } from 'path'; -import { extractYamlConfigFileName } from './ui5-yaml'; +import { extractYamlConfigFileName, isTestPath } from './ui5-yaml'; import { generateVariantsConfig } from '../variants-config'; import type { Editor } from 'mem-fs-editor'; import type { ToolsLogger } from '@sap-ux/logger'; @@ -49,8 +49,9 @@ export function extractUrlDetails(script: string): { intent: FlpConfig['intent'] | undefined; } { //extract the URL from the 'open' command of the script - let url = / (?:--open|-o|--o) ([^"]?\S*)/.exec(script)?.[1] ?? undefined; - url = url?.startsWith('"') ? url.slice(1) : url; + let url = / (?:--open|-o|--o) (\S*)/.exec(script)?.[1] ?? undefined; + //delete double or single quotes from the URL + url = url?.replace(/['"]/g, ''); //extract the path from the URL const path = /^[^?#]+\.html/.exec(url ?? '')?.[0] ?? undefined; @@ -83,20 +84,25 @@ export function extractUrlDetails(script: string): { * * @param scriptName - the name of the script from the package.json file * @param script - the content of the script from the package.json file + * @param convertTests - indicator if test suite and test runner should be included in the conversion (default: false) * @returns indicator if the script is valid */ -export function isValidPreviewScript(scriptName: string, script: string | undefined): boolean { +export function isValidPreviewScript( + scriptName: string, + script: string | undefined, + convertTests: boolean = false +): boolean { const isValidScriptName = scriptName != 'start-variants-management' && scriptName != 'start-control-property-editor'; //eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const startsWebServer = !!(script?.includes('ui5 serve') || script?.includes('fiori run')); - const { path } = extractUrlDetails(script ?? ''); - const opensTest = path?.includes('qunit.html'); + const opensTest = isTestPath(path); const opensIndexHtml = path === 'index.html'; - return isValidScriptName && startsWebServer && !opensTest && !opensIndexHtml; + //tests are only relevant if the conversion of test runners is excluded + return isValidScriptName && startsWebServer && !opensIndexHtml && (convertTests ? true : !opensTest); } /** diff --git a/packages/app-config-writer/src/preview-config/prerequisites.ts b/packages/app-config-writer/src/preview-config/prerequisites.ts index 06fa58223b..3f656b810d 100644 --- a/packages/app-config-writer/src/preview-config/prerequisites.ts +++ b/packages/app-config-writer/src/preview-config/prerequisites.ts @@ -4,6 +4,38 @@ import type { Editor } from 'mem-fs-editor'; import type { Package } from '@sap-ux/project-access'; import type { PromptObject } from 'prompts'; import type { ToolsLogger } from '@sap-ux/logger'; +import { satisfies, valid } from 'semver'; + +/** + * Check if the version of the given package is lower than the minimal version. + * + * @param packageJson - the package.json file content + * @param dependencyName - the name of the (dev)dependency to check + * @param minVersionInfo - the minimal version to check against + * @param mandatory - (default true) if the existence of the dependency is mandatory + * @returns indicator if the version is lower than the minimal version + */ +function isLowerThanMinimalVersion( + packageJson: Package, + dependencyName: string, + minVersionInfo: string, + mandatory: boolean = true +): boolean { + let versionInfo = packageJson?.devDependencies?.[dependencyName] ?? packageJson?.dependencies?.[dependencyName]; + if (!versionInfo) { + // In case no dependency is found we assume the minimal version is not met depending on the mandatory flag + return mandatory; + } + if (versionInfo === 'latest') { + // In case of 'latest' we know the minimal version is met + return false; + } + if (valid(versionInfo)) { + // In case of a valid version we add a prefix to make it a range + versionInfo = `<=${versionInfo}`; + } + return !satisfies(minVersionInfo, versionInfo); +} /** * Check if the prerequisites for the conversion are met. @@ -35,14 +67,20 @@ export async function checkPrerequisites(basePath: string, fs: Editor, logger?: prerequisitesMet = false; } - const ui5CliVersion = packageJson?.devDependencies?.['@ui5/cli'] ?? packageJson?.dependencies?.['@ui5/cli'] ?? '0'; - if (parseInt(ui5CliVersion.split('.')[0], 10) < 3) { + if (isLowerThanMinimalVersion(packageJson, '@ui5/cli', '3.0.0')) { logger?.error( 'UI5 CLI version 3.0.0 or higher is required to convert the preview to virtual files. For more information, see https://sap.github.io/ui5-tooling/v3/updates/migrate-v3.' ); prerequisitesMet = false; } + if (isLowerThanMinimalVersion(packageJson, '@sap/ux-ui5-tooling', '1.15.4', false)) { + logger?.error( + 'UX UI5 Tooling version 1.15.4 or higher is required to convert the preview to virtual files. For more information, see https://www.npmjs.com/package/@sap/ux-ui5-tooling.' + ); + prerequisitesMet = false; + } + const ui5MiddlewareMockserverExists = !!packageJson?.devDependencies?.['@sap-ux/ui5-middleware-fe-mockserver'] || !!packageJson?.dependencies?.['@sap-ux/ui5-middleware-fe-mockserver']; @@ -69,7 +107,7 @@ export async function getExplicitApprovalToAdjustFiles(): Promise { name: 'approval', initial: false, message: - 'The converter will rename the HTML files and delete the JS and TS files used for the existing preview functionality and configure virtual files instead. Do you want to proceed with the conversion?' + 'The converter will rename the HTML files and delete the JS and TS files used for the existing preview functionality and configure virtual endpoints instead. Do you want to proceed with the conversion?' }; return Boolean((await prompt([question])).approval); } diff --git a/packages/app-config-writer/src/preview-config/preview-files.ts b/packages/app-config-writer/src/preview-config/preview-files.ts index add5220707..d82e29d6eb 100644 --- a/packages/app-config-writer/src/preview-config/preview-files.ts +++ b/packages/app-config-writer/src/preview-config/preview-files.ts @@ -2,12 +2,13 @@ import { basename, join } from 'path'; import { getWebappPath } from '@sap-ux/project-access'; import type { Editor } from 'mem-fs-editor'; import type { ToolsLogger } from '@sap-ux/logger'; +import { TEST_CONFIG_DEFAULTS } from './ui5-yaml'; const renameMessage = (filename: string): string => `Renamed '${filename}' to '${filename.slice( 0, -5 - )}_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://www.npmjs.com/package/preview-middleware#migration.`; + )}_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://github.com/SAP/open-ux-tools/tree/main/packages/preview-middleware#migration.`; /** * Renames the sandbox file which is used in a given script. @@ -32,7 +33,7 @@ export async function renameSandbox(fs: Editor, path: string, logger?: ToolsLogg ) { logger?.debug(`The file '${basename(path)}', has already been renamed. Skipping renaming.`); } else { - logger?.warn(`The file '${basename(path)}', has not been found. Skipping renaming.`); + logger?.info(`The file '${basename(path)}', has not been found. Skipping renaming.`); } } @@ -53,6 +54,21 @@ export async function renameDefaultSandboxes(fs: Editor, basePath: string, logge } } +/** + * Renames the default test suite and runner files. + * + * The default files are 'testsuite.qunit.html', 'integration/opaTests.qunit.html' and 'unit/unitTests.qunit.html' located under webapp/test. + * + * @param fs - file system reference + * @param basePath - base path to be used for the conversion + * @param logger logger to report info to the user + */ +export async function renameDefaultTestFiles(fs: Editor, basePath: string, logger?: ToolsLogger): Promise { + for (const path of Object.values(TEST_CONFIG_DEFAULTS).map((config) => config.path)) { + await renameSandbox(fs, join(await getWebappPath(basePath), path), logger); + } +} + /** * Deletes the *.js and *.ts files which are no longer used for the virtual preview. * diff --git a/packages/app-config-writer/src/preview-config/ui5-yaml.ts b/packages/app-config-writer/src/preview-config/ui5-yaml.ts index 1fa9593c5c..bf27e56fbf 100644 --- a/packages/app-config-writer/src/preview-config/ui5-yaml.ts +++ b/packages/app-config-writer/src/preview-config/ui5-yaml.ts @@ -10,11 +10,16 @@ import type { FlpConfig, MiddlewareConfig as PreviewConfig, DefaultFlpPath, - DefaultIntent + DefaultIntent, + TestConfigDefaults as PreviewTestConfigDefaults } from '@sap-ux/preview-middleware'; import type { PreviewConfigOptions } from '../types'; import type { ToolsLogger } from '@sap-ux/logger'; +type ArrayElement = ArrayType[number]; + +type PreviewTestConfig = ArrayElement['test']>; + const DEFAULT_FLP_PATH: DefaultFlpPath = '/test/flp.html'; const DEFAULT_INTENT: DefaultIntent = { @@ -22,6 +27,29 @@ const DEFAULT_INTENT: DefaultIntent = { action: 'preview' }; +export const TEST_CONFIG_DEFAULTS: Record>> = { + qunit: { + path: '/test/unitTests.qunit.html', + framework: 'QUnit' + }, + opa5: { + path: '/test/opaTests.qunit.html', + framework: 'OPA5' + }, + testsuite: { + path: '/test/testsuite.qunit.html', + framework: 'Testsuite' + } +} as Omit< + PreviewTestConfigDefaults, + | PreviewTestConfigDefaults['testsuite']['init'] + | PreviewTestConfigDefaults['testsuite']['pattern'] + | PreviewTestConfigDefaults['opa5']['init'] + | PreviewTestConfigDefaults['opa5']['pattern'] + | PreviewTestConfigDefaults['qunit']['init'] + | PreviewTestConfigDefaults['qunit']['pattern'] +>; + /** * Checks if a script can be converted based on the used UI5 yaml configuration file. * @@ -58,41 +86,37 @@ function isUi5YamlToBeConverted( * @param ui5Yaml - the name of the UI5 yaml configuration file * @param scriptName - the name of the script from the package.json file * @param script - the content of the script from the package.json file + * @param convertTests - indicator if test suite and test runner should be included in the conversion * @param logger logger to report info to the user * @returns indicator if the UI5 yaml configuration file has already been converted */ -async function isUi5YamlAlreadyConverted( +async function isUi5YamlFlpPathAlreadyConverted( fs: Editor, basePath: string, ui5Yaml: string, scriptName: string, script: string, + convertTests: boolean, logger?: ToolsLogger ): Promise { - if ( + const yamlConfigAlreadyAdjusted = Object.keys( fs.dump(basePath, (file) => { return file.basename === ui5Yaml && file.state === 'modified'; }) - ).length === 0 - ) { - return false; - } + ).length > 0; const flpPath = ((await getPreviewMiddleware(undefined, basePath, ui5Yaml, fs)) as CustomMiddleware) ?.configuration?.flp?.path; const { path: scriptPath } = extractUrlDetails(script); - if (flpPath != scriptPath) { + if (yamlConfigAlreadyAdjusted && flpPath != scriptPath && (convertTests ? !isTestPath(scriptPath) : true)) { logger?.warn( - `Skipping script'${scriptName}', because another script also refers to UI5 YAML configuration file, '${ui5Yaml}'. Adjust the 'flp.path' property in the UI5 YAML configuration file to the correct endpoint or create a separate UI5 YAML configuration file for script '${scriptName}'. ${ui5Yaml} currently uses ${ + `Skipping script '${scriptName}', because another script also refers to UI5 YAML configuration file, '${ui5Yaml}'. Adjust the 'flp.path' property in the UI5 YAML configuration file to the correct endpoint or create a separate UI5 YAML configuration file for script '${scriptName}'. ${ui5Yaml} currently uses ${ flpPath ?? DEFAULT_FLP_PATH } whereas script '${scriptName}' uses '${scriptPath}'.` ); - } else { - logger?.info( - `Skipping script '${scriptName}', because the UI5 YAML configuration file '${ui5Yaml}' has already been adjusted based on another script.` - ); + return true; } - return true; + return false; } /** @@ -102,14 +126,31 @@ async function isUi5YamlAlreadyConverted( * @param configuration - the preview configuration * @returns indicator if the path is an FLP path */ -function pathIsFlpPath(path: string | undefined, configuration: PreviewConfig): boolean { +function isFlpPath(path: string | undefined, configuration: PreviewConfig): boolean { if (!path) { return false; } - const isNotRtaEditorPath = configuration.rta?.editors?.every((editor) => editor.path !== path) ?? true; - const isNotTestPath = configuration.test?.every((test) => test.path !== path) ?? true; + const isRtaEditorPath = configuration.rta?.editors?.some((editor) => editor.path === path) ?? false; + return !isRtaEditorPath && !isTestPath(path, configuration); +} - return isNotRtaEditorPath && isNotTestPath; +/** + * Check if the path is a test path. + * 1) path matches pattern '**.qunit.html' + * 2) path is being used as test configuration path in yaml configuration. + * + * @param path - the path + * @param configuration - the preview configuration + * @returns indicator if the path is a test path + */ +export function isTestPath(path: string | undefined, configuration?: PreviewConfig): boolean { + if (!path) { + return false; + } + if (path.includes('.qunit.html')) { + return true; + } + return configuration?.test?.some((testConfig) => testConfig.path === path) ?? false; } /** @@ -214,20 +255,26 @@ export function updatePreviewMiddlewareConfig( //copy of configuration to avoid ending up with an empty configuration object in some cases const configuration = { ...newMiddlewareConfig.configuration }; - configuration.flp = configuration.flp ?? {}; let writeConfig = false; - //check path and respect defaults - if (pathIsFlpPath(path, configuration) && !path?.includes(DEFAULT_FLP_PATH)) { - configuration.flp.path = path; - writeConfig = true; - } - //check intent and respect defaults - if (intent && `${intent?.object}-${intent?.action}` !== defaultIntent) { - configuration.flp.intent = { - object: intent.object, - action: intent.action - }; + if (isFlpPath(path, configuration)) { + //adjust path but respect defaults + if (!path?.includes(DEFAULT_FLP_PATH)) { + configuration.flp = configuration.flp ?? {}; + configuration.flp.path = path; + writeConfig = true; + } + //adjust intent but respect defaults + if (intent && `${intent?.object}-${intent?.action}` !== defaultIntent) { + configuration.flp = configuration.flp ?? {}; + configuration.flp.intent = { + object: intent.object, + action: intent.action + }; + writeConfig = true; + } + } else if (isTestPath(path, configuration)) { + configuration.test = updateTestConfig(configuration.test, path); writeConfig = true; } @@ -238,6 +285,99 @@ export function updatePreviewMiddlewareConfig( return newMiddlewareConfig as CustomMiddleware; } +/** + * Update the test configuration. + * + * @param testConfiguration - the test configuration + * @param path - the path + * @returns the updated test configuration + */ +export function updateTestConfig( + testConfiguration: PreviewConfig['test'], + path: string | undefined +): PreviewConfig['test'] { + testConfiguration = testConfiguration ?? []; + + let framework: PreviewTestConfig['framework'] | undefined; + if (path?.includes('testsuite.qunit.html')) { + framework = 'Testsuite'; + } else if (path?.includes('opaTests.qunit.html')) { + framework = 'OPA5'; + } else if (path?.includes('unitTests.qunit.html')) { + framework = 'QUnit'; + } + + if (!framework) { + return testConfiguration; + } + + const defaultPath = TEST_CONFIG_DEFAULTS[framework.toLowerCase()].path; + const testConfig = testConfiguration.find((test) => test.framework === framework); + if (testConfig) { + testConfig.path = path; + if (testConfig.path === defaultPath) { + //sanitize default path + delete testConfig.path; + } + } else if (path?.includes(defaultPath)) { + testConfiguration.push({ framework }); + } else { + testConfiguration.push({ framework, path }); + } + return testConfiguration; +} + +/** + * Updates the default test configurations in the 'ui5.yaml' in case no test config exists in any UI5 configuration file. + * + * @param fs - file system reference + * @param basePath - base path to be used for the conversion + * @param logger logger to report info to the user + */ +export async function updateDefaultTestConfig(fs: Editor, basePath: string, logger?: ToolsLogger): Promise { + const ui5YamlFileNames = await getAllUi5YamlFileNames(basePath, fs); + for (const ui5Yaml of ui5YamlFileNames.filter((ui5Yaml) => ui5Yaml !== FileName.Ui5Yaml)) { + const ui5YamlConfig = await readUi5Yaml(basePath, ui5Yaml, fs); + const previewMiddleware = (await getPreviewMiddleware(ui5YamlConfig)) as Partial< + CustomMiddleware + >; + if (previewMiddleware?.configuration?.test) { + return; + } + } + let ui5YamlConfig: UI5Config; + try { + ui5YamlConfig = await readUi5Yaml(basePath, FileName.Ui5Yaml, fs); + } catch (error) { + logger?.warn( + `The UI5 YAML configuration file 'ui5.yaml', can't be updated to support test frameworks: '${error}'. Please manually add the test configuration to the UI5 YAML configuration file used for testing according to https://github.com/SAP/open-ux-tools/tree/main/packages/preview-middleware#configuration-option-test.` + ); + return; + } + const previewMiddleware = (await getPreviewMiddleware(ui5YamlConfig)) as CustomMiddleware; + + for (const defaultConfig of Object.values(TEST_CONFIG_DEFAULTS)) { + if ( + previewMiddleware.configuration?.test?.some( + (testConfig) => testConfig.framework.toLowerCase() === defaultConfig.framework.toLowerCase() + ) + ) { + //do not touch existing test config + break; + } + previewMiddleware.configuration.test = updateTestConfig( + previewMiddleware.configuration.test, + defaultConfig.path + ); + logger?.info( + `The UI5 YAML configuration file 'ui5.yaml', has been updated to support the test framework '${defaultConfig.framework}'. Please consider transferring the test configuration to the UI5 YAML configuration file used for testing.` + ); + } + ui5YamlConfig.updateCustomMiddleware(previewMiddleware); + const yamlPath = join(basePath, FileName.Ui5Yaml); + fs.write(yamlPath, ui5YamlConfig.toString()); +} + /** * Updates the preview middleware configurations according to the scripts they are being used in package.json. * @@ -247,11 +387,13 @@ export function updatePreviewMiddlewareConfig( * * @param fs - file system reference * @param basePath - base path to be used for the conversion + * @param convertTests - indicator if test suite and test runner should be included in the conversion * @param logger logger to report info to the user */ export async function updatePreviewMiddlewareConfigs( fs: Editor, basePath: string, + convertTests: boolean, logger?: ToolsLogger ): Promise { const ui5YamlFileNames = await getAllUi5YamlFileNames(basePath, fs); @@ -259,7 +401,7 @@ export async function updatePreviewMiddlewareConfigs( const packageJsonPath = join(basePath, 'package.json'); const packageJson = fs.readJSON(packageJsonPath) as Package | undefined; for (const [scriptName, script] of Object.entries(packageJson?.scripts ?? {})) { - if (!script || !isValidPreviewScript(scriptName, script)) { + if (!script || !isValidPreviewScript(scriptName, script, convertTests)) { continue; } @@ -268,7 +410,7 @@ export async function updatePreviewMiddlewareConfigs( if ( !isUi5YamlToBeConverted(ui5Yaml, scriptName, ui5YamlFileNames, logger) || - (await isUi5YamlAlreadyConverted(fs, basePath, ui5Yaml, scriptName, script, logger)) + (await isUi5YamlFlpPathAlreadyConverted(fs, basePath, ui5Yaml, scriptName, script, convertTests, logger)) ) { continue; } diff --git a/packages/app-config-writer/test/fixtures/preview-config/various-configs/ui5-existing-preview-middleware.yaml b/packages/app-config-writer/test/fixtures/preview-config/various-configs/ui5-existing-preview-middleware.yaml index 81dd0eb835..deef5fe0ad 100644 --- a/packages/app-config-writer/test/fixtures/preview-config/various-configs/ui5-existing-preview-middleware.yaml +++ b/packages/app-config-writer/test/fixtures/preview-config/various-configs/ui5-existing-preview-middleware.yaml @@ -14,9 +14,9 @@ server: action: dance test: - framework: OPA5 - path: /test/integration/opaTests.qunit.html + path: test/integration/opaTests.qunit.html - framework: QUnit - path: /test/unit/unitTests.qunit.html + path: test/unit/unitTests.qunit.html - framework: Testsuite rta: editors: diff --git a/packages/app-config-writer/test/unit/preview-config/__snapshots__/index.test.ts.snap b/packages/app-config-writer/test/unit/preview-config/__snapshots__/index.test.ts.snap new file mode 100644 index 0000000000..61f0143a29 --- /dev/null +++ b/packages/app-config-writer/test/unit/preview-config/__snapshots__/index.test.ts.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`index convertToVirtualPreview convert project to virtual preview (including tests w/o own yaml config) 1`] = ` +"specVersion: '4.0' +metadata: +name: com.sap.cap.fe.ts.sample +server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + configuration: + test: + - framework: Testsuite + path: yet/another/path.html + - framework: OPA5 + - framework: QUnit +" +`; + +exports[`index convertToVirtualPreview convert project to virtual preview (including tests with own yaml config) 1`] = ` +" + specVersion: '4.0' + metadata: + name: com.sap.cap.fe.ts.sample + server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + " +`; + +exports[`index convertToVirtualPreview convert project to virtual preview (including tests with own yaml config) 2`] = ` +" + specVersion: '4.0' + metadata: + name: com.sap.cap.fe.ts.sample + server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + configuration: + test: + - framework: \\"Testsuite\\" + path: \\"yet/another/path.html\\" + - framework: \\"OPA5\\" + " +`; diff --git a/packages/app-config-writer/test/unit/preview-config/__snapshots__/ui5-yaml.test.ts.snap b/packages/app-config-writer/test/unit/preview-config/__snapshots__/ui5-yaml.test.ts.snap index 93109feede..a1f6835d00 100644 --- a/packages/app-config-writer/test/unit/preview-config/__snapshots__/ui5-yaml.test.ts.snap +++ b/packages/app-config-writer/test/unit/preview-config/__snapshots__/ui5-yaml.test.ts.snap @@ -39,7 +39,7 @@ server: " `; -exports[`update preview middleware config default ui5.yaml w/o index.html 2`] = `"{\\"scripts\\":{\\"start\\":\\"fiori run --open \\\\\\"test/flpSandbox.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile\\\\\\"\\",\\"start-index\\":\\"fiori run --open \\\\\\"index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile\\\\\\"\\"},\\"devDependencies\\":{\\"@sap/ux-ui5-tooling\\":\\"1.15.4\\"}}"`; +exports[`update preview middleware config default ui5.yaml w/o index.html 2`] = `"{\\"scripts\\":{\\"start\\":\\"fiori run --open \\\\\\"test/flpSandbox.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile\\\\\\"\\",\\"start-index\\":\\"fiori run --open \\\\\\"index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile\\\\\\"\\",\\"start-index2\\":\\"fiori run --open 'index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile'\\",\\"start-index3\\":\\"fiori run --open index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile\\"},\\"devDependencies\\":{\\"@sap/ux-ui5-tooling\\":\\"1.15.4\\"}}"`; exports[`update preview middleware config deprecated tools preview w/o theme 1`] = ` "specVersion: \\"3.1\\" @@ -82,6 +82,32 @@ server: exports[`update preview middleware config deprecated tools preview with theme 2`] = `"{\\"scripts\\":{\\"ui:mockserver\\":\\"fiori run --open \\\\\\"test/flpSandbox.html?sap-ui-xx-viewCache=false#Chicken-dance\\\\\\" --config ./ui5-deprecated-tools-preview-theme.yaml\\",\\"start-variants-management\\":\\"ui5 serve --o chicken.html\\"},\\"devDependencies\\":{\\"@sap/ux-ui5-tooling\\":\\"1.15.1\\"}}"`; +exports[`update preview middleware config deprecated tools preview with theme and tests 1`] = ` +"specVersion: \\"3.1\\" +metadata: + name: v2lrop0909 +type: application +server: + customMiddleware: + - name: fiori-tools-preview + afterMiddleware: compression + configuration: + flp: + theme: sap_horizon + path: test/flpSandbox.html + intent: + object: Chicken + action: dance + test: + - framework: OPA5 + path: test/integration/opaTests.qunit.html + - framework: QUnit + path: test/unit/unitTests.qunit.html +" +`; + +exports[`update preview middleware config deprecated tools preview with theme and tests 2`] = `"{\\"scripts\\":{\\"ui:mockserver\\":\\"fiori run --open \\\\\\"test/flpSandbox.html?sap-ui-xx-viewCache=false#Chicken-dance\\\\\\" --config ./ui5-deprecated-tools-preview-theme.yaml\\",\\"start-variants-management\\":\\"ui5 serve --o chicken.html\\",\\"ui:opa5\\":\\"fiori run -o test/integration/opaTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-deprecated-tools-preview-theme.yaml\\",\\"ui:unit\\":\\"fiori run -o test/unit/unitTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-deprecated-tools-preview-theme.yaml\\"},\\"devDependencies\\":{\\"@sap/ux-ui5-tooling\\":\\"1.15.1\\"}}"`; + exports[`update preview middleware config existing RTA script 1`] = ` "specVersion: \\"3.1\\" metadata: @@ -99,9 +125,9 @@ server: action: dance test: - framework: OPA5 - path: /test/integration/opaTests.qunit.html + path: test/integration/opaTests.qunit.html - framework: QUnit - path: /test/unit/unitTests.qunit.html + path: test/unit/unitTests.qunit.html - framework: Testsuite rta: editors: @@ -128,9 +154,9 @@ server: action: dance test: - framework: OPA5 - path: /test/integration/opaTests.qunit.html + path: test/integration/opaTests.qunit.html - framework: QUnit - path: /test/unit/unitTests.qunit.html + path: test/unit/unitTests.qunit.html - framework: Testsuite rta: editors: @@ -140,6 +166,35 @@ server: exports[`update preview middleware config existing preview middleware 2`] = `"{\\"scripts\\":{\\"ui:mockserver\\":\\"fiori run -o localService/index.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\",\\"ui:opa5\\":\\"fiori run -o test/integration/opaTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\",\\"ui:unit\\":\\"fiori run -o test/unit/unitTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\"},\\"devDependencies\\":{\\"@sap-ux/preview-middleware\\":\\"0.16.102\\"}}"`; +exports[`update preview middleware config existing preview middleware with tests 1`] = ` +"specVersion: \\"3.1\\" +metadata: + name: v2lrop0909 +type: application +server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + configuration: + flp: + path: localService/index.html + intent: + object: Chicken + action: dance + test: + - framework: OPA5 + path: test/integration/opaTests.qunit.html + - framework: QUnit + path: test/unit/unitTests.qunit.html + - framework: Testsuite + rta: + editors: + - path: preview.html +" +`; + +exports[`update preview middleware config existing preview middleware with tests 2`] = `"{\\"scripts\\":{\\"ui:mockserver\\":\\"fiori run -o localService/index.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\",\\"ui:opa5\\":\\"fiori run -o test/integration/opaTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\",\\"ui:unit\\":\\"fiori run -o test/unit/unitTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml\\"},\\"devDependencies\\":{\\"@sap-ux/preview-middleware\\":\\"0.16.102\\"}}"`; + exports[`update preview middleware config existing start-variants-management and start-control-property-editor script 1`] = ` "specVersion: \\"3.1\\" metadata: diff --git a/packages/app-config-writer/test/unit/preview-config/index.test.ts b/packages/app-config-writer/test/unit/preview-config/index.test.ts index 837869d60e..b52736bf81 100644 --- a/packages/app-config-writer/test/unit/preview-config/index.test.ts +++ b/packages/app-config-writer/test/unit/preview-config/index.test.ts @@ -1,6 +1,6 @@ import { create as createStorage } from 'mem-fs'; import { create, type Editor } from 'mem-fs-editor'; -import { convertToVirtualPreview } from '../../../src/preview-config'; +import { convertToVirtualPreview } from '../../../src'; import { join } from 'path'; import { ToolsLogger } from '@sap-ux/logger'; import * as packageJson from '../../../src/preview-config/package-json'; @@ -37,7 +37,81 @@ describe('index', () => { test('convert project to virtual preview', async () => { getExplicitApprovalToAdjustFilesSpy.mockResolvedValue(true); - await convertToVirtualPreview(basePath, logger, fs); + await convertToVirtualPreview(basePath, { convertTests: false, logger, fs }); + expect(checkPrerequisitesSpy).toHaveBeenCalled(); + expect(getExplicitApprovalToAdjustFilesSpy).toHaveBeenCalled(); + expect(updatePreviewMiddlewareConfigsSpy).toHaveBeenCalled(); + expect(renameDefaultSandboxesSpy).toHaveBeenCalled(); + expect(deleteNoLongerUsedFilesSpy).toHaveBeenCalled(); + expect(updateVariantsCreationScriptSpy).toHaveBeenCalled(); + }); + + test('convert project to virtual preview (including tests w/o own yaml config)', async () => { + fs.write( + join(basePath, 'ui5.yaml'), + ` + specVersion: '4.0' + metadata: + name: com.sap.cap.fe.ts.sample + server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + configuration: + test: + - framework: "Testsuite" + path: "yet/another/path.html" + - framework: "OPA5" + ` + ); + + getExplicitApprovalToAdjustFilesSpy.mockResolvedValue(true); + + await convertToVirtualPreview(basePath, { convertTests: true, logger, fs }); + expect(fs.read(join(basePath, 'ui5.yaml'))).toMatchSnapshot(); + expect(checkPrerequisitesSpy).toHaveBeenCalled(); + expect(getExplicitApprovalToAdjustFilesSpy).toHaveBeenCalled(); + expect(updatePreviewMiddlewareConfigsSpy).toHaveBeenCalled(); + expect(renameDefaultSandboxesSpy).toHaveBeenCalled(); + expect(deleteNoLongerUsedFilesSpy).toHaveBeenCalled(); + expect(updateVariantsCreationScriptSpy).toHaveBeenCalled(); + }); + + test('convert project to virtual preview (including tests with own yaml config)', async () => { + fs.write( + join(basePath, 'ui5.yaml'), + ` + specVersion: '4.0' + metadata: + name: com.sap.cap.fe.ts.sample + server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + ` + ); + fs.write( + join(basePath, 'ui5-test.yaml'), + ` + specVersion: '4.0' + metadata: + name: com.sap.cap.fe.ts.sample + server: + customMiddleware: + - name: preview-middleware + afterMiddleware: compression + configuration: + test: + - framework: "Testsuite" + path: "yet/another/path.html" + - framework: "OPA5" + ` + ); + getExplicitApprovalToAdjustFilesSpy.mockResolvedValue(true); + + await convertToVirtualPreview(basePath, { convertTests: true, logger, fs }); + expect(fs.read(join(basePath, 'ui5.yaml'))).toMatchSnapshot(); + expect(fs.read(join(basePath, 'ui5-test.yaml'))).toMatchSnapshot(); expect(checkPrerequisitesSpy).toHaveBeenCalled(); expect(getExplicitApprovalToAdjustFilesSpy).toHaveBeenCalled(); expect(updatePreviewMiddlewareConfigsSpy).toHaveBeenCalled(); @@ -53,9 +127,9 @@ describe('index', () => { JSON.stringify({ devDependencies: { '@ui5/cli': '2.0.0' } }) ); - await expect(convertToVirtualPreview(missingPrerequisitesPath, logger, fs)).rejects.toThrowError( - `The prerequisites are not met. For more information, see the log messages above.` - ); + await expect( + convertToVirtualPreview(missingPrerequisitesPath, { convertTests: false, logger, fs }) + ).rejects.toThrowError(`The prerequisites are not met. For more information, see the log messages above.`); expect(checkPrerequisitesSpy).toHaveBeenCalled(); expect(getExplicitApprovalToAdjustFilesSpy).not.toHaveBeenCalled(); expect(updatePreviewMiddlewareConfigsSpy).not.toHaveBeenCalled(); @@ -67,7 +141,7 @@ describe('index', () => { test('do not convert project to virtual preview - missing approval', async () => { getExplicitApprovalToAdjustFilesSpy.mockResolvedValue(false); - await convertToVirtualPreview(basePath, logger, fs); + await convertToVirtualPreview(basePath, { convertTests: false, logger, fs }); expect(checkPrerequisitesSpy).toHaveBeenCalled(); expect(getExplicitApprovalToAdjustFilesSpy).toHaveBeenCalled(); expect(errorLogMock).toHaveBeenCalledWith( diff --git a/packages/app-config-writer/test/unit/preview-config/prerequisites.test.ts b/packages/app-config-writer/test/unit/preview-config/prerequisites.test.ts index 945cc09be8..84c2cc0a75 100644 --- a/packages/app-config-writer/test/unit/preview-config/prerequisites.test.ts +++ b/packages/app-config-writer/test/unit/preview-config/prerequisites.test.ts @@ -40,6 +40,72 @@ describe('prerequisites', () => { ); }); + test('check prerequisites with UI5 cli ^3 dependency', async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ devDependencies: { '@ui5/cli': '^3', 'cds-plugin-ui5': '6.6.6' } }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeTruthy(); + }); + + test('check prerequisites with UI5 cli ^2 dependency', async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ devDependencies: { '@ui5/cli': '^2', 'cds-plugin-ui5': '6.6.6' } }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeFalsy(); + expect(errorLogMock).toHaveBeenCalledWith( + 'UI5 CLI version 3.0.0 or higher is required to convert the preview to virtual files. For more information, see https://sap.github.io/ui5-tooling/v3/updates/migrate-v3.' + ); + }); + + test('check prerequisites with UI5 ux-ui5-tooling 1.16.0 dependency', async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ + devDependencies: { '@sap/ux-ui5-tooling': '1.16.0', '@ui5/cli': '^3', 'cds-plugin-ui5': '6.6.6' } + }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeTruthy(); + }); + + test('check prerequisites with UI5 ux-ui5-tooling 1 dependency', async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ + devDependencies: { '@sap/ux-ui5-tooling': '1', '@ui5/cli': '^3', 'cds-plugin-ui5': '6.6.6' } + }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeTruthy(); + }); + + test("check prerequisites with UI5 ux-ui5-tooling 'latest' dependency", async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ + devDependencies: { '@sap/ux-ui5-tooling': 'latest', '@ui5/cli': '^3', 'cds-plugin-ui5': '6.6.6' } + }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeTruthy(); + }); + + test('check prerequisites with UI5 ux-ui5-tooling 1.15.0 dependency', async () => { + fs.write( + join(basePath, 'package.json'), + JSON.stringify({ devDependencies: { '@sap/ux-ui5-tooling': '1.15.0' } }) + ); + + expect(await checkPrerequisites(basePath, fs, logger)).toBeFalsy(); + expect(errorLogMock).toHaveBeenCalledWith( + 'UX UI5 Tooling version 1.15.4 or higher is required to convert the preview to virtual files. For more information, see https://www.npmjs.com/package/@sap/ux-ui5-tooling.' + ); + }); + test('check prerequisites w/o mockserver dependency', async () => { fs.write(join(basePath, 'package.json'), JSON.stringify({ devDependencies: { '@ui5/cli': '3.0.0' } })); diff --git a/packages/app-config-writer/test/unit/preview-config/preview-files.test.ts b/packages/app-config-writer/test/unit/preview-config/preview-files.test.ts index f8c0f51ba7..53ea7f6b7e 100644 --- a/packages/app-config-writer/test/unit/preview-config/preview-files.test.ts +++ b/packages/app-config-writer/test/unit/preview-config/preview-files.test.ts @@ -38,10 +38,10 @@ describe('preview-files', () => { '"dummy content flpSandboxMockserver"' ); expect(infoLogMock).toHaveBeenCalledWith( - `Renamed 'flpSandbox.html' to 'flpSandbox_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://www.npmjs.com/package/preview-middleware#migration.` + `Renamed 'flpSandbox.html' to 'flpSandbox_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://github.com/SAP/open-ux-tools/tree/main/packages/preview-middleware#migration.` ); expect(infoLogMock).toHaveBeenCalledWith( - `Renamed 'flpSandboxMockserver.html' to 'flpSandboxMockserver_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://www.npmjs.com/package/preview-middleware#migration.` + `Renamed 'flpSandboxMockserver.html' to 'flpSandboxMockserver_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://github.com/SAP/open-ux-tools/tree/main/packages/preview-middleware#migration.` ); }); @@ -75,7 +75,7 @@ describe('preview-files', () => { const path = join(basePath, 'test', 'IdoNotExist.html'); await renameSandbox(fs, path, logger); - expect(warnLogMock).toHaveBeenCalledWith(`The file 'IdoNotExist.html', has not been found. Skipping renaming.`); + expect(infoLogMock).toHaveBeenCalledWith(`The file 'IdoNotExist.html', has not been found. Skipping renaming.`); }); test('skip renaming for files which have already been renamed', async () => { @@ -85,7 +85,7 @@ describe('preview-files', () => { await renameSandbox(fs, path, logger); expect(infoLogMock).toHaveBeenCalledWith( - `Renamed 'ImAlreadyRenamed.html' to 'ImAlreadyRenamed_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://www.npmjs.com/package/preview-middleware#migration.` + `Renamed 'ImAlreadyRenamed.html' to 'ImAlreadyRenamed_old.html'. This file is no longer needed for the preview functionality. If you have not modified this file, you can delete it. If you have modified this file, move the modified content to a custom init script for the preview middleware. For more information, see https://github.com/SAP/open-ux-tools/tree/main/packages/preview-middleware#migration.` ); await renameSandbox(fs, path, logger); diff --git a/packages/app-config-writer/test/unit/preview-config/ui5-yaml.test.ts b/packages/app-config-writer/test/unit/preview-config/ui5-yaml.test.ts index 16215fd533..a3aa211b6b 100644 --- a/packages/app-config-writer/test/unit/preview-config/ui5-yaml.test.ts +++ b/packages/app-config-writer/test/unit/preview-config/ui5-yaml.test.ts @@ -55,7 +55,7 @@ describe('update preview middleware config', () => { const text = (filename: string) => `The UI5 YAML configuration file '${filename}', is not used in any preview script. Outdated preview middleware will be adjusted, if necessary.`; - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); expect(warnLogMock).toHaveBeenCalledWith(text('ui5-deprecated-tools-preview.yaml')); expect(fs.read(join(variousConfigsPath, 'ui5-deprecated-tools-preview.yaml'))).toMatchSnapshot(); @@ -81,7 +81,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); expect(warnLogMock).toHaveBeenCalledWith( `Skipping script 'invalid', which refers to the UI5 YAML configuration file 'ui5-invalid.yaml'. An error occurred when reading 'ui5-invalid.yaml': This file does not comply with the schema.` @@ -101,7 +101,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); expect(warnLogMock).toHaveBeenCalledWith( `Skipping script 'not:found', because the UI5 YAML configuration file, 'ui5-unavailable.yaml', could not be found.` @@ -121,11 +121,34 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-no-middleware.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); + test('deprecated tools preview with theme and tests', async () => { + const variousConfigsPath = join(basePath, 'various-configs'); + const packageJson = { + scripts: { + 'ui:mockserver': + 'fiori run --open "test/flpSandbox.html?sap-ui-xx-viewCache=false#Chicken-dance" --config ./ui5-deprecated-tools-preview-theme.yaml', + 'start-variants-management': 'ui5 serve --o chicken.html', + 'ui:opa5': + 'fiori run -o test/integration/opaTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-deprecated-tools-preview-theme.yaml', + 'ui:unit': + 'fiori run -o test/unit/unitTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-deprecated-tools-preview-theme.yaml' + }, + 'devDependencies': { + '@sap/ux-ui5-tooling': '1.15.1' + } + }; + fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); + + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, true, logger); + expect(fs.read(join(variousConfigsPath, 'ui5-deprecated-tools-preview-theme.yaml'))).toMatchSnapshot(); + expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); + }); + test('deprecated tools preview with theme', async () => { const variousConfigsPath = join(basePath, 'various-configs'); const packageJson = { @@ -140,7 +163,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-deprecated-tools-preview-theme.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); @@ -159,7 +182,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-deprecated-tools-preview.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); @@ -177,11 +200,33 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-existing-tools-preview.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); + test('existing preview middleware with tests', async () => { + const variousConfigsPath = join(basePath, 'various-configs'); + const packageJson = { + scripts: { + 'ui:mockserver': + 'fiori run -o localService/index.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml', + 'ui:opa5': + 'fiori run -o test/integration/opaTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml', + 'ui:unit': + 'fiori run -o test/unit/unitTests.qunit.html?sap-ui-xx-viewCache=false#Chicken-dance --config ./ui5-existing-preview-middleware.yaml' + }, + 'devDependencies': { + '@sap-ux/preview-middleware': '0.16.102' + } + }; + fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); + + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, true, logger); + expect(fs.read(join(variousConfigsPath, 'ui5-existing-preview-middleware.yaml'))).toMatchSnapshot(); + expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); + }); + test('existing preview middleware', async () => { const variousConfigsPath = join(basePath, 'various-configs'); const packageJson = { @@ -199,7 +244,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-existing-preview-middleware.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); @@ -218,7 +263,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-existing-preview-middleware.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); @@ -239,7 +284,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-deprecated-tools-preview.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); }); @@ -258,7 +303,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5-no-middleware.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); @@ -269,7 +314,9 @@ describe('update preview middleware config', () => { const packageJson = { scripts: { 'start': 'fiori run --open "test/flpSandbox.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile"', - 'start-index': 'fiori run --open "index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile"' + 'start-index': 'fiori run --open "index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile"', + 'start-index2': "fiori run --open 'index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile'", + 'start-index3': 'fiori run --open index.html?sap-ui-xx-viewCache=false#v4lropconvert0711-tile' }, 'devDependencies': { '@sap/ux-ui5-tooling': '1.15.4' @@ -277,7 +324,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); @@ -297,7 +344,7 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(warnLogMock).toHaveBeenCalledTimes(5); }); @@ -315,13 +362,13 @@ describe('update preview middleware config', () => { }; fs.write(join(variousConfigsPath, 'package.json'), JSON.stringify(packageJson)); - await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, logger); + await updatePreviewMiddlewareConfigs(fs, variousConfigsPath, false, logger); expect(fs.read(join(variousConfigsPath, 'ui5.yaml'))).toMatchSnapshot(); expect(fs.read(join(variousConfigsPath, 'package.json'))).toMatchSnapshot(); expect(warnLogMock).toHaveBeenCalledWith( - `Skipping script'start2', because another script also refers to UI5 YAML configuration file, 'ui5.yaml'. Adjust the 'flp.path' property in the UI5 YAML configuration file to the correct endpoint or create a separate UI5 YAML configuration file for script 'start2'. ui5.yaml currently uses test/flpSandbox.html whereas script 'start2' uses 'test/flpSandboxMockserver.html'.` + `Skipping script 'start2', because another script also refers to UI5 YAML configuration file, 'ui5.yaml'. Adjust the 'flp.path' property in the UI5 YAML configuration file to the correct endpoint or create a separate UI5 YAML configuration file for script 'start2'. ui5.yaml currently uses test/flpSandbox.html whereas script 'start2' uses 'test/flpSandboxMockserver.html'.` ); }); }); diff --git a/packages/axios-extension/CHANGELOG.md b/packages/axios-extension/CHANGELOG.md index d174b9439c..7c2e9a4867 100644 --- a/packages/axios-extension/CHANGELOG.md +++ b/packages/axios-extension/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/axios-extension +## 1.18.0 + +### Minor Changes + +- 2e3c15e: Proper check for cloud ABAP systems + ## 1.17.8 ### Patch Changes diff --git a/packages/axios-extension/package.json b/packages/axios-extension/package.json index 0498d189f9..cc94632f94 100644 --- a/packages/axios-extension/package.json +++ b/packages/axios-extension/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/axios-extension", - "version": "1.17.8", + "version": "1.18.0", "description": "Extension of the Axios module adding convenience methods to interact with SAP systems especially with OData services.", "repository": { "type": "git", diff --git a/packages/axios-extension/src/abap/abap-service-provider.ts b/packages/axios-extension/src/abap/abap-service-provider.ts index 644d272987..f35c306032 100644 --- a/packages/axios-extension/src/abap/abap-service-provider.ts +++ b/packages/axios-extension/src/abap/abap-service-provider.ts @@ -90,16 +90,11 @@ export class AbapServiceProvider extends ServiceProvider { * * @returns true if it an S/4HANA cloud system */ - public async isS4Cloud(): Promise { + public async isAbapCloud(): Promise { if (this.atoSettings === undefined) { await this.getAtoInfo(); } - return ( - this.atoSettings.tenantType === TenantType.Customer && - this.atoSettings.operationsType === 'C' && - this.atoSettings.developmentPrefix !== '' && - this.atoSettings.developmentPackage !== '' - ); + return this.atoSettings.tenantType === TenantType.Customer && this.atoSettings.operationsType === 'C'; } /** @@ -138,8 +133,8 @@ export class AbapServiceProvider extends ServiceProvider { } else { throw new Error('not implemented yet'); } - Object.defineProperty(service, 'isS4Cloud', { - get: this.isS4Cloud.bind(this) + Object.defineProperty(service, 'isAbapCloud', { + get: this.isAbapCloud.bind(this) }); return service; } diff --git a/packages/axios-extension/test/abap/abap-adt-service.test.ts b/packages/axios-extension/test/abap/abap-adt-service.test.ts index 0684a048f3..f0cc0c9b37 100644 --- a/packages/axios-extension/test/abap/abap-adt-service.test.ts +++ b/packages/axios-extension/test/abap/abap-adt-service.test.ts @@ -412,7 +412,7 @@ describe('Use existing connection session', () => { const provider = createForAbapOnCloud(existingCookieConfigForAbapOnCloudStandalone as any); expect(provider.cookies.toString()).toBe('sap-usercontext=sap-client=100; SAP_SESSIONID_Y05_100=abc'); - expect(await provider.isS4Cloud()).toBe(false); + expect(await provider.isAbapCloud()).toBe(false); expect(attachUaaAuthInterceptorSpy).toBeCalledTimes(0); expect(Uaa.prototype.getAccessToken).toBeCalledTimes(0); expect(Uaa.prototype.getAccessTokenWithClientCredentials).toBeCalledTimes(0); @@ -442,7 +442,7 @@ describe('Use existing connection session', () => { const cloneObj = cloneDeep(configForAbapOnCloud); delete cloneObj.service.uaa.username; const provider = createForAbapOnCloud(cloneObj as any); - expect(await provider.isS4Cloud()).toBe(true); + expect(await provider.isAbapCloud()).toBe(true); expect(await provider.user()).toBe('emailTest'); expect(Uaa.prototype.getAccessToken).toBeCalledTimes(3); expect(Uaa.prototype.getAccessTokenWithClientCredentials).toBeCalledTimes(0); @@ -468,7 +468,7 @@ describe('Use existing connection session', () => { } }; const provider = createForAbapOnCloud(configForAbapOnCloudWithAuthentication as any); - expect(await provider.isS4Cloud()).toBe(false); + expect(await provider.isAbapCloud()).toBe(false); expect(await provider.user()).toBe('email'); expect(Uaa.prototype.getAccessToken).toBeCalledTimes(0); expect(Uaa.prototype.getAccessTokenWithClientCredentials).toBeCalledTimes(2); diff --git a/packages/axios-extension/test/abap/abap-service-provider.test.ts b/packages/axios-extension/test/abap/abap-service-provider.test.ts index 0b5283c805..d469d06d74 100644 --- a/packages/axios-extension/test/abap/abap-service-provider.test.ts +++ b/packages/axios-extension/test/abap/abap-service-provider.test.ts @@ -95,7 +95,7 @@ describe('AbapServiceProvider', () => { .get(AdtServices.ATO_SETTINGS) .replyWithFile(200, join(__dirname, 'mockResponses/atoSettingsS4C.xml')); const abapSrvProvider = await createForAbap(config); - expect(await abapSrvProvider.isS4Cloud()).toBe(true); + expect(await abapSrvProvider.isAbapCloud()).toBe(true); expect(await abapSrvProvider.getAtoInfo()).toStrictEqual(ato); }); @@ -105,7 +105,7 @@ describe('AbapServiceProvider', () => { .replyWithFile(200, join(__dirname, 'mockResponses/discovery-1.xml')) .get(AdtServices.ATO_SETTINGS) .replyWithFile(200, join(__dirname, 'mockResponses/atoSettingsNotS4C.xml')); - expect(await createForAbap(config).isS4Cloud()).toBe(false); + expect(await createForAbap(config).isAbapCloud()).toBe(false); }); test('No request if known', async () => { @@ -115,8 +115,8 @@ describe('AbapServiceProvider', () => { .replyWithFile(200, join(__dirname, 'mockResponses/discovery-1.xml')) .get(AdtServices.ATO_SETTINGS) .replyWithFile(200, join(__dirname, 'mockResponses/atoSettingsS4C.xml')); - await provider.isS4Cloud(); - expect(await provider.isS4Cloud()).toBe(true); + await provider.isAbapCloud(); + expect(await provider.isAbapCloud()).toBe(true); }); test('Request failed', async () => { @@ -125,7 +125,7 @@ describe('AbapServiceProvider', () => { .replyWithFile(200, join(__dirname, 'mockResponses/discovery-1.xml')) .get(AdtServices.ATO_SETTINGS) .replyWithError('Something went wrong'); - expect(await createForAbap(config).isS4Cloud()).toBe(false); + expect(await createForAbap(config).isAbapCloud()).toBe(false); }); }); diff --git a/packages/backend-proxy-middleware/CHANGELOG.md b/packages/backend-proxy-middleware/CHANGELOG.md index 0224bee6ba..f6fcbcf8d1 100644 --- a/packages/backend-proxy-middleware/CHANGELOG.md +++ b/packages/backend-proxy-middleware/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/backend-proxy-middleware +## 0.8.28 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + ## 0.8.27 ### Patch Changes diff --git a/packages/backend-proxy-middleware/package.json b/packages/backend-proxy-middleware/package.json index a8ddcc76f9..f8c556ced6 100644 --- a/packages/backend-proxy-middleware/package.json +++ b/packages/backend-proxy-middleware/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Abackend-proxy-middleware" }, - "version": "0.8.27", + "version": "0.8.28", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", diff --git a/packages/cf-deploy-config-inquirer/CHANGELOG.md b/packages/cf-deploy-config-inquirer/CHANGELOG.md index da0211ad84..e25f8f6169 100644 --- a/packages/cf-deploy-config-inquirer/CHANGELOG.md +++ b/packages/cf-deploy-config-inquirer/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/cf-deploy-config-inquirer +## 0.1.16 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.1.15 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + ## 0.1.14 ### Patch Changes diff --git a/packages/cf-deploy-config-inquirer/package.json b/packages/cf-deploy-config-inquirer/package.json index 1442a19500..5c9d403f57 100644 --- a/packages/cf-deploy-config-inquirer/package.json +++ b/packages/cf-deploy-config-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/cf-deploy-config-inquirer", "description": "Prompts module that can provide prompts for cf deployment config writer", - "version": "0.1.14", + "version": "0.1.16", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/control-property-editor-common/CHANGELOG.md b/packages/control-property-editor-common/CHANGELOG.md index e927d13f97..bcf45019e5 100644 --- a/packages/control-property-editor-common/CHANGELOG.md +++ b/packages/control-property-editor-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux-private/control-property-editor-common +## 0.5.9 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File + ## 0.5.8 ### Patch Changes diff --git a/packages/control-property-editor-common/package.json b/packages/control-property-editor-common/package.json index 700ed7943f..7a395140e5 100644 --- a/packages/control-property-editor-common/package.json +++ b/packages/control-property-editor-common/package.json @@ -3,7 +3,7 @@ "displayName": "Control Property Editor Common", "description": "A common module for Control Property Editor react app and ui5", "license": "Apache-2.0", - "version": "0.5.8", + "version": "0.5.9", "main": "dist/index.js", "repository": { "type": "git", diff --git a/packages/control-property-editor-common/src/api.ts b/packages/control-property-editor-common/src/api.ts index cdd0762a03..ad01be69aa 100644 --- a/packages/control-property-editor-common/src/api.ts +++ b/packages/control-property-editor-common/src/api.ts @@ -160,6 +160,7 @@ export interface PendingOtherChange { type: typeof PENDING_CHANGE_TYPE; kind: typeof UNKNOWN_CHANGE_KIND; isActive: boolean; + title?: string; changeType: string; fileName: string; } @@ -171,6 +172,7 @@ export interface PendingControlChange { changeType: string; controlId: string; fileName: string; + title?: string; } export type PendingChange = @@ -199,6 +201,7 @@ export interface UnknownSavedChange { kind: typeof UNKNOWN_CHANGE_KIND; fileName: string; changeType: string; + title?: string; controlId?: string; timestamp?: number; } @@ -209,6 +212,7 @@ export interface SavedControlChange { controlId: string; fileName: string; changeType: string; + title?: string; timestamp?: number; } diff --git a/packages/control-property-editor/CHANGELOG.md b/packages/control-property-editor/CHANGELOG.md index cf06d1624b..b0295106cb 100644 --- a/packages/control-property-editor/CHANGELOG.md +++ b/packages/control-property-editor/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/control-property-editor +## 0.5.31 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File + ## 0.5.30 ### Patch Changes diff --git a/packages/control-property-editor/package.json b/packages/control-property-editor/package.json index 4f2bc725d6..96338cf837 100644 --- a/packages/control-property-editor/package.json +++ b/packages/control-property-editor/package.json @@ -3,7 +3,7 @@ "displayName": "Control Property Editor", "description": "Control Property Editor", "license": "Apache-2.0", - "version": "0.5.30", + "version": "0.5.31", "main": "dist/app.js", "repository": { "type": "git", diff --git a/packages/control-property-editor/src/panels/changes/ChangeStack.tsx b/packages/control-property-editor/src/panels/changes/ChangeStack.tsx index 14e3a77a26..bae77777fb 100644 --- a/packages/control-property-editor/src/panels/changes/ChangeStack.tsx +++ b/packages/control-property-editor/src/panels/changes/ChangeStack.tsx @@ -202,6 +202,7 @@ function handleUnknownChange(change: Change): Item { return { fileName: change.fileName, header: true, + ...(change?.kind === 'unknown' && change.type === 'saved' && change.title && { title: change.title }), timestamp: change.type === SAVED_CHANGE_TYPE ? change.timestamp : undefined, isActive: change.type === SAVED_CHANGE_TYPE ? true : change.isActive }; @@ -214,12 +215,14 @@ function handleUnknownChange(change: Change): Item { * @returns {Item} An item object containing the filename, controlId, type, and optional timestamp. */ function handleControlChange(change: SavedControlChange | PendingControlChange): Item { + const title = change.title; return { fileName: change.fileName, controlId: change.controlId, timestamp: change.type === SAVED_CHANGE_TYPE ? change.timestamp : undefined, isActive: change.type === SAVED_CHANGE_TYPE ? true : change.isActive, - type: change.type + type: change.type, + ...(title && { title }) }; } diff --git a/packages/control-property-editor/src/panels/changes/ControlChange.tsx b/packages/control-property-editor/src/panels/changes/ControlChange.tsx index a21b2b4e1a..eb4dd01a66 100644 --- a/packages/control-property-editor/src/panels/changes/ControlChange.tsx +++ b/packages/control-property-editor/src/panels/changes/ControlChange.tsx @@ -23,6 +23,7 @@ export interface ControlItemProps { controlId: string; isActive: boolean; type: typeof SAVED_CHANGE_TYPE | typeof PENDING_CHANGE_TYPE; + title?: string; } /** @@ -41,7 +42,8 @@ export function ControlChange({ fileName, timestamp, type, - isActive + isActive, + title }: Readonly): ReactElement { const { t } = useTranslation(); const dispatch = useDispatch(); @@ -63,7 +65,7 @@ export function ControlChange({ const onCancelDelete = (): void => { setDialogState(undefined); }; - + const headerTitle = title ?? `${name} ${t('CHANGE')}`; return ( <> @@ -85,7 +87,7 @@ export function ControlChange({ overflowX: 'hidden', lineHeight: '18px' }}> - {name} {t('CHANGE')} + {headerTitle} diff --git a/packages/control-property-editor/src/panels/changes/UnknownChange.tsx b/packages/control-property-editor/src/panels/changes/UnknownChange.tsx index 84e43a4b82..d671a71e35 100644 --- a/packages/control-property-editor/src/panels/changes/UnknownChange.tsx +++ b/packages/control-property-editor/src/panels/changes/UnknownChange.tsx @@ -11,6 +11,7 @@ import { getFormattedDateAndTime } from './utils'; export interface UnknownChangeProps { fileName: string; + title?: string; timestamp?: number; controlId?: string; isActive: boolean; @@ -24,7 +25,7 @@ export interface UnknownChangeProps { * @returns ReactElement */ export function UnknownChange(unknownChangeProps: UnknownChangeProps): ReactElement { - const { fileName, timestamp, header, controlId, isActive } = unknownChangeProps; + const { fileName, timestamp, header, controlId, isActive, title } = unknownChangeProps; const { t } = useTranslation(); const dispatch = useDispatch(); const [dialogState, setDialogState] = useState(undefined); @@ -43,17 +44,14 @@ export function UnknownChange(unknownChangeProps: UnknownChangeProps): ReactElem const parts = fileName.split('_'); const changeName = parts[parts.length - 1]; const name = convertCamelCaseToPascalCase(changeName); + const headerText = title ?? `${name} ${t('CHANGE')}`; return ( <> - {header && ( - - {name} {t('CHANGE')} - - )} + {header && {headerText}} {t('FILE')} diff --git a/packages/create/CHANGELOG.md b/packages/create/CHANGELOG.md index 79c0a2f1c0..f665af050d 100644 --- a/packages/create/CHANGELOG.md +++ b/packages/create/CHANGELOG.md @@ -1,5 +1,90 @@ # @sap-ux/create +## 0.11.22 + +### Patch Changes + +- 04f0547: fix: preview config update default configs +- Updated dependencies [04f0547] + - @sap-ux/app-config-writer@0.5.13 + +## 0.11.21 + +### Patch Changes + +- Updated dependencies [19d51f3] + - @sap-ux/preview-middleware@0.16.163 + - @sap-ux/adp-tooling@0.12.105 + - @sap-ux/app-config-writer@0.5.12 + - @sap-ux/flp-config-inquirer@0.2.10 + +## 0.11.20 + +### Patch Changes + +- Updated dependencies [8b7ed76] + - @sap-ux/preview-middleware@0.16.162 + - @sap-ux/app-config-writer@0.5.12 + +## 0.11.19 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.19 +- @sap-ux/adp-tooling@0.12.104 +- @sap-ux/flp-config-inquirer@0.2.9 +- @sap-ux/preview-middleware@0.16.161 +- @sap-ux/cap-config-writer@0.7.68 +- @sap-ux/app-config-writer@0.5.12 + +## 0.11.18 + +### Patch Changes + +- 20ab455: fix: prerequisites check for preview conversion +- Updated dependencies [20ab455] + - @sap-ux/app-config-writer@0.5.12 + +## 0.11.17 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.18 +- @sap-ux/adp-tooling@0.12.103 +- @sap-ux/flp-config-inquirer@0.2.8 +- @sap-ux/cap-config-writer@0.7.68 +- @sap-ux/preview-middleware@0.16.160 +- @sap-ux/app-config-writer@0.5.11 + +## 0.11.16 + +### Patch Changes + +- @sap-ux/abap-deploy-config-inquirer@1.1.17 +- @sap-ux/adp-tooling@0.12.102 +- @sap-ux/app-config-writer@0.5.11 +- @sap-ux/preview-middleware@0.16.159 +- @sap-ux/system-access@0.5.25 +- @sap-ux/flp-config-inquirer@0.2.7 +- @sap-ux/cap-config-writer@0.7.68 +- @sap-ux/abap-deploy-config-writer@0.0.72 + +## 0.11.15 + +### Patch Changes + +- Updated dependencies [0f1b457] + - @sap-ux/odata-service-writer@0.25.1 + +## 0.11.14 + +### Patch Changes + +- d964a24: feat: add option to convert test runners to preview-config command +- Updated dependencies [d964a24] + - @sap-ux/app-config-writer@0.5.10 + - @sap-ux/preview-middleware@0.16.158 + ## 0.11.13 ### Patch Changes diff --git a/packages/create/README.md b/packages/create/README.md index 66184db7f8..e96e66898d 100644 --- a/packages/create/README.md +++ b/packages/create/README.md @@ -155,8 +155,9 @@ Executing `sap-ux convert` converts an app to a new feature. ### preview Executing `sap-ux convert preview-config` in the root folder of an app will convert the respective app to the preview with virtual endpoints. It will use the configuration from the scripts in the `package.json` file to adjust the UI5 configuration YAML files accordingly. The obsolete JS and TS sources will be deleted and the HTML files previously used for the preview will be renamed to `*_old.html`. ```sh -sap-ux convert preview [path] +sap-ux convert preview-config [path] ``` +- `-t | --tests` include test suite and test runners in the conversion to virtual files ## sap-ux remove Calling `sap-ux remove` allows removing a feature from a project. diff --git a/packages/create/package.json b/packages/create/package.json index 5736105866..3fa5db0e5e 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/create", "description": "SAP Fiori tools module to add or remove features", - "version": "0.11.13", + "version": "0.11.22", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/create/src/cli/add/annotations-to-odata.ts b/packages/create/src/cli/add/annotations-to-odata.ts index bbe7206733..4cd513a98c 100644 --- a/packages/create/src/cli/add/annotations-to-odata.ts +++ b/packages/create/src/cli/add/annotations-to-odata.ts @@ -73,7 +73,8 @@ async function addAnnotationsToOdata(basePath: string, simulate: boolean, yamlPa filePath: answers.filePath, namespaces, serviceUrl: dataSources[answers.id].uri - } + }, + isCommand: true } ); diff --git a/packages/create/src/cli/convert/preview.ts b/packages/create/src/cli/convert/preview.ts index 2b7e4a354e..8c3c43c2ee 100644 --- a/packages/create/src/cli/convert/preview.ts +++ b/packages/create/src/cli/convert/preview.ts @@ -10,11 +10,12 @@ export function addConvertPreviewCommand(cmd: Command): void { cmd.command('preview-config [path]') .option('-s, --simulate', 'simulate only do not write or install') .option('-v, --verbose', 'show verbose information') + .option('-t, --tests', 'also convert test suite and test runners') .action(async (path, options) => { if (options.verbose === true || options.simulate) { setLogLevelVerbose(); } - await convertPreview(path, !!options.simulate); + await convertPreview(path, !!options.simulate, !!options.tests); }); } @@ -23,8 +24,9 @@ export function addConvertPreviewCommand(cmd: Command): void { * * @param {string} basePath - The path to the adaptation project. * @param {boolean} simulate - If set to true, then no files will be written to the filesystem. + * @param {boolean} convertTests - If set to true, then test suite and test runners fill be included in the conversion. */ -async function convertPreview(basePath: string, simulate: boolean): Promise { +async function convertPreview(basePath: string, simulate: boolean, convertTests: boolean): Promise { const logger = getLogger(); if (!basePath) { @@ -33,7 +35,7 @@ async function convertPreview(basePath: string, simulate: boolean): Promise logger.info(`The changes for preview conversion have been written.`)); diff --git a/packages/deploy-tooling/CHANGELOG.md b/packages/deploy-tooling/CHANGELOG.md index 65505e8cdf..9f8b7897b8 100644 --- a/packages/deploy-tooling/CHANGELOG.md +++ b/packages/deploy-tooling/CHANGELOG.md @@ -1,5 +1,13 @@ # @sap-ux/deploy-tooling +## 0.15.36 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + - @sap-ux/system-access@0.5.25 + ## 0.15.35 ### Patch Changes diff --git a/packages/deploy-tooling/package.json b/packages/deploy-tooling/package.json index 7b701bf8d2..b84765699a 100644 --- a/packages/deploy-tooling/package.json +++ b/packages/deploy-tooling/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Adeploy-tooling" }, - "version": "0.15.35", + "version": "0.15.36", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", diff --git a/packages/environment-check/CHANGELOG.md b/packages/environment-check/CHANGELOG.md index bd8ec4d04a..74ca403b99 100644 --- a/packages/environment-check/CHANGELOG.md +++ b/packages/environment-check/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/environment-check +## 0.17.66 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + ## 0.17.65 ### Patch Changes diff --git a/packages/environment-check/package.json b/packages/environment-check/package.json index 0401c2a914..64d52aa3e7 100644 --- a/packages/environment-check/package.json +++ b/packages/environment-check/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/environment-check", - "version": "0.17.65", + "version": "0.17.66", "description": "SAP Fiori environment check", "license": "Apache-2.0", "bin": { diff --git a/packages/fiori-elements-writer/CHANGELOG.md b/packages/fiori-elements-writer/CHANGELOG.md index c104af0322..92b8345573 100644 --- a/packages/fiori-elements-writer/CHANGELOG.md +++ b/packages/fiori-elements-writer/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/fiori-elements-writer +## 1.3.42 + +### Patch Changes + +- Updated dependencies [0f1b457] + - @sap-ux/odata-service-writer@0.25.1 + ## 1.3.41 ### Patch Changes diff --git a/packages/fiori-elements-writer/package.json b/packages/fiori-elements-writer/package.json index c554032b15..ee1800cc07 100644 --- a/packages/fiori-elements-writer/package.json +++ b/packages/fiori-elements-writer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/fiori-elements-writer", "description": "SAP Fiori elements application writer", - "version": "1.3.41", + "version": "1.3.42", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/fiori-freestyle-writer/CHANGELOG.md b/packages/fiori-freestyle-writer/CHANGELOG.md index a9097261f7..a506bf4f2c 100644 --- a/packages/fiori-freestyle-writer/CHANGELOG.md +++ b/packages/fiori-freestyle-writer/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/fiori-freestyle-writer +## 1.2.38 + +### Patch Changes + +- Updated dependencies [0f1b457] + - @sap-ux/odata-service-writer@0.25.1 + ## 1.2.37 ### Patch Changes diff --git a/packages/fiori-freestyle-writer/package.json b/packages/fiori-freestyle-writer/package.json index 04ac71d5e3..51d8b87fa7 100644 --- a/packages/fiori-freestyle-writer/package.json +++ b/packages/fiori-freestyle-writer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/fiori-freestyle-writer", "description": "SAP Fiori freestyle application writer", - "version": "1.2.37", + "version": "1.2.38", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/flp-config-inquirer/CHANGELOG.md b/packages/flp-config-inquirer/CHANGELOG.md index 777e6094a2..47dbc51281 100644 --- a/packages/flp-config-inquirer/CHANGELOG.md +++ b/packages/flp-config-inquirer/CHANGELOG.md @@ -1,5 +1,34 @@ # @sap-ux/flp-config-inquirer +## 0.2.10 + +### Patch Changes + +- Updated dependencies [19d51f3] + - @sap-ux/adp-tooling@0.12.105 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + - @sap-ux/adp-tooling@0.12.104 + +## 0.2.8 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + - @sap-ux/adp-tooling@0.12.103 + +## 0.2.7 + +### Patch Changes + +- @sap-ux/adp-tooling@0.12.102 + ## 0.2.6 ### Patch Changes diff --git a/packages/flp-config-inquirer/package.json b/packages/flp-config-inquirer/package.json index ea8f20a6c6..c0a88af57b 100644 --- a/packages/flp-config-inquirer/package.json +++ b/packages/flp-config-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/flp-config-inquirer", "description": "Prompts module that can prompt users for inputs required for FLP configuration", - "version": "0.2.6", + "version": "0.2.10", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/flp-config-sub-generator/CHANGELOG.md b/packages/flp-config-sub-generator/CHANGELOG.md index 63c317c0da..440dd47617 100644 --- a/packages/flp-config-sub-generator/CHANGELOG.md +++ b/packages/flp-config-sub-generator/CHANGELOG.md @@ -1,5 +1,59 @@ # @sap-ux/flp-config-sub-generator +## 0.0.8 + +### Patch Changes + +- Updated dependencies [04f0547] + - @sap-ux/app-config-writer@0.5.13 + +## 0.0.7 + +### Patch Changes + +- @sap-ux/app-config-writer@0.5.12 +- @sap-ux/flp-config-inquirer@0.2.10 + +## 0.0.6 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + - @sap-ux/flp-config-inquirer@0.2.9 + - @sap-ux/app-config-writer@0.5.12 + +## 0.0.5 + +### Patch Changes + +- Updated dependencies [20ab455] + - @sap-ux/app-config-writer@0.5.12 + +## 0.0.4 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + - @sap-ux/flp-config-inquirer@0.2.8 + - @sap-ux/app-config-writer@0.5.11 + +## 0.0.3 + +### Patch Changes + +- @sap-ux/app-config-writer@0.5.11 +- @sap-ux/deploy-config-generator-shared@0.0.9 +- @sap-ux/flp-config-inquirer@0.2.7 + +## 0.0.2 + +### Patch Changes + +- Updated dependencies [d964a24] + - @sap-ux/app-config-writer@0.5.10 + ## 0.0.1 ### Patch Changes diff --git a/packages/flp-config-sub-generator/package.json b/packages/flp-config-sub-generator/package.json index 8cfea48e20..204c286966 100644 --- a/packages/flp-config-sub-generator/package.json +++ b/packages/flp-config-sub-generator/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/flp-config-sub-generator", "description": "Generator for creating Fiori Launcpad configuration", - "version": "0.0.1", + "version": "0.0.8", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/guided-answers-helper/CHANGELOG.md b/packages/guided-answers-helper/CHANGELOG.md index b7868181c9..471698d39c 100644 --- a/packages/guided-answers-helper/CHANGELOG.md +++ b/packages/guided-answers-helper/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/guided-answers-helper +## 0.2.0 + +### Minor Changes + +- dac696a: Fix for incorrect error messsages connecting to destinations. Correct incorrect GA node name. + ## 0.1.1 ### Patch Changes diff --git a/packages/guided-answers-helper/package.json b/packages/guided-answers-helper/package.json index e1c6b89246..ebb333c133 100644 --- a/packages/guided-answers-helper/package.json +++ b/packages/guided-answers-helper/package.json @@ -6,7 +6,7 @@ "url": "https://github.com/SAP/open-ux-tools.git", "directory": "packages/guided-answers-helper" }, - "version": "0.1.1", + "version": "0.2.0", "license": "Apache-2.0", "main": "dist/index.js", "scripts": { diff --git a/packages/guided-answers-helper/src/index.ts b/packages/guided-answers-helper/src/index.ts index 6ff6c92644..0cf02ba1c7 100644 --- a/packages/guided-answers-helper/src/index.ts +++ b/packages/guided-answers-helper/src/index.ts @@ -23,7 +23,7 @@ export const HELP_NODES = { DESTINATION_UNAVAILABLE: 51208, // Launchpad service is not subscribed to DESTINATION_NOT_FOUND: 51208, // Destination URL is returning an empty response BAD_GATEWAY: 48366, // Bad gateway 502 - DESTINATION_BAD_GATEWAY_503: 52526, // Destination Service Unavailable 503 + DESTINATION_SERVICE_UNAVAILBLE: 52526, // Destination Service Unavailable 503 NO_V4_SERVICES: 57573, // No V4 OData services available NO_ADT_SERVICE_AUTH: 57266, //Users does not have authorizations to use ADT services DESTINATION_CONNECTION_ERRORS: 48366, // Non specific destination connection error help page, currently this is mapped to the same node as BAS_CATALOG_SERVICES_REQUEST_FAILED which will be updated in the future (and the entry here removed) diff --git a/packages/guided-answers-helper/test/unit/index.test.ts b/packages/guided-answers-helper/test/unit/index.test.ts index d1e9ad82fe..919c6c6c4b 100644 --- a/packages/guided-answers-helper/test/unit/index.test.ts +++ b/packages/guided-answers-helper/test/unit/index.test.ts @@ -33,10 +33,10 @@ describe('Guided Answers Helper', () => { "BAD_GATEWAY": 48366, "BAS_CATALOG_SERVICES_REQUEST_FAILED": 48366, "CERTIFICATE_ERROR": 53643, - "DESTINATION_BAD_GATEWAY_503": 52526, "DESTINATION_CONNECTION_ERRORS": 48366, "DESTINATION_MISCONFIGURED": 54336, "DESTINATION_NOT_FOUND": 51208, + "DESTINATION_SERVICE_UNAVAILBLE": 52526, "DESTINATION_UNAVAILABLE": 51208, "DEV_PLATFORM": 45996, "FIORI_APP_GENERATOR": 48363, diff --git a/packages/inquirer-common/CHANGELOG.md b/packages/inquirer-common/CHANGELOG.md index 04bd768607..fe7e93b26c 100644 --- a/packages/inquirer-common/CHANGELOG.md +++ b/packages/inquirer-common/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/inquirer-common +## 0.6.3 + +### Patch Changes + +- 40ba546: Add support to read additional ABAP service types + +## 0.6.2 + +### Patch Changes + +- dac696a: Fix for incorrect error messsages connecting to destinations. Correct incorrect GA node name. +- Updated dependencies [dac696a] + - @sap-ux/guided-answers-helper@0.2.0 + ## 0.6.1 ### Patch Changes diff --git a/packages/inquirer-common/README.md b/packages/inquirer-common/README.md index 0d73425db2..5b798b9f8a 100644 --- a/packages/inquirer-common/README.md +++ b/packages/inquirer-common/README.md @@ -2,6 +2,35 @@ Commonly used shared functionality that supports prompting. Currently used by inquirer modules for example `@sap-ux/ui5-application-inquirer`, `@sap-ux/ui5-library-inquirer`. For example, choice searching and UI5 choice grouping. Add functionality that may be used by multiple prompting modules to prompte re-use. +## Extending ABAP Service Offerings + +The default list of ABAP service name types can be extended to allow customised ABAP service plans to be shown during the Cloud Foundry ABAP environment flow. + +Supported list of technical service name types; + +```TypeScript +[ + AbapEnvType.ABAP, + AbapEnvType.ABAP_TRIAL, + AbapEnvType.ABAP_CANARY, + AbapEnvType.ABAP_OEM, + AbapEnvType.ABAP_OEM_CANARY, + AbapEnvType.ABAP_HAAS, + AbapEnvType.ABAP_STAGING, + AbapEnvType.ABAP_INTERNAL_STAGING +] +``` + +Configure the `ABAPEnvServiceTypes` environment variable with either a single or comma separated list, to update the existing list; + +```bash +# Single +export ABAPEnvServiceTypes=abap-staging +# Multiple +export ABAPEnvServiceTypes=abap-staging,abap-internal +``` + + ## License diff --git a/packages/inquirer-common/package.json b/packages/inquirer-common/package.json index 0d9f6773e2..a33e76f048 100644 --- a/packages/inquirer-common/package.json +++ b/packages/inquirer-common/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/inquirer-common", "description": "Commonly used shared functionality and types to support inquirer modules.", - "version": "0.6.1", + "version": "0.6.3", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/inquirer-common/src/error-handler/error-handler.ts b/packages/inquirer-common/src/error-handler/error-handler.ts index 984db57f09..9d03b4e13e 100644 --- a/packages/inquirer-common/src/error-handler/error-handler.ts +++ b/packages/inquirer-common/src/error-handler/error-handler.ts @@ -51,7 +51,7 @@ export enum ERROR_TYPE { ODATA_URL_NOT_FOUND = 'ODATA_URL_NOT_FOUND', BAD_GATEWAY = 'BAD_GATEWAY', // Can be caused by either local issue or endpoint configuration INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', - DESTINATION_BAD_GATEWAY_503 = 'DESTINATION_BAD_GATEWAY_503', // Caused by endpoint using a firewall or proxy + DESTINATION_SERVICE_UNAVAILABLE = 'DESTINATION_SERVICE_UNAVAILABLE', // Caused by endpoint using a firewall or proxy DESTINATION_UNAVAILABLE = 'DESTINATION_UNAVAILABLE', DESTINATION_NOT_FOUND = 'DESTINATION_NOT_FOUND', DESTINATION_MISCONFIGURED = 'DESTINATION_MISCONFIGURED', @@ -100,7 +100,7 @@ export const ERROR_MAP: Record = { [ERROR_TYPE.ODATA_URL_NOT_FOUND]: [], [ERROR_TYPE.INTERNAL_SERVER_ERROR]: [/500/], [ERROR_TYPE.BAD_GATEWAY]: [/502/], - [ERROR_TYPE.DESTINATION_BAD_GATEWAY_503]: [], + [ERROR_TYPE.DESTINATION_SERVICE_UNAVAILABLE]: [], [ERROR_TYPE.DESTINATION_UNAVAILABLE]: [], [ERROR_TYPE.DESTINATION_NOT_FOUND]: [], [ERROR_TYPE.DESTINATION_MISCONFIGURED]: [], @@ -245,7 +245,7 @@ export class ErrorHandler { }), [ERROR_TYPE.NO_V2_SERVICES]: () => t('errors.noServicesAvailable', { version: '2' }), [ERROR_TYPE.NO_V4_SERVICES]: () => t('errors.noServicesAvailable', { version: '4' }), - [ERROR_TYPE.DESTINATION_BAD_GATEWAY_503]: () => t('errors.destination.unavailable'), + [ERROR_TYPE.DESTINATION_SERVICE_UNAVAILABLE]: () => t('errors.destination.unavailable'), [ERROR_TYPE.REDIRECT]: () => t('errors.redirectError'), [ERROR_TYPE.NO_SUCH_HOST]: () => t('errors.noSuchHostError'), [ERROR_TYPE.NO_ABAP_ENVS]: () => t('errors.abapEnvsUnavailable'), @@ -291,7 +291,7 @@ export class ErrorHandler { [ERROR_TYPE.DESTINATION_UNAVAILABLE]: HELP_NODES.DESTINATION_UNAVAILABLE, [ERROR_TYPE.DESTINATION_NOT_FOUND]: HELP_NODES.DESTINATION_NOT_FOUND, [ERROR_TYPE.BAD_GATEWAY]: HELP_NODES.BAD_GATEWAY, - [ERROR_TYPE.DESTINATION_BAD_GATEWAY_503]: HELP_NODES.DESTINATION_BAD_GATEWAY_503, + [ERROR_TYPE.DESTINATION_SERVICE_UNAVAILABLE]: HELP_NODES.DESTINATION_SERVICE_UNAVAILBLE, [ERROR_TYPE.NO_V4_SERVICES]: HELP_NODES.NO_V4_SERVICES, [ERROR_TYPE.AUTH]: undefined, [ERROR_TYPE.AUTH_TIMEOUT]: undefined, @@ -538,7 +538,7 @@ export class ErrorHandler { this.currentErrorMsg = null; this.currentErrorType = null; } - return errorHelp ?? resolvedErrorMsg; // We mau not have a help link, so return the resolvedend user message + return errorHelp ?? resolvedErrorMsg; // We may not have a help link, so return the resolved end user message } /** * Get a more specific error type for the specified destination. @@ -559,14 +559,14 @@ export class ErrorHandler { destErrorMsg = this.getErrorMsgFromType(destErrorType, 'HTML5.DynamicDestination'); } else if (errorType === ERROR_TYPE.SERVICE_UNAVAILABLE) { if (isOnPremiseDestination(destination)) { - destErrorType = ERROR_TYPE.DESTINATION_BAD_GATEWAY_503; // Remap to specific gateway to allow GA link to be associated + destErrorType = ERROR_TYPE.DESTINATION_SERVICE_UNAVAILABLE; // Remap to specific gateway to allow GA link to be associated } else { destErrorType = ERROR_TYPE.DESTINATION_CONNECTION_ERROR; // General destination connection error, GA link to connection page } } else if (errorType === ERROR_TYPE.NOT_FOUND) { destErrorType = ERROR_TYPE.DESTINATION_NOT_FOUND; + destErrorMsg = this.getErrorMsgFromType(ERROR_TYPE.DESTINATION_NOT_FOUND); } else if (ERROR_TYPE.INTERNAL_SERVER_ERROR === errorType || ERROR_TYPE.SERVER_HTTP_ERROR === errorType) { - // We cannot tell in BAS what this means, so we will just say the connection failed destErrorType = ERROR_TYPE.DESTINATION_CONNECTION_ERROR; } else if (errorType === ERROR_TYPE.AUTH && destination.Authentication !== Authentication.NO_AUTHENTICATION) { // Auth errors for destinations are usually misconfiguration, unless the `Authentication` property is set to `NoAuthentication` diff --git a/packages/inquirer-common/src/prompts/cf-helper.ts b/packages/inquirer-common/src/prompts/cf-helper.ts index 3fe2bbe839..a1ffd49de7 100644 --- a/packages/inquirer-common/src/prompts/cf-helper.ts +++ b/packages/inquirer-common/src/prompts/cf-helper.ts @@ -26,7 +26,14 @@ export async function getCFAbapInstanceChoices( AbapEnvType.ABAP_STAGING, AbapEnvType.ABAP_INTERNAL_STAGING ]; - const serviceInstanceInfo: ServiceInstanceInfo[] = await getServicesInstances(filteredInstances); + // Load additional ABAP service types to extend the filtered instance list + const envFilteredInstances = process.env.ABAPEnvServiceTypes + ? process.env.ABAPEnvServiceTypes.split(',').map((item) => item.trim()) + : []; + const serviceInstanceInfo: ServiceInstanceInfo[] = await getServicesInstances([ + ...filteredInstances, + ...envFilteredInstances + ]); if (serviceInstanceInfo.length > 0) { serviceInstanceInfo.forEach((service) => { choices.push({ name: service['label'], value: service }); diff --git a/packages/inquirer-common/src/translations/inquirer-common.i18n.json b/packages/inquirer-common/src/translations/inquirer-common.i18n.json index 5e075cf8de..ce66886d13 100644 --- a/packages/inquirer-common/src/translations/inquirer-common.i18n.json +++ b/packages/inquirer-common/src/translations/inquirer-common.i18n.json @@ -8,7 +8,7 @@ "errors": { "destination": { "unavailable": "The selected destination references an instance that is not available. Please check your destination configuration and try again.", - "notFound": "The destination is misconfigured, HTTP Error 404 returned, the requested resource could not be found.", + "notFound": "The destination target URL cannot be found. The request failed with status code 404. Please check the destination target URL connectivity in your BTP cockpit.", "notReachable": "The selected system is not reachable. System name: {{systemName}}, error: {{- error}}", "misconfigured": "The destination is misconfigured. $t(errors.destination.missingPropMsg, {\"destinationProperty\": \"{{destinationProperty}}\" })", "missingPropMsg": "The property: `{{destinationProperty}}` is missing.", diff --git a/packages/inquirer-common/test/unit/error-handler/error-handler.test.ts b/packages/inquirer-common/test/unit/error-handler/error-handler.test.ts index c5e3a924c6..20dbee9862 100644 --- a/packages/inquirer-common/test/unit/error-handler/error-handler.test.ts +++ b/packages/inquirer-common/test/unit/error-handler/error-handler.test.ts @@ -303,7 +303,7 @@ describe('Test ErrorHandler', () => { expect(ErrorHandler.getErrorMsgFromType(ERROR_TYPE.NO_V4_SERVICES)).toEqual( t('errors.noServicesAvailable', { version: '4' }) ); - expect(ErrorHandler.getErrorMsgFromType(ERROR_TYPE.DESTINATION_BAD_GATEWAY_503)).toEqual( + expect(ErrorHandler.getErrorMsgFromType(ERROR_TYPE.DESTINATION_SERVICE_UNAVAILABLE)).toEqual( t('errors.destination.unavailable') ); expect(ErrorHandler.getErrorMsgFromType(ERROR_TYPE.REDIRECT)).toEqual(t('errors.redirectError')); diff --git a/packages/inquirer-common/test/unit/prompts/cf-helper.test.ts b/packages/inquirer-common/test/unit/prompts/cf-helper.test.ts index 83dab1bb2b..35f75906c1 100644 --- a/packages/inquirer-common/test/unit/prompts/cf-helper.test.ts +++ b/packages/inquirer-common/test/unit/prompts/cf-helper.test.ts @@ -4,13 +4,16 @@ import { initI18nInquirerCommon, t } from '../../../src/i18n'; import { getCFAbapInstanceChoices } from '../../../src/prompts/cf-helper'; let cfAbapServices: ServiceInstanceInfo[] = []; - jest.mock('@sap/cf-tools', () => ({ ...jest.requireActual('@sap/cf-tools'), apiGetServicesInstancesFilteredByType: jest.fn().mockImplementation(() => cfAbapServices) })); describe('cf-helper', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + beforeAll(async () => { await initI18nInquirerCommon(); }); @@ -37,4 +40,16 @@ describe('cf-helper', () => { expect(await getCFAbapInstanceChoices(errorHandler)).toEqual([]); expect(logErrorMsgsSpy).toHaveBeenCalledWith(ERROR_TYPE.NO_ABAP_ENVS, t('errors.abapEnvsCFDiscoveryFailed')); }); + + test('should append ABAP service types when loading ABAP instances', async () => { + // Reset + cfAbapServices = []; + // Pass instances + process.env.ABAPEnvServiceTypes = 'TestInternal, internal'; + expect(await getCFAbapInstanceChoices(new ErrorHandler())).toEqual([]); + expect(apiGetServicesInstancesFilteredByType).toBeCalledWith( + expect.arrayContaining(['TestInternal', 'internal']) + ); + delete process.env.ABAPEnvServiceTypes; + }); }); diff --git a/packages/jest-environment-ui5/CHANGELOG.md b/packages/jest-environment-ui5/CHANGELOG.md index 60b95099ce..ab897b7160 100644 --- a/packages/jest-environment-ui5/CHANGELOG.md +++ b/packages/jest-environment-ui5/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 5.3.5 + +### Patch Changes + +- 264458a: Do not reassign global.navigator + ## 5.3.4 ### Patch Changes diff --git a/packages/jest-environment-ui5/package.json b/packages/jest-environment-ui5/package.json index 31305cf373..3d4aae250c 100644 --- a/packages/jest-environment-ui5/package.json +++ b/packages/jest-environment-ui5/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/jest-environment-ui5", - "version": "5.3.4", + "version": "5.3.5", "description": "Jest matchers for files and folders", "repository": { "type": "git", diff --git a/packages/jest-environment-ui5/src/index.js b/packages/jest-environment-ui5/src/index.js index 646e96486d..0f929157f4 100644 --- a/packages/jest-environment-ui5/src/index.js +++ b/packages/jest-environment-ui5/src/index.js @@ -56,7 +56,6 @@ class UI5DOMEnvironment extends JSDOMEnvironment { window.NewObject = Object; [ 'sap', - 'navigator', 'location', 'top', 'self', diff --git a/packages/odata-service-inquirer/CHANGELOG.md b/packages/odata-service-inquirer/CHANGELOG.md index c90a8ce53e..48d99c3e9b 100644 --- a/packages/odata-service-inquirer/CHANGELOG.md +++ b/packages/odata-service-inquirer/CHANGELOG.md @@ -1,5 +1,27 @@ # @sap-ux/odata-service-inquirer +## 0.8.6 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.8.5 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/guided-answers-helper@0.2.0 + - @sap-ux/inquirer-common@0.6.2 + +## 0.8.4 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + ## 0.8.3 ### Patch Changes diff --git a/packages/odata-service-inquirer/package.json b/packages/odata-service-inquirer/package.json index 1e3ca4eedc..142d0b711d 100644 --- a/packages/odata-service-inquirer/package.json +++ b/packages/odata-service-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/odata-service-inquirer", "description": "Prompts module that can prompt users for inputs required for odata service writing", - "version": "0.8.3", + "version": "0.8.6", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/odata-service-inquirer/test/unit/prompts/connectionValidator.test.ts b/packages/odata-service-inquirer/test/unit/prompts/connectionValidator.test.ts index 21c9c43e92..1b4c8283ec 100644 --- a/packages/odata-service-inquirer/test/unit/prompts/connectionValidator.test.ts +++ b/packages/odata-service-inquirer/test/unit/prompts/connectionValidator.test.ts @@ -637,7 +637,8 @@ describe('ConnectionValidator', () => { text: 'Need help with this error?', url: `https://ga.support.sap.com/dtp/viewer/index.html#/tree/${HELP_TREE.FIORI_TOOLS}/actions/${HELP_NODES.DESTINATION_NOT_FOUND}` }, - message: t('errors.urlNotFound') + message: + 'The destination target URL cannot be found. The request failed with status code 404. Please check the destination target URL connectivity in your BTP cockpit.' } }) ); diff --git a/packages/odata-service-writer/CHANGELOG.md b/packages/odata-service-writer/CHANGELOG.md index ce89c9172d..89ab81dce7 100644 --- a/packages/odata-service-writer/CHANGELOG.md +++ b/packages/odata-service-writer/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/odata-service-writer +## 0.25.1 + +### Patch Changes + +- 0f1b457: Improved update function for manifest by reducing JSON write calls for manifest file. + ## 0.25.0 ### Minor Changes diff --git a/packages/odata-service-writer/package.json b/packages/odata-service-writer/package.json index ea3f4becb5..5c632a9a28 100644 --- a/packages/odata-service-writer/package.json +++ b/packages/odata-service-writer/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aodata-service-writer" }, - "version": "0.25.0", + "version": "0.25.1", "license": "Apache-2.0", "main": "dist/index.js", "scripts": { diff --git a/packages/odata-service-writer/src/updates.ts b/packages/odata-service-writer/src/updates.ts index 955cc77320..dc8d614444 100644 --- a/packages/odata-service-writer/src/updates.ts +++ b/packages/odata-service-writer/src/updates.ts @@ -234,8 +234,9 @@ function updateExistingService( * @param {string} webappPath - the webapp path of an existing UI5 application * @param {Manifest} manifest - the manifest.json of the application * @param {Editor} fs - the memfs editor instance + * @returns modified manifest object. */ -async function updateExistingServices(webappPath: string, manifest: Manifest, fs: Editor): Promise { +async function updateExistingServices(webappPath: string, manifest: Manifest, fs: Editor): Promise { const dataSources = manifest?.['sap.app']?.dataSources; for (const dataSourceKey in dataSources) { const dataSource = dataSources[dataSourceKey]; @@ -250,7 +251,7 @@ async function updateExistingServices(webappPath: string, manifest: Manifest, fs } } } - fs.writeJSON(join(webappPath, 'manifest.json'), manifest); + return manifest; } /** @@ -267,15 +268,14 @@ export async function updateManifest(basePath: string, service: OdataService, fs const manifest = fs.readJSON(manifestPath) as unknown as Manifest; const appProp = 'sap.app'; const appid = manifest?.[appProp]?.id; - // Check and update existing services - await updateExistingServices(webappPath, manifest, fs); - const modifiedManifest = fs.readJSON(manifestPath) as unknown as Manifest; // Throw if required property is not found manifest.json if (!appid) { throw new Error( t('error.requiredProjectPropertyNotFound', { property: `'${appProp}'.id`, path: manifestPath }) ); } + // Check and update existing services + const modifiedManifest = await updateExistingServices(webappPath, manifest, fs); // Add or update manifest.json with service enhanceManifest(service, modifiedManifest); fs.writeJSON(manifestPath, modifiedManifest); diff --git a/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-older-services.ts b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-older-services.ts new file mode 100644 index 0000000000..371564480a --- /dev/null +++ b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-older-services.ts @@ -0,0 +1,39 @@ +export const expectedEdmxManifestOlderServices = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + type: 'OData', + settings: { + annotations: ['SEPMRA_OVW_ANNO_MDL'] + } + }, + SEPMRA_OVW_ANNO_MDL: { + type: 'ODataAnnotation', + settings: { + localUri: 'localService/mainService/SEPMRA_OVW_ANNO_MDL.xml' + } + }, + aname: { + uri: '/a/path', + type: 'OData', + settings: { + annotations: [], + odataVersion: '2.0' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService' + }, + amodel: { + dataSource: 'aname', + preload: true, + settings: {} + } + } + } +}; diff --git a/packages/odata-service-writer/test/unit/updates.test.ts b/packages/odata-service-writer/test/unit/updates.test.ts index 6dbe9f7514..a344d22f89 100644 --- a/packages/odata-service-writer/test/unit/updates.test.ts +++ b/packages/odata-service-writer/test/unit/updates.test.ts @@ -8,6 +8,7 @@ import { OdataVersion, ServiceType } from '../../src'; import { expectedEdmxManifest } from '../test-data/manifest-json/edmx-manifest'; import { expectedEdmxManifestMultipleAnnotations } from '../test-data/manifest-json/edmx-manifest-multiple-annotations'; import { expectedEdmxManifestMultipleServices } from '../test-data/manifest-json/edmx-manifest-multiple-services'; +import { expectedEdmxManifestOlderServices } from '../test-data/manifest-json/edmx-manifest-older-services'; import { expectedCdsManifest } from '../test-data/manifest-json/cap-manifest'; import { expectedEdmxManifestLocalAnnotation } from '../test-data/manifest-json/edmx-manifest-local-annotation'; // single local annotation import { expectedEdmxManifestLocalAnnotations } from '../test-data/manifest-json/edmx-manifest-local-annotations'; // multiple local annotations @@ -305,6 +306,51 @@ describe('updates', () => { expect(manifestJson).toEqual(expectedEdmxManifestMultipleServices); }); + test('Ensure manifest updates are updated as expected as in edmx projects with older services', async () => { + // Test to basically check whether existing service definitions are updated (localUri attribute is modified) + const testManifest = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + type: 'OData', + settings: { + annotations: ['SEPMRA_OVW_ANNO_MDL'] + } + }, + SEPMRA_OVW_ANNO_MDL: { + type: 'ODataAnnotation', + settings: { + localUri: 'localService/SEPMRA_OVW_ANNO_MDL.xml' // localUri defined in localService folder - should be updated + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService' + } + } + } + }; + const service: OdataService = { + version: OdataVersion.v2, + client: '123', + model: 'amodel', + name: 'aname', + path: '/a/path', + type: ServiceType.EDMX, + annotations: [] + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + // Call updateManifest + await updateManifest('./', service, fs); + const manifestJson = fs.readJSON('./webapp/manifest.json'); + expect(manifestJson).toEqual(expectedEdmxManifestOlderServices); + }); + test('Ensure manifest updates are updated as expected as in cds projects', async () => { const testManifest = { 'sap.app': { diff --git a/packages/preview-middleware-client/CHANGELOG.md b/packages/preview-middleware-client/CHANGELOG.md index 531a8230bd..ef3f541077 100644 --- a/packages/preview-middleware-client/CHANGELOG.md +++ b/packages/preview-middleware-client/CHANGELOG.md @@ -1,5 +1,17 @@ # @sap-ux-private/preview-middleware-client +## 0.11.47 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File + +## 0.11.46 + +### Patch Changes + +- 8b7ed76: Fixed outline not being displayed in SAP Fiori Elements for OData V4 applications with multiple views. + ## 0.11.45 ### Patch Changes diff --git a/packages/preview-middleware-client/package.json b/packages/preview-middleware-client/package.json index 80f74b0d23..76d38af85f 100644 --- a/packages/preview-middleware-client/package.json +++ b/packages/preview-middleware-client/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux-private/preview-middleware-client", - "version": "0.11.45", + "version": "0.11.47", "description": "Client-side coding hosted by the preview middleware", "repository": { "type": "git", diff --git a/packages/preview-middleware-client/src/adp/api-handler.ts b/packages/preview-middleware-client/src/adp/api-handler.ts index 5145d058da..67c82d6701 100644 --- a/packages/preview-middleware-client/src/adp/api-handler.ts +++ b/packages/preview-middleware-client/src/adp/api-handler.ts @@ -6,6 +6,7 @@ export const enum ApiEndpoints { FRAGMENT = '/adp/api/fragment', CONTROLLER = '/adp/api/controller', CODE_EXT = '/adp/api/code_ext', + ANNOTATION_FILE = '/adp/api/annotation', MANIFEST_APP_DESCRIPTOR = '/manifest.appdescr_variant' } @@ -33,6 +34,17 @@ export interface CodeExtResponse { isRunningInBAS: boolean; } +export interface AnnotationFileResponse { + annotationExistsInWS: boolean; + annotationPath: string; + annotationPathFromRoot: string; + isRunningInBAS: boolean; +} + +interface DataSoruceAnnotationMap { + [key: string]: { serviceUrl: string; isRunningInBAS: boolean; annotationDetails: AnnotationFileResponse }; +} + export interface ControllersResponse { controllers: Controllers; message: string; @@ -139,6 +151,25 @@ export async function writeController(data: T): Promise { return request(ApiEndpoints.CONTROLLER, RequestMethod.POST, data); } +/** + * Writes a new annotation file to the project's workspace + * + * @param data Data to be send to the server + * @returns Generic Promise + */ +export async function writeAnnotationFile(data: T): Promise { + return request(ApiEndpoints.ANNOTATION_FILE, RequestMethod.POST, data); +} + +/** + * Writes a new annotation file to the project's workspace + * + * @returns Generic Promise + */ +export async function getDataSourceAnnotationFileMap(): Promise { + return request(ApiEndpoints.ANNOTATION_FILE, RequestMethod.GET); +} + /** * Checks for existing controller in the project's workspace * diff --git a/packages/preview-middleware-client/src/adp/controllers/BaseDialog.controller.ts b/packages/preview-middleware-client/src/adp/controllers/BaseDialog.controller.ts index fa8a6a1c9c..4f000bcf2f 100644 --- a/packages/preview-middleware-client/src/adp/controllers/BaseDialog.controller.ts +++ b/packages/preview-middleware-client/src/adp/controllers/BaseDialog.controller.ts @@ -56,7 +56,7 @@ export default abstract class BaseDialog | void; - abstract buildDialogData(): Promise; + abstract buildDialogData(): Promise | void; /** * Method is used in add fragment dialog controllers to get current control metadata which are needed on the dialog @@ -246,5 +246,4 @@ export default abstract class BaseDialog { + private options: FileExistsDialogOptions; + public model: JSONModel; + private readonly _name: string; + constructor(name: string, options: FileExistsDialogOptions) { + super(name); + this.model = new JSONModel(); + this.options = options; + } + + /** + * Setups the Dialog and the JSON Model + * + * @param {Dialog} dialog - Dialog instance + */ + async setup(dialog: Dialog): Promise { + this.dialog = dialog; + + this.model.setProperty('/filePath', this.options.filePath); + this.model.setProperty('/filePathFromRoot', this.options.fileName); + this.model.setProperty('/isRunningInBAS', this.options.isRunningInBAS); + this.buildDialogData(); + const resourceModel = await getResourceModel(); + this.dialog.setModel(this.model); + this.dialog.setModel(resourceModel, 'i18n'); + this.dialog.open(); + } + + /** + * Handles create button press + * + * @param _event Event + */ + onShowFileInVscodeBtn(_event: Event) { + const annotationPath = this.model.getProperty('/filePath'); + window.open(`vscode://file${annotationPath}`); + + this.handleDialogClose(); + } + + handleDialogClose() { + this.dialog.close(); + this.dialog.destroy(); + } + + /** + * Builds data that is used in the dialog. + */ + buildDialogData(): void { + const content = this.dialog.getContent(); + + const messageForm = content[0] as SimpleForm; + messageForm.setVisible(true); + + const isRunningInBAS = this.model.getProperty('/isRunningInBAS'); + if (isRunningInBAS) { + this.dialog.getBeginButton().setVisible(false); + } + } + + /** + * Handles create button press + * + * @param _event Event + */ + onCreateBtnPress(_event: Event): Promise | void {} +} diff --git a/packages/preview-middleware-client/src/adp/dialog-factory.ts b/packages/preview-middleware-client/src/adp/dialog-factory.ts index b5bd79c776..5f70483c82 100644 --- a/packages/preview-middleware-client/src/adp/dialog-factory.ts +++ b/packages/preview-middleware-client/src/adp/dialog-factory.ts @@ -11,15 +11,17 @@ import ControllerExtension from './controllers/ControllerExtension.controller'; import ExtensionPoint from './controllers/ExtensionPoint.controller'; import { ExtensionPointData } from './extension-point'; +import FileExistsDialog, { FileExistsDialogOptions } from './controllers/FileExistsDialog.controller'; export const enum DialogNames { ADD_FRAGMENT = 'AddFragment', ADD_TABLE_COLUMN_FRAGMENTS = 'AddTableColumnFragments', CONTROLLER_EXTENSION = 'ControllerExtension', - ADD_FRAGMENT_AT_EXTENSION_POINT = 'ExtensionPoint' + ADD_FRAGMENT_AT_EXTENSION_POINT = 'ExtensionPoint', + FILE_EXISTS = 'FileExistsDialog' } -type Controller = AddFragment | AddTableColumnFragments | ControllerExtension | ExtensionPoint; +type Controller = AddFragment | AddTableColumnFragments | ControllerExtension | ExtensionPoint | FileExistsDialog; export const OPEN_DIALOG_STATUS_CHANGED = 'OPEN_DIALOG_STATUS_CHANGED'; @@ -48,7 +50,7 @@ export class DialogFactory { rta: RuntimeAuthoring, dialogName: DialogNames, extensionPointData?: ExtensionPointData, - options: Partial = {} + options: Partial | Partial = {} ): Promise { if (this.isDialogOpen) { return; @@ -59,7 +61,7 @@ export class DialogFactory { switch (dialogName) { case DialogNames.ADD_FRAGMENT: controller = new AddFragment(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta, { - aggregation: options.aggregation, + ...('aggregation' in options && { aggregation: options.aggregation }), title: resources.getText(options.title ?? 'ADP_ADD_FRAGMENT_DIALOG_TITLE') }); break; @@ -69,7 +71,7 @@ export class DialogFactory { overlay, rta, { - aggregation: options.aggregation, + ...('aggregation' in options && { aggregation: options.aggregation }), title: resources.getText(options.title ?? 'ADP_ADD_FRAGMENT_DIALOG_TITLE') } ); @@ -89,6 +91,12 @@ export class DialogFactory { extensionPointData! ); break; + case DialogNames.FILE_EXISTS: + controller = new FileExistsDialog( + `open.ux.preview.client.adp.controllers.${dialogName}`, + options as FileExistsDialogOptions + ); + break; } const id = dialogName === DialogNames.ADD_FRAGMENT_AT_EXTENSION_POINT ? `dialog--${dialogName}` : undefined; @@ -110,7 +118,7 @@ export class DialogFactory { /** * Updates open dialog status. - * + * * @param isDialogOpen Flag indicating if there is an open dialog. */ private static updateStatus(isDialogOpen: boolean) { @@ -121,7 +129,7 @@ export class DialogFactory { /** * Attach event handler for OPEN_DIALOG_STATUS_CHANGED event. - * + * * @param handler Event handler. * @returns Function that removes listener. */ diff --git a/packages/preview-middleware-client/src/adp/quick-actions/common/add-new-annotation-file.ts b/packages/preview-middleware-client/src/adp/quick-actions/common/add-new-annotation-file.ts new file mode 100644 index 0000000000..06d1d34c00 --- /dev/null +++ b/packages/preview-middleware-client/src/adp/quick-actions/common/add-new-annotation-file.ts @@ -0,0 +1,141 @@ +import FlexCommand from 'sap/ui/rta/command/FlexCommand'; + +import { QuickActionContext, NestedQuickActionDefinition } from '../../../cpe/quick-actions/quick-action-definition'; +import { getDataSourceAnnotationFileMap } from '../../api-handler'; +import { + NESTED_QUICK_ACTION_KIND, + NestedQuickAction, + NestedQuickActionChild +} from '@sap-ux-private/control-property-editor-common'; +import { DialogFactory, DialogNames } from '../../dialog-factory'; +import OverlayRegistry from 'sap/ui/dt/OverlayRegistry'; +import { QuickActionDefinitionBase } from '../quick-action-base'; +import { DIALOG_ENABLEMENT_VALIDATOR } from '../dialog-enablement-validator'; +import CommandFactory from 'sap/ui/rta/command/CommandFactory'; +import { getUi5Version, isLowerThanMinimalUi5Version } from '../../../utils/version'; + +export const ADD_NEW_ANNOTATION_FILE = 'add-new-annotation-file'; + +/** + * Add New Annotation File. + */ +export class AddNewAnnotationFile + extends QuickActionDefinitionBase + implements NestedQuickActionDefinition +{ + public children: NestedQuickActionChild[] = []; + readonly kind = NESTED_QUICK_ACTION_KIND; + readonly type = ADD_NEW_ANNOTATION_FILE; + readonly forceRefreshAfterExecution = true; + public isApplicable = true; + public get id(): string { + return `${this.context.key}-${this.type}`; + } + constructor(protected readonly context: QuickActionContext) { + super(ADD_NEW_ANNOTATION_FILE, NESTED_QUICK_ACTION_KIND, 'QUICK_ACTION_ADD_NEW_ANNOTATION_FILE', context, [ + DIALOG_ENABLEMENT_VALIDATOR + ]); + } + + async initialize(): Promise { + const version = await getUi5Version(); + if (isLowerThanMinimalUi5Version(version, { major: 1, minor: 132, patch: 0 })) { + this.isApplicable = false; + return; + } + const dataSourceAnnotationFileMap = await getDataSourceAnnotationFileMap(); + if (!dataSourceAnnotationFileMap) { + throw new Error('No data sources found in the manifest'); + } + for (const key in dataSourceAnnotationFileMap) { + if (Object.prototype.hasOwnProperty.call(dataSourceAnnotationFileMap, key)) { + const source = dataSourceAnnotationFileMap[key]; + this.children.push({ + enabled: true, + label: source.annotationDetails.annotationExistsInWS + ? this.context.resourceBundle.getText('SHOW_ANNOTATION_FILE', [key]) + : this.context.resourceBundle.getText('ODATA_SOURCE', [key]), + children: [] + }); + } + } + } + async execute(path: string): Promise { + const index = Number(path); + if (index >= 0) { + // Do not cache the result of getDataSourceAnnotationFileMap api, + // as annotation file or datasource can be added outside using create command. + // So refresh would be required for the cache to be updated. + const dataSourceAnnotationFileMap = await getDataSourceAnnotationFileMap(); + const dataSourceId = Object.keys(dataSourceAnnotationFileMap)[index]; + const dataSource = dataSourceAnnotationFileMap?.[dataSourceId]; + if (dataSource?.annotationDetails.annotationExistsInWS) { + const annotationFileDetails = dataSource.annotationDetails; + const { annotationPath, annotationPathFromRoot } = annotationFileDetails; + await DialogFactory.createDialog( + OverlayRegistry.getOverlay(this.context.view), // this passed only because, for method param is required. + this.context.rta, // same as above + DialogNames.FILE_EXISTS, + undefined, + { + fileName: annotationPathFromRoot, + filePath: annotationPath, + isRunningInBAS: dataSource.isRunningInBAS + } + ); + } + // Create annotation file only, if no file exists already for datasource id or if the change file exist and but no annotation file exists in file system. + else if (dataSource) { + const timestamp = Date.now(); + const annotationFileNameWithoutExtension = `annotation_${timestamp}`; + const annotationFileName = `${annotationFileNameWithoutExtension}.xml`; + const annotationNameSpace = + this.context.flexSettings.layer === 'CUSTOMER_BASE' + ? `customer.annotation.${annotationFileNameWithoutExtension}` + : `annotation.${annotationFileNameWithoutExtension}`; + const content = { + dataSourceId: dataSourceId, + annotations: [annotationNameSpace], + annotationsInsertPosition: 'END', + dataSource: { + [annotationNameSpace]: { + uri: `../annotations/${annotationFileName}`, + type: 'ODataAnnotation' + } + } + }; + const modifiedValue = { + changeType: 'appdescr_app_addAnnotationsToOData', + generator: this.context.flexSettings.generator, + reference: this.context.flexSettings.projectId, + fileName: `id_${timestamp}_addAnnotationsToOData`, + content: content, + serviceUrl: dataSource.serviceUrl + }; + const command = await CommandFactory.getCommandFor( + this.context.view, + 'annotation', + modifiedValue, + null, + this.context.flexSettings + ); + return [command]; + } + } + return []; + } + + /** + * Prepares nested quick action object + * @returns action instance + */ + getActionObject(): NestedQuickAction { + return { + kind: NESTED_QUICK_ACTION_KIND, + id: this.id, + enabled: this.isApplicable, + title: this.context.resourceBundle.getText('QUICK_ACTION_ADD_NEW_ANNOTATION_FILE'), + children: this.children + }; + } +} diff --git a/packages/preview-middleware-client/src/adp/quick-actions/fe-v2/registry.ts b/packages/preview-middleware-client/src/adp/quick-actions/fe-v2/registry.ts index d3fb8f6bb3..f767723fb2 100644 --- a/packages/preview-middleware-client/src/adp/quick-actions/fe-v2/registry.ts +++ b/packages/preview-middleware-client/src/adp/quick-actions/fe-v2/registry.ts @@ -20,6 +20,7 @@ import { AddPageActionQuickAction } from '../common/create-page-action'; import { EnableTableFilteringQuickAction } from './lr-enable-table-filtering'; import { ToggleSemanticDateRangeFilterBar } from './lr-enable-semantic-date-range-filter-bar'; import { EnableTableEmptyRowModeQuickAction } from './op-enable-empty-row-mode'; +import { AddNewAnnotationFile } from '../common/add-new-annotation-file'; type PageName = 'listReport' | 'objectPage' | 'analyticalListPage'; const OBJECT_PAGE_TYPE = 'sap.suite.ui.generic.template.ObjectPage.view.Details'; @@ -51,7 +52,8 @@ export default class FEV2QuickActionRegistry extends QuickActionDefinitionRegist ChangeTableColumnsQuickAction, AddTableActionQuickAction, AddTableCustomColumnQuickAction, - EnableTableFilteringQuickAction + EnableTableFilteringQuickAction, + AddNewAnnotationFile ], view, key: name + index @@ -67,7 +69,8 @@ export default class FEV2QuickActionRegistry extends QuickActionDefinitionRegist ChangeTableColumnsQuickAction, AddTableActionQuickAction, AddTableCustomColumnQuickAction, - EnableTableEmptyRowModeQuickAction + EnableTableEmptyRowModeQuickAction, + AddNewAnnotationFile ], view, key: name + index diff --git a/packages/preview-middleware-client/src/adp/quick-actions/fe-v4/registry.ts b/packages/preview-middleware-client/src/adp/quick-actions/fe-v4/registry.ts index 5e20947e3f..8d7173c882 100644 --- a/packages/preview-middleware-client/src/adp/quick-actions/fe-v4/registry.ts +++ b/packages/preview-middleware-client/src/adp/quick-actions/fe-v4/registry.ts @@ -15,6 +15,7 @@ import { AddTableActionQuickAction } from './create-table-action'; import { EnableTableFilteringQuickAction } from './lr-enable-table-filtering'; import { ToggleSemanticDateRangeFilterBar } from './lr-enable-semantic-date-range-filter-bar'; import { EnableTableEmptyRowModeQuickAction } from './op-enable-empty-row-mode'; +import { AddNewAnnotationFile } from '../common/add-new-annotation-file'; type PageName = 'listReport' | 'objectPage'; @@ -46,7 +47,8 @@ export default class FEV4QuickActionRegistry extends QuickActionDefinitionRegist ChangeTableColumnsQuickAction, AddTableActionQuickAction, AddTableCustomColumnQuickAction, - EnableTableFilteringQuickAction + EnableTableFilteringQuickAction, + AddNewAnnotationFile ], view, key: name + index @@ -62,7 +64,8 @@ export default class FEV4QuickActionRegistry extends QuickActionDefinitionRegist ChangeTableColumnsQuickAction, AddTableActionQuickAction, AddTableCustomColumnQuickAction, - EnableTableEmptyRowModeQuickAction + EnableTableEmptyRowModeQuickAction, + AddNewAnnotationFile ], view, key: name + index diff --git a/packages/preview-middleware-client/src/adp/ui/FileExistsDialog.fragment.xml b/packages/preview-middleware-client/src/adp/ui/FileExistsDialog.fragment.xml new file mode 100644 index 0000000000..406f31e2e0 --- /dev/null +++ b/packages/preview-middleware-client/src/adp/ui/FileExistsDialog.fragment.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/packages/preview-middleware-client/src/cpe/changes/service.ts b/packages/preview-middleware-client/src/cpe/changes/service.ts index 31576306e7..c2f62c1828 100644 --- a/packages/preview-middleware-client/src/cpe/changes/service.ts +++ b/packages/preview-middleware-client/src/cpe/changes/service.ts @@ -39,6 +39,10 @@ import { getControlById, isA } from '../../utils/core'; import UI5Element from 'sap/ui/core/Element'; import { getConfigMapControlIdMap } from '../../utils/fe-v4'; +const TITLE_MAP: { [key: string]: string } = { + appdescr_app_addAnnotationsToOData: 'Add New Annotation File' +}; + interface ChangeContent { property: string; newValue: string; @@ -317,6 +321,7 @@ export class ChangeService extends EventTarget { } } catch (error) { // Gracefully handle change files with invalid content + const title = TITLE_MAP[change.changeType] ?? ''; if (change.fileName) { this.changedFiles[change.fileName] = change; const unknownChange: UnknownSavedChange = { @@ -324,7 +329,8 @@ export class ChangeService extends EventTarget { kind: 'unknown', changeType: change.changeType, fileName: change.fileName, - timestamp: new Date(change.creation).getTime() + timestamp: new Date(change.creation).getTime(), + ...(title && { title }) }; if (change.creation) { unknownChange.timestamp = new Date(change.creation).getTime(); @@ -431,7 +437,8 @@ export class ChangeService extends EventTarget { const changesRequiringReload = this.pendingChanges.reduce( (sum, change) => change.kind === CONFIGURATION_CHANGE_KIND || - change.changeType === 'appdescr_ui_generic_app_changePageConfiguration' + change.changeType === 'appdescr_ui_generic_app_changePageConfiguration' || + change.changeType === 'appdescr_app_addAnnotationsToOData' ? sum + 1 : sum, 0 @@ -628,7 +635,9 @@ export class ChangeService extends EventTarget { return undefined; } - const { fileName } = change.getDefinition(); + const { fileName } = change.getDefinition + ? change.getDefinition() + : (change.getJson() as { fileName: string }); if ((changeType === 'propertyChange' || changeType === 'propertyBindingChange') && selectorId) { let value = ''; switch (changeType) { @@ -660,9 +669,11 @@ export class ChangeService extends EventTarget { } else if (changeType === 'appdescr_ui_generic_app_changePageConfiguration') { return this.prepareV2ConfigurationChange(command, fileName, index, inactiveCommandCount); } else { + const title = TITLE_MAP[changeType] ?? ''; let result: PendingChange = { type: PENDING_CHANGE_TYPE, kind: UNKNOWN_CHANGE_KIND, + ...(title && { title }), changeType, isActive: index >= inactiveCommandCount, fileName diff --git a/packages/preview-middleware-client/src/cpe/outline/nodes.ts b/packages/preview-middleware-client/src/cpe/outline/nodes.ts index 7c360e44b9..0829020094 100644 --- a/packages/preview-middleware-client/src/cpe/outline/nodes.ts +++ b/packages/preview-middleware-client/src/cpe/outline/nodes.ts @@ -1,9 +1,11 @@ import type { OutlineNode } from '@sap-ux-private/control-property-editor-common'; import type { OutlineViewNode } from 'sap/ui/rta/command/OutlineService'; import type { Scenario } from 'sap/ui/fl/Scenario'; +import Log from 'sap/base/Log'; import { getUi5Version, Ui5VersionInfo } from '../../utils/version'; import { getControlById } from '../../utils/core'; +import { getError } from '../../utils/error'; import type { ControlTreeIndex } from '../types'; import { getOverlay, isReuseComponent } from '../utils'; @@ -81,7 +83,7 @@ function addChildToExtensionPoint(id: string, children: OutlineNode[], changeSer }); } /** - * Creates conrol index for all controls in the app. + * Creates control index for all controls in the app. * * @param {ControlTreeIndex} controlIndex - Control index for the ui5 app. * @param {OutlineNode} node - control node added to the outline. @@ -127,8 +129,8 @@ function addToPropertyIdMap(node: OutlineNode, propertyIdMap: Map { - addChildToExtensionPoint(id, children, changeService); - }); + let children: OutlineNode[] = []; + // We can combine both because there can only be either defaultContent or createdControls for one extension point node. + [...defaultContent, ...createdControls].forEach((id: string) => { + addChildToExtensionPoint(id, children, changeService); + }); - const node: OutlineNode = { - controlId: current.id, - controlType: current.technicalName, - name: current.name ?? '', - editable, - visible: current.visible ?? true, - children, - hasDefaultContent: defaultContent.length > 0 - }; + const node: OutlineNode = { + controlId: current.id, + controlType: current.technicalName, + name: current.name ?? '', + editable, + visible: current.visible ?? true, + children, + hasDefaultContent: defaultContent.length > 0 + }; - items.push(node); + items.push(node); + } + } catch (error) { + Log.error('Failed to transform outline node!', getError(error)); } } return items; diff --git a/packages/preview-middleware-client/src/messagebundle.properties b/packages/preview-middleware-client/src/messagebundle.properties index fb97dab963..344aa9f748 100644 --- a/packages/preview-middleware-client/src/messagebundle.properties +++ b/packages/preview-middleware-client/src/messagebundle.properties @@ -1,3 +1,5 @@ +CANCEL_BUTTON_LABEL=Cancel + QUICK_ACTION_ADD_PAGE_CONTROLLER=Add Controller to Page QUICK_ACTION_SHOW_PAGE_CONTROLLER=Show Page Controller QUICK_ACTION_OP_ADD_HEADER_FIELD=Add Header Field @@ -5,6 +7,7 @@ QUICK_ACTION_OP_ADD_CUSTOM_SECTION=Add Custom Section QUICK_ACTION_ADD_CUSTOM_PAGE_ACTION=Add Custom Page Action QUICK_ACTION_ADD_CUSTOM_TABLE_ACTION=Add Custom Table Action QUICK_ACTION_ADD_CUSTOM_TABLE_COLUMN=Add Custom Table Column +QUICK_ACTION_ADD_NEW_ANNOTATION_FILE=Add New Annotation File QUICK_ACTION_ENABLE_TABLE_FILTERING=Enable Table Filtering for Page Variants QUICK_ACTION_ENABLE_TABLE_EMPTY_ROW_MODE=Enable Empty Row Mode for Tables @@ -43,3 +46,9 @@ TABLE_CUSTOM_COLUMN_ACTION_NOT_AVAILABLE=This action has been disabled because t TABLE_FILTERING_CHANGE_HAS_ALREADY_BEEN_MADE=This option is disabled because table filtering for page variants is already enabled EMPTY_ROW_MODE_IS_ALREADY_ENABLED=This option has been disabled because empty row mode is already enabled for this table EMPTY_ROW_MODE_IS_NOT_SUPPORTED=This action is disabled because empty row mode is not supported for analytical and tree tables + +ODATA_SOURCE=''{0}'' datasource +SHOW_ANNOTATION_FILE=Show ''{0}'' annotation file +ANNOTATION_FILE_EXISTS=Annotation File Exists +ANNOTATION_FILE_HAS_BEEN_FOUND=An Annotation file has been found. +SHOW_FILE_IN_VSCODE=Show File in VSCode \ No newline at end of file diff --git a/packages/preview-middleware-client/test/__mock__/sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory.ts b/packages/preview-middleware-client/test/__mock__/sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory.ts index 400096b410..fecf2d4281 100644 --- a/packages/preview-middleware-client/test/__mock__/sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory.ts +++ b/packages/preview-middleware-client/test/__mock__/sap/ui/fl/apply/_internal/flexObjects/FlexObjectFactory.ts @@ -15,6 +15,7 @@ export default { getChangeType: jest.fn().mockReturnValue(oFileContent.changeType), getLayer: jest.fn().mockReturnValue(oFileContent.layer), getDefinition: jest.fn(), + getJson: jest.fn(), getContent: jest.fn(), setContent: jest.fn() }; diff --git a/packages/preview-middleware-client/test/unit/adp/api-handler.test.ts b/packages/preview-middleware-client/test/unit/adp/api-handler.test.ts index 19fd13f345..280fad2600 100644 --- a/packages/preview-middleware-client/test/unit/adp/api-handler.test.ts +++ b/packages/preview-middleware-client/test/unit/adp/api-handler.test.ts @@ -1,9 +1,11 @@ import { ApiEndpoints, RequestMethod, + getDataSourceAnnotationFileMap, getFragments, getManifestAppdescr, request, + writeAnnotationFile, writeFragment } from '../../../src/adp/api-handler'; import { fetchMock } from 'mock/window'; @@ -122,4 +124,65 @@ describe('API Handler', () => { expect(data.layer).toBe('VENDOR'); }); }); + + describe('writeAnnotationFile', () => { + afterEach(() => { + fetchMock.mockRestore(); + }); + + test('request is called and message is recieved from the backend', async () => { + fetchMock.mockResolvedValue({ + text: jest.fn().mockReturnValue('Annotation File Created'), + ok: true + }); + + const data = await writeAnnotationFile({ + dataSource: 'mainService', + serviceUrl: 'main/service/url' + }); + + expect(data).toBe('Annotation File Created'); + }); + }); + + describe('getDataSourceAnnotationFileMap', () => { + afterEach(() => { + fetchMock.mockRestore(); + }); + + test('request is called and correct data is returned', async () => { + fetchMock.mockResolvedValue({ + json: jest.fn().mockReturnValue( + JSON.stringify({ + mainService: { + serviceUrl: 'main/service/url', + annotationDetails: { + annotationExistsInWS: false, + annotationPath: 'c/drive/main/service/url', + annotationPathFromRoot: '/main/service/url', + isRunningInBAS: false + } + } + }) + ), + ok: true + }); + + const data = await getDataSourceAnnotationFileMap(); + + expect(data).toEqual( + JSON.stringify({ + mainService: { + serviceUrl: 'main/service/url', + annotationDetails: { + annotationExistsInWS: false, + annotationPath: 'c/drive/main/service/url', + annotationPathFromRoot: '/main/service/url', + isRunningInBAS: false + } + } + }) + ); + }); + }); }); diff --git a/packages/preview-middleware-client/test/unit/adp/controllers/FileExistDialog.controller.test.ts b/packages/preview-middleware-client/test/unit/adp/controllers/FileExistDialog.controller.test.ts new file mode 100644 index 0000000000..5c8e310409 --- /dev/null +++ b/packages/preview-middleware-client/test/unit/adp/controllers/FileExistDialog.controller.test.ts @@ -0,0 +1,159 @@ +import ControlUtils from '../../../../src/adp/control-utils'; +import { fetchMock, sapCoreMock } from 'mock/window'; +import OverlayRegistry from 'mock/sap/ui/dt/OverlayRegistry'; +import type Dialog from 'sap/m/Dialog'; +import FileExistsDialog from '../../../../src/adp/controllers/FileExistsDialog.controller'; +import JSONModel from 'sap/ui/model/json/JSONModel'; + +describe('FileExistsDialog', () => { + beforeAll(() => { + fetchMock.mockResolvedValue({ + json: jest.fn().mockReturnValue({ fragments: [] }), + text: jest.fn(), + ok: true + }); + }); + + describe('setup', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('fills json model with data - show file in vscode button', async () => { + const testModel = { + setProperty: jest.fn(), + getProperty: jest.fn().mockReturnValue(false) + } as unknown as JSONModel; + ControlUtils.getRuntimeControl = jest.fn().mockReturnValue({ + getMetadata: jest.fn().mockReturnValue({ + getAllAggregations: jest.fn().mockReturnValue({ + 'tooltip': {}, + 'customData': {}, + 'layoutData': {}, + 'dependents': {}, + 'dragDropConfig': {}, + 'content': {} + }), + getDefaultAggregationName: jest.fn().mockReturnValue('content'), + getName: jest.fn().mockReturnValue('Toolbar') + }) + }); + + ControlUtils.getControlAggregationByName = jest + .fn() + .mockReturnValue({ 0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {} }); + + const overlayControl = { + getDesignTimeMetadata: jest.fn().mockReturnValue({ + getData: jest.fn().mockReturnValue({ + aggregations: { content: { actions: { move: null }, domRef: ':sap-domref' } } + }) + }) + }; + sapCoreMock.byId.mockReturnValue(overlayControl); + + OverlayRegistry.getOverlay = jest.fn().mockReturnValue({ + getDesignTimeMetadata: jest.fn().mockReturnValue({ + getData: jest.fn().mockReturnValue({ + aggregations: {} + }) + }) + }); + + const fileExistDialog = new FileExistsDialog('adp.extension.controllers.FileExists', { + fileName: 'annotation_123434343.xml', + filePath: 'adp.demo.app/changes/annnotation/annotation_123434343.xml', + isRunningInBAS: false, + title: '' + }); + fileExistDialog.model = testModel; + const openSpy = jest.fn(); + + await fileExistDialog.setup({ + setEscapeHandler: jest.fn(), + destroy: jest.fn(), + setModel: jest.fn(), + open: openSpy, + close: jest.fn(), + getContent: jest.fn().mockReturnValue([ + { + setVisible: jest.fn() + } + ]) + } as unknown as Dialog); + + expect(openSpy).toHaveBeenCalledTimes(1); + }); + + test('fills json model with data - hide file in vscode button in SBAS', async () => { + const testModel = { + setProperty: jest.fn(), + getProperty: jest.fn().mockReturnValue(true) + } as unknown as JSONModel; + ControlUtils.getRuntimeControl = jest.fn().mockReturnValue({ + getMetadata: jest.fn().mockReturnValue({ + getAllAggregations: jest.fn().mockReturnValue({ + 'tooltip': {}, + 'customData': {}, + 'layoutData': {}, + 'dependents': {}, + 'dragDropConfig': {}, + 'content': {} + }), + getDefaultAggregationName: jest.fn().mockReturnValue('content'), + getName: jest.fn().mockReturnValue('Toolbar') + }) + }); + + const overlayControl = { + getDesignTimeMetadata: jest.fn().mockReturnValue({ + getData: jest.fn().mockReturnValue({ + aggregations: { content: { actions: { move: null }, domRef: ':sap-domref' } } + }) + }) + }; + sapCoreMock.byId.mockReturnValue(overlayControl); + + OverlayRegistry.getOverlay = jest.fn().mockReturnValue({ + getDesignTimeMetadata: jest.fn().mockReturnValue({ + getData: jest.fn().mockReturnValue({ + aggregations: {} + }) + }) + }); + + const fileExistDialog = new FileExistsDialog('adp.extension.controllers.FileExists', { + fileName: 'annotation_123434343.xml', + filePath: 'adp.demo.app/changes/annnotation/annotation_123434343.xml', + isRunningInBAS: false, + title: '' + }); + fileExistDialog.model = testModel; + const openSpy = jest.fn(); + const showInVsCodeSetVisibleSpy = jest.fn(); + const endButtonSetTextSpy = jest.fn(); + + await fileExistDialog.setup({ + setEscapeHandler: jest.fn(), + destroy: jest.fn(), + setModel: jest.fn(), + open: openSpy, + close: jest.fn(), + getContent: jest.fn().mockReturnValue([ + { + setVisible: jest.fn() + } + ]), + getBeginButton: jest.fn().mockReturnValue({ + setVisible: showInVsCodeSetVisibleSpy.mockReturnValue({ + setEnabled: jest.fn() + }) + }), + getEndButton: jest.fn().mockReturnValue({ setText: endButtonSetTextSpy }) + } as unknown as Dialog); + + expect(openSpy).toHaveBeenCalledTimes(1); + expect(showInVsCodeSetVisibleSpy).toHaveBeenCalledWith(false); + }); + }); +}); diff --git a/packages/preview-middleware-client/test/unit/adp/dialog-factory.test.ts b/packages/preview-middleware-client/test/unit/adp/dialog-factory.test.ts index fc5b910fdf..9a9c8a5eea 100644 --- a/packages/preview-middleware-client/test/unit/adp/dialog-factory.test.ts +++ b/packages/preview-middleware-client/test/unit/adp/dialog-factory.test.ts @@ -11,6 +11,7 @@ import AddFragment from '../../../src/adp/controllers/AddFragment.controller'; import ControllerExtension from '../../../src/adp/controllers/ControllerExtension.controller'; import ExtensionPoint from '../../../src/adp/controllers/ExtensionPoint.controller'; import AddTableColumnFragments from 'open/ux/preview/client/adp/controllers/AddTableColumnFragments.controller'; +import FileExistsDialog from '../../../src/adp/controllers/FileExistsDialog.controller'; describe('DialogFactory', () => { afterEach(() => { @@ -136,4 +137,24 @@ describe('DialogFactory', () => { expect(DialogFactory.canOpenDialog).toBe(false); }); + + test('Show File Exists Dialog', async () => { + const controller = { overlays: {}, rta: { 'yes': 'no' } }; + Controller.create.mockResolvedValue(controller); + const rtaMock = new RuntimeAuthoringMock({} as RTAOptions); + + FileExistsDialog.prototype.setup = jest.fn(); + + await DialogFactory.createDialog( + {} as unknown as UI5Element, + rtaMock as unknown as RuntimeAuthoring, + DialogNames.FILE_EXISTS + ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(Fragment.load.mock.calls[0][0].name).toStrictEqual('open.ux.preview.client.adp.ui.FileExistsDialog'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(Fragment.load.mock.calls[0][0].id).toStrictEqual(undefined); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(Fragment.load.mock.calls[0][0].controller).toBeInstanceOf(FileExistsDialog); + }); }); diff --git a/packages/preview-middleware-client/test/unit/adp/quick-actions/fe-v2.test.ts b/packages/preview-middleware-client/test/unit/adp/quick-actions/fe-v2.test.ts index a5fb71b4f4..5293fc8de2 100644 --- a/packages/preview-middleware-client/test/unit/adp/quick-actions/fe-v2.test.ts +++ b/packages/preview-middleware-client/test/unit/adp/quick-actions/fe-v2.test.ts @@ -1262,6 +1262,198 @@ describe('FE V2 quick actions', () => { ); }); }); + + describe('create new annotation file', () => { + const pageView = new XMLView(); + let rtaMock: RuntimeAuthoring; + beforeEach(async () => { + jest.clearAllMocks(); + jest.spyOn(versionUtils, 'getUi5Version').mockResolvedValue({ major: 1, minor: 132, patch: 0 }); + FlexUtils.getViewForControl.mockImplementation(() => { + return { + getId: () => 'MyView', + getController: () => { + return { + getMetadata: () => { + return { + getName: () => 'MyController' + }; + } + }; + } + }; + }); + fetchMock.mockResolvedValue({ + json: jest.fn().mockReturnValue({ + mainService: { + serviceUrl: 'main/service/url', + isRunningInBAS: false, + annotationDetails: { + annotationExistsInWS: false + } + }, + dataService: { + serviceUrl: 'data/service/url', + isRunningInBAS: false, + annotationDetails: { + annotationExistsInWS: true, + annotationPath: 'mock/adp/project/annotation/path', + annotationPathFromRoot: 'mock/adp.project.annotation/path' + } + } + }), + text: jest.fn(), + ok: true + }); + sapCoreMock.byId.mockImplementation((id) => { + if (id == 'DynamicPage') { + return { + getDomRef: () => ({}), + getParent: () => pageView + }; + } + if (id == 'NavContainer') { + const container = new NavContainer(); + const component = new UIComponentMock(); + const view = new XMLView(); + pageView.getDomRef.mockImplementation(() => { + return { + contains: () => true + }; + }); + pageView.getViewName.mockImplementation( + () => 'sap.suite.ui.generic.template.ListReport.view.ListReport' + ); + const componentContainer = new ComponentContainer(); + const spy = jest.spyOn(componentContainer, 'getComponent'); + spy.mockImplementation(() => { + return 'component-id'; + }); + jest.spyOn(Component, 'getComponentById').mockImplementation((id: string | undefined) => { + if (id === 'component-id') { + return component; + } + }); + view.getContent.mockImplementation(() => { + return [componentContainer]; + }); + container.getCurrentPage.mockImplementation(() => { + return view; + }); + component.getRootControl.mockImplementation(() => { + return pageView; + }); + return container; + } + }); + + rtaMock = new RuntimeAuthoringMock({} as RTAOptions) as unknown as RuntimeAuthoring; + const registry = new FEV2QuickActionRegistry(); + const service = new QuickActionService( + rtaMock, + new OutlineService(rtaMock, mockChangeService), + [registry], + { onStackChange: jest.fn() } as any + ); + await service.init(sendActionMock, subscribeMock); + + await service.reloadQuickActions({ + 'sap.f.DynamicPage': [ + { + controlId: 'DynamicPage' + } as any + ], + 'sap.m.NavContainer': [ + { + controlId: 'NavContainer' + } as any + ], + 'sap.ui.core.XMLView': [ + { + controlId: 'ListReportView' + } as any + ] + }); + }); + test('initialize and execute action', async () => { + jest.spyOn(Date, 'now').mockReturnValue(1736143853603); + expect(sendActionMock).toHaveBeenCalledWith( + quickActionListChanged([ + { + title: 'LIST REPORT', + actions: [ + { + 'kind': 'simple', + id: 'listReport0-add-controller-to-page', + title: 'Add Controller to Page', + enabled: true + }, + { + 'kind': 'nested', + id: 'listReport0-add-new-annotation-file', + title: 'Add New Annotation File', + enabled: true, + children: [ + { + children: [], + enabled: true, + label: '\'\'{0}\'\' datasource' + }, + { + children: [], + enabled: true, + label: 'Show \'\'{0}\'\' annotation file' + } + ] + } + ] + } + ]) + ); + + await subscribeMock.mock.calls[0][0]( + executeQuickAction({ id: 'listReport0-add-new-annotation-file', kind: 'nested', path: '0' }) + ); + expect(rtaMock.getCommandStack().pushAndExecute).toHaveBeenCalledWith({ + settings: {}, + type: 'annotation', + value: { + changeType: 'appdescr_app_addAnnotationsToOData', + content: { + annotations: ['annotation.annotation_1736143853603'], + annotationsInsertPosition: 'END', + dataSource: { + 'annotation.annotation_1736143853603': { + type: 'ODataAnnotation', + uri: '../annotations/annotation_1736143853603.xml' + } + }, + dataSourceId: 'mainService', + reference: undefined + }, + fileName: 'id_1736143853603_addAnnotationsToOData', + generator: undefined, + serviceUrl: 'main/service/url' + } + }); + }); + test('initialize and execute action - when file exists', async () => { + await subscribeMock.mock.calls[0][0]( + executeQuickAction({ id: 'listReport0-add-new-annotation-file', kind: 'nested', path: '1' }) + ); + expect(DialogFactory.createDialog).toHaveBeenCalledWith( + mockOverlay, + rtaMock, + 'FileExistsDialog', + undefined, + { + fileName: 'mock/adp.project.annotation/path', + filePath: 'mock/adp/project/annotation/path', + isRunningInBAS: false + } + ); + }); + }); }); describe('ObjectPage', () => { describe('add header field', () => { diff --git a/packages/preview-middleware-client/types/sap.ui.fl.d.ts b/packages/preview-middleware-client/types/sap.ui.fl.d.ts index 34fd8b9009..464c661e73 100644 --- a/packages/preview-middleware-client/types/sap.ui.fl.d.ts +++ b/packages/preview-middleware-client/types/sap.ui.fl.d.ts @@ -53,6 +53,7 @@ declare module 'sap/ui/fl/Change' { class Change { constructor(file: object): void; getDefinition: () => ChangeDefinition; + getJson: () => unknown; getSelector: () => Selector; getChangeType: () => string; getLayer: () => Layer; diff --git a/packages/preview-middleware/CHANGELOG.md b/packages/preview-middleware/CHANGELOG.md index 95f46fee8a..84ee6d96bc 100644 --- a/packages/preview-middleware/CHANGELOG.md +++ b/packages/preview-middleware/CHANGELOG.md @@ -1,5 +1,43 @@ # @sap-ux/preview-middleware +## 0.16.163 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File +- Updated dependencies [19d51f3] + - @sap-ux/adp-tooling@0.12.105 + +## 0.16.162 + +### Patch Changes + +- 8b7ed76: Fixed outline not being displayed in SAP Fiori Elements for OData V4 applications with multiple views. + +## 0.16.161 + +### Patch Changes + +- @sap-ux/adp-tooling@0.12.104 + +## 0.16.160 + +### Patch Changes + +- @sap-ux/adp-tooling@0.12.103 + +## 0.16.159 + +### Patch Changes + +- @sap-ux/adp-tooling@0.12.102 + +## 0.16.158 + +### Patch Changes + +- d964a24: feat: add option to convert test runners to preview-config command + ## 0.16.157 ### Patch Changes diff --git a/packages/preview-middleware/package.json b/packages/preview-middleware/package.json index 74fba977d3..9072ebb20e 100644 --- a/packages/preview-middleware/package.json +++ b/packages/preview-middleware/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware" }, - "version": "0.16.157", + "version": "0.16.163", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", diff --git a/packages/preview-middleware/src/base/flex.ts b/packages/preview-middleware/src/base/flex.ts index 3432e8dcb0..3ffe8507db 100644 --- a/packages/preview-middleware/src/base/flex.ts +++ b/packages/preview-middleware/src/base/flex.ts @@ -1,8 +1,8 @@ import type { Logger } from '@sap-ux/logger'; import type { ReaderCollection } from '@ui5/fs'; import type { Editor } from 'mem-fs-editor'; -import { existsSync, readdirSync, unlinkSync } from 'fs'; -import { join, parse } from 'path'; +import { existsSync, readdirSync, statSync, unlinkSync } from 'fs'; +import { join, parse, sep } from 'path'; import type { CommonChangeProperties } from '@sap-ux/adp-tooling'; /** @@ -17,7 +17,7 @@ export async function readChanges( logger: Logger ): Promise> { const changes: Record = {}; - const files = await project.byGlob('/**/changes/*.*'); + const files = await project.byGlob('/**/changes/**/*.*'); for (const file of files) { try { changes[`sap.ui.fl.${parse(file.getName()).name}`] = JSON.parse( @@ -79,15 +79,36 @@ export function deleteChange( if (fileName) { const path = join(webappPath, 'changes'); if (existsSync(path)) { - const files = readdirSync(path); - const file = files.find((element) => element.includes(fileName)); - if (file) { - logger.debug(`Write change ${file}`); - const filePath = join(path, file); + // Changes can be in subfolders of changes directory. For eg: New Annotation File Change + const files: string[] = []; + readDirectoriesRecursively(path, files); + const filePath = files.find((element) => element.includes(fileName)); + if (filePath) { + const fileNameWithExt = filePath.split(sep).pop(); + logger.debug(`Write change ${fileNameWithExt}`); unlinkSync(filePath); - return { success: true, message: `FILE_DELETED ${file}` }; + return { success: true, message: `FILE_DELETED ${fileNameWithExt}` }; } } } return { success: false }; } + +/** + * Recursively find all files in the given folder. + * + * @param path path to the folder. + * @param files all files in the given folder and subfolders. + */ +function readDirectoriesRecursively(path: string, files: string[] = []): void { + const items = readdirSync(path); + items.forEach((item) => { + const fullPath = join(path, item); + const stats = statSync(fullPath); + if (stats.isDirectory()) { + readDirectoriesRecursively(fullPath, files); + } else if (stats.isFile()) { + files.push(fullPath); + } + }); +} diff --git a/packages/preview-middleware/src/base/test.ts b/packages/preview-middleware/src/base/test.ts index 830338f6b6..1ec735d1b7 100644 --- a/packages/preview-middleware/src/base/test.ts +++ b/packages/preview-middleware/src/base/test.ts @@ -1,7 +1,7 @@ import type { Resource } from '@ui5/fs'; -import type { InternalTestConfig, TestConfig } from '../types'; +import type { InternalTestConfig, TestConfig, TestConfigDefaults } from '../types'; -const DEFAULTS: Record = { +const DEFAULTS: Record> = { qunit: { path: '/test/unitTests.qunit.html', init: '/test/unitTests.qunit.js', @@ -20,7 +20,7 @@ const DEFAULTS: Record = { pattern: '', framework: 'Testsuite' } -}; +} satisfies TestConfigDefaults; /** * Merge the given test configuration with the default values. diff --git a/packages/preview-middleware/src/index.ts b/packages/preview-middleware/src/index.ts index 563d59ea63..c01737e6f6 100644 --- a/packages/preview-middleware/src/index.ts +++ b/packages/preview-middleware/src/index.ts @@ -1,3 +1,11 @@ export * from './ui5/middleware'; export { FlpSandbox, initAdp, generatePreviewFiles, getPreviewPaths } from './base'; -export { FlpConfig, RtaConfig, TestConfig, MiddlewareConfig, DefaultFlpPath, DefaultIntent } from './types'; +export { + FlpConfig, + RtaConfig, + TestConfig, + MiddlewareConfig, + DefaultFlpPath, + DefaultIntent, + TestConfigDefaults +} from './types'; diff --git a/packages/preview-middleware/src/types/index.ts b/packages/preview-middleware/src/types/index.ts index 1ba2587e94..7b15c2ad25 100644 --- a/packages/preview-middleware/src/types/index.ts +++ b/packages/preview-middleware/src/types/index.ts @@ -82,6 +82,27 @@ export interface TestConfig extends Partial { export type InternalTestConfig = TestConfig & OptionalTestConfig; +export type TestConfigDefaults = { + qunit: { + path: '/test/unitTests.qunit.html'; + init: '/test/unitTests.qunit.js'; + pattern: '/test/**/*Test.*'; + framework: 'QUnit'; + }; + opa5: { + path: '/test/opaTests.qunit.html'; + init: '/test/opaTests.qunit.js'; + pattern: '/test/**/*Journey.*'; + framework: 'OPA5'; + }; + testsuite: { + path: '/test/testsuite.qunit.html'; + init: '/test/testsuite.qunit.js'; + pattern: ''; + framework: 'Testsuite'; + }; +}; + /** * Middleware configuration. */ diff --git a/packages/preview-middleware/test/unit/base/flex.test.ts b/packages/preview-middleware/test/unit/base/flex.test.ts index dc2e4303dd..1869e594c1 100644 --- a/packages/preview-middleware/test/unit/base/flex.test.ts +++ b/packages/preview-middleware/test/unit/base/flex.test.ts @@ -21,9 +21,9 @@ describe('flex', () => { const project = { byGlob: byGlobMock } as unknown as ReaderCollection; - function mockChange(id: string, ext: string = 'change', content?: object) { + function mockChange(id: string, subfolderPath: string = '', ext: string = 'change', content?: object) { return { - getPath: () => `test/changes/${id}.${ext}`, + getPath: () => `test/changes/${subfolderPath}/${id}.${ext}`, getName: () => `${id}.${ext}`, getString: () => Promise.resolve(JSON.stringify(content ?? { id })) }; @@ -35,19 +35,24 @@ describe('flex', () => { }); test('valid changes', async () => { - byGlobMock.mockResolvedValueOnce([mockChange('id1'), mockChange('id2', 'ctrl_variant_management_change')]); + byGlobMock.mockResolvedValueOnce([ + mockChange('id1'), + mockChange('id2', '', 'ctrl_variant_management_change'), + mockChange('id3', 'manifest', 'appdescr_app_addAnnotationsToOData') + ]); const changes = await readChanges(project, logger); - expect(Object.keys(changes)).toHaveLength(2); + expect(Object.keys(changes)).toHaveLength(3); expect(changes).toEqual({ 'sap.ui.fl.id1': { id: 'id1' }, - 'sap.ui.fl.id2': { id: 'id2' } + 'sap.ui.fl.id2': { id: 'id2' }, + 'sap.ui.fl.id3': { id: 'id3' } }); }); test('mix of valid and invalid changes', async () => { byGlobMock.mockResolvedValueOnce([ mockChange('id1'), // valid - mockChange('id2', 'change', { changeType: 'addXML' }), // valid but moduleName cannot be replaced + mockChange('id2', '', 'change', { changeType: 'addXML' }), // valid but moduleName cannot be replaced { invalid: 'change' } // invalid ]); const changes = await readChanges(project, logger); @@ -63,7 +68,7 @@ describe('flex', () => { codeRef: 'controller/MyExtension.js' } }; - byGlobMock.mockResolvedValueOnce([mockChange('id1', 'change', change)]); + byGlobMock.mockResolvedValueOnce([mockChange('id1', '', 'change', change)]); const changes = await readChanges(project, logger); expect(changes['sap.ui.fl.id1'].changeType).toBe('codeExt'); }); @@ -76,7 +81,7 @@ describe('flex', () => { fragmentPath: 'fragment/MyFragment.xml' } }; - byGlobMock.mockResolvedValueOnce([mockChange('id1', 'change', change)]); + byGlobMock.mockResolvedValueOnce([mockChange('id1', '', 'change', change)]); const changes = await readChanges(project, logger); expect(changes['sap.ui.fl.id1'].changeType).toBe('addXML'); }); diff --git a/packages/project-integrity/.eslintignore b/packages/project-integrity/.eslintignore new file mode 100644 index 0000000000..93a3fe1f45 --- /dev/null +++ b/packages/project-integrity/.eslintignore @@ -0,0 +1,3 @@ +test/test-output +test/test-input +dist diff --git a/packages/project-integrity/.eslintrc.js b/packages/project-integrity/.eslintrc.js new file mode 100644 index 0000000000..b717f83ae9 --- /dev/null +++ b/packages/project-integrity/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['../../.eslintrc'], + parserOptions: { + project: './tsconfig.eslint.json', + tsconfigRootDir: __dirname + } +}; diff --git a/packages/project-integrity/CHANGELOG.md b/packages/project-integrity/CHANGELOG.md new file mode 100644 index 0000000000..e222c9e5e8 --- /dev/null +++ b/packages/project-integrity/CHANGELOG.md @@ -0,0 +1,7 @@ +# @sap-ux/project-integrity + +## 0.0.2 + +### Patch Changes + +- 2bf91ea: New module @sap-ux/project-integrity diff --git a/packages/project-integrity/LICENSE b/packages/project-integrity/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/packages/project-integrity/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/project-integrity/README.md b/packages/project-integrity/README.md new file mode 100644 index 0000000000..ef5d2d13f8 --- /dev/null +++ b/packages/project-integrity/README.md @@ -0,0 +1,23 @@ +# @sap-ux/project-integrity + +Library that offers functions to detect modifications to a set of files within a project. It also allows to pass addition key -> string values that are stored together with integrity data and can also be checked. +For specific Fiori project, this module contains convenience functions that take care of building up the file and additional string content list, consumer need just to pass a valid Fiori project root. + +## Installation +Npm +`npm install --save @sap-ux/project-integrity` + +Yarn +`yarn add @sap-ux/project-integrity` + +Pnpm +`pnpm add @sap-ux/project-integrity` + +## Usage +See usage in `test/` folder. + +## Keywords +* SAP Fiori tools +* SAP Fiori elements +* SAP Fiori freestyle +* Project integrity \ No newline at end of file diff --git a/packages/project-integrity/jest.config.js b/packages/project-integrity/jest.config.js new file mode 100644 index 0000000000..9e9be597ec --- /dev/null +++ b/packages/project-integrity/jest.config.js @@ -0,0 +1,2 @@ +const config = require('../../jest.base'); +module.exports = config; diff --git a/packages/project-integrity/package.json b/packages/project-integrity/package.json new file mode 100644 index 0000000000..18aa416230 --- /dev/null +++ b/packages/project-integrity/package.json @@ -0,0 +1,38 @@ +{ + "name": "@sap-ux/project-integrity", + "version": "0.0.2", + "description": "Library to check the integrity of projects", + "repository": { + "type": "git", + "url": "https://github.com/SAP/open-ux-tools.git", + "directory": "packages/project-integrity" + }, + "bugs": { + "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aproject-integrity" + }, + "license": "Apache-2.0", + "private": false, + "main": "dist/index.js", + "scripts": { + "build": "tsc --build", + "clean": "rimraf --glob dist coverage *.tsbuildinfo", + "format": "prettier --write '**/*.{js,json,ts,yaml,yml}' --ignore-path ../../.prettierignore", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "test": "jest --ci --forceExit --detectOpenHandles --colors", + "watch": "tsc --watch" + }, + "files": [ + "dist", + "LICENSE", + "!dist/*.map", + "!dist/**/*.map" + ], + "engines": { + "node": ">=18.x" + }, + "dependencies": { + "@sap-ux/project-access": "workspace:*", + "lz-string": "1.5.0" + } +} diff --git a/packages/project-integrity/src/fiori-project/index.ts b/packages/project-integrity/src/fiori-project/index.ts new file mode 100644 index 0000000000..033415ec6e --- /dev/null +++ b/packages/project-integrity/src/fiori-project/index.ts @@ -0,0 +1,118 @@ +import { existsSync } from 'fs'; +import { join } from 'path'; +import { getCapCustomPaths } from '@sap-ux/project-access'; +import { + checkProjectIntegrity, + disableProjectIntegrity, + enableProjectIntegrity, + initProject, + isProjectIntegrityEnabled, + updateProjectIntegrity +} from '../integrity'; +import type { CheckIntegrityResult, Content } from '../types'; + +export const fioriIntegrityDataPath = join('.fiori-ai/ai-integrity.json'); + +/** + * Get the list of files to protect the integrity of. + * + * @param projectRoot - root folder of the project + * @returns - list of file paths + */ +async function getFileList(projectRoot: string): Promise { + const fileList: string[] = []; + + const schemaCds = join(projectRoot, 'db/schema.cds'); + if (existsSync(schemaCds)) { + fileList.push(schemaCds); + } else { + throw new Error(`File ${schemaCds} does not exist.`); + } + const servicesCds = join(projectRoot, 'srv/service.cds'); + if (existsSync(servicesCds)) { + fileList.push(servicesCds); + } else { + throw new Error(`File ${servicesCds} does not exist.`); + } + + return fileList; +} + +/** + * Returns additional string content, like the CAP environment. + * This content will be stored in the integrity data. + * + * @param projectRoot - root folder of the project + * @returns - additional content to store in the integrity data + */ +async function getAdditionalStringContent(projectRoot: string): Promise { + const capCustomPaths = await getCapCustomPaths(projectRoot); + return { capPaths: JSON.stringify(capCustomPaths) }; +} + +/** + * Initialize a Fiori project for integrity protection. + * + * @param projectRoot - root folder of the project + */ +export async function initFioriProject(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + const fileList = await getFileList(projectRoot); + const additionalStringContent = await getAdditionalStringContent(projectRoot); + await initProject({ integrityFilePath, fileList, additionalStringContent }); +} + +/** + * Check the integrity of a Fiori project. + * + * @param projectRoot - root folder of the project + * @returns - results of the check + */ +export async function checkFioriProjectIntegrity(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + const additionalStringContent = await getAdditionalStringContent(projectRoot); + return checkProjectIntegrity(integrityFilePath, additionalStringContent); +} + +/** + * Updates the integrity data of a Fiori project. + * + * @param projectRoot - root folder of the project + */ +export async function updateFioriProjectIntegrity(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + const additionalStringContent = await getAdditionalStringContent(projectRoot); + await updateProjectIntegrity(integrityFilePath, additionalStringContent); +} + +/** + * Return whether integrity is enabled for a Fiori project. + * + * @param projectRoot - root folder of the project + * @returns true if integrity is enabled, false otherwise + */ +export async function isFioriProjectIntegrityEnabled(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + return isProjectIntegrityEnabled(integrityFilePath); +} + +/** + * Enable integrity protection for a Fiori project. The Fiori project must be initialized first. + * After initialization, Fiori project integrity is enabled by default. + * + * @param projectRoot - root folder of the project + */ +export async function enableFioriProjectIntegrity(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + await enableProjectIntegrity(integrityFilePath); +} + +/** + * Disable integrity protection for a Fiori project. The Fiori project must be initialized first. + * + * @param projectRoot - root folder of the project + */ +export async function disableFioriProjectIntegrity(projectRoot: string): Promise { + const integrityFilePath = join(projectRoot, fioriIntegrityDataPath); + await disableProjectIntegrity(integrityFilePath); +} diff --git a/packages/project-integrity/src/index.ts b/packages/project-integrity/src/index.ts new file mode 100644 index 0000000000..ace2e448b9 --- /dev/null +++ b/packages/project-integrity/src/index.ts @@ -0,0 +1,16 @@ +export { + checkProjectIntegrity, + disableProjectIntegrity, + enableProjectIntegrity, + initProject, + isProjectIntegrityEnabled, + updateProjectIntegrity +} from './integrity'; +export { + checkFioriProjectIntegrity, + disableFioriProjectIntegrity, + enableFioriProjectIntegrity, + initFioriProject, + isFioriProjectIntegrityEnabled, + updateFioriProjectIntegrity +} from './fiori-project'; diff --git a/packages/project-integrity/src/integrity/check.ts b/packages/project-integrity/src/integrity/check.ts new file mode 100644 index 0000000000..86d5ad6e12 --- /dev/null +++ b/packages/project-integrity/src/integrity/check.ts @@ -0,0 +1,99 @@ +import { existsSync } from 'fs'; +import type { CheckIntegrityResult, Content, ContentIntegrity, FileIntegrity, Integrity } from '../types'; +import { getContentIntegrity, getFileIntegrity } from './hash'; + +/** + * Check existing integrity data. + * + * @param integrityData - integrity data + * @param [additionalStringContent] - optional additional new string content + * @returns - results of the check + */ +export async function checkIntegrity( + integrityData: Integrity, + additionalStringContent?: Content +): Promise { + return { + files: await checkFileIntegrity(integrityData.fileIntegrity), + additionalStringContent: checkContentIntegrity(integrityData.contentIntegrity, additionalStringContent) + }; +} + +/** + * Check an array of file hashes against the current state of the files. + * + * @param fileIntegrity - array of file integrity data + * @returns - results of the check + */ +async function checkFileIntegrity(fileIntegrity: FileIntegrity[]): Promise { + const differentFiles: CheckIntegrityResult['files']['differentFiles'] = []; + const equalFiles: string[] = []; + const checkFiles: FileIntegrity[] = []; + + for (const integrity of fileIntegrity) { + if (!existsSync(integrity.filePath)) { + differentFiles.push({ filePath: integrity.filePath, oldContent: integrity.content, newContent: '' }); + } else { + checkFiles.push(integrity); + } + } + const newFileIntegrityArray = await getFileIntegrity(checkFiles.map((fileIntegrity) => fileIntegrity.filePath)); + for (const newFileIntegrity of newFileIntegrityArray) { + const oldFileIntegrity = checkFiles.find((fileHash) => fileHash.filePath === newFileIntegrity.filePath); + if (oldFileIntegrity && oldFileIntegrity.hash === newFileIntegrity.hash) { + equalFiles.push(oldFileIntegrity.filePath); + } else { + differentFiles.push({ + filePath: newFileIntegrity.filePath, + oldContent: oldFileIntegrity?.content, + newContent: newFileIntegrity.content + }); + } + } + return { differentFiles, equalFiles }; +} + +/** + * Check old content integrity against new key->string values. + * + * @param contentIntegrity - existing content integrity from integrity data + * @param additionalStringContent - new additional key->string values + * @returns - result of the check + */ +function checkContentIntegrity( + contentIntegrity: ContentIntegrity[], + additionalStringContent?: Content +): CheckIntegrityResult['additionalStringContent'] { + const oldContentIntegrityArray = structuredClone(contentIntegrity); + const differentContent: CheckIntegrityResult['additionalStringContent']['differentContent'] = []; + const equalContent: string[] = []; + + const newContentIntegrityArray = getContentIntegrity(additionalStringContent); + for (const newContentIntegrity of newContentIntegrityArray) { + const index = oldContentIntegrityArray.findIndex( + (content) => content.contentKey === newContentIntegrity.contentKey + ); + let foundOldContentIntegrity; + if (index > -1) { + foundOldContentIntegrity = oldContentIntegrityArray[index]; + oldContentIntegrityArray.splice(index, 1); + } + if (foundOldContentIntegrity?.hash === newContentIntegrity.hash) { + equalContent.push(newContentIntegrity.contentKey); + } else { + differentContent.push({ + key: newContentIntegrity.contentKey, + newContent: newContentIntegrity.content, + oldContent: foundOldContentIntegrity?.content + }); + } + } + for (const missingContent of oldContentIntegrityArray) { + differentContent.push({ + key: missingContent.contentKey, + newContent: undefined, + oldContent: missingContent.content + }); + } + return { differentContent, equalContent }; +} diff --git a/packages/project-integrity/src/integrity/hash.ts b/packages/project-integrity/src/integrity/hash.ts new file mode 100644 index 0000000000..da2bdf4b86 --- /dev/null +++ b/packages/project-integrity/src/integrity/hash.ts @@ -0,0 +1,59 @@ +import { createReadStream, existsSync } from 'fs'; +import { createHash } from 'crypto'; +import type { Content, ContentIntegrity, FileIntegrity } from '../types'; + +/** + * Create a md5 hash for a given file. + * + * @param filePath - path to file + * @returns - promise that resolves to a FileHash object + */ +async function computeFileIntegrityData(filePath: string): Promise { + return new Promise((resolve, reject) => { + let content = ''; + const hash = createHash('md5'); + const fileStream = createReadStream(filePath); + fileStream.on('data', (chunk: Buffer) => { + content += chunk.toString(); + hash.update(chunk); + }); + fileStream.on('end', () => resolve({ filePath, hash: hash.digest('hex'), content })); + fileStream.on('error', (err) => reject(err)); + }); +} + +/** + * Returns integrity data for a list of given files. + * + * @param files - list of files to + * @returns - promise that resolves to an array of FileHash objects + */ +export async function getFileIntegrity(files: string[]): Promise { + const nonExistingFiles = files.filter((file) => !existsSync(file)); + if (nonExistingFiles.length > 0) { + throw new Error(`The following files do not exist: ${nonExistingFiles.join(', ')}`); + } + const promises = files.map(computeFileIntegrityData); + return await Promise.all(promises); +} + +/** + * Returns content integrity data for a map of key/value (string). + * + * @param additionalStringContent - key value map of additional content to write as integrity data + * @returns - array of + */ +export function getContentIntegrity(additionalStringContent?: Content): ContentIntegrity[] { + const contentIntegrity: ContentIntegrity[] = []; + if (additionalStringContent) { + for (const contentKey in additionalStringContent) { + const content = additionalStringContent[contentKey]; + contentIntegrity.push({ + contentKey, + hash: createHash('md5').update(content).digest('hex'), + content + }); + } + } + return contentIntegrity; +} diff --git a/packages/project-integrity/src/integrity/index.ts b/packages/project-integrity/src/integrity/index.ts new file mode 100644 index 0000000000..79d97c436c --- /dev/null +++ b/packages/project-integrity/src/integrity/index.ts @@ -0,0 +1,8 @@ +export { + checkProjectIntegrity, + disableProjectIntegrity, + enableProjectIntegrity, + initProject, + isProjectIntegrityEnabled, + updateProjectIntegrity +} from './project'; diff --git a/packages/project-integrity/src/integrity/persistence.ts b/packages/project-integrity/src/integrity/persistence.ts new file mode 100644 index 0000000000..e41eaf8087 --- /dev/null +++ b/packages/project-integrity/src/integrity/persistence.ts @@ -0,0 +1,80 @@ +import { mkdir, readFile, writeFile } from 'fs/promises'; +import { existsSync } from 'fs'; +import { dirname, join, relative } from 'path'; +import { compressToBase64, decompressFromBase64 } from 'lz-string'; +import type { ContentIntegrity, FileIntegrity, Integrity } from '../types'; + +/** + * Read hashes from a previously stored hash file. + * Throws an error if the file does not exist. + * + * @param integrityFilePath - path to the integrity file + * @returns - integrity data + */ +export async function readIntegrityData(integrityFilePath: string): Promise { + if (!existsSync(integrityFilePath)) { + throw new Error(`Integrity file not found at ${integrityFilePath}`); + } + const content = JSON.parse(await readFile(integrityFilePath, { encoding: 'utf-8' })) as Integrity; + const integrityDir = dirname(integrityFilePath); + for (const fileIntegrity of content.fileIntegrity) { + fileIntegrity.filePath = join(integrityDir, fileIntegrity.filePath); + getifyContent(fileIntegrity); + } + for (const contentIntegrity of content.contentIntegrity) { + getifyContent(contentIntegrity); + } + return content; +} + +/** + * Write file integrity information to file. When storing, use relative paths to the hash file + * so the project as whole can be moved without breaking integrity. + * + * @param integrityFilePath - path to the integrity file + * @param content - content to write to the integrity file + */ +export async function writeIntegrityData(integrityFilePath: string, content: Integrity): Promise { + const integrityDir = dirname(integrityFilePath); + if (!existsSync(integrityDir)) { + await mkdir(integrityDir, { recursive: true }); + } + + for (const fileIntegrity of content.fileIntegrity) { + fileIntegrity.filePath = relative(integrityDir, fileIntegrity.filePath); + if (typeof fileIntegrity.content === 'string') { + fileIntegrity.content = compressToBase64(fileIntegrity.content); + } + } + + for (const contentIntegrity of content.contentIntegrity) { + if (typeof contentIntegrity.content === 'string') { + contentIntegrity.content = compressToBase64(contentIntegrity.content); + } + } + + await writeFile(integrityFilePath, JSON.stringify(content), { encoding: 'utf-8' }); +} + +/** + * Wrap content with getter to decompress on first access. Do nothing if the content does not exist. + * + * @param integrityObject - file or content integrity data with compressed content + */ +function getifyContent(integrityObject: FileIntegrity | ContentIntegrity): void { + if (typeof integrityObject?.content === 'string') { + const compressedContent = integrityObject.content; + let content: string | undefined; + Object.defineProperty(integrityObject, 'content', { + get: () => { + if (!content) { + content = decompressFromBase64(compressedContent); + } + return content; + }, + set: () => { + // Read-only property, ignore content changes + } + }); + } +} diff --git a/packages/project-integrity/src/integrity/project.ts b/packages/project-integrity/src/integrity/project.ts new file mode 100644 index 0000000000..06d16da6f0 --- /dev/null +++ b/packages/project-integrity/src/integrity/project.ts @@ -0,0 +1,133 @@ +import { existsSync } from 'fs'; +import type { CheckIntegrityResult, Content, ProjectSettings } from '../types'; +import { getContentIntegrity, getFileIntegrity } from './hash'; +import { readIntegrityData, writeIntegrityData } from './persistence'; +import { checkIntegrity } from './check'; + +/** + * Function to ensure correct sorting of strings. + * + * @param a - first value to compare + * @param b - second value to compare + * @returns - 1 if a is greater than b, -1 if a is less than b, 0 if a is equal to b + */ +const sortLocal = (a: string, b: string): number => a.localeCompare(b); + +/** + * Initialize a project by creating hashes for all selected files in the project. There is an option to add + * additional key->string content to the integrity data. + * + * @param settings - settings for the project + * @param settings.integrityFilePath - path to file where integrity data will be stored + * @param settings.fileList - list of file paths for files to create integrity data for + * @param [settings.additionalStringContent] - optional key/string map to add to integrity data + */ +export async function initProject(settings: ProjectSettings): Promise { + const enabled = true; + const fileIntegrity = await getFileIntegrity(settings.fileList); + const contentIntegrity = await getContentIntegrity(settings.additionalStringContent); + await writeIntegrityData(settings.integrityFilePath, { enabled, fileIntegrity, contentIntegrity }); +} + +/** + * Check the integrity of a project by comparing the stored integrity data with the current state + * of the files and additional key->string. Throws an error if the project is not initialized, + * which means no integrity data found at 'integrityFilePath'. + * + * @param integrityFilePath - path to file where integrity data is stored + * @param [additionalStringContent] - optional key/string map to add to integrity data + * @returns - results of the check + */ +export async function checkProjectIntegrity( + integrityFilePath: string, + additionalStringContent?: Content +): Promise { + const integrityData = await readIntegrityData(integrityFilePath); + if (!integrityData.enabled) { + throw new Error(`Integrity is disabled for the project with integrity data ${integrityFilePath}`); + } + const checkResult = checkIntegrity(integrityData, additionalStringContent); + return checkResult; +} + +/** + * Updates the integrity data of a project. Throws an error if a file or string content is missing + * or new. + * + * @param integrityFilePath - path to file where integrity data is stored + * @param additionalStringContent - optional key/string map to add to integrity data + */ +export async function updateProjectIntegrity( + integrityFilePath: string, + additionalStringContent?: Content +): Promise { + if (!existsSync(integrityFilePath)) { + throw new Error(`Integrity data not found at ${integrityFilePath}`); + } + const integrityData = await readIntegrityData(integrityFilePath); + if (!integrityData.enabled) { + throw new Error(`Integrity is disabled for the project with integrity data ${integrityFilePath}`); + } + const existingContentKeys = integrityData.contentIntegrity.map((content) => content.contentKey).sort(sortLocal); + const newContentKeys = Object.keys(additionalStringContent ?? {}).sort(sortLocal); + if ( + existingContentKeys.length !== newContentKeys.length || + !existingContentKeys.every((key, index) => key === newContentKeys[index]) + ) { + throw new Error( + `There is a mismatch of additional content keys. +Stored content keys: ${existingContentKeys.join(', ')} +New content keys: ${newContentKeys.join(', ')}` + ); + } + const fileIntegrity = await getFileIntegrity(integrityData.fileIntegrity.map((file) => file.filePath)); + const contentIntegrity = getContentIntegrity(additionalStringContent); + await writeIntegrityData(integrityFilePath, { enabled: integrityData.enabled, fileIntegrity, contentIntegrity }); +} + +/** + * Return whether integrity is enabled for a project. + * + * @param integrityFilePath - path to file where integrity data is stored + * @returns - true if integrity is enabled, false otherwise + */ +export async function isProjectIntegrityEnabled(integrityFilePath: string): Promise { + if (!existsSync(integrityFilePath)) { + throw new Error(`Integrity data not found at ${integrityFilePath}`); + } + const { enabled } = await readIntegrityData(integrityFilePath); + return enabled; +} + +/** + * Enable integrity for a project. The project has to be initialized before enabling integrity. After initialization, + * the project integrity is enabled by default. + * + * @param integrityFilePath - path to file where integrity data is stored + */ +export async function enableProjectIntegrity(integrityFilePath: string): Promise { + if (!existsSync(integrityFilePath)) { + throw new Error(`Integrity data not found at ${integrityFilePath}`); + } + const integrityData = await readIntegrityData(integrityFilePath); + if (!integrityData.enabled) { + integrityData.enabled = true; + await writeIntegrityData(integrityFilePath, integrityData); + } +} + +/** + * Disable integrity for a project. The project has to be initialized before disabling integrity. + * + * @param integrityFilePath - path to file where integrity data is stored + */ +export async function disableProjectIntegrity(integrityFilePath: string): Promise { + if (!existsSync(integrityFilePath)) { + throw new Error(`Integrity data not found at ${integrityFilePath}`); + } + const integrityData = await readIntegrityData(integrityFilePath); + if (integrityData.enabled) { + integrityData.enabled = false; + await writeIntegrityData(integrityFilePath, integrityData); + } +} diff --git a/packages/project-integrity/src/types.ts b/packages/project-integrity/src/types.ts new file mode 100644 index 0000000000..a3d363b862 --- /dev/null +++ b/packages/project-integrity/src/types.ts @@ -0,0 +1,75 @@ +/** + * Project settings when initializing project integrity. + */ +export interface ProjectSettings { + /** + * Path to the file where integrity data, like hash values, are stored + */ + integrityFilePath: string; + + /** + * List of files to create a hash value for + */ + fileList: string[]; + + /** + * List of content (key->string) to create a hash value for + */ + additionalStringContent?: Content; +} + +/** + * Integrity data for a project, stored as JSON in integrity file. + */ +export interface Integrity { + enabled: boolean; + fileIntegrity: FileIntegrity[]; + contentIntegrity: ContentIntegrity[]; +} + +/** + * Integrity data for a file. + */ +export interface FileIntegrity { + filePath: string; + hash: string; + content?: string; +} + +/** + * Integrity data for a content (key->string). + */ +export interface ContentIntegrity { + contentKey: string; + hash: string; + content?: string; +} + +/** + * Content (key->string) to store in integrity data. + */ +export interface Content { + [contentKey: string]: string; +} + +/** + * Result of a project integrity check. + */ +export interface CheckIntegrityResult { + files: { + differentFiles: { + filePath: string; + oldContent?: string; + newContent?: string; + }[]; + equalFiles: string[]; + }; + additionalStringContent: { + differentContent: { + key: string; + oldContent?: string; + newContent?: string; + }[]; + equalContent: string[]; + }; +} diff --git a/packages/project-integrity/test/test-input/disabled-fiori-project/.fiori-ai/ai-integrity.json b/packages/project-integrity/test/test-input/disabled-fiori-project/.fiori-ai/ai-integrity.json new file mode 100644 index 0000000000..605f083514 --- /dev/null +++ b/packages/project-integrity/test/test-input/disabled-fiori-project/.fiori-ai/ai-integrity.json @@ -0,0 +1,5 @@ +{ + "enabled": false, + "fileIntegrity": [], + "contentIntegrity": [] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/disabled-project/integrity.json b/packages/project-integrity/test/test-input/disabled-project/integrity.json new file mode 100644 index 0000000000..605f083514 --- /dev/null +++ b/packages/project-integrity/test/test-input/disabled-project/integrity.json @@ -0,0 +1,5 @@ +{ + "enabled": false, + "fileIntegrity": [], + "contentIntegrity": [] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/enabled-fiori-project/.fiori-ai/ai-integrity.json b/packages/project-integrity/test/test-input/enabled-fiori-project/.fiori-ai/ai-integrity.json new file mode 100644 index 0000000000..426c2386fe --- /dev/null +++ b/packages/project-integrity/test/test-input/enabled-fiori-project/.fiori-ai/ai-integrity.json @@ -0,0 +1,5 @@ +{ + "enabled": true, + "fileIntegrity": [], + "contentIntegrity": [] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/enabled-project/integrity.json b/packages/project-integrity/test/test-input/enabled-project/integrity.json new file mode 100644 index 0000000000..426c2386fe --- /dev/null +++ b/packages/project-integrity/test/test-input/enabled-project/integrity.json @@ -0,0 +1,5 @@ +{ + "enabled": true, + "fileIntegrity": [], + "contentIntegrity": [] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/invalid-fiori-project-no-schema-cds/srv/service.cds b/packages/project-integrity/test/test-input/invalid-fiori-project-no-schema-cds/srv/service.cds new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/project-integrity/test/test-input/invalid-fiori-project-no-service-cds/db/schema.cds b/packages/project-integrity/test/test-input/invalid-fiori-project-no-service-cds/db/schema.cds new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/project-integrity/test/test-input/invalid-project/bad.xml b/packages/project-integrity/test/test-input/invalid-project/bad.xml new file mode 100644 index 0000000000..68c8465ba7 --- /dev/null +++ b/packages/project-integrity/test/test-input/invalid-project/bad.xml @@ -0,0 +1,3 @@ + + Changed XML + \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/invalid-project/good.txt b/packages/project-integrity/test/test-input/invalid-project/good.txt new file mode 100644 index 0000000000..e3ab9e012e --- /dev/null +++ b/packages/project-integrity/test/test-input/invalid-project/good.txt @@ -0,0 +1 @@ +Just a test file. \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/invalid-project/integrity.json b/packages/project-integrity/test/test-input/invalid-project/integrity.json new file mode 100644 index 0000000000..b64f7ac74a --- /dev/null +++ b/packages/project-integrity/test/test-input/invalid-project/integrity.json @@ -0,0 +1,37 @@ +{ + "enabled": true, + "fileIntegrity": [ + { + "filePath": "good.txt", + "hash": "1a8a45c145cca8cb08b03e0eb1f6fcd4", + "content": "FIVwzgLgBAhlEFNJQGYEsA2CB0Q=" + }, + { + "filePath": "bad.xml", + "hash": "1f1cfb2f4e58c4e5c8870252f5d522a6", + "content": "DwDwtgNgfAUABAuwAuBTEyoBVUGdlwAaAsgDJwBmAlhKsAPRoawPjRA=" + }, + { + "filePath": "non-existing.file", + "hash": "0123456789", + "content": "HIewdgBApgHglgZwC5zAcyA=" + } + ], + "contentIntegrity": [ + { + "contentKey": "one", + "hash": "e567579e9506605c8acf5e817d4dc9d8", + "content": "G4QwNgrgpgBA9gOykA==" + }, + { + "contentKey": "two", + "hash": "e4254bf5deaa77586c0012fd21858279", + "content": "G4QwNgrgpgBALgdwPZA=" + }, + { + "contentKey": "three", + "hash": "e883ac885a1b2a06043ba166a5f00c30", + "content": "G4QwNgrgpgBALgCwE5SkA===" + } + ] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/update-project/file-to-update.txt b/packages/project-integrity/test/test-input/update-project/file-to-update.txt new file mode 100644 index 0000000000..47d2739ba2 --- /dev/null +++ b/packages/project-integrity/test/test-input/update-project/file-to-update.txt @@ -0,0 +1 @@ +new content \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/update-project/integrity.json b/packages/project-integrity/test/test-input/update-project/integrity.json new file mode 100644 index 0000000000..60465c3700 --- /dev/null +++ b/packages/project-integrity/test/test-input/update-project/integrity.json @@ -0,0 +1,17 @@ +{ + "enabled": true, + "fileIntegrity": [ + { + "filePath": "file-to-update.txt", + "hash": "96c15c2bb2921193bf290df8cd85e2ba", + "content": "HYUw7gBAxg9sAuIFA===" + } + ], + "contentIntegrity": [ + { + "contentKey": "key", + "hash": "b200a3adbe85fe848b920dc35d5a69b2", + "content": "HYUw7gBAzgLgTgS2AcyA" + } + ] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/ai-integrity.json b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/ai-integrity.json new file mode 100644 index 0000000000..cca7db6757 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/ai-integrity.json @@ -0,0 +1 @@ +{"enabled":true,"fileIntegrity":[{"filePath":"../db/schema.cds","hash":"7c8aeabf4a42453e88d3309692ab45a0","content":"HYQwtgpgzgDiDGEAEAXATiAbhANgWRFAHMJJgUBuAKConIEsUBPJAFQ2xyQG8qkkA1hBYBJACIAuJAFVp46v3T0YAOXAQpAZSXAiACgCsABgCUCpKRg4A9kwgak2tPV2HT5qChAoArlC06+gBM7nyozjDaIGgoYt4OcSgQ5kowAKLAACaJCfHmMM6I0pqSSGIQ8PRgIDjmAEbW1gIuRP5IAIJQUNaV3vTWwKjWSNXALABCjc26UEgDSA1NLVAAdOhYuEgAvEgAJFC4AGbUAL40dCiME1PLPGFCoqWy8mGWNnYOTi1uZmEg9GgcC5oAFnK5jL9+ItpkQclIcuZDkCiAALWLxeF5MIFehFErwipVGopDi4KSdbq9S7zFDDdgbWpUM5AA=="},{"filePath":"../srv/service.cds","hash":"0c598d02c2ca62ced4de09026ca62811","content":"K4Zwlgdg5gBA3jALgJwIYDcCmAbAsqiVKTAW0wkRgF8YAzZAexJgHIA6NgegBMAjTkAGMAFqVRtB3ECwDcAKDkhMydGEGYkaLHgJFS5RAGUV8OTBgABBt1SJx3NLURtyqXtkzczMA2EQBPGAAVLRwYVBAYAAdGACtMQUQwBggYFM0MHHxCYjIKNhDM7HlzXwCYACEGBgBrSChIiOi4hKT09JQi7L085yra+pB5KiA==="}],"contentIntegrity":[{"contentKey":"capPaths","hash":"a6b6345c05e820d69b779cef1587a209","content":"N4IghgDhIFzlB6EAaEATARrdGmoM4BOAbtkcUgL5A==="}]} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/generator-config.json b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/generator-config.json new file mode 100644 index 0000000000..04732a29f5 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/generator-config.json @@ -0,0 +1,31 @@ +{ + "version": "0.2", + "floorplan": "FE_LROP", + "project": { + "name": "travelmanagement", + "targetFolder": "/Users/d045154/.fioritools/fiori-tools-ai", + "title": "AI generated App travelmanagement", + "description": "AI generated App travelmanagement", + "ui5Version": "", + "localUI5Version": "1.82.2", + "sapux": true + }, + "service": { + "servicePath": "/odata/v4/travel-management-srv/", + "capService": { + "projectPath": "/Users/d045154/.fioritools/fiori-tools-ai", + "serviceName": "travelManagementSrv", + "serviceCdsPath": "srv/service" + } + }, + "entityConfig": { + "mainEntity": { + "entityName": "Travel" + } + }, + "projectType": "LIST_REPORT_OBJECT_PAGE", + "telemetryData": { + "generationSourceName": "AI Headless", + "generationSourceVersion": "1.15.3" + } +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/log.txt b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/log.txt new file mode 100644 index 0000000000..47b1caad27 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/log.txt @@ -0,0 +1,553 @@ +2024-10-28T20:25:39.272Z [info] Project generation uses model: "gpt-4o", version: "latest" +2024-10-28T20:25:39.291Z [info] 1/5: Regenerate CAP model via LLM +2024-10-28T20:25:39.292Z [info] Fiori AI Generation Workbench: no 'fioriAiConfig.json' present +2024-10-28T20:25:39.292Z [info] Starting built-in flow "appSkeletonFirstImage" +2024-10-28T20:25:44.811Z [info] +==========LLM call for step "extractImage":========== +The below image is used to describe a Fiori Element application. +Your task is to characterize this image! + + + + +----------Requested Schema:--- +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "user readable name to refer to that image; upperCamelCase, no spaces" + }, + "description": { + "type": "string", + "description": "short description of what is on this image (single sentence)" + }, + "contentKindReason": { + "type": "string", + "description": "provide short reason for the chosen 'contentKind'" + }, + "contentKind": { + "enum": [ + "ListReport", + "ObjectPage", + "DataModel", + "Mixture", + "None" + ], + "description": "image is showing a Fiori ELement List Report page ('ListReport'), Object Page ('ObjectPage'), a data model diagram consisting of entities, their properties and associations ('DataModel'), a 'Mixture' of these or 'None' of these" + }, + "contentSize": { + "enum": [ + "small", + "medium", + "large" + ], + "description": "use 'small' for e.g. icons or company logos, 'medium' for screenshot or diagram with up to twelve labels/captions/texts, 'large' for screenshot or diagram with more than twelve labels/captions/texts" + } + }, + "required": [ + "name", + "description", + "contentKind", + "contentSize" + ], + "additionalProperties": false +} + +----------Response:---------- +{ + "name": "FioriAppManagingTravels", + "description": "A whiteboard diagram describing a Fiori app for managing travels, including entities for Travel and Bookings with their properties and relationships.", + "contentKindReason": "The image shows a data model diagram with entities and their properties and associations.", + "contentKind": "DataModel", + "contentSize": "large" +} +2024-10-28T20:25:47.412Z [info] +==========LLM call for step "extractAppSkeleton":========== +You help the user building a Fiori Elements application described in the user input. + +A Fiori Elements application typically starts with a ListReport page showing the data of the base entity of the application in a table. +Details of a specific table row are shown in the ObjectPage. This first ObjectPage is therefore based on the base entity of the application. +An ObjectPage can contain one or more table sections based on to-many associations of its entity type. +The details of a table section row can be shown in an another ObjectPage based on the associations target entity. + +Your task is to find the entities (with their to-many associations) as well as the ObjectPages (with the to-many associations they use in table sections) described in the user input below. + + + + (DataModel): A whiteboard diagram describing a Fiori app for managing travels, including entities for Travel and Bookings with their properties and relationships. + + + +----------Requested Schema:--- +{ + "type": "object", + "properties": { + "entities": { + "type": "array", + "description": "Define all entities and their to-many associations; entity properties will be determined later", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "UpperCamelCase, no spaces, plural, e.g. 'Orders'" + }, + "label": { + "type": "string", + "description": "max two words, singular" + }, + "associations": { + "type": "array", + "description": "Define to-many associations between entities", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "target": { + "type": "string", + "description": "Points to existing entity, value is entities.name" + } + }, + "required": [ + "name", + "target" + ], + "additionalProperties": false + } + }, + "labelPlural": { + "type": "string" + } + }, + "required": [ + "name", + "label", + "associations", + "labelPlural" + ], + "additionalProperties": false + } + }, + "baseEntity": { + "type": "string", + "description": "base entity of ListReport page (if present) and first ObjectPage" + }, + "noListReportPage": { + "type": "boolean", + "description": "true: no ListReport page will generated, e.g. for applications which start with an (input enabled) Object Page to enter data" + }, + "objectPages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entityType": { + "type": "string", + "description": "Entity that the ObjectPage is based on, the first ObjectPage is always based on base entity" + }, + "tableSectionsAssociations": { + "type": "array", + "items": { + "type": "string" + }, + "description": "to-many associations of page entity for table sections shown in the ObjectPage" + } + }, + "required": [ + "entityType" + ], + "additionalProperties": false + } + }, + "namespace": { + "type": "string", + "description": "lowerCamelCase, no spaces allowed" + }, + "applicationName": { + "type": "string", + "description": "lowerCamelCase, no spaces allowed" + }, + "userInputKind": { + "enum": [ + "UserInterfaceFirst", + "DataModelFirst" + ], + "description": "use 'UserInterfaceFirst' if User input describes the app based on the user interface, i.e. how List Report and Object Pages should look like; use 'DataModelFirst' if User input describes the app based on its data model, i.e. its entities, their elements and associations" + } + }, + "required": [ + "entities", + "baseEntity", + "objectPages", + "namespace", + "applicationName", + "userInputKind" + ], + "additionalProperties": false +} + +----------Response:---------- +{ + "entities": [ + { + "name": "Travels", + "label": "Travel", + "associations": [ + { + "name": "Bookings", + "target": "Bookings" + } + ], + "labelPlural": "Travels" + }, + { + "name": "Bookings", + "label": "Booking", + "associations": [], + "labelPlural": "Bookings" + } + ], + "baseEntity": "Travels", + "noListReportPage": false, + "objectPages": [ + { + "entityType": "Travels", + "tableSectionsAssociations": [ + "Bookings" + ] + }, + { + "entityType": "Bookings", + "tableSectionsAssociations": [] + } + ], + "namespace": "travelApp", + "applicationName": "manageTravels", + "userInputKind": "DataModelFirst" +} +2024-10-28T20:25:47.413Z [info] Step "switchBasedOnAppSkeleton": case 0 blocked! Evaluation details: + - JSONPath "$.model.applications[0].pages[0]" : [{"type":"ListReport","entityType":"Travels"}] +2024-10-28T20:25:47.414Z [info] Step "switchBasedOnAppSkeleton": case 1 blocked! Evaluation details: + - JSONPath "$.model.applications[0].pages[0].type" : ["ListReport"] +2024-10-28T20:25:47.414Z [info] Step "switchBasedOnAppSkeleton": case 2 blocked! Evaluation details: + - JSONPath "$.userInput.imageCount" : [1] +2024-10-28T20:25:47.414Z [info] Step "switchBasedOnAppSkeleton": case 3 passed! Evaluation details: + - JSONPath "$.userInput.imageCount" : [1] + - JSONPath "$.userInput.lineCount" : [1] +2024-10-28T20:25:47.414Z [info] Step "switchBasedOnSingleImagesKind": case 0 passed! Evaluation details: + - JSONPath "$.imagesArray[0].extractedData.contentKind" : ["DataModel"] +2024-10-28T20:25:47.414Z [info] Step "runDataModelImageOnly": workflow "dataModelImageOnly" started +" +2024-10-28T20:25:55.133Z [info] +==========LLM call for step "extractDataModel":========== +You help the user creating the data model (entities with their associations and elements) from the users input. + +User input is the following image. + + +----------Requested Schema:--- +{ + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "lowerCamelCase, no spaces allowed" + }, + "entities": { + "type": "array", + "items": { + "type": "object", + "description": "Mark at max 3 elements as 'essentialElement', the most essential elements. for example elements like: status, priority, state, quantity, daysProcessed.", + "properties": { + "name": { + "type": "string", + "description": "UpperCamelCase, no spaces, plural, e.g. 'Orders'" + }, + "label": { + "type": "string", + "description": "max two words, singular" + }, + "elements": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "camelCase, no spaces" + }, + "type": { + "type": "string", + "description": "Allowed values: String(50), Boolean, Integer, Decimal, Double, Date, Time, DateTime, Timestamp; String length can vary - i.e. String(20)" + }, + "key": { + "type": "boolean", + "description": "max. one optional key, only if this element is a semantic key and can uniquely identify the instance" + }, + "essentialElement": { + "type": "boolean", + "description": "'true' only if element is marked as essential'" + }, + "additionalInfo": { + "type": "string", + "description": "All additional information from user input, e.g. user description, example values; also consider referenced parts in the user input" + } + }, + "required": [ + "name", + "type", + "additionalInfo" + ], + "additionalProperties": false + }, + "description": "At least 5 realistic elements (unless explicitly specified)" + }, + "associations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "target": { + "type": "string", + "description": "Points to existing entity, value is entities.name" + }, + "multiplicity": { + "type": "string", + "enum": [ + "one", + "many" + ] + } + }, + "required": [ + "name", + "target", + "multiplicity" + ], + "additionalProperties": false + }, + "description": "Define all relationships between entities" + }, + "labelPlural": { + "type": "string" + } + }, + "required": [ + "name", + "label", + "elements", + "associations", + "labelPlural" + ], + "additionalProperties": false + } + }, + "baseEntityName": { + "type": "string", + "description": "name of the entity which can be used as base entity of a Giori Elements app - should not be the target of any 1:n association" + }, + "applicationName": { + "type": "string", + "description": "name of the Fiori Elements app that shows data of this data model" + } + }, + "required": [ + "namespace", + "entities", + "baseEntityName", + "applicationName" + ], + "additionalProperties": false +} + +----------Response:---------- +{ + "namespace": "travelManagement", + "entities": [ + { + "name": "Travel", + "label": "Travel", + "elements": [ + { + "name": "tripName", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Name of the trip" + }, + { + "name": "employee", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Employee associated with the trip" + }, + { + "name": "status", + "type": "String(20)", + "key": false, + "essentialElement": true, + "additionalInfo": "Status of the trip" + }, + { + "name": "tripStartDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Start date of the trip" + }, + { + "name": "tripEndDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "End date of the trip" + }, + { + "name": "priceUSD", + "type": "Decimal", + "key": false, + "essentialElement": false, + "additionalInfo": "Price of the trip in USD" + } + ], + "associations": [ + { + "name": "bookings", + "target": "Bookings", + "multiplicity": "many" + } + ], + "labelPlural": "Travels" + }, + { + "name": "Bookings", + "label": "Booking", + "elements": [ + { + "name": "employee", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Employee associated with the booking" + }, + { + "name": "airlines", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Airlines for the booking" + }, + { + "name": "bookingDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Date of booking" + }, + { + "name": "flightDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Date of the flight" + }, + { + "name": "priceUSD", + "type": "Decimal", + "key": false, + "essentialElement": false, + "additionalInfo": "Price of the booking in USD" + } + ], + "associations": [], + "labelPlural": "Bookings" + } + ], + "baseEntityName": "Travel", + "applicationName": "Managing Travels" +} +2024-10-28T20:25:55.136Z [info] 2/5: Build CDS files +2024-10-28T20:25:55.152Z [info] 3/5: Generating mock data +2024-10-28T20:26:04.144Z [info] +==========LLM call for data generation:========== + +For the following list of entities, create data for the empty CSV files. +Entity "Travel" - Properties: + tripName: String(50), Name of the trip + employee: String(50), Employee associated with the trip + status: String(20), Status of the trip + tripStartDate: Date, Start date of the trip + tripEndDate: Date, End date of the trip + priceUSD: Decimal, Price of the trip in USD + +Entity "Bookings" - Properties: + employee: String(50), Employee associated with the booking + airlines: String(50), Airlines for the booking + bookingDate: Date, Date of booking + flightDate: Date, Date of the flight + priceUSD: Decimal, Price of the booking in USD + travel_tripName: String(50), tripName of associated Travel + +Empty CSV files in json format (only generate content for these files!): +{"Travel.csv":"tripName;employee;status;tripStartDate;tripEndDate;priceUSD","Bookings.csv":"employee;airlines;bookingDate;flightDate;priceUSD;travel_tripName"} + +Rules: + - Take over the given header line, do not invent new columns! + - For a given file, add rows, add '\n' characters before each row but don't break the JSON string + - Do not generate rows with same key values, they must be unique! + - Make sure that associations match, i.e. a field containing "Name of the associated Customer" must contain an existing Name of entity Customer + - Always use ";" as a delimiter in CSV files + - The result must be proper JSON format so it can be parsed, don't break JSON strings within, don't add quotes around values. + - Create about 5 rows for the first csv, 7 for the second and so on + - All values must be realistic with meaningful content, e.g. 'Laptop Screen' and not just placeholder values with incremented numbers like 'Product 1', 'Product 2' etc. + +Use format for values: + String(50): string no longer than 50 characters + Date: 2021-06-27 + Decimal(3,2): 1.23 + + +Example output (Do NOT include in output!): +```json +{"Blogs":"blogID;title;description;createdDateTime;views\nBlog1;Tech Blog;A blog about the latest technology trends.;2023-06-27T14:52:23Z;1000\nBlog2;Fashion Blog;Explore the world of fashion and style.;2023-06-27T14:52:23Z;500","Posts":"postID;title;content;publishDate;likes;comments;blogID\n1;The Future of Artificial Intelligence;Artificial intelligence is shaping the future of technology.;2023-06-27;50;10;Blog1 +2;Summer Fashion Trends;Discover the hottest fashion trends for the summer season.;2023-06-27;100;20;Blog2\n3;Must-Have Accessories;10 must-have accessories for the upcoming season;2023-06-27;100;20;Blog2"} +``` +Output ONLY the json contents for the csv files and try to be as accurate as possible. +The generated data should be suitable for the following User Input: + + + +----------Requested Schema:--- +{ + "type": "object", + "properties": { + "Travel.csv": { + "type": "string", + "description": "content for Travel.csv" + }, + "Bookings.csv": { + "type": "string", + "description": "content for Bookings.csv" + } + }, + "required": [ + "Travel.csv", + "Bookings.csv" + ], + "additionalProperties": false +} + +----------Response:---------- +{ + "Travel.csv": "tripName;employee;status;tripStartDate;tripEndDate;priceUSD\nSales Conference;John Doe;Confirmed;2021-07-01;2021-07-05;1500.00\nTech Expo;Jane Smith;Pending;2021-08-15;2021-08-20;2000.00\nMarketing Summit;Alice Johnson;Cancelled;2021-09-10;2021-09-12;800.00\nLeadership Retreat;Bob Brown;Confirmed;2021-10-05;2021-10-10;2500.00\nClient Meeting;Charlie Davis;Completed;2021-11-01;2021-11-03;1200.00", + "Bookings.csv": "employee;airlines;bookingDate;flightDate;priceUSD;travel_tripName\nJohn Doe;Delta Airlines;2021-06-20;2021-07-01;500.00;Sales Conference\nJohn Doe;American Airlines;2021-06-22;2021-07-05;450.00;Sales Conference\nJane Smith;United Airlines;2021-08-01;2021-08-15;600.00;Tech Expo\nAlice Johnson;Southwest Airlines;2021-09-01;2021-09-10;300.00;Marketing Summit\nBob Brown;JetBlue;2021-09-25;2021-10-05;700.00;Leadership Retreat\nCharlie Davis;Alaska Airlines;2021-10-20;2021-11-01;400.00;Client Meeting\nCharlie Davis;Spirit Airlines;2021-10-22;2021-11-03;350.00;Client Meeting" +} +2024-10-28T20:26:04.146Z [info] 4/5: Regenerating application manifest +2024-10-28T20:26:07.223Z [info] 5/5: Copying updated files to CAP project \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/model.json b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/model.json new file mode 100644 index 0000000000..22f9b45e3d --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/model.json @@ -0,0 +1,179 @@ +{ + "namespace": "travelManagement", + "entities": [ + { + "name": "Travel", + "label": "Travel", + "elements": [ + { + "name": "ID", + "type": "UUID", + "key": true, + "label": "" + }, + { + "name": "tripName", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Name of the trip", + "label": "Trip Name" + }, + { + "name": "employee", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Employee associated with the trip", + "label": "Employee" + }, + { + "name": "status", + "type": "String(20)", + "key": false, + "essentialElement": true, + "additionalInfo": "Status of the trip", + "label": "Status" + }, + { + "name": "tripStartDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Start date of the trip", + "label": "Trip Start Date" + }, + { + "name": "tripEndDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "End date of the trip", + "label": "Trip End Date" + }, + { + "name": "priceUSD", + "type": "Decimal", + "key": false, + "essentialElement": false, + "additionalInfo": "Price of the trip in USD", + "label": "Price USD" + } + ], + "associations": [ + { + "name": "bookings", + "target": "Bookings", + "multiplicity": "many", + "backAssociation": "travel" + } + ], + "labelPlural": "Travels" + }, + { + "name": "Bookings", + "label": "Booking", + "elements": [ + { + "name": "ID", + "type": "UUID", + "key": true, + "label": "" + }, + { + "name": "employee", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Employee associated with the booking", + "label": "Employee" + }, + { + "name": "airlines", + "type": "String(50)", + "key": false, + "essentialElement": false, + "additionalInfo": "Airlines for the booking", + "label": "Airlines" + }, + { + "name": "bookingDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Date of booking", + "label": "Booking Date" + }, + { + "name": "flightDate", + "type": "Date", + "key": false, + "essentialElement": true, + "additionalInfo": "Date of the flight", + "label": "Flight Date" + }, + { + "name": "priceUSD", + "type": "Decimal", + "key": false, + "essentialElement": false, + "additionalInfo": "Price of the booking in USD", + "label": "Price USD" + } + ], + "associations": [ + { + "name": "travel", + "target": "Travel", + "multiplicity": "one", + "isComposition": false + } + ], + "labelPlural": "Bookings" + } + ], + "service": { + "name": "travelManagementSrv", + "path": "srv/service.cds" + }, + "applications": [ + { + "name": "TravelManagement", + "pages": [ + { + "type": "ListReport", + "entityType": "Travel", + "listReportContent": { + "loadDataOnAppLaunch": "Enabled", + "filters": [ + "status", + "tripStartDate", + "tripEndDate" + ], + "tables": [] + } + }, + { + "type": "ObjectPage", + "entityType": "Travel", + "objectPageContent": { + "sections": [], + "tabs": [], + "tableSectionAssociations": [ + "bookings" + ] + } + }, + { + "type": "ObjectPage", + "entityType": "Bookings", + "objectPageContent": { + "sections": [], + "tabs": [], + "tableSectionAssociations": [] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/user-input.txt b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/user-input.txt new file mode 100644 index 0000000000..801992f0c1 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/user-input.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/userImage.png b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/userImage.png new file mode 100644 index 0000000000..362318a3d3 Binary files /dev/null and b/packages/project-integrity/test/test-input/valid-fiori-project/.fiori-ai/userImage.png differ diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.gitignore b/packages/project-integrity/test/test-input/valid-fiori-project/.gitignore new file mode 100644 index 0000000000..bf21225f8d --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.gitignore @@ -0,0 +1,34 @@ +# CAP fiori-tools-ai +_out +*.db +*.sqlite +connection.properties +default-*.json +.cdsrc-private.json +gen/ +node_modules/ +target/ + +# Web IDE, App Studio +.che/ +.gen/ + +# MTA +*_mta_build_tmp +*.mtar +mta_archives/ + +# Other +.DS_Store +*.orig +*.log + +*.iml +*.flattened-pom.xml + +# IDEs +# .vscode +# .idea + +# @cap-js/cds-typer +@cds-models diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/extensions.json b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/extensions.json new file mode 100644 index 0000000000..d558687bc3 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/extensions.json @@ -0,0 +1,18 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "SAPSE.vscode-cds", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "mechatroner.rainbow-csv", + "qwtel.sqlite-viewer", + "humao.rest-client" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + + ] +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/launch.json b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/launch.json new file mode 100644 index 0000000000..188a19c4f1 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "cds serve", + "request": "launch", + "type": "node", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "cds", + "args": [ + "serve", + "--with-mocks", + "--in-memory?" + ], + "skipFiles": [ + "/**" + ] + } + ] +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/settings.json b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/settings.json new file mode 100644 index 0000000000..aa9de54c84 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + // uncomment entries once all libraries have been installed via 'npm install' + "eslint.validate": [ + // "cds", + // "csn", + // "csv", + // "csv (semicolon)", + // "tab", + // "tsv" + ] +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/tasks.json b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/tasks.json new file mode 100644 index 0000000000..68697ef725 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "cds watch", + "command": "cds", + "args": ["watch"], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "type": "shell", + "label": "cds serve", + "command": "cds", + "args": ["serve", "--with-mocks", "--in-memory?"], + "problemMatcher": [] + } + ] +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/README.md b/packages/project-integrity/test/test-input/valid-fiori-project/README.md new file mode 100644 index 0000000000..dbac29eb15 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/README.md @@ -0,0 +1,25 @@ +# Getting Started + +Welcome to your new project. + +It contains these folders and files, following our recommended project layout: + +File or Folder | Purpose +---------|---------- +`app/` | content for UI frontends goes here +`db/` | your domain models and data go here +`srv/` | your service models and code go here +`package.json` | project metadata and configuration +`readme.md` | this getting started guide + + +## Next Steps + +- Open a new terminal and run `cds watch` +- (in VS Code simply choose _**Terminal** > Run Task > cds watch_) +- Start adding content, for example, a [db/schema.cds](db/schema.cds). + + +## Learn More + +Learn more at https://cap.cloud.sap/docs/get-started/. diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/services.cds b/packages/project-integrity/test/test-input/valid-fiori-project/app/services.cds new file mode 100644 index 0000000000..81b0dc78c1 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/services.cds @@ -0,0 +1,2 @@ + +using from './travelmanagement/annotations'; \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/README.md b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/README.md new file mode 100644 index 0000000000..0b3083b899 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/README.md @@ -0,0 +1,35 @@ +## Application Details +| | +| ------------- | +|**Generation Date and Time**
Mon Oct 21 2024 13:50:13 GMT-0700 (Pacific Daylight Time)| +|**App Generator**
@sap/generator-fiori-elements| +|**App Generator Version**
1.15.3-pre-20241016112524-cb4533d78.0| +|**Generation Platform**
CLI| +|**Template Used**
List Report Page V4| +|**Service Type**
Local Cap| +|**Service URL**
http://localhost:4004/odata/v4/travel-management-srv/| +|**Module Name**
travelmanagement| +|**Application Title**
AI generated App travelmanagement| +|**Namespace**
| +|**UI5 Theme**
sap_horizon| +|**UI5 Version**
1.126.0| +|**Enable Code Assist Libraries**
False| +|**Enable TypeScript**
False| +|**Add Eslint configuration**
False| +|**Main Entity**
Travel| + +## travelmanagement + +AI generated App travelmanagement + +### Starting the generated app + +- This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: + +http://localhost:4004/travelmanagement/webapp/index.html + +#### Pre-requisites: + +1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) + + diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/annotations.cds b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/annotations.cds new file mode 100644 index 0000000000..9482676c46 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/annotations.cds @@ -0,0 +1,148 @@ +using { travelManagementSrv } from '../../srv/service.cds'; + +annotate travelManagementSrv.Travel with @UI.DataPoint #status: { + Value: status, + Title: 'Status', +}; +annotate travelManagementSrv.Travel with @UI.DataPoint #tripStartDate: { + Value: tripStartDate, + Title: 'Trip Start Date', +}; +annotate travelManagementSrv.Travel with @UI.DataPoint #tripEndDate: { + Value: tripEndDate, + Title: 'Trip End Date', +}; +annotate travelManagementSrv.Travel with @UI.HeaderFacets: [ + { $Type : 'UI.ReferenceFacet', Target : '@UI.DataPoint#status', ID: 'Status' }, + { $Type : 'UI.ReferenceFacet', Target : '@UI.DataPoint#tripStartDate', ID: 'TripStartDate' }, + { $Type : 'UI.ReferenceFacet', Target : '@UI.DataPoint#tripEndDate', ID: 'TripEndDate' } +]; +annotate travelManagementSrv.Travel with @UI.HeaderInfo: { + TypeName: 'Travel', + TypeNamePlural: 'Travels' +}; +annotate travelManagementSrv.Travel with { + tripName @Common.Label: 'Trip Name'; + employee @Common.Label: 'Employee'; + status @Common.Label: 'Status'; + tripStartDate @Common.Label: 'Trip Start Date'; + tripEndDate @Common.Label: 'Trip End Date'; + priceUSD @Common.Label: 'Price USD'; + bookings @Common.Label: 'Bookings' +}; +annotate travelManagementSrv.Travel with @UI.SelectionFields : [ + status, + tripStartDate, + tripEndDate +]; +annotate travelManagementSrv.Travel with @UI.LineItem: [ + { $Type: 'UI.DataField', Value: tripName }, + { $Type: 'UI.DataField', Value: employee }, + { $Type: 'UI.DataField', Value: status }, + { $Type: 'UI.DataField', Value: tripStartDate }, + { $Type: 'UI.DataField', Value: tripEndDate }, + { $Type: 'UI.DataField', Value: priceUSD } +]; +annotate travelManagementSrv.Travel with @UI.FieldGroup #Main: { + $Type: 'UI.FieldGroupType', Data: [ + { $Type: 'UI.DataField', Value: tripName }, + { $Type: 'UI.DataField', Value: employee }, + { $Type: 'UI.DataField', Value: status }, + { $Type: 'UI.DataField', Value: tripStartDate }, + { $Type: 'UI.DataField', Value: tripEndDate }, + { $Type: 'UI.DataField', Value: priceUSD } +]}; +annotate travelManagementSrv.Bookings with @UI.LineItem #Travel_bookings: [ + { $Type: 'UI.DataField', Value: employee }, + { $Type: 'UI.DataField', Value: airlines }, + { $Type: 'UI.DataField', Value: bookingDate }, + { $Type: 'UI.DataField', Value: flightDate }, + { $Type: 'UI.DataField', Value: priceUSD } +]; +annotate travelManagementSrv.Travel with @UI.Facets: [ + { $Type: 'UI.ReferenceFacet', ID: 'Main', Label: 'General Information', Target: '@UI.FieldGroup#Main' }, + { $Type: 'UI.ReferenceFacet', ID: 'bookings', Label: 'Bookings', Target: 'bookings/@UI.LineItem#Travel_bookings' } +]; +annotate travelManagementSrv.Bookings with { + travel @Common.ValueList: { + CollectionPath: 'Travel', + Parameters : [ + { + $Type : 'Common.ValueListParameterInOut', + LocalDataProperty: travel_ID, + ValueListProperty: 'ID' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'tripName' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'employee' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'status' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'tripStartDate' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'tripEndDate' + }, + { + $Type : 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'priceUSD' + }, + ], + } +}; +annotate travelManagementSrv.Bookings with @UI.DataPoint #bookingDate: { + Value: bookingDate, + Title: 'Booking Date', +}; +annotate travelManagementSrv.Bookings with @UI.DataPoint #flightDate: { + Value: flightDate, + Title: 'Flight Date', +}; +annotate travelManagementSrv.Bookings with @UI.HeaderFacets: [ + { $Type : 'UI.ReferenceFacet', Target : '@UI.DataPoint#bookingDate', ID: 'BookingDate' }, + { $Type : 'UI.ReferenceFacet', Target : '@UI.DataPoint#flightDate', ID: 'FlightDate' } +]; +annotate travelManagementSrv.Bookings with @UI.HeaderInfo: { + TypeName: 'Booking', + TypeNamePlural: 'Bookings' +}; +annotate travelManagementSrv.Bookings with { + employee @Common.Label: 'Employee'; + airlines @Common.Label: 'Airlines'; + bookingDate @Common.Label: 'Booking Date'; + flightDate @Common.Label: 'Flight Date'; + priceUSD @Common.Label: 'Price USD'; + travel @Common.Label: 'Travel' +}; +annotate travelManagementSrv.Bookings with @UI.SelectionFields: [ + travel_ID +]; +annotate travelManagementSrv.Bookings with @UI.LineItem: [ + { $Type: 'UI.DataField', Value: employee }, + { $Type: 'UI.DataField', Value: airlines }, + { $Type: 'UI.DataField', Value: bookingDate }, + { $Type: 'UI.DataField', Value: flightDate }, + { $Type: 'UI.DataField', Value: priceUSD }, + { $Type: 'UI.DataField', Label: 'Travel', Value: travel_ID } +]; +annotate travelManagementSrv.Bookings with @UI.FieldGroup #Main: { + $Type: 'UI.FieldGroupType', Data: [ + { $Type: 'UI.DataField', Value: employee }, + { $Type: 'UI.DataField', Value: airlines }, + { $Type: 'UI.DataField', Value: bookingDate }, + { $Type: 'UI.DataField', Value: flightDate }, + { $Type: 'UI.DataField', Value: priceUSD }, + { $Type: 'UI.DataField', Label: 'Travel', Value: travel_ID } +]}; +annotate travelManagementSrv.Bookings with @UI.Facets: [ + { $Type: 'UI.ReferenceFacet', ID: 'Main', Label: 'General Information', Target: '@UI.FieldGroup#Main' } +]; \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/package.json b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/package.json new file mode 100644 index 0000000000..e0e5a202b3 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/package.json @@ -0,0 +1,19 @@ +{ + "name": "travelmanagement", + "version": "0.0.1", + "description": "AI generated App travelmanagement", + "keywords": [ + "ui5", + "openui5", + "sapui5" + ], + "main": "webapp/index.html", + "dependencies": {}, + "devDependencies": { + "@ui5/cli": "^3.0.0", + "@sap/ux-ui5-tooling": "1" + }, + "scripts": { + "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf" + } +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/ui5.yaml b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/ui5.yaml new file mode 100644 index 0000000000..c8a0728ea5 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/ui5.yaml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json + +specVersion: "3.1" +metadata: + name: travelmanagement +type: application +server: + customMiddleware: + - name: fiori-tools-proxy + afterMiddleware: compression + configuration: + ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted + ui5: + path: + - /resources + - /test-resources + url: https://sapui5.hana.ondemand.com + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + port: 35729 + path: webapp + delay: 300 diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/Component.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/Component.js new file mode 100644 index 0000000000..e4eb4a1b53 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/Component.js @@ -0,0 +1,12 @@ +sap.ui.define( + ["sap/fe/core/AppComponent"], + function (Component) { + "use strict"; + + return Component.extend("travelmanagement.Component", { + metadata: { + manifest: "json" + } + }); + } +); \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/i18n/i18n.properties b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/i18n/i18n.properties new file mode 100644 index 0000000000..c71fd9b259 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/i18n/i18n.properties @@ -0,0 +1,9 @@ +# This is the resource bundle for travelmanagement + +#Texts for manifest.json + +#XTIT: Application name +appTitle=AI generated App travelmanagement + +#YDES: Application description +appDescription=AI generated App travelmanagement \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/index.html b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/index.html new file mode 100644 index 0000000000..85f0adb203 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/index.html @@ -0,0 +1,35 @@ + + + + + + + AI generated App travelmanagement + + + + +
+ + \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/manifest.json b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/manifest.json new file mode 100644 index 0000000000..28d040976b --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/manifest.json @@ -0,0 +1,156 @@ +{ + "_version": "1.65.0", + "sap.app": { + "id": "travelmanagement", + "type": "application", + "i18n": "i18n/i18n.properties", + "applicationVersion": { + "version": "0.0.1" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "resources": "resources.json", + "sourceTemplate": { + "id": "@sap/generator-fiori:lrop", + "version": "1.15.3-pre-20241016112524-cb4533d78.0", + "toolsId": "4d8f2790-c93f-4c0c-9c83-5786770b1d8f" + }, + "dataSources": { + "mainService": { + "uri": "/odata/v4/travel-management-srv/", + "type": "OData", + "settings": { + "annotations": [], + "odataVersion": "4.0" + } + } + } + }, + "sap.ui": { + "technology": "UI5", + "icons": { + "icon": "", + "favIcon": "", + "phone": "", + "phone@2": "", + "tablet": "", + "tablet@2": "" + }, + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "flexEnabled": true, + "dependencies": { + "minUI5Version": "1.126.0", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.fe.templates": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "travelmanagement.i18n.i18n" + } + }, + "": { + "dataSource": "mainService", + "preload": true, + "settings": { + "operationMode": "Server", + "autoExpandSelect": true, + "earlyRequests": true + } + }, + "@i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "uri": "i18n/i18n.properties" + } + }, + "resources": { + "css": [] + }, + "routing": { + "config": {}, + "routes": [ + { + "pattern": ":?query:", + "name": "TravelList", + "target": "TravelList" + }, + { + "pattern": "Travel({key}):?query:", + "name": "TravelObjectPage", + "target": "TravelObjectPage" + }, + { + "pattern": "Travel({key})/bookings({key2}):?query:", + "name": "BookingsObjectPage", + "target": "BookingsObjectPage" + } + ], + "targets": { + "TravelList": { + "type": "Component", + "id": "TravelList", + "name": "sap.fe.templates.ListReport", + "options": { + "settings": { + "contextPath": "/Travel", + "variantManagement": "Page", + "initialLoad": "Enabled", + "navigation": { + "Travel": { + "detail": { + "route": "TravelObjectPage" + } + } + } + } + } + }, + "TravelObjectPage": { + "type": "Component", + "id": "TravelObjectPage", + "name": "sap.fe.templates.ObjectPage", + "options": { + "settings": { + "contextPath": "/Travel", + "navigation": { + "bookings": { + "detail": { + "route": "BookingsObjectPage" + } + } + } + } + } + }, + "BookingsObjectPage": { + "type": "Component", + "id": "BookingsObjectPage", + "name": "sap.fe.templates.ObjectPage", + "options": { + "settings": { + "contextPath": "/Travel/bookings" + } + } + } + } + } + }, + "sap.fiori": { + "registrationIds": [], + "archeType": "transactional" + } +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/flpSandbox.html b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/flpSandbox.html new file mode 100644 index 0000000000..298e085971 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/flpSandbox.html @@ -0,0 +1,84 @@ + + + + + + + + {{appTitle}} + + + + + + + + + + + + + + + + diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/FirstJourney.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/FirstJourney.js new file mode 100644 index 0000000000..bd9533fd02 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/FirstJourney.js @@ -0,0 +1,38 @@ +sap.ui.define([ + "sap/ui/test/opaQunit" +], function (opaTest) { + "use strict"; + + var Journey = { + run: function() { + QUnit.module("First journey"); + + opaTest("Start application", function (Given, When, Then) { + Given.iStartMyApp(); + + Then.onTheTravelList.iSeeThisPage(); + + }); + + + opaTest("Navigate to ObjectPage", function (Given, When, Then) { + // Note: this test will fail if the ListReport page doesn't show any data + + When.onTheTravelList.onFilterBar().iExecuteSearch(); + + Then.onTheTravelList.onTable().iCheckRows(); + + When.onTheTravelList.onTable().iPressRow(0); + Then.onTheTravelObjectPage.iSeeThisPage(); + + }); + + opaTest("Teardown", function (Given, When, Then) { + // Cleanup + Given.iTearDownMyApp(); + }); + } + } + + return Journey; +}); \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.html b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.html new file mode 100644 index 0000000000..d032ebe371 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.html @@ -0,0 +1,30 @@ + + + + Integration tests + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.js new file mode 100644 index 0000000000..6740434445 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/opaTests.qunit.js @@ -0,0 +1,26 @@ +sap.ui.require( + [ + 'sap/fe/test/JourneyRunner', + 'travelmanagement/test/integration/FirstJourney', + 'travelmanagement/test/integration/pages/TravelList', + 'travelmanagement/test/integration/pages/TravelObjectPage' + ], + function(JourneyRunner, opaJourney, TravelList, TravelObjectPage) { + 'use strict'; + var JourneyRunner = new JourneyRunner({ + // start index.html in web folder + launchUrl: sap.ui.require.toUrl('travelmanagement') + '/index.html' + }); + + + JourneyRunner.run( + { + pages: { + onTheTravelList: TravelList, + onTheTravelObjectPage: TravelObjectPage + } + }, + opaJourney.run + ); + } +); \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelList.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelList.js new file mode 100644 index 0000000000..e3d058424c --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelList.js @@ -0,0 +1,17 @@ +sap.ui.define(['sap/fe/test/ListReport'], function(ListReport) { + 'use strict'; + + var CustomPageDefinitions = { + actions: {}, + assertions: {} + }; + + return new ListReport( + { + appId: 'travelmanagement', + componentId: 'TravelList', + contextPath: '/Travel' + }, + CustomPageDefinitions + ); +}); \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelObjectPage.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelObjectPage.js new file mode 100644 index 0000000000..cbb69247c1 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/integration/pages/TravelObjectPage.js @@ -0,0 +1,17 @@ +sap.ui.define(['sap/fe/test/ObjectPage'], function(ObjectPage) { + 'use strict'; + + var CustomPageDefinitions = { + actions: {}, + assertions: {} + }; + + return new ObjectPage( + { + appId: 'travelmanagement', + componentId: 'TravelObjectPage', + contextPath: '/Travel' + }, + CustomPageDefinitions + ); +}); \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.html b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.html new file mode 100644 index 0000000000..1fea4a26d9 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.html @@ -0,0 +1,9 @@ + + + + QUnit test suite + + + + + \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.js b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.js new file mode 100644 index 0000000000..a37a5c973c --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/app/travelmanagement/webapp/test/testsuite.qunit.js @@ -0,0 +1,11 @@ +window.suite = function() { + 'use strict'; + + // eslint-disable-next-line + var oSuite = new parent.jsUnitTestSuite(), + + sContextPath = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1); + oSuite.addTestPage(sContextPath + 'integration/opaTests.qunit.html'); + + return oSuite; +}; \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/db/schema.cds b/packages/project-integrity/test/test-input/valid-fiori-project/db/schema.cds new file mode 100644 index 0000000000..230b93decf --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/db/schema.cds @@ -0,0 +1,22 @@ +namespace travelManagement; + +entity Travel { + key ID: UUID; + tripName: String(50); + employee: String(50); + status: String(20); + tripStartDate: Date; + tripEndDate: Date; + priceUSD: Decimal; + bookings: Association to many Bookings on bookings.travel = $self; +} + +entity Bookings { + key ID: UUID; + employee: String(50); + airlines: String(50); + bookingDate: Date; + flightDate: Date; + priceUSD: Decimal; + travel: Association to Travel; +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/eslint.config.mjs b/packages/project-integrity/test/test-input/valid-fiori-project/eslint.config.mjs new file mode 100644 index 0000000000..2fdb4320ca --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/eslint.config.mjs @@ -0,0 +1,2 @@ +import cds from '@sap/cds/eslint.config.mjs' +export default [ ...cds.recommended ] diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/package-lock.json b/packages/project-integrity/test/test-input/valid-fiori-project/package-lock.json new file mode 100644 index 0000000000..4709b86393 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/package-lock.json @@ -0,0 +1,2974 @@ +{ + "name": "fiori-tools-ai", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "fiori-tools-ai", + "version": "1.0.0", + "license": "UNLICENSED", + "dependencies": { + "@sap/cds": "^8", + "express": "^4" + }, + "devDependencies": { + "@cap-js/cds-types": "^0.6", + "@cap-js/sqlite": "^1", + "@sap/ux-specification": "UI5-1.126" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cap-js/cds-types": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@cap-js/cds-types/-/cds-types-0.6.5.tgz", + "integrity": "sha512-lcsc0Bp9aINW2cQIqaadGZ1lSWqCmk9lagoyoHfQimddbSngexevoBm7RDOL9s/L4RMKGO+kBumcPq5sBBU9SA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@types/express": "^4.17.21" + }, + "peerDependencies": { + "@sap/cds": "^8.0.0" + } + }, + "node_modules/@cap-js/db-service": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@cap-js/db-service/-/db-service-1.14.0.tgz", + "integrity": "sha512-2OgzjRhrRhRYmwDLfQubUTszsigSxj/NAIAjUPrWcBMzY56fHTexpnclCr+pr0T07jMajiHfBqkz0YjGwtH1kw==", + "dev": true, + "dependencies": { + "generic-pool": "^3.9.0" + }, + "peerDependencies": { + "@sap/cds": ">=7.9" + } + }, + "node_modules/@cap-js/sqlite": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@cap-js/sqlite/-/sqlite-1.7.4.tgz", + "integrity": "sha512-UGKd4wUwli4ZLdailbtigeNFKQnhwRmA5ei1LmJnX0K61j4tTIAUQG+2AqN73707yOKMhas3B9fdvlSs1Vw67A==", + "dev": true, + "dependencies": { + "@cap-js/db-service": "^1.9.0", + "better-sqlite3": "^11.0.0" + }, + "peerDependencies": { + "@sap/cds": ">=7.6" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sap-ux/annotation-converter": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@sap-ux/annotation-converter/-/annotation-converter-0.8.0.tgz", + "integrity": "sha512-LrTDVMRGC2V5xphaB9cDDJMxsK+YBjNbObmQcHJCk6TL2P/trcZzV2UKDsckVv43ZCXjYGK5jkhkbiGgaLbokA==", + "dev": true, + "dependencies": { + "@sap-ux/vocabularies-types": "0.10.0" + } + }, + "node_modules/@sap-ux/annotation-converter/node_modules/@sap-ux/vocabularies-types": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@sap-ux/vocabularies-types/-/vocabularies-types-0.10.0.tgz", + "integrity": "sha512-uTQ2tJJes9qGGAn8e/Npvy0grIul3mAVHzjxsj10U3lF+RZsoFawsdYGxvb6sJYkhY+YEsQzL8GTL6tyYpMv8g==", + "dev": true, + "engines": { + "node": ">=18.0.0 < 19.0.0 || >=20.0.0 < 21.0.0", + "pnpm": ">=8" + } + }, + "node_modules/@sap-ux/cds-annotation-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@sap-ux/cds-annotation-parser/-/cds-annotation-parser-0.2.2.tgz", + "integrity": "sha512-PBMvH8o8Brg/VihhPEsGUu4gac7xi70zvvHJ8xScPU5sLa5B7K0bpnbbStYU4luqqYL4UXJVqJNdWqpTp4li6w==", + "dev": true, + "dependencies": { + "@sap-ux/odata-annotation-core": "0.2.1", + "@sap-ux/odata-entity-model": "0.3.0", + "@sap-ux/odata-vocabularies": "0.4.2", + "@sap-ux/text-document-utils": "0.2.0", + "chevrotain": "7.1.1" + } + }, + "node_modules/@sap-ux/cds-odata-annotation-converter": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@sap-ux/cds-odata-annotation-converter/-/cds-odata-annotation-converter-0.3.6.tgz", + "integrity": "sha512-45QozhF3JJgycpmDdodq8Pil3K1acCzvh03UN9xILEURIjKY8ZA4gGVQPCrUHftXqNSidL0XzBHkQt1epIlrAA==", + "dev": true, + "dependencies": { + "@sap-ux/cds-annotation-parser": "0.2.2", + "@sap-ux/odata-annotation-core": "0.2.1", + "@sap-ux/odata-vocabularies": "0.4.2", + "@sap-ux/text-document-utils": "0.2.0", + "@sap/ux-cds-compiler-facade": "1.15.0", + "i18next": "20.6.1" + } + }, + "node_modules/@sap-ux/cds-odata-annotation-converter/node_modules/i18next": { + "version": "20.6.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz", + "integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/@sap-ux/fe-fpm-writer": { + "version": "0.31.4", + "resolved": "https://registry.npmjs.org/@sap-ux/fe-fpm-writer/-/fe-fpm-writer-0.31.4.tgz", + "integrity": "sha512-qPA5bupuZpK7rK2ZVuQdZQ3Cif49Qfi0e9ksKMuio75M1TBtnlHZyYvdBlNnOG33yAwGT69Ewamg1xf+rZbSVQ==", + "dev": true, + "dependencies": { + "@sap-ux/annotation-converter": "0.8.0", + "@sap-ux/fiori-annotation-api": "0.2.4", + "@sap-ux/project-access": "1.27.4", + "@sap-ux/vocabularies-types": "0.10.0", + "@xmldom/xmldom": "0.8.10", + "ejs": "3.1.10", + "i18next": "21.6.11", + "i18next-fs-backend": "1.1.1", + "mem-fs": "2.1.0", + "mem-fs-editor": "9.4.0", + "semver": "7.5.4", + "xml-formatter": "2.6.1", + "xpath": "0.0.33" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/fe-fpm-writer/node_modules/@sap-ux/vocabularies-types": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@sap-ux/vocabularies-types/-/vocabularies-types-0.10.0.tgz", + "integrity": "sha512-uTQ2tJJes9qGGAn8e/Npvy0grIul3mAVHzjxsj10U3lF+RZsoFawsdYGxvb6sJYkhY+YEsQzL8GTL6tyYpMv8g==", + "dev": true, + "engines": { + "node": ">=18.0.0 < 19.0.0 || >=20.0.0 < 21.0.0", + "pnpm": ">=8" + } + }, + "node_modules/@sap-ux/fiori-annotation-api": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@sap-ux/fiori-annotation-api/-/fiori-annotation-api-0.2.4.tgz", + "integrity": "sha512-RWo4sJOOWi9rHdyvbD/u5wHRo+7awnKiqJnFQ8ofhgQEvvkhH9bsPE1IxDUY6hHFoB5QTHA0GZyH06wzRDXQ1w==", + "dev": true, + "dependencies": { + "@sap-ux/annotation-converter": "0.8.0", + "@sap-ux/cds-annotation-parser": "0.2.2", + "@sap-ux/cds-odata-annotation-converter": "0.3.6", + "@sap-ux/logger": "0.6.0", + "@sap-ux/odata-annotation-core": "0.2.1", + "@sap-ux/odata-annotation-core-types": "0.4.1", + "@sap-ux/odata-entity-model": "0.3.0", + "@sap-ux/odata-vocabularies": "0.4.2", + "@sap-ux/project-access": "1.27.4", + "@sap-ux/vocabularies-types": "0.10.0", + "@sap-ux/xml-odata-annotation-converter": "0.3.1", + "@sap/ux-cds-compiler-facade": "1.15.0", + "@xml-tools/ast": "5.0.5", + "@xml-tools/parser": "1.0.11", + "mem-fs": "2.1.0", + "mem-fs-editor": "9.4.0", + "vscode-languageserver-textdocument": "1.0.8" + } + }, + "node_modules/@sap-ux/fiori-annotation-api/node_modules/@sap-ux/vocabularies-types": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@sap-ux/vocabularies-types/-/vocabularies-types-0.10.0.tgz", + "integrity": "sha512-uTQ2tJJes9qGGAn8e/Npvy0grIul3mAVHzjxsj10U3lF+RZsoFawsdYGxvb6sJYkhY+YEsQzL8GTL6tyYpMv8g==", + "dev": true, + "engines": { + "node": ">=18.0.0 < 19.0.0 || >=20.0.0 < 21.0.0", + "pnpm": ">=8" + } + }, + "node_modules/@sap-ux/i18n": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sap-ux/i18n/-/i18n-0.2.0.tgz", + "integrity": "sha512-PVaf1xWsRzvbkjduwI1EPx4df/+bfzwfDsbe/aN3aJpbLV47Cao6X03JM8H12Jg7xT5N11Zn2nYNfSbSGGdsMg==", + "dev": true, + "dependencies": { + "@sap-ux/text-document-utils": "0.2.0", + "jsonc-parser": "3.2.0", + "vscode-languageserver-textdocument": "1.0.7" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/i18n/node_modules/vscode-languageserver-textdocument": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", + "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==", + "dev": true + }, + "node_modules/@sap-ux/logger": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@sap-ux/logger/-/logger-0.6.0.tgz", + "integrity": "sha512-/p9Lv3GXdASJbs5CLJy69t2OLRK6VG2zOL/jLRyOPEK1tcZYFF/LerBYDFfFxkvCpHz+cYvUJuhAmxqljLxmJw==", + "dev": true, + "dependencies": { + "chalk": "4.1.2", + "lodash": "4.17.21", + "winston": "3.11.0", + "winston-transport": "4.7.0" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/odata-annotation-core": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@sap-ux/odata-annotation-core/-/odata-annotation-core-0.2.1.tgz", + "integrity": "sha512-HHue359RcSXfn32S578oPkcuvHMGbU0GekFGV2YWuJ3RANcGeAMqatE9gIHzei7I5mNDTPgeEmYEFFhv4+upSA==", + "dev": true, + "dependencies": { + "@sap-ux/odata-annotation-core-types": "0.4.1", + "@sap-ux/text-document-utils": "0.2.0" + } + }, + "node_modules/@sap-ux/odata-annotation-core-types": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sap-ux/odata-annotation-core-types/-/odata-annotation-core-types-0.4.1.tgz", + "integrity": "sha512-pWu7h3BgWftFvOkafdYVW/8fUJ7EfBDn4jWZREt1DqGbMjmhNtTlbipQ6krRZZ9gs/Y7DHMrx3lyDfsCZ7WC1Q==", + "dev": true, + "dependencies": { + "@sap-ux/text-document-utils": "0.2.0" + } + }, + "node_modules/@sap-ux/odata-entity-model": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@sap-ux/odata-entity-model/-/odata-entity-model-0.3.0.tgz", + "integrity": "sha512-vWOHb9F6758nV4lIz7dO+p0EeE/lpOM+9g4S7AK383YgRYaAE3kn1Sdh76aF+C7D97E1r2Iia7nobExDbEpYfA==", + "dev": true + }, + "node_modules/@sap-ux/odata-vocabularies": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@sap-ux/odata-vocabularies/-/odata-vocabularies-0.4.2.tgz", + "integrity": "sha512-7u8t9rwuufbBcLVT/tXmNxwN54GkHcuztKbEKUb5dPqBaIqsTb9g7nmHANDVOaqoLjMUk1c8dX7k7U/QE0GlhA==", + "dev": true, + "dependencies": { + "@sap-ux/odata-annotation-core-types": "0.4.1" + } + }, + "node_modules/@sap-ux/project-access": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@sap-ux/project-access/-/project-access-1.27.4.tgz", + "integrity": "sha512-OURZXN3RmeqVmM4+FzR/W1m94wmA9+lT/f0FCtUlISbvbvq7R3JIyFKO1bqL8pj7dZcIIQql0IMW6plyA4y29A==", + "dev": true, + "dependencies": { + "@sap-ux/i18n": "0.2.0", + "@sap-ux/ui5-config": "0.25.0", + "fast-xml-parser": "4.4.1", + "findit2": "2.2.3", + "json-parse-even-better-errors": "3.0.2", + "mem-fs": "2.1.0", + "mem-fs-editor": "9.4.0", + "semver": "7.5.4" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/text-document-utils": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sap-ux/text-document-utils/-/text-document-utils-0.2.0.tgz", + "integrity": "sha512-igbsYKY05afkWfehWgapMmCub4L9a8YJxesYcRk00JHffCanfl7N9Pf9xmxflzQUKUKLPkfE/Vjicq1AImOqSg==", + "dev": true, + "dependencies": { + "vscode-languageserver-types": "3.17.2" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/ui5-config": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@sap-ux/ui5-config/-/ui5-config-0.25.0.tgz", + "integrity": "sha512-Yisd6b6anNvDNqGzYDc6uboFbq8mkq/tmG+GffVqsnuVO1sWttReppJGKsYqozPXM6jYN6ZLMdHyuzN7zdNvUA==", + "dev": true, + "dependencies": { + "@sap-ux/yaml": "0.16.0", + "lodash": "4.17.21", + "semver": "7.5.4" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/vocabularies-types": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@sap-ux/vocabularies-types/-/vocabularies-types-0.11.3.tgz", + "integrity": "sha512-bOQz8dk7I/VNtH50hUnfqtiYSMUh/t28nyrMFZcVBbVTwlnxHUTWEUKUGu1Dwllw1yQVHuArCKy+rWhb0v7h6A==", + "dev": true, + "engines": { + "node": ">=18.0.0 < 19.0.0 || >=20.0.0 < 21.0.0", + "pnpm": ">=8" + } + }, + "node_modules/@sap-ux/xml-odata-annotation-converter": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@sap-ux/xml-odata-annotation-converter/-/xml-odata-annotation-converter-0.3.1.tgz", + "integrity": "sha512-qcdJ72RXTkiwV9yP6sOgOiLeoKYY4E1qRhjiG13XeOwg0GOTo5ohUoARqFpENYTE+i52gocVpLNl77Jw011rAA==", + "dev": true, + "dependencies": { + "@sap-ux/odata-annotation-core": "0.2.1" + } + }, + "node_modules/@sap-ux/yaml": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sap-ux/yaml/-/yaml-0.16.0.tgz", + "integrity": "sha512-jojo+NDZyuguUmTznj+yof2luSRarfqR2x09jRoVhS0CT2oJaAxyd60yXRtuCUFW7FbuEii+jSf+aeBU7QSPVw==", + "dev": true, + "dependencies": { + "lodash": "4.17.21", + "yaml": "2.2.2" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@sap-ux/yaml/node_modules/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@sap/cds": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@sap/cds/-/cds-8.3.1.tgz", + "integrity": "sha512-v44OYZqeFYrH6ghfO/RqQ7eqgcNYAAQmVVFy9pIbOkxXay0uVDXB1nC3hu6cuqIYvp2PFInL0c3l1e+ycFSkPA==", + "dependencies": { + "@sap/cds-compiler": ">=5.1", + "@sap/cds-fiori": "^1", + "@sap/cds-foss": "^5.0.0" + }, + "bin": { + "cds-deploy": "lib/dbs/cds-deploy.js", + "cds-serve": "bin/serve.js", + "cds-test": "bin/test.js", + "chest": "bin/test.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "express": ">=4" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, + "node_modules/@sap/cds-compiler": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@sap/cds-compiler/-/cds-compiler-5.3.2.tgz", + "integrity": "sha512-aePHQMZHb13+oQuHU+ug5bwxXO11NThBEfUyA3uMGlnjkAxa8pbzuot9pHiiAIMavXLcqTzbCfm/uFGKcfjqBQ==", + "dependencies": { + "antlr4": "4.9.3" + }, + "bin": { + "cdsc": "bin/cdsc.js", + "cdshi": "bin/cdshi.js", + "cdsse": "bin/cdsse.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sap/cds-fiori": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@sap/cds-fiori/-/cds-fiori-1.2.7.tgz", + "integrity": "sha512-F6Uf9wvkv0fXW+Fh7PiV2BbB/k+p1cFJLkQCCKDRJH8HvlxWEcXcn/YIvBrQGuX+GToi125MxB3wd712d8OLTA==", + "peerDependencies": { + "@sap/cds": ">=7.6", + "express": ">=4" + } + }, + "node_modules/@sap/cds-foss": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sap/cds-foss/-/cds-foss-5.0.1.tgz", + "integrity": "sha512-q6h7LkEx6w9LswCIQzJJ2mnoyeGS8jrmBXN4I4+aECRL60mkLskoqGetot+2tX2xXGxCYJuo5v1dtSafwBqTRQ==", + "dependencies": { + "big.js": "^6.1.1", + "generic-pool": "^3.8.2", + "xmlbuilder": "^15.1.1", + "yaml": "^2.2.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sap/ux-cds-compiler-facade": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@sap/ux-cds-compiler-facade/-/ux-cds-compiler-facade-1.15.0.tgz", + "integrity": "sha512-WeA/wnF0NoHNod6H/Rcpgbh3ue4TVBSAlHQoupWzdu91yl+RZ4rKS4hARaRH+LV7gTXxJ5k6llJ/wyUN7sMDdw==", + "dev": true, + "engines": { + "node": ">=18.16.0" + }, + "peerDependencies": { + "@sap-ux/odata-annotation-core": ">=0.2.1", + "@sap-ux/odata-annotation-core-types": ">=0.4.1", + "@sap-ux/project-access": ">=1.26.6" + } + }, + "node_modules/@sap/ux-specification": { + "version": "1.124.3", + "resolved": "https://registry.npmjs.org/@sap/ux-specification/-/ux-specification-1.124.3.tgz", + "integrity": "sha512-9a0fDI2Qma2hGZn4eTacHWTWILDzBNt0a8hNhT5YMkzFbiSezgSKbd0Sda5JQ2fPKgopTEUbThy8KecUt9VV0g==", + "dev": true, + "dependencies": { + "@sap-ux/fe-fpm-writer": "0.31.4", + "@sap-ux/vocabularies-types": "0.11.3" + }, + "engines": { + "node": ">= 18.0.0 < 19.0.0 || >= 20.0.0 < 21.0.0 || >= 22.0.0", + "yarn": ">=1.22.19 < 2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz", + "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@xml-tools/ast": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@xml-tools/ast/-/ast-5.0.5.tgz", + "integrity": "sha512-avvzTOvGplCx9JSKdsTe3vK+ACvsHy2HxVfkcfIqPzu+kF5CT4rw5aUVzs0tJF4cnDyMRVkSyVxR07X0Px8gPA==", + "dev": true, + "dependencies": { + "@xml-tools/common": "^0.1.6", + "@xml-tools/parser": "^1.0.11", + "lodash": "4.17.21" + } + }, + "node_modules/@xml-tools/common": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@xml-tools/common/-/common-0.1.6.tgz", + "integrity": "sha512-7aVZeEYccs1KI/Asd6KKnrB4dTAWXTkjRMjG40ApGEUp5NpfQIvWLEBvMv85Koj2lbSpagcAERwDy9qMsfWGdA==", + "dev": true, + "dependencies": { + "lodash": "4.17.21" + } + }, + "node_modules/@xml-tools/parser": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.11.tgz", + "integrity": "sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==", + "dev": true, + "dependencies": { + "chevrotain": "7.1.1" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.9.3.tgz", + "integrity": "sha512-qNy2odgsa0skmNMCuxzXhM4M8J1YDaPv3TI+vCdnOAanu0N982wBrSqziDKRDctEZLZy9VffqIZXc0UGjjSP/g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-sqlite3": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.4.0.tgz", + "integrity": "sha512-B7C9y2aSvtTwDJIz34iUxMjQWmbAYFmpq0Rwf9weYTtx6jUYsUKVt5ePPYlGyLVBoySppPa41PBrzl1ipMhG7A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/binaryextensions": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-4.19.0.tgz", + "integrity": "sha512-DRxnVbOi/1OgA5pA9EDiRT8gvVYeqfuN7TmPfLyt6cyho3KbHCi3EtDQf39TTmGDrR5dZ9CspdXhPkL/j/WGbg==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chevrotain": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz", + "integrity": "sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/findit2": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", + "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==", + "dev": true, + "engines": { + "node": ">=0.8.22" + } + }, + "node_modules/first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/first-chunk-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/first-chunk-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/first-chunk-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/i18next": { + "version": "21.6.11", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.11.tgz", + "integrity": "sha512-tJ2+o0lVO+fhi8bPkCpBAeY1SgkqmQm5NzgPWCQssBrywJw98/o+Kombhty5nxQOpHtvMmsxcOopczUiH6bJxQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-fs-backend": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz", + "integrity": "sha512-RFkfy10hNxJqc7MVAp5iAZq0Tum6msBCNebEe3OelOBvrROvzHUPaR8Qe10RQrOGokTm0W4vJGEJzruFkEt+hQ==", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem-fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-2.1.0.tgz", + "integrity": "sha512-55vFOT4rfJx/9uoWntNrfzEj9209rd26spsSvKsCVBfOPH001YS5IakfElhcyagieC4uL++Ry/XDcwvgxF4/zQ==", + "dev": true, + "dependencies": { + "vinyl": "^2.0.1", + "vinyl-file": "^3.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mem-fs-editor": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-9.4.0.tgz", + "integrity": "sha512-HSSOLSVRrsDdui9I6i96dDtG+oAez/4EB2g4cjSrNhgNQ3M+L57/+22NuPdORSoxvOHjIg/xeOE+C0wwF91D2g==", + "dev": true, + "dependencies": { + "binaryextensions": "^4.16.0", + "commondir": "^1.0.1", + "deep-extend": "^0.6.0", + "ejs": "^3.1.6", + "globby": "^11.0.3", + "isbinaryfile": "^4.0.8", + "minimatch": "^3.0.4", + "multimatch": "^5.0.0", + "normalize-path": "^3.0.0", + "textextensions": "^5.13.0" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "mem-fs": "^2.1.0" + }, + "peerDependenciesMeta": { + "mem-fs": { + "optional": true + } + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/multimatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "dev": true, + "dependencies": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/textextensions": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-5.16.0.tgz", + "integrity": "sha512-7D/r3s6uPZyU//MCYrX6I14nzauDwJ5CxazouuRGNuvSCihW87ufN6VLoROLCrHg6FblLuJrT6N2BVaPVzqElw==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz", + "integrity": "sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "strip-bom-buf": "^1.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz", + "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==", + "dev": true + }, + "node_modules/winston": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", + "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dev": true, + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml-formatter": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-2.6.1.tgz", + "integrity": "sha512-dOiGwoqm8y22QdTNI7A+N03tyVfBlQ0/oehAzxIZtwnFAHGeSlrfjF73YQvzSsa/Kt6+YZasKsrdu6OIpuBggw==", + "dev": true, + "dependencies": { + "xml-parser-xo": "^3.2.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/xml-parser-xo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-3.2.0.tgz", + "integrity": "sha512-8LRU6cq+d7mVsoDaMhnkkt3CTtAs4153p49fRo+HIB3I1FD1o5CeXRjRH29sQevIfVJIcPjKSsPU/+Ujhq09Rg==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xpath": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", + "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/package.json b/packages/project-integrity/test/test-input/valid-fiori-project/package.json new file mode 100644 index 0000000000..7ec1674955 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/package.json @@ -0,0 +1,24 @@ +{ + "name": "fiori-tools-ai", + "version": "1.0.0", + "description": "A simple CAP project.", + "repository": "", + "license": "UNLICENSED", + "private": true, + "dependencies": { + "@sap/cds": "^8", + "express": "^4" + }, + "devDependencies": { + "@cap-js/sqlite": "^1", + "@cap-js/cds-types": "^0.6", + "@sap/ux-specification": "UI5-1.126" + }, + "scripts": { + "start": "cds-serve", + "watch-travelmanagement": "cds watch --open travelmanagement/webapp/index.html?sap-ui-xx-viewCache=false" + }, + "sapux": [ + "app/travelmanagement" + ] +} diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/srv/service.cds b/packages/project-integrity/test/test-input/valid-fiori-project/srv/service.cds new file mode 100644 index 0000000000..fe5789f6c2 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/srv/service.cds @@ -0,0 +1,7 @@ +using { travelManagement } from '../db/schema.cds'; + +service travelManagementSrv { + @odata.draft.enabled + entity Travel as projection on travelManagement.Travel; + entity Bookings as projection on travelManagement.Bookings; +} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Bookings.csv b/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Bookings.csv new file mode 100644 index 0000000000..3c8ccec3ce --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Bookings.csv @@ -0,0 +1,8 @@ +ID;employee;airlines;bookingDate;flightDate;priceUSD;travel_ID +3efbe1e6-de90-4ae7-9a94-71c669faeea7;John Doe;Delta Airlines;2021-06-20;2021-07-01;500.00;bda3b521-6a7c-4c6a-b67c-47b6d4333abe +e984fce5-8f2f-4a45-bec7-856391aaf733;John Doe;American Airlines;2021-06-22;2021-07-05;450.00;bda3b521-6a7c-4c6a-b67c-47b6d4333abe +246cdfc3-480f-4a59-948c-67532b3a528d;Jane Smith;United Airlines;2021-08-01;2021-08-15;600.00;48ffb874-9713-4402-a994-8075295d2e6e +a1404f08-5dfb-4f91-bc55-85d27cb9463e;Alice Johnson;Southwest Airlines;2021-09-01;2021-09-10;300.00;95481ec0-8a0b-4ec6-91b0-9fbea59285ab +2fb675de-8cbd-4b39-aa9a-0e66cd9474eb;Bob Brown;JetBlue;2021-09-25;2021-10-05;700.00;eee164c0-ed83-4ec2-8f95-844ab4099fc4 +0f501a38-7484-432d-b7d3-fa5a69a8f813;Charlie Davis;Alaska Airlines;2021-10-20;2021-11-01;400.00;f03c3b75-8592-4f85-a557-28257c4a976d +c9cd31a1-f1fd-42be-97f4-6a8f529a87bf;Charlie Davis;Spirit Airlines;2021-10-22;2021-11-03;350.00;f03c3b75-8592-4f85-a557-28257c4a976d \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Travel.csv b/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Travel.csv new file mode 100644 index 0000000000..0e9cae92dd --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-fiori-project/test/data/travelManagement-Travel.csv @@ -0,0 +1,6 @@ +ID;tripName;employee;status;tripStartDate;tripEndDate;priceUSD +bda3b521-6a7c-4c6a-b67c-47b6d4333abe;Sales Conference;John Doe;Confirmed;2021-07-01;2021-07-05;1500.00 +48ffb874-9713-4402-a994-8075295d2e6e;Tech Expo;Jane Smith;Pending;2021-08-15;2021-08-20;2000.00 +95481ec0-8a0b-4ec6-91b0-9fbea59285ab;Marketing Summit;Alice Johnson;Cancelled;2021-09-10;2021-09-12;800.00 +eee164c0-ed83-4ec2-8f95-844ab4099fc4;Leadership Retreat;Bob Brown;Confirmed;2021-10-05;2021-10-10;2500.00 +f03c3b75-8592-4f85-a557-28257c4a976d;Client Meeting;Charlie Davis;Completed;2021-11-01;2021-11-03;1200.00 \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-project/.integrity.json b/packages/project-integrity/test/test-input/valid-project/.integrity.json new file mode 100644 index 0000000000..6e33d04d32 --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-project/.integrity.json @@ -0,0 +1 @@ +{"enabled":true,"fileIntegrity":[{"filePath":"test.txt","hash":"1a8a45c145cca8cb08b03e0eb1f6fcd4","content":"FIVwzgLgBAhlEFNJQGYEsA2CB0Q="}],"contentIntegrity":[{"contentKey":"key1","hash":"9946687e5fa0dab5993ededddb398d2e","content":"G4QwNgrgpgjEA==="},{"contentKey":"key2","hash":"f066ce9385512ee02afc6e14d627e9f2","content":"G4QwNgrgpgTEA==="}]} \ No newline at end of file diff --git a/packages/project-integrity/test/test-input/valid-project/test.txt b/packages/project-integrity/test/test-input/valid-project/test.txt new file mode 100644 index 0000000000..e3ab9e012e --- /dev/null +++ b/packages/project-integrity/test/test-input/valid-project/test.txt @@ -0,0 +1 @@ +Just a test file. \ No newline at end of file diff --git a/packages/project-integrity/test/unit/fiori-project/index.test.ts b/packages/project-integrity/test/unit/fiori-project/index.test.ts new file mode 100644 index 0000000000..e93bd0f570 --- /dev/null +++ b/packages/project-integrity/test/unit/fiori-project/index.test.ts @@ -0,0 +1,123 @@ +import { join } from 'path'; +import { + checkFioriProjectIntegrity, + disableFioriProjectIntegrity, + enableFioriProjectIntegrity, + initFioriProject, + isFioriProjectIntegrityEnabled, + updateFioriProjectIntegrity +} from '../../../src'; +import * as persistence from '../../../src/integrity/persistence'; +import * as updateMock from '../../../src/integrity'; + +describe('Test for initFioriProject()', () => { + test('Init valid Fiori project', async () => { + const projectRoot = join(__dirname, '../../test-input/valid-fiori-project'); + const integrityFilePath = join(projectRoot, '.fiori-ai/ai-integrity.json'); + const targetIntegrityData = await persistence.readIntegrityData(integrityFilePath); + await initFioriProject(projectRoot); + const newIntegrityData = await persistence.readIntegrityData(integrityFilePath); + expect(newIntegrityData).toStrictEqual(targetIntegrityData); + }); + + test('Init invalid Fiori project, no db/schema.cds', async () => { + const projectRoot = join(__dirname, '../../test-input/invalid-fiori-project-no-schema-cds'); + try { + await initFioriProject(projectRoot); + expect(false).toBe('initFioriProject() should have thrown error but did not'); + } catch (error) { + expect(error.message).toContain('schema.cds'); + } + }); + + test('Init valid Fiori project no srv/service.cds', async () => { + const projectRoot = join(__dirname, '../../test-input/invalid-fiori-project-no-service-cds'); + try { + await initFioriProject(projectRoot); + expect(false).toBe('initFioriProject() should have thrown error but did not'); + } catch (error) { + expect(error.message).toContain('service.cds'); + } + }); +}); + +describe('Test for checkFioriProjectIntegrity()', () => { + test('Check valid project', async () => { + const projectRoot = join(__dirname, '../../test-input/valid-fiori-project'); + const results = await checkFioriProjectIntegrity(projectRoot); + expect(results).toStrictEqual({ + 'files': { + 'differentFiles': [], + 'equalFiles': [expect.stringContaining('schema.cds'), expect.stringContaining('service.cds')] + }, + 'additionalStringContent': { 'differentContent': [], 'equalContent': ['capPaths'] } + }); + }); +}); + +describe('Test for updateFioriProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Update additional string content', async () => { + const mockUpdateFioriProjectIntegrity = jest + .spyOn(updateMock, 'updateProjectIntegrity') + .mockResolvedValueOnce(); + const projectRoot = join(__dirname, '../../test-input/valid-fiori-project'); + await updateFioriProjectIntegrity(projectRoot); + expect(mockUpdateFioriProjectIntegrity).toBeCalledWith(expect.stringContaining('integrity.json'), { + capPaths: '{"app":"app/","db":"db/","srv":"srv/"}' + }); + }); +}); + +describe('Test isFioriProjectIntegrityEnabled()', () => { + test('Check enabled Fiori project', async () => { + const projectRoot = join(__dirname, '../../test-input/enabled-fiori-project'); + const enabled = await isFioriProjectIntegrityEnabled(projectRoot); + expect(enabled).toBe(true); + }); + + test('Check disabled Fiori project', async () => { + const projectRoot = join(__dirname, '../../test-input/disabled-fiori-project'); + const enabled = await isFioriProjectIntegrityEnabled(projectRoot); + expect(enabled).toBe(false); + }); +}); + +describe('Test enableFioriProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Enable disabled Fiori project', async () => { + const mockWriteIntegrityData = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const projectRoot = join(__dirname, '../../test-input/disabled-fiori-project'); + + await enableFioriProjectIntegrity(projectRoot); + expect(mockWriteIntegrityData).toBeCalledWith(expect.stringContaining('ai-integrity.json'), { + 'enabled': true, + 'fileIntegrity': [], + 'contentIntegrity': [] + }); + }); +}); + +describe('Test disableFioriProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Disable enabled Fiori project', async () => { + const mockWriteIntegrityData = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const projectRoot = join(__dirname, '../../test-input/enabled-fiori-project'); + + await disableFioriProjectIntegrity(projectRoot); + expect(mockWriteIntegrityData).toBeCalledWith(expect.stringContaining('ai-integrity.json'), { + 'enabled': false, + 'fileIntegrity': [], + 'contentIntegrity': [] + }); + }); +}); diff --git a/packages/project-integrity/test/unit/index.test.ts b/packages/project-integrity/test/unit/index.test.ts new file mode 100644 index 0000000000..2a2f85ff67 --- /dev/null +++ b/packages/project-integrity/test/unit/index.test.ts @@ -0,0 +1,20 @@ +import { + initFioriProject, + initProject, + checkFioriProjectIntegrity, + checkProjectIntegrity, + updateFioriProjectIntegrity, + updateProjectIntegrity +} from '../../src'; + +test('Check public interface for project integrity', () => { + expect(typeof initProject).toBe('function'); + expect(typeof checkProjectIntegrity).toBe('function'); + expect(typeof updateProjectIntegrity).toBe('function'); +}); + +test('Check public interface for Fiori project integrity', () => { + expect(typeof initFioriProject).toBe('function'); + expect(typeof checkFioriProjectIntegrity).toBe('function'); + expect(typeof updateFioriProjectIntegrity).toBe('function'); +}); diff --git a/packages/project-integrity/test/unit/integrity/hash.test.ts b/packages/project-integrity/test/unit/integrity/hash.test.ts new file mode 100644 index 0000000000..81e3abdf62 --- /dev/null +++ b/packages/project-integrity/test/unit/integrity/hash.test.ts @@ -0,0 +1,12 @@ +import { getFileIntegrity } from '../../../src/integrity/hash'; + +test('Test getFileIntegrity()', async () => { + try { + await getFileIntegrity(['non-existing-file', 'other-non-existing-file']); + expect(false).toBe('Call to getFileIntegrity() should have thrown an error, but did not.'); + } catch (error) { + expect(error.message).toBe('The following files do not exist: non-existing-file, other-non-existing-file'); + } +}); + +// Other export of hash.ts are already covered by test in project.test.ts diff --git a/packages/project-integrity/test/unit/integrity/persistence.test.ts b/packages/project-integrity/test/unit/integrity/persistence.test.ts new file mode 100644 index 0000000000..3601a8f1c9 --- /dev/null +++ b/packages/project-integrity/test/unit/integrity/persistence.test.ts @@ -0,0 +1,46 @@ +import { mkdir } from 'fs/promises'; +import { join } from 'path'; +import { readIntegrityData, writeIntegrityData } from '../../../src/integrity/persistence'; +import type { Integrity } from '../../../src/types'; + +jest.mock('fs/promises', () => ({ + ...jest.requireActual('fs/promises'), + mkdir: jest.fn(), + writeFile: jest.fn() +})); + +describe('Test readIntegrityData()', () => { + test('Read integrity data, integrity file does not exist', async () => { + try { + await readIntegrityData('none-existing-file'); + expect(false).toBe('readIntegrityData() should have thrown error but did not.'); + } catch (error) { + expect(error.message).toBe('Integrity file not found at none-existing-file'); + } + }); + + test('Read integrity data, set content should not be possible', async () => { + const integrityFilePath = join(__dirname, '../../test-input/valid-project/.integrity.json'); + const integrityData = await readIntegrityData(integrityFilePath); + expect(integrityData.fileIntegrity[0].content).toBe('Just a test file.'); + integrityData.fileIntegrity[0].content = 'set to readonly property should not do anything'; + expect(integrityData.fileIntegrity[0].content).toBe('Just a test file.'); + }); + + // Other, valid cases, are tested in project.test.ts +}); + +describe('Test writeIntegrityData()', () => { + test('Write integrity data, path to integrity data does not exists', async () => { + const mockedMkdir = mkdir as jest.Mock; + const integrityFilePath = join(__dirname, '../../test-input/new-folder/integrity.json'); + const content: Partial = { + fileIntegrity: [], + contentIntegrity: [] + }; + await writeIntegrityData(integrityFilePath, content as Integrity); + expect(mockedMkdir).toBeCalledWith(expect.stringContaining('new-folder'), { recursive: true }); + }); + + // Other, valid cases, are tested in project.test.ts +}); diff --git a/packages/project-integrity/test/unit/integrity/project.test.ts b/packages/project-integrity/test/unit/integrity/project.test.ts new file mode 100644 index 0000000000..a17566751e --- /dev/null +++ b/packages/project-integrity/test/unit/integrity/project.test.ts @@ -0,0 +1,227 @@ +import { join } from 'path'; +import { + checkProjectIntegrity, + disableProjectIntegrity, + enableProjectIntegrity, + initProject, + isProjectIntegrityEnabled, + updateProjectIntegrity +} from '../../../src'; +import * as persistence from '../../../src/integrity/persistence'; + +describe('Test initProject()', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('Test initProject() with valid project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/valid-project/.integrity.json'); + const targetIntegrityData = await persistence.readIntegrityData(integrityFilePath); + await initProject({ + integrityFilePath, + fileList: [join(__dirname, '../../test-input/valid-project/test.txt')], + additionalStringContent: { 'key1': 'value1', 'key2': 'value2' } + }); + const newIntegrityData = await persistence.readIntegrityData(integrityFilePath); + expect(newIntegrityData).toStrictEqual(targetIntegrityData); + }); +}); + +describe('Test checkProjectIntegrity()', () => { + test('Valid project, valid additional content', async () => { + const integrityFilePath = join(__dirname, '../../test-input/valid-project/.integrity.json'); + const result = await checkProjectIntegrity(integrityFilePath, { 'key1': 'value1', 'key2': 'value2' }); + expect(result.files.differentFiles.length).toBe(0); + expect(result.files.equalFiles.find((ef) => ef.includes('test.txt'))).toBeDefined(); + expect(result.additionalStringContent.differentContent.length).toBe(0); + expect(result.additionalStringContent.equalContent).toEqual(['key1', 'key2']); + }); + + test('Invalid project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/invalid-project/integrity.json'); + const result = await checkProjectIntegrity(integrityFilePath, { + 'one': 'value one', + 'two': 'not value two', + 'four': 'non existing' + }); + const bad = result.files.differentFiles.find((df) => df.filePath.includes('bad.xml')); + const noneExistingFile = result.files.differentFiles.find((df) => df.filePath.includes('non-existing.file')); + expect(result.files.equalFiles.length).toBe(1); + expect(result.files.equalFiles.find((ef) => ef.includes('good.txt'))).toBeDefined(); + expect(bad?.newContent).toBe('\n Changed XML\n'); + expect(bad?.oldContent).toBe('\n Test XML file\n'); + expect(noneExistingFile?.oldContent).toBe('Non existing'); + expect(result.additionalStringContent.equalContent).toContain('one'); + expect(result.additionalStringContent.differentContent).toStrictEqual([ + { 'key': 'two', 'newContent': 'not value two', 'oldContent': 'value two' }, + { 'key': 'four', 'newContent': 'non existing', 'oldContent': undefined }, + { 'key': 'three', 'newContent': undefined, 'oldContent': 'value three' } + ]); + }); + + test('Disabled project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/disabled-project/integrity.json'); + try { + await checkProjectIntegrity(integrityFilePath); + expect(false).toBe('checkProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe( + `Integrity is disabled for the project with integrity data ${integrityFilePath}` + ); + } + }); +}); + +describe('Test updateProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Update invalid project', async () => { + const writeSpy = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + + const integrityFilePath = join(__dirname, '../../test-input/update-project/integrity.json'); + await updateProjectIntegrity(integrityFilePath, { 'key': 'new string' }); + expect(writeSpy).toBeCalledWith(expect.stringContaining('integrity.json'), { + 'enabled': true, + 'fileIntegrity': [ + { + 'filePath': expect.stringContaining('file-to-update.txt') as string, + 'hash': '96c15c2bb2921193bf290df8cd85e2ba', + 'content': 'new content' + } + ], + 'contentIntegrity': [ + { 'contentKey': 'key', 'hash': 'b200a3adbe85fe848b920dc35d5a69b2', 'content': 'new string' } + ] + }); + }); + + test('Update with non existing additional string content', async () => { + const integrityFilePath = join(__dirname, '../../test-input/valid-project/.integrity.json'); + try { + await updateProjectIntegrity(integrityFilePath, { 'key1': 'value1', 'wrong': 'wrong content' }); + expect(false).toBe('updateProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe( + 'There is a mismatch of additional content keys.\nStored content keys: key1, key2\nNew content keys: key1, wrong' + ); + } + }); + + test('Update with non existing integrity file', async () => { + try { + await updateProjectIntegrity('non-existing'); + expect(false).toBe('updateProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe('Integrity data not found at non-existing'); + } + }); + + test('Update disabled project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/disabled-project/integrity.json'); + try { + await updateProjectIntegrity(integrityFilePath); + expect(false).toBe('updateProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe( + `Integrity is disabled for the project with integrity data ${integrityFilePath}` + ); + } + }); +}); + +describe('Test isProjectIntegrityEnabled()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Check enabled project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/enabled-project/integrity.json'); + const enabled = await isProjectIntegrityEnabled(integrityFilePath); + expect(enabled).toBe(true); + }); + + test('Check disabled project', async () => { + const integrityFilePath = join(__dirname, '../../test-input/disabled-project/integrity.json'); + const enabled = await isProjectIntegrityEnabled(integrityFilePath); + expect(enabled).toBe(false); + }); + + test('Check non existing project', async () => { + try { + await isProjectIntegrityEnabled('non-existing'); + expect(false).toBe('isProjectIntegrityEnabled() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe('Integrity data not found at non-existing'); + } + }); +}); + +describe('Test enableProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Enable integrity for disabled project', async () => { + const writeSpy = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const integrityFilePath = join(__dirname, '../../test-input/disabled-project/integrity.json'); + await enableProjectIntegrity(integrityFilePath); + expect(writeSpy).toBeCalledWith(expect.stringContaining('integrity.json'), { + 'enabled': true, + 'fileIntegrity': [], + 'contentIntegrity': [] + }); + }); + + test('Enable integrity for enabled project', async () => { + const writeSpy = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const integrityFilePath = join(__dirname, '../../test-input/enabled-project/integrity.json'); + await enableProjectIntegrity(integrityFilePath); + // project already enabled, so writeIntegrityData should not be called + expect(writeSpy).not.toBeCalled(); + }); + + test('Enable integrity for non existing project', async () => { + try { + await enableProjectIntegrity('non-existing'); + expect(false).toBe('enableProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe('Integrity data not found at non-existing'); + } + }); +}); + +describe('Test disableProjectIntegrity()', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('Disable integrity for enabled project', async () => { + const writeSpy = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const integrityFilePath = join(__dirname, '../../test-input/enabled-project/integrity.json'); + await disableProjectIntegrity(integrityFilePath); + expect(writeSpy).toBeCalledWith(expect.stringContaining('integrity.json'), { + 'enabled': false, + 'fileIntegrity': [], + 'contentIntegrity': [] + }); + }); + + test('Disable integrity for disabled project', async () => { + const writeSpy = jest.spyOn(persistence, 'writeIntegrityData').mockResolvedValueOnce(); + const integrityFilePath = join(__dirname, '../../test-input/disabled-project/integrity.json'); + await disableProjectIntegrity(integrityFilePath); + // project already disabled, so writeIntegrityData should not be called + expect(writeSpy).not.toBeCalled(); + }); + + test('Disable integrity for non existing project', async () => { + try { + await disableProjectIntegrity('non-existing'); + expect(false).toBe('disableProjectIntegrity() should have thrown error but did not'); + } catch (error) { + expect(error.message).toBe('Integrity data not found at non-existing'); + } + }); +}); diff --git a/packages/project-integrity/tsconfig.eslint.json b/packages/project-integrity/tsconfig.eslint.json new file mode 100644 index 0000000000..d5f1aa3474 --- /dev/null +++ b/packages/project-integrity/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "test", ".eslintrc.js"] +} diff --git a/packages/project-integrity/tsconfig.json b/packages/project-integrity/tsconfig.json new file mode 100644 index 0000000000..f64fd8025e --- /dev/null +++ b/packages/project-integrity/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src" + ], + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "references": [ + { + "path": "../project-access" + } + ] +} diff --git a/packages/reload-middleware/CHANGELOG.md b/packages/reload-middleware/CHANGELOG.md index eae3d5f6f2..9c225e40f7 100644 --- a/packages/reload-middleware/CHANGELOG.md +++ b/packages/reload-middleware/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/reload-middleware +## 0.2.12 + +### Patch Changes + +- 261cf6e: Detect annotation changes in CPE. + ## 0.2.11 ### Patch Changes diff --git a/packages/reload-middleware/package.json b/packages/reload-middleware/package.json index 711eaeede1..69e3ec6ca5 100644 --- a/packages/reload-middleware/package.json +++ b/packages/reload-middleware/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Areload-middleware" }, - "version": "0.2.11", + "version": "0.2.12", "author": "@SAP/ux-tools-team", "license": "Apache-2.0", "main": "dist/index.js", diff --git a/packages/reload-middleware/src/base/livereload.ts b/packages/reload-middleware/src/base/livereload.ts index 78482e9634..0013c64496 100644 --- a/packages/reload-middleware/src/base/livereload.ts +++ b/packages/reload-middleware/src/base/livereload.ts @@ -75,7 +75,8 @@ export function watchManifestChanges(livereload: LiveReloadServer): void { } else if (fileExtension === '.change') { if ( path.endsWith('appdescr_fe_changePageConfiguration.change') || - path.endsWith('appdescr_ui_generic_app_changePageConfiguration.change') + path.endsWith('appdescr_ui_generic_app_changePageConfiguration.change') || + path.endsWith('appdescr_app_addAnnotationsToOData.change') ) { global.__SAP_UX_MANIFEST_SYNC_REQUIRED__ = true; } diff --git a/packages/system-access/CHANGELOG.md b/packages/system-access/CHANGELOG.md index 902aafa73a..53707e7aed 100644 --- a/packages/system-access/CHANGELOG.md +++ b/packages/system-access/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/system-access +## 0.5.25 + +### Patch Changes + +- Updated dependencies [2e3c15e] + - @sap-ux/axios-extension@1.18.0 + ## 0.5.24 ### Patch Changes diff --git a/packages/system-access/package.json b/packages/system-access/package.json index 4c53af6d4d..ff761e4325 100644 --- a/packages/system-access/package.json +++ b/packages/system-access/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Asystem-access" }, - "version": "0.5.24", + "version": "0.5.25", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", diff --git a/packages/ui5-application-inquirer/CHANGELOG.md b/packages/ui5-application-inquirer/CHANGELOG.md index 1a33566f1b..3db44630ee 100644 --- a/packages/ui5-application-inquirer/CHANGELOG.md +++ b/packages/ui5-application-inquirer/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/ui5-application-inquirer +## 0.9.3 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.9.2 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + ## 0.9.1 ### Patch Changes diff --git a/packages/ui5-application-inquirer/package.json b/packages/ui5-application-inquirer/package.json index 21c53c1812..5d5029a397 100644 --- a/packages/ui5-application-inquirer/package.json +++ b/packages/ui5-application-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/ui5-application-inquirer", "description": "Prompts module that can prompt users for inputs required for UI5 application writing", - "version": "0.9.1", + "version": "0.9.3", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/ui5-library-inquirer/CHANGELOG.md b/packages/ui5-library-inquirer/CHANGELOG.md index 2148c0a737..bc18901c15 100644 --- a/packages/ui5-library-inquirer/CHANGELOG.md +++ b/packages/ui5-library-inquirer/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/ui5-library-inquirer +## 0.3.32 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.3.31 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + ## 0.3.30 ### Patch Changes diff --git a/packages/ui5-library-inquirer/package.json b/packages/ui5-library-inquirer/package.json index 0b333bf55e..fb1fdd87aa 100644 --- a/packages/ui5-library-inquirer/package.json +++ b/packages/ui5-library-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/ui5-library-inquirer", "description": "Prompts module that can provide prompts for UI5 library writer", - "version": "0.3.30", + "version": "0.3.32", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/ui5-library-reference-inquirer/CHANGELOG.md b/packages/ui5-library-reference-inquirer/CHANGELOG.md index 62ccc6182b..d035fdbaf1 100644 --- a/packages/ui5-library-reference-inquirer/CHANGELOG.md +++ b/packages/ui5-library-reference-inquirer/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/ui5-library-reference-inquirer +## 0.3.64 + +### Patch Changes + +- Updated dependencies [40ba546] + - @sap-ux/inquirer-common@0.6.3 + +## 0.3.63 + +### Patch Changes + +- Updated dependencies [dac696a] + - @sap-ux/inquirer-common@0.6.2 + ## 0.3.62 ### Patch Changes diff --git a/packages/ui5-library-reference-inquirer/package.json b/packages/ui5-library-reference-inquirer/package.json index 29ce81d996..22e01cc14a 100644 --- a/packages/ui5-library-reference-inquirer/package.json +++ b/packages/ui5-library-reference-inquirer/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/ui5-library-reference-inquirer", "description": "Prompts module that can provide prompts for UI5 library writer", - "version": "0.3.62", + "version": "0.3.64", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/ui5-library-reference-sub-generator/CHANGELOG.md b/packages/ui5-library-reference-sub-generator/CHANGELOG.md index 29fb51d600..7f8a7c246b 100644 --- a/packages/ui5-library-reference-sub-generator/CHANGELOG.md +++ b/packages/ui5-library-reference-sub-generator/CHANGELOG.md @@ -1,5 +1,17 @@ # @sap-ux/ui5-library-reference-sub-generator +## 0.0.24 + +### Patch Changes + +- @sap-ux/ui5-library-reference-inquirer@0.3.64 + +## 0.0.23 + +### Patch Changes + +- @sap-ux/ui5-library-reference-inquirer@0.3.63 + ## 0.0.22 ### Patch Changes diff --git a/packages/ui5-library-reference-sub-generator/package.json b/packages/ui5-library-reference-sub-generator/package.json index 26d426c1f3..e01e0f9040 100644 --- a/packages/ui5-library-reference-sub-generator/package.json +++ b/packages/ui5-library-reference-sub-generator/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/ui5-library-reference-sub-generator", "description": "Generator for adding reference libraries to a project", - "version": "0.0.22", + "version": "0.0.24", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/ui5-library-sub-generator/CHANGELOG.md b/packages/ui5-library-sub-generator/CHANGELOG.md index c972f71178..e6e2501635 100644 --- a/packages/ui5-library-sub-generator/CHANGELOG.md +++ b/packages/ui5-library-sub-generator/CHANGELOG.md @@ -1,5 +1,24 @@ # @sap-ux/ui5-library-sub-generator +## 0.0.44 + +### Patch Changes + +- @sap-ux/ui5-library-inquirer@0.3.32 + +## 0.0.43 + +### Patch Changes + +- Updated dependencies [9bfbd73] + - @sap-ux/ui5-library-writer@0.5.40 + +## 0.0.42 + +### Patch Changes + +- @sap-ux/ui5-library-inquirer@0.3.31 + ## 0.0.41 ### Patch Changes diff --git a/packages/ui5-library-sub-generator/package.json b/packages/ui5-library-sub-generator/package.json index 13e248b390..808adbd72e 100644 --- a/packages/ui5-library-sub-generator/package.json +++ b/packages/ui5-library-sub-generator/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/ui5-library-sub-generator", "description": "Generator for creating UI5 libraries", - "version": "0.0.41", + "version": "0.0.44", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/ui5-library-writer/CHANGELOG.md b/packages/ui5-library-writer/CHANGELOG.md index 8fad352fa6..0e94e5ef29 100644 --- a/packages/ui5-library-writer/CHANGELOG.md +++ b/packages/ui5-library-writer/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/ui5-library-writer +## 0.5.40 + +### Patch Changes + +- 9bfbd73: add versioning to ui5 lib templates, update deprecated code + ## 0.5.39 ### Patch Changes diff --git a/packages/ui5-library-writer/package.json b/packages/ui5-library-writer/package.json index 05266d3976..0ef4418a64 100644 --- a/packages/ui5-library-writer/package.json +++ b/packages/ui5-library-writer/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Aui5-library-writer" }, - "version": "0.5.39", + "version": "0.5.40", "license": "Apache-2.0", "main": "dist/index.js", "scripts": { @@ -36,7 +36,6 @@ }, "dependencies": { "@sap-ux/project-access": "workspace:*", - "@sap-ux/ui5-application-writer": "workspace:*", "@sap-ux/ui5-config": "workspace:*", "ejs": "3.1.10", "i18next": "20.6.1", diff --git a/packages/ui5-library-writer/src/index.ts b/packages/ui5-library-writer/src/index.ts index d833cdaa41..427fd5c247 100644 --- a/packages/ui5-library-writer/src/index.ts +++ b/packages/ui5-library-writer/src/index.ts @@ -6,6 +6,7 @@ import cloneDeep from 'lodash/cloneDeep'; import type { UI5LibConfig } from './types'; import { enableTypescript } from './options'; import { mergeWithDefaults } from './data'; +import { getTemplateVersionPath } from './utils'; /** * Writes the template to the memfs editor instance. @@ -28,7 +29,9 @@ async function generate(basePath: string, ui5LibConfig: UI5LibConfig, fs?: Edito const tmplPath = join(__dirname, '..', 'templates'); const ignore = [reuseLib.typescript ? '**/*.js' : '**/*.ts']; - fs.copyTpl(join(tmplPath, 'common', '**/*.*'), basePath, libInput, undefined, { + const templateVersionPath = getTemplateVersionPath(reuseLib.frameworkVersion); + + fs.copyTpl(join(tmplPath, 'common', templateVersionPath, '**/*.*'), basePath, libInput, undefined, { globOptions: { dot: true, ignore }, processDestinationPath: (filePath: string) => filePath diff --git a/packages/ui5-library-writer/src/utils.ts b/packages/ui5-library-writer/src/utils.ts new file mode 100644 index 0000000000..1318f867ec --- /dev/null +++ b/packages/ui5-library-writer/src/utils.ts @@ -0,0 +1,37 @@ +import { gte } from 'semver'; + +export const ui5LtsVersion_1_71 = '1.71.0'; +export const ui5LtsVersion_1_120 = '1.120.0'; + +/** + * Compares two UI5 versions to determine if the first is greater than or equal to the second. + * + * @param {string} ui5VersionA - The first UI5 version to compare. + * @param {string} ui5VersionB - The second UI5 version to compare. + * @returns {boolean} - True if the first version is greater than or equal to the second, false otherwise. + */ +export function compareUI5VersionGte(ui5VersionA: string, ui5VersionB: string): boolean { + if (ui5VersionA === '') { + // latest version + return true; + } else { + return gte(ui5VersionA, ui5VersionB, { loose: true }); + } +} + +/** + * Gets the template version path based on the UI5 version. + * + * @param version - The framework version. + * @returns {string} - The template version path. + */ +export function getTemplateVersionPath(version: string): string { + let templateVersionPath = ''; + if (compareUI5VersionGte(version, ui5LtsVersion_1_120)) { + templateVersionPath = ui5LtsVersion_1_120; + } else { + templateVersionPath = ui5LtsVersion_1_71; + } + + return templateVersionPath; +} diff --git a/packages/ui5-library-writer/templates/common/gitignore.tmpl b/packages/ui5-library-writer/templates/common/1.120.0/gitignore.tmpl similarity index 100% rename from packages/ui5-library-writer/templates/common/gitignore.tmpl rename to packages/ui5-library-writer/templates/common/1.120.0/gitignore.tmpl diff --git a/packages/ui5-library-writer/templates/common/karma.conf.tmpl b/packages/ui5-library-writer/templates/common/1.120.0/karma.conf.tmpl similarity index 100% rename from packages/ui5-library-writer/templates/common/karma.conf.tmpl rename to packages/ui5-library-writer/templates/common/1.120.0/karma.conf.tmpl diff --git a/packages/ui5-library-writer/templates/common/package.json b/packages/ui5-library-writer/templates/common/1.120.0/package.json similarity index 100% rename from packages/ui5-library-writer/templates/common/package.json rename to packages/ui5-library-writer/templates/common/1.120.0/package.json diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/.library b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/.library similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/.library rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/.library diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/Example.gen.d.ts b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.gen.d.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/Example.gen.d.ts rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.gen.d.ts diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/Example.js b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.js similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/Example.js rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.js diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/Example.ts b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/Example.ts rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/Example.ts diff --git a/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.js b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.js new file mode 100644 index 0000000000..721405d44d --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.js @@ -0,0 +1,47 @@ +/*! + * ${copyright} + */ + +sap.ui.define([ + "sap/ui/core/Lib", + "./library" +], function (Lib, library) { + "use strict"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Example renderer. + * @namespace + */ + var ExampleRenderer = { + apiVersion: 2 // usage of DOM Patcher + }; + + /** + * Renders the HTML for the given control, using the provided + * {@link sap.ui.core.RenderManager}. + * + * @param {sap.ui.core.RenderManager} rm The reference to the sap.ui.core.RenderManager + * @param {sap.ui.core.Control} control The control instance to be rendered + */ + ExampleRenderer.render = function (rm, control) { + + var i18n = Lib.getResourceBundleFor("<%= libraryNamespace %>"); + + rm.openStart("div", control); + if (control.getColor() === ExampleColor.Highlight) { + rm.class("myLibPrefixExampleHighlight"); + } else { + rm.class("myLibPrefixExample"); + } + rm.openEnd( ); + rm.text(i18n.getText("ANY_TEXT") + ": " + control.getText()); + rm.close("div"); + + }; + + return ExampleRenderer; + +}); diff --git a/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.ts b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.ts new file mode 100644 index 0000000000..0261962de5 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/ExampleRenderer.ts @@ -0,0 +1,41 @@ +/*! + * ${copyright} + */ + +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import Lib from "sap/ui/core/Lib"; +import RenderManager from "sap/ui/core/RenderManager"; +import Example from "./Example"; +import { ExampleColor } from "./library"; + +/** + * Example renderer. + * @namespace + */ +const ExampleRenderer = { + apiVersion: 2, // usage of DOM Patcher + + /** + * Renders the HTML for the given control, using the provided {@link RenderManager}. + * + * @param {RenderManager} rm The reference to the sap.ui.core.RenderManager + * @param {Example} control The control instance to be rendered + */ + render: function (rm: RenderManager, control: Example) { + + const i18n = Lib.getResourceBundleFor("<%= libraryNamespace %>") as ResourceBundle; + + rm.openStart("div", control); + if (control.getColor() === ExampleColor.Highlight) { + rm.class("myLibPrefixExampleHighlight"); + } else { + rm.class("myLibPrefixExample"); + } + rm.openEnd( ); + rm.text(i18n.getText("ANY_TEXT") + ": " + control.getText()); + rm.close("div"); + } + +}; + +export default ExampleRenderer; \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.js b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.js new file mode 100644 index 0000000000..23120837c0 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.js @@ -0,0 +1,68 @@ +/*! + * ${copyright} + */ + +/** + * Initialization Code and shared classes of library <%= libraryNamespace %>. + */ +sap.ui.define([ + "sap/ui/core/Lib" +], function (Lib) { + "use strict"; + + // delegate further initialization of this library to the Core + // Hint: sap.ui.getCore() must still be used to support preload with sync bootstrap! + Lib.init({ + name: "<%= libraryNamespace %>", + version: "${version}", + dependencies: [ // keep in sync with the ui5.yaml and .library files + "sap.ui.core" + ], + types: [ + "<%= libraryNamespace %>.ExampleColor" + ], + interfaces: [], + controls: [ + "<%= libraryNamespace %>.Example" + ], + elements: [], + noLibraryCSS: false, // if no CSS is provided, you can disable the library.css load here + apiVersion: 2 + }); + + /** + * Some description about <%= libraryName %> + * + * @namespace + * @name <%= libraryNamespace %> + * @author <%= author %> + * @version ${version} + * @public + */ + var thisLib = <%= libraryNamespace %>; + + /** + * Semantic Colors of the <%= libraryNamespace %>.Example. + * + * @enum {string} + * @public + */ + thisLib.ExampleColor = { + + /** + * Default color (brand color) + * @public + */ + Default : "Default", + + /** + * Highlight color + * @public + */ + Highlight : "Highlight" + + }; + + return thisLib; + +}); diff --git a/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.ts b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.ts new file mode 100644 index 0000000000..bb39a4baaa --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/library.ts @@ -0,0 +1,58 @@ +/*! + * ${copyright} + */ + +import ObjectPath from "sap/base/util/ObjectPath"; +import Lib from "sap/ui/core/Lib"; + +/** + * Initialization Code and shared classes of library <%= libraryNamespace %>. + */ + +// delegate further initialization of this library to the Core +// Hint: sap.ui.getCore() must still be used here to support preload with sync bootstrap! +Lib.init({ + name: "<%= libraryNamespace %>", + version: "${version}", + dependencies: [ // keep in sync with the ui5.yaml and .library files + "sap.ui.core" + ], + types: [ + "<%= libraryNamespace %>.ExampleColor" + ], + interfaces: [], + controls: [ + "<%= libraryNamespace %>.Example" + ], + elements: [], + noLibraryCSS: false, // if no CSS is provided, you can disable the library.css load here + apiVersion: 2, +}); + +// get the library object from global object space because all enums must be attached to it to be usable as UI5 types +// FIXME: this line is planned to become obsolete and may need to be removed later +const thisLib : {[key: string]: unknown} = ObjectPath.get("<%= libraryNamespace %>") as {[key: string]: unknown}; + +/** + * Semantic Colors of the com.myorg.myUI5Library.Example control. + * + * @enum {string} + * @public + */ +export enum ExampleColor { + + /** + * Default color (brand color) + * @public + */ + Default = "Default", + + /** + * Highlight color + * @public + */ + Highlight = "Highlight" + +} +// FIXME: this line is planned to become obsolete and may need to be removed later +thisLib.ExampleColor = ExampleColor; // add the enum to the library; this is important because UI5 otherwise cannot identify the type and will skip type checking for properties of this type \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/messagebundle.properties b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/messagebundle.properties similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/messagebundle.properties rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/messagebundle.properties diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/base/Example.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/base/Example.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/base/Example.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/base/Example.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/base/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/base/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/base/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/base/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_hcb/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_hcb/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_hcb/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_hcb/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_hcw/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_hcw/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_hcw/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_hcw/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_plus/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_plus/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_belize_plus/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_belize_plus/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_dark/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_dark/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_dark/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_dark/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less b/packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less rename to packages/ui5-library-writer/templates/common/1.120.0/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less diff --git a/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.html b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.html new file mode 100644 index 0000000000..e2b8e25363 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.html @@ -0,0 +1,21 @@ + + + + + Test Page for <%= libraryNamespace %>.Example + + + + +

Test Page for <%= libraryNamespace %>.Example

+
+ + diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/Example.js b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.js similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/Example.js rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.js diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/Example.ts b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/Example.ts rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/Example.ts diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/qunit/Example.qunit.js b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/Example.qunit.js similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/qunit/Example.qunit.js rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/Example.qunit.js diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/qunit/Example.qunit.ts b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/Example.qunit.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/qunit/Example.qunit.ts rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/Example.qunit.ts diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.html b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.html similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.html rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.html diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.js b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.js similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.js rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.js diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.ts b/packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/qunit/testsuite.qunit.ts rename to packages/ui5-library-writer/templates/common/1.120.0/test/baselibrary/qunit/testsuite.qunit.ts diff --git a/packages/ui5-library-writer/templates/common/1.120.0/ui5.yaml b/packages/ui5-library-writer/templates/common/1.120.0/ui5.yaml new file mode 100644 index 0000000000..2622622ec5 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.120.0/ui5.yaml @@ -0,0 +1,18 @@ +specVersion: '2.2' +metadata: + name: "<%= libraryName %>" +type: library +framework: + name: <%= framework %> + version: <%= frameworkVersion %> + libraries: + - name: sap.ui.core + - name: themelib_sap_fiori_3 +server: + customMiddleware: + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + path: test + port: 35729 + delay: 300 \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/1.71.0/gitignore.tmpl b/packages/ui5-library-writer/templates/common/1.71.0/gitignore.tmpl new file mode 100644 index 0000000000..d0f499126c --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/gitignore.tmpl @@ -0,0 +1,9 @@ +node_modules/ +dist/ +.scp/ +Makefile*.mta +mta_archives +mta-* +resources +archive.zip +.*_mta_build_tmp \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/1.71.0/karma.conf.tmpl b/packages/ui5-library-writer/templates/common/1.71.0/karma.conf.tmpl new file mode 100644 index 0000000000..11f8dbff81 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/karma.conf.tmpl @@ -0,0 +1,7 @@ +// karma-ui5 usage: https://github.com/SAP/karma-ui5 +module.exports = function (config) { + config.set({ + frameworks: ["ui5"], + browsers: ["Chrome"] + }); +}; \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/1.71.0/package.json b/packages/ui5-library-writer/templates/common/1.71.0/package.json new file mode 100644 index 0000000000..2db425eed5 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/package.json @@ -0,0 +1,18 @@ +{ + "name": "<%= libraryName %>", + "version": "1.0.0", + "devDependencies": { + "@ui5/cli": "^3.9.1", + "karma": "6.3.17", + "karma-chrome-launcher": "^3.1.1", + "karma-cli": "^2.0.0", + "karma-ui5": "^3.0.3", + "@sap/ux-ui5-tooling": "1" + }, + "scripts": { + "build": "ui5 build --clean-dest", + "start": "fiori run --open test-resources/<%= libraryNamespaceURI %>/Example.html", + "testsuite": "fiori run --open test-resources/<%= libraryNamespaceURI %>/qunit/testsuite.qunit.html", + "test": "karma start --browsers=ChromeHeadless --singleRun=true" + } +} diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/.library b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/.library new file mode 100644 index 0000000000..8075bd19ea --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/.library @@ -0,0 +1,14 @@ + + + <%= libraryNamespace %> + <%= author %> + ${version} + ${copyright} + <%= libraryName %> + Some description about <%= libraryName %> + + + sap.ui.core + + + diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.gen.d.ts b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.gen.d.ts new file mode 100644 index 0000000000..05bec07ece --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.gen.d.ts @@ -0,0 +1,33 @@ +import { ExampleColor } from "./library"; +import Event from "sap/ui/base/Event"; +import { PropertyBindingInfo } from "sap/ui/base/ManagedObject"; +import { $ControlSettings } from "sap/ui/core/Control"; + +declare module "./Example" { + + /** + * Interface defining the settings object used in constructor calls + */ + interface $ExampleSettings extends $ControlSettings { + text?: string | PropertyBindingInfo; + color?: ExampleColor | PropertyBindingInfo | `{${string}}`; + press?: (event: Event) => void; + } + + export default interface Example { + + // property: text + getText(): string; + setText(text: string): this; + + // property: color + getColor(): ExampleColor; + setColor(color: ExampleColor): this; + + // event: press + attachPress(fn: (event: Event) => void, listener?: object): this; + attachPress(data: CustomDataType, fn: (event: Event, data: CustomDataType) => void, listener?: object): this; + detachPress(fn: (event: Event) => void, listener?: object): this; + firePress(parameters?: object): this; + } +} \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.js b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.js new file mode 100644 index 0000000000..6c44bb4b8e --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.js @@ -0,0 +1,68 @@ +/*! + * ${copyright} + */ + +// Provides control <%= libraryNamespace %>.Example. +sap.ui.define([ + "./library", + "sap/ui/core/Control", + "./ExampleRenderer" +], function (library, Control, ExampleRenderer) { + "use strict"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Constructor for a new <%= libraryNamespace %>.Example control. + * + * @param {string} [sId] id for the new control, generated automatically if no id is given + * @param {object} [mSettings] initial settings for the new control + * + * @class + * Some class description goes here. + * @extends sap.ui.core.Control + * + * @author <%= author %> + * @version ${version} + * + * @constructor + * @public + * @alias <%= libraryNamespace %>.Example + */ + var Example = Control.extend("<%= libraryNamespace %>.Example", /** @lends <%= libraryNamespace %>.Example.prototype */ { + metadata: { + library: "<%= libraryNamespace %>", + properties: { + /** + * The text to display. + */ + text: { + type: "string", + group: "Data", + defaultValue: null + }, + /** + * The color to use (default to "Default" color). + */ + color: { + type: "<%= libraryNamespace %>.ExampleColor", + group: "Appearance", + defaultValue: ExampleColor.Default + } + }, + events: { + /** + * Event is fired when the user clicks the control. + */ + press: {} + } + }, + renderer: ExampleRenderer, + onclick: function() { + this.firePress(); + } + }); + return Example; + +}); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.ts b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.ts new file mode 100644 index 0000000000..6f019795db --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/Example.ts @@ -0,0 +1,65 @@ +/*! + * ${copyright} + */ + +// Provides control <%= libraryNamespace %>.Example. +import Control from "sap/ui/core/Control"; +import ExampleRenderer from "./ExampleRenderer"; +import { ExampleColor } from "./library"; + + + +/** + * Constructor for a new <%= libraryNamespace %>.Example control. + * + * Some class description goes here. + * @extends Control + * + * @author OpenUI5 Team + * @version ${version} + * + * @constructor + * @public + * @name <%= libraryNamespace %>.Example + */ +export default class Example extends Control { + + // The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures + constructor(id?: string | $ExampleSettings); + constructor(id?: string, settings?: $ExampleSettings); + constructor(id?: string, settings?: $ExampleSettings) { super(id, settings); } + + static readonly metadata = { + library: "<%= libraryNamespace %>", + properties: { + /** + * The text to display. + */ + text: { + type: "string", + group: "Data", + defaultValue: null + }, + /** + * The color to use (default to "Default" color). + */ + color: { + type: "<%= libraryNamespace %>.ExampleColor", + group: "Appearance", + defaultValue: ExampleColor.Default + } + }, + events: { + /** + * Event is fired when the user clicks the control. + */ + press: {} + } + }; + + static renderer = ExampleRenderer; + + onclick = () => { + this.firePress(); + }; +} \ No newline at end of file diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/ExampleRenderer.js b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/ExampleRenderer.js similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/ExampleRenderer.js rename to packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/ExampleRenderer.js diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/ExampleRenderer.ts b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/ExampleRenderer.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/ExampleRenderer.ts rename to packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/ExampleRenderer.ts diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/library.js b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/library.js similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/library.js rename to packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/library.js diff --git a/packages/ui5-library-writer/templates/common/src/baselibrary/library.ts b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/library.ts similarity index 100% rename from packages/ui5-library-writer/templates/common/src/baselibrary/library.ts rename to packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/library.ts diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/messagebundle.properties b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/messagebundle.properties new file mode 100644 index 0000000000..fc0c39f97b --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/messagebundle.properties @@ -0,0 +1,2 @@ +# Translation file of library <%= libraryNamespace %>. +ANY_TEXT=AnyText diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/Example.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/Example.less new file mode 100644 index 0000000000..92e2d5df45 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/Example.less @@ -0,0 +1,19 @@ +/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ + +.myLibPrefixExample, +.myLibPrefixExampleHighlight { + color: @sapUiText; + background-color: @sapUiNeutralBG; + border: 1rem solid @sapUiContentForegroundBorderColor; + border-radius: 1rem; + opacity: 0.8; + padding: 2rem; + margin: 2rem 8rem; + text-align: center; + font-size: 2em; + line-height: 3em; +} + +.myLibPrefixExampleHighlight { + background-color: @sapUiSuccessBG; +} diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/library.source.less new file mode 100644 index 0000000000..1886702355 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/base/library.source.less @@ -0,0 +1,4 @@ +@import "/resources/sap/ui/core/themes/base/base.less"; +@import "/resources/sap/ui/core/themes/base/global.less"; + +@import "Example.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize/library.source.less new file mode 100644 index 0000000000..7b9e8613c4 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_belize/base.less"; +@import "/resources/sap/ui/core/themes/sap_belize/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcb/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcb/library.source.less new file mode 100644 index 0000000000..9bcf82f9d0 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcb/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_belize_hcb/base.less"; +@import "/resources/sap/ui/core/themes/sap_belize_hcb/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcw/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcw/library.source.less new file mode 100644 index 0000000000..9c85013ee8 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_hcw/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_belize_hcw/base.less"; +@import "/resources/sap/ui/core/themes/sap_belize_hcw/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_plus/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_plus/library.source.less new file mode 100644 index 0000000000..8402b96caf --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_belize_plus/library.source.less @@ -0,0 +1,3 @@ +@import "../sap_belize/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_belize_plus/base.less"; +@import "/resources/sap/ui/core/themes/sap_belize_plus/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3/library.source.less new file mode 100644 index 0000000000..98a4794216 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3/base.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_dark/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_dark/library.source.less new file mode 100644 index 0000000000..f0b9c891bb --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_dark/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less new file mode 100644 index 0000000000..9239a9308a --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcb/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less"; diff --git a/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less new file mode 100644 index 0000000000..721aae859a --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/src/baselibrary/themes/sap_fiori_3_hcw/library.source.less @@ -0,0 +1,3 @@ +@import "../base/library.source.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less"; +@import "/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less"; diff --git a/packages/ui5-library-writer/templates/common/test/baselibrary/Example.html b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.html similarity index 100% rename from packages/ui5-library-writer/templates/common/test/baselibrary/Example.html rename to packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.html diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.js b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.js new file mode 100644 index 0000000000..07dab35d24 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.js @@ -0,0 +1,18 @@ +// eslint-disable-next-line no-undef +sap.ui.define([ + "<%= libraryNamespaceURI %>/library", + "<%= libraryNamespaceURI %>/Example" +], function(library, Example) { + "use strict"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // create a new instance of the Example control and + // place it into the DOM element with the id "content" + new Example({ + text: "Example", + color: ExampleColor.Highlight + }).placeAt("content"); + +}); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.ts b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.ts new file mode 100644 index 0000000000..9eaa0113fc --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/Example.ts @@ -0,0 +1,9 @@ +import { ExampleColor } from '<%= libraryNamespaceURI %>/library'; +import Example from '<%= libraryNamespaceURI %>/Example'; + +// Create a new instance of the Example control and +// place it into the DOM element with the id "content" +new Example({ + text: 'Example', + color: ExampleColor.Highlight, +}).placeAt('content'); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.js b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.js new file mode 100644 index 0000000000..5253fd82d3 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.js @@ -0,0 +1,65 @@ +/*global QUnit */ +// eslint-disable-next-line no-undef +sap.ui.define([ + "sap/ui/qunit/QUnitUtils", + "sap/ui/qunit/utils/createAndAppendDiv", + "<%= libraryNamespaceURI %>/library", + "<%= libraryNamespaceURI %>/Example" +], function(qutils, createAndAppendDiv, library, Example) { + "use strict"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // prepare DOM + createAndAppendDiv("uiArea1"); + + // module for basic checks + QUnit.module("Example Tests"); + + // example sync test + QUnit.test("Sync", function(assert) { + assert.expect(1); + assert.ok(true, "ok"); + }); + + // example async test + QUnit.test("Async", function(assert) { + assert.expect(1); + return new Promise(function(resolve, reject) { + assert.ok(true, "ok"); + resolve(); + }); + }) + + // module for basic checks + QUnit.module("Basic Control Checks"); + + // some basic control checks + QUnit.test("Test get properties", function(assert) { + assert.expect(2); + var oExample = new Example({ + text: "Example" + }); + assert.equal(oExample.getText(), "Example", "Check text equals 'Example'"); + assert.equal(oExample.getColor(), ExampleColor.Default, "Check color equals 'Default'"); + }); + + // some basic eventing check + QUnit.test("Test click event", function(assert) { + assert.expect(1); + var oExample = new Example("example", { + text: "Example", + press: function() { + assert.ok(true, "Event has been fired!") + } + }).placeAt("uiArea1"); + return new Promise(function(resolve, reject) { + setTimeout(function() { + qutils.triggerMouseEvent("example", "click", 1, 1); + resolve(); + }, 100); + }); + }); + +}); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.ts b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.ts new file mode 100644 index 0000000000..9e0f0d3f02 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/Example.qunit.ts @@ -0,0 +1,68 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +/*global QUnit */ +// eslint-disable-next-line no-undef +sap.ui.define([ + "sap/ui/qunit/QUnitUtils", + "sap/ui/qunit/utils/createAndAppendDiv", + "<%= libraryNamespaceURI %>/library", + "<%= libraryNamespaceURI %>/Example" +], function(qutils, createAndAppendDiv, library, Example) { + "use strict"; + + // refer to library types + const ExampleColor = library.ExampleColor; + + // prepare DOM + createAndAppendDiv("uiArea1"); + + // module for basic checks + QUnit.module("Example Tests"); + + // example sync test + QUnit.test("Sync", function(assert) { + assert.expect(1); + assert.ok(true, "ok"); + }); + + // example async test + QUnit.test("Async", function(assert) { + assert.expect(1); + return new Promise(function(resolve, reject) { + assert.ok(true, "ok"); + resolve(); + }); + }) + + // module for basic checks + QUnit.module("Basic Control Checks"); + + // some basic control checks + QUnit.test("Test get properties", function(assert) { + assert.expect(2); + const oExample = new Example({ + text: "Example" + }); + assert.equal(oExample.getText(), "Example", "Check text equals 'Example'"); + assert.equal(oExample.getColor(), ExampleColor.Default, "Check color equals 'Default'"); + }); + + // some basic eventing check + QUnit.test("Test click event", function(assert) { + assert.expect(1); + const oExample = new Example("example", { + text: "Example", + press: function() { + assert.ok(true, "Event has been fired!") + } + }).placeAt("uiArea1"); + return new Promise(function(resolve, reject) { + setTimeout(function() { + qutils.triggerMouseEvent("example", "click", 1, 1); + resolve(); + }, 100); + }); + }); + +}); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.html b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.html new file mode 100644 index 0000000000..57a2486bd0 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.html @@ -0,0 +1,13 @@ + + + + + + + QUnit TestSuite for <%= libraryNamespace %> + + + + + diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.js b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.js new file mode 100644 index 0000000000..4d89d0be58 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.js @@ -0,0 +1,35 @@ +// eslint-disable-next-line no-undef +sap.ui.define(function() { + "use strict"; + + return { + name: "QUnit TestSuite for <%= libraryNamespace %>", + defaults: { + bootCore: true, + ui5: { + libs: "sap.ui.core,<%= libraryNamespace %>", + theme: "sap_fiori_3", + noConflict: true, + preload: "auto" + }, + qunit: { + version: 2, + reorder: false + }, + sinon: { + version: 4, + qunitBridge: true, + useFakeTimers: false + }, + module: "./{name}.qunit" + }, + tests: { + // test file for the Example control + Example: { + title: "QUnit Test for Example", + _alternativeTitle: "QUnit tests: <%= libraryNamespace %>.Example" + } + } + }; + +}); diff --git a/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.ts b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.ts new file mode 100644 index 0000000000..4d89d0be58 --- /dev/null +++ b/packages/ui5-library-writer/templates/common/1.71.0/test/baselibrary/qunit/testsuite.qunit.ts @@ -0,0 +1,35 @@ +// eslint-disable-next-line no-undef +sap.ui.define(function() { + "use strict"; + + return { + name: "QUnit TestSuite for <%= libraryNamespace %>", + defaults: { + bootCore: true, + ui5: { + libs: "sap.ui.core,<%= libraryNamespace %>", + theme: "sap_fiori_3", + noConflict: true, + preload: "auto" + }, + qunit: { + version: 2, + reorder: false + }, + sinon: { + version: 4, + qunitBridge: true, + useFakeTimers: false + }, + module: "./{name}.qunit" + }, + tests: { + // test file for the Example control + Example: { + title: "QUnit Test for Example", + _alternativeTitle: "QUnit tests: <%= libraryNamespace %>.Example" + } + } + }; + +}); diff --git a/packages/ui5-library-writer/templates/common/ui5.yaml b/packages/ui5-library-writer/templates/common/1.71.0/ui5.yaml similarity index 100% rename from packages/ui5-library-writer/templates/common/ui5.yaml rename to packages/ui5-library-writer/templates/common/1.71.0/ui5.yaml diff --git a/packages/ui5-library-writer/test/__snapshots__/index.test.ts.snap b/packages/ui5-library-writer/test/__snapshots__/index.test.ts.snap index 47c96c697f..3f795fcbb6 100644 --- a/packages/ui5-library-writer/test/__snapshots__/index.test.ts.snap +++ b/packages/ui5-library-writer/test/__snapshots__/index.test.ts.snap @@ -547,16 +547,1733 @@ server: } `; +exports[`Reuse lib templates Generate files for config: lib-js-1.121.0 1`] = ` +Object { + "com.sap.myui5jslib121/.gitignore": Object { + "contents": "node_modules/ +dist/ +.scp/ +Makefile*.mta +mta_archives +mta-* +resources +archive.zip +.*_mta_build_tmp", + "state": "modified", + }, + "com.sap.myui5jslib121/karma.conf.js": Object { + "contents": "// karma-ui5 usage: https://github.com/SAP/karma-ui5 +module.exports = function (config) { + config.set({ + frameworks: [\\"ui5\\"], + browsers: [\\"Chrome\\"] + }); +};", + "state": "modified", + }, + "com.sap.myui5jslib121/package.json": Object { + "contents": "{ + \\"name\\": \\"myui5jslib121\\", + \\"version\\": \\"1.0.0\\", + \\"devDependencies\\": { + \\"@ui5/cli\\": \\"^3.9.1\\", + \\"karma\\": \\"6.3.17\\", + \\"karma-chrome-launcher\\": \\"^3.1.1\\", + \\"karma-cli\\": \\"^2.0.0\\", + \\"karma-ui5\\": \\"^3.0.3\\", + \\"@sap/ux-ui5-tooling\\": \\"1\\" + }, + \\"scripts\\": { + \\"build\\": \\"ui5 build --clean-dest\\", + \\"start\\": \\"fiori run --open test-resources/com/sap/myui5jslib121/Example.html\\", + \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5jslib121/qunit/testsuite.qunit.html\\", + \\"test\\": \\"karma start --browsers=ChromeHeadless --singleRun=true\\" + } +} +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/.library": Object { + "contents": " + + com.sap.myui5jslib121 + UI5 Lib Author + \${version} + \${copyright} + myui5jslib121 + Some description about myui5jslib121 + + + sap.ui.core + + + +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/Example.js": Object { + "contents": "/*! + * \${copyright} + */ + +// Provides control com.sap.myui5jslib121.Example. +sap.ui.define([ + \\"./library\\", + \\"sap/ui/core/Control\\", + \\"./ExampleRenderer\\" +], function (library, Control, ExampleRenderer) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Constructor for a new com.sap.myui5jslib121.Example control. + * + * @param {string} [sId] id for the new control, generated automatically if no id is given + * @param {object} [mSettings] initial settings for the new control + * + * @class + * Some class description goes here. + * @extends sap.ui.core.Control + * + * @author UI5 Lib Author + * @version \${version} + * + * @constructor + * @public + * @alias com.sap.myui5jslib121.Example + */ + var Example = Control.extend(\\"com.sap.myui5jslib121.Example\\", /** @lends com.sap.myui5jslib121.Example.prototype */ { + metadata: { + library: \\"com.sap.myui5jslib121\\", + properties: { + /** + * The text to display. + */ + text: { + type: \\"string\\", + group: \\"Data\\", + defaultValue: null + }, + /** + * The color to use (default to \\"Default\\" color). + */ + color: { + type: \\"com.sap.myui5jslib121.ExampleColor\\", + group: \\"Appearance\\", + defaultValue: ExampleColor.Default + } + }, + events: { + /** + * Event is fired when the user clicks the control. + */ + press: {} + } + }, + renderer: ExampleRenderer, + onclick: function() { + this.firePress(); + } + }); + return Example; + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/ExampleRenderer.js": Object { + "contents": "/*! + * \${copyright} + */ + +sap.ui.define([ + \\"sap/ui/core/Lib\\", + \\"./library\\" +], function (Lib, library) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Example renderer. + * @namespace + */ + var ExampleRenderer = { + apiVersion: 2 // usage of DOM Patcher + }; + + /** + * Renders the HTML for the given control, using the provided + * {@link sap.ui.core.RenderManager}. + * + * @param {sap.ui.core.RenderManager} rm The reference to the sap.ui.core.RenderManager + * @param {sap.ui.core.Control} control The control instance to be rendered + */ + ExampleRenderer.render = function (rm, control) { + + var i18n = Lib.getResourceBundleFor(\\"com.sap.myui5jslib121\\"); + + rm.openStart(\\"div\\", control); + if (control.getColor() === ExampleColor.Highlight) { + rm.class(\\"myLibPrefixExampleHighlight\\"); + } else { + rm.class(\\"myLibPrefixExample\\"); + } + rm.openEnd( ); + rm.text(i18n.getText(\\"ANY_TEXT\\") + \\": \\" + control.getText()); + rm.close(\\"div\\"); + + }; + + return ExampleRenderer; + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/library.js": Object { + "contents": "/*! + * \${copyright} + */ + +/** + * Initialization Code and shared classes of library com.sap.myui5jslib121. + */ +sap.ui.define([ + \\"sap/ui/core/Lib\\" +], function (Lib) { + \\"use strict\\"; + + // delegate further initialization of this library to the Core + // Hint: sap.ui.getCore() must still be used to support preload with sync bootstrap! + Lib.init({ + name: \\"com.sap.myui5jslib121\\", + version: \\"\${version}\\", + dependencies: [ // keep in sync with the ui5.yaml and .library files + \\"sap.ui.core\\" + ], + types: [ + \\"com.sap.myui5jslib121.ExampleColor\\" + ], + interfaces: [], + controls: [ + \\"com.sap.myui5jslib121.Example\\" + ], + elements: [], + noLibraryCSS: false, // if no CSS is provided, you can disable the library.css load here + apiVersion: 2 + }); + + /** + * Some description about myui5jslib121 + * + * @namespace + * @name com.sap.myui5jslib121 + * @author UI5 Lib Author + * @version \${version} + * @public + */ + var thisLib = com.sap.myui5jslib121; + + /** + * Semantic Colors of the com.sap.myui5jslib121.Example. + * + * @enum {string} + * @public + */ + thisLib.ExampleColor = { + + /** + * Default color (brand color) + * @public + */ + Default : \\"Default\\", + + /** + * Highlight color + * @public + */ + Highlight : \\"Highlight\\" + + }; + + return thisLib; + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/messagebundle.properties": Object { + "contents": "# Translation file of library com.sap.myui5jslib121. +ANY_TEXT=AnyText +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/base/Example.less": Object { + "contents": "/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ + +.myLibPrefixExample, +.myLibPrefixExampleHighlight { + color: @sapUiText; + background-color: @sapUiNeutralBG; + border: 1rem solid @sapUiContentForegroundBorderColor; + border-radius: 1rem; + opacity: 0.8; + padding: 2rem; + margin: 2rem 8rem; + text-align: center; + font-size: 2em; + line-height: 3em; +} + +.myLibPrefixExampleHighlight { + background-color: @sapUiSuccessBG; +} +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/base/library.source.less": Object { + "contents": "@import \\"/resources/sap/ui/core/themes/base/base.less\\"; +@import \\"/resources/sap/ui/core/themes/base/global.less\\"; + +@import \\"Example.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_belize/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_belize_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_belize_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_belize_plus/library.source.less": Object { + "contents": "@import \\"../sap_belize/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_fiori_3/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_fiori_3_dark/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_fiori_3_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/src/com/sap/myui5jslib121/themes/sap_fiori_3_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jslib121/test/com/sap/myui5jslib121/Example.html": Object { + "contents": " + + + + Test Page for com.sap.myui5jslib121.Example + + + + +

Test Page for com.sap.myui5jslib121.Example

+
+ + +", + "state": "modified", + }, + "com.sap.myui5jslib121/test/com/sap/myui5jslib121/Example.js": Object { + "contents": "// eslint-disable-next-line no-undef +sap.ui.define([ + \\"com/sap/myui5jslib121/library\\", + \\"com/sap/myui5jslib121/Example\\" +], function(library, Example) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // create a new instance of the Example control and + // place it into the DOM element with the id \\"content\\" + new Example({ + text: \\"Example\\", + color: ExampleColor.Highlight + }).placeAt(\\"content\\"); + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/test/com/sap/myui5jslib121/qunit/Example.qunit.js": Object { + "contents": "/*global QUnit */ +// eslint-disable-next-line no-undef +sap.ui.define([ + \\"sap/ui/qunit/QUnitUtils\\", + \\"sap/ui/qunit/utils/createAndAppendDiv\\", + \\"com/sap/myui5jslib121/library\\", + \\"com/sap/myui5jslib121/Example\\" +], function(qutils, createAndAppendDiv, library, Example) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // prepare DOM + createAndAppendDiv(\\"uiArea1\\"); + + // module for basic checks + QUnit.module(\\"Example Tests\\"); + + // example sync test + QUnit.test(\\"Sync\\", function(assert) { + assert.expect(1); + assert.ok(true, \\"ok\\"); + }); + + // example async test + QUnit.test(\\"Async\\", function(assert) { + assert.expect(1); + return new Promise(function(resolve, reject) { + assert.ok(true, \\"ok\\"); + resolve(); + }); + }) + + // module for basic checks + QUnit.module(\\"Basic Control Checks\\"); + + // some basic control checks + QUnit.test(\\"Test get properties\\", function(assert) { + assert.expect(2); + var oExample = new Example({ + text: \\"Example\\" + }); + assert.equal(oExample.getText(), \\"Example\\", \\"Check text equals 'Example'\\"); + assert.equal(oExample.getColor(), ExampleColor.Default, \\"Check color equals 'Default'\\"); + }); + + // some basic eventing check + QUnit.test(\\"Test click event\\", function(assert) { + assert.expect(1); + var oExample = new Example(\\"example\\", { + text: \\"Example\\", + press: function() { + assert.ok(true, \\"Event has been fired!\\") + } + }).placeAt(\\"uiArea1\\"); + return new Promise(function(resolve, reject) { + setTimeout(function() { + qutils.triggerMouseEvent(\\"example\\", \\"click\\", 1, 1); + resolve(); + }, 100); + }); + }); + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/test/com/sap/myui5jslib121/qunit/testsuite.qunit.html": Object { + "contents": " + + + + + + QUnit TestSuite for com.sap.myui5jslib121 + + + + + +", + "state": "modified", + }, + "com.sap.myui5jslib121/test/com/sap/myui5jslib121/qunit/testsuite.qunit.js": Object { + "contents": "// eslint-disable-next-line no-undef +sap.ui.define(function() { + \\"use strict\\"; + + return { + name: \\"QUnit TestSuite for com.sap.myui5jslib121\\", + defaults: { + bootCore: true, + ui5: { + libs: \\"sap.ui.core,com.sap.myui5jslib121\\", + theme: \\"sap_fiori_3\\", + noConflict: true, + preload: \\"auto\\" + }, + qunit: { + version: 2, + reorder: false + }, + sinon: { + version: 4, + qunitBridge: true, + useFakeTimers: false + }, + module: \\"./{name}.qunit\\" + }, + tests: { + // test file for the Example control + Example: { + title: \\"QUnit Test for Example\\", + _alternativeTitle: \\"QUnit tests: com.sap.myui5jslib121.Example\\" + } + } + }; + +}); +", + "state": "modified", + }, + "com.sap.myui5jslib121/ui5.yaml": Object { + "contents": "specVersion: '2.2' +metadata: + name: \\"myui5jslib121\\" +type: library +framework: + name: SAPUI5 + version: 1.121.0 + libraries: + - name: sap.ui.core + - name: themelib_sap_fiori_3 +server: + customMiddleware: + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + path: test + port: 35729 + delay: 300", + "state": "modified", + }, +} +`; + +exports[`Reuse lib templates Generate files for config: lib-js-latest 1`] = ` +Object { + "com.sap.myui5jsliblatest/.gitignore": Object { + "contents": "node_modules/ +dist/ +.scp/ +Makefile*.mta +mta_archives +mta-* +resources +archive.zip +.*_mta_build_tmp", + "state": "modified", + }, + "com.sap.myui5jsliblatest/karma.conf.js": Object { + "contents": "// karma-ui5 usage: https://github.com/SAP/karma-ui5 +module.exports = function (config) { + config.set({ + frameworks: [\\"ui5\\"], + browsers: [\\"Chrome\\"] + }); +};", + "state": "modified", + }, + "com.sap.myui5jsliblatest/package.json": Object { + "contents": "{ + \\"name\\": \\"myui5jsliblatest\\", + \\"version\\": \\"1.0.0\\", + \\"devDependencies\\": { + \\"@ui5/cli\\": \\"^3.9.1\\", + \\"karma\\": \\"6.3.17\\", + \\"karma-chrome-launcher\\": \\"^3.1.1\\", + \\"karma-cli\\": \\"^2.0.0\\", + \\"karma-ui5\\": \\"^3.0.3\\", + \\"@sap/ux-ui5-tooling\\": \\"1\\" + }, + \\"scripts\\": { + \\"build\\": \\"ui5 build --clean-dest\\", + \\"start\\": \\"fiori run --open test-resources/com/sap/myui5jsliblatest/Example.html\\", + \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5jsliblatest/qunit/testsuite.qunit.html\\", + \\"test\\": \\"karma start --browsers=ChromeHeadless --singleRun=true\\" + } +} +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/.library": Object { + "contents": " + + com.sap.myui5jsliblatest + UI5 Lib Author + \${version} + \${copyright} + myui5jsliblatest + Some description about myui5jsliblatest + + + sap.ui.core + + + +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/Example.js": Object { + "contents": "/*! + * \${copyright} + */ + +// Provides control com.sap.myui5jsliblatest.Example. +sap.ui.define([ + \\"./library\\", + \\"sap/ui/core/Control\\", + \\"./ExampleRenderer\\" +], function (library, Control, ExampleRenderer) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Constructor for a new com.sap.myui5jsliblatest.Example control. + * + * @param {string} [sId] id for the new control, generated automatically if no id is given + * @param {object} [mSettings] initial settings for the new control + * + * @class + * Some class description goes here. + * @extends sap.ui.core.Control + * + * @author UI5 Lib Author + * @version \${version} + * + * @constructor + * @public + * @alias com.sap.myui5jsliblatest.Example + */ + var Example = Control.extend(\\"com.sap.myui5jsliblatest.Example\\", /** @lends com.sap.myui5jsliblatest.Example.prototype */ { + metadata: { + library: \\"com.sap.myui5jsliblatest\\", + properties: { + /** + * The text to display. + */ + text: { + type: \\"string\\", + group: \\"Data\\", + defaultValue: null + }, + /** + * The color to use (default to \\"Default\\" color). + */ + color: { + type: \\"com.sap.myui5jsliblatest.ExampleColor\\", + group: \\"Appearance\\", + defaultValue: ExampleColor.Default + } + }, + events: { + /** + * Event is fired when the user clicks the control. + */ + press: {} + } + }, + renderer: ExampleRenderer, + onclick: function() { + this.firePress(); + } + }); + return Example; + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/ExampleRenderer.js": Object { + "contents": "/*! + * \${copyright} + */ + +sap.ui.define([ + \\"sap/ui/core/Lib\\", + \\"./library\\" +], function (Lib, library) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + /** + * Example renderer. + * @namespace + */ + var ExampleRenderer = { + apiVersion: 2 // usage of DOM Patcher + }; + + /** + * Renders the HTML for the given control, using the provided + * {@link sap.ui.core.RenderManager}. + * + * @param {sap.ui.core.RenderManager} rm The reference to the sap.ui.core.RenderManager + * @param {sap.ui.core.Control} control The control instance to be rendered + */ + ExampleRenderer.render = function (rm, control) { + + var i18n = Lib.getResourceBundleFor(\\"com.sap.myui5jsliblatest\\"); + + rm.openStart(\\"div\\", control); + if (control.getColor() === ExampleColor.Highlight) { + rm.class(\\"myLibPrefixExampleHighlight\\"); + } else { + rm.class(\\"myLibPrefixExample\\"); + } + rm.openEnd( ); + rm.text(i18n.getText(\\"ANY_TEXT\\") + \\": \\" + control.getText()); + rm.close(\\"div\\"); + + }; + + return ExampleRenderer; + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/library.js": Object { + "contents": "/*! + * \${copyright} + */ + +/** + * Initialization Code and shared classes of library com.sap.myui5jsliblatest. + */ +sap.ui.define([ + \\"sap/ui/core/Lib\\" +], function (Lib) { + \\"use strict\\"; + + // delegate further initialization of this library to the Core + // Hint: sap.ui.getCore() must still be used to support preload with sync bootstrap! + Lib.init({ + name: \\"com.sap.myui5jsliblatest\\", + version: \\"\${version}\\", + dependencies: [ // keep in sync with the ui5.yaml and .library files + \\"sap.ui.core\\" + ], + types: [ + \\"com.sap.myui5jsliblatest.ExampleColor\\" + ], + interfaces: [], + controls: [ + \\"com.sap.myui5jsliblatest.Example\\" + ], + elements: [], + noLibraryCSS: false, // if no CSS is provided, you can disable the library.css load here + apiVersion: 2 + }); + + /** + * Some description about myui5jsliblatest + * + * @namespace + * @name com.sap.myui5jsliblatest + * @author UI5 Lib Author + * @version \${version} + * @public + */ + var thisLib = com.sap.myui5jsliblatest; + + /** + * Semantic Colors of the com.sap.myui5jsliblatest.Example. + * + * @enum {string} + * @public + */ + thisLib.ExampleColor = { + + /** + * Default color (brand color) + * @public + */ + Default : \\"Default\\", + + /** + * Highlight color + * @public + */ + Highlight : \\"Highlight\\" + + }; + + return thisLib; + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/messagebundle.properties": Object { + "contents": "# Translation file of library com.sap.myui5jsliblatest. +ANY_TEXT=AnyText +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/base/Example.less": Object { + "contents": "/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ + +.myLibPrefixExample, +.myLibPrefixExampleHighlight { + color: @sapUiText; + background-color: @sapUiNeutralBG; + border: 1rem solid @sapUiContentForegroundBorderColor; + border-radius: 1rem; + opacity: 0.8; + padding: 2rem; + margin: 2rem 8rem; + text-align: center; + font-size: 2em; + line-height: 3em; +} + +.myLibPrefixExampleHighlight { + background-color: @sapUiSuccessBG; +} +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/base/library.source.less": Object { + "contents": "@import \\"/resources/sap/ui/core/themes/base/base.less\\"; +@import \\"/resources/sap/ui/core/themes/base/global.less\\"; + +@import \\"Example.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_belize/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_belize_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_belize_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_belize_plus/library.source.less": Object { + "contents": "@import \\"../sap_belize/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_fiori_3/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_fiori_3_dark/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_fiori_3_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/src/com/sap/myui5jsliblatest/themes/sap_fiori_3_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/test/com/sap/myui5jsliblatest/Example.html": Object { + "contents": " + + + + Test Page for com.sap.myui5jsliblatest.Example + + + + +

Test Page for com.sap.myui5jsliblatest.Example

+
+ + +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/test/com/sap/myui5jsliblatest/Example.js": Object { + "contents": "// eslint-disable-next-line no-undef +sap.ui.define([ + \\"com/sap/myui5jsliblatest/library\\", + \\"com/sap/myui5jsliblatest/Example\\" +], function(library, Example) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // create a new instance of the Example control and + // place it into the DOM element with the id \\"content\\" + new Example({ + text: \\"Example\\", + color: ExampleColor.Highlight + }).placeAt(\\"content\\"); + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/test/com/sap/myui5jsliblatest/qunit/Example.qunit.js": Object { + "contents": "/*global QUnit */ +// eslint-disable-next-line no-undef +sap.ui.define([ + \\"sap/ui/qunit/QUnitUtils\\", + \\"sap/ui/qunit/utils/createAndAppendDiv\\", + \\"com/sap/myui5jsliblatest/library\\", + \\"com/sap/myui5jsliblatest/Example\\" +], function(qutils, createAndAppendDiv, library, Example) { + \\"use strict\\"; + + // refer to library types + var ExampleColor = library.ExampleColor; + + // prepare DOM + createAndAppendDiv(\\"uiArea1\\"); + + // module for basic checks + QUnit.module(\\"Example Tests\\"); + + // example sync test + QUnit.test(\\"Sync\\", function(assert) { + assert.expect(1); + assert.ok(true, \\"ok\\"); + }); + + // example async test + QUnit.test(\\"Async\\", function(assert) { + assert.expect(1); + return new Promise(function(resolve, reject) { + assert.ok(true, \\"ok\\"); + resolve(); + }); + }) + + // module for basic checks + QUnit.module(\\"Basic Control Checks\\"); + + // some basic control checks + QUnit.test(\\"Test get properties\\", function(assert) { + assert.expect(2); + var oExample = new Example({ + text: \\"Example\\" + }); + assert.equal(oExample.getText(), \\"Example\\", \\"Check text equals 'Example'\\"); + assert.equal(oExample.getColor(), ExampleColor.Default, \\"Check color equals 'Default'\\"); + }); + + // some basic eventing check + QUnit.test(\\"Test click event\\", function(assert) { + assert.expect(1); + var oExample = new Example(\\"example\\", { + text: \\"Example\\", + press: function() { + assert.ok(true, \\"Event has been fired!\\") + } + }).placeAt(\\"uiArea1\\"); + return new Promise(function(resolve, reject) { + setTimeout(function() { + qutils.triggerMouseEvent(\\"example\\", \\"click\\", 1, 1); + resolve(); + }, 100); + }); + }); + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/test/com/sap/myui5jsliblatest/qunit/testsuite.qunit.html": Object { + "contents": " + + + + + + QUnit TestSuite for com.sap.myui5jsliblatest + + + + + +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/test/com/sap/myui5jsliblatest/qunit/testsuite.qunit.js": Object { + "contents": "// eslint-disable-next-line no-undef +sap.ui.define(function() { + \\"use strict\\"; + + return { + name: \\"QUnit TestSuite for com.sap.myui5jsliblatest\\", + defaults: { + bootCore: true, + ui5: { + libs: \\"sap.ui.core,com.sap.myui5jsliblatest\\", + theme: \\"sap_fiori_3\\", + noConflict: true, + preload: \\"auto\\" + }, + qunit: { + version: 2, + reorder: false + }, + sinon: { + version: 4, + qunitBridge: true, + useFakeTimers: false + }, + module: \\"./{name}.qunit\\" + }, + tests: { + // test file for the Example control + Example: { + title: \\"QUnit Test for Example\\", + _alternativeTitle: \\"QUnit tests: com.sap.myui5jsliblatest.Example\\" + } + } + }; + +}); +", + "state": "modified", + }, + "com.sap.myui5jsliblatest/ui5.yaml": Object { + "contents": "specVersion: '2.2' +metadata: + name: \\"myui5jsliblatest\\" +type: library +framework: + name: SAPUI5 + version: + libraries: + - name: sap.ui.core + - name: themelib_sap_fiori_3 +server: + customMiddleware: + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + path: test + port: 35729 + delay: 300", + "state": "modified", + }, +} +`; + exports[`Reuse lib templates Generate files for config: lib-t-1.113.0 1`] = ` Object { - "com.sap.myui5tslib113/.eslintrc": Object { + "com.sap.myui5tslib113/.eslintrc": Object { + "contents": "{ + \\"extends\\": \\"plugin:@sap-ux/eslint-plugin-fiori-tools/defaultTS\\", + \\"root\\": true +}", + "state": "modified", + }, + "com.sap.myui5tslib113/.gitignore": Object { + "contents": "node_modules/ +dist/ +.scp/ +Makefile*.mta +mta_archives +mta-* +resources +archive.zip +.*_mta_build_tmp", + "state": "modified", + }, + "com.sap.myui5tslib113/karma.conf.js": Object { + "contents": "// karma-ui5 usage: https://github.com/SAP/karma-ui5 +module.exports = function (config) { + config.set({ + frameworks: [\\"ui5\\"], + browsers: [\\"Chrome\\"] + }); +};", + "state": "modified", + }, + "com.sap.myui5tslib113/package.json": Object { + "contents": "{ + \\"name\\": \\"myui5tslib113\\", + \\"version\\": \\"1.0.0\\", + \\"devDependencies\\": { + \\"@ui5/cli\\": \\"^3.9.1\\", + \\"karma\\": \\"^6.3.17\\", + \\"karma-chrome-launcher\\": \\"^3.1.1\\", + \\"karma-cli\\": \\"^2.0.0\\", + \\"karma-ui5\\": \\"^3.0.3\\", + \\"@sap/ux-ui5-tooling\\": \\"1\\", + \\"@sapui5/types\\": \\"1.113.0\\", + \\"@typescript-eslint/eslint-plugin\\": \\"^7.1.1\\", + \\"@typescript-eslint/parser\\": \\"^7.1.1\\", + \\"@ui5/ts-interface-generator\\": \\"^0.8.1\\", + \\"eslint\\": \\"^8.57.0\\", + \\"npm-run-all\\": \\"^4.1.5\\", + \\"typescript\\": \\"^5.1.6\\", + \\"ui5-tooling-transpile\\": \\"^3.3.7\\", + \\"@sap-ux/eslint-plugin-fiori-tools\\": \\"^0.4.0\\" + }, + \\"scripts\\": { + \\"build\\": \\"run-p -l build-app build-interface\\", + \\"start\\": \\"fiori run --open \\\\\\"test-resources/com/sap/myui5tslib113/Example.html\\\\\\"\\", + \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5tslib113/qunit/testsuite.qunit.html\\", + \\"test\\": \\"karma start --browsers=ChromeHeadless --singleRun=true\\", + \\"build-app\\": \\"ui5 build --config=ui5.yaml --clean-dest --dest dist\\", + \\"build-interface\\": \\"npx @ui5/ts-interface-generator\\", + \\"ts-typecheck\\": \\"tsc --noEmit\\", + \\"lint\\": \\"eslint .\\" + } +} +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/.library": Object { + "contents": " + + com.sap.myui5tslib113 + UX Tools + \${version} + \${copyright} + myui5tslib113 + Some description about myui5tslib113 + + + sap.ui.core + + + +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/Example.gen.d.ts": Object { + "contents": "import { ExampleColor } from \\"./library\\"; +import Event from \\"sap/ui/base/Event\\"; +import { PropertyBindingInfo } from \\"sap/ui/base/ManagedObject\\"; +import { $ControlSettings } from \\"sap/ui/core/Control\\"; + +declare module \\"./Example\\" { + + /** + * Interface defining the settings object used in constructor calls + */ + interface $ExampleSettings extends $ControlSettings { + text?: string | PropertyBindingInfo; + color?: ExampleColor | PropertyBindingInfo | \`{\${string}}\`; + press?: (event: Event) => void; + } + + export default interface Example { + + // property: text + getText(): string; + setText(text: string): this; + + // property: color + getColor(): ExampleColor; + setColor(color: ExampleColor): this; + + // event: press + attachPress(fn: (event: Event) => void, listener?: object): this; + attachPress(data: CustomDataType, fn: (event: Event, data: CustomDataType) => void, listener?: object): this; + detachPress(fn: (event: Event) => void, listener?: object): this; + firePress(parameters?: object): this; + } +}", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/Example.ts": Object { + "contents": "/*! + * \${copyright} + */ + +// Provides control com.sap.myui5tslib113.Example. +import Control from \\"sap/ui/core/Control\\"; +import ExampleRenderer from \\"./ExampleRenderer\\"; +import { ExampleColor } from \\"./library\\"; + + + +/** + * Constructor for a new com.sap.myui5tslib113.Example control. + * + * Some class description goes here. + * @extends Control + * + * @author OpenUI5 Team + * @version \${version} + * + * @constructor + * @public + * @name com.sap.myui5tslib113.Example + */ +export default class Example extends Control { + + // The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures + constructor(id?: string | $ExampleSettings); + constructor(id?: string, settings?: $ExampleSettings); + constructor(id?: string, settings?: $ExampleSettings) { super(id, settings); } + + static readonly metadata = { + library: \\"com.sap.myui5tslib113\\", + properties: { + /** + * The text to display. + */ + text: { + type: \\"string\\", + group: \\"Data\\", + defaultValue: null + }, + /** + * The color to use (default to \\"Default\\" color). + */ + color: { + type: \\"com.sap.myui5tslib113.ExampleColor\\", + group: \\"Appearance\\", + defaultValue: ExampleColor.Default + } + }, + events: { + /** + * Event is fired when the user clicks the control. + */ + press: {} + } + }; + + static renderer = ExampleRenderer; + + onclick = () => { + this.firePress(); + }; +}", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/ExampleRenderer.ts": Object { + "contents": "/*! + * \${copyright} + */ + +import ResourceBundle from \\"sap/base/i18n/ResourceBundle\\"; +import Core from \\"sap/ui/core/Core\\"; +import RenderManager from \\"sap/ui/core/RenderManager\\"; +import Example from \\"./Example\\"; +import { ExampleColor } from \\"./library\\"; + +/** + * Example renderer. + * @namespace + */ +const ExampleRenderer = { + apiVersion: 2, // usage of DOM Patcher + + /** + * Renders the HTML for the given control, using the provided {@link RenderManager}. + * + * @param {RenderManager} rm The reference to the sap.ui.core.RenderManager + * @param {Example} control The control instance to be rendered + */ + render: function (rm: RenderManager, control: Example) { + + const i18n = Core.getLibraryResourceBundle(\\"com.sap.myui5tslib113\\") as ResourceBundle; + + rm.openStart(\\"div\\", control); + if (control.getColor() === ExampleColor.Highlight) { + rm.class(\\"myLibPrefixExampleHighlight\\"); + } else { + rm.class(\\"myLibPrefixExample\\"); + } + rm.openEnd( ); + rm.text(i18n.getText(\\"ANY_TEXT\\") + \\": \\" + control.getText()); + rm.close(\\"div\\"); + } + +}; + +export default ExampleRenderer;", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/library.ts": Object { + "contents": "/*! + * \${copyright} + */ + +import ObjectPath from \\"sap/base/util/ObjectPath\\"; + +/** + * Initialization Code and shared classes of library com.sap.myui5tslib113. + */ + +// delegate further initialization of this library to the Core +// Hint: sap.ui.getCore() must still be used here to support preload with sync bootstrap! +sap.ui.getCore().initLibrary({ + name: \\"com.sap.myui5tslib113\\", + version: \\"\${version}\\", + dependencies: [ // keep in sync with the ui5.yaml and .library files + \\"sap.ui.core\\" + ], + types: [ + \\"com.sap.myui5tslib113.ExampleColor\\" + ], + interfaces: [], + controls: [ + \\"com.sap.myui5tslib113.Example\\" + ], + elements: [], + noLibraryCSS: false // if no CSS is provided, you can disable the library.css load here +}); + +// get the library object from global object space because all enums must be attached to it to be usable as UI5 types +// FIXME: this line is planned to become obsolete and may need to be removed later +const thisLib : {[key: string]: unknown} = ObjectPath.get(\\"com.sap.myui5tslib113\\") as {[key: string]: unknown}; + +/** + * Semantic Colors of the com.myorg.myUI5Library.Example control. + * + * @enum {string} + * @public + */ +export enum ExampleColor { + + /** + * Default color (brand color) + * @public + */ + Default = \\"Default\\", + + /** + * Highlight color + * @public + */ + Highlight = \\"Highlight\\" + +} +// FIXME: this line is planned to become obsolete and may need to be removed later +thisLib.ExampleColor = ExampleColor; // add the enum to the library; this is important because UI5 otherwise cannot identify the type and will skip type checking for properties of this type", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/messagebundle.properties": Object { + "contents": "# Translation file of library com.sap.myui5tslib113. +ANY_TEXT=AnyText +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/base/Example.less": Object { + "contents": "/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ + +.myLibPrefixExample, +.myLibPrefixExampleHighlight { + color: @sapUiText; + background-color: @sapUiNeutralBG; + border: 1rem solid @sapUiContentForegroundBorderColor; + border-radius: 1rem; + opacity: 0.8; + padding: 2rem; + margin: 2rem 8rem; + text-align: center; + font-size: 2em; + line-height: 3em; +} + +.myLibPrefixExampleHighlight { + background-color: @sapUiSuccessBG; +} +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/base/library.source.less": Object { + "contents": "@import \\"/resources/sap/ui/core/themes/base/base.less\\"; +@import \\"/resources/sap/ui/core/themes/base/global.less\\"; + +@import \\"Example.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_plus/library.source.less": Object { + "contents": "@import \\"../sap_belize/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_belize_plus/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_dark/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_hcb/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_hcw/library.source.less": Object { + "contents": "@import \\"../base/library.source.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less\\"; +@import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less\\"; +", + "state": "modified", + }, + "com.sap.myui5tslib113/test/com/sap/myui5tslib113/Example.html": Object { + "contents": " + + + + Test Page for com.sap.myui5tslib113.Example + + + + +

Test Page for com.sap.myui5tslib113.Example

+
+ + +", + "state": "modified", + }, + "com.sap.myui5tslib113/test/com/sap/myui5tslib113/Example.ts": Object { + "contents": "import { ExampleColor } from 'com/sap/myui5tslib113/library'; +import Example from 'com/sap/myui5tslib113/Example'; + +// Create a new instance of the Example control and +// place it into the DOM element with the id \\"content\\" +new Example({ + text: 'Example', + color: ExampleColor.Highlight, +}).placeAt('content'); +", + "state": "modified", + }, + "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/Example.qunit.ts": Object { + "contents": "// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +/*global QUnit */ +// eslint-disable-next-line no-undef +sap.ui.define([ + \\"sap/ui/qunit/QUnitUtils\\", + \\"sap/ui/qunit/utils/createAndAppendDiv\\", + \\"com/sap/myui5tslib113/library\\", + \\"com/sap/myui5tslib113/Example\\" +], function(qutils, createAndAppendDiv, library, Example) { + \\"use strict\\"; + + // refer to library types + const ExampleColor = library.ExampleColor; + + // prepare DOM + createAndAppendDiv(\\"uiArea1\\"); + + // module for basic checks + QUnit.module(\\"Example Tests\\"); + + // example sync test + QUnit.test(\\"Sync\\", function(assert) { + assert.expect(1); + assert.ok(true, \\"ok\\"); + }); + + // example async test + QUnit.test(\\"Async\\", function(assert) { + assert.expect(1); + return new Promise(function(resolve, reject) { + assert.ok(true, \\"ok\\"); + resolve(); + }); + }) + + // module for basic checks + QUnit.module(\\"Basic Control Checks\\"); + + // some basic control checks + QUnit.test(\\"Test get properties\\", function(assert) { + assert.expect(2); + const oExample = new Example({ + text: \\"Example\\" + }); + assert.equal(oExample.getText(), \\"Example\\", \\"Check text equals 'Example'\\"); + assert.equal(oExample.getColor(), ExampleColor.Default, \\"Check color equals 'Default'\\"); + }); + + // some basic eventing check + QUnit.test(\\"Test click event\\", function(assert) { + assert.expect(1); + const oExample = new Example(\\"example\\", { + text: \\"Example\\", + press: function() { + assert.ok(true, \\"Event has been fired!\\") + } + }).placeAt(\\"uiArea1\\"); + return new Promise(function(resolve, reject) { + setTimeout(function() { + qutils.triggerMouseEvent(\\"example\\", \\"click\\", 1, 1); + resolve(); + }, 100); + }); + }); + +}); +", + "state": "modified", + }, + "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/testsuite.qunit.html": Object { + "contents": " + + + + + + QUnit TestSuite for com.sap.myui5tslib113 + + + + + +", + "state": "modified", + }, + "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/testsuite.qunit.ts": Object { + "contents": "// eslint-disable-next-line no-undef +sap.ui.define(function() { + \\"use strict\\"; + + return { + name: \\"QUnit TestSuite for com.sap.myui5tslib113\\", + defaults: { + bootCore: true, + ui5: { + libs: \\"sap.ui.core,com.sap.myui5tslib113\\", + theme: \\"sap_fiori_3\\", + noConflict: true, + preload: \\"auto\\" + }, + qunit: { + version: 2, + reorder: false + }, + sinon: { + version: 4, + qunitBridge: true, + useFakeTimers: false + }, + module: \\"./{name}.qunit\\" + }, + tests: { + // test file for the Example control + Example: { + title: \\"QUnit Test for Example\\", + _alternativeTitle: \\"QUnit tests: com.sap.myui5tslib113.Example\\" + } + } + }; + +}); +", + "state": "modified", + }, + "com.sap.myui5tslib113/tsconfig.json": Object { + "contents": "{ + \\"compilerOptions\\": { + \\"target\\": \\"es2022\\", + \\"module\\": \\"es2022\\", + \\"moduleResolution\\": \\"node\\", + \\"skipLibCheck\\": true, + \\"allowJs\\": true, + \\"strict\\": true, + \\"strictPropertyInitialization\\": false, + \\"rootDir\\": \\"./\\", + \\"outDir\\": \\"./dist\\", + \\"baseUrl\\": \\"./\\", + \\"typeRoots\\": [ + \\"./node_modules/@types\\", + \\"./node_modules/@sapui5/types\\" + ], + \\"paths\\": { + \\"com/sap/myui5tslib113/*\\": [ + \\"./src/com/sap/myui5tslib113/*\\" + ] + } + }, + \\"include\\": [ + \\"./src/**/*\\", + \\"./test/**/*\\" + ] +}", + "state": "modified", + }, + "com.sap.myui5tslib113/ui5.yaml": Object { + "contents": "specVersion: '2.2' +metadata: + name: \\"myui5tslib113\\" +type: library +framework: + name: SAPUI5 + version: 1.113.0 + libraries: + - name: sap.ui.core + - name: themelib_sap_belize + - name: themelib_sap_fiori_3 +server: + customMiddleware: + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + path: src + port: 35729 + delay: 300 + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + configuration: + debug: true + transformModulesToUI5: + overridesToOverride: true + excludePatterns: + - /Component-preload.js +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + debug: true + transformModulesToUI5: + overridesToOverride: true +", + "state": "modified", + }, +} +`; + +exports[`Reuse lib templates Generate files for config: lib-ts 1`] = ` +Object { + "com.sap.myui5tslib/.eslintrc": Object { "contents": "{ \\"extends\\": \\"plugin:@sap-ux/eslint-plugin-fiori-tools/defaultTS\\", \\"root\\": true }", "state": "modified", }, - "com.sap.myui5tslib113/.gitignore": Object { + "com.sap.myui5tslib/.gitignore": Object { "contents": "node_modules/ dist/ .scp/ @@ -568,7 +2285,7 @@ archive.zip .*_mta_build_tmp", "state": "modified", }, - "com.sap.myui5tslib113/karma.conf.js": Object { + "com.sap.myui5tslib/karma.conf.js": Object { "contents": "// karma-ui5 usage: https://github.com/SAP/karma-ui5 module.exports = function (config) { config.set({ @@ -578,9 +2295,9 @@ module.exports = function (config) { };", "state": "modified", }, - "com.sap.myui5tslib113/package.json": Object { + "com.sap.myui5tslib/package.json": Object { "contents": "{ - \\"name\\": \\"myui5tslib113\\", + \\"name\\": \\"myui5tslib\\", \\"version\\": \\"1.0.0\\", \\"devDependencies\\": { \\"@ui5/cli\\": \\"^3.9.1\\", @@ -589,7 +2306,7 @@ module.exports = function (config) { \\"karma-cli\\": \\"^2.0.0\\", \\"karma-ui5\\": \\"^3.0.3\\", \\"@sap/ux-ui5-tooling\\": \\"1\\", - \\"@sapui5/types\\": \\"1.113.0\\", + \\"@sapui5/ts-types-esm\\": \\"1.102.19\\", \\"@typescript-eslint/eslint-plugin\\": \\"^7.1.1\\", \\"@typescript-eslint/parser\\": \\"^7.1.1\\", \\"@ui5/ts-interface-generator\\": \\"^0.8.1\\", @@ -601,8 +2318,8 @@ module.exports = function (config) { }, \\"scripts\\": { \\"build\\": \\"run-p -l build-app build-interface\\", - \\"start\\": \\"fiori run --open \\\\\\"test-resources/com/sap/myui5tslib113/Example.html\\\\\\"\\", - \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5tslib113/qunit/testsuite.qunit.html\\", + \\"start\\": \\"fiori run --open \\\\\\"test-resources/com/sap/myui5tslib/Example.html\\\\\\"\\", + \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5tslib/qunit/testsuite.qunit.html\\", \\"test\\": \\"karma start --browsers=ChromeHeadless --singleRun=true\\", \\"build-app\\": \\"ui5 build --config=ui5.yaml --clean-dest --dest dist\\", \\"build-interface\\": \\"npx @ui5/ts-interface-generator\\", @@ -613,15 +2330,15 @@ module.exports = function (config) { ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/.library": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/.library": Object { "contents": " - com.sap.myui5tslib113 - UX Tools + com.sap.myui5tslib + UI5 Lib Author \${version} \${copyright} - myui5tslib113 - Some description about myui5tslib113 + myui5tslib + Some description about myui5tslib sap.ui.core @@ -631,7 +2348,7 @@ module.exports = function (config) { ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/Example.gen.d.ts": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/Example.gen.d.ts": Object { "contents": "import { ExampleColor } from \\"./library\\"; import Event from \\"sap/ui/base/Event\\"; import { PropertyBindingInfo } from \\"sap/ui/base/ManagedObject\\"; @@ -667,12 +2384,12 @@ declare module \\"./Example\\" { }", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/Example.ts": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/Example.ts": Object { "contents": "/*! * \${copyright} */ -// Provides control com.sap.myui5tslib113.Example. +// Provides control com.sap.myui5tslib.Example. import Control from \\"sap/ui/core/Control\\"; import ExampleRenderer from \\"./ExampleRenderer\\"; import { ExampleColor } from \\"./library\\"; @@ -680,7 +2397,7 @@ import { ExampleColor } from \\"./library\\"; /** - * Constructor for a new com.sap.myui5tslib113.Example control. + * Constructor for a new com.sap.myui5tslib.Example control. * * Some class description goes here. * @extends Control @@ -690,7 +2407,7 @@ import { ExampleColor } from \\"./library\\"; * * @constructor * @public - * @name com.sap.myui5tslib113.Example + * @name com.sap.myui5tslib.Example */ export default class Example extends Control { @@ -700,7 +2417,7 @@ export default class Example extends Control { constructor(id?: string, settings?: $ExampleSettings) { super(id, settings); } static readonly metadata = { - library: \\"com.sap.myui5tslib113\\", + library: \\"com.sap.myui5tslib\\", properties: { /** * The text to display. @@ -714,7 +2431,7 @@ export default class Example extends Control { * The color to use (default to \\"Default\\" color). */ color: { - type: \\"com.sap.myui5tslib113.ExampleColor\\", + type: \\"com.sap.myui5tslib.ExampleColor\\", group: \\"Appearance\\", defaultValue: ExampleColor.Default } @@ -735,7 +2452,7 @@ export default class Example extends Control { }", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/ExampleRenderer.ts": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/ExampleRenderer.ts": Object { "contents": "/*! * \${copyright} */ @@ -761,7 +2478,7 @@ const ExampleRenderer = { */ render: function (rm: RenderManager, control: Example) { - const i18n = Core.getLibraryResourceBundle(\\"com.sap.myui5tslib113\\") as ResourceBundle; + const i18n = Core.getLibraryResourceBundle(\\"com.sap.myui5tslib\\") as ResourceBundle; rm.openStart(\\"div\\", control); if (control.getColor() === ExampleColor.Highlight) { @@ -779,7 +2496,7 @@ const ExampleRenderer = { export default ExampleRenderer;", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/library.ts": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/library.ts": Object { "contents": "/*! * \${copyright} */ @@ -787,23 +2504,23 @@ export default ExampleRenderer;", import ObjectPath from \\"sap/base/util/ObjectPath\\"; /** - * Initialization Code and shared classes of library com.sap.myui5tslib113. + * Initialization Code and shared classes of library com.sap.myui5tslib. */ // delegate further initialization of this library to the Core // Hint: sap.ui.getCore() must still be used here to support preload with sync bootstrap! sap.ui.getCore().initLibrary({ - name: \\"com.sap.myui5tslib113\\", + name: \\"com.sap.myui5tslib\\", version: \\"\${version}\\", dependencies: [ // keep in sync with the ui5.yaml and .library files \\"sap.ui.core\\" ], types: [ - \\"com.sap.myui5tslib113.ExampleColor\\" + \\"com.sap.myui5tslib.ExampleColor\\" ], interfaces: [], controls: [ - \\"com.sap.myui5tslib113.Example\\" + \\"com.sap.myui5tslib.Example\\" ], elements: [], noLibraryCSS: false // if no CSS is provided, you can disable the library.css load here @@ -811,7 +2528,7 @@ sap.ui.getCore().initLibrary({ // get the library object from global object space because all enums must be attached to it to be usable as UI5 types // FIXME: this line is planned to become obsolete and may need to be removed later -const thisLib : {[key: string]: unknown} = ObjectPath.get(\\"com.sap.myui5tslib113\\") as {[key: string]: unknown}; +const thisLib : {[key: string]: unknown} = ObjectPath.get(\\"com.sap.myui5tslib\\") as {[key: string]: unknown}; /** * Semantic Colors of the com.myorg.myUI5Library.Example control. @@ -838,13 +2555,13 @@ export enum ExampleColor { thisLib.ExampleColor = ExampleColor; // add the enum to the library; this is important because UI5 otherwise cannot identify the type and will skip type checking for properties of this type", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/messagebundle.properties": Object { - "contents": "# Translation file of library com.sap.myui5tslib113. + "com.sap.myui5tslib/src/com/sap/myui5tslib/messagebundle.properties": Object { + "contents": "# Translation file of library com.sap.myui5tslib. ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/base/Example.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/base/Example.less": Object { "contents": "/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ .myLibPrefixExample, @@ -867,7 +2584,7 @@ ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/base/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/base/library.source.less": Object { "contents": "@import \\"/resources/sap/ui/core/themes/base/base.less\\"; @import \\"/resources/sap/ui/core/themes/base/global.less\\"; @@ -875,89 +2592,89 @@ ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_hcb/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_hcb/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcb/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcb/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_hcw/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_hcw/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcw/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcw/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_belize_plus/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_plus/library.source.less": Object { "contents": "@import \\"../sap_belize/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_plus/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_plus/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_dark/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_dark/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_hcb/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_hcb/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/src/com/sap/myui5tslib113/themes/sap_fiori_3_hcw/library.source.less": Object { + "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_hcw/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib113/test/com/sap/myui5tslib113/Example.html": Object { + "com.sap.myui5tslib/test/com/sap/myui5tslib/Example.html": Object { "contents": " - Test Page for com.sap.myui5tslib113.Example + Test Page for com.sap.myui5tslib.Example -

Test Page for com.sap.myui5tslib113.Example

+

Test Page for com.sap.myui5tslib.Example

", "state": "modified", }, - "com.sap.myui5tslib113/test/com/sap/myui5tslib113/Example.ts": Object { - "contents": "import { ExampleColor } from 'com/sap/myui5tslib113/library'; -import Example from 'com/sap/myui5tslib113/Example'; + "com.sap.myui5tslib/test/com/sap/myui5tslib/Example.ts": Object { + "contents": "import { ExampleColor } from 'com/sap/myui5tslib/library'; +import Example from 'com/sap/myui5tslib/Example'; // Create a new instance of the Example control and // place it into the DOM element with the id \\"content\\" @@ -968,7 +2685,7 @@ new Example({ ", "state": "modified", }, - "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/Example.qunit.ts": Object { + "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/Example.qunit.ts": Object { "contents": "// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck @@ -977,8 +2694,8 @@ new Example({ sap.ui.define([ \\"sap/ui/qunit/QUnitUtils\\", \\"sap/ui/qunit/utils/createAndAppendDiv\\", - \\"com/sap/myui5tslib113/library\\", - \\"com/sap/myui5tslib113/Example\\" + \\"com/sap/myui5tslib/library\\", + \\"com/sap/myui5tslib/Example\\" ], function(qutils, createAndAppendDiv, library, Example) { \\"use strict\\"; @@ -1040,16 +2757,16 @@ sap.ui.define([ ", "state": "modified", }, - "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/testsuite.qunit.html": Object { + "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/testsuite.qunit.html": Object { "contents": " - QUnit TestSuite for com.sap.myui5tslib113 + QUnit TestSuite for com.sap.myui5tslib + data-sap-ui-testsuite=\\"test-resources/com/sap/myui5tslib/qunit/testsuite.qunit\\"> @@ -1057,17 +2774,17 @@ sap.ui.define([ ", "state": "modified", }, - "com.sap.myui5tslib113/test/com/sap/myui5tslib113/qunit/testsuite.qunit.ts": Object { + "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/testsuite.qunit.ts": Object { "contents": "// eslint-disable-next-line no-undef sap.ui.define(function() { \\"use strict\\"; return { - name: \\"QUnit TestSuite for com.sap.myui5tslib113\\", + name: \\"QUnit TestSuite for com.sap.myui5tslib\\", defaults: { bootCore: true, ui5: { - libs: \\"sap.ui.core,com.sap.myui5tslib113\\", + libs: \\"sap.ui.core,com.sap.myui5tslib\\", theme: \\"sap_fiori_3\\", noConflict: true, preload: \\"auto\\" @@ -1087,7 +2804,7 @@ sap.ui.define(function() { // test file for the Example control Example: { title: \\"QUnit Test for Example\\", - _alternativeTitle: \\"QUnit tests: com.sap.myui5tslib113.Example\\" + _alternativeTitle: \\"QUnit tests: com.sap.myui5tslib.Example\\" } } }; @@ -1096,7 +2813,7 @@ sap.ui.define(function() { ", "state": "modified", }, - "com.sap.myui5tslib113/tsconfig.json": Object { + "com.sap.myui5tslib/tsconfig.json": Object { "contents": "{ \\"compilerOptions\\": { \\"target\\": \\"es2022\\", @@ -1111,11 +2828,11 @@ sap.ui.define(function() { \\"baseUrl\\": \\"./\\", \\"typeRoots\\": [ \\"./node_modules/@types\\", - \\"./node_modules/@sapui5/types\\" + \\"./node_modules/@sapui5/ts-types-esm\\" ], \\"paths\\": { - \\"com/sap/myui5tslib113/*\\": [ - \\"./src/com/sap/myui5tslib113/*\\" + \\"com/sap/myui5tslib/*\\": [ + \\"./src/com/sap/myui5tslib/*\\" ] } }, @@ -1126,14 +2843,14 @@ sap.ui.define(function() { }", "state": "modified", }, - "com.sap.myui5tslib113/ui5.yaml": Object { + "com.sap.myui5tslib/ui5.yaml": Object { "contents": "specVersion: '2.2' metadata: - name: \\"myui5tslib113\\" + name: \\"myui5tslib\\" type: library framework: name: SAPUI5 - version: 1.113.0 + version: 1.102.19 libraries: - name: sap.ui.core - name: themelib_sap_belize @@ -1168,16 +2885,16 @@ builder: } `; -exports[`Reuse lib templates Generate files for config: lib-ts 1`] = ` +exports[`Reuse lib templates Generate files for config: lib-ts-1.121.0 1`] = ` Object { - "com.sap.myui5tslib/.eslintrc": Object { + "com.sap.myui5tslib121/.eslintrc": Object { "contents": "{ \\"extends\\": \\"plugin:@sap-ux/eslint-plugin-fiori-tools/defaultTS\\", \\"root\\": true }", "state": "modified", }, - "com.sap.myui5tslib/.gitignore": Object { + "com.sap.myui5tslib121/.gitignore": Object { "contents": "node_modules/ dist/ .scp/ @@ -1189,7 +2906,7 @@ archive.zip .*_mta_build_tmp", "state": "modified", }, - "com.sap.myui5tslib/karma.conf.js": Object { + "com.sap.myui5tslib121/karma.conf.js": Object { "contents": "// karma-ui5 usage: https://github.com/SAP/karma-ui5 module.exports = function (config) { config.set({ @@ -1199,9 +2916,9 @@ module.exports = function (config) { };", "state": "modified", }, - "com.sap.myui5tslib/package.json": Object { + "com.sap.myui5tslib121/package.json": Object { "contents": "{ - \\"name\\": \\"myui5tslib\\", + \\"name\\": \\"myui5tslib121\\", \\"version\\": \\"1.0.0\\", \\"devDependencies\\": { \\"@ui5/cli\\": \\"^3.9.1\\", @@ -1210,7 +2927,7 @@ module.exports = function (config) { \\"karma-cli\\": \\"^2.0.0\\", \\"karma-ui5\\": \\"^3.0.3\\", \\"@sap/ux-ui5-tooling\\": \\"1\\", - \\"@sapui5/ts-types-esm\\": \\"1.102.19\\", + \\"@sapui5/types\\": \\"1.121.0\\", \\"@typescript-eslint/eslint-plugin\\": \\"^7.1.1\\", \\"@typescript-eslint/parser\\": \\"^7.1.1\\", \\"@ui5/ts-interface-generator\\": \\"^0.8.1\\", @@ -1222,8 +2939,8 @@ module.exports = function (config) { }, \\"scripts\\": { \\"build\\": \\"run-p -l build-app build-interface\\", - \\"start\\": \\"fiori run --open \\\\\\"test-resources/com/sap/myui5tslib/Example.html\\\\\\"\\", - \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5tslib/qunit/testsuite.qunit.html\\", + \\"start\\": \\"fiori run --open \\\\\\"test-resources/com/sap/myui5tslib121/Example.html\\\\\\"\\", + \\"testsuite\\": \\"fiori run --open test-resources/com/sap/myui5tslib121/qunit/testsuite.qunit.html\\", \\"test\\": \\"karma start --browsers=ChromeHeadless --singleRun=true\\", \\"build-app\\": \\"ui5 build --config=ui5.yaml --clean-dest --dest dist\\", \\"build-interface\\": \\"npx @ui5/ts-interface-generator\\", @@ -1234,15 +2951,15 @@ module.exports = function (config) { ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/.library": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/.library": Object { "contents": " - com.sap.myui5tslib + com.sap.myui5tslib121 UI5 Lib Author \${version} \${copyright} - myui5tslib - Some description about myui5tslib + myui5tslib121 + Some description about myui5tslib121 sap.ui.core @@ -1252,7 +2969,7 @@ module.exports = function (config) { ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/Example.gen.d.ts": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/Example.gen.d.ts": Object { "contents": "import { ExampleColor } from \\"./library\\"; import Event from \\"sap/ui/base/Event\\"; import { PropertyBindingInfo } from \\"sap/ui/base/ManagedObject\\"; @@ -1288,12 +3005,12 @@ declare module \\"./Example\\" { }", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/Example.ts": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/Example.ts": Object { "contents": "/*! * \${copyright} */ -// Provides control com.sap.myui5tslib.Example. +// Provides control com.sap.myui5tslib121.Example. import Control from \\"sap/ui/core/Control\\"; import ExampleRenderer from \\"./ExampleRenderer\\"; import { ExampleColor } from \\"./library\\"; @@ -1301,7 +3018,7 @@ import { ExampleColor } from \\"./library\\"; /** - * Constructor for a new com.sap.myui5tslib.Example control. + * Constructor for a new com.sap.myui5tslib121.Example control. * * Some class description goes here. * @extends Control @@ -1311,7 +3028,7 @@ import { ExampleColor } from \\"./library\\"; * * @constructor * @public - * @name com.sap.myui5tslib.Example + * @name com.sap.myui5tslib121.Example */ export default class Example extends Control { @@ -1321,7 +3038,7 @@ export default class Example extends Control { constructor(id?: string, settings?: $ExampleSettings) { super(id, settings); } static readonly metadata = { - library: \\"com.sap.myui5tslib\\", + library: \\"com.sap.myui5tslib121\\", properties: { /** * The text to display. @@ -1335,7 +3052,7 @@ export default class Example extends Control { * The color to use (default to \\"Default\\" color). */ color: { - type: \\"com.sap.myui5tslib.ExampleColor\\", + type: \\"com.sap.myui5tslib121.ExampleColor\\", group: \\"Appearance\\", defaultValue: ExampleColor.Default } @@ -1356,13 +3073,13 @@ export default class Example extends Control { }", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/ExampleRenderer.ts": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/ExampleRenderer.ts": Object { "contents": "/*! * \${copyright} */ import ResourceBundle from \\"sap/base/i18n/ResourceBundle\\"; -import Core from \\"sap/ui/core/Core\\"; +import Lib from \\"sap/ui/core/Lib\\"; import RenderManager from \\"sap/ui/core/RenderManager\\"; import Example from \\"./Example\\"; import { ExampleColor } from \\"./library\\"; @@ -1382,7 +3099,7 @@ const ExampleRenderer = { */ render: function (rm: RenderManager, control: Example) { - const i18n = Core.getLibraryResourceBundle(\\"com.sap.myui5tslib\\") as ResourceBundle; + const i18n = Lib.getResourceBundleFor(\\"com.sap.myui5tslib121\\") as ResourceBundle; rm.openStart(\\"div\\", control); if (control.getColor() === ExampleColor.Highlight) { @@ -1400,39 +3117,41 @@ const ExampleRenderer = { export default ExampleRenderer;", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/library.ts": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/library.ts": Object { "contents": "/*! * \${copyright} */ import ObjectPath from \\"sap/base/util/ObjectPath\\"; +import Lib from \\"sap/ui/core/Lib\\"; /** - * Initialization Code and shared classes of library com.sap.myui5tslib. + * Initialization Code and shared classes of library com.sap.myui5tslib121. */ // delegate further initialization of this library to the Core // Hint: sap.ui.getCore() must still be used here to support preload with sync bootstrap! -sap.ui.getCore().initLibrary({ - name: \\"com.sap.myui5tslib\\", +Lib.init({ + name: \\"com.sap.myui5tslib121\\", version: \\"\${version}\\", dependencies: [ // keep in sync with the ui5.yaml and .library files \\"sap.ui.core\\" ], types: [ - \\"com.sap.myui5tslib.ExampleColor\\" + \\"com.sap.myui5tslib121.ExampleColor\\" ], interfaces: [], controls: [ - \\"com.sap.myui5tslib.Example\\" + \\"com.sap.myui5tslib121.Example\\" ], elements: [], - noLibraryCSS: false // if no CSS is provided, you can disable the library.css load here + noLibraryCSS: false, // if no CSS is provided, you can disable the library.css load here + apiVersion: 2, }); // get the library object from global object space because all enums must be attached to it to be usable as UI5 types // FIXME: this line is planned to become obsolete and may need to be removed later -const thisLib : {[key: string]: unknown} = ObjectPath.get(\\"com.sap.myui5tslib\\") as {[key: string]: unknown}; +const thisLib : {[key: string]: unknown} = ObjectPath.get(\\"com.sap.myui5tslib121\\") as {[key: string]: unknown}; /** * Semantic Colors of the com.myorg.myUI5Library.Example control. @@ -1459,13 +3178,13 @@ export enum ExampleColor { thisLib.ExampleColor = ExampleColor; // add the enum to the library; this is important because UI5 otherwise cannot identify the type and will skip type checking for properties of this type", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/messagebundle.properties": Object { - "contents": "# Translation file of library com.sap.myui5tslib. + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/messagebundle.properties": Object { + "contents": "# Translation file of library com.sap.myui5tslib121. ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/base/Example.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/base/Example.less": Object { "contents": "/* Theme Parameter Toolbox: https://sdk.openui5.org/test-resources/sap/m/demokit/theming/webapp/index.html */ .myLibPrefixExample, @@ -1488,7 +3207,7 @@ ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/base/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/base/library.source.less": Object { "contents": "@import \\"/resources/sap/ui/core/themes/base/base.less\\"; @import \\"/resources/sap/ui/core/themes/base/global.less\\"; @@ -1496,89 +3215,90 @@ ANY_TEXT=AnyText ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_belize/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_hcb/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_belize_hcb/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcb/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcb/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_hcw/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_belize_hcw/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcw/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_hcw/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_belize_plus/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_belize_plus/library.source.less": Object { "contents": "@import \\"../sap_belize/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_plus/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_belize_plus/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_fiori_3/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_dark/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_fiori_3_dark/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_dark/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_hcb/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_fiori_3_hcb/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcb/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/src/com/sap/myui5tslib/themes/sap_fiori_3_hcw/library.source.less": Object { + "com.sap.myui5tslib121/src/com/sap/myui5tslib121/themes/sap_fiori_3_hcw/library.source.less": Object { "contents": "@import \\"../base/library.source.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/base.less\\"; @import \\"/resources/sap/ui/core/themes/sap_fiori_3_hcw/global.less\\"; ", "state": "modified", }, - "com.sap.myui5tslib/test/com/sap/myui5tslib/Example.html": Object { + "com.sap.myui5tslib121/test/com/sap/myui5tslib121/Example.html": Object { "contents": " - Test Page for com.sap.myui5tslib.Example + Test Page for com.sap.myui5tslib121.Example -

Test Page for com.sap.myui5tslib.Example

+

Test Page for com.sap.myui5tslib121.Example

", "state": "modified", }, - "com.sap.myui5tslib/test/com/sap/myui5tslib/Example.ts": Object { - "contents": "import { ExampleColor } from 'com/sap/myui5tslib/library'; -import Example from 'com/sap/myui5tslib/Example'; + "com.sap.myui5tslib121/test/com/sap/myui5tslib121/Example.ts": Object { + "contents": "import { ExampleColor } from 'com/sap/myui5tslib121/library'; +import Example from 'com/sap/myui5tslib121/Example'; // Create a new instance of the Example control and // place it into the DOM element with the id \\"content\\" @@ -1589,7 +3309,7 @@ new Example({ ", "state": "modified", }, - "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/Example.qunit.ts": Object { + "com.sap.myui5tslib121/test/com/sap/myui5tslib121/qunit/Example.qunit.ts": Object { "contents": "// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck @@ -1598,8 +3318,8 @@ new Example({ sap.ui.define([ \\"sap/ui/qunit/QUnitUtils\\", \\"sap/ui/qunit/utils/createAndAppendDiv\\", - \\"com/sap/myui5tslib/library\\", - \\"com/sap/myui5tslib/Example\\" + \\"com/sap/myui5tslib121/library\\", + \\"com/sap/myui5tslib121/Example\\" ], function(qutils, createAndAppendDiv, library, Example) { \\"use strict\\"; @@ -1661,16 +3381,16 @@ sap.ui.define([ ", "state": "modified", }, - "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/testsuite.qunit.html": Object { + "com.sap.myui5tslib121/test/com/sap/myui5tslib121/qunit/testsuite.qunit.html": Object { "contents": " - QUnit TestSuite for com.sap.myui5tslib + QUnit TestSuite for com.sap.myui5tslib121 + data-sap-ui-testsuite=\\"test-resources/com/sap/myui5tslib121/qunit/testsuite.qunit\\"> @@ -1678,17 +3398,17 @@ sap.ui.define([ ", "state": "modified", }, - "com.sap.myui5tslib/test/com/sap/myui5tslib/qunit/testsuite.qunit.ts": Object { + "com.sap.myui5tslib121/test/com/sap/myui5tslib121/qunit/testsuite.qunit.ts": Object { "contents": "// eslint-disable-next-line no-undef sap.ui.define(function() { \\"use strict\\"; return { - name: \\"QUnit TestSuite for com.sap.myui5tslib\\", + name: \\"QUnit TestSuite for com.sap.myui5tslib121\\", defaults: { bootCore: true, ui5: { - libs: \\"sap.ui.core,com.sap.myui5tslib\\", + libs: \\"sap.ui.core,com.sap.myui5tslib121\\", theme: \\"sap_fiori_3\\", noConflict: true, preload: \\"auto\\" @@ -1708,7 +3428,7 @@ sap.ui.define(function() { // test file for the Example control Example: { title: \\"QUnit Test for Example\\", - _alternativeTitle: \\"QUnit tests: com.sap.myui5tslib.Example\\" + _alternativeTitle: \\"QUnit tests: com.sap.myui5tslib121.Example\\" } } }; @@ -1717,7 +3437,7 @@ sap.ui.define(function() { ", "state": "modified", }, - "com.sap.myui5tslib/tsconfig.json": Object { + "com.sap.myui5tslib121/tsconfig.json": Object { "contents": "{ \\"compilerOptions\\": { \\"target\\": \\"es2022\\", @@ -1732,11 +3452,11 @@ sap.ui.define(function() { \\"baseUrl\\": \\"./\\", \\"typeRoots\\": [ \\"./node_modules/@types\\", - \\"./node_modules/@sapui5/ts-types-esm\\" + \\"./node_modules/@sapui5/types\\" ], \\"paths\\": { - \\"com/sap/myui5tslib/*\\": [ - \\"./src/com/sap/myui5tslib/*\\" + \\"com/sap/myui5tslib121/*\\": [ + \\"./src/com/sap/myui5tslib121/*\\" ] } }, @@ -1747,17 +3467,16 @@ sap.ui.define(function() { }", "state": "modified", }, - "com.sap.myui5tslib/ui5.yaml": Object { + "com.sap.myui5tslib121/ui5.yaml": Object { "contents": "specVersion: '2.2' metadata: - name: \\"myui5tslib\\" + name: \\"myui5tslib121\\" type: library framework: name: SAPUI5 - version: 1.102.19 + version: 1.121.0 libraries: - name: sap.ui.core - - name: themelib_sap_belize - name: themelib_sap_fiori_3 server: customMiddleware: diff --git a/packages/ui5-library-writer/test/common.ts b/packages/ui5-library-writer/test/common.ts index 33b33bc751..84dfc0e163 100644 --- a/packages/ui5-library-writer/test/common.ts +++ b/packages/ui5-library-writer/test/common.ts @@ -3,6 +3,7 @@ import { exec as execCP } from 'child_process'; import { promisify } from 'util'; import type { UI5LibConfig } from '../src/types'; import type { Editor } from 'mem-fs-editor'; +import { compareUI5VersionGte, ui5LtsVersion_1_120 } from '../src/utils'; const exec = promisify(execCP); @@ -22,6 +23,7 @@ export function prepareDebug(): { enabled: boolean; debugFull: boolean } { export const projectChecks = async (rootPath: string, config: UI5LibConfig, debugFull = false): Promise => { // Do additional checks on generated projects const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx'; let npmResult; try { if (debugFull) { @@ -42,6 +44,13 @@ export const projectChecks = async (rootPath: string, config: UI5LibConfig, debu npmResult = await exec(`${npm} run lint`, { cwd: rootPath }); console.log('stdout:', npmResult.stdout); console.log('stderr:', npmResult.stderr); + + // UI5 linter for UI5 1.120.0 and above + if (config.frameworkVersion && compareUI5VersionGte(config.frameworkVersion, ui5LtsVersion_1_120)) { + npmResult = await exec(`${npx} --yes @ui5/linter@latest`, { cwd: rootPath }); + console.log('stdout:', npmResult.stdout); + console.log('stderr:', npmResult.stderr); + } } } } catch (error) { diff --git a/packages/ui5-library-writer/test/index.test.ts b/packages/ui5-library-writer/test/index.test.ts index a3e37c5576..6a7249d2b4 100644 --- a/packages/ui5-library-writer/test/index.test.ts +++ b/packages/ui5-library-writer/test/index.test.ts @@ -22,6 +22,7 @@ describe('Reuse lib templates', () => { typescript: false }; const V1_113_0 = '1.113.0'; + const V1_121_0 = '1.121.0'; const configuration = [ { name: 'lib-js', @@ -44,6 +45,28 @@ describe('Reuse lib templates', () => { frameworkVersion: V1_113_0, author: undefined // to test default is added instead } + }, + { + name: 'lib-js-1.121.0', + config: { ...ui5LibConfig, libraryName: 'myui5jslib121', frameworkVersion: V1_121_0 } + }, + { + name: 'lib-ts-1.121.0', + config: { + ...ui5LibConfig, + libraryName: 'myui5tslib121', + frameworkVersion: V1_121_0, + typescript: true + } + }, + { + name: 'lib-js-latest', + config: { + ...ui5LibConfig, + libraryName: 'myui5jsliblatest', + frameworkVersion: '', + typescript: false + } } ]; @@ -58,7 +81,7 @@ describe('Reuse lib templates', () => { const pkgData = fs.read(join(testOutputDir, projectFolder, 'package.json')); const packageJson = JSON.parse(pkgData); if (config.typescript === true) { - if (config.frameworkVersion === V1_113_0) { + if (config.frameworkVersion === V1_113_0 || config.frameworkVersion === V1_121_0) { expect(packageJson.devDependencies).toHaveProperty('@sapui5/types'); } else { expect(packageJson.devDependencies).toHaveProperty('@sapui5/ts-types-esm'); diff --git a/packages/ui5-library-writer/tsconfig.json b/packages/ui5-library-writer/tsconfig.json index 57ab8d2ff2..fe67e3dbac 100644 --- a/packages/ui5-library-writer/tsconfig.json +++ b/packages/ui5-library-writer/tsconfig.json @@ -16,9 +16,6 @@ { "path": "../project-access" }, - { - "path": "../ui5-application-writer" - }, { "path": "../ui5-config" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ca99d90ba..a972173ddb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -168,7 +168,7 @@ importers: version: link:../../packages/system-access yeoman-generator: specifier: 5.10.0 - version: 5.10.0(mem-fs@2.1.0)(yeoman-environment@3.19.3) + version: 5.10.0 devDependencies: '@sap-ux/odata-service-writer': specifier: workspace:* @@ -495,6 +495,9 @@ importers: '@sap-ux/logger': specifier: workspace:* version: link:../logger + '@sap-ux/odata-service-writer': + specifier: workspace:* + version: link:../odata-service-writer '@sap-ux/project-access': specifier: workspace:* version: link:../project-access @@ -656,6 +659,9 @@ importers: prompts: specifier: 2.4.2 version: 2.4.2 + semver: + specifier: 7.6.3 + version: 7.6.3 devDependencies: '@sap-ux/preview-middleware': specifier: workspace:* @@ -672,6 +678,9 @@ importers: '@types/prompts': specifier: 2.4.4 version: 2.4.4 + '@types/semver': + specifier: 7.5.8 + version: 7.5.8 axios: specifier: 1.7.4 version: 1.7.4 @@ -1318,7 +1327,7 @@ importers: version: 20.6.1 yeoman-generator: specifier: 5.10.0 - version: 5.10.0(mem-fs@2.1.0)(yeoman-environment@3.19.3) + version: 5.10.0 devDependencies: '@sap-ux/axios-extension': specifier: workspace:* @@ -2658,6 +2667,15 @@ importers: specifier: 4.0.2 version: 4.0.2(jest@29.7.0) + packages/project-integrity: + dependencies: + '@sap-ux/project-access': + specifier: workspace:* + version: link:../project-access + lz-string: + specifier: 1.5.0 + version: 1.5.0 + packages/reload-middleware: dependencies: '@sap-ux/btp-utils': @@ -3484,9 +3502,6 @@ importers: '@sap-ux/project-access': specifier: workspace:* version: link:../project-access - '@sap-ux/ui5-application-writer': - specifier: workspace:* - version: link:../ui5-application-writer '@sap-ux/ui5-config': specifier: workspace:* version: link:../ui5-config @@ -6219,7 +6234,7 @@ packages: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.5.4 + semver: 7.6.3 dev: true /@changesets/assemble-release-plan@6.0.5: @@ -6230,7 +6245,7 @@ packages: '@changesets/should-skip-package': 0.1.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - semver: 7.5.4 + semver: 7.6.3 dev: true /@changesets/changelog-git@0.2.0: @@ -6268,7 +6283,7 @@ packages: package-manager-detector: 0.2.2 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.5.4 + semver: 7.6.3 spawndamnit: 3.0.1 term-size: 2.2.1 dev: true @@ -6297,7 +6312,7 @@ packages: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.5.4 + semver: 7.6.3 dev: true /@changesets/get-release-plan@4.0.5: @@ -7515,7 +7530,7 @@ packages: read-package-json-fast: 2.0.3 readdir-scoped-modules: 1.1.0 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.3 ssri: 8.0.1 treeverse: 1.0.4 walk-up-path: 1.0.0 @@ -7533,7 +7548,7 @@ packages: nopt: 7.2.0 proc-log: 3.0.0 read-package-json-fast: 3.0.2 - semver: 7.5.4 + semver: 7.6.3 walk-up-path: 3.0.1 dev: true @@ -7541,20 +7556,20 @@ packages: resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} dependencies: '@gar/promisify': 1.1.3 - semver: 7.5.4 + semver: 7.6.3 /@npmcli/fs@2.1.2: resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: '@gar/promisify': 1.1.3 - semver: 7.5.4 + semver: 7.6.3 /@npmcli/fs@3.1.0: resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - semver: 7.5.4 + semver: 7.6.3 /@npmcli/git@2.1.0: resolution: {integrity: sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==} @@ -7565,7 +7580,7 @@ packages: npm-pick-manifest: 6.1.1 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.5.4 + semver: 7.6.3 which: 2.0.2 transitivePeerDependencies: - bluebird @@ -7580,7 +7595,7 @@ packages: proc-log: 3.0.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.5.4 + semver: 7.6.3 which: 3.0.1 transitivePeerDependencies: - bluebird @@ -7595,7 +7610,7 @@ packages: proc-log: 3.0.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.5.4 + semver: 7.6.3 which: 4.0.0 transitivePeerDependencies: - bluebird @@ -7643,7 +7658,7 @@ packages: cacache: 15.3.0 json-parse-even-better-errors: 2.3.1 pacote: 12.0.3 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - bluebird - supports-color @@ -7960,7 +7975,7 @@ packages: '@types/shimmer': 1.0.5 import-in-the-middle: 1.4.2 require-in-the-middle: 7.2.0 - semver: 7.5.4 + semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -8369,7 +8384,7 @@ packages: dependencies: '@storybook/core-webpack': 8.4.2(storybook@8.4.2) '@types/node': 22.9.0 - '@types/semver': 7.5.4 + '@types/semver': 7.5.8 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.3.1 @@ -8381,7 +8396,7 @@ packages: magic-string: 0.30.11 path-browserify: 1.0.1 process: 0.11.10 - semver: 7.5.4 + semver: 7.6.3 storybook: 8.4.2(prettier@2.8.8) style-loader: 3.3.3(webpack@5.96.1) terser-webpack-plugin: 5.3.10(esbuild@0.19.2)(webpack@5.96.1) @@ -8530,14 +8545,14 @@ packages: '@storybook/react': 8.4.2(react-dom@16.14.0)(react@16.14.0)(storybook@8.4.2)(typescript@5.6.2) '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.96.1) '@types/node': 22.9.0 - '@types/semver': 7.5.4 + '@types/semver': 7.5.8 find-up: 5.0.0 magic-string: 0.30.11 react: 16.14.0 react-docgen: 7.0.3 react-dom: 16.14.0(react@16.14.0) resolve: 1.22.8 - semver: 7.5.4 + semver: 7.6.3 storybook: 8.4.2(prettier@2.8.8) tsconfig-paths: 4.2.0 typescript: 5.6.2 @@ -9361,6 +9376,10 @@ packages: /@types/semver@7.5.4: resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} + dev: true + + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} /@types/send@0.17.1: resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} @@ -9608,7 +9627,7 @@ packages: graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 - semver: 7.5.4 + semver: 7.6.3 ts-api-utils: 1.2.1(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: @@ -9790,7 +9809,7 @@ packages: debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + semver: 7.6.3 tsutils: 3.21.0(typescript@5.6.2) typescript: 5.6.2 transitivePeerDependencies: @@ -9811,7 +9830,7 @@ packages: debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + semver: 7.6.3 tsutils: 3.21.0(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: @@ -9855,7 +9874,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.5.4 + semver: 7.6.3 ts-api-utils: 1.2.1(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: @@ -9870,13 +9889,13 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.4 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.2) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -9890,13 +9909,13 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.4 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -9926,12 +9945,12 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.4 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 7.2.0 '@typescript-eslint/types': 7.2.0 '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.6.3) eslint: 8.57.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -9977,7 +9996,7 @@ packages: less-openui5: 0.11.6 pretty-data: 0.40.0 rimraf: 5.0.5 - semver: 7.5.4 + semver: 7.6.3 terser: 5.32.0 workerpool: 6.5.1 xml2js: 0.6.2 @@ -9999,7 +10018,7 @@ packages: js-yaml: 4.1.0 open: 9.1.0 pretty-hrtime: 1.0.3 - semver: 7.5.4 + semver: 7.6.3 update-notifier: 6.0.2 yargs: 17.7.2 transitivePeerDependencies: @@ -10073,7 +10092,7 @@ packages: read-pkg-up: 10.1.0 resolve: 1.22.8 rimraf: 5.0.5 - semver: 7.5.4 + semver: 7.6.3 xml2js: 0.6.2 yesno: 0.4.0 transitivePeerDependencies: @@ -11429,7 +11448,7 @@ packages: /builtins@5.0.1: resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} dependencies: - semver: 7.5.4 + semver: 7.6.3 /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} @@ -12623,7 +12642,7 @@ packages: /diagnostic-channel@1.1.1: resolution: {integrity: sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==} dependencies: - semver: 7.5.4 + semver: 7.6.3 dev: false /diff-sequences@29.6.3: @@ -13665,7 +13684,7 @@ packages: eslint: 8.56.0 esquery: 1.6.0 is-builtin-module: 3.2.1 - semver: 7.5.4 + semver: 7.6.3 spdx-expression-parse: 4.0.0 transitivePeerDependencies: - supports-color @@ -14360,7 +14379,7 @@ packages: minimatch: 3.0.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.5.4 + semver: 7.6.3 tapable: 2.2.1 typescript: 5.6.2 webpack: 5.96.1(esbuild@0.19.2) @@ -15898,7 +15917,7 @@ packages: '@babel/parser': 7.25.3 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: true @@ -16370,7 +16389,7 @@ packages: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: true @@ -16956,7 +16975,7 @@ packages: /lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - dev: true + hasBin: true /macos-release@2.5.1: resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} @@ -16991,7 +17010,7 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} dependencies: - semver: 7.5.4 + semver: 7.6.3 dev: true /make-error@1.3.6: @@ -17609,7 +17628,7 @@ packages: engines: {node: '>=10'} requiresBuild: true dependencies: - semver: 7.5.4 + semver: 7.6.3 dev: true /node-abort-controller@3.1.1: @@ -17639,7 +17658,7 @@ packages: make-fetch-happen: 13.0.0 nopt: 7.2.0 proc-log: 3.0.0 - semver: 7.5.4 + semver: 7.6.3 tar: 6.2.1 which: 4.0.0 transitivePeerDependencies: @@ -17658,7 +17677,7 @@ packages: nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.3 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -17678,7 +17697,7 @@ packages: nopt: 6.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.3 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -17751,7 +17770,7 @@ packages: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.15.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-license: 3.0.4 /normalize-package-data@6.0.0: @@ -17760,7 +17779,7 @@ packages: dependencies: hosted-git-info: 7.0.0 is-core-module: 2.15.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-license: 3.0.4 dev: true @@ -17793,13 +17812,13 @@ packages: resolution: {integrity: sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==} engines: {node: '>=10'} dependencies: - semver: 7.5.4 + semver: 7.6.3 /npm-install-checks@6.1.1: resolution: {integrity: sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - semver: 7.5.4 + semver: 7.6.3 /npm-normalize-package-bin@1.0.1: resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} @@ -17818,7 +17837,7 @@ packages: dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-name: 5.0.0 /npm-package-arg@11.0.1: @@ -17827,7 +17846,7 @@ packages: dependencies: hosted-git-info: 7.0.0 proc-log: 3.0.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-name: 5.0.0 dev: true @@ -17836,7 +17855,7 @@ packages: engines: {node: '>=10'} dependencies: hosted-git-info: 4.1.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-name: 3.0.0 /npm-packlist@3.0.0: @@ -17868,7 +17887,7 @@ packages: npm-install-checks: 4.0.0 npm-normalize-package-bin: 1.0.1 npm-package-arg: 8.1.5 - semver: 7.5.4 + semver: 7.6.3 /npm-pick-manifest@8.0.1: resolution: {integrity: sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==} @@ -17877,7 +17896,7 @@ packages: npm-install-checks: 6.1.1 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 - semver: 7.5.4 + semver: 7.6.3 /npm-pick-manifest@9.0.0: resolution: {integrity: sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==} @@ -17886,7 +17905,7 @@ packages: npm-install-checks: 6.1.1 npm-normalize-package-bin: 3.0.1 npm-package-arg: 11.0.1 - semver: 7.5.4 + semver: 7.6.3 dev: true /npm-registry-fetch@12.0.2: @@ -18056,7 +18075,7 @@ packages: npm-run-path: 4.0.1 open: 8.4.2 ora: 5.3.0 - semver: 7.5.4 + semver: 7.6.3 string-width: 4.2.3 strong-log-transformer: 2.1.0 tar-stream: 2.2.0 @@ -18495,7 +18514,7 @@ packages: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 - semver: 7.5.4 + semver: 7.6.3 dev: true /package-manager-detector@0.2.2: @@ -20214,7 +20233,7 @@ packages: resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} engines: {node: '>=12'} dependencies: - semver: 7.5.4 + semver: 7.6.3 dev: true /semver@5.7.2: @@ -20244,7 +20263,6 @@ packages: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true - dev: true /send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} @@ -20983,7 +21001,7 @@ packages: methods: 1.1.2 mime: 2.6.0 qs: 6.11.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: true @@ -21324,7 +21342,7 @@ packages: json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.5.4 + semver: 7.6.3 typescript: 5.6.2 yargs-parser: 21.1.1 dev: true @@ -21359,7 +21377,7 @@ packages: json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.5.4 + semver: 7.6.3 typescript: 5.6.3 yargs-parser: 21.1.1 dev: true @@ -21958,7 +21976,7 @@ packages: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.1.0 - semver: 7.5.4 + semver: 7.6.3 semver-diff: 4.0.0 xdg-basedir: 5.1.0 dev: true @@ -22711,7 +22729,7 @@ packages: preferred-pm: 3.1.4 pretty-bytes: 5.6.0 readable-stream: 4.5.2 - semver: 7.5.4 + semver: 7.6.3 slash: 3.0.0 strip-ansi: 6.0.1 text-table: 0.2.0 @@ -22721,7 +22739,7 @@ packages: - bluebird - supports-color - /yeoman-generator@5.10.0(mem-fs@2.1.0)(yeoman-environment@3.19.3): + /yeoman-generator@5.10.0: resolution: {integrity: sha512-iDUKykV7L4nDNzeYSedRmSeJ5eMYFucnKDi6KN1WNASXErgPepKqsQw55TgXPHnmpcyOh2Dd/LAZkyc+f0qaAw==} engines: {node: '>=12.10.0'} peerDependencies: @@ -22745,6 +22763,37 @@ packages: shelljs: 0.8.5 sort-keys: 4.2.0 text-table: 0.2.0 + transitivePeerDependencies: + - bluebird + - encoding + - mem-fs + - supports-color + dev: false + + /yeoman-generator@5.10.0(mem-fs@2.1.0)(yeoman-environment@3.19.3): + resolution: {integrity: sha512-iDUKykV7L4nDNzeYSedRmSeJ5eMYFucnKDi6KN1WNASXErgPepKqsQw55TgXPHnmpcyOh2Dd/LAZkyc+f0qaAw==} + engines: {node: '>=12.10.0'} + peerDependencies: + yeoman-environment: ^3.2.0 + peerDependenciesMeta: + yeoman-environment: + optional: true + dependencies: + chalk: 4.1.2 + dargs: 7.0.0 + debug: 4.3.7 + execa: 5.1.1 + github-username: 6.0.0 + lodash: 4.17.21 + mem-fs-editor: 9.4.0(mem-fs@2.1.0) + minimist: 1.2.8 + pacote: 15.2.0 + read-pkg-up: 7.0.1 + run-async: 2.4.1 + semver: 7.6.3 + shelljs: 0.8.5 + sort-keys: 4.2.0 + text-table: 0.2.0 yeoman-environment: 3.19.3 transitivePeerDependencies: - bluebird diff --git a/sonar-project.properties b/sonar-project.properties index 90e1c32904..546c5d689f 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -80,6 +80,7 @@ sonar.javascript.lcov.reportPaths=packages/abap-deploy-config-inquirer/coverage/ packages/ui5-application-inquirer/coverage/lcov.info, \ packages/inquirer-common/coverage/lcov.info, \ packages/odata-service-inquirer/coverage/lcov.info, \ + packages/project-integrity/coverage/lcov.info, \ packages/jest-environment-ui5/coverage/lcov.info sonar.testExecutionReportPaths=packages/abap-deploy-config-inquirer/coverage/sonar-report.xml, \ packages/abap-deploy-config-sub-generator/coverage/sonar-report.xml, \ @@ -154,5 +155,6 @@ sonar.testExecutionReportPaths=packages/abap-deploy-config-inquirer/coverage/son packages/reload-middleware/coverage/sonar-report.xml, \ packages/ui5-application-inquirer/coverage/sonar-report.xml, \ packages/inquirer-common/coverage/sonar-report.xml, \ + packages/project-integrity/coverage/sonar-report.xml, \ packages/odata-service-inquirer/coverage/sonar-report.xml, \ packages/jest-environment-ui5/coverage/sonar-report.xml diff --git a/tsconfig.json b/tsconfig.json index 33f3c244c4..747a679dc2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -179,6 +179,9 @@ { "path": "packages/project-input-validator" }, + { + "path": "packages/project-integrity" + }, { "path": "packages/reload-middleware" }, diff --git a/types/CHANGELOG.md b/types/CHANGELOG.md index 853c80fa81..282113cde9 100644 --- a/types/CHANGELOG.md +++ b/types/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/types +## 1.2.1 + +### Patch Changes + +- 19d51f3: feat: Quick Action For Add New Annotation File + ## 1.2.0 ### Minor Changes diff --git a/types/package.json b/types/package.json index 5c14b2f75d..d916790c40 100644 --- a/types/package.json +++ b/types/package.json @@ -1,6 +1,6 @@ { "name": "@sap-ux/types", - "version": "1.2.0", + "version": "1.2.1", "private": true, "description": "Type augmentations for imported modules", "repository": { diff --git a/types/ui5.d.ts b/types/ui5.d.ts index c0956076da..e7cb615d1f 100644 --- a/types/ui5.d.ts +++ b/types/ui5.d.ts @@ -119,6 +119,11 @@ declare module '@ui5/server' { * Get the name of the project. */ getName(): string; + + /** + * Gets the app id. + */ + getNamespace(): string; }; }