@hubspot/cli 4.2.1-beta.0 → 4.2.1-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.
@@ -19,7 +19,7 @@ const {
19
19
  pollProjectBuildAndDeploy,
20
20
  } = require('../../lib/projects');
21
21
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
22
- const { uiAccountDescription, uiLine } = require('../../lib/ui');
22
+ const { uiAccountDescription, uiBetaMessage, uiLine } = require('../../lib/ui');
23
23
  const { confirmPrompt } = require('../../lib/prompts/promptUtils');
24
24
  const {
25
25
  selectTargetAccountPrompt,
@@ -29,6 +29,7 @@ const {
29
29
  LocalDevManager,
30
30
  UPLOAD_PERMISSIONS,
31
31
  } = require('../../lib/LocalDevManager');
32
+ const LocalDevManagerV2 = require('../../lib/LocalDevManagerV2');
32
33
  const { isSandbox } = require('../../lib/sandboxes');
33
34
  const { getAccountConfig, getEnv } = require('@hubspot/cli-lib');
34
35
  const { sandboxNamePrompt } = require('../../lib/prompts/sandboxesPrompt');
@@ -38,7 +39,11 @@ const {
38
39
  getAvailableSyncTypes,
39
40
  } = require('../../lib/sandboxes');
40
41
  const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
41
- const { ERROR_TYPES } = require('@hubspot/cli-lib/lib/constants');
42
+ const {
43
+ PROJECT_BUILD_TEXT,
44
+ PROJECT_DEPLOY_TEXT,
45
+ ERROR_TYPES,
46
+ } = require('@hubspot/cli-lib/lib/constants');
42
47
  const {
43
48
  logErrorInstance,
44
49
  logApiErrorInstance,
@@ -67,7 +72,7 @@ exports.handler = async options => {
67
72
 
68
73
  const { projectConfig, projectDir } = await getProjectConfig();
69
74
 
70
- logger.log(i18n(`${i18nKey}.logs.betaMessage`));
75
+ uiBetaMessage(i18n(`${i18nKey}.logs.betaMessage`));
71
76
 
72
77
  if (!projectConfig) {
73
78
  logger.error(i18n(`${i18nKey}.errors.noProjectConfig`));
@@ -176,7 +181,7 @@ exports.handler = async options => {
176
181
  targetAccountId,
177
182
  projectConfig.name
178
183
  );
179
- if (sourceIntegration) {
184
+ if (options.extension || sourceIntegration) {
180
185
  uploadPermission = UPLOAD_PERMISSIONS.never;
181
186
  }
182
187
  }
@@ -253,6 +258,8 @@ exports.handler = async options => {
253
258
  );
254
259
 
255
260
  if (initialUploadResult.uploadError) {
261
+ SpinniesManager.fail('devModeSetup');
262
+
256
263
  if (
257
264
  isSpecifiedError(initialUploadResult.uploadError, {
258
265
  subCategory: ERROR_TYPES.PROJECT_LOCKED,
@@ -274,22 +281,63 @@ exports.handler = async options => {
274
281
  }
275
282
  }
276
283
 
284
+ // Let the user know when the initial build or deploy fails
285
+ // Do this before starting the dev server for v2 behavior because we cannot
286
+ // run a server on a broken project
287
+ if (
288
+ options.extension &&
289
+ initialUploadResult &&
290
+ !initialUploadResult.succeeded
291
+ ) {
292
+ SpinniesManager.fail('devModeSetup');
293
+
294
+ let subTasks = [];
295
+
296
+ if (initialUploadResult.buildResult.status === 'FAILURE') {
297
+ subTasks =
298
+ initialUploadResult.buildResult[PROJECT_BUILD_TEXT.SUBTASK_KEY];
299
+ } else if (initialUploadResult.deployResult.status === 'FAILURE') {
300
+ subTasks =
301
+ initialUploadResult.deployResult[PROJECT_DEPLOY_TEXT.SUBTASK_KEY];
302
+ }
303
+
304
+ const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
305
+
306
+ logger.log();
307
+ failedSubTasks.forEach(failedSubTask => {
308
+ console.log(failedSubTask.errorMessage);
309
+ });
310
+ logger.log();
311
+
312
+ process.exit(EXIT_CODES.ERROR);
313
+ }
314
+
277
315
  SpinniesManager.remove('devModeSetup');
278
316
 
279
- const LocalDev = new LocalDevManager({
280
- debug: options.debug,
281
- extension: options.extension,
282
- projectConfig,
283
- projectDir,
284
- targetAccountId,
285
- uploadPermission,
286
- devServerPath: options.devServerPath,
287
- });
317
+ const LocalDev = options.extension
318
+ ? new LocalDevManagerV2({
319
+ debug: options.debug,
320
+ extension: options.extension,
321
+ projectConfig,
322
+ projectDir,
323
+ targetAccountId,
324
+ })
325
+ : new LocalDevManager({
326
+ debug: options.debug,
327
+ projectConfig,
328
+ projectDir,
329
+ targetAccountId,
330
+ uploadPermission,
331
+ });
288
332
 
289
333
  await LocalDev.start();
290
334
 
291
335
  // Let the user know when the initial build or deploy fails
292
- if (initialUploadResult && !initialUploadResult.succeeded) {
336
+ if (
337
+ !options.extension &&
338
+ initialUploadResult &&
339
+ !initialUploadResult.succeeded
340
+ ) {
293
341
  if (initialUploadResult.buildResult.status === 'FAILURE') {
294
342
  LocalDev.logBuildError(initialUploadResult.buildResult);
295
343
  } else if (initialUploadResult.deployResult.status === 'FAILURE') {
@@ -312,12 +360,6 @@ exports.builder = yargs => {
312
360
  hidden: true,
313
361
  });
314
362
 
315
- yargs.option('devServerPath', {
316
- describe: i18n(`${i18nKey}.options.devServerPath.describe`),
317
- type: 'string',
318
- hidden: true,
319
- });
320
-
321
363
  yargs.example([['$0 project dev', i18n(`${i18nKey}.examples.default`)]]);
322
364
 
323
365
  return yargs;
package/lang/en.lyaml CHANGED
@@ -448,7 +448,7 @@ en:
448
448
  dev:
449
449
  describe: "Start local dev for the current project"
450
450
  logs:
451
- betaMessage: "{{#yellow}}{{#bold}}[beta]{{/bold}}{{/yellow}} HubSpot projects local development"
451
+ betaMessage: "HubSpot projects local development"
452
452
  nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
453
453
  placeholderAccountSelection: "Using default account as target account (for now)"
454
454
  projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
@@ -465,8 +465,6 @@ en:
465
465
  options:
466
466
  extension:
467
467
  describe: "The extension that you would like to run locally"
468
- devServerPath:
469
- describe: "Relative path to the dev server interface file"
470
468
  errors:
471
469
  noProjectConfig: "No project detected. Please run this command again from a project directory."
472
470
  projectLockedError: "Your project is locked. This may mean that another user is running the {{#bold}}`hs project dev`{{/bold}} command for this project. If this is you, unlock the project in Projects UI."
@@ -850,6 +848,18 @@ en:
850
848
  lib:
851
849
  DevServerManager:
852
850
  portConflict: "The port {{ port }} is already in use."
851
+ LocalDevManagerV2:
852
+ failedToInitialize: "Missing required arguments to initialize Local Dev"
853
+ betaMessage: "HubSpot projects local development"
854
+ running: "Running {{#bold}}{{ projectName }}{{/bold}} locally on {{ accountIdentifier }}, waiting for changes ..."
855
+ quitHelper: "Press {{#bold}}'q'{{/bold}} to stop the local dev server"
856
+ viewInHubSpotLink: "View in HubSpot"
857
+ exitingStart: "Stopping local dev server ..."
858
+ exitingSucceed: "Successfully exited"
859
+ exitingFail: "Failed to cleanup before exiting"
860
+ devServer:
861
+ cleanupError: "Failed to cleanup local dev server: {{ message }}"
862
+ startError: "Failed to start local dev server: {{ message }}"
853
863
  LocalDevManager:
854
864
  failedToInitialize: "Missing required arguments to initialize Local Dev Manager"
855
865
  exitingStart: "Stopping local dev server ..."
@@ -859,7 +869,7 @@ en:
859
869
  cancelledFromUI: "The dev process has been cancelled from the UI. Any changes made since cancelling have not been uploaded. To resume dev mode, rerun {{#yellow}}`hs project dev`{{/yellow}}."
860
870
  header:
861
871
  betaMessage: "{{#yellow}}{{#bold}}[beta]{{/bold}}{{/yellow}} HubSpot projects local development"
862
- running: "Running {{ projectName}} locally on {{ accountIdentifier }}, waiting for changes ..."
872
+ running: "Running {{#bold}}{{ projectName }}{{/bold}} locally on {{ accountIdentifier }}, waiting for changes ..."
863
873
  quitHelper: "Press {{#bold}}'q'{{/bold}} to stop the local dev server"
864
874
  viewInHubSpotLink: "View in HubSpot"
865
875
  status:
@@ -916,6 +926,7 @@ en:
916
926
  feedbackHeader: "We'd love to hear your feedback!"
917
927
  feedbackMessage: "How are you liking the new projects and developer tools? \n > Run `{{#yellow}}hs feedback{{/yellow}}` to let us know what you think!\n"
918
928
  ui:
929
+ betaTag: "{{#bold}}[beta]{{/bold}}"
919
930
  betaWarning:
920
931
  header: "{{#yellow}}***************************** WARNING ****************************{{/yellow}}"
921
932
  footer: "{{#yellow}}******************************************************************{{/yellow}}"
@@ -19,13 +19,12 @@ class DevServerManager {
19
19
  this.devServers = {};
20
20
  }
21
21
 
22
- setServer(key, serverInterfacePath) {
22
+ safeLoadServer() {
23
23
  try {
24
- this.devServers[key] = require(serverInterfacePath);
24
+ const { DevModeInterface } = require('@hubspot/ui-extensions-dev-server');
25
+ this.devServers['uie'] = DevModeInterface;
25
26
  } catch (e) {
26
- logger.debug(
27
- `Failed to load dev server interface at ${serverInterfacePath}`
28
- );
27
+ logger.debug('Failed to load dev server interface: ', e);
29
28
  }
30
29
  }
31
30
 
@@ -43,23 +42,12 @@ class DevServerManager {
43
42
  return this.path ? `${this.path}/${path}` : null;
44
43
  }
45
44
 
46
- makeLogger(spinniesLogger, serverKey) {
47
- return {
48
- debug: (...args) => spinniesLogger(serverKey, '[DEBUG] ', ...args),
49
- error: (...args) => spinniesLogger(serverKey, '[ERROR] ', ...args),
50
- info: (...args) => spinniesLogger(serverKey, '[INFO] ', ...args),
51
- log: (...args) => spinniesLogger(serverKey, '[INFO] ', ...args),
52
- warn: (...args) => spinniesLogger(serverKey, '[WARN] ', ...args),
53
- };
54
- }
55
-
56
45
  async start({
57
46
  accountId,
58
47
  debug,
59
48
  extension,
60
49
  projectConfig,
61
50
  projectSourceDir,
62
- spinniesLogger,
63
51
  }) {
64
52
  const app = express();
65
53
 
@@ -93,12 +81,12 @@ class DevServerManager {
93
81
  const projectFiles = await walk(projectSourceDir);
94
82
 
95
83
  // Initialize component servers
96
- await this.iterateDevServers(async (serverInterface, serverKey) => {
84
+ await this.iterateDevServers(async serverInterface => {
97
85
  if (serverInterface.start) {
98
86
  await serverInterface.start({
87
+ accountId,
99
88
  debug,
100
89
  extension,
101
- logger: this.makeLogger(spinniesLogger, serverKey),
102
90
  projectConfig,
103
91
  projectFiles,
104
92
  });
@@ -119,6 +107,7 @@ class DevServerManager {
119
107
  await serverInterface.cleanup();
120
108
  }
121
109
  });
110
+
122
111
  if (this.server) {
123
112
  await this.server.close();
124
113
  }
@@ -54,8 +54,6 @@ class LocalDevManager {
54
54
  this.targetAccountId = options.targetAccountId;
55
55
  this.projectConfig = options.projectConfig;
56
56
  this.projectDir = options.projectDir;
57
- this.extension = options.extension;
58
- this.devServerPath = options.devServerPath;
59
57
  this.uploadPermission =
60
58
  options.uploadPermission || UPLOAD_PERMISSIONS.always;
61
59
  this.debug = options.debug || false;
@@ -102,12 +100,8 @@ class LocalDevManager {
102
100
 
103
101
  await this.devServerStart();
104
102
 
105
- if (!this.devServerPath) {
106
- this.uploadQueue.start();
107
- await this.startWatching();
108
- } else {
109
- this.uploadPermission = UPLOAD_PERMISSIONS.never;
110
- }
103
+ this.uploadQueue.start();
104
+ await this.startWatching();
111
105
 
112
106
  this.updateKeypressListeners();
113
107
 
@@ -485,10 +479,12 @@ class LocalDevManager {
485
479
  }),
486
480
  status: 'non-spinnable',
487
481
  });
482
+ const path =
483
+ event === WATCH_EVENTS.unlinkDir ? `${remotePath}/` : remotePath;
488
484
  await deleteFileFromBuild(
489
485
  this.targetAccountId,
490
486
  this.projectConfig.name,
491
- remotePath
487
+ path
492
488
  );
493
489
  SpinniesManager.update(spinnerName, {
494
490
  text: i18n(`${i18nKey}.upload.uploadedRemoveChange`, {
@@ -667,13 +663,9 @@ class LocalDevManager {
667
663
 
668
664
  async devServerStart() {
669
665
  try {
670
- if (this.devServerPath) {
671
- DevServerManager.setServer('uie', this.devServerPath);
672
- }
673
666
  await DevServerManager.start({
674
667
  accountId: this.targetAccountId,
675
668
  debug: this.debug,
676
- extension: this.extension,
677
669
  spinniesLogger: this.handleServerLog,
678
670
  projectConfig: this.projectConfig,
679
671
  projectSourceDir: this.projectSourceDir,
@@ -0,0 +1,129 @@
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const { i18n } = require('./lang');
4
+ const { logger } = require('@hubspot/cli-lib/logger');
5
+ const { handleKeypress } = require('@hubspot/cli-lib/lib/process');
6
+ const SpinniesManager = require('./SpinniesManager');
7
+ const DevServerManager = require('./DevServerManager');
8
+ const { EXIT_CODES } = require('./enums/exitCodes');
9
+ const { getProjectDetailUrl } = require('./projects');
10
+ const { uiAccountDescription, uiBetaMessage, uiLink, uiLine } = require('./ui');
11
+
12
+ const i18nKey = 'cli.lib.LocalDevManagerV2';
13
+
14
+ class LocalDevManagerV2 {
15
+ constructor(options) {
16
+ this.targetAccountId = options.targetAccountId;
17
+ this.projectConfig = options.projectConfig;
18
+ this.projectDir = options.projectDir;
19
+ this.extension = options.extension;
20
+ this.debug = options.debug || false;
21
+
22
+ this.projectSourceDir = path.join(
23
+ this.projectDir,
24
+ this.projectConfig.srcDir
25
+ );
26
+
27
+ if (!this.targetAccountId || !this.projectConfig || !this.projectDir) {
28
+ logger.log(i18n(`${i18nKey}.failedToInitialize`));
29
+ process.exit(EXIT_CODES.ERROR);
30
+ }
31
+ }
32
+
33
+ async start() {
34
+ console.clear();
35
+ SpinniesManager.removeAll();
36
+ SpinniesManager.init();
37
+
38
+ uiBetaMessage(i18n(`${i18nKey}.betaMessage`));
39
+ logger.log();
40
+ logger.log(
41
+ chalk.hex('#FF8F59')(
42
+ i18n(`${i18nKey}.running`, {
43
+ accountIdentifier: uiAccountDescription(this.targetAccountId),
44
+ projectName: this.projectConfig.name,
45
+ })
46
+ )
47
+ );
48
+ logger.log(
49
+ uiLink(
50
+ i18n(`${i18nKey}.viewInHubSpotLink`),
51
+ getProjectDetailUrl(this.projectConfig.name, this.targetAccountId)
52
+ )
53
+ );
54
+ logger.log();
55
+ logger.log(i18n(`${i18nKey}.quitHelper`));
56
+ uiLine();
57
+ logger.log();
58
+
59
+ await this.devServerStart();
60
+
61
+ this.updateKeypressListeners();
62
+ }
63
+
64
+ async stop() {
65
+ SpinniesManager.add('cleanupMessage', {
66
+ text: i18n(`${i18nKey}.exitingStart`),
67
+ });
68
+
69
+ const cleanupSucceeded = await this.devServerCleanup();
70
+
71
+ if (!cleanupSucceeded) {
72
+ SpinniesManager.fail('cleanupMessage', {
73
+ text: i18n(`${i18nKey}.exitingFail`),
74
+ });
75
+ process.exit(EXIT_CODES.ERROR);
76
+ }
77
+
78
+ SpinniesManager.succeed('cleanupMessage', {
79
+ text: i18n(`${i18nKey}.exitingSucceed`),
80
+ });
81
+ process.exit(EXIT_CODES.SUCCESS);
82
+ }
83
+
84
+ updateKeypressListeners() {
85
+ handleKeypress(async key => {
86
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') {
87
+ this.stop();
88
+ }
89
+ });
90
+ }
91
+
92
+ async devServerStart() {
93
+ try {
94
+ DevServerManager.safeLoadServer();
95
+ await DevServerManager.start({
96
+ accountId: this.targetAccountId,
97
+ debug: this.debug,
98
+ extension: this.extension,
99
+ projectConfig: this.projectConfig,
100
+ projectSourceDir: this.projectSourceDir,
101
+ });
102
+ } catch (e) {
103
+ if (this.debug) {
104
+ logger.error(e);
105
+ }
106
+ logger.error(
107
+ i18n(`${i18nKey}.devServer.startError`, { message: e.message })
108
+ );
109
+ process.exit(EXIT_CODES.ERROR);
110
+ }
111
+ }
112
+
113
+ async devServerCleanup() {
114
+ try {
115
+ await DevServerManager.cleanup();
116
+ return true;
117
+ } catch (e) {
118
+ if (this.debug) {
119
+ logger.error(e);
120
+ }
121
+ logger.error(
122
+ i18n(`${i18nKey}.devServer.cleanupError`, { message: e.message })
123
+ );
124
+ return false;
125
+ }
126
+ }
127
+ }
128
+
129
+ module.exports = LocalDevManagerV2;
@@ -53,6 +53,9 @@ class SpinniesManager {
53
53
  // Default Spinnies fields
54
54
  this.spinners = {};
55
55
  this.isCursorHidden = false;
56
+ if (this.currentInterval) {
57
+ clearInterval(this.currentInterval);
58
+ }
56
59
  this.currentInterval = null;
57
60
  this.stream = process.stderr;
58
61
  this.lineCount = 0;
package/lib/ui.js CHANGED
@@ -105,6 +105,12 @@ const uiFeatureHighlight = (commands, title) => {
105
105
  });
106
106
  };
107
107
 
108
+ const uiBetaMessage = message => {
109
+ const i18nKey = 'cli.lib.ui';
110
+
111
+ logger.log(chalk.hex('#bda9ea')(i18n(`${i18nKey}.betaTag`)), message);
112
+ };
113
+
108
114
  const uiBetaWarning = logMessage => {
109
115
  const i18nKey = 'cli.lib.ui.betaWarning';
110
116
 
@@ -115,6 +121,7 @@ const uiBetaWarning = logMessage => {
115
121
 
116
122
  module.exports = {
117
123
  uiAccountDescription,
124
+ uiBetaMessage,
118
125
  uiBetaWarning,
119
126
  uiFeatureHighlight,
120
127
  uiInfoSection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "4.2.1-beta.0",
3
+ "version": "4.2.1-beta.1",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,6 +10,7 @@
10
10
  "dependencies": {
11
11
  "@hubspot/cli-lib": "^4.1.14",
12
12
  "@hubspot/serverless-dev-runtime": "4.1.8-beta.6",
13
+ "@hubspot/ui-extensions-dev-server": "^0.4.0",
13
14
  "archiver": "^5.3.0",
14
15
  "body-parser": "^1.19.0",
15
16
  "chalk": "^4.1.2",
@@ -35,7 +36,7 @@
35
36
  "mock-stdin": "^1.0.0"
36
37
  },
37
38
  "engines": {
38
- "node": ">=10"
39
+ "node": ">=16"
39
40
  },
40
41
  "bin": {
41
42
  "hs": "./bin/hs",
@@ -44,5 +45,5 @@
44
45
  "publishConfig": {
45
46
  "access": "public"
46
47
  },
47
- "gitHead": "2e2976ffd17135fbc4dcdb8a8b9d078613ed5d53"
48
+ "gitHead": "a79d4a4d6c61971986a7df3aaa8374cbe2405a0b"
48
49
  }