@hubspot/cli 6.3.0 → 6.4.0-beta.1

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.
Files changed (49) hide show
  1. package/bin/cli.js +2 -0
  2. package/commands/doctor.d.ts +8 -0
  3. package/commands/doctor.js +60 -0
  4. package/commands/init.js +20 -7
  5. package/commands/lint.d.ts +4 -1
  6. package/commands/lint.js +11 -2
  7. package/commands/project/installDeps.js +5 -5
  8. package/commands/project/logs.js +1 -1
  9. package/commands/theme/preview.js +38 -20
  10. package/lang/en.lyaml +68 -0
  11. package/lib/DevServerManager.js +2 -5
  12. package/lib/commonOpts.d.ts +3 -2
  13. package/lib/commonOpts.js +2 -2
  14. package/lib/dependencyManagement.d.ts +3 -1
  15. package/lib/dependencyManagement.js +19 -0
  16. package/lib/doctor/Diagnosis.d.ts +27 -0
  17. package/lib/doctor/Diagnosis.js +119 -0
  18. package/lib/doctor/DiagnosticInfoBuilder.d.ts +61 -0
  19. package/lib/doctor/DiagnosticInfoBuilder.js +158 -0
  20. package/lib/doctor/Doctor.d.ts +21 -0
  21. package/lib/doctor/Doctor.js +328 -0
  22. package/lib/errorHandlers/index.d.ts +14 -1
  23. package/lib/errorHandlers/index.js +43 -50
  24. package/lib/errorHandlers/suppressError.d.ts +2 -1
  25. package/lib/errorHandlers/suppressError.js +32 -37
  26. package/lib/interpolation.d.ts +3 -0
  27. package/lib/interpolation.js +4 -3
  28. package/lib/links.d.ts +9 -0
  29. package/lib/links.js +99 -97
  30. package/lib/projects.d.ts +4 -1
  31. package/lib/projects.js +5 -3
  32. package/lib/ui/SpinniesManager.d.ts +39 -1
  33. package/lib/ui/SpinniesManager.js +66 -35
  34. package/lib/ui/git.d.ts +1 -1
  35. package/lib/ui/git.js +17 -17
  36. package/lib/ui/index.d.ts +16 -1
  37. package/lib/ui/index.js +87 -114
  38. package/lib/ui/serverlessFunctionLogs.js +28 -19
  39. package/lib/ui/spinniesUtils.d.ts +31 -0
  40. package/lib/ui/spinniesUtils.js +45 -31
  41. package/lib/ui/supportHyperlinks.d.ts +6 -0
  42. package/lib/ui/supportHyperlinks.js +10 -10
  43. package/lib/ui/supportsColor.d.ts +16 -0
  44. package/lib/ui/supportsColor.js +19 -17
  45. package/lib/ui/table.d.ts +3 -1
  46. package/lib/ui/table.js +17 -11
  47. package/lib/usageTracking.d.ts +2 -1
  48. package/lib/usageTracking.js +2 -0
  49. package/package.json +10 -5
package/bin/cli.js CHANGED
@@ -38,6 +38,7 @@ const accountsCommand = require('../commands/accounts');
38
38
  const sandboxesCommand = require('../commands/sandbox');
39
39
  const cmsCommand = require('../commands/cms');
40
40
  const feedbackCommand = require('../commands/feedback');
