1 | ;
|
2 | Object.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 | */
|
9 | const path_1 = require("path");
|
10 | const configAggregator_1 = require("./config/configAggregator");
|
11 | const configFile_1 = require("./config/configFile");
|
12 | const kit_1 = require("@salesforce/kit");
|
13 | const validator_1 = require("./schema/validator");
|
14 | const internal_1 = require("./util/internal");
|
15 | const sfdxError_1 = require("./sfdxError");
|
16 | const 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 | */
|
33 | class 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 | }
|
126 | SfdxProjectJson.BLACKLIST = ['packageAliases'];
|
127 | exports.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 | */
|
138 | class 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.
|
236 | SfdxProject.instances = new Map();
|
237 | exports.SfdxProject = SfdxProject;
|
238 | //# sourceMappingURL=sfdxProject.js.map |
\ | No newline at end of file |