UNPKG

9.95 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3/*
4 * Copyright (c) 2018, salesforce.com, inc.
5 * All rights reserved.
6 * SPDX-License-Identifier: BSD-3-Clause
7 * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
8 */
9const path_1 = require("path");
10const configAggregator_1 = require("./config/configAggregator");
11const configFile_1 = require("./config/configFile");
12const kit_1 = require("@salesforce/kit");
13const validator_1 = require("./schema/validator");
14const internal_1 = require("./util/internal");
15const sfdxError_1 = require("./sfdxError");
16const sfdc_1 = require("./util/sfdc");
17/**
18 * The sfdx-project.json config object. This file determines if a folder is a valid sfdx project.
19 *
20 * *Note:* Any non-standard (not owned by Salesforce) properties stored in sfdx-project.json should
21 * be in a top level property that represents your project or plugin.
22 *
23 * ```
24 * const project = await SfdxProjectJson.retrieve();
25 * const myPluginProperties = project.get('myplugin') || {};
26 * myPluginProperties.myprop = 'someValue';
27 * project.set('myplugin', myPluginProperties);
28 * await project.write();
29 * ```
30 *
31 * **See** [force:project:create](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm)
32 */
33class SfdxProjectJson extends configFile_1.ConfigFile {
34 constructor(options) {
35 super(options);
36 }
37 static getFileName() {
38 return internal_1.SFDX_PROJECT_JSON;
39 }
40 static getDefaultOptions(isGlobal = false) {
41 const options = configFile_1.ConfigFile.getDefaultOptions(isGlobal, SfdxProjectJson.getFileName());
42 options.isState = false;
43 return options;
44 }
45 async read() {
46 const contents = await super.read();
47 // Verify that the configObject does not have upper case keys; throw if it does. Must be heads down camel case.
48 const upperCaseKey = sfdc_1.sfdc.findUpperCaseKeys(this.toObject(), SfdxProjectJson.BLACKLIST);
49 if (upperCaseKey) {
50 throw sfdxError_1.SfdxError.create('@salesforce/core', 'core', 'InvalidJsonCasing', [upperCaseKey, this.getPath()]);
51 }
52 await this.schemaValidate();
53 return contents;
54 }
55 async write(newContents) {
56 // Verify that the configObject does not have upper case keys; throw if it does. Must be heads down camel case.
57 const upperCaseKey = sfdc_1.sfdc.findUpperCaseKeys(newContents, SfdxProjectJson.BLACKLIST);
58 if (upperCaseKey) {
59 throw sfdxError_1.SfdxError.create('@salesforce/core', 'core', 'InvalidJsonCasing', [upperCaseKey, this.getPath()]);
60 }
61 await this.schemaValidate();
62 return super.write(newContents);
63 }
64 getContents() {
65 return super.getContents();
66 }
67 getDefaultOptions(options) {
68 const defaultOptions = {
69 isState: false
70 };
71 Object.assign(defaultOptions, options || {});
72 return defaultOptions;
73 }
74 /**
75 * Validates sfdx-project.json against the schema.
76 *
77 * Set the `SFDX_PROJECT_JSON_VALIDATION` environment variable to `true` to throw an error when schema validation fails.
78 * A warning is logged by default when the file is invalid.
79 *
80 * ***See*** [sfdx-project.schema.json] (https://raw.githubusercontent.com/forcedotcom/schemas/master/schemas/sfdx-project.schema.json)
81 */
82 async schemaValidate() {
83 if (!this.hasRead) {
84 // read calls back into this method after necessarily setting this.hasRead=true
85 await this.read();
86 }
87 else {
88 try {
89 const projectJsonSchemaPath = require.resolve('@salesforce/schemas/sfdx-project.schema.json');
90 const validator = new validator_1.SchemaValidator(this.logger, projectJsonSchemaPath);
91 await validator.load();
92 await validator.validate(this.getContents());
93 }
94 catch (err) {
95 if (kit_1.env.getBoolean('SFDX_PROJECT_JSON_VALIDATION', false)) {
96 err.name = 'SfdxSchemaValidationError';
97 const sfdxError = sfdxError_1.SfdxError.wrap(err);
98 sfdxError.actions = [this.messages.getMessage('SchemaValidationErrorAction', [this.getPath()])];
99 throw sfdxError;
100 }
101 else {
102 this.logger.warn(this.messages.getMessage('SchemaValidationWarning', [this.getPath(), err.message]));
103 }
104 }
105 }
106 }
107 /**
108 * Returns the `packageDirectories` within sfdx-project.json, first reading
109 * and validating the file if necessary.
110 */
111 async getPackageDirectories() {
112 // Ensure sfdx-project.json has first been read and validated.
113 if (!this.hasRead) {
114 await this.read();
115 }
116 const contents = this.getContents();
117 const packageDirs = contents.packageDirectories.map(packageDir => {
118 // Change packageDir paths to have path separators that match the OS
119 const regex = path_1.sep === '/' ? /\\/g : /\//g;
120 packageDir.path = packageDir.path.replace(regex, path_1.sep);
121 return packageDir;
122 });
123 return packageDirs;
124 }
125}
126SfdxProjectJson.BLACKLIST = ['packageAliases'];
127exports.SfdxProjectJson = SfdxProjectJson;
128/**
129 * Represents an SFDX project directory. This directory contains a {@link SfdxProjectJson} config file as well as
130 * a hidden .sfdx folder that contains all the other local project config files.
131 *
132 * ```
133 * const project = await SfdxProject.resolve();
134 * const projectJson = await project.resolveProjectConfig();
135 * console.log(projectJson.sfdcLoginUrl);
136 * ```
137 */
138class SfdxProject {
139 /**
140 * Do not directly construct instances of this class -- use {@link SfdxProject.resolve} instead.
141 *
142 * @ignore
143 */
144 constructor(path) {
145 this.path = path;
146 }
147 /**
148 * Get a Project from a given path or from the working directory.
149 * @param path The path of the project.
150 *
151 * **Throws** *{@link SfdxError}{ name: 'InvalidProjectWorkspace' }* If the current folder is not located in a workspace.
152 */
153 static async resolve(path) {
154 const _path = path || process.cwd();
155 if (!SfdxProject.instances.has(_path)) {
156 const project = new SfdxProject(await this.resolveProjectPath(_path));
157 SfdxProject.instances.set(_path, project);
158 }
159 // @ts-ignore Because of the pattern above this is guaranteed to return an instance
160 return SfdxProject.instances.get(_path);
161 }
162 /**
163 * Performs an upward directory search for an sfdx project file. Returns the absolute path to the project.
164 *
165 * @param dir The directory path to start traversing from.
166 *
167 * **Throws** *{@link SfdxError}{ name: 'InvalidProjectWorkspace' }* If the current folder is not located in a workspace.
168 *
169 * **See** {@link traverseForFile}
170 *
171 * **See** [process.cwd()](https://nodejs.org/api/process.html#process_process_cwd)
172 */
173 static async resolveProjectPath(dir) {
174 return internal_1.resolveProjectPath(dir);
175 }
176 /**
177 * Returns the project path.
178 */
179 getPath() {
180 return this.path;
181 }
182 /**
183 * Get the sfdx-project.json config. The global sfdx-project.json is used for user defaults
184 * that are not checked in to the project specific file.
185 *
186 * *Note:* When reading values from {@link SfdxProjectJson}, it is recommended to use
187 * {@link SfdxProject.resolveProjectConfig} instead.
188 *
189 * @param isGlobal True to get the global project file, otherwise the local project config.
190 */
191 async retrieveSfdxProjectJson(isGlobal = false) {
192 const options = SfdxProjectJson.getDefaultOptions(isGlobal);
193 if (isGlobal) {
194 if (!this.sfdxProjectJsonGlobal) {
195 this.sfdxProjectJsonGlobal = await SfdxProjectJson.create(options);
196 }
197 return this.sfdxProjectJsonGlobal;
198 }
199 else {
200 options.rootFolder = this.getPath();
201 if (!this.sfdxProjectJson) {
202 this.sfdxProjectJson = await SfdxProjectJson.create(options);
203 }
204 return this.sfdxProjectJson;
205 }
206 }
207 /**
208 * The project config is resolved from local and global {@link SfdxProjectJson},
209 * {@link ConfigAggregator}, and a set of defaults. It is recommended to use
210 * this when reading values from SfdxProjectJson.
211 * @returns A resolved config object that contains a bunch of different
212 * properties, including some 3rd party custom properties.
213 */
214 async resolveProjectConfig() {
215 if (!this.projectConfig) {
216 // Get sfdx-project.json from the ~/.sfdx directory to provide defaults
217 const global = await this.retrieveSfdxProjectJson(true);
218 const local = await this.retrieveSfdxProjectJson();
219 await global.read();
220 await local.read();
221 const defaultValues = {
222 sfdcLoginUrl: 'https://login.salesforce.com'
223 };
224 this.projectConfig = kit_1.defaults(local.toObject(), global.toObject(), defaultValues);
225 // Add fields in sfdx-config.json
226 Object.assign(this.projectConfig, (await configAggregator_1.ConfigAggregator.create()).getConfig());
227 // LEGACY - Allow override of sfdcLoginUrl via env var FORCE_SFDC_LOGIN_URL
228 if (process.env.FORCE_SFDC_LOGIN_URL) {
229 this.projectConfig.sfdcLoginUrl = process.env.FORCE_SFDC_LOGIN_URL;
230 }
231 }
232 return this.projectConfig;
233 }
234}
235// Cache of SfdxProject instances per path.
236SfdxProject.instances = new Map();
237exports.SfdxProject = SfdxProject;
238//# sourceMappingURL=sfdxProject.js.map
\No newline at end of file