41
+ const doctorCommand = require('../commands/doctor');
41
42
  const notifier = updateNotifier({
42
43
  pkg: { ...pkg, name: '@hubspot/cli' },
43
44
  distTag: 'latest',
@@ -167,6 +168,7 @@ const argv = yargs
167
168
  .command(accountsCommand)
168
169
  .command(sandboxesCommand)
169
170
  .command(feedbackCommand)
171
+ .command(doctorCommand)
170
172
  .help()
171
173
  .recommendCommands()
172
174
  .demandCommand(1, '')
@@ -0,0 +1,8 @@
1
+ import { ArgumentsCamelCase, BuilderCallback } from 'yargs';
2
+ export interface DoctorOptions {
3
+ 'output-dir'?: string;
4
+ }
5
+ export declare const command = "doctor";
6
+ export declare const describe: any;
7
+ export declare const handler: ({ outputDir, }: ArgumentsCamelCase<DoctorOptions>) => Promise<never>;
8
+ export declare const builder: BuilderCallback<DoctorOptions, DoctorOptions>;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.builder = exports.handler = exports.describe = exports.command = void 0;
7
+ const usageTracking_1 = require("../lib/usageTracking");
8
+ const logger_1 = require("@hubspot/local-dev-lib/logger");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const Doctor_1 = require("../lib/doctor/Doctor");
11
+ const exitCodes_1 = require("../lib/enums/exitCodes");
12
+ const path_1 = __importDefault(require("path"));
13
+ const path_2 = require("@hubspot/local-dev-lib/path");
14
+ const { i18n } = require('../lib/lang');
15
+ const i18nKey = 'commands.doctor';
16
+ exports.command = 'doctor';
17
+ exports.describe = i18n(`${i18nKey}.describe`);
18
+ const handler = async ({ outputDir, }) => {
19
+ const doctor = new Doctor_1.Doctor();
20
+ (0, usageTracking_1.trackCommandUsage)(exports.command, undefined, doctor.accountId);
21
+ const output = await doctor.diagnose();
22
+ const totalCount = (output?.errorCount || 0) + (output?.warningCount || 0);
23
+ if (totalCount > 0) {
24
+ (0, usageTracking_1.trackCommandMetadataUsage)(exports.command, { success: false, type: totalCount }, doctor.accountId);
25
+ }
26
+ if (!outputDir) {
27
+ if (output?.diagnosis) {
28
+ logger_1.logger.log(output.diagnosis);
29
+ }
30
+ else {
31
+ logger_1.logger.error(i18n(`${i18nKey}.errors.generatingDiagnosis`));
32
+ return process.exit(exitCodes_1.EXIT_CODES.ERROR);
33
+ }
34
+ return process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
35
+ }
36
+ if (!path_1.default.isAbsolute(outputDir)) {
37
+ outputDir = path_1.default.join((0, path_2.getCwd)(), outputDir);
38
+ }
39
+ const outputFile = path_1.default.join(outputDir, `hubspot-doctor-${new Date().toISOString()}.json`);
40
+ try {
41
+ fs_1.default.writeFileSync(outputFile, JSON.stringify(output, null, 4));
42
+ logger_1.logger.success(i18n(`${i18nKey}.outputWritten`, { filename: outputFile }));
43
+ }
44
+ catch (e) {
45
+ logger_1.logger.error(i18n(`${i18nKey}.errors.unableToWriteOutputFile`, {
46
+ file: outputFile,
47
+ errorMessage: e instanceof Error ? e.message : e,
48
+ }));
49
+ return process.exit(exitCodes_1.EXIT_CODES.ERROR);
50
+ }
51
+ return process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
52
+ };
53
+ exports.handler = handler;
54
+ const builder = yargs => {
55
+ yargs.option('output-dir', {
56
+ describe: i18n(`${i18nKey}.options.outputDir`),
57
+ type: 'string',
58
+ });
59
+ };
60
+ exports.builder = builder;
package/commands/init.js CHANGED
@@ -67,12 +67,14 @@ exports.describe = i18n(`${i18nKey}.describe`, {
67
67
  configName: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
68
68
  });
69
69
  exports.handler = async (options) => {
70
- const { auth: authType = PERSONAL_ACCESS_KEY_AUTH_METHOD.value, c, account: optionalAccount, } = options;
70
+ const { auth: authType = PERSONAL_ACCESS_KEY_AUTH_METHOD.value, c, account: optionalAccount, disableTracking, } = options;
71
71
  const configPath = (c && path.join(getCwd(), c)) || getConfigPath();
72
72
  setLogLevel(options);
73
- trackCommandUsage('init', {
74
- authType,
75
- });
73
+ if (!disableTracking) {
74
+ trackCommandUsage('init', {
75
+ authType,
76
+ });
77
+ }
76
78
  const env = options.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
77
79
  if (fs.existsSync(configPath)) {
78
80
  logger.error(i18n(`${i18nKey}.errors.configFileExists`, {
@@ -81,7 +83,9 @@ exports.handler = async (options) => {
81
83
  logger.info(i18n(`${i18nKey}.logs.updateConfig`));
82
84
  process.exit(EXIT_CODES.ERROR);
83
85
  }
84
- trackAuthAction('init', authType, TRACKING_STATUS.STARTED);
86
+ if (!disableTracking) {
87
+ await trackAuthAction('init', authType, TRACKING_STATUS.STARTED);
88
+ }
85
89
  createEmptyConfigFile({ path: configPath });
86
90
  handleExit(deleteEmptyConfigFile);
87
91
  try {
@@ -102,12 +106,16 @@ exports.handler = async (options) => {
102
106
  account: name || accountId,
103
107
  }));
104
108
  uiFeatureHighlight(['helpCommand', 'authCommand', 'accountsListCommand']);
105
- await trackAuthAction('init', authType, TRACKING_STATUS.COMPLETE, accountId);
109
+ if (!disableTracking) {
110
+ await trackAuthAction('init', authType, TRACKING_STATUS.COMPLETE, accountId);
111
+ }
106
112
  process.exit(EXIT_CODES.SUCCESS);
107
113
  }
108
114
  catch (err) {
109
115
  logError(err);
110
- await trackAuthAction('init', authType, TRACKING_STATUS.ERROR);
116
+ if (!disableTracking) {
117
+ await trackAuthAction('init', authType, TRACKING_STATUS.ERROR);
118
+ }
111
119
  process.exit(EXIT_CODES.ERROR);
112
120
  }
113
121
  };
@@ -129,6 +137,11 @@ exports.builder = yargs => {
129
137
  describe: i18n(`${i18nKey}.options.account.describe`),
130
138
  type: 'string',
131
139
  },
140
+ 'disable-tracking': {
141
+ type: 'boolean',
142
+ hidden: true,
143
+ default: false,
144
+ },
132
145
  });
133
146
  addConfigOptions(yargs);
134
147
  addTestingOptions(yargs);
@@ -1 +1,4 @@
1
- export {};
1
+ export declare const command = "lint <path>";
2
+ export declare const describe: null;
3
+ export declare const handler: (options: any) => Promise<void>;
4
+ export declare const builder: (yargs: any) => any;
package/commands/lint.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.builder = exports.handler = exports.describe = exports.command = void 0;
3
4
  // @ts-nocheck
4
5
  const { lint } = require('@hubspot/local-dev-lib/cms/validate');
5
6
  const { logger } = require('@hubspot/local-dev-lib/logger');
@@ -42,7 +43,7 @@ function printHublValidationResult({ file, validation }) {
42
43
  logger.groupEnd();
43
44
  return count;
44
45
  }
45
- exports.handler = async (options) => {
46
+ const handler = async (options) => {
46
47
  const { path: lintPath } = options;
47
48
  await loadAndValidateOptions(options);
48
49
  const accountId = getAccountId(options);
@@ -68,7 +69,8 @@ exports.handler = async (options) => {
68
69
  count,
69
70
  }));
70
71
  };
71
- exports.builder = yargs => {
72
+ exports.handler = handler;
73
+ const builder = yargs => {
72
74
  addConfigOptions(yargs);
73
75
  addAccountOptions(yargs);
74
76
  yargs.positional('path', {
@@ -77,3 +79,10 @@ exports.builder = yargs => {
77
79
  });
78
80
  return yargs;
79
81
  };
82
+ exports.builder = builder;
83
+ module.exports = {
84
+ builder: exports.builder,
85
+ handler: exports.handler,
86
+ command: exports.command,
87
+ describe: exports.describe,
88
+ };
@@ -10,14 +10,14 @@ const path = require('path');
10
10
  const { i18n } = require('../../lib/lang');
11
11
  const { trackCommandUsage } = require('../../lib/usageTracking');
12
12
  const { getAccountId } = require('../../lib/commonOpts');
13
+ const { uiBetaTag } = require('../../lib/ui');
13
14
  const i18nKey = `commands.project.subcommands.installDeps`;
14
15
  exports.command = 'install-deps [packages..]';
15
- // Intentionally making this null to hide command
16
- exports.describe = null;
17
- // exports.describe = uiBetaTag(i18n(`${i18nKey}.help.describe`), false);
18
- exports.handler = async ({ packages }) => {
16
+ exports.describe = uiBetaTag(i18n(`${i18nKey}.help.describe`), false);
17
+ exports.handler = async (options) => {
18
+ const { packages } = options || {};
19
19
  try {
20
- const accountId = getAccountId();
20
+ const accountId = getAccountId(options);
21
21
  trackCommandUsage('project-install-deps', null, accountId);
22
22
  const projectConfig = await getProjectConfig();
23
23
  if (!projectConfig || !projectConfig.projectDir) {
@@ -65,7 +65,7 @@ exports.handler = async (options) => {
65
65
  }
66
66
  catch (e) {
67
67
  logError(e, {
68
- accountId: getAccountId(),
68
+ accountId,
69
69
  projectName: ProjectLogsManager.projectName,
70
70
  });
71
71
  return process.exit(EXIT_CODES.ERROR);
@@ -7,7 +7,6 @@ const { i18n } = require('../../lib/lang');
7
7
  const { logger } = require('@hubspot/local-dev-lib/logger');
8
8
  const { addAccountOptions, addConfigOptions, getAccountId, } = require('../../lib/commonOpts');
9
9
  const { getCwd } = require('@hubspot/local-dev-lib/path');
10
- const { preview } = require('@hubspot/theme-preview-dev-server');
11
10
  const { getUploadableFileList } = require('../../lib/upload');
12
11
  const { trackCommandUsage } = require('../../lib/usageTracking');
13
12
  const { loadAndValidateOptions } = require('../../lib/validation');
@@ -20,6 +19,8 @@ const { handleExit, handleKeypress } = require('../../lib/process');
20
19
  const { getThemeJSONPath } = require('@hubspot/local-dev-lib/cms/themes');
21
20
  const { getProjectConfig } = require('../../lib/projects');
22
21
  const { findProjectComponents, COMPONENT_TYPES, } = require('../../lib/projectStructure');
22
+ const { preview } = require('@hubspot/theme-preview-dev-server');
23
+ const { hasFeature } = require('../../lib/hasFeature');
23
24
  const i18nKey = 'commands.theme.subcommands.preview';
24
25
  exports.command = 'preview [--src] [--dest]';
25
26
  exports.describe = i18n(`${i18nKey}.describe`);
@@ -89,7 +90,7 @@ const determineSrcAndDest = async (options) => {
89
90
  return { absoluteSrc, dest };
90
91
  };
91
92
  exports.handler = async (options) => {
92
- const { notify, skipUpload, noSsl, port, debug, resetSession } = options;
93
+ const { notify, noSsl, resetSession, port, generateFieldsTypes } = options;
93
94
  await loadAndValidateOptions(options);
94
95
  const accountId = getAccountId(options);
95
96
  const { absoluteSrc, dest } = await determineSrcAndDest(options);
@@ -144,17 +145,39 @@ exports.handler = async (options) => {
144
145
  return uploadOptions;
145
146
  };
146
147
  trackCommandUsage('preview', accountId);
147
- preview(accountId, absoluteSrc, dest, {
148
- notify,
149
- filePaths,
150
- skipUpload,
151
- noSsl,
152
- port,
153
- debug,
154
- resetSession,
155
- startProgressBar,
156
- handleUserInput,
157
- });
148
+ let createUnifiedDevServer;
149
+ try {
150
+ require.resolve('@hubspot/cms-dev-server');
151
+ const { createDevServer } = await import('@hubspot/cms-dev-server');
152
+ createUnifiedDevServer = createDevServer;
153
+ }
154
+ catch (e) {
155
+ logger.warn('Unified dev server requires node 20 to run. Defaulting to legacy preview.');
156
+ }
157
+ const isUngatedForUnified = await hasFeature(accountId, 'cms:react:unifiedThemePreview');
158
+ if (isUngatedForUnified && createUnifiedDevServer) {
159
+ if (port) {
160
+ process.env['PORT'] = port;
161
+ }
162
+ createUnifiedDevServer(absoluteSrc, false, false, false, !noSsl, generateFieldsTypes, {
163
+ filePaths,
164
+ resetSession,
165
+ startProgressBar,
166
+ handleUserInput,
167
+ dest,
168
+ });
169
+ }
170
+ else {
171
+ preview(accountId, absoluteSrc, dest, {
172
+ notify,
173
+ filePaths,
174
+ noSsl,
175
+ port,
176
+ resetSession,
177
+ startProgressBar,
178
+ handleUserInput,
179
+ });
180
+ }
158
181
  };
159
182
  exports.builder = yargs => {
160
183
  addConfigOptions(yargs);
@@ -183,16 +206,11 @@ exports.builder = yargs => {
183
206
  describe: i18n(`${i18nKey}.options.port.describe`),
184
207
  type: 'number',
185
208
  });
186
- yargs.option('debug', {
187
- describe: false,
188
- type: 'boolean',
189
- });
190
- yargs.option('skipUpload', {
191
- alias: 'skip',
209
+ yargs.option('resetSession', {
192
210
  describe: false,
193
211
  type: 'boolean',
194
212
  });
195
- yargs.option('resetSession', {
213
+ yargs.option('generateFieldsTypes', {
196
214
  describe: false,
197
215
  type: 'boolean',
198
216
  });
package/lang/en.lyaml CHANGED
@@ -275,6 +275,14 @@ en:
275
275
  success:
276
276
  update: "Your schema has been updated in account \"{{ accountId }}\""
277
277
  viewAtUrl: "Schema can be viewed at {{ url }}"
278
+ doctor:
279
+ describe: "Retrieve diagnostic information about your local HubSpot configurations."
280
+ options:
281
+ outputDir: "Directory to save a detailed diagnosis JSON file in"
282
+ errors:
283
+ generatingDiagnosis: "Error generating diagnosis"
284
+ unableToWriteOutputFile: "Unable to write output to {{#bold}}{{ file }}{{/bold}}, {{ errorMessage }}"
285
+ outputWritten: "Output written to {{#bold}}{{ filename }}{{/bold}}"
278
286
  fetch:
279
287
  describe: "Fetch a file, directory or module from HubSpot and write to a path on your computer."
280
288
  errors:
@@ -1135,6 +1143,7 @@ en:
1135
1143
  checkFailed: "Unable to determine if config file is properly ignored by git."
1136
1144
  serverlessFunctionLogs:
1137
1145
  unableToProcessLog: "Unable to process log {{ log }}"
1146
+ noLogsFound: "No logs found."
1138
1147
  commonOpts:
1139
1148
  options:
1140
1149
  portal:
@@ -1418,3 +1427,62 @@ en:
1418
1427
  portalMissingScope: "Your account does not have access to this action. Talk to an account admin to request it."
1419
1428
  userMissingScope: "You don't have access to this action. Ask an account admin to change your permissions in Users & Teams settings."
1420
1429
  genericMissingScope: "Your access key does not allow this action. Please generate a new access key by running `hs auth personalaccesskey`."
1430
+ doctor:
1431
+ runningDiagnostics: "Running diagnostics..."
1432
+ diagnosticsComplete: "Diagnostics complete"
1433
+ accountChecks:
1434
+ active: "Default account active"
1435
+ inactive: "Default account isn't active"
1436
+ inactiveSecondary: "Run {{ command }} to remove inactive accounts from your CLI config"
1437
+ unableToDetermine: "Unable to determine if the portal is active"
1438
+ pak:
1439
+ invalid: "Personal access key is invalid"
1440
+ invalidSecondary: "To get a new key, run {{ command }}, deactivate your access key, and generate a new one. Then use that new key to authenticate your account."
1441
+ valid: "Personal Access Key is valid. {{ link }}"
1442
+ viewScopes: "View selected scopes"
1443
+ nodeChecks:
1444
+ unableToDetermine: "Unable to determine what version of node is installed"
1445
+ minimumNotMet: "Minimum Node version is not met. Upgrade to {{ nodeVersion }} or higher"
1446
+ success: "node v{{ nodeVersion }} is installed"
1447
+ npmChecks:
1448
+ notInstalled: "npm is not installed"
1449
+ installed: "npm v{{ npmVersion }} is installed"
1450
+ unableToDetermine: "Unable to determine if npm is installed"
1451
+ hsChecks:
1452
+ notLatest: "Version {{ hsVersion }} outdated"
1453
+ notLatestSecondary: "Run {{ command }} to upgrade to the latest version {{ hsVersion }}"
1454
+ latest: "HubSpot CLI v{{ hsVersion }} up to date"
1455
+ unableToDetermine: "Unable to determine if HubSpot CLI is up to date."
1456
+ unableToDetermineSecondary: "Run {{ command }} to check your installed version; then visit the {{ link }} to validate whether you have the latest version"
1457
+ unableToDetermineSecondaryLink: "npm HubSpot CLI version history"
1458
+ projectDependenciesChecks:
1459
+ missingDependencies: "missing dependencies in {{#bold}}{{ dir }}{{/bold}}"
1460
+ missingDependenciesSecondary: "Run {{ command }} to install all project dependencies locally"
1461
+ unableToDetermine: "Unable to determine if dependencies are installed {{ dir }}"
1462
+ success: "App dependencies are installed and up to date"
1463
+ files:
1464
+ invalidJson: "invalid JSON in {{#bold}}{{ filename }}{{/bold}}"
1465
+ validJson: "JSON files valid"
1466
+ port:
1467
+ inUse: "Port {{ port }} is in use"
1468
+ inUseSecondary: "Make sure it is available if before running {{ command }}"
1469
+ available: "Port {{ port }} available for local development"
1470
+ diagnosis:
1471
+ cli:
1472
+ header: "HubSpot CLI install"
1473
+ cliConfig:
1474
+ header: "CLI configuration"
1475
+ configFileSubHeader: "Config File: {{#bold}}{{ filename }}{{/bold}}"
1476
+ defaultAccountSubHeader: "Default Account: {{accountDetails}}"
1477
+ noConfigFile: "CLI configuration not found"
1478
+ noConfigFileSecondary: "Run {{command}} and follow the prompts to create your CLI configuration file and connect it to your HubSpot account"
1479
+ projectConfig:
1480
+ header: "Project configuration"
1481
+ projectDirSubHeader: "Project dir: {{#bold}}{{ projectDir }}{{/bold}}"
1482
+ projectNameSubHeader: "Project name: {{#bold}}{{ projectName }}{{/bold}}"
1483
+ counts:
1484
+ errors: '{{#bold}}Errors:{{/bold}} {{ count }}'
1485
+ warnings: "{{#bold}}Warning:{{/bold}} {{ count }}"
1486
+
1487
+
1488
+
@@ -6,7 +6,7 @@ const { COMPONENT_TYPES } = require('./projectStructure');
6
6
  const { i18n } = require('./lang');
7
7
  const { promptUser } = require('./prompts/promptUtils');
8
8
  const { DevModeInterface } = require('@hubspot/ui-extensions-dev-server');
9
- const { startPortManagerServer, portManagerHasActiveServers, stopPortManagerServer, requestPorts, } = require('@hubspot/local-dev-lib/portManager');
9
+ const { startPortManagerServer, stopPortManagerServer, requestPorts, } = require('@hubspot/local-dev-lib/portManager');
10
10
  const { getHubSpotApiOrigin, getHubSpotWebsiteOrigin, } = require('@hubspot/local-dev-lib/urls');
11
11
  const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
12
12
  const i18nKey = 'lib.DevServerManager';
@@ -109,10 +109,7 @@ class DevServerManager {
109
109
  await serverInterface.cleanup();
110
110
  }
111
111
  });
112
- const hasActiveServers = await portManagerHasActiveServers();
113
- if (!hasActiveServers) {
114
- await stopPortManagerServer();
115
- }
112
+ await stopPortManagerServer();
116
113
  }
117
114
  }
118
115
  }
@@ -1,4 +1,5 @@
1
1
  import { Argv, Arguments } from 'yargs';
2
+ import { Mode } from '@hubspot/local-dev-lib/types/Files';
2
3
  export declare function addAccountOptions(yargs: Argv): Argv;
3
4
  export declare function addConfigOptions(yargs: Argv): Argv;
4
5
  export declare function addOverwriteOptions(yargs: Argv): Argv;
@@ -22,5 +23,5 @@ export declare function getAccountId(options: Arguments<{
22
23
  account?: number | string;
23
24
  }>): number | null;
24
25
  export declare function getMode(options: Arguments<{
25
- mode?: string;
26
- }>): string;
26
+ mode?: Mode;
27
+ }>): Mode;
package/lib/commonOpts.js CHANGED
@@ -76,8 +76,8 @@ function getCommandName(argv) {
76
76
  * Obtains accountId using supplied --account flag or from environment variables
77
77
  */
78
78
  function getAccountId(options) {
79
- const { portal, account } = options;
80
- if (options.useEnv && process.env.HUBSPOT_PORTAL_ID) {
79
+ const { portal, account } = options || {};
80
+ if (options?.useEnv && process.env.HUBSPOT_PORTAL_ID) {
81
81
  return parseInt(process.env.HUBSPOT_PORTAL_ID, 10);
82
82
  }
83
83
  return (0, config_1.getAccountId)(portal || account);
@@ -1 +1,3 @@
1
- export {};
1
+ export declare function isGloballyInstalled(command: any): Promise<boolean>;
2
+ export declare function getLatestCliVersion(): string;
3
+ export declare function hasMissingPackages(directory: any): Promise<boolean>;
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isGloballyInstalled = isGloballyInstalled;
4
+ exports.getLatestCliVersion = getLatestCliVersion;
5
+ exports.hasMissingPackages = hasMissingPackages;
3
6
  // @ts-nocheck
4
7
  const { logger } = require('@hubspot/local-dev-lib/logger');
5
8
  const { getProjectConfig } = require('./projects');
@@ -11,6 +14,7 @@ const util = require('util');
11
14
  const { i18n } = require('./lang');
12
15
  const SpinniesManager = require('./ui/SpinniesManager');
13
16
  const fs = require('fs');
17
+ const pkg = require('../package.json');
14
18
  const DEFAULT_PACKAGE_MANAGER = 'npm';
15
19
  const i18nKey = `commands.project.subcommands.installDeps`;
16
20
  class NoPackageJsonFilesError extends Error {
@@ -31,6 +35,12 @@ async function isGloballyInstalled(command) {
31
35
  return false;
32
36
  }
33
37
  }
38
+ async function getLatestCliVersion() {
39
+ const exec = util.promisify(execAsync);
40
+ const { stdout } = await exec(`npm info ${pkg.name} dist-tags --json`);
41
+ const { latest } = JSON.parse(stdout);
42
+ return latest;
43
+ }
34
44
  async function installPackages({ packages, installLocations }) {
35
45
  const installDirs = installLocations || (await getProjectPackageJsonLocations());
36
46
  await Promise.all(installDirs.map(async (dir) => {
@@ -109,9 +119,18 @@ async function getProjectPackageJsonLocations() {
109
119
  });
110
120
  return packageParentDirs;
111
121
  }
122
+ async function hasMissingPackages(directory) {
123
+ const exec = util.promisify(execAsync);
124
+ const { stdout } = await exec(`npm install --ignore-scripts --dry-run`, {
125
+ cwd: directory,
126
+ });
127
+ return !stdout?.includes('up to date in');
128
+ }
112
129
  module.exports = {
113
130
  isGloballyInstalled,
114
131
  installPackages,
115
132
  DEFAULT_PACKAGE_MANAGER,
116
133
  getProjectPackageJsonLocations,
134
+ getLatestCliVersion,
135
+ hasMissingPackages,
117
136
  };
@@ -0,0 +1,27 @@
1
+ import { DiagnosticInfo } from './DiagnosticInfoBuilder';
2
+ interface DiagnosisOptions {
3
+ diagnosticInfo: DiagnosticInfo;
4
+ accountId: number | null;
5
+ }
6
+ interface Section {
7
+ type: 'error' | 'warning' | 'success';
8
+ message: string;
9
+ secondaryMessaging?: string;
10
+ }
11
+ export declare class Diagnosis {
12
+ private readonly prefixes;
13
+ private readonly diagnosis;
14
+ private readonly indentation;
15
+ private errorCount;
16
+ private warningCount;
17
+ constructor({ diagnosticInfo, accountId }: DiagnosisOptions);
18
+ private indent;
19
+ getErrorCount(): number;
20
+ getWarningCount(): number;
21
+ addCliSection(section: Section): void;
22
+ addProjectSection(section: Section): void;
23
+ addCLIConfigSection(section: Section): void;
24
+ toString(): string;
25
+ private generateSections;
26
+ }
27
+ export {};
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Diagnosis = void 0;
4
+ const spinniesUtils_1 = require("../ui/spinniesUtils");
5
+ const chalk_1 = require("chalk");
6
+ const interpolation_1 = require("../interpolation");
7
+ const ui_1 = require("../ui");
8
+ const { i18n } = require('../lang');
9
+ const i18nKey = `lib.doctor.diagnosis`;
10
+ class Diagnosis {
11
+ prefixes;
12
+ diagnosis;
13
+ indentation = ' ';
14
+ errorCount = 0;
15
+ warningCount = 0;
16
+ constructor({ diagnosticInfo, accountId }) {
17
+ const { succeedPrefix, failPrefix } = (0, spinniesUtils_1.prefixOptions)({});
18
+ this.prefixes = {
19
+ success: (0, chalk_1.green)(succeedPrefix),
20
+ error: (0, chalk_1.red)(failPrefix),
21
+ warning: interpolation_1.helpers.orange('!'),
22
+ };
23
+ this.diagnosis = {
24
+ cli: {
25
+ header: i18n(`${i18nKey}.cli.header`),
26
+ sections: [],
27
+ },
28
+ cliConfig: {
29
+ header: i18n(`${i18nKey}.cliConfig.header`),
30
+ sections: [],
31
+ },
32
+ project: {
33
+ header: i18n(`${i18nKey}.projectConfig.header`),
34
+ subheaders: [
35
+ i18n(`${i18nKey}.projectConfig.projectDirSubHeader`, {
36
+ projectDir: diagnosticInfo.project?.config?.projectDir,
37
+ }),
38
+ i18n(`${i18nKey}.projectConfig.projectNameSubHeader`, {
39
+ projectName: diagnosticInfo.project?.config?.projectConfig?.name,
40
+ }),
41
+ ],
42
+ sections: [],
43
+ },
44
+ };
45
+ if (diagnosticInfo.config) {
46
+ this.diagnosis.cliConfig.subheaders = [
47
+ i18n(`${i18nKey}.cliConfig.configFileSubHeader`, {
48
+ filename: diagnosticInfo.config,
49
+ }),
50
+ i18n(`${i18nKey}.cliConfig.defaultAccountSubHeader`, {
51
+ accountDetails: (0, ui_1.uiAccountDescription)(accountId),
52
+ }),
53
+ ];
54
+ }
55
+ }
56
+ indent(level) {
57
+ return this.indentation.repeat(level);
58
+ }
59
+ getErrorCount() {
60
+ return this.errorCount;
61
+ }
62
+ getWarningCount() {
63
+ return this.warningCount;
64
+ }
65
+ addCliSection(section) {
66
+ this.diagnosis.cli.sections.push(section);
67
+ }
68
+ addProjectSection(section) {
69
+ this.diagnosis.project.sections.push(section);
70
+ }
71
+ addCLIConfigSection(section) {
72
+ this.diagnosis.cliConfig.sections.push(section);
73
+ }
74
+ toString() {
75
+ const output = [];
76
+ for (const value of Object.values(this.diagnosis)) {
77
+ const section = this.generateSections(value);
78
+ if (section) {
79
+ output.push(section);
80
+ }
81
+ }
82
+ if (output.length === 0) {
83
+ return '';
84
+ }
85
+ output.push('');
86
+ output.push(i18n(`${i18nKey}.counts.errors`, {
87
+ count: this.errorCount,
88
+ }));
89
+ output.push(i18n(`${i18nKey}.counts.warnings`, {
90
+ count: this.warningCount,
91
+ }));
92
+ output.push('');
93
+ return output.join('\n');
94
+ }
95
+ generateSections(category) {
96
+ const output = [];
97
+ if (category.sections && category.sections.length === 0) {
98
+ return '';
99
+ }
100
+ output.push(`\n${(0, chalk_1.bold)(category.header)}`);
101
+ (category.subheaders || []).forEach(subheader => {
102
+ output.push(`${subheader}`);
103
+ });
104
+ category.sections.forEach(section => {
105
+ if (section.type === 'error') {
106
+ this.errorCount++;
107
+ }
108
+ else if (section.type === 'warning') {
109
+ this.warningCount++;
110
+ }
111
+ output.push(`${this.indent(1)}${this.prefixes[section.type]} ${section.message}`);
112
+ if (section.secondaryMessaging) {
113
+ output.push(`${this.indent(2)}- ${section.secondaryMessaging}`);
114
+ }
115
+ });
116
+ return output.join('\n');
117
+ }
118
+ }
119
+ exports.Diagnosis = Diagnosis;