@sap-ux/project-access
Version:
Library to access SAP Fiori tools projects
198 lines • 8.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSpecification = getSpecification;
exports.refreshSpecificationDistTags = refreshSpecificationDistTags;
exports.getSpecificationPath = getSpecificationPath;
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const path_1 = require("path");
const semver_1 = require("semver");
const module_loader_1 = require("./module-loader");
const ui5_config_1 = require("./ui5-config");
const info_1 = require("./info");
const constants_1 = require("../constants");
const file_1 = require("../file");
const command_1 = require("../command");
const specificationDistTagPath = (0, path_1.join)(constants_1.fioriToolsDirectory, constants_1.FileName.SpecificationDistTags);
/**
* Gets the dist-tag for the provided project/app and returns it.
*
* @param root - root path of the project/app
* @param [options] - optional options
* @param [options.logger] - logger instance
* @returns - specification instance
*/
async function getProjectDistTag(root, options) {
let distTag = 'latest';
try {
const webappPath = await (0, ui5_config_1.getWebappPath)(root);
const manifest = await (0, file_1.readJSON)((0, path_1.join)(webappPath, constants_1.FileName.Manifest));
const minUI5Version = (0, info_1.getMinimumUI5Version)(manifest);
if (minUI5Version && (0, semver_1.valid)(minUI5Version)) {
const [mayor, minor] = minUI5Version.split('.');
distTag = `UI5-${mayor}.${minor}`;
}
}
catch (error) {
options?.logger?.error(`Failed to get minimum UI5 version from manifest: ${error} using 'latest'`);
}
return distTag;
}
/**
* Checks if package.json contains dev dependency to specification.
*
* @param root - root path of the project/app
* @returns If dev dependency to specification is found in package.json
*/
async function hasSpecificationDevDependency(root) {
const packageJson = await (0, file_1.readJSON)((0, path_1.join)(root, constants_1.FileName.Package));
return !!packageJson.devDependencies?.['@sap/ux-specification'];
}
/**
* Loads the specification module from cache and returns it.
*
* @param root - root path of the project/app
* @param [options] - optional options
* @param [options.logger] - logger instance
* @returns - specification instance
*/
async function getSpecificationModule(root, options) {
const logger = options?.logger;
let specification;
const version = await getSpecificationVersion(root, { logger });
try {
specification = await getSpecificationByVersion(version, { logger });
logger?.debug(`Specification loaded from cache using version '${version}'`);
}
catch (error) {
logger?.error(`Failed to load specification: ${error}`);
throw new Error(`Failed to load specification: ${error}`);
}
return specification;
}
/**
* Loads and return specification from project or cache.
* 1. if package.json contains devDependency to specification, attempts to load from project.
* 2. if not in package.json of project, attempts to load from cache.
*
* @param root - root path of the project/app
* @param [options] - optional options
* @param [options.logger] - logger instance
* @returns - specification instance
*/
async function getSpecification(root, options) {
const logger = options?.logger;
try {
if (await hasSpecificationDevDependency(root)) {
logger?.debug(`Specification found in devDependencies of project '${root}', trying to load`);
// Early return with load module from project. If it throws an error it is not handled here.
return (0, module_loader_1.loadModuleFromProject)(root, '@sap/ux-specification');
}
}
catch {
logger?.debug(`Specification not found in project '${root}', trying to load from cache`);
}
return await getSpecificationModule(root, { logger });
}
/**
* Refreshes the specification dist-tags cache. Also cleans specification modules in cache that are not required anymore.
*
* @param [options] - optional options, like logger
* @param [options.logger] - logger instance
*/
async function refreshSpecificationDistTags(options) {
const logger = options?.logger;
try {
const distTagsString = await (0, command_1.execNpmCommand)(['view', '@sap/ux-specification', 'dist-tags', '--json'], {
logger
});
const distTags = JSON.parse(distTagsString);
await (0, file_1.writeFile)(specificationDistTagPath, JSON.stringify(distTags, null, 4));
const uniqueVersions = new Set(Object.values(distTags));
// Check if we have cached versions that are not required anymore
const specificationCachePath = (0, path_1.join)(constants_1.moduleCacheRoot, '@sap/ux-specification');
const removeExistingVersions = (0, fs_1.existsSync)(specificationCachePath)
? (await (0, promises_1.readdir)(specificationCachePath, { withFileTypes: true }))
.filter((d) => d.isDirectory())
.filter((d) => !uniqueVersions.has(d.name))
.map((d) => d.name)
: [];
// Delete cached versions that are not required anymore
for (const version of removeExistingVersions) {
await (0, module_loader_1.deleteModule)('@sap/ux-specification', version);
logger?.debug(`Deleted unused specification module '@sap/ux-specification@${version}' from cache`);
}
}
catch (error) {
logger?.error(`Error refreshing specification dist-tags: ${error}`);
}
}
/**
* Loads and return specification from cache by version.
*
* @param version - version of the specification
* @param [options] - optional options
* @param [options.logger] - optional logger instance
* @returns - specification instance
*/
async function getSpecificationByVersion(version, options) {
const logger = options?.logger;
const specification = await (0, module_loader_1.getModule)('@sap/ux-specification', version, { logger });
return specification;
}
/**
* Converts dist-tag to version.
*
* @param distTag - dist-tag of the specification, like 'latest' or 'UI5-1.71'
* @param [options] - optional options
* @param [options.logger] - optional logger instance
* @returns - version for given dist-tag
*/
async function convertDistTagToVersion(distTag, options) {
const logger = options?.logger;
if (!(0, fs_1.existsSync)(specificationDistTagPath)) {
logger?.debug(`Specification dist-tags not found at '${specificationDistTagPath}'. Trying to refresh.`);
await refreshSpecificationDistTags({ logger });
}
const specificationDistTags = await (0, file_1.readJSON)(specificationDistTagPath);
const version = specificationDistTags[distTag] ?? specificationDistTags.latest;
return version;
}
/**
* Gets the dist-tag of a project specification and returns the version from it.
*
* @param root - root path of the project/app
* @param [options] - optional options
* @param [options.logger] - optional logger instance
* @returns - version of specification
*/
async function getSpecificationVersion(root, options) {
const logger = options?.logger;
const distTag = await getProjectDistTag(root, { logger });
return await convertDistTagToVersion(distTag, { logger });
}
/**
* Returns the path to the specification used.
* Can be path to node_modules in project, or cache.
*
* @param root - root path of the project/app
* @param [options] - optional options
* @param [options.logger] - optional logger instance
* @returns - path to specification
*/
async function getSpecificationPath(root, options) {
const logger = options?.logger;
const moduleName = '@sap/ux-specification';
if (await hasSpecificationDevDependency(root)) {
const modulePath = await (0, module_loader_1.getModulePath)(root, moduleName);
logger?.debug(`Specification root found in project '${root}'`);
return modulePath.slice(0, modulePath.lastIndexOf((0, path_1.join)(moduleName)) + (0, path_1.join)(moduleName).length);
}
await getSpecificationModule(root, { logger });
const version = await getSpecificationVersion(root, { logger });
logger?.debug(`Specification not found in project '${root}', using path from cache with version '${version}'`);
const moduleRoot = (0, path_1.join)(constants_1.moduleCacheRoot, moduleName, version);
const modulePath = await (0, module_loader_1.getModulePath)(moduleRoot, moduleName);
return modulePath.slice(0, modulePath.lastIndexOf((0, path_1.join)(moduleName)) + (0, path_1.join)(moduleName).length);
}
//# sourceMappingURL=specification.js.map