@hubspot/cli 4.1.8-beta.4 → 4.1.8-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@ const {
8
8
  } = require('../../lib/commonOpts');
9
9
  const { loadAndValidateOptions } = require('../../lib/validation');
10
10
  const { i18n } = require('../../lib/lang');
11
+ const { getTableContents } = require('@hubspot/cli-lib/lib/table');
11
12
 
12
13
  const i18nKey = 'cli.commands.accounts.subcommands.info';
13
14
  exports.describe = i18n(`${i18nKey}.describe`);
@@ -26,11 +27,12 @@ exports.handler = async options => {
26
27
 
27
28
  const response = await getAccessToken(personalAccessKey, env, accountId);
28
29
 
29
- let scopeGroups = response.scopeGroups.join('\n');
30
+ const scopeGroups = response.scopeGroups.map(s => [s]);
30
31
 
31
32
  logger.log(i18n(`${i18nKey}.name`, { name }));
32
33
  logger.log(i18n(`${i18nKey}.accountId`, { accountId }));
33
- logger.log(i18n(`${i18nKey}.scopeGroups`, { scopeGroups }));
34
+ logger.log(i18n(`${i18nKey}.scopeGroups`));
35
+ logger.log(getTableContents(scopeGroups, { border: { bodyLeft: ' ' } }));
34
36
  } else {
35
37
  logger.log(i18n(`${i18nKey}.errors.notUsingPersonalAccessKey`));
36
38
  }
@@ -35,7 +35,7 @@ exports.handler = async options => {
35
35
  path.resolve(getCwd(), options.location || location),
36
36
  options.name || name,
37
37
  options.template || template,
38
- options.repoPath
38
+ options.templateSource
39
39
  );
40
40
 
41
41
  logger.log('');
@@ -62,8 +62,8 @@ exports.builder = yargs => {
62
62
  describe: i18n(`${i18nKey}.options.template.describe`),
63
63
  type: 'string',
64
64
  },
65
- repoPath: {
66
- describe: i18n(`${i18nKey}.options.repoPath.describe`),
65
+ templateSource: {
66
+ describe: i18n(`${i18nKey}.options.templateSource.describe`),
67
67
  type: 'string',
68
68
  },
69
69
  });
@@ -52,32 +52,30 @@ exports.handler = async options => {
52
52
 
53
53
  let buildIdToDeploy = buildIdOption;
54
54
 
55
- if (!buildIdOption) {
56
- const { latestBuild, deployedBuildId } = await fetchProject(
57
- accountId,
58
- projectName
59
- );
60
- if (!latestBuild || !latestBuild.buildId) {
61
- logger.error(i18n(`${i18nKey}.errors.noBuilds`));
62
- process.exit(EXIT_CODES.ERROR);
63
- }
64
- const buildIdPromptResponse = await buildIdPrompt(
65
- latestBuild.buildId,
66
- deployedBuildId,
67
- projectName
68
- );
69
-
70
- buildIdToDeploy = buildIdPromptResponse.buildId;
71
- }
55
+ try {
56
+ if (!buildIdOption) {
57
+ const { latestBuild, deployedBuildId } = await fetchProject(
58
+ accountId,
59
+ projectName
60
+ );
61
+ if (!latestBuild || !latestBuild.buildId) {
62
+ logger.error(i18n(`${i18nKey}.errors.noBuilds`));
63
+ process.exit(EXIT_CODES.ERROR);
64
+ }
65
+ const buildIdPromptResponse = await buildIdPrompt(
66
+ latestBuild.buildId,
67
+ deployedBuildId,
68
+ projectName
69
+ );
72
70
 
73
- if (!buildIdToDeploy) {
74
- logger.error(i18n(`${i18nKey}.errors.noBuildId`));
75
- process.exit(EXIT_CODES.ERROR);
76
- }
71
+ buildIdToDeploy = buildIdPromptResponse.buildId;
72
+ }
77
73
 
78
- let exitCode = EXIT_CODES.SUCCESS;
74
+ if (!buildIdToDeploy) {
75
+ logger.error(i18n(`${i18nKey}.errors.noBuildId`));
76
+ process.exit(EXIT_CODES.ERROR);
77
+ }
79
78
 
80
- try {
81
79
  const deployResp = await deployProject(
82
80
  accountId,
83
81
  projectName,
@@ -90,7 +88,6 @@ exports.handler = async options => {
90
88
  details: deployResp.error.message,
91
89
  })
92
90
  );
93
- exitCode = EXIT_CODES.ERROR;
94
91
  return;
95
92
  }
96
93
 
@@ -106,9 +103,8 @@ exports.handler = async options => {
106
103
  } else {
107
104
  logApiErrorInstance(e, new ApiErrorContext({ accountId, projectName }));
108
105
  }
109
- exitCode = 1;
106
+ process.exit(EXIT_CODES.ERROR);
110
107
  }
