UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

736 lines 36 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isNamedPackagingDirectory = exports.isPackagingDirectory = exports.SfProject = exports.SfProjectJson = void 0; /* * 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 */ const node_path_1 = require("node:path"); const fs = __importStar(require("node:fs")); const kit_1 = require("@salesforce/kit"); const ts_types_1 = require("@salesforce/ts-types"); const sfdcUrl_1 = require("./util/sfdcUrl"); const configAggregator_1 = require("./config/configAggregator"); const configFile_1 = require("./config/configFile"); const validator_1 = require("./schema/validator"); const internal_1 = require("./util/internal"); const sfError_1 = require("./sfError"); const messages_1 = require("./messages"); const findUppercaseKeys_1 = require("./util/findUppercaseKeys"); ; const messages = new messages_1.Messages('@salesforce/core', 'config', new Map([["unknownConfigKey", "Unknown config name: %s."], ["deprecatedConfigKey", "Deprecated config name: %s. Please use %s instead."], ["invalidWrite", "The writeSync method is not allowed on SfdxConfig. Use the async write method instead."], ["invalidConfigValue", "Invalid config value: %s."], ["invalidInstanceUrl", "Specify a valid Salesforce instance URL."], ["invalidApiVersion", "Specify a valid Salesforce API version, for example, 42.0."], ["invalidCustomOrgMetadataTemplates", "Specify a valid repository URL or directory for the custom org metadata templates."], ["invalidIsvDebuggerSid", "Specify a valid Debugger SID."], ["invalidIsvDebuggerUrl", "Specify a valid Debugger URL."], ["invalidNumberConfigValue", "Specify a valid positive integer, for example, 150000."], ["invalidBooleanConfigValue", "The config value can only be set to true or false."], ["invalidProjectWorkspace", "This directory does not contain a valid Salesforce DX project."], ["schemaValidationError", "The config file \"%s\" is not schema valid.\nDue to: %s"], ["schemaValidationError.actions", ["Fix the invalid entries at %s."]], ["missingDefaultPath", "In sfdx-project.json, be sure to specify which package directory (path) is the default. Example: `[{ \"path\": \"packageDirectory1\", \"default\": true }, { \"path\": \"packageDirectory2\" }]`"], ["missingPackageDirectory", "The path \"%s\", specified in sfdx-project.json, does not exist. Be sure this directory is included in your project root."], ["invalidPackageDirectory", "The path \"%s\", specified in sfdx-project.json, must be indicated as a relative path to the project root."], ["multipleDefaultPaths", "In sfdx-project.json, indicate only one package directory (path) as the default."], ["singleNonDefaultPackage", "The sfdx-project.json file must include one, and only one, default package directory (path). Because your sfdx-project.json file contains only one package directory, it must be the default. Remove the `\"default\": false` key and try again."], ["target-org", "Username or alias of the org that all commands run against by default. (sf only)"], ["target-dev-hub", "Username or alias of your default Dev Hub org. (sf only)"], ["defaultUsername", "Username or alias of the org that all commands run against by default. (sfdx only)"], ["defaultDevHubUsername", "Username or alias of your default Dev Hub org. (sfdx only)"], ["isvDebuggerSid", "ISV debugger SID (sfdx only)"], ["isvDebuggerUrl", "ISV debugger URL (sfdx only)"], ["org-isv-debugger-sid", "ISV debugger SID."], ["org-isv-debugger-url", "ISV debugger URL."], ["apiVersion", "API version of your project. Default: API version of your Dev Hub org. (sfdx only)"], ["org-api-version", "API version of your project. Default: API version of your Dev Hub org."], ["disableTelemetry", "Disables the collection of usage and user environment information, etc. Default: false. (sfdx only)"], ["disable-telemetry", "Disables the collection of usage and user environment information, etc. Default: false."], ["maxQueryLimit", "Maximum number of Salesforce records returned by a CLI command. Default: 10,000. (sfdx only)"], ["org-max-query-limit", "Maximum number of Salesforce records returned by a CLI command. Default: 10,000."], ["restDeploy", "Whether deployments use the Metadata REST API (true) or SOAP API (false, default value). (sfdx only)"], ["instanceUrl", "URL of the Salesforce instance hosting your org. Default: https://login.salesforce.com. (sfdx only)"], ["org-instance-url", "URL of the Salesforce instance hosting your org. Default: https://login.salesforce.com."], ["customOrgMetadataTemplates", "A valid repository URL or directory for the custom org metadata templates."], ["org-custom-metadata-templates", "A valid repository URL or directory for the custom org metadata templates."], ["org-capitalize-record-types", "Whether record types are capitalized on scratch org creation."], ["invalidId", "The given id %s is not a valid 15 or 18 character Salesforce ID."]])); /** * The sfdx-project.json config object. This file determines if a folder is a valid sfdx project. * * *Note:* Any non-standard (not owned by Salesforce) properties stored in sfdx-project.json should * be in a top level property that represents your project. * Plugins should store their configuration @see SfProject.getPluginConfiguration and @see SfProject.setPluginConfiguration * * @example reading a standard property * ``` * const project = await SfProject.resolve(); * const projectJson = await project.resolveProjectConfig(); * const namespace = projectJson.get('namespace'); * ``` * * ``` * @example writing * const project = await SfProject.resolve(); * const projectJson = await project.resolveProjectConfig(); * projectJson.set('namespace', 'new'); * await projectJson.write(); * ``` * * **See** [force:project:create](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm) */ class SfProjectJson extends configFile_1.ConfigFile { /** json properties that are uppercase, or allow uppercase keys inside them */ static BLOCKLIST = ['packageAliases', 'plugins']; static getFileName() { return internal_1.SFDX_PROJECT_JSON; } static getDefaultOptions(isGlobal = false) { const options = configFile_1.ConfigFile.getDefaultOptions(isGlobal, SfProjectJson.getFileName()); options.isState = false; return options; } async read() { const contents = await super.read(); this.validateKeys(); return contents; } /** force a reread of the project contents if you know they may have been modified */ refreshSync() { super.readSync(false, true); return this; } readSync() { const contents = super.readSync(); this.validateKeys(); return contents; } async write() { this.validateKeys(); return super.write(); } writeSync() { this.validateKeys(); return super.writeSync(); } // eslint-disable-next-line class-methods-use-this getDefaultOptions(options) { return { ...{ isState: false }, ...(options ?? {}) }; } /** * Validates sfdx-project.json against the schema. * * Set the `SFDX_PROJECT_JSON_VALIDATION` environment variable to `true` to throw an error when schema validation fails. * A warning is logged by default when the file is invalid. * * ***See*** [sfdx-project.schema.json] ((https://github.com/forcedotcom/schemas/blob/main/sfdx-project.schema.json) */ async schemaValidate() { if (!this.hasRead) { // read calls back into this method after necessarily setting this.hasRead=true await this.read(); } try { const projectJsonSchemaPath = require.resolve('@salesforce/schemas/sfdx-project.schema.json'); const validator = new validator_1.SchemaValidator(this.logger, projectJsonSchemaPath); await validator.validate(this.getContents()); } catch (err) { const error = err; // Don't throw errors if the global isn't valid, but still warn the user. if (kit_1.env.getBoolean('SFDX_PROJECT_JSON_VALIDATION', false) && !this.options.isGlobal) { throw messages.createError('schemaValidationError', [this.getPath(), error.message], [this.getPath()], error); } else { this.logger.warn(messages.getMessage('schemaValidationError', [this.getPath(), error.message])); } } } /** * Returns the `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. */ // eslint-disable-next-line @typescript-eslint/require-await async getPackageDirectories() { return this.getPackageDirectoriesSync(); } /** * Validates sfdx-project.json against the schema. * * Set the `SFDX_PROJECT_JSON_VALIDATION` environment variable to `true` to throw an error when schema validation fails. * A warning is logged by default when the file is invalid. * * ***See*** [sfdx-project.schema.json] ((https://github.com/forcedotcom/schemas/blob/main/sfdx-project.schema.json) */ schemaValidateSync() { if (!this.hasRead) { // read calls back into this method after necessarily setting this.hasRead=true this.readSync(); } try { const projectJsonSchemaPath = require.resolve('@salesforce/schemas/sfdx-project.schema.json'); const validator = new validator_1.SchemaValidator(this.logger, projectJsonSchemaPath); validator.validateSync(this.getContents()); } catch (err) { const error = err; // Don't throw errors if the global isn't valid, but still warn the user. if (kit_1.env.getBoolean('SFDX_PROJECT_JSON_VALIDATION', false) && !this.options.isGlobal) { throw messages.createError('schemaValidationError', [this.getPath(), error.message], [this.getPath()], error); } else { this.logger.warn(messages.getMessage('schemaValidationError', [this.getPath(), error.message])); } } } /** * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. i.e. modifying this array will not affect the * sfdx-project.json file. */ getPackageDirectoriesSync() { const contents = this.getContents(); // This has to be done on the fly so it won't be written back to the file // This is a fast operation so no need to cache it so it stays immutable. const packageDirs = (contents.packageDirectories || []).map((packageDir) => { if ((0, node_path_1.isAbsolute)(packageDir.path)) { throw messages.createError('invalidPackageDirectory', [packageDir.path]); } const regex = node_path_1.sep === '/' ? /\\/g : /\//g; // Change packageDir paths to have path separators that match the OS const path = packageDir.path.replace(regex, node_path_1.sep); // Normalize and remove any ending path separators const name = (0, node_path_1.normalize)(path).replace(new RegExp(`\\${node_path_1.sep}$`), ''); // Always end in a path sep for standardization on folder paths const fullPath = `${(0, node_path_1.dirname)(this.getPath())}${node_path_1.sep}${name}${node_path_1.sep}`; if (!this.doesPackageExist(fullPath)) { throw messages.createError('missingPackageDirectory', [packageDir.path]); } return Object.assign({}, packageDir, { name, path, fullPath }); }); // If we only have one package entry, it must be the default even if not explicitly labelled if (packageDirs.length === 1) { if (packageDirs[0].default === false) { // we have one package but it is explicitly labelled as default=false throw messages.createError('singleNonDefaultPackage'); } // add default=true to the package packageDirs[0].default = true; } const defaultDirs = packageDirs.filter((packageDir) => packageDir.default); // Don't throw about a missing default path if we are in the global file. // Package directories are not really meant to be set at the global level. if (defaultDirs.length === 0 && !this.isGlobal()) { throw messages.createError('missingDefaultPath'); } else if (defaultDirs.length > 1) { throw messages.createError('multipleDefaultPaths'); } return packageDirs; } /** * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. i.e. modifying this array will not affect the * sfdx-project.json file. * * There can be multiple packages in packageDirectories that point to the same directory. * This method only returns one packageDirectory entry per unique directory path. This is * useful when doing source operations based on directories but probably not as useful * for packaging operations that want to do something for each package entry. */ getUniquePackageDirectories() { const visited = new Set(); const uniqueValues = []; // Keep original order defined in sfdx-project.json this.getPackageDirectoriesSync().forEach((packageDir) => { if (!visited.has(packageDir.name)) { visited.add(packageDir.name); uniqueValues.push(packageDir); } }); return uniqueValues; } /** * Get a list of the unique package names from within sfdx-project.json. Use {@link SfProject.getUniquePackageDirectories} * for data other than the names. */ getUniquePackageNames() { return this.getUniquePackageDirectories().map((pkgDir) => pkgDir.name); } /** * Has package directories defined in the project. */ hasPackages() { return this.getContents()?.packageDirectories?.length > 0; } /** * Has multiple package directories (MPD) defined in the project. */ hasMultiplePackages() { return this.getContents()?.packageDirectories?.length > 1; } /** * Has at least one package alias defined in the project. */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/require-await async hasPackageAliases() { return Object.keys(this.getContents().packageAliases ?? {}).length > 0; } /** * Get package aliases defined in the project. */ getPackageAliases() { return this.getContents().packageAliases; } /** * Add a package alias to the project. * If the alias already exists, it will be overwritten. * * @param alias * @param id */ addPackageAlias(alias, id) { // TODO: validate id (e.g. 04t, 0Ho) if (!/^.{15,18}$/.test(id)) { throw messages.createError('invalidId', [id]); } const newAliases = { ...(this.getContents().packageAliases ?? {}), [alias]: id }; this.contents.set('packageAliases', newAliases); } /** * Add a package directory to the project. * If the package directory already exists, the new directory * properties will be merged with the existing properties. * * @param packageDir */ addPackageDirectory(packageDir) { const dirIndex = this.getContents().packageDirectories.findIndex(findPackageDir(packageDir)); // merge new package dir with existing entry, if present const packageDirEntry = Object.assign({}, dirIndex > -1 ? this.getContents().packageDirectories[dirIndex] : packageDir, packageDir); const modifiedPackagesDirs = dirIndex > -1 ? // replace the matching entry with the new entry this.getContents().packageDirectories.map((pd, i) => (i === dirIndex ? packageDir : pd)) : // add the new entry to the end of the list [...(this.getContents()?.packageDirectories ?? []), packageDirEntry]; this.set('packageDirectories', modifiedPackagesDirs); } // keep it because testSetup stubs it! // eslint-disable-next-line class-methods-use-this doesPackageExist(packagePath) { return fs.existsSync(packagePath); } validateKeys() { (0, findUppercaseKeys_1.ensureNoUppercaseKeys)(this.getPath())(SfProjectJson.BLOCKLIST)(this.toObject()); } } exports.SfProjectJson = SfProjectJson; /** * Represents an SFDX project directory. This directory contains a {@link SfProjectJson} config file as well as * a hidden .sfdx folder that contains all the other local project config files. * * ``` * const project = await SfProject.resolve(); * const projectJson = await project.resolveProjectConfig(); * console.log(projectJson.sfdcLoginUrl); * ``` */ class SfProject { path; // Cache of SfProject instances per path. static instances = new Map(); // eslint-disable-next-line @typescript-eslint/no-explicit-any projectConfig; // Dynamically referenced in retrieveSfProjectJson sfProjectJson; sfProjectJsonGlobal; packageDirectories; activePackage; packageAliases; /** * Do not directly construct instances of this class -- use {@link SfProject.resolve} instead. * * @ignore */ constructor(path) { this.path = path; } /** * Clear the cache to force reading from disk. * * *NOTE: Only call this method if you must and you know what you are doing.* */ static clearInstances() { SfProject.instances.clear(); } /** * Get a Project from a given path or from the working directory. * * @param path The path of the project. * * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace. */ static async resolve(path) { const resolvedPath = await this.resolveProjectPath(path ?? process.cwd()); return this.getMemoizedInstance(resolvedPath); } /** * Get a Project from a given path or from the working directory. * * @param path The path of the project. * * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace. */ static getInstance(path) { // Store instance based on the path of the actual project. const resolvedPath = this.resolveProjectPathSync(path ?? process.cwd()); return this.getMemoizedInstance(resolvedPath); } /** * Performs an upward directory search for an sfdx project file. Returns the absolute path to the project. * * @param dir The directory path to start traversing from. * * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace. * * **See** {@link traverseForFile} * * **See** [process.cwd()](https://nodejs.org/api/process.html#process_process_cwd) */ static async resolveProjectPath(dir) { return (0, internal_1.resolveProjectPath)(dir); } /** * Performs a synchronous upward directory search for an sfdx project file. Returns the absolute path to the project. * * @param dir The directory path to start traversing from. * * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace. * * **See** {@link traverseForFileSync} * * **See** [process.cwd()](https://nodejs.org/api/process.html#process_process_cwd) */ static resolveProjectPathSync(dir) { return (0, internal_1.resolveProjectPathSync)(dir); } /** shared method for resolve and getInstance. * Cannot be a module-level function because instances is private */ static getMemoizedInstance(path) { if (!SfProject.instances.has(path)) { const project = new SfProject(path); SfProject.instances.set(path, project); } return (0, ts_types_1.ensure)(SfProject.instances.get(path)); } /** * Returns the project path. */ getPath() { return this.path; } /** * Get the sfdx-project.json config. The global sfdx-project.json is used for user defaults * that are not checked in to the project specific file. * * *Note:* When reading values from {@link SfProjectJson}, it is recommended to use * {@link SfProject.resolveProjectConfig} instead. * * @param isGlobal True to get the global project file, otherwise the local project config. */ async retrieveSfProjectJson(isGlobal = false) { const options = SfProjectJson.getDefaultOptions(isGlobal); if (isGlobal) { if (!this.sfProjectJsonGlobal) { this.sfProjectJsonGlobal = await SfProjectJson.create(options); } return this.sfProjectJsonGlobal; } else { options.rootFolder = this.getPath(); if (!this.sfProjectJson) { this.sfProjectJson = await SfProjectJson.create(options); } return this.sfProjectJson; } } /** * Get the sfdx-project.json config. The global sfdx-project.json is used for user defaults * that are not checked in to the project specific file. * * *Note:* When reading values from {@link SfProjectJson}, it is recommended to use * {@link SfProject.resolveProjectConfig} instead. * * This is the sync method of {@link SfProject.resolveSfProjectJson} * * @param isGlobal True to get the global project file, otherwise the local project config. */ getSfProjectJson(isGlobal = false) { const options = SfProjectJson.getDefaultOptions(isGlobal); if (isGlobal) { if (!this.sfProjectJsonGlobal) { this.sfProjectJsonGlobal = new SfProjectJson(options); this.sfProjectJsonGlobal.readSync(); } return this.sfProjectJsonGlobal; } else { options.rootFolder = this.getPath(); if (!this.sfProjectJson) { this.sfProjectJson = new SfProjectJson(options); this.sfProjectJson.readSync(); } return this.sfProjectJson; } } /** * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. i.e. modifying this array will not affect the * sfdx-project.json file. */ getPackageDirectories() { if (!this.packageDirectories) { this.packageDirectories = this.getSfProjectJson().getPackageDirectoriesSync(); } return this.packageDirectories; } /** * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. i.e. modifying this array will not affect the * sfdx-project.json file. * * There can be multiple packages in packageDirectories that point to the same directory. * This method only returns one packageDirectory entry per unique directory path. This is * useful when doing source operations based on directories but probably not as useful * for packaging operations that want to do something for each package entry. */ getUniquePackageDirectories() { return this.getSfProjectJson().getUniquePackageDirectories(); } /** * Get a list of the unique package names from within sfdx-project.json. Use {@link SfProject.getUniquePackageDirectories} * for data other than the names. */ getUniquePackageNames() { return this.getSfProjectJson().getUniquePackageNames(); } /** * Returns the package from a file path. * * @param path A file path. E.g. /Users/jsmith/projects/ebikes-lwc/force-app/apex/my-cls.cls */ getPackageFromPath(path) { const packageDirs = this.getPackageDirectories(); // resolve the given path const fullPath = (0, node_path_1.resolve)(path); const match = packageDirs.find((packageDir) => { // fullPath will not have a trailing slash, so remove it from packageDir.fullPath, if it exists const fullPathSansTrailingSep = packageDir.fullPath.replace(/(\\|\/)$/, ''); return ((0, node_path_1.basename)(path) === packageDir.path || fullPath === fullPathSansTrailingSep || fullPath.includes(packageDir.fullPath)); }); return match; } /** * Returns the package name, E.g. 'force-app', from a file path. * * @param path A file path. E.g. /Users/jsmith/projects/ebikes-lwc/force-app/apex/my-cls.cls */ getPackageNameFromPath(path) { const packageDir = this.getPackageFromPath(path); if (!packageDir) return undefined; return (0, exports.isNamedPackagingDirectory)(packageDir) ? packageDir.package : packageDir.path; } /** * Returns the package directory. * * @param packageName Name of the package directory. E.g., 'force-app' */ getPackage(packageName) { const packageDirs = this.getPackageDirectories(); return packageDirs.find((packageDir) => packageDir.name === packageName); } /** * Returns the package directory. * * @param packageName Name of the package directory. E.g., 'force-app' */ findPackage(predicate) { return this.getPackageDirectories().find(predicate); } /** * Returns the absolute path of the package directory ending with the path separator. * E.g., /Users/jsmith/projects/ebikes-lwc/force-app/ * * @param packageName Name of the package directory. E.g., 'force-app' */ getPackagePath(packageName) { const packageDir = this.getPackage(packageName); return packageDir?.fullPath; } /** * Has package directories defined in the project. */ hasPackages() { return this.getSfProjectJson().hasPackages(); } /** * Has multiple package directories (MPD) defined in the project. */ hasMultiplePackages() { return this.getSfProjectJson().hasMultiplePackages(); } /** * Get the currently activated package on the project. This has no implication on sfdx-project.json * but is useful for keeping track of package and source specific options in a process. */ getActivePackage() { return this.activePackage; } /** * Set the currently activated package on the project. This has no implication on sfdx-project.json * but is useful for keeping track of package and source specific options in a process. * * @param packageName The package name to activate. E.g. 'force-app' */ setActivePackage(packageName) { if (packageName == null) { this.activePackage = null; } else { this.activePackage = this.getPackage(packageName); } } /** * Get the project's default package directory defined in sfdx-project.json using first 'default: true' * found. The first entry is returned if no default is specified. */ getDefaultPackage() { if (!this.hasPackages()) { throw new sfError_1.SfError('The sfdx-project.json does not have any packageDirectories defined.', 'NoPackageDirectories', [ `Check ${this.getPath()} for packageDirectories.`, ]); } const defaultPackage = this.findPackage((packageDir) => packageDir.default === true); return defaultPackage ?? this.getPackageDirectories()[0]; } /** * The project config is resolved from local and global {@link SfProjectJson}, * {@link ConfigAggregator}, and a set of defaults. It is recommended to use * this when reading values from SfProjectJson. * * The global {@link SfProjectJson} is used to allow the user to provide default values they * may not want checked into their project's source. * * @returns A resolved config object that contains a bunch of different * properties, including some 3rd party custom properties. */ async resolveProjectConfig() { if (!this.projectConfig) { // Do fs operations in parallel const [global, local, configAggregator] = await Promise.all([ this.retrieveSfProjectJson(true), this.retrieveSfProjectJson(), configAggregator_1.ConfigAggregator.create(), ]); await Promise.all([global.read(), local.read()]); this.projectConfig = (0, kit_1.defaults)(local.toObject(), global.toObject()); // Add fields in sfdx-config.json Object.assign(this.projectConfig, configAggregator.getConfig()); // we don't have a login url yet, so use instanceUrl from config or default // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!this.projectConfig.sfdcLoginUrl) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.projectConfig.sfdcLoginUrl = configAggregator.getConfig()['org-instance-url'] ?? sfdcUrl_1.SfdcUrl.PRODUCTION; } // LEGACY - Allow override of sfdcLoginUrl via env var FORCE_SFDC_LOGIN_URL if (process.env.FORCE_SFDC_LOGIN_URL) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.projectConfig.sfdcLoginUrl = process.env.FORCE_SFDC_LOGIN_URL; } // Allow override of signupTargetLoginUrl via env var SFDX_SCRATCH_ORG_CREATION_LOGIN_URL if (process.env.SFDX_SCRATCH_ORG_CREATION_LOGIN_URL) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.projectConfig.signupTargetLoginUrl = process.env.SFDX_SCRATCH_ORG_CREATION_LOGIN_URL; } } // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this.projectConfig; } async hasPackageAliases() { return this.getSfProjectJson().hasPackageAliases(); } /** * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading * and validating the file if necessary. i.e. modifying this array will not affect the * sfdx-project.json file. */ getPackageAliases() { if (!this.packageAliases) { this.packageAliases = this.getSfProjectJson().getPackageAliases(); } return this.packageAliases; } getPackageIdFromAlias(alias) { const packageAliases = this.getPackageAliases(); return packageAliases ? packageAliases[alias] : undefined; } getAliasesFromPackageId(id) { if (!/^.{15,18}$/.test(id)) { throw messages.createError('invalidId', [id]); } return Object.entries(this.getPackageAliases() ?? {}) .filter(([, value]) => value?.startsWith(id)) .map(([key]) => key); } /** * retrieve the configuration for a named plugin from sfdx-project.json.plugins.pluginName * * @example * ``` * const project = await SfProject.resolve(); * const pluginConfig = await project.getPluginConfiguration('myPlugin'); * ``` * * optionally pass a type parameter for your plugin configuration's schema * */ async getPluginConfiguration(pluginName) { await this.retrieveSfProjectJson(); const plugins = this.sfProjectJson.get('plugins'); if (!plugins) { throw new sfError_1.SfError('No plugins defined in sfdx-project.json', 'NoPluginsDefined'); } if (!plugins[pluginName]) { throw new sfError_1.SfError(`No configuration defined in sfdx-project.json for plugin ${pluginName}`, 'PluginNotFound'); } return plugins[pluginName]; } /** * set the configuration for a named plugin from sfdx-project.json.plugins.pluginName, overwriting existing configuration * * @example * ``` * const project = await SfProject.resolve(); * const pluginConfig = await project.setPluginConfiguration('myPlugin', {foo: 'bar', myLimit: 25}); * ``` * * optionally pass a type parameter for your plugin configuration's schema * */ async setPluginConfiguration(pluginName, config) { await this.retrieveSfProjectJson(); const plugins = this.getSfProjectJson().get('plugins') ?? {}; const modified = { ...plugins, [pluginName]: config }; this.sfProjectJson.set('plugins', modified); this.sfProjectJson.writeSync(); } } exports.SfProject = SfProject; /** differentiate between the Base PackageDir (path, maybe default) and the Packaging version (package and maybe a LOT of other fields) by whether is has the `package` property */ const isPackagingDirectory = (packageDir) => isPackagingDir(packageDir); exports.isPackagingDirectory = isPackagingDirectory; /** differentiate between the Base PackageDir (path, maybe default) and the Packaging version (package and maybe a LOT of other fields) by whether is has the `package` property */ const isNamedPackagingDirectory = (packageDir) => isPackagingDir(packageDir); exports.isNamedPackagingDirectory = isNamedPackagingDirectory; const isPackagingDir = (packageDir) => 'package' in packageDir && typeof packageDir.package === 'string'; /** * there is no notion of uniqueness in package directory entries * so an attempt of matching an existing entry is a bit convoluted */ const findPackageDir = (target) => (potentialMatch) => // an entry w/o a package or id is considered a directory entry for which a package has yet to be created // find a matching dir entry that where path is the same and id and package are not present (potentialMatch.path === target.path && !('id' in potentialMatch) && !(0, exports.isPackagingDirectory)(potentialMatch)) || // if that fails, then find a matching dir entry package is present and is same as the new entry ((0, exports.isPackagingDirectory)(target) && (0, exports.isPackagingDirectory)(potentialMatch) && target.package === potentialMatch.package); //# sourceMappingURL=sfProject.js.map