UNPKG

8.58 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright Google Inc. All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.ConfigCommand = void 0;
11const core_1 = require("@angular-devkit/core");
12const fs_1 = require("fs");
13const uuid_1 = require("uuid");
14const command_1 = require("../models/command");
15const interface_1 = require("../models/interface");
16const config_1 = require("../utilities/config");
17function _validateBoolean(value) {
18 if (('' + value).trim() === 'true') {
19 return true;
20 }
21 else if (('' + value).trim() === 'false') {
22 return false;
23 }
24 else {
25 throw new Error(`Invalid value type; expected Boolean, received ${JSON.stringify(value)}.`);
26 }
27}
28function _validateString(value) {
29 return value;
30}
31function _validateAnalytics(value) {
32 if (value === '') {
33 // Disable analytics.
34 return null;
35 }
36 else {
37 return value;
38 }
39}
40function _validateAnalyticsSharingUuid(value) {
41 if (value == '') {
42 return uuid_1.v4();
43 }
44 else {
45 return value;
46 }
47}
48function _validateAnalyticsSharingTracking(value) {
49 if (!value.match(/^GA-\d+-\d+$/)) {
50 throw new Error(`Invalid GA property ID: ${JSON.stringify(value)}.`);
51 }
52 return value;
53}
54const validCliPaths = new Map([
55 ['cli.warnings.versionMismatch', _validateBoolean],
56 ['cli.defaultCollection', _validateString],
57 ['cli.packageManager', _validateString],
58 ['cli.analytics', _validateAnalytics],
59 ['cli.analyticsSharing.tracking', _validateAnalyticsSharingTracking],
60 ['cli.analyticsSharing.uuid', _validateAnalyticsSharingUuid],
61]);
62/**
63 * Splits a JSON path string into fragments. Fragments can be used to get the value referenced
64 * by the path. For example, a path of "a[3].foo.bar[2]" would give you a fragment array of
65 * ["a", 3, "foo", "bar", 2].
66 * @param path The JSON string to parse.
67 * @returns {(string|number)[]} The fragments for the string.
68 * @private
69 */
70function parseJsonPath(path) {
71 const fragments = (path || '').split(/\./g);
72 const result = [];
73 while (fragments.length > 0) {
74 const fragment = fragments.shift();
75 if (fragment == undefined) {
76 break;
77 }
78 const match = fragment.match(/([^\[]+)((\[.*\])*)/);
79 if (!match) {
80 throw new Error('Invalid JSON path.');
81 }
82 result.push(match[1]);
83 if (match[2]) {
84 const indices = match[2]
85 .slice(1, -1)
86 .split('][')
87 .map(x => (/^\d$/.test(x) ? +x : x.replace(/\"|\'/g, '')));
88 result.push(...indices);
89 }
90 }
91 return result.filter(fragment => fragment != null);
92}
93function getValueFromPath(root, path) {
94 const fragments = parseJsonPath(path);
95 try {
96 return fragments.reduce((value, current) => {
97 if (value == undefined || typeof value != 'object') {
98 return undefined;
99 }
100 else if (typeof current == 'string' && !Array.isArray(value)) {
101 return value[current];
102 }
103 else if (typeof current == 'number' && Array.isArray(value)) {
104 return value[current];
105 }
106 else {
107 return undefined;
108 }
109 }, root);
110 }
111 catch (_a) {
112 return undefined;
113 }
114}
115function setValueFromPath(root, path, newValue) {
116 const fragments = parseJsonPath(path);
117 try {
118 return fragments.reduce((value, current, index) => {
119 if (value == undefined || typeof value != 'object') {
120 return undefined;
121 }
122 else if (typeof current == 'string' && !Array.isArray(value)) {
123 if (index === fragments.length - 1) {
124 value[current] = newValue;
125 }
126 else if (value[current] == undefined) {
127 if (typeof fragments[index + 1] == 'number') {
128 value[current] = [];
129 }
130 else if (typeof fragments[index + 1] == 'string') {
131 value[current] = {};
132 }
133 }
134 return value[current];
135 }
136 else if (typeof current == 'number' && Array.isArray(value)) {
137 if (index === fragments.length - 1) {
138 value[current] = newValue;
139 }
140 else if (value[current] == undefined) {
141 if (typeof fragments[index + 1] == 'number') {
142 value[current] = [];
143 }
144 else if (typeof fragments[index + 1] == 'string') {
145 value[current] = {};
146 }
147 }
148 return value[current];
149 }
150 else {
151 return undefined;
152 }
153 }, root);
154 }
155 catch (_a) {
156 return undefined;
157 }
158}
159function normalizeValue(value, path) {
160 const cliOptionType = validCliPaths.get(path);
161 if (cliOptionType) {
162 return cliOptionType('' + value);
163 }
164 if (typeof value === 'string') {
165 try {
166 return core_1.parseJson(value, core_1.JsonParseMode.Loose);
167 }
168 catch (e) {
169 if (e instanceof core_1.InvalidJsonCharacterException && !value.startsWith('{')) {
170 return value;
171 }
172 else {
173 throw e;
174 }
175 }
176 }
177 return value;
178}
179class ConfigCommand extends command_1.Command {
180 async run(options) {
181 const level = options.global ? 'global' : 'local';
182 if (!options.global) {
183 await this.validateScope(interface_1.CommandScope.InProject);
184 }
185 let [config] = config_1.getWorkspaceRaw(level);
186 if (options.global && !config) {
187 try {
188 if (config_1.migrateLegacyGlobalConfig()) {
189 config = config_1.getWorkspaceRaw(level)[0];
190 this.logger.info(core_1.tags.oneLine `
191 We found a global configuration that was used in Angular CLI 1.
192 It has been automatically migrated.`);
193 }
194 }
195 catch (_a) { }
196 }
197 if (options.value == undefined) {
198 if (!config) {
199 this.logger.error('No config found.');
200 return 1;
201 }
202 return this.get(config.value, options);
203 }
204 else {
205 return this.set(options);
206 }
207 }
208 get(config, options) {
209 let value;
210 if (options.jsonPath) {
211 value = getValueFromPath(config, options.jsonPath);
212 }
213 else {
214 value = config;
215 }
216 if (value === undefined) {
217 this.logger.error('Value cannot be found.');
218 return 1;
219 }
220 else if (typeof value == 'object') {
221 this.logger.info(JSON.stringify(value, null, 2));
222 }
223 else {
224 this.logger.info(value.toString());
225 }
226 return 0;
227 }
228 async set(options) {
229 if (!options.jsonPath || !options.jsonPath.trim()) {
230 throw new Error('Invalid Path.');
231 }
232 if (options.global &&
233 !options.jsonPath.startsWith('schematics.') &&
234 !validCliPaths.has(options.jsonPath)) {
235 throw new Error('Invalid Path.');
236 }
237 const [config, configPath] = config_1.getWorkspaceRaw(options.global ? 'global' : 'local');
238 if (!config || !configPath) {
239 this.logger.error('Confguration file cannot be found.');
240 return 1;
241 }
242 // TODO: Modify & save without destroying comments
243 const configValue = config.value;
244 const value = normalizeValue(options.value || '', options.jsonPath);
245 const result = setValueFromPath(configValue, options.jsonPath, value);
246 if (result === undefined) {
247 this.logger.error('Value cannot be found.');
248 return 1;
249 }
250 try {
251 await config_1.validateWorkspace(configValue);
252 }
253 catch (error) {
254 this.logger.fatal(error.message);
255 return 1;
256 }
257 const output = JSON.stringify(configValue, null, 2);
258 fs_1.writeFileSync(configPath, output);
259 return 0;
260 }
261}
262exports.ConfigCommand = ConfigCommand;