UNPKG

@fontoxml/fontoxml-development-tools

Version:

Development tools for Fonto.

255 lines (226 loc) 6.77 kB
import fs from 'fs'; import path from 'path'; import { getFileMatchInDirectoryAncestrySync } from './getParentDirectoryContainingFileSync.js'; import Version from './Version.js'; /** @typedef {import('./App').default} App */ /** @typedef {import('./request/FdtCommand').default} FdtCommand */ /** @typedef {import('./request/FdtRequest').default} FdtRequest */ /** @typedef {{ location?: string, configLocation?: string }} Overrides */ /** @typedef {(request: FdtRequest) => Overrides} GetOverrides */ /** @typedef {Map<FdtCommand, GetOverrides>} GetOverridesByCommand */ /** * @typedef Product * * @property {string} productId * @property {string} manifestFileName * @property {string} manifestVersionPropertyName * @property {GetOverridesByCommand} getOverridesByCommand */ export default class BackendAppRepository { /** @type {{[key:string]: Product }} */ static _products = {}; /** * @param {string} productId * @param {string} manifestFileName * @param {string} manifestVersionPropertyName * @param {GetOverridesByCommand} [getOverridesByCommand=new Map()] */ static registerBackendApp( productId, manifestFileName, manifestVersionPropertyName, getOverridesByCommand = new Map() ) { this._products[productId] = { productId, manifestFileName, manifestVersionPropertyName, getOverridesByCommand, }; } /** * @param {string} manifestFilename * * @return {string | undefined} */ static getProductIdByManifestFilename(manifestFilename) { const product = Object.values(this._products).find( (product) => product.manifestFileName === manifestFilename ); return product ? product.productId : undefined; } /** * @param {string} productId * * @return {string | undefined} */ static getManifestVersionPropertyNameByProductId(productId) { const product = this._products[productId]; return product ? product.manifestVersionPropertyName : undefined; } /** * @return {string[]} */ static getManifestFileNames() { return Object.values(this._products).map( (product) => product.manifestFileName ); } /** * @param {FdtRequest} request * * @return {Overrides} */ static getOverridesForRequest(request) { /** @type {GetOverrides | undefined} */ const getOverrides = Object.values(this._products).reduce( (acc, product) => { if (acc) { return acc; } return product.getOverridesByCommand.get(request.command); }, undefined ); let location; let configLocation; if (getOverrides) { const overrides = getOverrides(request); location = overrides.location; configLocation = overrides.configLocation; } return { location, configLocation }; } /** * @param {App} app * @param {FdtRequest} request * * @return {Promise<void>} */ async init(app, request) { this._app = app; this.path = null; this.productId = null; this.manifestFilename = null; this.manifestVersionPropertyName = null; this.config = null; this.configSource = null; /** @type {Version | null} */ this.sdkVersion = null; /** @type {Error | null} */ this.manifestError = null; const overrides = BackendAppRepository.getOverridesForRequest(request); const manifestFileNames = BackendAppRepository.getManifestFileNames(); const manifestPath = getFileMatchInDirectoryAncestrySync( path.resolve(overrides.location || process.cwd()), manifestFileNames ); if (!manifestPath) { return; } const manifestFilename = path.basename(manifestPath); let productId; try { productId = BackendAppRepository.getProductIdByManifestFilename( manifestFilename ); if (!productId) { throw new Error( `Could not get productId from manifest filename "${manifestFilename}".` ); } } catch (error) { this.manifestError = error; this._app.logger.notice( `Could not determine product from "${manifestFilename}".` ); this._app.logger.notice( `Please check and fix your "${manifestFilename}" file.` ); this._app.logger.break(); return; } let sdkVersion = null; let manifestVersionPropertyName = null; try { const manifestJson = JSON.parse( fs.readFileSync(manifestPath, 'utf8') ); const propertyName = BackendAppRepository.getManifestVersionPropertyNameByProductId( productId ); if (!propertyName) { throw new Error( `Could not map product ${productId} to version property name.` ); } const version = manifestJson[propertyName]; if (!version) { throw new Error( `Manifest does not have a version specified in the ${propertyName} property.` ); } manifestVersionPropertyName = propertyName; sdkVersion = new Version(version); } catch (error) { this.manifestError = error; this._app.logger.notice(`Could not read ${manifestFilename}.`); this._app.logger.notice( `Please check and fix your ${productId} ${manifestFilename} file.` ); this._app.logger.break(); return; } this.path = path.dirname(manifestPath); this.productId = productId; this.manifestFilename = manifestFilename; this.manifestVersionPropertyName = manifestVersionPropertyName; this.sdkVersion = sdkVersion; try { const configPath = overrides.configLocation ? path.resolve(overrides.configLocation) : path.join(this.path, '.env'); this.config = fs .readFileSync(configPath, 'utf8') .split(/\r?\n/g) .reduce((acc, line) => { if (!line.startsWith('#') && line.includes('=')) { const [key, ...rest] = line.split('='); const value = rest.join('='); acc[key] = value; } return acc; }, {}); this.configSource = configPath; } catch (_error) { // No-op } } /** * @param {string} [productId] */ throwIfNotInsideBackendAppRepository(productId) { if (!this.path) { const requiredProductId = productId ? `Fonto ${productId}` : 'Fonto backend app'; throw new this._app.logger.ErrorWithSolution( `Not running from inside a ${requiredProductId} repository.`, `This command requires to be run from inside a ${requiredProductId} repository.`, this.manifestError ? this.manifestError : undefined ); } if (productId && this.productId !== productId) { const currentProductId = this.productId ? `Fonto ${this.productId}` : 'different Fonto backend app'; throw new this._app.logger.ErrorWithSolution( `Not running from inside a Fonto ${productId} repository, but from a ${currentProductId} repository.`, `This command requires to be run from inside a Fonto ${productId} repository.`, this.manifestError ? this.manifestError : undefined ); } } }