111
- process.exit(exitCode);
112
108
  };
113
109
 
114
110
  exports.builder = yargs => {
@@ -29,6 +29,7 @@ const {
29
29
  LocalDevManager,
30
30
  UPLOAD_PERMISSIONS,
31
31
  } = require('../../lib/LocalDevManager');
32
+ const { isSandbox } = require('../../lib/sandboxes');
32
33
  const { getAccountConfig, getEnv } = require('@hubspot/cli-lib');
33
34
  const { sandboxNamePrompt } = require('../../lib/prompts/sandboxesPrompt');
34
35
  const {
@@ -37,12 +38,18 @@ const {
37
38
  getAvailableSyncTypes,
38
39
  } = require('../../lib/sandboxes');
39
40
  const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
40
- const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
41
+ const { ERROR_TYPES } = require('@hubspot/cli-lib/lib/constants');
42
+ const {
43
+ logErrorInstance,
44
+ logApiErrorInstance,
45
+ ApiErrorContext,
46
+ } = require('@hubspot/cli-lib/errorHandlers');
41
47
  const { buildSandbox } = require('../../lib/sandbox-create');
42
48
  const { syncSandbox } = require('../../lib/sandbox-sync');
43
49
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
44
50
  const {
45
51
  isMissingScopeError,
52
+ isSpecifiedError,
46
53
  } = require('@hubspot/cli-lib/errorHandlers/apiErrors');
47
54
 
48
55
  const i18nKey = 'cli.commands.project.subcommands.dev';
@@ -60,13 +67,7 @@ exports.handler = async options => {
60
67
 
61
68
  const { projectConfig, projectDir } = await getProjectConfig();
62
69
 
63
- logger.log(i18n(`${i18nKey}.logs.introHeader`));
64
- uiLine();
65
- logger.log(i18n(`${i18nKey}.logs.introBody1`));
66
- logger.log();
67
- logger.log(i18n(`${i18nKey}.logs.introBody2`));
68
- uiLine();
69
- logger.log();
70
+ logger.log(i18n(`${i18nKey}.logs.betaMessage`));
70
71
 
71
72
  if (!projectConfig) {
72
73
  logger.error(i18n(`${i18nKey}.errors.noProjectConfig`));
@@ -74,47 +75,32 @@ exports.handler = async options => {
74
75
  }
75
76
 
76
77
  const accounts = getConfigAccounts();
77
- let targetAccountId = options.accountId;
78
+ let targetAccountId = options.account ? accountId : null;
78
79
  let createNewSandbox = false;
79
- let chooseNonSandbox = false;
80
+ const defaultAccountIsSandbox = isSandbox(accountConfig);
81
+
82
+ if (!targetAccountId && defaultAccountIsSandbox) {
83
+ targetAccountId = accountId;
84
+ }
80
85
 
81
86
  if (!targetAccountId) {
87
+ //logger.log(i18n(`${i18nKey}.logs.learnMoreLink`));
88
+ logger.log();
89
+ uiLine();
90
+ logger.warn(i18n(`${i18nKey}.logs.nonSandboxWarning`));
91
+ uiLine();
92
+ logger.log();
93
+
82
94
  const {
83
95
  targetAccountId: promptTargetAccountId,
84
- chooseNonSandbox: promptChooseNonSandbox,
85
96
  createNewSandbox: promptCreateNewSandbox,
86
- } = await selectTargetAccountPrompt(accounts, accountConfig, false);
97
+ } = await selectTargetAccountPrompt(accounts, accountConfig);
87
98
 
88
99
  targetAccountId = promptTargetAccountId;
89
- chooseNonSandbox = promptChooseNonSandbox;
90
100
  createNewSandbox = promptCreateNewSandbox;
91
101
  }
92
102
 
93
- logger.log();
94
-
95
- // Show a warning if the user chooses a non-sandbox account (false)
96
- let shouldTargetNonSandboxAccount;
97
- if (chooseNonSandbox) {
98
- uiLine();
99
- logger.warn(i18n(`${i18nKey}.logs.prodAccountWarning`));
100
- uiLine();
101
- logger.log();
102
-
103
- shouldTargetNonSandboxAccount = await confirmPrompt(
104
- i18n(`${i18nKey}.prompt.targetNonSandbox`)
105
- );
106
-
107
- if (shouldTargetNonSandboxAccount) {
108
- const {
109
- targetAccountId: promptNonSandboxTargetAccountId,
110
- } = await selectTargetAccountPrompt(accounts, accountConfig, true);
111
-
112
- targetAccountId = promptNonSandboxTargetAccountId;
113
- logger.log();
114
- } else {
115
- process.exit(EXIT_CODES.SUCCESS);
116
- }
117
- } else if (createNewSandbox) {
103
+ if (createNewSandbox) {
118
104
  try {
119
105
  await validateSandboxUsageLimits(accountConfig, DEVELOPER_SANDBOX, env);
120
106
  } catch (err) {
@@ -179,7 +165,8 @@ exports.handler = async options => {
179
165
  }
180
166
  );
181
167
 
182
- const isNonSandboxAccount = shouldTargetNonSandboxAccount;
168
+ const isNonSandboxAccount =
169
+ !defaultAccountIsSandbox && targetAccountId === accountId;
183
170
 
184
171
  let uploadPermission = isNonSandboxAccount
185
172
  ? UPLOAD_PERMISSIONS.manual
@@ -198,21 +185,26 @@ exports.handler = async options => {
198
185
  const spinnies = SpinniesManager.init();
199
186
 
200
187
  if (!projectExists) {
201
- uiLine();
202
- logger.warn(
203
- i18n(`${i18nKey}.logs.projectMustExistExplanation`, {
204
- accountIdentifier: uiAccountDescription(targetAccountId),
205
- projectName: projectConfig.name,
206
- })
207
- );
208
- uiLine();
188
+ // Create the project without prompting if this is a newly created sandbox
189
+ let shouldCreateProject = createNewSandbox;
190
+
191
+ if (!shouldCreateProject) {
192
+ uiLine();
193
+ logger.warn(
194
+ i18n(`${i18nKey}.logs.projectMustExistExplanation`, {
195
+ accountIdentifier: uiAccountDescription(targetAccountId),
196
+ projectName: projectConfig.name,
197
+ })
198
+ );
199
+ uiLine();
209
200
 
210
- const shouldCreateProject = await confirmPrompt(
211
- i18n(`${i18nKey}.prompt.createProject`, {
212
- accountIdentifier: uiAccountDescription(targetAccountId),
213
- projectName: projectConfig.name,
214
- })
215
- );
201
+ shouldCreateProject = await confirmPrompt(
202
+ i18n(`${i18nKey}.prompt.createProject`, {
203
+ accountIdentifier: uiAccountDescription(targetAccountId),
204
+ projectName: projectConfig.name,
205
+ })
206
+ );
207
+ }
216
208
 
217
209
  if (shouldCreateProject) {
218
210
  try {
@@ -228,9 +220,10 @@ exports.handler = async options => {
228
220
  accountIdentifier: uiAccountDescription(targetAccountId),
229
221
  projectName: projectConfig.name,
230
222
  }),
223
+ succeedColor: 'white',
231
224
  });
232
225
  } catch (err) {
233
- logger.log(i18n(`${i18nKey}.logs.failedToCreateProject`));
226
+ logger.log(i18n(`${i18nKey}.status.failedToCreateProject`));
234
227
  process.exit(EXIT_CODES.ERROR);
235
228
  }
236
229
  } else {
@@ -260,10 +253,24 @@ exports.handler = async options => {
260
253
  );
261
254
  }
262
255
 
263
- if (result && !result.succeeded) {
264
- spinnies.fail('devModeSetup', {
265
- text: i18n(`${i18nKey}.status.startupFailed`),
266
- });
256
+ if (result && result.error) {
257
+ if (
258
+ isSpecifiedError(result.error, {
259
+ subCategory: ERROR_TYPES.PROJECT_LOCKED,
260
+ })
261
+ ) {
262
+ logger.log();
263
+ logger.error(i18n(`${i18nKey}.errors.projectLockedError`));
264
+ logger.log();
265
+ } else {
266
+ logApiErrorInstance(
267
+ result.error,
268
+ new ApiErrorContext({
269
+ accountId,
270
+ projectName: projectConfig.name,
271
+ })
272
+ );
273
+ }
267
274
  process.exit(EXIT_CODES.ERROR);
268
275
  } else {
269
276
  spinnies.remove('devModeSetup');
@@ -44,83 +44,77 @@ exports.handler = async options => {
44
44
 
45
45
  trackCommandUsage('project-download', null, accountId);
46
46
 
47
- const projectExists = await ensureProjectExists(accountId, projectName, {
48
- allowCreate: false,
49
- noLogs: true,
50
- });
51
-
52
- if (!projectExists) {
53
- logger.error(
54
- i18n(`${i18nKey}.errors.projectNotFound`, {
55
- projectName: chalk.bold(projectName),
56
- accountId: chalk.bold(accountId),
57
- })
58
- );
59
- let { name: promptedProjectName } = await downloadProjectPrompt(options);
60
- let projectName = promptedProjectName || project;
61
- }
47
+ try {
48
+ const projectExists = await ensureProjectExists(accountId, projectName, {
49
+ allowCreate: false,
50
+ noLogs: true,
51
+ });
52
+
53
+ if (!projectExists) {
54
+ logger.error(
55
+ i18n(`${i18nKey}.errors.projectNotFound`, {
56
+ projectName: chalk.bold(projectName),
57
+ accountId: chalk.bold(accountId),
58
+ })
59
+ );
60
+ let { name: promptedProjectName } = await downloadProjectPrompt(options);
61
+ let projectName = promptedProjectName || project;
62
+ }
62
63
 
63
- const absoluteDestPath = dest ? path.resolve(getCwd(), dest) : getCwd();
64
+ const absoluteDestPath = dest ? path.resolve(getCwd(), dest) : getCwd();
64
65
 
65
- const projectConfigCreated = await createProjectConfig(
66
- absoluteDestPath,
67
- projectName,
68
- { name: 'no-template' }
69
- );
66
+ const projectConfigCreated = await createProjectConfig(
67
+ absoluteDestPath,
68
+ projectName,
69
+ { name: 'no-template' }
70
+ );
70
71
 
71
- if (!projectConfigCreated) {
72
- logger.log(i18n(`${i18nKey}.logs.downloadCancelled`));
73
- process.exit(EXIT_CODES.SUCCESS);
74
- }
72
+ if (!projectConfigCreated) {
73
+ logger.log(i18n(`${i18nKey}.logs.downloadCancelled`));
74
+ process.exit(EXIT_CODES.SUCCESS);
75
+ }
75
76
 
76
- let success = false;
77
- let buildNumberToDownload = buildNumber;
77
+ let buildNumberToDownload = buildNumber;
78
78
 
79
- if (!buildNumberToDownload) {
80
- let projectBuildsResult;
79
+ if (!buildNumberToDownload) {
80
+ let projectBuildsResult;
81
81
 
82
- try {
83
82
  projectBuildsResult = await fetchProjectBuilds(accountId, projectName);
84
- } catch (e) {
85
- logApiErrorInstance(e, new ApiErrorContext({ accountId }));
86
- process.exit(EXIT_CODES.ERROR);
87
- }
88
83
 
89
- const { results: projectBuilds } = projectBuildsResult;
84
+ const { results: projectBuilds } = projectBuildsResult;
90
85
 
91
- if (projectBuilds && projectBuilds.length) {
92
- const latestBuild = projectBuilds[0];
93
- buildNumberToDownload = latestBuild.buildId;
86
+ if (projectBuilds && projectBuilds.length) {
87
+ const latestBuild = projectBuilds[0];
88
+ buildNumberToDownload = latestBuild.buildId;
89
+ }
94
90
  }
95
- }
96
91
 
97
- const zippedProject = await downloadProject(
98
- accountId,
99
- projectName,
100
- buildNumberToDownload
101
- );
102
-
103
- success = await extractZipArchive(
104
- zippedProject,
105
- projectName,
106
- path.resolve(absoluteDestPath, 'src'),
107
- {
108
- includesRootDir: false,
109
- }
110
- );
92
+ const zippedProject = await downloadProject(
93
+ accountId,
94
+ projectName,
95
+ buildNumberToDownload
96
+ );
97
+
98
+ await extractZipArchive(
99
+ zippedProject,
100
+ projectName,
101
+ path.resolve(absoluteDestPath, 'src'),
102
+ {
103
+ includesRootDir: false,
104
+ }
105
+ );
111
106
 
112
- if (!success) {
113
- logger.log(i18n(`${i18nKey}.errors.downloadFailed`));
107
+ logger.log(
108
+ i18n(`${i18nKey}.logs.downloadSucceeded`, {
109
+ buildId: buildNumberToDownload,
110
+ projectName,
111
+ })
112
+ );
113
+ process.exit(EXIT_CODES.SUCCESS);
114
+ } catch (e) {
115
+ logApiErrorInstance(e, new ApiErrorContext({ accountId, projectName }));
114
116
  process.exit(EXIT_CODES.ERROR);
115
117
  }
116
-
117
- logger.log(
118
- i18n(`${i18nKey}.logs.downloadSucceeded`, {
119
- buildId: buildNumberToDownload,
120
- projectName,
121
- })
122
- );
123
- process.exit(EXIT_CODES.SUCCESS);
124
118
  };
125
119
 
126
120
  exports.builder = yargs => {
@@ -127,7 +127,10 @@ exports.handler = async options => {
127
127
  if (e.statusCode === 404) {
128
128
  logger.error(`Project ${projectConfig.name} not found. `);
129
129
  } else {
130
- logApiErrorInstance(e, new ApiErrorContext({ accountId }));
130
+ logApiErrorInstance(
131
+ e,
132
+ new ApiErrorContext({ accountId, projectName: projectConfig.name })
133
+ );
131
134
  }
132
135
  }
133
136
  };
@@ -22,6 +22,10 @@ const {
22
22
  getProjectAppFunctionLogs,
23
23
  getLatestProjectAppFunctionLog,
24
24
  } = require('@hubspot/cli-lib/api/functions');
25
+ const {
26
+ logApiErrorInstance,
27
+ ApiErrorContext,
28
+ } = require('@hubspot/cli-lib/errorHandlers');
25
29
  const {
26
30
  getFunctionLogs,
27
31
  getLatestFunctionLog,
@@ -44,10 +48,16 @@ const getPrivateAppsUrl = accountId => {
44
48
  return `${baseUrl}/private-apps/${accountId}`;
45
49
  };
46
50
 
47
- const handleLogsError = (e, name) => {
48
- logger.debug(`Log fetch error: ${e.message}`);
51
+ const handleLogsError = (e, name, projectName) => {
49
52
  if (e.statusCode === 404) {
53
+ logger.debug(`Log fetch error: ${e.message}`);
50
54
  logger.log(i18n(`${i18nKey}.logs.noLogsFound`, { name }));
55
+ } else {
56
+ logApiErrorInstance(
57
+ e,
58
+ new ApiErrorContext({ accountId: getAccountId(), projectName })
59
+ );
60
+ process.exit(EXIT_CODES.ERROR);
51
61
  }
52
62
  };
53
63
 
@@ -77,7 +87,7 @@ const handleFunctionLog = async (accountId, options) => {
77
87
  )
78
88
  : getFunctionLogs(accountId, functionName, { after });
79
89
  } catch (e) {
80
- handleLogsError(e, functionName);
90
+ handleLogsError(e, functionName, projectName);
81
91
  }
82
92
  };
83
93
 
@@ -89,7 +99,7 @@ const handleFunctionLog = async (accountId, options) => {
89
99
  projectName,
90
100
  appPath
91
101
  )
92
- : getLatestFunctionLog(accountId, functionName);
102
+ : getLatestFunctionLog(accountId, functionName, projectName);
93
103
  };
94
104
 
95
105
  if (follow) {
@@ -104,14 +114,14 @@ const handleFunctionLog = async (accountId, options) => {
104
114
  try {
105
115
  logsResp = await fetchLatest();
106
116
  } catch (e) {
107
- handleLogsError(e, functionName);
117
+ handleLogsError(e, functionName, projectName);
108
118
  return true;
109
119
  }
110
120
  } else {
111
121
  try {
112
122
  logsResp = await tailCall();
113
123
  } catch (e) {
114
- handleLogsError(e, functionName);
124
+ handleLogsError(e, functionName, projectName);
115
125
  return true;
116
126
  }
117
127
  }
@@ -19,6 +19,14 @@ const {
19
19
  } = require('../../lib/projects');
20
20
  const { i18n } = require('../../lib/lang');
21
21
  const { getAccountConfig } = require('@hubspot/cli-lib');
22
+ const { ERROR_TYPES } = require('@hubspot/cli-lib/lib/constants');
23
+ const {
24
+ isSpecifiedError,
25
+ } = require('@hubspot/cli-lib/errorHandlers/apiErrors');
26
+ const {
27
+ logApiErrorInstance,
28
+ ApiErrorContext,
29
+ } = require('@hubspot/cli-lib/errorHandlers');
22
30
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
23
31
 
24
32
  const i18nKey = 'cli.commands.project.subcommands.upload';
@@ -42,34 +50,53 @@ exports.handler = async options => {
42
50
 
43
51
  await ensureProjectExists(accountId, projectConfig.name, { forceCreate });
44
52
 
45
- const result = await handleProjectUpload(
46
- accountId,
47
- projectConfig,
48
- projectDir,
49
- pollProjectBuildAndDeploy,
50
- message
51
- );
52
-
53
- if (result.buildSucceeded && !result.autodeployEnabled) {
54
- uiLine();
55
- logger.log(
56
- chalk.bold(
57
- i18n(`${i18nKey}.logs.buildSucceeded`, {
58
- buildId: result.buildId,
59
- })
60
- )
61
- );
62
- logger.log(i18n(`${i18nKey}.logs.readyToGoLive`));
63
- logger.log(
64
- i18n(`${i18nKey}.logs.runCommand`, {
65
- command: chalk.hex('f5c26b')('hs project deploy'),
66
- })
53
+ try {
54
+ const result = await handleProjectUpload(
55
+ accountId,
56
+ projectConfig,
57
+ projectDir,
58
+ pollProjectBuildAndDeploy,
59
+ message
67
60
  );
68
- uiLine();
69
- }
70
61
 
71
- logFeedbackMessage(result.buildId);
72
- process.exit(result.succeeded ? EXIT_CODES.SUCCESS : EXIT_CODES.ERROR);
62
+ if (result.error) {
63
+ if (
64
+ isSpecifiedError(result.error, {
65
+ subCategory: ERROR_TYPES.PROJECT_LOCKED,
66
+ })
67
+ ) {
68
+ logger.log();
69
+ logger.error(i18n(`${i18nKey}.errors.projectLockedError`));
70
+ logger.log();
71
+ } else {
72
+ logApiErrorInstance(
73
+ result.error,
74
+ new ApiErrorContext({
75
+ accountId,
76
+ projectName: projectConfig.name,
77
+ })
78
+ );
79
+ }
80
+ process.exit(EXIT_CODES.ERROR);
81
+ }
82
+ if (result.buildSucceeded && !result.autodeployEnabled) {
83
+ uiLine();
84
+ logger.log(
85
+ chalk.bold(
86
+ i18n(`${i18nKey}.logs.buildSucceeded`, {
87
+ buildId: result.buildId,
88
+ })
89
+ )
90
+ );
91
+ uiLine();
92
+ logFeedbackMessage(result.buildId);
93
+ process.exit(EXIT_CODES.SUCCESS);
94
+ }
95
+ } catch (e) {
96
+ const projectName = projectConfig.name;
97
+ logApiErrorInstance(e, new ApiErrorContext({ accountId, projectName }));
98
+ process.exit(EXIT_CODES.ERROR);
99
+ }
73
100
  };
74
101
 
75
102
  exports.builder = yargs => {