diff --git a/.changeset/red-jokes-flash.md b/.changeset/red-jokes-flash.md new file mode 100644 index 0000000000..a98fdfe999 --- /dev/null +++ b/.changeset/red-jokes-flash.md @@ -0,0 +1,6 @@ +--- +'@sap-ux-private/preview-middleware-client': patch +'@sap-ux/preview-middleware': patch +--- + +feat: handling of legacy free ui5 version diff --git a/examples/simple-generator/CHANGELOG.md b/examples/simple-generator/CHANGELOG.md index c951c83938..1c8a9d66b7 100644 --- a/examples/simple-generator/CHANGELOG.md +++ b/examples/simple-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/generator-simple-fe +## 1.0.140 + +### Patch Changes + +- @sap-ux/fiori-elements-writer@2.1.19 +- @sap-ux/fiori-freestyle-writer@2.1.1 + ## 1.0.139 ### Patch Changes diff --git a/examples/simple-generator/package.json b/examples/simple-generator/package.json index 559a5fb102..37981db285 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.139", + "version": "1.0.140", "description": "Simple example of a yeoman generator for Fiori elements.", "license": "Apache-2.0", "private": true, diff --git a/packages/adp-flp-config-sub-generator/CHANGELOG.md b/packages/adp-flp-config-sub-generator/CHANGELOG.md index 19415cad76..9a92462f6b 100644 --- a/packages/adp-flp-config-sub-generator/CHANGELOG.md +++ b/packages/adp-flp-config-sub-generator/CHANGELOG.md @@ -1,5 +1,13 @@ # @sap-ux/adp-flp-config-sub-generator +## 0.0.16 + +### Patch Changes + +- Updated dependencies [127bd12] + - @sap-ux/adp-tooling@0.13.0 + - @sap-ux/flp-config-inquirer@0.2.45 + ## 0.0.15 ### Patch Changes diff --git a/packages/adp-flp-config-sub-generator/package.json b/packages/adp-flp-config-sub-generator/package.json index 91af9685df..a6bc0bd918 100644 --- a/packages/adp-flp-config-sub-generator/package.json +++ b/packages/adp-flp-config-sub-generator/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/adp-flp-config-sub-generator", "description": "Generator for adding FLP configuration to an Adaptation Project", - "version": "0.0.15", + "version": "0.0.16", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/adp-tooling/CHANGELOG.md b/packages/adp-tooling/CHANGELOG.md index 23471430bf..704e83ee58 100644 --- a/packages/adp-tooling/CHANGELOG.md +++ b/packages/adp-tooling/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/adp-tooling +## 0.13.0 + +### Minor Changes + +- 127bd12: feat: Add Typescript support for Adaptation Project + ## 0.12.138 ### Patch Changes diff --git a/packages/adp-tooling/package.json b/packages/adp-tooling/package.json index 3c2e328a3e..691bad75a6 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.138", + "version": "0.13.0", "license": "Apache-2.0", "author": "@SAP/ux-tools-team", "main": "dist/index.js", @@ -20,8 +20,8 @@ "format": "prettier --write '**/*.{js,json,ts,yaml,yml}' --ignore-path ../../.prettierignore", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", - "test": "jest FIORI_TOOLS_DISABLE_SECURE_STORE=true --ci --forceExit --detectOpenHandles --colors --testPathPattern=test/unit", - "test-u": "jest FIORI_TOOLS_DISABLE_SECURE_STORE=true --ci --forceExit --detectOpenHandles --colors -u", + "test": "cross-env FIORI_TOOLS_DISABLE_SECURE_STORE=true jest --ci --forceExit --detectOpenHandles --colors --testPathPattern=test/unit", + "test-u": "cross-env FIORI_TOOLS_DISABLE_SECURE_STORE=true jest --ci --forceExit --detectOpenHandles --colors -u", "link": "pnpm link --global", "unlink": "pnpm unlink --global" }, @@ -44,6 +44,7 @@ "@sap-ux/system-access": "workspace:*", "@sap-ux/ui5-config": "workspace:*", "@sap-ux/odata-service-writer": "workspace:*", + "@sap-ux/nodejs-utils": "workspace:*", "@sap-ux/i18n": "workspace:*", "adm-zip": "0.5.10", "ejs": "3.1.10", @@ -70,7 +71,8 @@ "express": "4.21.2", "nock": "13.4.0", "rimraf": "5.0.5", - "supertest": "6.3.3" + "supertest": "6.3.3", + "cross-env": "^7.0.3" }, "engines": { "node": ">=18.x" diff --git a/packages/adp-tooling/src/base/helper.ts b/packages/adp-tooling/src/base/helper.ts index b236b411c8..04c5f7d60b 100644 --- a/packages/adp-tooling/src/base/helper.ts +++ b/packages/adp-tooling/src/base/helper.ts @@ -1,8 +1,9 @@ import type { Editor } from 'mem-fs-editor'; -import { readdirSync, readFileSync } from 'fs'; +import { existsSync, readdirSync, readFileSync } from 'fs'; import { join, isAbsolute, relative, basename, dirname } from 'path'; import { getWebappPath, FileName, readUi5Yaml } from '@sap-ux/project-access'; import type { UI5Config } from '@sap-ux/ui5-config'; + import type { DescriptorVariant, AdpPreviewConfig } from '../types'; /** @@ -53,6 +54,18 @@ export async function flpConfigurationExists(basePath: string): Promise } } +/** + * Checks whether TypeScript is supported in the project by verifying the existence of `tsconfig.json`. + * + * @param basePath - The base path of the project. + * @param fs - An optional `mem-fs-editor` instance to check for the file's existence. + * @returns `true` if `tsconfig.json` exists, otherwise `false`. + */ +export function isTypescriptSupported(basePath: string, fs?: Editor): boolean { + const path = join(basePath, 'tsconfig.json'); + return fs ? fs.exists(path) : existsSync(path); +} + /** * Returns the adaptation project configuration, throws an error if not found. * diff --git a/packages/adp-tooling/src/base/project-builder.ts b/packages/adp-tooling/src/base/project-builder.ts new file mode 100644 index 0000000000..d61f3dc39d --- /dev/null +++ b/packages/adp-tooling/src/base/project-builder.ts @@ -0,0 +1,21 @@ +import { CommandRunner } from '@sap-ux/nodejs-utils'; + +/** + * Executes a build command in the specified project directory. + * + * This function uses the `CommandRunner` to run the build process via the command `npm run build`. + * + * @param {string} projectPath - The absolute path to the project directory where the build command will be executed. + * @returns {Promise} Resolves when the build process has completed successfully. + * @throws {Error} If the build process fails or if an error occurs during cleanup. + */ +export async function runBuild(projectPath: string): Promise { + const commandRunner = new CommandRunner(); + + try { + await commandRunner.run('npm', ['run', 'build'], { cwd: projectPath }); + } catch (e) { + console.error(`Error during build and clean: ${e.message}`); + throw e; + } +} diff --git a/packages/adp-tooling/src/base/prompt.ts b/packages/adp-tooling/src/base/prompt.ts index 0d4a08ea38..bafafa8f28 100644 --- a/packages/adp-tooling/src/base/prompt.ts +++ b/packages/adp-tooling/src/base/prompt.ts @@ -18,6 +18,7 @@ export type PromptDefaults = { ft?: boolean; package?: string; transport?: string; + ts?: boolean; }; /** @@ -93,6 +94,13 @@ export async function promptGeneratorInput( message: 'Enable Fiori tools?', initial: defaults.ft !== false, validate: (input) => input?.length > 0 + }, + { + type: 'confirm', + name: 'enableTypeScript', + message: 'Enable TypeScript?', + initial: defaults.ts !== false, + validate: (input) => input?.length > 0 } ]); diff --git a/packages/adp-tooling/src/index.ts b/packages/adp-tooling/src/index.ts index 78cf060b31..bad2ab3eae 100644 --- a/packages/adp-tooling/src/index.ts +++ b/packages/adp-tooling/src/index.ts @@ -2,6 +2,7 @@ export * from './types'; export * from './prompts'; export * from './common'; export * from './base/cf'; +export * from './base/project-builder'; export * from './base/abap/manifest-service'; export * from './base/helper'; export * from './preview/adp-preview'; diff --git a/packages/adp-tooling/src/preview/routes-handler.ts b/packages/adp-tooling/src/preview/routes-handler.ts index fd42f90f0b..d5d8ce32a1 100644 --- a/packages/adp-tooling/src/preview/routes-handler.ts +++ b/packages/adp-tooling/src/preview/routes-handler.ts @@ -14,7 +14,7 @@ import { DirName, FileName } from '@sap-ux/project-access'; import { type CodeExtChange } from '../types'; import { ManifestService } from '../base/abap/manifest-service'; import type { DataSources } from '../base/abap/manifest-service'; -import { getAdpConfig, getVariant } from '../base/helper'; +import { getAdpConfig, getVariant, isTypescriptSupported } from '../base/helper'; import { createAbapServiceProvider } from '@sap-ux/system-access'; interface WriteControllerBody { @@ -163,8 +163,11 @@ export default class RoutesHandler { const project = this.util.getProject(); const sourcePath = project.getSourcePath(); + const rootPath = this.util.getProject().getRootPath(); const projectName = project.getName(); + const isTsSupported = isTypescriptSupported(rootPath); + const getPath = (projectPath: string, fileName: string, folder: string = DirName.Coding) => path.join(projectPath, DirName.Changes, folder, fileName).split(path.sep).join(path.posix.sep); @@ -173,7 +176,8 @@ export default class RoutesHandler { const change = JSON.parse(fileStr) as CodeExtChange; if (change.selector.controllerName === controllerName) { - const fileName = change.content.codeRef.replace('coding/', ''); + const baseFileName = change.content.codeRef.replace('coding/', ''); + const fileName = isTsSupported ? baseFileName.replace('.js', '.ts') : baseFileName; controllerPath = getPath(sourcePath, fileName); controllerPathFromRoot = getPath(projectName, fileName); changeFilePath = getPath(projectName, file.getName(), ''); @@ -195,7 +199,8 @@ export default class RoutesHandler { controllerExists, controllerPath: os.platform() === 'win32' ? `/${controllerPath}` : controllerPath, controllerPathFromRoot, - isRunningInBAS + isRunningInBAS, + isTsSupported }); this.logger.debug( controllerExists @@ -218,47 +223,37 @@ export default class RoutesHandler { try { const data = req.body as WriteControllerBody; - const controllerExtName = sanitize(data.controllerName); - const projectId = data.projectId; + const name = sanitize(data.controllerName); const sourcePath = this.util.getProject().getSourcePath(); + const rootPath = this.util.getProject().getRootPath(); - if (!controllerExtName) { + if (!name) { res.status(HttpStatusCodes.BAD_REQUEST).send('Controller extension name was not provided!'); this.logger.debug('Bad request. Controller extension name was not provided!'); return; } + const isTsSupported = isTypescriptSupported(rootPath); + const fullPath = path.join(sourcePath, DirName.Changes, DirName.Coding); - const filePath = path.join(fullPath, `${controllerExtName}.js`); + const filePath = path.join(fullPath, `${name}.${isTsSupported ? 'ts' : 'js'}`); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } if (fs.existsSync(filePath)) { - res.status(HttpStatusCodes.CONFLICT).send( - `Controller extension with name "${controllerExtName}" already exists` - ); - this.logger.debug(`Controller extension with name "${controllerExtName}" already exists`); + res.status(HttpStatusCodes.CONFLICT).send(`Controller extension with name "${name}" already exists`); + this.logger.debug(`Controller extension with name "${name}" already exists`); return; } - const controllerExtPath = `${projectId}.${controllerExtName}`; - - const controllerTemplateFilePath = path.join(__dirname, '../../templates/rta', TemplateFileName.Controller); - - renderFile(controllerTemplateFilePath, { controllerExtPath }, {}, (err, str) => { - if (err) { - throw new Error('Error rendering template: ' + err.message); - } - - fs.writeFileSync(filePath, str, { encoding: 'utf8' }); - }); + generateControllerFile(rootPath, filePath, name); const message = 'Controller extension created!'; res.status(HttpStatusCodes.CREATED).send(message); - this.logger.debug(`Controller extension with name "${controllerExtName}" was created`); + this.logger.debug(`Controller extension with name "${name}" was created`); } catch (e) { const sanitizedMsg = sanitize(e.message); this.logger.error(sanitizedMsg); @@ -358,3 +353,32 @@ export default class RoutesHandler { return await ManifestService.initMergedManifest(provider, basePath, variant, this.logger); } } + +/** + * Generates a controller file for the Adaptation Project based on the project's TypeScript support. + * + * This function creates a controller file in the specified `filePath` by rendering a template. + * It determines whether to use a TypeScript or JavaScript template based on the TypeScript support of the project. + * + * @param {string} rootPath - The root directory of the project. + * @param {string} filePath - The destination path where the generated controller file should be saved. + * @param {string} name - The name of the controller extension (used in TypeScript templates). + * @throws {Error} Throws an error if rendering the template fails. + */ +function generateControllerFile(rootPath: string, filePath: string, name: string): void { + const id = getVariant(rootPath)?.id; + const isTsSupported = isTypescriptSupported(rootPath); + const tmplFileName = isTsSupported ? TemplateFileName.TSController : TemplateFileName.Controller; + const tmplPath = path.join(__dirname, '../../templates/rta', tmplFileName); + const extensionPath = `${id}.${name}`; + + const templateData = isTsSupported ? { name, ns: id } : { extensionPath }; + + renderFile(tmplPath, templateData, {}, (err, str) => { + if (err) { + throw new Error(`Error rendering ${isTsSupported ? 'TypeScript' : 'JavaScript'} template: ${err.message}`); + } + + fs.writeFileSync(filePath, str, { encoding: 'utf8' }); + }); +} diff --git a/packages/adp-tooling/src/types.ts b/packages/adp-tooling/src/types.ts index 4cc155604b..89607d9993 100644 --- a/packages/adp-tooling/src/types.ts +++ b/packages/adp-tooling/src/types.ts @@ -85,6 +85,10 @@ export interface AdpWriterConfig { * Optional: if set to true then the generated project will be recognized by the SAP Fiori tools */ fioriTools?: boolean; + /** + * Optional: if set to true then the generated project will support typescript + */ + enableTypeScript?: boolean; }; } @@ -278,6 +282,7 @@ export type ParameterRules = { export const enum TemplateFileName { Fragment = 'fragment.xml', Controller = 'controller.ejs', + TSController = 'ts-controller.ejs', Annotation = 'annotation.xml' } diff --git a/packages/adp-tooling/src/writer/index.ts b/packages/adp-tooling/src/writer/index.ts index 35f3a4d48d..832b3d7afd 100644 --- a/packages/adp-tooling/src/writer/index.ts +++ b/packages/adp-tooling/src/writer/index.ts @@ -5,7 +5,7 @@ import type { AdpWriterConfig, InternalInboundNavigation } from '../types'; import { enhanceManifestChangeContentWithFlpConfig } from './options'; import { writeTemplateToFolder, writeUI5Yaml, writeUI5DeployYaml } from './project-utils'; -const tmplPath = join(__dirname, '../../templates/project'); +const baseTmplPath = join(__dirname, '../../templates'); /** * Set default values for optional properties. @@ -60,7 +60,7 @@ export async function generate(basePath: string, config: AdpWriterConfig, fs?: E fullConfig.app.content ); } - writeTemplateToFolder(join(tmplPath, '**/*.*'), join(basePath), fullConfig, fs); + writeTemplateToFolder(baseTmplPath, join(basePath), fullConfig, fs); await writeUI5DeployYaml(basePath, fullConfig, fs); await writeUI5Yaml(basePath, fullConfig, fs); @@ -82,6 +82,8 @@ export async function migrate(basePath: string, config: AdpWriterConfig, fs?: Ed const fullConfig = setDefaults(config); + const tmplPath = join(baseTmplPath, 'project'); + // Copy the specified files to target project fs.copyTpl(join(tmplPath, '**/ui5.yaml'), join(basePath), fullConfig, undefined, { globOptions: { dot: true } diff --git a/packages/adp-tooling/src/writer/options.ts b/packages/adp-tooling/src/writer/options.ts index 4eee949d2d..6571991ee7 100644 --- a/packages/adp-tooling/src/writer/options.ts +++ b/packages/adp-tooling/src/writer/options.ts @@ -5,8 +5,8 @@ import type { AbapTarget, FioriToolsProxyConfigBackend } from '@sap-ux/ui5-config'; + import type { - CustomConfig, AdpWriterConfig, InboundContent, Language, @@ -37,14 +37,35 @@ export function enhanceUI5Yaml(ui5Config: UI5Config, config: AdpWriterConfig) { } /** - * Generate the configuration for the custom tasks required for the ui5.yaml. + * Generates the configuration for the custom tasks required for the ui5.yaml. * - * @param ui5Config configuration representing the ui5.yaml - * @param config full project configuration + * Adds a custom task for building TypeScript projects. + * + * @param {UI5Config} ui5Config - The UI5 configuration object representing the ui5.yaml. + * @param {AdpWriterConfig} config - The configuration object containing options for the adaptation project. */ export function enhanceUI5YamlWithCustomTask(ui5Config: UI5Config, config: AdpWriterConfig & { app: CloudApp }) { - const tasks = getAdpCloudCustomTasks(config); - ui5Config.addCustomTasks(tasks); + if (config.options?.enableTypeScript) { + ui5Config.addCustomTasks([ + { + name: 'ui5-tooling-transpile-task', + afterTask: 'replaceVersion', + configuration: { + debug: true, + omitSourceMaps: true, + omitTSFromBuildResult: true, + transformModulesToUI5: { + overridesToOverride: true + } + } + } + ]); + } + + if (config.customConfig?.adp?.environment === 'C') { + const tasks = getAdpCloudCustomTasks(config); + ui5Config.addCustomTasks(tasks); + } } /** @@ -53,13 +74,36 @@ export function enhanceUI5YamlWithCustomTask(ui5Config: UI5Config, config: AdpWr * @param ui5Config configuration representing the ui5.yaml * @param config full project configuration */ -export function enhanceUI5YamlWithCustomConfig(ui5Config: UI5Config, config?: CustomConfig) { - if (config?.adp) { - const { support } = config.adp; +export function enhanceUI5YamlWithCustomConfig(ui5Config: UI5Config, config: AdpWriterConfig) { + const adp = config.customConfig?.adp; + if (adp) { + const { support } = adp; ui5Config.addCustomConfiguration('adp', { support }); } } +/** + * Enhances a UI5 YAML configuration with the transpile middleware for TypeScript support. + * + * @param {UI5Config} ui5Config - The UI5 configuration object representing the ui5.yaml. + * @param {AdpWriterConfig} config - The configuration object containing options for the adaptation project. + * @param {boolean} [config.options.enableTypeScript] - Flag indicating if TypeScript support is enabled. + */ +export function enhanceUI5YamlWithTranspileMiddleware(ui5Config: UI5Config, config: AdpWriterConfig) { + if (config.options?.enableTypeScript) { + ui5Config.updateCustomMiddleware({ + name: 'ui5-tooling-transpile-middleware', + afterMiddleware: 'compression', + configuration: { + debug: true, + transformModulesToUI5: { + overridesToOverride: true + } + } + }); + } +} + /** * Writer configuration with deploy configuration. */ diff --git a/packages/adp-tooling/src/writer/project-utils.ts b/packages/adp-tooling/src/writer/project-utils.ts index b5cb6d1ac0..c4f176e614 100644 --- a/packages/adp-tooling/src/writer/project-utils.ts +++ b/packages/adp-tooling/src/writer/project-utils.ts @@ -7,10 +7,11 @@ import { enhanceUI5Yaml, hasDeployConfig, enhanceUI5YamlWithCustomConfig, - enhanceUI5YamlWithCustomTask + enhanceUI5YamlWithCustomTask, + enhanceUI5YamlWithTranspileMiddleware } from './options'; -import { UI5Config } from '@sap-ux/ui5-config'; +import { UI5Config, getEsmTypesVersion, getTypesPackage } from '@sap-ux/ui5-config'; type PackageJSON = { name: string; version: string }; @@ -35,23 +36,35 @@ export function getPackageJSONInfo(): PackageJSON { /** * Writes a given project template files within a specified folder in the project directory. * - * @param {string} templatePath - The root path of the project template. + * @param {string} baseTmplPath - The root path of the templates folder. * @param {string} projectPath - The root path of the project. * @param {AdpWriterConfig} data - The data to be populated in the template file. * @param {Editor} fs - The `mem-fs-editor` instance used for file operations. * @returns {void} */ export function writeTemplateToFolder( - templatePath: string, + baseTmplPath: string, projectPath: string, data: AdpWriterConfig, fs: Editor ): void { + const tmplPath = join(baseTmplPath, 'project', '**/*.*'); + const tsConfigPath = join(baseTmplPath, 'typescript', 'tsconfig.json'); + const typesVersion = getEsmTypesVersion(data.ui5?.version); + const typesPackage = getTypesPackage(typesVersion); + try { - fs.copyTpl(templatePath, projectPath, data, undefined, { + fs.copyTpl(tmplPath, projectPath, { ...data, typesPackage, typesVersion }, undefined, { globOptions: { dot: true }, processDestinationPath: (filePath: string) => filePath.replace(/gitignore.tmpl/g, '.gitignore') }); + + if (data.options?.enableTypeScript) { + const id = data.app?.id?.split('.').join('/'); + fs.copyTpl(tsConfigPath, join(projectPath, 'tsconfig.json'), { id, typesPackage }, undefined, { + globOptions: { dot: true } + }); + } } catch (e) { throw new Error(`Could not write template files to folder. Reason: ${e.message}`); } @@ -71,11 +84,10 @@ export async function writeUI5Yaml(projectPath: string, data: AdpWriterConfig, f const baseUi5ConfigContent = fs.read(ui5ConfigPath); const ui5Config = await UI5Config.newInstance(baseUi5ConfigContent); ui5Config.setConfiguration({ propertiesFileSourceEncoding: 'UTF-8' }); - enhanceUI5YamlWithCustomConfig(ui5Config, data?.customConfig); + enhanceUI5YamlWithCustomConfig(ui5Config, data); + enhanceUI5YamlWithTranspileMiddleware(ui5Config, data); enhanceUI5Yaml(ui5Config, data); - if (data.customConfig?.adp?.environment === 'C') { - enhanceUI5YamlWithCustomTask(ui5Config, data as AdpWriterConfig & { app: CloudApp }); - } + enhanceUI5YamlWithCustomTask(ui5Config, data as AdpWriterConfig & { app: CloudApp }); fs.write(ui5ConfigPath, ui5Config.toString()); } catch (e) { diff --git a/packages/adp-tooling/templates/project/package.json b/packages/adp-tooling/templates/project/package.json index 57f76789bc..1208a8d77b 100644 --- a/packages/adp-tooling/templates/project/package.json +++ b/packages/adp-tooling/templates/project/package.json @@ -15,7 +15,10 @@ "@sap-ux/ui5-proxy-middleware": "^1.3.0", "@sap-ux/deploy-tooling": "^0.11.7"<%}%>, "@ui5/task-adaptation": "^1.3.0", - "@ui5/cli": "^3.9.2" + "@ui5/cli": "^3.9.2"<%if (locals.options?.enableTypeScript) {%>, + "<%- typesPackage %>": "<%- typesVersion %>", + "typescript": "^5.7.3", + "ui5-tooling-transpile": "^3.6.1"<%}%> }, "scripts": { "build": "ui5 build --exclude-task generateFlexChangesBundle generateComponentPreload minify --clean-dest", diff --git a/packages/adp-tooling/templates/rta/controller.ejs b/packages/adp-tooling/templates/rta/controller.ejs index 30b6214ff7..5e63083996 100644 --- a/packages/adp-tooling/templates/rta/controller.ejs +++ b/packages/adp-tooling/templates/rta/controller.ejs @@ -8,7 +8,7 @@ sap.ui.define( // ,OverrideExecution ) { 'use strict'; - return ControllerExtension.extend("<%= controllerExtPath %>", { + return ControllerExtension.extend("<%= extensionPath %>", { // metadata: { // // extension can declare the public methods // // in general methods that start with "_" are private @@ -47,27 +47,27 @@ sap.ui.define( // /** // * Called when a controller is instantiated and its View controls (if available) are already created. // * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization. - // * @memberOf {{controllerExtPath}} + // * @memberOf <%= extensionPath %> // */ // onInit: function() { // }, // /** // * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered // * (NOT before the first rendering! onInit() is used for that one!). - // * @memberOf {{controllerExtPath}} + // * @memberOf <%= extensionPath %> // */ // onBeforeRendering: function() { // }, // /** // * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here. // * This hook is the same one that SAPUI5 controls get after being rendered. - // * @memberOf {{controllerExtPath}} + // * @memberOf <%= extensionPath %> // */ // onAfterRendering: function() { // }, // /** // * Called when the Controller is destroyed. Use this one to free resources and finalize activities. - // * @memberOf {{controllerExtPath}} + // * @memberOf <%= extensionPath %> // */ // onExit: function() { // }, diff --git a/packages/adp-tooling/templates/rta/ts-controller.ejs b/packages/adp-tooling/templates/rta/ts-controller.ejs new file mode 100644 index 0000000000..a7e6e15b9d --- /dev/null +++ b/packages/adp-tooling/templates/rta/ts-controller.ejs @@ -0,0 +1,18 @@ +import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; + +/** + * @namespace <%= ns %> + * @controller + */ +export default class <%= name %> extends ControllerExtension { + overrides = { + /** + * Called when a controller is instantiated and its View controls (if available) are already created. + * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization. + * @memberOf <%= ns %>.<%= name %> + */ + onInit: () => { + const view = this.getView(); + }, + }; +} diff --git a/packages/adp-tooling/templates/typescript/tsconfig.json b/packages/adp-tooling/templates/typescript/tsconfig.json new file mode 100644 index 0000000000..53ea1827f2 --- /dev/null +++ b/packages/adp-tooling/templates/typescript/tsconfig.json @@ -0,0 +1,19 @@ +{ + "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/<%- typesPackage %>"], + "paths": { + "<%= id %>/*": ["./webapp/*"] + } + }, + "include": ["./webapp/**/*"] +} \ No newline at end of file diff --git a/packages/adp-tooling/test/unit/base/helper.test.ts b/packages/adp-tooling/test/unit/base/helper.test.ts index 32a42994c1..de4356c9a9 100644 --- a/packages/adp-tooling/test/unit/base/helper.test.ts +++ b/packages/adp-tooling/test/unit/base/helper.test.ts @@ -1,5 +1,5 @@ import { join } from 'path'; -import { readFileSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; import type { create, Editor } from 'mem-fs-editor'; import { UI5Config } from '@sap-ux/ui5-config'; @@ -11,10 +11,12 @@ import { getAdpConfig, getWebappFiles, flpConfigurationExists, - updateVariant + updateVariant, + isTypescriptSupported } from '../../../src/base/helper'; const readFileSyncMock = readFileSync as jest.Mock; +const existsSyncMock = existsSync as jest.Mock; jest.mock('fs', () => { return { @@ -129,6 +131,55 @@ describe('helper', () => { }); }); + describe('isTypescriptSupported', () => { + const basePath = '/mock/project/path'; + const tsconfigPath = join(basePath, 'tsconfig.json'); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return true if tsconfig.json exists and fs is not provided', () => { + existsSyncMock.mockReturnValueOnce(true); + + const result = isTypescriptSupported(basePath); + + expect(result).toBe(true); + expect(existsSyncMock).toHaveBeenCalledWith(tsconfigPath); + }); + + it('should return false if tsconfig.json does not exist and fs is not provided', () => { + existsSyncMock.mockReturnValueOnce(false); + + const result = isTypescriptSupported(basePath); + + expect(result).toBe(false); + expect(existsSyncMock).toHaveBeenCalledWith(tsconfigPath); + }); + + it('should return true if tsconfig.json exists and fs is provided', () => { + const mockEditor = { + exists: jest.fn().mockReturnValueOnce(true) + } as unknown as Editor; + + const result = isTypescriptSupported(basePath, mockEditor); + + expect(result).toBe(true); + expect(mockEditor.exists).toHaveBeenCalledWith(tsconfigPath); + }); + + it('should return false if tsconfig.json does not exist and fs is provided', () => { + const mockEditor = { + exists: jest.fn().mockReturnValueOnce(false) + } as unknown as Editor; + + const result = isTypescriptSupported(basePath, mockEditor); + + expect(result).toBe(false); + expect(mockEditor.exists).toHaveBeenCalledWith(tsconfigPath); + }); + }); + describe('getAdpConfig', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/adp-tooling/test/unit/base/project-builder.test.ts b/packages/adp-tooling/test/unit/base/project-builder.test.ts new file mode 100644 index 0000000000..571d096e16 --- /dev/null +++ b/packages/adp-tooling/test/unit/base/project-builder.test.ts @@ -0,0 +1,35 @@ +import { CommandRunner } from '@sap-ux/nodejs-utils'; + +import { runBuild } from '../../../src/base/project-builder'; + +const projectPath = '/mock/project/path'; + +describe('runBuildAndClean', () => { + let commandSpy: jest.SpyInstance; + + beforeEach(() => { + commandSpy = jest.spyOn(CommandRunner.prototype, 'run'); + console.error = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should execute the build command', async () => { + commandSpy.mockResolvedValueOnce('Build completed.'); + + await runBuild(projectPath); + + expect(commandSpy).toHaveBeenCalledWith('npm', ['run', 'build'], { cwd: projectPath }); + }); + + it('should throw an error if the build command fails', async () => { + const errorMsg = 'Build failed'; + commandSpy.mockRejectedValueOnce(new Error(errorMsg)); + + await expect(runBuild(projectPath)).rejects.toThrow(errorMsg); + + expect(console.error).toHaveBeenCalledWith(`Error during build and clean: ${errorMsg}`); + }); +}); diff --git a/packages/adp-tooling/test/unit/base/prompt.test.ts b/packages/adp-tooling/test/unit/base/prompt.test.ts index b149a02261..bc71a04b4c 100644 --- a/packages/adp-tooling/test/unit/base/prompt.test.ts +++ b/packages/adp-tooling/test/unit/base/prompt.test.ts @@ -113,7 +113,8 @@ describe('base/prompts', () => { transport: defaults.transport }, options: { - fioriTools: true + fioriTools: true, + enableTypeScript: true } }); }); @@ -156,7 +157,8 @@ describe('base/prompts', () => { transport: defaults.transport }, options: { - fioriTools: false + fioriTools: false, + enableTypeScript: true } }); }); 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 b2ff687d3d..5a7b6cd9b4 100644 --- a/packages/adp-tooling/test/unit/preview/adp-preview.test.ts +++ b/packages/adp-tooling/test/unit/preview/adp-preview.test.ts @@ -1,21 +1,23 @@ import nock from 'nock'; +import * as fs from 'fs'; import { join } from 'path'; import express from 'express'; +import { renderFile } from 'ejs'; import supertest from 'supertest'; 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 * as fs from 'fs'; -import { AdpPreview } from '../../../src'; -import type { AdpPreviewConfig, CommonChangeProperties } from '../../../src'; +import { type Logger, ToolsLogger } from '@sap-ux/logger'; +import * as systemAccess from '@sap-ux/system-access/dist/base/connect'; +import * as serviceWriter from '@sap-ux/odata-service-writer/dist/data/annotations'; + import * as helper from '../../../src/base/helper'; import * as editors from '../../../src/writer/editors'; +import { AdpPreview } from '../../../src'; import * as manifestService from '../../../src/base/abap/manifest-service'; +import type { AdpPreviewConfig, CommonChangeProperties } from '../../../src'; 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 }[]; @@ -55,6 +57,13 @@ jest.mock('@sap-ux/store', () => { }; }); +jest.mock('ejs', () => ({ + ...jest.requireActual('ejs'), + renderFile: jest.fn() +})); + +const renderFileMock = renderFile as jest.Mock; + const tryFixChangeMock = tryFixChange as jest.Mock; const addXmlFragmentMock = addXmlFragment as jest.Mock; @@ -490,6 +499,8 @@ describe('AdaptationProject', () => { }, ignoreCertErrors: false }); + jest.spyOn(helper, 'isTypescriptSupported').mockReturnValue(false); + jest.spyOn(systemAccess, 'createAbapServiceProvider').mockResolvedValue({} as any); jest.spyOn(manifestService.ManifestService, 'initMergedManifest').mockResolvedValue({ getDataSourceMetadata: jest.fn().mockResolvedValue(` @@ -539,6 +550,7 @@ describe('AdaptationProject', () => { afterEach(() => { mockExistsSync.mockRestore(); + mockWriteFileSync.mockRestore(); }); test('GET /adp/api/fragment', async () => { @@ -617,14 +629,53 @@ describe('AdaptationProject', () => { test('POST /adp/api/controller - creates controller', async () => { mockExistsSync.mockReturnValue(false); + renderFileMock.mockImplementation((templatePath, data, options, callback) => { + callback(undefined, 'test-js-controller'); + }); + const controllerName = 'Share'; + const controllerPath = join('/adp.project', 'webapp', 'changes', 'coding', 'Share.js'); + const response = await server.post('/adp/api/controller').send({ controllerName }).expect(201); + + const message = response.text; + expect(mockWriteFileSync).toHaveBeenNthCalledWith(1, controllerPath, 'test-js-controller', { + encoding: 'utf8' + }); + expect(message).toBe('Controller extension created!'); + }); + + test('POST /adp/api/controller - creates TypeScript controller', async () => { + mockExistsSync.mockReturnValue(false); + jest.spyOn(helper, 'isTypescriptSupported').mockReturnValue(true); + renderFileMock.mockImplementation((templatePath, data, options, callback) => { + callback(undefined, 'test-ts-controller'); + }); + const controllerName = 'Share'; + const controllerPath = join('/adp.project', 'webapp', 'changes', 'coding', 'Share.ts'); const response = await server.post('/adp/api/controller').send({ controllerName }).expect(201); const message = response.text; - expect(mockWriteFileSync).toHaveBeenCalledTimes(1); + expect(mockWriteFileSync).toHaveBeenNthCalledWith(1, controllerPath, 'test-ts-controller', { + encoding: 'utf8' + }); expect(message).toBe('Controller extension created!'); }); + test('POST /adp/api/controller - throws error during rendering a ts template', async () => { + mockExistsSync.mockReturnValue(false); + jest.spyOn(helper, 'isTypescriptSupported').mockReturnValue(true); + renderFileMock.mockImplementation((templatePath, data, options, callback) => { + callback(new Error('Failed to render template'), ''); + }); + + const controllerName = 'Share'; + const response = await server.post('/adp/api/controller').send({ controllerName }).expect(500); + + const message = response.text; + expect(mockWriteFileSync).not.toHaveBeenCalled(); + expect(message).toBe('Error rendering TypeScript template Failed to render template'); + }); + test('POST /adp/api/controller - controller already exists', async () => { mockExistsSync.mockReturnValueOnce(false).mockResolvedValueOnce(true); diff --git a/packages/adp-tooling/test/unit/writer/__snapshots__/index.test.ts.snap b/packages/adp-tooling/test/unit/writer/__snapshots__/index.test.ts.snap index 4f7e7583ee..3eabf7bf68 100644 --- a/packages/adp-tooling/test/unit/writer/__snapshots__/index.test.ts.snap +++ b/packages/adp-tooling/test/unit/writer/__snapshots__/index.test.ts.snap @@ -117,7 +117,7 @@ builder: } `; -exports[`ADP writer generate S/4HANA cloud config with target destination 1`] = ` +exports[`ADP writer generate S/4HANA cloud config with inboundId 1`] = ` Object { "package.json": Object { "contents": "{ @@ -165,7 +165,7 @@ builder: afterTask: generateCachebusterInfo configuration: target: - destination: UYTCLNT902 + url: http://sap.example app: package: $TMP ", @@ -200,15 +200,14 @@ server: configuration: adp: target: - destination: UYTCLNT902 + url: http://sap.example ignoreCertErrors: false - name: fiori-tools-proxy afterMiddleware: fiori-tools-preview configuration: ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted backend: - - destination: UYTCLNT902 - url: https://REQUIRED_FOR_VSCODE.example + - url: http://sap.example path: /sap ui5: path: @@ -227,15 +226,15 @@ builder: - sap: testId i18n: testKey target: - destination: UYTCLNT902 - url: https://REQUIRED_FOR_VSCODE.example + url: http://sap.example + ignoreCertErrors: false ", "state": "modified", }, } `; -exports[`ADP writer generate S/4HANA cloud config with inboundId 1`] = ` +exports[`ADP writer generate S/4HANA cloud config with target destination 1`] = ` Object { "package.json": Object { "contents": "{ @@ -283,7 +282,7 @@ builder: afterTask: generateCachebusterInfo configuration: target: - url: http://sap.example + destination: DUMMY_DESTINATION app: package: $TMP ", @@ -318,14 +317,15 @@ server: configuration: adp: target: - url: http://sap.example + destination: DUMMY_DESTINATION ignoreCertErrors: false - name: fiori-tools-proxy afterMiddleware: fiori-tools-preview configuration: ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted backend: - - url: http://sap.example + - destination: DUMMY_DESTINATION + url: https://REQUIRED_FOR_VSCODE.example path: /sap ui5: path: @@ -344,8 +344,8 @@ builder: - sap: testId i18n: testKey target: - url: http://sap.example - ignoreCertErrors: false + destination: DUMMY_DESTINATION + url: https://REQUIRED_FOR_VSCODE.example ", "state": "modified", }, @@ -624,6 +624,145 @@ server: } `; +exports[`ADP writer generate enable TypeScript support 1`] = ` +Object { + "package.json": Object { + "contents": "{ + \\"name\\": \\"my-test-app\\", + \\"version\\": \\"0.1.0\\", + \\"private\\": true, + \\"description\\": \\"Adaptation of the.original.app\\", + \\"keywords\\": [ + \\"ui5\\", + \\"sapui5\\", + \\"adaptation-project\\" + ], + \\"dependencies\\": {}, + \\"devDependencies\\": { + \\"@sap/ux-ui5-tooling\\": \\"1\\", + \\"@ui5/task-adaptation\\": \\"^1.3.0\\", + \\"@ui5/cli\\": \\"^3.9.2\\", + \\"@sapui5/types\\": \\"~1.133.0\\", + \\"typescript\\": \\"^5.7.3\\", + \\"ui5-tooling-transpile\\": \\"^3.6.1\\" + }, + \\"scripts\\": { + \\"build\\": \\"ui5 build --exclude-task generateFlexChangesBundle generateComponentPreload minify --clean-dest\\", + \\"start\\": \\"fiori run --open /test/flp.html#app-preview\\", + \\"start-editor\\": \\"fiori run --open /test/adaptation-editor.html\\", + \\"deploy\\": \\"npm run build && fiori deploy --config ui5-deploy.yaml\\", + \\"undeploy\\": \\"npm run build && fiori undeploy --config ui5-deploy.yaml\\", + \\"deploy-test\\": \\"npm run build && fiori deploy --config ui5-deploy.yaml --testMode true\\" + } +} +", + "state": "modified", + }, + "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\\": { + \\"my/test/app/*\\": [\\"./webapp/*\\"] + } + }, + \\"include\\": [\\"./webapp/**/*\\"] +}", + "state": "modified", + }, + "ui5-deploy.yaml": Object { + "contents": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json + +specVersion: \\"3.0\\" +metadata: + name: my-test-app +type: application +builder: + resources: + excludes: + - /test/** + - /localService/** + customTasks: + - name: deploy-to-abap + afterTask: generateCachebusterInfo + configuration: + target: + url: http://sap.example + app: + package: $TMP +", + "state": "modified", + }, + "ui5.yaml": Object { + "contents": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json + +specVersion: \\"3.0\\" +metadata: + name: my-test-app +type: application +resources: + configuration: + propertiesFileSourceEncoding: UTF-8 +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + configuration: + debug: true + transformModulesToUI5: + overridesToOverride: true + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + port: 35729 + path: webapp + delay: 300 + - name: fiori-tools-preview + afterMiddleware: fiori-tools-appreload + configuration: + adp: + target: + url: http://sap.example + ignoreCertErrors: false + - name: fiori-tools-proxy + afterMiddleware: fiori-tools-preview + configuration: + ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted + backend: + - url: http://sap.example + path: /sap + ui5: + path: + - /resources + - /test-resources + url: https://ui5.sap.com + version: '' +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + debug: true + omitSourceMaps: true + omitTSFromBuildResult: true + transformModulesToUI5: + overridesToOverride: true +", + "state": "modified", + }, +} +`; + exports[`ADP writer generate minimal config 1`] = ` Object { ".gitignore": Object { @@ -1446,7 +1585,7 @@ builder: afterTask: generateCachebusterInfo configuration: target: - destination: UYTCLNT902 + destination: DUMMY_DESTINATION app: package: $TMP ", @@ -1481,14 +1620,14 @@ server: configuration: adp: target: - destination: UYTCLNT902 + destination: DUMMY_DESTINATION ignoreCertErrors: false - name: fiori-tools-proxy afterMiddleware: fiori-tools-preview configuration: ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted backend: - - destination: UYTCLNT902 + - destination: DUMMY_DESTINATION url: https://REQUIRED_FOR_VSCODE.example path: /sap ui5: @@ -1508,7 +1647,7 @@ builder: - sap: testId i18n: testKey target: - destination: UYTCLNT902 + destination: DUMMY_DESTINATION url: https://REQUIRED_FOR_VSCODE.example ", "state": "modified", @@ -1704,6 +1843,180 @@ my.test.app_sap.app.crossNavigation.inbounds.sampleId.subTitle=sampleSubTitle } ] } +", + "state": "modified", + }, + "../ts-support/.gitignore": Object { + "contents": "node_modules/ +dist/ +.tmp +.env +*.zip", + "state": "modified", + }, + "../ts-support/package.json": Object { + "contents": "{ + \\"name\\": \\"my-test-app\\", + \\"version\\": \\"0.1.0\\", + \\"private\\": true, + \\"description\\": \\"Adaptation of the.original.app\\", + \\"keywords\\": [ + \\"ui5\\", + \\"sapui5\\", + \\"adaptation-project\\" + ], + \\"dependencies\\": {}, + \\"devDependencies\\": { + \\"@sap/ux-ui5-tooling\\": \\"1\\", + \\"@ui5/task-adaptation\\": \\"^1.3.0\\", + \\"@ui5/cli\\": \\"^3.9.2\\", + \\"@sapui5/types\\": \\"~1.133.0\\", + \\"typescript\\": \\"^5.7.3\\", + \\"ui5-tooling-transpile\\": \\"^3.6.1\\" + }, + \\"scripts\\": { + \\"build\\": \\"ui5 build --exclude-task generateFlexChangesBundle generateComponentPreload minify --clean-dest\\", + \\"start\\": \\"fiori run --open /test/flp.html#app-preview\\", + \\"start-editor\\": \\"fiori run --open /test/adaptation-editor.html\\", + \\"deploy\\": \\"npm run build && fiori deploy --config ui5-deploy.yaml\\", + \\"undeploy\\": \\"npm run build && fiori undeploy --config ui5-deploy.yaml\\", + \\"deploy-test\\": \\"npm run build && fiori deploy --config ui5-deploy.yaml --testMode true\\" + } +} +", + "state": "modified", + }, + "../ts-support/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\\": { + \\"my/test/app/*\\": [\\"./webapp/*\\"] + } + }, + \\"include\\": [\\"./webapp/**/*\\"] +}", + "state": "modified", + }, + "../ts-support/ui5-deploy.yaml": Object { + "contents": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json + +specVersion: \\"3.0\\" +metadata: + name: my-test-app +type: application +builder: + resources: + excludes: + - /test/** + - /localService/** + customTasks: + - name: deploy-to-abap + afterTask: generateCachebusterInfo + configuration: + target: + url: http://sap.example + app: + package: $TMP +", + "state": "modified", + }, + "../ts-support/ui5.yaml": Object { + "contents": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json + +specVersion: \\"3.0\\" +metadata: + name: my-test-app +type: application +resources: + configuration: + propertiesFileSourceEncoding: UTF-8 +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + configuration: + debug: true + transformModulesToUI5: + overridesToOverride: true + - name: fiori-tools-appreload + afterMiddleware: compression + configuration: + port: 35729 + path: webapp + delay: 300 + - name: fiori-tools-preview + afterMiddleware: fiori-tools-appreload + configuration: + adp: + target: + url: http://sap.example + ignoreCertErrors: false + - name: fiori-tools-proxy + afterMiddleware: fiori-tools-preview + configuration: + ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted + backend: + - url: http://sap.example + path: /sap + ui5: + path: + - /resources + - /test-resources + url: https://ui5.sap.com + version: '' +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + debug: true + omitSourceMaps: true + omitTSFromBuildResult: true + transformModulesToUI5: + overridesToOverride: true +", + "state": "modified", + }, + "../ts-support/webapp/i18n/i18n.properties": Object { + "contents": "# This is the resource bundle for my.test.app + +#Texts for manifest.json + +#XTIT: Application name +my.test.app_sap.app.title=Adaptation of the.original.app +", + "state": "modified", + }, + "../ts-support/webapp/manifest.appdescr_variant": Object { + "contents": "{ + \\"fileName\\": \\"manifest\\", + \\"layer\\": \\"CUSTOMER_BASE\\", + \\"fileType\\": \\"appdescr_variant\\", + \\"reference\\": \\"the.original.app\\", + \\"id\\": \\"my.test.app\\", + \\"namespace\\": \\"apps/the.original.app/appVariants/my.test.app/\\", + \\"version\\": \\"0.1.0\\", + \\"content\\": [ + { + \\"changeType\\": \\"appdescr_app_setTitle\\", + \\"content\\": {}, + \\"texts\\": { + \\"i18n\\": \\"i18n/i18n.properties\\" + } + } + ] +} ", "state": "modified", }, diff --git a/packages/adp-tooling/test/unit/writer/index.test.ts b/packages/adp-tooling/test/unit/writer/index.test.ts index ddc2e3b556..aee84ee712 100644 --- a/packages/adp-tooling/test/unit/writer/index.test.ts +++ b/packages/adp-tooling/test/unit/writer/index.test.ts @@ -111,6 +111,35 @@ describe('ADP writer', () => { ).toMatchSnapshot(); }); + test('enable TypeScript support', async () => { + const projectDir = join(outputDir, 'ts-support'); + await generate( + projectDir, + { + ...config, + deploy: { + package: '$TMP' + }, + options: { + fioriTools: true, + enableTypeScript: true + }, + ui5: { + version: '1.133.0' + } + }, + fs + ); + expect( + fs.dump( + projectDir, + (file) => + file.dirname === projectDir && + ['package.json', 'ui5.yaml', 'ui5-deploy.yaml', 'tsconfig.json'].includes(file.basename) + ) + ).toMatchSnapshot(); + }); + test('S/4HANA cloud config', async () => { const projectDir = join(outputDir, 's4hana'); Object.assign(config.app, { @@ -130,7 +159,8 @@ describe('ADP writer', () => { package: '$TMP' }, options: { - fioriTools: true + fioriTools: true, + enableTypeScript: false }, ui5: { version: '1.122.1' @@ -171,7 +201,7 @@ describe('ADP writer', () => { reference: 'the.original.app' }, target: { - destination: 'UYTCLNT902' + destination: 'DUMMY_DESTINATION' } }; @@ -193,7 +223,8 @@ describe('ADP writer', () => { package: '$TMP' }, options: { - fioriTools: true + fioriTools: true, + enableTypeScript: false }, ui5: { version: '1.122.1' @@ -237,7 +268,8 @@ describe('ADP writer', () => { package: '$TMP' }, options: { - fioriTools: true + fioriTools: true, + enableTypeScript: false }, ui5: { version: '1.122.1' @@ -292,7 +324,11 @@ describe('ADP writer', () => { } }, options: { - fioriTools: true + fioriTools: true, + enableTypeScript: false + }, + ui5: { + version: '1.133.0' } }; const migrateInputDir = join(__dirname, '../../fixtures/webide-adaptation-project'); diff --git a/packages/adp-tooling/test/unit/writer/project-utils.test.ts b/packages/adp-tooling/test/unit/writer/project-utils.test.ts index b4cc322a11..23972cd5f2 100644 --- a/packages/adp-tooling/test/unit/writer/project-utils.test.ts +++ b/packages/adp-tooling/test/unit/writer/project-utils.test.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import path, { join } from 'path'; import { readFileSync } from 'fs'; import type { Editor } from 'mem-fs-editor'; @@ -28,6 +28,12 @@ describe('Project Utils', () => { }, target: { url: 'http://sap.example' + }, + options: { + enableTypeScript: false + }, + ui5: { + version: '1.133.1' } }; @@ -67,7 +73,7 @@ describe('Project Utils', () => { jest.clearAllMocks(); }); - const templatePath = '../../../templates/project'; + const templatePath = '../../../templates'; const projectPath = 'project'; const writeFilesSpy = jest.fn(); @@ -76,9 +82,26 @@ describe('Project Utils', () => { it('should write template to the specified folder', () => { writeTemplateToFolder(templatePath, projectPath, data, mockFs as unknown as Editor); - expect(writeFilesSpy.mock.calls[0][0]).toEqual(templatePath); + expect(writeFilesSpy.mock.calls[0][0]).toEqual(join(templatePath, 'project', '**', '*.*')); + expect(writeFilesSpy.mock.calls[0][1]).toEqual(projectPath); + expect(writeFilesSpy.mock.calls[0][2]).toEqual({ + ...data, + typesPackage: '@sapui5/types', + typesVersion: '~1.133.0' + }); + }); + + it('should write TS template to the specified folder when project supports typescript', () => { + const newData = { ...data, options: { enableTypeScript: true } }; + writeTemplateToFolder(templatePath, projectPath, newData, mockFs as unknown as Editor); + + expect(writeFilesSpy.mock.calls[0][0]).toEqual(join(templatePath, 'project', '**', '*.*')); expect(writeFilesSpy.mock.calls[0][1]).toEqual(projectPath); - expect(writeFilesSpy.mock.calls[0][2]).toEqual(data); + expect(writeFilesSpy.mock.calls[0][2]).toEqual({ + ...newData, + typesPackage: '@sapui5/types', + typesVersion: '~1.133.0' + }); }); it('should throw error when writing file fails', () => { diff --git a/packages/adp-tooling/tsconfig.json b/packages/adp-tooling/tsconfig.json index a3cd92f5ba..8daf46522e 100644 --- a/packages/adp-tooling/tsconfig.json +++ b/packages/adp-tooling/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../logger" }, + { + "path": "../nodejs-utils" + }, { "path": "../odata-service-writer" }, diff --git a/packages/cf-deploy-config-sub-generator/CHANGELOG.md b/packages/cf-deploy-config-sub-generator/CHANGELOG.md index 32667357d6..7ac5a9e164 100644 --- a/packages/cf-deploy-config-sub-generator/CHANGELOG.md +++ b/packages/cf-deploy-config-sub-generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux/cf-deploy-config-sub-generator +## 0.1.12 + +### Patch Changes + +- a92b715: Init should only be run when standalone + ## 0.1.11 ### Patch Changes diff --git a/packages/cf-deploy-config-sub-generator/package.json b/packages/cf-deploy-config-sub-generator/package.json index 915412ad74..b13dfd5888 100644 --- a/packages/cf-deploy-config-sub-generator/package.json +++ b/packages/cf-deploy-config-sub-generator/package.json @@ -1,7 +1,7 @@ { "name": "@sap-ux/cf-deploy-config-sub-generator", "description": "Generators for configuring Cloud Foundry deployment configuration", - "version": "0.1.11", + "version": "0.1.12", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/cf-deploy-config-sub-generator/src/app/index.ts b/packages/cf-deploy-config-sub-generator/src/app/index.ts index a6fb4d56e3..3c2718bf3c 100644 --- a/packages/cf-deploy-config-sub-generator/src/app/index.ts +++ b/packages/cf-deploy-config-sub-generator/src/app/index.ts @@ -28,14 +28,13 @@ import { mtaExecutable, cdsExecutable, generateDestinationName, - getDestination, - getConfirmMtaContinuePrompt + getDestination } from '@sap-ux/deploy-config-generator-shared'; import { t, initI18n, DESTINATION_AUTHTYPE_NOTFOUND, API_BUSINESS_HUB_ENTERPRISE_PREFIX } from '../utils'; import { loadManifest } from './utils'; import { getMtaPath, findCapProjectRoot, FileName } from '@sap-ux/project-access'; import { EventName } from '../telemetryEvents'; -import { getCFApprouterQuestionsForCap, getCFQuestions } from './questions'; +import { getCFQuestions, getCAPMTAQuestions } from './questions'; import type { ApiHubConfig, CFAppConfig, CAPConfig } from '@sap-ux/cf-deploy-config-writer'; import type { Logger } from '@sap-ux/logger'; import { CfDeployConfigOptions } from './types'; @@ -45,8 +44,7 @@ import { CfDeployConfigAnswers } from '@sap-ux/cf-deploy-config-inquirer'; import type { YeomanEnvironment } from '@sap-ux/fiori-generator-shared'; -import { withCondition } from '@sap-ux/inquirer-common'; -import type { Answers, Question } from 'inquirer'; +import type { Answers } from 'inquirer'; /** * Cloud Foundry deployment configuration generator. @@ -120,8 +118,6 @@ export default class extends DeploymentGenerator { if (!this.launchDeployConfigAsSubGenerator) { await this._init(); - } else { - await this._processProjectConfigs(); } } @@ -187,13 +183,9 @@ export default class extends DeploymentGenerator { const isCAPMissingMTA = this.isCap && this.projectRoot && !this.mtaPath; if (isCAPMissingMTA) { DeploymentGenerator.logger?.debug(t('cfGen.debug.capMissingMTA')); - // If launched as root generator, add a prompt to allow user decide if they want to add an MTA config - let questions = (await getCFApprouterQuestionsForCap({ - projectRoot: this.projectRoot ?? process.cwd() - })) as Question[]; - questions = withCondition(questions, (answers: Answers) => answers.addCapMtaContinue === true); - questions.unshift(...getConfirmMtaContinuePrompt()); - this.appRouterAnswers = (await this.prompt(questions)) as CfAppRouterDeployConfigAnswers; + this.appRouterAnswers = (await this.prompt( + await getCAPMTAQuestions({ projectRoot: this.projectRoot ?? process.cwd() }) + )) as CfAppRouterDeployConfigAnswers; if ((this.appRouterAnswers as Answers).addCapMtaContinue !== true) { this.abort = true; return; diff --git a/packages/cf-deploy-config-sub-generator/src/app/questions.ts b/packages/cf-deploy-config-sub-generator/src/app/questions.ts index 7bd7efffdd..47e5c36e03 100644 --- a/packages/cf-deploy-config-sub-generator/src/app/questions.ts +++ b/packages/cf-deploy-config-sub-generator/src/app/questions.ts @@ -1,5 +1,5 @@ import { isAppStudio } from '@sap-ux/btp-utils'; -import { DeploymentGenerator } from '@sap-ux/deploy-config-generator-shared'; +import { DeploymentGenerator, getConfirmMtaContinuePrompt } from '@sap-ux/deploy-config-generator-shared'; import { getMtaPath } from '@sap-ux/project-access'; import { appRouterPromptNames, @@ -15,6 +15,8 @@ import { getHostEnvironment, hostEnvironment } from '@sap-ux/fiori-generator-sha import { destinationQuestionDefaultOption, getCFChoices } from './utils'; import { t } from '../utils'; import type { ApiHubConfig } from '@sap-ux/cf-deploy-config-writer'; +import type { Answers, Question } from 'inquirer'; +import { withCondition } from '@sap-ux/inquirer-common'; /** * Fetches the Cloud Foundry deployment configuration questions. @@ -76,7 +78,7 @@ export async function getCFQuestions({ * @param options.projectRoot - the root path of the project. * @returns the cf approuter config questions. */ -export async function getCFApprouterQuestionsForCap({ +async function getCFApprouterQuestionsForCap({ projectRoot }: { projectRoot: string; @@ -94,3 +96,20 @@ export async function getCFApprouterQuestionsForCap({ return getAppRouterPrompts(appRouterPromptOptions); } + +/** + * Generate CF Approuter questions for CAP project with an existing HTML5 app and missing MTA configuration. + * + * @param options - the options required for retrieving the prompts. + * @param options.projectRoot - the root path of the project. + * @returns the cf approuter config questions, restricting prompts being shown to the user + */ +export async function getCAPMTAQuestions({ projectRoot }: { projectRoot: string }): Promise { + // If launched as root generator, add a prompt to allow user decide if they want to add an MTA config + let questions = (await getCFApprouterQuestionsForCap({ + projectRoot + })) as Question[]; + questions = withCondition(questions, (answers: Answers) => answers.addCapMtaContinue === true); + questions.unshift(...getConfirmMtaContinuePrompt()); + return questions; +} diff --git a/packages/cf-deploy-config-sub-generator/test/app.test.ts b/packages/cf-deploy-config-sub-generator/test/app.test.ts index 2c767c8ea2..fc4f71bd77 100644 --- a/packages/cf-deploy-config-sub-generator/test/app.test.ts +++ b/packages/cf-deploy-config-sub-generator/test/app.test.ts @@ -960,6 +960,32 @@ describe('Cloud foundry generator tests', () => { ); }); + it('Ensure init is loaded when loaded as a subgenerator', async () => { + hasbinSyncMock.mockReturnValue(true); + mockGetHostEnvironment.mockReturnValue(hostEnvironment.cli); + memfs.vol.fromNestedJSON({}, '/'); + const appDir = join(OUTPUT_DIR_PREFIX, 'app1'); + + await expect( + yeomanTest + .create( + CFGenerator, + { + resolved: cfGenPath + }, + { cwd: appDir } + ) + .withOptions({ + skipInstall: true, + launchDeployConfigAsSubGenerator: true + }) + .withPrompts({}) + .run() + ).resolves.not.toThrow(); + expect(hasbinSyncMock).toHaveBeenCalledWith('mta'); + expect(mockFindCapProjectRoot).toHaveBeenCalled(); + }); + it('Should throw error when base config is not found', async () => { hasbinSyncMock.mockReturnValue(true); mockGetHostEnvironment.mockReturnValue(hostEnvironment.cli); diff --git a/packages/create/CHANGELOG.md b/packages/create/CHANGELOG.md index 6886a981eb..96fe148a3e 100644 --- a/packages/create/CHANGELOG.md +++ b/packages/create/CHANGELOG.md @@ -1,5 +1,19 @@ # @sap-ux/create +## 0.12.0 + +### Minor Changes + +- 127bd12: feat: Add Typescript support for Adaptation Project + +### Patch Changes + +- Updated dependencies [127bd12] + - @sap-ux/adp-tooling@0.13.0 + - @sap-ux/preview-middleware@0.18.0 + - @sap-ux/flp-config-inquirer@0.2.45 + - @sap-ux/app-config-writer@0.5.32 + ## 0.11.95 ### Patch Changes diff --git a/packages/create/package.json b/packages/create/package.json index e6d3792706..c69977a3dd 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.95", + "version": "0.12.0", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/create/src/cli/generate/adaptation-project.ts b/packages/create/src/cli/generate/adaptation-project.ts index 8a031e6932..d208b90e54 100644 --- a/packages/create/src/cli/generate/adaptation-project.ts +++ b/packages/create/src/cli/generate/adaptation-project.ts @@ -21,6 +21,7 @@ export function addGenerateAdaptationProjectCommand(cmd: Command): void { .option('--url [url]', 'url pointing to the target system containing the original app') .option('--ignoreCertErrors', 'ignore certificate errors when connecting to the target system') .option('--ft', 'enable the Fiori tools for the generated project') + .option('--ts', 'enable the TypeScript support for the generated project') .option('--package [package]', 'ABAP package to be used for deployments') .option('--transport [transport]', 'ABAP transport to be used for deployments') .action(async (path, options) => { @@ -111,7 +112,8 @@ function createConfigFromDefaults(defaults: PromptDefaults): AdpWriterConfig { transport: defaults.transport ? defaults.transport.toUpperCase() : undefined }, options: { - fioriTools: defaults.ft + fioriTools: defaults.ft, + enableTypeScript: defaults.ts } }; } else { diff --git a/packages/fiori-elements-writer/CHANGELOG.md b/packages/fiori-elements-writer/CHANGELOG.md index c47b7219bf..f29e460bc3 100644 --- a/packages/fiori-elements-writer/CHANGELOG.md +++ b/packages/fiori-elements-writer/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/fiori-elements-writer +## 2.1.19 + +### Patch Changes + +- Updated dependencies [f4867e5] + - @sap-ux/ui5-test-writer@0.5.1 + ## 2.1.18 ### Patch Changes diff --git a/packages/fiori-elements-writer/package.json b/packages/fiori-elements-writer/package.json index 8ac9947022..976b650428 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": "2.1.18", + "version": "2.1.19", "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 0c19e951d0..ddbff64b89 100644 --- a/packages/fiori-freestyle-writer/CHANGELOG.md +++ b/packages/fiori-freestyle-writer/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/fiori-freestyle-writer +## 2.1.1 + +### Patch Changes + +- Updated dependencies [f4867e5] + - @sap-ux/ui5-test-writer@0.5.1 + ## 2.1.0 ### Minor Changes diff --git a/packages/fiori-freestyle-writer/package.json b/packages/fiori-freestyle-writer/package.json index 098d337d69..591c91d903 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": "2.1.0", + "version": "2.1.1", "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 ba9a2d8847..319b94e7c1 100644 --- a/packages/flp-config-inquirer/CHANGELOG.md +++ b/packages/flp-config-inquirer/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/flp-config-inquirer +## 0.2.45 + +### Patch Changes + +- Updated dependencies [127bd12] + - @sap-ux/adp-tooling@0.13.0 + ## 0.2.44 ### Patch Changes diff --git a/packages/flp-config-inquirer/package.json b/packages/flp-config-inquirer/package.json index beb8a9b59e..4b5cfe1909 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.44", + "version": "0.2.45", "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 70188584ec..6cd42d5d01 100644 --- a/packages/flp-config-sub-generator/CHANGELOG.md +++ b/packages/flp-config-sub-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @sap-ux/flp-config-sub-generator +## 0.1.26 + +### Patch Changes + +- @sap-ux/flp-config-inquirer@0.2.45 +- @sap-ux/app-config-writer@0.5.32 + ## 0.1.25 ### Patch Changes diff --git a/packages/flp-config-sub-generator/package.json b/packages/flp-config-sub-generator/package.json index b227023a54..3a7450052b 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.1.25", + "version": "0.1.26", "repository": { "type": "git", "url": "https://github.com/SAP/open-ux-tools.git", diff --git a/packages/preview-middleware-client/CHANGELOG.md b/packages/preview-middleware-client/CHANGELOG.md index f14921d3a1..70446f908f 100644 --- a/packages/preview-middleware-client/CHANGELOG.md +++ b/packages/preview-middleware-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @sap-ux-private/preview-middleware-client +## 0.12.0 + +### Minor Changes + +- 127bd12: feat: Add Typescript support for Adaptation Project + ## 0.11.69 ### Patch Changes diff --git a/packages/preview-middleware-client/package.json b/packages/preview-middleware-client/package.json index b240570eeb..b711a837b6 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.69", + "version": "0.12.0", "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 af69bffdba..4d2610017e 100644 --- a/packages/preview-middleware-client/src/adp/api-handler.ts +++ b/packages/preview-middleware-client/src/adp/api-handler.ts @@ -32,6 +32,7 @@ export interface CodeExtResponse { controllerPath: string; controllerPathFromRoot: string; isRunningInBAS: boolean; + isTsSupported: boolean; } export interface AnnotationFileDetails { diff --git a/packages/preview-middleware-client/src/adp/controllers/ControllerExtension.controller.ts b/packages/preview-middleware-client/src/adp/controllers/ControllerExtension.controller.ts index cc108aecce..a7eb9140d4 100644 --- a/packages/preview-middleware-client/src/adp/controllers/ControllerExtension.controller.ts +++ b/packages/preview-middleware-client/src/adp/controllers/ControllerExtension.controller.ts @@ -24,13 +24,7 @@ import type SimpleForm from 'sap/ui/layout/form/SimpleForm'; import type ElementOverlay from 'sap/ui/dt/ElementOverlay'; import type { CodeExtResponse, ControllersResponse } from '../api-handler'; -import { - getExistingController, - getManifestAppdescr, - readControllers, - writeChange, - writeController -} from '../api-handler'; +import { getExistingController, readControllers, writeChange, writeController } from '../api-handler'; import BaseDialog from './BaseDialog.controller'; import { getControllerInfo } from '../utils'; @@ -44,6 +38,7 @@ type ControllerModel = JSONModel & { getProperty(sPath: '/newControllerName'): string; getProperty(sPath: '/viewId'): string; getProperty(sPath: '/controllerPath'): string; + getProperty(sPath: '/controllerExtension'): string; }; /** @@ -158,20 +153,13 @@ export default class ControllerExtension extends BaseDialog { const overlayControl = sap.ui.getCore().byId(selectorId) as unknown as ElementOverlay; const { controllerName, viewId } = getControllerInfo(overlayControl); - const existingController = await this.getExistingController(controllerName); - - if (existingController) { - const { controllerExists, controllerPath, controllerPathFromRoot, isRunningInBAS } = existingController; - - if (controllerExists) { - this.updateModelForExistingController( - controllerExists, - controllerPath, - controllerPathFromRoot, - isRunningInBAS - ); + const data = await this.getExistingController(controllerName); + + if (data) { + if (data?.controllerExists) { + this.updateModelForExistingController(data); } else { - this.updateModelForNewController(viewId); + this.updateModelForNewController(viewId, data.isTsSupported); await this.getControllers(); } @@ -180,17 +168,11 @@ export default class ControllerExtension extends BaseDialog { /** * Updates the model properties for an existing controller. * - * @param {boolean} controllerExists - Whether the controller exists. - * @param {string} controllerPath - The controller path. - * @param {string} controllerPathFromRoot - The controller path from the project root. - * @param {boolean} isRunningInBAS - Whether the environment is BAS or VS Code. + * @param {CodeExtResponse} data - Existing controller data from the server. */ - private updateModelForExistingController( - controllerExists: boolean, - controllerPath: string, - controllerPathFromRoot: string, - isRunningInBAS: boolean - ): void { + private updateModelForExistingController(data: CodeExtResponse): void { + const { controllerExists, controllerPath, controllerPathFromRoot, isRunningInBAS } = data; + this.model.setProperty('/controllerExists', controllerExists); this.model.setProperty('/controllerPath', controllerPath); this.model.setProperty('/controllerPathFromRoot', controllerPathFromRoot); @@ -214,17 +196,19 @@ export default class ControllerExtension extends BaseDialog { /** * Updates the model property for a new controller. * - * @param viewId The view ID + * @param {string} viewId - The view ID. + * @param {boolean} isTsSupported - Whether TypeScript supported for the current project. */ - private updateModelForNewController(viewId: string): void { + private updateModelForNewController(viewId: string, isTsSupported: boolean): void { this.model.setProperty('/viewId', viewId); + this.model.setProperty('/controllerExtension', isTsSupported ? '.ts' : '.js'); } /** * Retrieves existing controller data if found in the project's workspace. * - * @param controllerName Controller name that exists in the view - * @returns Returnsexisting controller data + * @param controllerName Controller name that exists in the view. + * @returns Returns existing controller data. */ private async getExistingController(controllerName: string): Promise { let data: CodeExtResponse | undefined; @@ -257,8 +241,7 @@ export default class ControllerExtension extends BaseDialog { */ private async createNewController(controllerName: string, viewId: string): Promise { try { - const manifest = await getManifestAppdescr(); - await writeController({ controllerName, projectId: manifest.id }); + await writeController({ controllerName }); const controllerRef = { codeRef: `coding/${controllerName}.js`, diff --git a/packages/preview-middleware-client/src/adp/ui/ControllerExtension.fragment.xml b/packages/preview-middleware-client/src/adp/ui/ControllerExtension.fragment.xml index e3a365533a..6dab5b6b06 100644 --- a/packages/preview-middleware-client/src/adp/ui/ControllerExtension.fragment.xml +++ b/packages/preview-middleware-client/src/adp/ui/ControllerExtension.fragment.xml @@ -16,8 +16,8 @@