@hubspot/cli 3.0.13-beta.2 → 4.0.0

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.
package/bin/cli.js CHANGED
@@ -58,47 +58,74 @@ const getTerminalWidth = () => {
58
58
  return width;
59
59
  };
60
60
 
61
+ const handleFailure = (msg, err, yargs) => {
62
+ if (msg) {
63
+ logger.error(msg);
64
+ } else if (err) {
65
+ logErrorInstance(err);
66
+ }
67
+
68
+ if (msg === null) {
69
+ yargs.showHelp();
70
+ process.exit(EXIT_CODES.SUCCESS);
71
+ } else {
72
+ process.exit(EXIT_CODES.ERROR);
73
+ }
74
+ };
75
+
76
+ const performChecks = argv => {
77
+ // "hs config set default-account" has moved to "hs accounts use"
78
+ if (
79
+ argv._[0] === 'config' &&
80
+ argv._[1] === 'set' &&
81
+ argv._[2] === 'default-account'
82
+ ) {
83
+ logger.error(i18n(`${i18nKey}.setDefaultAccountMoved`));
84
+ process.exit(EXIT_CODES.ERROR);
85
+ }
86
+
87
+ // Require "project" command when running upload/watch inside of a project
88
+ if (argv._.length === 1 && ['upload', 'watch'].includes(argv._[0])) {
89
+ if (getIsInProject(argv.src)) {
90
+ logger.error(
91
+ i18n(`${i18nKey}.srcIsProject`, {
92
+ src: argv.src || './',
93
+ command: argv._.join(' '),
94
+ })
95
+ );
96
+ process.exit(EXIT_CODES.ERROR);
97
+ } else {
98
+ return true;
99
+ }
100
+ } else {
101
+ return true;
102
+ }
103
+ };
104
+
61
105
  const argv = yargs
62
106
  .usage('Tools for working with HubSpot')
63
107
  .middleware([setLogLevel])
64
108
  .exitProcess(false)
65
- .fail((msg, err, yargs) => {
66
- if (msg) {
67
- logger.error(msg);
68
- } else if (err) {
69
- logErrorInstance(err);
70
- }
71
-
72
- if (msg === null) {
73
- yargs.showHelp();
74
- process.exit(EXIT_CODES.SUCCESS);
75
- } else {
76
- process.exit(EXIT_CODES.ERROR);
77
- }
78
- })
109
+ .fail(handleFailure)
79
110
  .option('debug', {
80
111
  alias: 'd',
81
112
  default: false,
82
113
  describe: 'set log level to debug',
83
114
  type: 'boolean',
84
115
  })
