UNPKG

salesforce-alm

Version:

This package contains tools, and APIs, for an improved salesforce.com developer experience.

348 lines (346 loc) 14.8 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Config = void 0; /* -------------------------------------------------------------------------------------------------------------------- * WARNING: This file has been deprecated and should now be considered locked against further changes. Its contents * have been partially or wholely superceded by functionality included in the @salesforce/core npm package, and exists * now to service prior uses in this repository only until they can be ported to use the new @salesforce/core library. * * If you need or want help deciding where to add new functionality or how to migrate to the new library, please * contact the CLI team at alm-cli@salesforce.com. * ----------------------------------------------------------------------------------------------------------------- */ // Node const fs = require("fs"); const path = require("path"); // Thirdparty const BBPromise = require("bluebird"); const _ = require("lodash"); // Local module object. const core_1 = require("@salesforce/core"); const messages = require("../messages"); const errors = require("./errors"); const almError = require("./almError"); const projectDirectory = require("./projectDir"); const srcDevUtil = require("./srcDevUtil"); const consts = require("./constants"); const pjson = require('../../../package.json'); const _DEFAULT_PORT = 1717; const fsWriteFile = BBPromise.promisify(fs.writeFile); const checkHiddenStateFolder = function (projectDir) { const stateFolderPath = path.join(projectDir, srcDevUtil.getWorkspaceStateFolderName()); if (!srcDevUtil.pathExistsSync(stateFolderPath)) { try { // Make sure state folder exists in the root of the workspace. // @todo Fix sync. fs.mkdirSync(stateFolderPath); } catch (err) { // Rethrow the error if it's something other than directory already exists. if (err.code !== 'EEXIST') { throw err; } } } }; const sfdxProjectBlockList = ['packageAliases']; // constructor exports.Config = function (projectDir) { this.projectDir = projectDir; this.pjson = pjson; }; const _throwUnexpectedVersionFormat = function (incorrectVersion) { const errorName = 'UnexpectedVersionFormat'; throw srcDevUtil.getError(messages().getMessage(errorName, [incorrectVersion], 'versionCommand'), errorName); }; /** * Return the salesforce toolbelt api version: * * How we find the version number: * 1) see if it's defined in $HOME/.sfdx/sfdx-project.json * 2) Otherwise get it from package.json. * * @returns {string} */ exports.Config.prototype.getApiVersion = function () { // If we already stored an api version return it. if (this.apiVersion) { return this.apiVersion; } try { const apiVersion = core_1.ConfigAggregator.getValue('apiVersion').value; const apiVersionRegEx = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)/; if (apiVersionRegEx.test(apiVersion)) { this.apiVersion = apiVersion; } // If there is something in workspace config and it didn't validate throw an error. else if (apiVersion) { _throwUnexpectedVersionFormat(apiVersion); } // else proceed } catch (e) { if (e.code !== 'ENOENT') { throw e; } } // Not globally defined so the apiVersion comes off of package.json version. if (!this.apiVersion) { // No version specified in pjson - unlikely but... if (this.pjson.version == null) { const errorName = 'MissingVersionAttribute'; throw srcDevUtil.getError(messages().getMessage(errorName, null, 'versionCommand'), errorName); } const versionTrimmed = this.pjson.version.trim(); const sfdxValidVersionRegEx = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?\b/gi; if (sfdxValidVersionRegEx.test(versionTrimmed)) { this.apiVersion = `${versionTrimmed.split('.')[0]}.0`; } else { _throwUnexpectedVersionFormat(versionTrimmed); } } return this.apiVersion; }; exports.Config.prototype.getProjectPath = function () { if (!this.projectDir) { this.projectDir = projectDirectory.getPath(); checkHiddenStateFolder(this.projectDir); } return this.projectDir; }; // The toolbelt only supports us english. exports.Config.prototype.getLocale = function () { return messages().getLocale(); }; /** * Users may override the oauth port used for the http server. * * @returns {number} 1717 is the default listen port number */ exports.Config.prototype.getOauthLocalPort = function () { const appConfig = this.getAppConfigIfInWorkspace(); const configPort = Number(appConfig.OauthLocalPort || appConfig.oauthLocalPort || _DEFAULT_PORT); if (Number.isNaN(configPort) || configPort < 1 || configPort > Math.pow(2, 16) - 1) { const error = new Error(messages(this.getLocale()).getMessage('invalidPortNumber', [configPort])); error['name'] = 'InvalidOAuthRedirectUrlPort'; throw error; } return configPort; }; /** * @returns {string} The App Cloud standard Connected App Callback URL. */ exports.Config.prototype.getOauthCallbackUrl = function () { return `http://localhost:${this.getOauthLocalPort()}/OauthRedirect`; }; /** * Reads the app config from disk and caches it. * * @returns {*} A key value. */ exports.Config.prototype.getAppConfig = function () { if (!this.appConfig) { this.appConfig = this.getConfigContent(); } return this.appConfig; }; /** * Reads the workspace app config, if we are in a workspace. * * @param {object} force The force object * @returns {object} the workspace config object, or an empty object if not in a workspace */ exports.Config.prototype.getAppConfigIfInWorkspace = function () { try { return this.getAppConfig(); } catch (e) { if (e.name !== 'InvalidProjectWorkspace') { throw e; } } return {}; }; exports.Config.prototype.getWorkspaceConfigFilename = function () { return consts.WORKSPACE_CONFIG_FILENAME; }; exports.Config.prototype.getOldAndBustedWorkspaceConfigFilename = function () { return consts.OLD_WORKSPACE_CONFIG_FILENAME; }; /** * Copy the artifact paths out of the sfdx-project.json, if they exist. * * @param messagesLocale This is for unit tests to solve a dependency issue * @param configObject The app config object * @param workspaceConfig The JSON representation of the workspace config file. * @param projectDir The root workspace directory * @returns {*} An array representing the artifact paths. */ const _extractPackageDirPaths = function (messagesLocale, configObject, workspaceConfig, projectDir) { const pathsArray = []; const packageDirectories = workspaceConfig.packageDirectories; if (packageDirectories && packageDirectories.length !== 0) { packageDirectories.forEach((packageDir) => { if (packageDir.path) { if (path.isAbsolute(packageDir.path)) { const error = new Error(messagesLocale.getMessage('InvalidAbsolutePath', packageDir.path)); error['name'] = 'InvalidProjectWorkspace'; throw error; } // Change packageDir paths to have path separators that match the OS const regex = path.sep === '/' ? /\\/g : /\//g; packageDir.path = packageDir.path.replace(regex, path.sep); pathsArray.push(path.resolve(projectDir, packageDir.path)); } if (packageDir.default != null) { if (typeof packageDir.default !== 'boolean') { const error = new Error(messagesLocale.getMessage('InvalidValueForDefaultPath')); error['name'] = 'InvalidProjectWorkspace'; throw error; } if (packageDir.default === true) { if (!configObject.defaultPackagePath) { configObject.defaultPackagePath = packageDir.path; } else { const error = new Error(messagesLocale.getMessage('MultipleDefaultPaths')); error['name'] = 'InvalidProjectWorkspace'; throw error; } } } else if (packageDirectories.length === 1) { configObject.defaultPackagePath = packageDir.path; } }); if (!configObject.defaultPackagePath) { const error = new Error(messagesLocale.getMessage('MissingDefaultPath')); error['name'] = 'InvalidProjectWorkspace'; throw error; } } return pathsArray; }; exports.Config.prototype._getConfigContent = function (projectDir, workspaceConfigObject) { let configObject; try { // get sfdx-project.json from the ~/.sfdx directory configObject = srcDevUtil.getGlobalConfigSync(this.getWorkspaceConfigFilename()); // Verify that the configObject does not have upper case keys; throw if it does. Must be heads down camelcase. const upperCaseKey = core_1.sfdc.findUpperCaseKeys(configObject, sfdxProjectBlockList); if (upperCaseKey) { throw almError('InvalidJsonCasing', [upperCaseKey, JSON.stringify(configObject, null, 4)]); } } catch (e) { if (e.code === 'ENOENT') { configObject = {}; } else { throw e; } } // Add additional fields from the workspace sfdx-project.json const projectConfigDotJsonPath = path.join(projectDir, this.getWorkspaceConfigFilename()); try { // Verify that the workspaceConfigObject does not have upper case keys; throw if it does. Must be heads down camelcase. const upperCaseKey = core_1.sfdc.findUpperCaseKeys(workspaceConfigObject, sfdxProjectBlockList); if (upperCaseKey) { throw almError('InvalidJsonCasing', [upperCaseKey, JSON.stringify(workspaceConfigObject, null, 4)]); } const defaultConfig = { packageDirectoryPaths: _extractPackageDirPaths(messages(this.getLocale()), configObject, workspaceConfigObject, projectDir), sfdcLoginUrl: 'https://login.salesforce.com', defaultSrcWaitMinutes: consts.DEFAULT_SRC_WAIT_MINUTES, defaultSrcWaitMs: consts.DEFAULT_SRC_WAIT_MINUTES * 60000, defaultMdapiPollIntervalMinutes: consts.DEFAULT_MDAPI_POLL_INTERVAL_MINUTES, defaultMdapiPollIntervalMs: consts.DEFAULT_MDAPI_POLL_INTERVAL_MINUTES * 60000, defaultMdapiWaitMinutes: consts.DEFAULT_MDAPI_WAIT_MINUTES, }; _.defaults(configObject, workspaceConfigObject, defaultConfig); // TODO move to SfdxAggregator when this method is converted to async // Add fields in sfdx-config.json const workspaceOrgConfigPath = srcDevUtil.getWorkspaceOrgConfigPath(projectDir); if (srcDevUtil.pathExistsSync(workspaceOrgConfigPath)) { try { const fileContents = fs.readFileSync(workspaceOrgConfigPath, 'utf8'); if (fileContents.length) { _.assign(configObject, JSON.parse(fileContents)); } } catch (e) { throw srcDevUtil.processReadAndParseJsonFileError(e, workspaceOrgConfigPath); } } // Allow override of sfdcLoginUrl via env var FORCE_SFDC_LOGIN_URL if (process.env.FORCE_SFDC_LOGIN_URL) { configObject.sfdcLoginUrl = process.env.FORCE_SFDC_LOGIN_URL; } return configObject; } catch (e) { throw srcDevUtil.processReadAndParseJsonFileError(e, projectConfigDotJsonPath); } }; const _getWorkspaceConfigObject = function (projectConfigDotJsonPath) { if (srcDevUtil.pathExistsSync(projectConfigDotJsonPath)) { return JSON.parse(fs.readFileSync(projectConfigDotJsonPath, 'utf8')); } else { throw new errors.MissingAppConfig(); } }; exports.Config.prototype.getConfigContent = function (projectDir = this.getProjectPath()) { const projectConfigDotJsonPath = path.join(projectDir, this.getWorkspaceConfigFilename()); const workspaceConfigObject = _getWorkspaceConfigObject(projectConfigDotJsonPath); return this._getConfigContent(projectDir, workspaceConfigObject); }; /** * Updates a config object on disk. * * @param config The object to save. * @param projectDir The absolute path to the directory containing the workspace. */ exports.Config.prototype.setConfigContent = function (configFileName, configDir, config) { if (config) { srcDevUtil.ensureDirectoryExistsSync(configDir); let promise = BBPromise.resolve({}); const configFilePath = path.join(configDir, configFileName); if (srcDevUtil.pathExistsSync(configFilePath)) { promise = srcDevUtil.readJSON(configFilePath); } return promise .then((existingConfig) => _.assign(existingConfig, config)) .then(async (newConfig) => { // file is customer-editable, so write w/ spaces for readability await fsWriteFile(configFilePath, JSON.stringify(newConfig, null, 4), { flag: 'w', encoding: 'utf-8', }); }); } else { throw new errors.MissingRequiredParameter(config); } }; /** * Updates the workspace org config object on disk. * * @param config The object to save. * @param projectDir The absolute path to the directory containing the workspace. */ exports.Config.prototype.setWorkspaceConfigContent = function (projectDir, config) { return this.setConfigContent(this.getWorkspaceConfigFilename(), projectDir, config); }; exports.Config.defaultSrcWaitMinutes = consts.DEFAULT_SRC_WAIT_MINUTES; exports.Config.defaultSrcWaitMs = consts.DEFAULT_SRC_WAIT_MINUTES * 60000; exports.Config.defaultMdapiPollIntervalMinutes = consts.DEFAULT_MDAPI_POLL_INTERVAL_MINUTES; exports.Config.defaultMdapiPollIntervalMs = consts.DEFAULT_MDAPI_POLL_INTERVAL_MINUTES * 60000; exports.Config.defaultMdapiWaitMinutes = consts.DEFAULT_MDAPI_WAIT_MINUTES; //# sourceMappingURL=configApi.js.map