project/lib/ui5Framework/Openui5Resolver.js

import path from "node:path";
import os from "node:os";
import AbstractResolver from "./AbstractResolver.js";
import Installer from "./npm/Installer.js";

const OPENUI5_CORE_PACKAGE = "@openui5/sap.ui.core";

/**
 * Resolver for the OpenUI5 framework
 *
 * @public
 * @class
 * @alias @ui5/project/ui5Framework/Openui5Resolver
 * @extends @ui5/project/ui5Framework/AbstractResolver
 */
class Openui5Resolver extends AbstractResolver {
	/**
	 * @param {*} options options
	 * @param {string} options.version OpenUI5 version to use
	 * @param {string} [options.cwd=process.cwd()] Working directory to resolve configurations like .npmrc
	 * @param {string} [options.ui5DataDir="~/.ui5"] UI5 home directory location. This will be used to store packages,
	 * metadata and configuration used by the resolvers. Relative to `process.cwd()`
	 * @param {string} [options.cacheDir] Where to store temp/cached packages.
	 * @param {string} [options.packagesDir] Where to install packages
	 * @param {string} [options.stagingDir] The staging directory for the packages
	 */
	constructor(options) {
		super(options);

		const {cacheDir, packagesDir, stagingDir} = options;

		this._installer = new Installer({
			cwd: this._cwd,
			ui5DataDir: this._ui5DataDir,
			cacheDir, packagesDir, stagingDir
		});
		this._loadLibraryMetadata = Object.create(null);
	}
	static _getNpmPackageName(libraryName) {
		return "@openui5/" + libraryName;
	}
	static _getLibaryName(pkgName) {
		return pkgName.replace(/^@openui5\//, "");
	}
	getLibraryMetadata(libraryName) {
		if (!this._loadLibraryMetadata[libraryName]) {
			this._loadLibraryMetadata[libraryName] = Promise.resolve().then(async () => {
				// Trigger manifest request to gather transitive dependencies
				const pkgName = Openui5Resolver._getNpmPackageName(libraryName);
				const libraryManifest = await this._installer.fetchPackageManifest({pkgName, version: this._version});
				let dependencies = [];
				if (libraryManifest.dependencies) {
					const depNames = Object.keys(libraryManifest.dependencies);
					dependencies = depNames.map(Openui5Resolver._getLibaryName);
				}

				// npm devDependencies are handled as "optionalDependencies"
				// in terms of the UI5 framework metadata structure
				let optionalDependencies = [];
				if (libraryManifest.devDependencies) {
					const devDepNames = Object.keys(libraryManifest.devDependencies);
					optionalDependencies = devDepNames.map(Openui5Resolver._getLibaryName);
				}

				return {
					id: pkgName,
					version: this._version,
					dependencies,
					optionalDependencies
				};
			});
		}
		return this._loadLibraryMetadata[libraryName];
	}
	async handleLibrary(libraryName) {
		const pkgName = Openui5Resolver._getNpmPackageName(libraryName);
		return {
			// Trigger metadata request
			metadata: this.getLibraryMetadata(libraryName),
			// Also trigger installation of package
			install: this._installer.installPackage({
				pkgName,
				version: this._version
			})
		};
	}
	static async fetchAllVersions(options) {
		const installer = this._getInstaller(options);
		return await installer.fetchPackageVersions({pkgName: OPENUI5_CORE_PACKAGE});
	}

	static async fetchAllTags(options) {
		const installer = this._getInstaller(options);
		return installer.fetchPackageDistTags({pkgName: OPENUI5_CORE_PACKAGE});
	}

	static _getInstaller({ui5DataDir, cwd} = {}) {
		return new Installer({
			cwd: cwd ? path.resolve(cwd) : process.cwd(),
			ui5DataDir:
				ui5DataDir ? path.resolve(ui5DataDir) :
					path.join(os.homedir(), ".ui5")
		});
	}
}

export default Openui5Resolver;