85
- .check(argv => {
86
- if (argv._.length === 1 && ['upload', 'watch'].includes(argv._[0])) {
87
- if (getIsInProject(argv.src)) {
88
- logger.error(
89
- i18n(`${i18nKey}.srcIsProject`, {
90
- src: argv.src,
91
- command: argv._.join(' '),
92
- })
93
- );
94
- process.exit(EXIT_CODES.ERROR);
95
- } else {
96
- return true;
97
- }
98
- } else {
99
- return true;
100
- }
116
+ .option('noHyperlinks', {
117
+ default: false,
118
+ describe: 'prevent hyperlinks from displaying in the ui',
119
+ hidden: true,
120
+ type: 'boolean',
121
+ })
122
+ .option('noColor', {
123
+ default: false,
124
+ describe: 'prevent color from displaying in the ui',
125
+ hidden: true,
126
+ type: 'boolean',
101
127
  })
128
+ .check(performChecks)
102
129
  .command(authCommand)
103
130
  .command(initCommand)
104
131
  .command(logsCommand)
@@ -33,7 +33,7 @@ const selectAccountFromConfig = async config => {
33
33
  return selectedDefault;
34
34
  };
35
35
 
36
- exports.command = 'use [account]';
36
+ exports.command = 'use [--account]';
37
37
  exports.describe = i18n(`${i18nKey}.describe`);
38
38
 
39
39
  exports.handler = async options => {
@@ -68,14 +68,17 @@ exports.handler = async options => {
68
68
  };
69
69
 
70
70
  exports.builder = yargs => {
71
- yargs.positional('account', {
72
- describe: i18n(`${i18nKey}.positionals.account.describe`),
71
+ yargs.option('account', {
72
+ describe: i18n(`${i18nKey}.options.account.describe`),
73
73
  type: 'string',
74
74
  });
75
75
  yargs.example([
76
76
  ['$0 accounts use', i18n(`${i18nKey}.examples.default`)],
77
- ['$0 accounts use MyAccount', i18n(`${i18nKey}.examples.nameBased`)],
78
- ['$0 accounts use 1234567', i18n(`${i18nKey}.examples.idBased`)],
77
+ [
78
+ '$0 accounts use --account=MyAccount',
79
+ i18n(`${i18nKey}.examples.nameBased`),
80
+ ],
81
+ ['$0 accounts use --account=1234567', i18n(`${i18nKey}.examples.idBased`)],
79
82
  ]);
80
83
 
81
84
  return yargs;
@@ -33,7 +33,7 @@ const selectOptions = async () => {
33
33
  };
34
34
 
35
35
  const handleConfigUpdate = async (accountId, options) => {
36
- const { defaultMode, httpTimeout, allowUsageTracking } = options;
36
+ const { allowUsageTracking, defaultMode, httpTimeout } = options;
37
37
 
38
38
  if (typeof defaultMode !== 'undefined') {
39
39
  await setDefaultMode({ defaultMode, accountId });
@@ -89,5 +89,8 @@ exports.builder = yargs => {
89
89
 
90
90
  yargs.example([['$0 config set', i18n(`${i18nKey}.examples.default`)]]);
91
91
 
92
+ //TODO remove this when "hs accounts use" is fully rolled out
93
+ yargs.strict(false);
94
+
92
95
  return yargs;
93
96
  };
@@ -120,7 +120,7 @@ const handleFunctionLog = async (accountId, options) => {
120
120
  return false;
121
121
  };
122
122
 
123
- exports.command = 'logs [--project] [--app] [--function]';
123
+ exports.command = 'logs [--project] [--app] [--function] [--endpoint]';
124
124
  exports.describe = i18n(`${i18nKey}.describe`);
125
125
 
126
126
  exports.handler = async options => {
@@ -128,21 +128,22 @@ exports.handler = async options => {
128
128
 
129
129
  const accountId = getAccountId(options);
130
130
 
131
- logger.log(options);
132
131
  const {
133
132
  projectName: promptProjectName,
134
133
  appName: promptAppName,
135
134
  functionName: promptFunctionName,
135
+ endpointName: promptEndpointName,
136
136
  } = await projectLogsPrompt(accountId, options);
137
137
 
138
138
  const projectName = options.project || promptProjectName;
139
139
  const appName = options.app || promptAppName;
140
140
  const functionName =
141
141
  options.function || promptFunctionName || options.endpoint;
142
+ const endpointName = options.endpoint || promptEndpointName;
142
143
 
143
144
  let relativeAppPath;
144
145
 
145
- if (appName && !options.endpoint) {
146
+ if (appName && !endpointName) {
146
147
  await ensureProjectExists(accountId, projectName, {
147
148
  allowCreate: false,
148
149
  });
@@ -168,21 +169,31 @@ exports.handler = async options => {
168
169
 
169
170
  trackCommandUsage('project-logs', { latest: options.latest }, accountId);
170
171
 
171
- const logsInfo = [
172
- [accountId, `"${projectName}"`, `"${appName}"`, functionName],
173
- ];
172
+ const logsInfo = [accountId, `"${projectName}"`];
173
+ let tableHeader;
174
174
 
175
- logsInfo.unshift(
176
- getTableHeader([
175
+ if (endpointName) {
176
+ logsInfo.push(`"${endpointName}"`);
177
+ tableHeader = getTableHeader([
178
+ i18n(`${i18nKey}.table.accountHeader`),
179
+ i18n(`${i18nKey}.table.projectHeader`),
180
+ i18n(`${i18nKey}.table.endpointHeader`),
181
+ ]);
182
+ } else {
183
+ logsInfo.push(`"${appName}"`);
184
+ logsInfo.push(functionName);
185
+ tableHeader = getTableHeader([
177
186
  i18n(`${i18nKey}.table.accountHeader`),
178
187
  i18n(`${i18nKey}.table.projectHeader`),
179
188
  i18n(`${i18nKey}.table.appHeader`),
180
189
  i18n(`${i18nKey}.table.functionHeader`),
181
- ])
182
- );
190
+ ]);
191
+ }
183
192
 
184
193
  logger.log(i18n(`${i18nKey}.logs.showingLogs`));
185
- logger.log(getTableContents(logsInfo, { border: { bodyLeft: ' ' } }));
194
+ logger.log(
195
+ getTableContents([tableHeader, logsInfo], { border: { bodyLeft: ' ' } })
196
+ );
186
197
 
187
198
  logger.log(
188
199
  uiLink(
@@ -197,7 +208,7 @@ exports.handler = async options => {
197
208
  ...options,
198
209
  projectName,
199
210
  appPath: relativeAppPath,
200
- functionName,
211
+ functionName: functionName || endpointName,
201
212
  });
202
213
 
203
214
  if (showFinalMessage) {
@@ -217,7 +228,6 @@ exports.builder = yargs => {
217
228
  endpoint: {
218
229
  alias: 'endpoint',
219
230
  describe: i18n(`${i18nKey}.options.endpoint.describe`),
220
- hidden: true,
221
231
  requiresArg: true,
222
232
  type: 'string',
223
233
  },
@@ -253,10 +263,11 @@ exports.builder = yargs => {
253
263
  })
254
264
  .conflicts('follow', 'limit');
255
265
 
266
+ yargs.example([['$0 project logs', i18n(`${i18nKey}.examples.default`)]]);
256
267
  yargs.example([
257
268
  [
258
- '$0 project logs --project="my-project" --app="app" --function=my-function',
259
- i18n(`${i18nKey}.examples.default`),
269
+ '$0 project logs --project=my-project --app=app --function=my-function',
270
+ i18n(`${i18nKey}.examples.withOptions`),
260
271
  ],
261
272
  ]);
262
273
 
@@ -26,6 +26,7 @@ const {
26
26
  getAccountId,
27
27
  getMode,
28
28
  } = require('../lib/commonOpts');
29
+ const { uploadPrompt } = require('../lib/prompts/uploadPrompt');
29
30
  const { validateMode, loadAndValidateOptions } = require('../lib/validation');
30
31
  const { trackCommandUsage } = require('../lib/usageTracking');
31
32
  const { getThemePreviewUrl } = require('@hubspot/cli-lib/lib/files');
@@ -34,7 +35,7 @@ const { i18n } = require('@hubspot/cli-lib/lib/lang');
34
35
  const i18nKey = 'cli.commands.upload';
35
36
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
36
37
 
37
- exports.command = 'upload <src> <dest>';
38
+ exports.command = 'upload [--src] [--dest]';
38
39
  exports.describe = i18n(`${i18nKey}.describe`);
39
40
 
40
41
  const logThemePreview = (filePath, accountId) => {
@@ -50,8 +51,6 @@ const logThemePreview = (filePath, accountId) => {
50
51
  };
51
52
 
52
53
  exports.handler = async options => {
53
- const { src, dest } = options;
54
-
55
54
  await loadAndValidateOptions(options);
56
55
 
57
56
  if (!validateMode(options)) {
@@ -60,6 +59,12 @@ exports.handler = async options => {
60
59
 
61
60
  const accountId = getAccountId(options);
62
61
  const mode = getMode(options);
62
+
63
+ const uploadPromptAnswers = await uploadPrompt(options);
64
+
65
+ const src = options.src || uploadPromptAnswers.src;
66
+ const dest = options.dest || uploadPromptAnswers.dest;
67
+
63
68
  const absoluteSrcPath = path.resolve(getCwd(), src);
64
69
  let stats;
65
70
  try {
package/commands/watch.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  getAccountId,
14
14
  getMode,
15
15
  } = require('../lib/commonOpts');
16
+ const { uploadPrompt } = require('../lib/prompts/uploadPrompt');
16
17
  const { validateMode, loadAndValidateOptions } = require('../lib/validation');
17
18
  const { trackCommandUsage } = require('../lib/usageTracking');
18
19
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
@@ -20,11 +21,11 @@ const { i18n } = require('@hubspot/cli-lib/lib/lang');
20
21
  const i18nKey = 'cli.commands.watch';
21
22
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
22
23
 
23
- exports.command = 'watch <src> <dest>';
24
+ exports.command = 'watch [--src] [--dest]';
24
25
  exports.describe = i18n(`${i18nKey}.describe`);
25
26
 
26
27
  exports.handler = async options => {
27
- const { src, dest, remove, initialUpload, disableInitial, notify } = options;
28
+ const { remove, initialUpload, disableInitial, notify } = options;
28
29
 
29
30
  await loadAndValidateOptions(options);
30
31
 
@@ -35,6 +36,11 @@ exports.handler = async options => {
35
36
  const accountId = getAccountId(options);
36
37
  const mode = getMode(options);
37
38
 
39
+ const uploadPromptAnswers = await uploadPrompt(options);
40
+
41
+ const src = options.src || uploadPromptAnswers.src;
42
+ const dest = options.dest || uploadPromptAnswers.dest;
43
+
38
44
  const absoluteSrcPath = path.resolve(getCwd(), src);
39
45
  try {
40
46
  const stats = fs.statSync(absoluteSrcPath);
package/lib/hasFlag.js ADDED
@@ -0,0 +1,15 @@
1
+ const process = require('process');
2
+
3
+ // See https://github.com/sindresorhus/has-flag/blob/main/index.js (License: https://github.com/sindresorhus/has-flag/blob/main/license)
4
+
5
+ function hasFlag(flag, argv = process.argv) {
6
+ const prefix = flag.startsWith('-') ? '' : flag.length === 1 ? '-' : '--';
7
+ const position = argv.indexOf(prefix + flag);
8
+ const terminatorPosition = argv.indexOf('--');
9
+ return (
10
+ position !== -1 &&
11
+ (terminatorPosition === -1 || position < terminatorPosition)
12
+ );
13
+ }
14
+
15
+ module.exports = hasFlag;
package/lib/projects.js CHANGED
@@ -211,7 +211,7 @@ const getProjectDetailUrl = (projectName, accountId) => {
211
211
  if (!projectName) return;
212
212
 
213
213
  const baseUrl = getHubSpotWebsiteOrigin(
214
- getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
214
+ getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
215
215
  );
216
216
 
217
217
  return `${baseUrl}/developer-projects/${accountId}/project/${projectName}`;
@@ -483,8 +483,7 @@ const pollBuildStatus = makePollTaskStatusFunc({
483
483
  linkToHubSpot: (projectName, buildId, accountId) =>
484
484
  uiLink(
485
485
  `View build #${buildId} in HubSpot`,
486
- getProjectBuildDetailUrl(projectName, buildId, accountId),
487
- { useColor: true }
486
+ getProjectBuildDetailUrl(projectName, buildId, accountId)
488
487
  ),
489
488
  statusFn: getBuildStatus,
490
489
  statusText: PROJECT_BUILD_TEXT,
@@ -7,22 +7,55 @@ const { EXIT_CODES } = require('../enums/exitCodes');
7
7
 
8
8
  const i18nKey = 'cli.lib.prompts.projectLogsPrompt';
9
9
 
10
+ const SERVERLESS_FUNCTION_TYPES = {
11
+ APP_FUNCTION: 'app-function',
12
+ PUBLIC_ENDPOINT: 'public-endpoint',
13
+ };
14
+
10
15
  const projectLogsPrompt = (accountId, promptOptions = {}) => {
11
16
  return promptUser([
12
17
  {
13
18
  name: 'projectName',
14
- message: i18n(`${i18nKey}.projectName`),
19
+ message: i18n(`${i18nKey}.projectName.message`),
15
20
  when: !promptOptions.project,
16
21
  default: async () => {
17
22
  const { projectConfig } = await getProjectConfig();
18
23
  return projectConfig && projectConfig.name ? projectConfig.name : null;
19
24
  },
25
+ validate: name =>
26
+ !name.length ? i18n(`${i18nKey}.projectName.error`) : true,
27
+ },
28
+ {
29
+ name: 'logType',
30
+ type: 'list',
31
+ message: i18n(`${i18nKey}.logType.message`),
32
+ when:
33
+ !promptOptions.app &&
34
+ !promptOptions.function &&
35
+ !promptOptions.endpoint,
36
+ choices: [
37
+ {
38
+ name: i18n(`${i18nKey}.logType.function`),
39
+ value: SERVERLESS_FUNCTION_TYPES.APP_FUNCTION,
40
+ },
41
+ {
42
+ name: i18n(`${i18nKey}.logType.endpoint`),
43
+ value: SERVERLESS_FUNCTION_TYPES.PUBLIC_ENDPOINT,
44
+ },
45
+ ],
20
46
  },
21
47
  {
22
48
  name: 'appName',
23
49
  type: 'list',
24
50
  message: i18n(`${i18nKey}.appName`),
25
- when: !promptOptions.app && !promptOptions.endpoint,
51
+ when: ({ logType }) => {
52
+ return (
53
+ (promptOptions.function ||
54
+ logType === SERVERLESS_FUNCTION_TYPES.APP_FUNCTION) &&
55
+ !promptOptions.app &&
56
+ !promptOptions.endpoint
57
+ );
58
+ },
26
59
  choices: async ({ projectName }) => {
27
60
  const name = projectName || promptOptions.project;
28
61
 
@@ -47,7 +80,25 @@ const projectLogsPrompt = (accountId, promptOptions = {}) => {
47
80
  {
48
81
  name: 'functionName',
49
82
  message: i18n(`${i18nKey}.functionName`),
50
- when: !promptOptions.function && !promptOptions.endpoint,
83
+ when: ({ logType }) => {
84
+ return (
85
+ (promptOptions.app ||
86
+ logType === SERVERLESS_FUNCTION_TYPES.APP_FUNCTION) &&
87
+ !promptOptions.function &&
88
+ !promptOptions.endpoint
89
+ );
90
+ },
91
+ },
92
+ {
93
+ name: 'endpointName',
94
+ message: i18n(`${i18nKey}.endpointName`),
95
+ when: ({ logType }) => {
96
+ return (
97
+ logType === SERVERLESS_FUNCTION_TYPES.PUBLIC_ENDPOINT &&
98
+ !promptOptions.function &&
99
+ !promptOptions.endpoint
100
+ );
101
+ },
51
102
  },
52
103
  ]);
53
104
  };
@@ -0,0 +1,39 @@
1
+ const path = require('path');
2
+ const { getCwd } = require('@hubspot/cli-lib/path');
3
+ const { promptUser } = require('./promptUtils');
4
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
5
+
6
+ const i18nKey = 'cli.lib.prompts.uploadPrompt';
7
+
8
+ const uploadPrompt = (promptOptions = {}) => {
9
+ return promptUser([
10
+ {
11
+ name: 'src',
12
+ message: i18n(`${i18nKey}.enterSrc`),
13
+ when: !promptOptions.src,
14
+ default: '.',
15
+ validate: input => {
16
+ if (!input) {
17
+ return i18n(`${i18nKey}.errors.srcRequired`);
18
+ }
19
+ return true;
20
+ },
21
+ },
22
+ {
23
+ name: 'dest',
24
+ message: i18n(`${i18nKey}.enterDest`),
25
+ when: !promptOptions.dest,
26
+ default: path.basename(getCwd()),
27
+ validate: input => {
28
+ if (!input) {
29
+ return i18n(`${i18nKey}.errors.destRequired`);
30
+ }
31
+ return true;
32
+ },
33
+ },
34
+ ]);
35
+ };
36
+
37
+ module.exports = {
38
+ uploadPrompt,
39
+ };
@@ -1,5 +1,6 @@
1
1
  const https = require('https');
2
2
  const Spinnies = require('spinnies');
3
+ const chalk = require('chalk');
3
4
  const { logger } = require('@hubspot/cli-lib/logger');
4
5
  const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
5
6
  const {
@@ -17,6 +18,7 @@ const TAIL_DELAY = 5000;
17
18
  const handleUserInput = spinnies => {
18
19
  const onTerminate = async () => {
19
20
  spinnies.remove('tailLogs');
21
+ spinnies.remove('stopMessage');
20
22
  process.exit(EXIT_CODES.SUCCESS);
21
23
  };
22
24
 
@@ -83,7 +85,11 @@ const tailLogs = async ({
83
85
  const spinnies = new Spinnies();
84
86
 
85
87
  spinnies.add('tailLogs', {
86
- text: `Following logs for ${name}. Press "Q" to stop following`,
88
+ text: `Following logs for ${name}`,
89
+ });
90
+ spinnies.add('stopMessage', {
91
+ text: `> Press ${chalk.bold('q')} to stop following`,
92
+ status: 'non-spinnable',
87
93
  });
88
94
 
89
95
  handleUserInput(spinnies);
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+ const hasFlag = require('./hasFlag');
3
+
4
+ //See https://github.com/jamestalmage/supports-hyperlinks (License: https://github.com/jamestalmage/supports-hyperlinks/blob/master/license)
5
+
6
+ function parseVersion(versionString) {
7
+ if (/^\d{3,4}$/.test(versionString)) {
8
+ // Env var doesn't always use dots. example: 4601 => 46.1.0
9
+ const m = /(\d{1,2})(\d{2})/.exec(versionString);
10
+ return {
11
+ major: 0,
12
+ minor: parseInt(m[1], 10),
13
+ patch: parseInt(m[2], 10),
14
+ };
15
+ }
16
+
17
+ const versions = (versionString || '').split('.').map(n => parseInt(n, 10));
18
+ return {
19
+ major: versions[0],
20
+ minor: versions[1],
21
+ patch: versions[2],
22
+ };
23
+ }
24
+
25
+ function supportsHyperlink(stream) {
26
+ const { env } = process;
27
+
28
+ if (hasFlag('noHyperlinks')) {
29
+ return false;
30
+ }
31
+
32
+ if (stream && !stream.isTTY) {
33
+ return false;
34
+ }
35
+
36
+ if (process.platform === 'win32') {
37
+ return false;
38
+ }
39
+
40
+ if ('CI' in env) {
41
+ return false;
42
+ }
43
+
44
+ if ('TERM_PROGRAM' in env) {
45
+ const version = parseVersion(env.TERM_PROGRAM_VERSION);
46
+
47
+ switch (env.TERM_PROGRAM) {
48
+ case 'iTerm.app':
49
+ if (version.major === 3) {
50
+ return version.minor >= 1;
51
+ }
52
+
53
+ return version.major > 3;
54
+ // No default
55
+ }
56
+ }
57
+
58
+ if ('VTE_VERSION' in env) {
59
+ // 0.50.0 was supposed to support hyperlinks, but throws a segfault
60
+ if (env.VTE_VERSION === '0.50.0') {
61
+ return false;
62
+ }
63
+
64
+ const version = parseVersion(env.VTE_VERSION);
65
+ return version.major > 0 || version.minor >= 50;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ module.exports = {
72
+ supportsHyperlink,
73
+ stdout: supportsHyperlink(process.stdout),
74
+ stderr: supportsHyperlink(process.stderr),
75
+ };
@@ -0,0 +1,129 @@
1
+ const process = require('process');
2
+ const os = require('os');
3
+ const tty = require('tty');
4
+ const hasFlag = require('./hasFlag');
5
+
6
+ const { env } = process;
7
+
8
+ //From: https://github.com/chalk/supports-color/blob/main/index.js (License: https://github.com/chalk/supports-color/blob/main/license)
9
+
10
+ function translateLevel(level) {
11
+ if (level === 0) {
12
+ return {
13
+ level,
14
+ hasBasic: false,
15
+ has256: false,
16
+ has16m: false,
17
+ };
18
+ }
19
+
20
+ return {
21
+ level,
22
+ hasBasic: true,
23
+ has256: level >= 2,
24
+ has16m: level >= 3,
25
+ };
26
+ }
27
+
28
+ function _supportsColor(haveStream, { streamIsTTY } = {}) {
29
+ if (haveStream && !streamIsTTY) {
30
+ return 0;
31
+ }
32
+
33
+ const min = 0;
34
+
35
+ if (env.TERM === 'dumb') {
36
+ return min;
37
+ }
38
+
39
+ if (hasFlag('noColor')) {
40
+ return 0;
41
+ }
42
+
43
+ if (process.platform === 'win32') {
44
+ // Windows 10 build 10586 is the first Windows release that supports 256 colors.
45
+ // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
46
+ const osRelease = os.release().split('.');
47
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
48
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
49
+ }
50
+
51
+ return 1;
52
+ }
53
+
54
+ if ('CI' in env) {
55
+ if (
56
+ [
57
+ 'TRAVIS',
58
+ 'CIRCLECI',
59
+ 'APPVEYOR',
60
+ 'GITLAB_CI',
61
+ 'GITHUB_ACTIONS',
62
+ 'BUILDKITE',
63
+ 'DRONE',
64
+ ].some(sign => sign in env) ||
65
+ env.CI_NAME === 'codeship'
66
+ ) {
67
+ return 1;
68
+ }
69
+
70
+ return min;
71
+ }
72
+
73
+ // Check for Azure DevOps pipelines
74
+ if ('TF_BUILD' in env && 'AGENT_NAME' in env) {
75
+ return 1;
76
+ }
77
+
78
+ if (env.COLORTERM === 'truecolor') {
79
+ return 3;
80
+ }
81
+
82
+ if ('TERM_PROGRAM' in env) {
83
+ const version = Number.parseInt(
84
+ (env.TERM_PROGRAM_VERSION || '').split('.')[0],
85
+ 10
86
+ );
87
+
88
+ switch (env.TERM_PROGRAM) {
89
+ case 'iTerm.app':
90
+ return version >= 3 ? 3 : 2;
91
+ case 'Apple_Terminal':
92
+ return 2;
93
+ // No default
94
+ }
95
+ }
96
+
97
+ if (/-256(color)?$/i.test(env.TERM)) {
98
+ return 2;
99
+ }
100
+
101
+ if (
102
+ /^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)
103
+ ) {
104
+ return 1;
105
+ }
106
+
107
+ if ('COLORTERM' in env) {
108
+ return 1;
109
+ }
110
+
111
+ return min;
112
+ }
113
+
114
+ function createSupportsColor(stream, options = {}) {
115
+ const level = _supportsColor(stream, {
116
+ streamIsTTY: stream && stream.isTTY,
117
+ ...options,
118
+ });
119
+
120
+ return translateLevel(level);
121
+ }
122
+
123
+ const supportsColor = {
124
+ createSupportsColor,
125
+ stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
126
+ stderr: createSupportsColor({ isTTY: tty.isatty(2) }),
127
+ };
128
+
129
+ module.exports = supportsColor;
package/lib/ui.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const chalk = require('chalk');
2
- const supportsHyperlinks = require('supports-hyperlinks');
2
+ const supportsHyperlinks = require('../lib/supportHyperlinks');
3
+ const supportsColor = require('../lib/supportsColor');
3
4
  const { getAccountConfig } = require('@hubspot/cli-lib/lib/config');
4
5
  const { logger } = require('@hubspot/cli-lib/logger');
5
6
 
@@ -12,6 +13,19 @@ const uiLine = () => {
12
13
  logger.log('-'.repeat(50));
13
14
  };
14
15
 
16
+ /**
17
+ * Returns an object that aggregates what the terminal supports (eg. hyperlinks and color)
18
+ *
19
+ * @returns {object}
20
+ */
21
+
22
+ const getTerminalUISupport = () => {
23
+ return {
24
+ hyperlinks: supportsHyperlinks.stdout,
25
+ color: supportsColor.stdout.hasBasic,
26
+ };
27
+ };
28
+
15
29
  /**
16
30
  * Returns a hyperlink or link and description
17
31
  *
@@ -20,8 +34,9 @@ const uiLine = () => {
20
34
  * @param {object} options
21
35
  * @returns {string}
22
36
  */
23
- const uiLink = (linkText, url, options = {}) => {
24
- if (supportsHyperlinks.stdout) {
37
+ const uiLink = (linkText, url) => {
38
+ const terminalUISupport = getTerminalUISupport();
39
+ if (terminalUISupport.hyperlinks) {
25
40
  const result = [
26
41
  '\u001B]8;;',
27
42
  url,
@@ -29,9 +44,11 @@ const uiLink = (linkText, url, options = {}) => {
29
44
  linkText,
30
45
  '\u001B]8;;\u0007',
31
46
  ].join('');
32
- return options.useColor ? chalk.cyan(result) : result;
47
+ return terminalUISupport.color ? chalk.cyan(result) : result;
33
48
  } else {
34
- return options.fallback ? `${linkText}: ${url}` : linkText;
49
+ return terminalUISupport.color
50
+ ? `${linkText}: ${chalk.cyan(url)}`
51
+ : `${linkText}: ${url}`;
35
52
  }
36
53
  };
37
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "3.0.13-beta.2",
3
+ "version": "4.0.0",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/cli-lib": "3.0.13-beta.2",
12
- "@hubspot/serverless-dev-runtime": "3.0.13-beta.2",
11
+ "@hubspot/cli-lib": "4.0.0",
12
+ "@hubspot/serverless-dev-runtime": "4.0.0",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
15
  "express": "^4.17.1",
@@ -20,7 +20,6 @@
20
20
  "open": "^7.0.3",
21
21
  "ora": "^4.0.3",
22
22
  "spinnies": "^0.5.1",
23
- "supports-hyperlinks": "^2.2.0",
24
23
  "tmp": "^0.2.1",
25
24
  "update-notifier": "^5.1.0",
26
25
  "yargs": "15.4.1"
@@ -38,5 +37,5 @@
38
37
  "publishConfig": {
39
38
  "access": "public"
40
39
  },
41
- "gitHead": "ff694f8c61c2326159f7ad4d3f2cfdb18f8c1875"
40
+ "gitHead": "c11f159c8768c0a0beac7b221d4c2489bcf0064d"
42
41
  }