@sap-ux/project-access
Version:
Library to access SAP Fiori tools projects
719 lines • 29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.toReferenceUri = exports.toAbsoluteUri = void 0;
exports.isCapNodeJsProject = isCapNodeJsProject;
exports.isCapJavaProject = isCapJavaProject;
exports.getCapProjectType = getCapProjectType;
exports.isCapProject = isCapProject;
exports.getCapCustomPaths = getCapCustomPaths;
exports.getCapModelAndServices = getCapModelAndServices;
exports.getCdsFiles = getCdsFiles;
exports.getCdsRoots = getCdsRoots;
exports.getCdsServices = getCdsServices;
exports.readCapServiceMetadataEdmx = readCapServiceMetadataEdmx;
exports.getCapEnvironment = getCapEnvironment;
exports.clearCdsModuleCache = clearCdsModuleCache;
exports.clearGlobalCdsModulePromiseCache = clearGlobalCdsModulePromiseCache;
exports.getCapServiceName = getCapServiceName;
exports.deleteCapApp = deleteCapApp;
const child_process_1 = require("child_process");
const path_1 = require("path");
const constants_1 = require("../constants");
const file_1 = require("../file");
const module_loader_1 = require("./module-loader");
const search_1 = require("./search");
/**
* Returns true if the project is a CAP Node.js project.
*
* @param packageJson - the parsed package.json object
* @returns - true if the project is a CAP Node.js project
*/
function isCapNodeJsProject(packageJson) {
return !!(packageJson.cds ?? packageJson.dependencies?.['@sap/cds']);
}
/**
* Returns true if the project is a CAP Java project.
*
* @param projectRoot - the root path of the project
* @param [capCustomPaths] - optional, relative CAP paths like app, db, srv
* @param memFs - optional mem-fs-editor instance
* @returns - true if the project is a CAP project
*/
async function isCapJavaProject(projectRoot, capCustomPaths, memFs) {
const srv = capCustomPaths?.srv ?? (await getCapCustomPaths(projectRoot)).srv;
return (0, file_1.fileExists)((0, path_1.join)(projectRoot, srv, 'src', 'main', 'resources', constants_1.FileName.CapJavaApplicationYaml), memFs);
}
/**
* Checks if there are files in the `srv` folder, using node fs or mem-fs.
*
* @param {string} srvFolderPath - The path to the `srv` folder to check for files.
* @param {Editor} [memFs] - An optional `mem-fs-editor` instance. If provided, the function checks files within the in-memory file system.
* @returns {Promise<boolean>} - Resolves to `true` if files are found in the `srv` folder; otherwise, `false`.
*/
async function checkFilesInSrvFolder(srvFolderPath, memFs) {
try {
return (await (0, file_1.findBy)({ root: srvFolderPath, memFs })).length > 0;
}
catch (error) {
return false;
}
}
/**
* Returns the CAP project type, undefined if it is not a CAP project.
*
* @param projectRoot - root of the project, where the package.json resides.
* @param memFs - optional mem-fs-editor instance
* @returns - CAPJava for Java based CAP projects; CAPNodejs for node.js based CAP projects; undefined if it is no CAP project
*/
async function getCapProjectType(projectRoot, memFs) {
const capCustomPaths = await getCapCustomPaths(projectRoot);
if (!(await checkFilesInSrvFolder((0, path_1.join)(projectRoot, capCustomPaths.srv), memFs))) {
return undefined;
}
if (await isCapJavaProject(projectRoot, capCustomPaths, memFs)) {
return 'CAPJava';
}
let packageJson;
try {
packageJson = await (0, file_1.readJSON)((0, path_1.join)(projectRoot, constants_1.FileName.Package), memFs);
}
catch {
// Ignore errors while reading the package.json file
}
if (packageJson && isCapNodeJsProject(packageJson)) {
return 'CAPNodejs';
}
return undefined;
}
/**
* Returns true if the project is either a CAP Node.js or a CAP Java project.
*
* @param projectRoot - the root path of the project
* @returns - true if the project is a CAP project
*/
async function isCapProject(projectRoot) {
return !!(await getCapProjectType(projectRoot));
}
/**
* Get CAP CDS project custom paths for project root.
*
* @param capProjectPath - project root of cap project
* @returns - paths to app, db, and srv for CAP project
*/
async function getCapCustomPaths(capProjectPath) {
const result = {
app: 'app/',
db: 'db/',
srv: 'srv/'
};
try {
const cdsCustomPaths = await getCapEnvironment(capProjectPath);
if (cdsCustomPaths.folders) {
result.app = cdsCustomPaths.folders.app;
result.db = cdsCustomPaths.folders.db;
result.srv = cdsCustomPaths.folders.srv;
}
}
catch (error) {
// In case of issues, fall back to the defaults
}
return result;
}
/**
* Filters service endpoints to include only OData endpoints.
*
* @param endpoint The endpoint object to check.
* @param endpoint.kind The type of the endpoint.
* @returns `true` if the endpoint is of kind 'odata' or 'odata-v4'.
*/
function filterCapServiceEndpoints(endpoint) {
return endpoint.kind === 'odata' || endpoint.kind === 'odata-v4';
}
/**
* Return the CAP model and all services. The cds.root will be set to the provided project root path.
*
* @param projectRoot - CAP project root where package.json resides or object specifying project root and optional logger to log additional info
* @returns {Promise<{ model: csn; services: ServiceInfo[]; cdsVersionInfo: CdsVersionInfo }>} - CAP Model and Services
*/
async function getCapModelAndServices(projectRoot) {
let _projectRoot;
let _logger;
let _pathSelection;
const defaultPathSelection = new Set(['app', 'srv', 'db']);
if (typeof projectRoot === 'object') {
_projectRoot = projectRoot.projectRoot;
_logger = projectRoot.logger;
_pathSelection = projectRoot.pathSelection ? projectRoot.pathSelection : defaultPathSelection;
}
else {
_pathSelection = defaultPathSelection;
_projectRoot = projectRoot;
}
const cds = await loadCdsModuleFromProject(_projectRoot, true);
const capProjectPaths = await getCapCustomPaths(_projectRoot);
const modelPaths = [];
_pathSelection?.forEach((path) => {
modelPaths.push((0, path_1.join)(_projectRoot, capProjectPaths[path]));
});
const model = await cds.load(modelPaths, { root: _projectRoot });
_logger?.info(`@sap-ux/project-access:getCapModelAndServices - Using 'cds.home': ${cds.home}`);
_logger?.info(`@sap-ux/project-access:getCapModelAndServices - Using 'cds.version': ${cds.version}`);
_logger?.info(`@sap-ux/project-access:getCapModelAndServices - Using 'cds.root': ${cds.root}`);
_logger?.info(`@sap-ux/project-access:getCapModelAndServices - Using 'projectRoot': ${_projectRoot}`);
let services = cds.compile.to.serviceinfo(model, { root: _projectRoot }) ?? [];
// filter services that have ( urlPath defined AND no endpoints) OR have endpoints with kind 'odata'
// i.e. ignore services for websockets and other unsupported protocols
if (services.filter) {
services = services.filter((service) => (service.urlPath && service.endpoints === undefined) ||
service.endpoints?.find(filterCapServiceEndpoints));
}
if (services.map) {
services = services.map((value) => {
const { endpoints, urlPath } = value;
const odataEndpoint = endpoints?.find(filterCapServiceEndpoints);
const endpointPath = odataEndpoint?.path ?? urlPath;
return {
name: value.name,
urlPath: uniformUrl(endpointPath),
runtime: value.runtime
};
});
}
return {
model,
services,
cdsVersionInfo: {
home: cds.home,
version: cds.version,
root: cds.root
}
};
}
/**
* Returns a list of cds file paths (layers). By default return list of all, but you can also restrict it to one envRoot.
*
* @param projectRoot - root of the project, where the package.json is
* @param [ignoreErrors] - optionally, default is false; if set to true the thrown error will be checked for CDS file paths in model and returned
* @param [envRoot] - optionally, the root folder or CDS file to get the layer files
* @returns - array of strings containing cds file paths
*/
async function getCdsFiles(projectRoot, ignoreErrors = false, envRoot) {
let cdsFiles = [];
try {
let csn;
envRoot ??= await getCdsRoots(projectRoot);
try {
const cds = await loadCdsModuleFromProject(projectRoot);
csn = await cds.load(envRoot, { root: projectRoot });
cdsFiles = [...(csn['$sources'] ?? [])];
}
catch (e) {
if (ignoreErrors && e.model?.sources && typeof e.model.sources === 'object') {
cdsFiles.push(...extractCdsFilesFromMessage(e.model.sources));
}
else {
throw e;
}
}
}
catch (error) {
throw Error(`Error while retrieving the list of cds files for project ${projectRoot}, envRoot ${envRoot}. Error was: ${error}`);
}
return cdsFiles;
}
/**
* Returns a list of filepaths to CDS files in root folders. Same what is done if you execute cds.resolve('*') on command line in a project.
*
* @param projectRoot - root of the project, where the package.json is
* @param [clearCache] - optionally, clear the cache, default false
* @returns - array of root paths
*/
async function getCdsRoots(projectRoot, clearCache = false) {
const roots = [];
const capCustomPaths = await getCapCustomPaths(projectRoot);
const cdsEnvRoots = [capCustomPaths.db, capCustomPaths.srv, capCustomPaths.app, 'schema', 'services'];
// clear cache is enforced to also resolve newly created cds file at design time
const cds = await loadCdsModuleFromProject(projectRoot);
if (clearCache) {
clearCdsResolveCache(cds);
}
for (const cdsEnvRoot of cdsEnvRoots) {
const resolvedRoots = cds.resolve((0, path_1.join)(projectRoot, cdsEnvRoot), {
skipModelCache: true
}) || [];
for (const resolvedRoot of resolvedRoots) {
roots.push(resolvedRoot);
}
}
return roots;
}
/**
* Return a list of services in a CAP project.
*
* @param projectRoot - root of the CAP project, where the package.json is
* @param ignoreErrors - in case loading the cds model throws an error, try to use the model from the exception object
* @returns - array of service definitions
*/
async function getCdsServices(projectRoot, ignoreErrors = true) {
let cdsServices = [];
try {
const cds = await loadCdsModuleFromProject(projectRoot);
const roots = await getCdsRoots(projectRoot);
let model;
try {
model = await cds.load(roots, { root: projectRoot });
}
catch (e) {
if (ignoreErrors && e.model) {
model = e.model;
}
else {
throw e;
}
}
const linked = cds.linked(model);
if (Array.isArray(linked.services)) {
cdsServices = linked.services;
}
else {
Object.keys(linked.services).forEach((service) => {
cdsServices.push(linked.services[service]);
});
}
}
catch (error) {
throw Error(`Error while resolving cds roots for '${projectRoot}'. ${error}`);
}
return cdsServices;
}
/**
* When an error occurs while trying to read cds files, the error object contains the source file
* information. This function extracts this file paths.
*
* @param sources - map containing the file name
* @returns - array of strings containing cds file paths
*/
function extractCdsFilesFromMessage(sources) {
const cdsFiles = [];
for (const source in sources) {
let filename = sources[source].filename;
if (typeof filename === 'string' && !filename.startsWith(path_1.sep)) {
filename = (0, path_1.join)(path_1.sep, filename);
}
if (filename) {
cdsFiles.push(filename);
}
}
return cdsFiles;
}
/**
* Remove rogue '\\' - cds windows if needed.
* Replaces all backslashes with forward slashes, removes double slashes, and trailing slashes.
*
* @param url - url to uniform
* @returns - uniform url
*/
function uniformUrl(url) {
return url
.replace(/\\/g, '/')
.replace(/\/\//g, '/')
.replace(/(?:^\/)/g, '');
}
/**
* Return the EDMX string of a CAP service.
*
* @param root - CAP project root where package.json resides
* @param uri - service path, e.g 'incident/'
* @param version - optional OData version v2 or v4
* @returns - string containing the edmx
*/
async function readCapServiceMetadataEdmx(root, uri, version = 'v4') {
try {
const { model, services } = await getCapModelAndServices(root);
const service = findServiceByUri(services, uri);
if (!service) {
throw Error(`Service for uri: '${uri}' not found. Available services: ${JSON.stringify(services)}`);
}
const cds = await loadCdsModuleFromProject(root);
const edmx = cds.compile.to.edmx(model, { service: service.name, version });
return edmx;
}
catch (error) {
throw Error(`Error while reading CAP service metadata. Path: '${root}', service uri: '${uri}', error: '${error.toString()}'}`);
}
}
/**
* Find a service in a list of services ignoring leading and trailing slashes.
*
* @param services - list of services from cds.compile.to['serviceinfo'](model)
* @param uri - search uri (usually from data source in manifest.json)
* @returns - name and uri of the service, undefined if service not found
*/
function findServiceByUri(services, uri) {
const searchUri = uniformUrl(uri).replace(/(?:^\/)|(?:\/$)/g, '');
return services.find((srv) => srv.urlPath.replace(/(?:^\/)|(?:\/$)/g, '') === searchUri);
}
/**
* Get CAP CDS project environment config for project root.
*
* @param capProjectPath - project root of a CAP project
* @returns - environment config for a CAP project
*/
async function getCapEnvironment(capProjectPath) {
const cds = await loadCdsModuleFromProject(capProjectPath);
return cds.env.for('cds', capProjectPath);
}
/**
* Load CAP CDS module. First attempt loads @sap/cds for a project based on its root.
* Second attempt loads @sap/cds from global installed @sap/cds-dk.
* Throws error if module could not be loaded or strict mode is true and there is a version mismatch.
*
* @param capProjectPath - project root of a CAP project
* @param [strict] - optional, when set true an error is thrown, if global loaded cds version does not match the cds version from package.json dependency. Default is false.
* @returns - CAP CDS module for a CAP project
*/
async function loadCdsModuleFromProject(capProjectPath, strict = false) {
let module;
let loadProjectError;
let loadError;
try {
// First approach, load @sap/cds from project
module = await (0, module_loader_1.loadModuleFromProject)(capProjectPath, '@sap/cds');
}
catch (error) {
loadProjectError = error;
}
if (!module) {
try {
// Second approach, load @sap/cds from @sap/cds-dk
module = await loadGlobalCdsModule();
}
catch (error) {
loadError = error;
}
}
if (!module) {
throw Error(`Could not load cds module. Attempt to load module @sap/cds from project threw error '${loadProjectError}', attempt to load module @sap/cds from @sap/cds-dk threw error '${loadError}'`);
}
const cds = 'default' in module ? module.default : module;
// In case strict is true and there was a fallback to global cds installation for a project that has a cds dependency, check if major versions match
if (strict && loadProjectError) {
const cdsDependencyVersion = await getCdsVersionFromPackageJson((0, path_1.join)(capProjectPath, constants_1.FileName.Package));
if (typeof cdsDependencyVersion === 'string') {
const globalCdsVersion = cds.version;
if (getMajorVersion(cdsDependencyVersion) !== getMajorVersion(globalCdsVersion)) {
const error = new Error(`The @sap/cds major version (${cdsDependencyVersion}) specified in your CAP project is different to the @sap/cds version you have installed globally (${globalCdsVersion}). Please run 'npm install' on your CAP project to ensure that the correct CDS version is loaded.`);
error.code = 'CDS_VERSION_MISMATCH';
throw error;
}
}
}
// Fix when switching cds versions dynamically
if (global) {
global.cds = cds;
}
// correct cds.env for current project root. Especially needed CAP Java projects loading cds dependency from jar file
cds.env = cds.env.for('cds', capProjectPath);
return cds;
}
/**
* Method to clear CAP CDS module cache for passed project path.
*
* @param projectRoot root of a CAP project.
* @returns True if cache cleared successfully.
*/
async function clearCdsModuleCache(projectRoot) {
let result = false;
try {
const cds = await loadCdsModuleFromProject(projectRoot);
if (cds) {
clearCdsResolveCache(cds);
result = true;
}
}
catch (e) {
// ignore exception
}
return result;
}
/**
* Method to clear CAP CDS module cache for passed cds module.
*
* @param cds CAP CDS module
*/
function clearCdsResolveCache(cds) {
cds.resolve.cache = {};
}
/**
* Get absolute path to a resource.
*
* @param projectRoot - project root of a CAP project
* @param relativeUri - relative resource path.
* @returns {string} - absolute path.
*/
const toAbsoluteUri = (projectRoot, relativeUri) => (0, path_1.join)(projectRoot, relativeUri);
exports.toAbsoluteUri = toAbsoluteUri;
/**
* Converts to referenced uri to be used in using statements.
*
* @param projectRoot - project root of a CAP project
* @param relativeUriFrom - relative uri of from directory
* @param relativeUriTo - relative uri of to directory
* @returns {Promise<string>} - reference uri
*/
const toReferenceUri = async (projectRoot, relativeUriFrom, relativeUriTo) => {
let relativeUri = '';
const indexNodeModules = relativeUriTo.lastIndexOf('node_modules');
if (indexNodeModules >= 0) {
// extract module name from fileUri - e.g. '@sap/cds/common' from '../../node_modules/@sap/cds/common.cds'
const indexLastDot = relativeUriTo.lastIndexOf('.');
if (indexLastDot > indexNodeModules + 13) {
relativeUri = relativeUriTo.slice(indexNodeModules + 13, indexLastDot);
}
else {
relativeUri = relativeUriTo.slice(indexNodeModules + 13);
}
}
else if (relativeUriTo.startsWith('../') || relativeUriTo.startsWith('..\\')) {
// file outside current project (e.g. mono repo)
const result = await getPackageNameInFolder(projectRoot, relativeUriTo);
if (result.packageName) {
relativeUri = result.packageName + relativeUriTo.slice(result.packageFolder.length);
}
}
if (!relativeUri) {
// build relative path
const fromDir = (0, path_1.dirname)((0, exports.toAbsoluteUri)(projectRoot, relativeUriFrom));
relativeUri = (0, path_1.relative)(fromDir, (0, exports.toAbsoluteUri)(projectRoot, relativeUriTo));
if (!relativeUri.startsWith('.')) {
relativeUri = './' + relativeUri;
}
}
// remove file extension
const fileExtension = relativeUri.lastIndexOf('.') > 0 ? relativeUri.slice(relativeUri.lastIndexOf('.') + 1) : '';
if (['CDS', 'JSON'].includes(fileExtension.toUpperCase())) {
relativeUri = relativeUri.slice(0, relativeUri.length - fileExtension.length - 1);
}
// always use '/' instead of platform specific separator
return relativeUri.split(path_1.sep).join('/');
};
exports.toReferenceUri = toReferenceUri;
/**
* Gets package name from the folder.
*
* @param baseUri - base uri of the cap project
* @param relativeUri - relative uri to the resource folder
* @returns {Promise<{ packageName: string; packageFolder: string }>} - package name and folder
*/
async function getPackageNameInFolder(baseUri, relativeUri) {
const refUriParts = relativeUri.split(path_1.sep);
const result = { packageName: '', packageFolder: relativeUri };
for (let i = refUriParts.length - 1; i >= 0 && !result.packageName; i--) {
const currentFolder = refUriParts.slice(0, i).join(path_1.sep);
result.packageName = await readPackageNameForFolder(baseUri, currentFolder);
if (result.packageName) {
result.packageFolder = currentFolder;
}
}
return result;
}
/**
* Reads package name from package json of the folder.
*
* @param baseUri - base uri of the cap project
* @param relativeUri - relative uri to the resource folder
* @returns {Promise<string>} - package name
*/
async function readPackageNameForFolder(baseUri, relativeUri) {
let packageName = '';
try {
const path = (0, path_1.normalize)(baseUri + '/' + relativeUri + '/' + constants_1.FileName.Package);
const content = await (0, file_1.readJSON)(path);
if (typeof content?.name === 'string') {
packageName = content.name;
}
}
catch (e) {
packageName = '';
}
return packageName;
}
// Cache for request to load global cds. Cache the promise to avoid starting multiple identical requests in parallel.
let globalCdsModulePromise;
/**
* Try to load global installation of @sap/cds, usually child of @sap/cds-dk.
*
* @returns - module @sap/cds from global installed @sap/cds-dk
*/
async function loadGlobalCdsModule() {
globalCdsModulePromise =
globalCdsModulePromise ??
new Promise((resolve, reject) => {
return getCdsVersionInfo().then((versions) => {
if (versions.home) {
resolve((0, module_loader_1.loadModuleFromProject)(versions.home, '@sap/cds'));
}
else {
reject(new Error('Can not find global installation of module @sap/cds, which should be part of @sap/cds-dk'));
}
}, reject);
});
return globalCdsModulePromise;
}
/**
* Clear cache of request to load global cds module.
*/
function clearGlobalCdsModulePromiseCache() {
globalCdsModulePromise = undefined;
}
/**
* Get cds information, which includes versions and also the home path of cds module.
*
* @param [cwd] - optional folder in which cds --version should be executed
* @returns - result of call 'cds --version'
*/
async function getCdsVersionInfo(cwd) {
return new Promise((resolve, reject) => {
let out = '';
const cdsVersionInfo = (0, child_process_1.spawn)('cds', ['--version'], { cwd, shell: true });
cdsVersionInfo.stdout.on('data', (data) => {
out += data.toString();
});
cdsVersionInfo.on('close', () => {
if (out) {
const versions = {};
for (const line of out.split('\n').filter((v) => v)) {
const [key, value] = line.split(': ');
versions[key] = value;
}
resolve(versions);
}
else {
reject(new Error('Module path not found'));
}
});
cdsVersionInfo.on('error', (error) => {
reject(error);
});
});
}
/**
* Read the version string of the @sap/cds module from the package.json file.
*
* @param packageJsonPath - path to package.json
* @returns - version of @sap/cds from package.json or undefined
*/
async function getCdsVersionFromPackageJson(packageJsonPath) {
let version;
try {
if (await (0, file_1.fileExists)(packageJsonPath)) {
const packageJson = await (0, file_1.readJSON)(packageJsonPath);
version = packageJson?.dependencies?.['@sap/cds'];
}
}
catch {
// If we can't read or parse the package.json we return undefined
}
return version;
}
/**
* Get major version from version string.
*
* @param versionString - version string
* @returns - major version as number
*/
function getMajorVersion(versionString) {
return parseInt(/\d+/.exec(versionString.split('.')[0])?.[0] ?? '0', 10);
}
/**
* Method resolves cap service name for passed project root and service uri.
*
* @param projectRoot - project root
* @param datasourceUri - service uri
* @returns - found cap service name
*/
async function getCapServiceName(projectRoot, datasourceUri) {
const services = (await getCapModelAndServices(projectRoot)).services;
const service = findServiceByUri(services, datasourceUri);
if (!service?.name) {
const errorMessage = `Service for uri: '${datasourceUri}' not found. Available services: ${JSON.stringify(services)}`;
throw Error(errorMessage);
}
return service.name;
}
/**
* Method cleans up cds files after deletion of passed appName.
*
* @param cdsFilePaths - cds files to cleanup
* @param appName - CAP application name
* @param memFs - optional mem-fs-editor instance
* @param logger - function to log messages (optional)
*/
async function cleanupCdsFiles(cdsFilePaths, appName, memFs, logger) {
const usingEntry = `using from './${appName}/annotations';`;
for (const cdsFilePath of cdsFilePaths) {
if (await (0, file_1.fileExists)(cdsFilePath, memFs)) {
try {
let cdsFile = await (0, file_1.readFile)(cdsFilePath, memFs);
if (cdsFile.indexOf(usingEntry) !== -1) {
logger?.info(`Removing using statement for './${appName}/annotations' from '${cdsFilePath}'.`);
cdsFile = cdsFile.replace(usingEntry, '');
if (cdsFile.replace(/\n/g, '').trim() === '') {
logger?.info(`File '${cdsFilePath}' is now empty, removing it.`);
await (0, file_1.deleteFile)(cdsFilePath, memFs);
}
else {
await (0, file_1.writeFile)(cdsFilePath, cdsFile, memFs);
}
}
}
catch (error) {
logger?.error(`Could not modify file '${cdsFilePath}'. Skipping this file.`);
}
}
}
}
/**
* Delete application from CAP project.
*
* @param appPath - path to the application in a CAP project
* @param [memFs] - optional mem-fs-editor instance
* @param [logger] - function to log messages (optional)
*/
async function deleteCapApp(appPath, memFs, logger) {
const appName = (0, path_1.basename)(appPath);
const projectRoot = await (0, search_1.findCapProjectRoot)(appPath);
if (!projectRoot) {
const message = `Project root was not found for CAP application with path '${appPath}'`;
logger?.error(message);
throw Error(message);
}
const packageJsonPath = (0, path_1.join)(projectRoot, constants_1.FileName.Package);
const packageJson = await (0, file_1.readJSON)(packageJsonPath, memFs);
const cdsFilePaths = [(0, path_1.join)((0, path_1.dirname)(appPath), constants_1.FileName.ServiceCds), (0, path_1.join)((0, path_1.dirname)(appPath), constants_1.FileName.IndexCds)];
logger?.info(`Deleting app '${appName}' from CAP project '${projectRoot}'.`);
// Update `sapux` array if presented in package.json
if (Array.isArray(packageJson.sapux)) {
const posixAppPath = appPath.replace(/\\/g, '/');
packageJson.sapux = packageJson.sapux.filter((a) => !posixAppPath.endsWith(a.replace(/\\/g, '/')));
if (packageJson.sapux.length === 0) {
logger?.info(`This was the last app in this CAP project. Deleting property 'sapux' from '${packageJsonPath}'.`);
delete packageJson.sapux;
}
}
if (packageJson.scripts?.[`watch-${appName}`]) {
delete packageJson.scripts[`watch-${appName}`];
}
await (0, file_1.updatePackageJSON)(packageJsonPath, packageJson, memFs);
logger?.info(`File '${packageJsonPath}' updated.`);
await (0, file_1.deleteDirectory)(appPath, memFs);
logger?.info(`Directory '${appPath}' deleted.`);
// Cleanup app/service.cds and app/index.cds files
await cleanupCdsFiles(cdsFilePaths, appName, memFs, logger);
// Check if app folder is now empty
if ((await (0, file_1.readDirectory)((0, path_1.dirname)(appPath))).length === 0) {
logger?.info(`Directory '${(0, path_1.dirname)(appPath)}' is now empty. Deleting it.`);
await (0, file_1.deleteDirectory)((0, path_1.dirname)(appPath), memFs);
}
}
//# sourceMappingURL=cap.js.map