@hubspot/cli 7.2.0-experimental.0 → 7.2.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.
Files changed (48) hide show
  1. package/commands/account/info.d.ts +1 -1
  2. package/commands/account/info.js +8 -5
  3. package/commands/auth.d.ts +9 -0
  4. package/commands/auth.js +89 -83
  5. package/commands/init.d.ts +11 -0
  6. package/commands/init.js +114 -100
  7. package/commands/list.d.ts +9 -0
  8. package/commands/list.js +63 -57
  9. package/commands/mv.d.ts +10 -0
  10. package/commands/mv.js +32 -32
  11. package/commands/open.d.ts +10 -0
  12. package/commands/open.js +37 -34
  13. package/commands/project/dev/deprecatedFlow.d.ts +5 -0
  14. package/commands/project/{dev.js → dev/deprecatedFlow.js} +14 -44
  15. package/commands/project/{dev.d.ts → dev/index.d.ts} +1 -3
  16. package/commands/project/dev/index.js +52 -0
  17. package/commands/project/dev/unifiedFlow.d.ts +5 -0
  18. package/commands/project/dev/unifiedFlow.js +112 -0
  19. package/commands/remove.d.ts +9 -0
  20. package/commands/remove.js +25 -23
  21. package/lang/en.lyaml +10 -6
  22. package/lib/DevServerManagerV2.d.ts +34 -0
  23. package/lib/DevServerManagerV2.js +85 -0
  24. package/lib/LocalDevManagerV2.d.ts +64 -0
  25. package/lib/LocalDevManagerV2.js +382 -0
  26. package/lib/commonOpts.d.ts +1 -0
  27. package/lib/commonOpts.js +30 -0
  28. package/lib/constants.d.ts +12 -0
  29. package/lib/constants.js +13 -1
  30. package/lib/localDev.d.ts +1 -1
  31. package/lib/localDev.js +2 -2
  32. package/lib/projects/structure.d.ts +4 -0
  33. package/lib/projects/structure.js +9 -0
  34. package/lib/prompts/accountNamePrompt.d.ts +2 -3
  35. package/lib/prompts/personalAccessKeyPrompt.d.ts +4 -4
  36. package/lib/ui/index.d.ts +1 -1
  37. package/lib/ui/index.js +2 -2
  38. package/lib/ui/supportHyperlinks.js +2 -2
  39. package/lib/ui/supportsColor.js +2 -2
  40. package/lib/usageTracking.d.ts +1 -1
  41. package/lib/yargsUtils.d.ts +9 -0
  42. package/lib/yargsUtils.js +40 -0
  43. package/package.json +3 -3
  44. package/types/ProjectComponents.d.ts +38 -0
  45. package/types/ProjectComponents.js +3 -0
  46. package/types/Yargs.d.ts +1 -0
  47. package/lib/hasFlag.d.ts +0 -1
  48. package/lib/hasFlag.js +0 -15
@@ -0,0 +1,382 @@
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
+ const path_1 = __importDefault(require("path"));
7
+ const chokidar_1 = __importDefault(require("chokidar"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const logger_1 = require("@hubspot/local-dev-lib/logger");
10
+ const localDevAuth_1 = require("@hubspot/local-dev-lib/api/localDevAuth");
11
+ const appsDev_1 = require("@hubspot/local-dev-lib/api/appsDev");
12
+ const config_1 = require("@hubspot/local-dev-lib/config");
13
+ const constants_1 = require("./constants");
14
+ const SpinniesManager_1 = __importDefault(require("./ui/SpinniesManager"));
15
+ const DevServerManagerV2_1 = __importDefault(require("./DevServerManagerV2"));
16
+ const exitCodes_1 = require("./enums/exitCodes");
17
+ const urls_1 = require("./projects/urls");
18
+ const structure_1 = require("./projects/structure");
19
+ const ui_1 = require("./ui");
20
+ const index_1 = require("./errorHandlers/index");
21
+ const installPublicAppPrompt_1 = require("./prompts/installPublicAppPrompt");
22
+ const promptUtils_1 = require("./prompts/promptUtils");
23
+ const lang_1 = require("./lang");
24
+ const process_1 = require("./process");
25
+ const WATCH_EVENTS = {
26
+ add: 'add',
27
+ change: 'change',
28
+ unlink: 'unlink',
29
+ unlinkDir: 'unlinkDir',
30
+ };
31
+ const i18nKey = 'lib.LocalDevManager';
32
+ class LocalDevManagerV2 {
33
+ targetProjectAccountId;
34
+ targetTestingAccountId;
35
+ projectConfig;
36
+ projectDir;
37
+ projectId;
38
+ debug;
39
+ deployedBuild;
40
+ isGithubLinked;
41
+ watcher;
42
+ uploadWarnings;
43
+ projectNodes;
44
+ activeApp;
45
+ activePublicAppData;
46
+ env;
47
+ publicAppActiveInstalls;
48
+ projectSourceDir;
49
+ mostRecentUploadWarning;
50
+ constructor(options) {
51
+ this.targetProjectAccountId = options.targetProjectAccountId;
52
+ this.targetTestingAccountId = options.targetTestingAccountId;
53
+ this.projectConfig = options.projectConfig;
54
+ this.projectDir = options.projectDir;
55
+ this.projectId = options.projectId;
56
+ this.debug = options.debug || false;
57
+ this.deployedBuild = options.deployedBuild;
58
+ this.isGithubLinked = options.isGithubLinked;
59
+ this.watcher = null;
60
+ this.uploadWarnings = {};
61
+ this.projectNodes = options.projectNodes;
62
+ this.activeApp = null;
63
+ this.activePublicAppData = null;
64
+ this.env = options.env;
65
+ this.publicAppActiveInstalls = null;
66
+ this.mostRecentUploadWarning = null;
67
+ this.projectSourceDir = path_1.default.join(this.projectDir, this.projectConfig.srcDir);
68
+ if (!this.targetProjectAccountId ||
69
+ !this.projectConfig ||
70
+ !this.projectDir) {
71
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.failedToInitialize`));
72
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
73
+ }
74
+ }
75
+ async setActiveApp(appUid) {
76
+ if (!appUid) {
77
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.missingUid`, {
78
+ devCommand: (0, ui_1.uiCommandReference)('hs project dev'),
79
+ }));
80
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
81
+ }
82
+ const app = Object.values(this.projectNodes).find(component => component.uid === appUid) || null;
83
+ if (app && (0, structure_1.isAppIRNode)(app)) {
84
+ this.activeApp = app;
85
+ if (app.config.distribution === constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE) {
86
+ try {
87
+ await this.setActivePublicAppData();
88
+ await this.checkActivePublicAppInstalls();
89
+ await this.checkPublicAppInstallation();
90
+ }
91
+ catch (e) {
92
+ (0, index_1.logError)(e);
93
+ }
94
+ }
95
+ }
96
+ return;
97
+ }
98
+ async setActivePublicAppData() {
99
+ const { data: { results: portalPublicApps }, } = await (0, appsDev_1.fetchPublicAppsForPortal)(this.targetProjectAccountId);
100
+ const activePublicAppData = portalPublicApps.find(({ sourceId }) => sourceId === this.activeApp?.uid);
101
+ if (!activePublicAppData) {
102
+ return;
103
+ }
104
+ const { data: { uniquePortalInstallCount }, } = await (0, appsDev_1.fetchPublicAppProductionInstallCounts)(activePublicAppData.id, this.targetProjectAccountId);
105
+ this.activePublicAppData = activePublicAppData;
106
+ this.publicAppActiveInstalls = uniquePortalInstallCount;
107
+ }
108
+ async checkActivePublicAppInstalls() {
109
+ if (!this.activePublicAppData ||
110
+ !this.publicAppActiveInstalls ||
111
+ this.publicAppActiveInstalls < 1) {
112
+ return;
113
+ }
114
+ (0, ui_1.uiLine)();
115
+ logger_1.logger.warn((0, lang_1.i18n)(`${i18nKey}.activeInstallWarning.installCount`, {
116
+ appName: this.activePublicAppData.name,
117
+ installCount: this.publicAppActiveInstalls,
118
+ installText: this.publicAppActiveInstalls === 1 ? 'install' : 'installs',
119
+ }));
120
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.activeInstallWarning.explanation`));
121
+ (0, ui_1.uiLine)();
122
+ const proceed = await (0, promptUtils_1.confirmPrompt)((0, lang_1.i18n)(`${i18nKey}.activeInstallWarning.confirmationPrompt`), { defaultAnswer: false });
123
+ if (!proceed) {
124
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
125
+ }
126
+ }
127
+ async start() {
128
+ SpinniesManager_1.default.stopAll();
129
+ SpinniesManager_1.default.init();
130
+ // Local dev currently relies on the existence of a deployed build in the target account
131
+ if (!this.deployedBuild) {
132
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.noDeployedBuild`, {
133
+ projectName: this.projectConfig.name,
134
+ accountIdentifier: (0, ui_1.uiAccountDescription)(this.targetProjectAccountId),
135
+ uploadCommand: this.getUploadCommand(),
136
+ }));
137
+ logger_1.logger.log();
138
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
139
+ }
140
+ const setupSucceeded = await this.devServerSetup();
141
+ if (!setupSucceeded) {
142
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
143
+ }
144
+ else if (!this.debug) {
145
+ console.clear();
146
+ }
147
+ (0, ui_1.uiBetaTag)((0, lang_1.i18n)(`${i18nKey}.betaMessage`));
148
+ logger_1.logger.log((0, ui_1.uiLink)((0, lang_1.i18n)(`${i18nKey}.learnMoreLocalDevServer`), 'https://developers.hubspot.com/docs/platform/project-cli-commands#start-a-local-development-server'));
149
+ logger_1.logger.log();
150
+ logger_1.logger.log(chalk_1.default.hex(ui_1.UI_COLORS.SORBET)((0, lang_1.i18n)(`${i18nKey}.running`, {
151
+ accountIdentifier: (0, ui_1.uiAccountDescription)(this.targetProjectAccountId),
152
+ projectName: this.projectConfig.name,
153
+ })));
154
+ logger_1.logger.log((0, ui_1.uiLink)((0, lang_1.i18n)(`${i18nKey}.viewProjectLink`), (0, urls_1.getProjectDetailUrl)(this.projectConfig.name, this.targetProjectAccountId) || ''));
155
+ logger_1.logger.log();
156
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.quitHelper`));
157
+ (0, ui_1.uiLine)();
158
+ logger_1.logger.log();
159
+ await this.devServerStart();
160
+ // Initialize project file watcher to detect configuration file changes
161
+ this.startWatching();
162
+ this.updateKeypressListeners();
163
+ this.monitorConsoleOutput();
164
+ // Verify that there are no mismatches between components in the local project
165
+ // and components in the deployed build of the project.
166
+ this.compareLocalProjectToDeployed();
167
+ }
168
+ async stop(showProgress = true) {
169
+ if (showProgress) {
170
+ SpinniesManager_1.default.add('cleanupMessage', {
171
+ text: (0, lang_1.i18n)(`${i18nKey}.exitingStart`),
172
+ });
173
+ }
174
+ await this.stopWatching();
175
+ const cleanupSucceeded = await this.devServerCleanup();
176
+ if (!cleanupSucceeded) {
177
+ if (showProgress) {
178
+ SpinniesManager_1.default.fail('cleanupMessage', {
179
+ text: (0, lang_1.i18n)(`${i18nKey}.exitingFail`),
180
+ });
181
+ }
182
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
183
+ }
184
+ if (showProgress) {
185
+ SpinniesManager_1.default.succeed('cleanupMessage', {
186
+ text: (0, lang_1.i18n)(`${i18nKey}.exitingSucceed`),
187
+ });
188
+ }
189
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
190
+ }
191
+ async checkPublicAppInstallation() {
192
+ if (!this.activeApp || !this.activePublicAppData) {
193
+ return;
194
+ }
195
+ const { data: { isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups }, } = await (0, localDevAuth_1.fetchAppInstallationData)(this.targetTestingAccountId, this.projectId, this.activeApp.uid, this.activeApp.config.auth.requiredScopes, this.activeApp.config.auth.optionalScopes);
196
+ const isReinstall = previouslyAuthorizedScopeGroups.length > 0;
197
+ if (!isInstalledWithScopeGroups) {
198
+ await (0, installPublicAppPrompt_1.installPublicAppPrompt)(this.env, this.targetTestingAccountId, this.activePublicAppData.clientId, this.activeApp.config.auth.requiredScopes, this.activeApp.config.auth.redirectUrls, isReinstall);
199
+ }
200
+ }
201
+ updateKeypressListeners() {
202
+ (0, process_1.handleKeypress)(async (key) => {
203
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') {
204
+ this.stop();
205
+ }
206
+ });
207
+ }
208
+ getUploadCommand() {
209
+ const currentDefaultAccount = (0, config_1.getConfigDefaultAccount)() || undefined;
210
+ return this.targetProjectAccountId !== (0, config_1.getAccountId)(currentDefaultAccount)
211
+ ? (0, ui_1.uiCommandReference)(`hs project upload --account=${this.targetProjectAccountId}`)
212
+ : (0, ui_1.uiCommandReference)('hs project upload');
213
+ }
214
+ logUploadWarning(reason) {
215
+ let warning;
216
+ if (reason) {
217
+ warning = reason;
218
+ }
219
+ else {
220
+ warning =
221
+ this.publicAppActiveInstalls && this.publicAppActiveInstalls > 0
222
+ ? (0, lang_1.i18n)(`${i18nKey}.uploadWarning.defaultPublicAppWarning`, {
223
+ installCount: this.publicAppActiveInstalls,
224
+ installText: this.publicAppActiveInstalls === 1 ? 'install' : 'installs',
225
+ })
226
+ : (0, lang_1.i18n)(`${i18nKey}.uploadWarning.defaultWarning`);
227
+ }
228
+ // Avoid logging the warning to the console if it is currently the most
229
+ // recently logged warning. We do not want to spam the console with the same message.
230
+ if (!this.uploadWarnings[warning]) {
231
+ logger_1.logger.log();
232
+ logger_1.logger.warn((0, lang_1.i18n)(`${i18nKey}.uploadWarning.header`, { warning }));
233
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadWarning.stopDev`, {
234
+ command: (0, ui_1.uiCommandReference)('hs project dev'),
235
+ }));
236
+ if (this.isGithubLinked) {
237
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadWarning.pushToGithub`));
238
+ }
239
+ else {
240
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadWarning.runUpload`, {
241
+ command: this.getUploadCommand(),
242
+ }));
243
+ }
244
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadWarning.restartDev`, {
245
+ command: (0, ui_1.uiCommandReference)('hs project dev'),
246
+ }));
247
+ this.mostRecentUploadWarning = warning;
248
+ this.uploadWarnings[warning] = true;
249
+ }
250
+ }
251
+ monitorConsoleOutput() {
252
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
253
+ function customStdoutWrite(chunk, encoding, callback) {
254
+ // Reset the most recently logged warning
255
+ if (this.mostRecentUploadWarning &&
256
+ this.uploadWarnings[this.mostRecentUploadWarning]) {
257
+ delete this.uploadWarnings[this.mostRecentUploadWarning];
258
+ }
259
+ if (typeof encoding === 'function') {
260
+ return originalStdoutWrite(chunk, callback);
261
+ }
262
+ return originalStdoutWrite(chunk, encoding, callback);
263
+ }
264
+ customStdoutWrite.bind(this);
265
+ process.stdout.write = customStdoutWrite;
266
+ }
267
+ compareLocalProjectToDeployed() {
268
+ const deployedComponentNames = this.deployedBuild.subbuildStatuses.map(subbuildStatus => subbuildStatus.buildName);
269
+ const missingProjectNodes = [];
270
+ Object.values(this.projectNodes).forEach(node => {
271
+ if ((0, structure_1.isAppIRNode)(node) || (0, structure_1.isCardIRNode)(node)) {
272
+ if (!deployedComponentNames.includes(node.uid)) {
273
+ missingProjectNodes.push(`${(0, lang_1.i18n)(`${i18nKey}.uploadWarning.appLabel`)} ${node.uid}`);
274
+ }
275
+ }
276
+ });
277
+ if (missingProjectNodes.length) {
278
+ this.logUploadWarning((0, lang_1.i18n)(`${i18nKey}.uploadWarning.missingComponents`, {
279
+ missingComponents: missingProjectNodes.join(', '),
280
+ }));
281
+ }
282
+ }
283
+ startWatching() {
284
+ this.watcher = chokidar_1.default.watch(this.projectDir, {
285
+ ignoreInitial: true,
286
+ });
287
+ const configPaths = Object.values(this.projectNodes)
288
+ .filter(structure_1.isAppIRNode)
289
+ .map(component => component.localDev.componentConfigPath);
290
+ const projectConfigPath = path_1.default.join(this.projectDir, constants_1.PROJECT_CONFIG_FILE);
291
+ configPaths.push(projectConfigPath);
292
+ this.watcher.on('add', filePath => {
293
+ this.handleWatchEvent(filePath, WATCH_EVENTS.add, configPaths);
294
+ });
295
+ this.watcher.on('change', filePath => {
296
+ this.handleWatchEvent(filePath, WATCH_EVENTS.change, configPaths);
297
+ });
298
+ this.watcher.on('unlink', filePath => {
299
+ this.handleWatchEvent(filePath, WATCH_EVENTS.unlink, configPaths);
300
+ });
301
+ this.watcher.on('unlinkDir', filePath => {
302
+ this.handleWatchEvent(filePath, WATCH_EVENTS.unlinkDir, configPaths);
303
+ });
304
+ }
305
+ async stopWatching() {
306
+ await this.watcher?.close();
307
+ }
308
+ handleWatchEvent(filePath, event, configPaths) {
309
+ if (configPaths.includes(filePath)) {
310
+ this.logUploadWarning();
311
+ }
312
+ else {
313
+ this.devServerFileChange(filePath, event);
314
+ }
315
+ }
316
+ async devServerSetup() {
317
+ try {
318
+ await DevServerManagerV2_1.default.setup({
319
+ projectNodes: this.projectNodes,
320
+ onUploadRequired: this.logUploadWarning.bind(this),
321
+ accountId: this.targetTestingAccountId,
322
+ setActiveApp: this.setActiveApp.bind(this),
323
+ });
324
+ return true;
325
+ }
326
+ catch (e) {
327
+ if (this.debug) {
328
+ logger_1.logger.error(e);
329
+ }
330
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.devServer.setupError`, {
331
+ message: e instanceof Error ? e.message : '',
332
+ }));
333
+ return false;
334
+ }
335
+ }
336
+ async devServerStart() {
337
+ try {
338
+ await DevServerManagerV2_1.default.start({
339
+ accountId: this.targetTestingAccountId,
340
+ projectConfig: this.projectConfig,
341
+ });
342
+ }
343
+ catch (e) {
344
+ if (this.debug) {
345
+ logger_1.logger.error(e);
346
+ }
347
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.devServer.startError`, {
348
+ message: e instanceof Error ? e.message : '',
349
+ }));
350
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
351
+ }
352
+ }
353
+ devServerFileChange(filePath, event) {
354
+ try {
355
+ DevServerManagerV2_1.default.fileChange({ filePath, event });
356
+ }
357
+ catch (e) {
358
+ if (this.debug) {
359
+ logger_1.logger.error(e);
360
+ }
361
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.devServer.fileChangeError`, {
362
+ message: e instanceof Error ? e.message : '',
363
+ }));
364
+ }
365
+ }
366
+ async devServerCleanup() {
367
+ try {
368
+ await DevServerManagerV2_1.default.cleanup();
369
+ return true;
370
+ }
371
+ catch (e) {
372
+ if (this.debug) {
373
+ logger_1.logger.error(e);
374
+ }
375
+ logger_1.logger.error((0, lang_1.i18n)(`${i18nKey}.devServer.cleanupError`, {
376
+ message: e instanceof Error ? e.message : '',
377
+ }));
378
+ return false;
379
+ }
380
+ }
381
+ }
382
+ exports.default = LocalDevManagerV2;
@@ -13,6 +13,7 @@ export declare function addCmsPublishModeOptions(yargs: Argv, { read, write }: {
13
13
  }): Argv;
14
14
  export declare function addTestingOptions(yargs: Argv): Argv;
15
15
  export declare function addUseEnvironmentOptions(yargs: Argv): Argv;
16
+ export declare function addCustomHelpOutput(yargs: Argv, command: string, describe: string): Promise<void>;
16
17
  export declare function setLogLevel(options: Arguments<{
17
18
  debug?: boolean;
18
19
  }>): void;
package/lib/commonOpts.js CHANGED
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.addGlobalOptions = addGlobalOptions;
4
7
  exports.addAccountOptions = addAccountOptions;
@@ -7,13 +10,19 @@ exports.addOverwriteOptions = addOverwriteOptions;
7
10
  exports.addCmsPublishModeOptions = addCmsPublishModeOptions;
8
11
  exports.addTestingOptions = addTestingOptions;
9
12
  exports.addUseEnvironmentOptions = addUseEnvironmentOptions;
13
+ exports.addCustomHelpOutput = addCustomHelpOutput;
10
14
  exports.setLogLevel = setLogLevel;
11
15
  exports.getCommandName = getCommandName;
12
16
  exports.getCmsPublishMode = getCmsPublishMode;
17
+ const chalk_1 = __importDefault(require("chalk"));
18
+ const yargs_parser_1 = __importDefault(require("yargs-parser"));
13
19
  const logger_1 = require("@hubspot/local-dev-lib/logger");
14
20
  const files_1 = require("@hubspot/local-dev-lib/constants/files");
15
21
  const config_1 = require("@hubspot/local-dev-lib/config");
16
22
  const lang_1 = require("./lang");
23
+ const errorHandlers_1 = require("./errorHandlers");
24
+ const exitCodes_1 = require("./enums/exitCodes");
25
+ const ui_1 = require("./ui");
17
26
  const i18nKey = 'lib.commonOpts';
18
27
  function addGlobalOptions(yargs) {
19
28
  yargs.version(false);
@@ -70,6 +79,27 @@ function addUseEnvironmentOptions(yargs) {
70
79
  })
71
80
  .conflicts('use-env', 'account');
72
81
  }
82
+ async function addCustomHelpOutput(yargs, command, describe) {
83
+ try {
84
+ const parsedArgv = (0, yargs_parser_1.default)(process.argv.slice(2));
85
+ if (parsedArgv && parsedArgv.help) {
86
+ // Construct the full command, including positional arguments
87
+ const commandBase = `hs ${parsedArgv._.slice(0, -1).join(' ')}`;
88
+ const fullCommand = `${commandBase.trim()} ${command}`;
89
+ // Format the original help output to be more readable
90
+ let commandHelp = await yargs.getHelp();
91
+ ['Options:', 'Examples:', 'Positionals:'].forEach(header => {
92
+ commandHelp = commandHelp.replace(header, chalk_1.default.bold(header));
93
+ });
94
+ logger_1.logger.log(`${(0, ui_1.uiCommandReference)(fullCommand, false)}\n\n${describe}\n\n${commandHelp}`);
95
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
96
+ }
97
+ }
98
+ catch (e) {
99
+ // Ignore error to allow yargs to show the default help output using the command description
100
+ (0, errorHandlers_1.debugError)(e);
101
+ }
102
+ }
73
103
  function setLogLevel(options) {
74
104
  const { debug } = options;
75
105
  if (debug) {
@@ -62,3 +62,15 @@ export declare const PLATFORM_VERSION_ERROR_TYPES: {
62
62
  readonly PLATFORM_VERSION_RETIRED: "PlatformVersionErrorType.PLATFORM_VERSION_RETIRED";
63
63
  readonly PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST: "PlatformVersionErrorType.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST";
64
64
  };
65
+ export declare const IR_COMPONENT_TYPES: {
66
+ readonly APPLICATION: "APPLICATION";
67
+ readonly CARD: "CARD";
68
+ };
69
+ export declare const APP_DISTRIBUTION_TYPES: {
70
+ readonly MARKETPLACE: "marketplace";
71
+ readonly PRIVATE: "private";
72
+ };
73
+ export declare const APP_AUTH_TYPES: {
74
+ readonly OAUTH: "oauth";
75
+ readonly STATIC: "static";
76
+ };
package/lib/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PLATFORM_VERSION_ERROR_TYPES = exports.PROJECT_COMPONENT_TYPES = exports.PROJECT_TASK_TYPES = exports.PROJECT_ERROR_TYPES = exports.PROJECT_DEPLOY_TEXT = exports.PROJECT_BUILD_TEXT = exports.PROJECT_DEPLOY_STATES = exports.PROJECT_BUILD_STATES = exports.PROJECT_CONFIG_FILE = exports.DEFAULT_POLLING_DELAY = exports.MARKETPLACE_FOLDER = exports.HUBSPOT_FOLDER = exports.FEEDBACK_INTERVAL = exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = void 0;
3
+ exports.APP_AUTH_TYPES = exports.APP_DISTRIBUTION_TYPES = exports.IR_COMPONENT_TYPES = exports.PLATFORM_VERSION_ERROR_TYPES = exports.PROJECT_COMPONENT_TYPES = exports.PROJECT_TASK_TYPES = exports.PROJECT_ERROR_TYPES = exports.PROJECT_DEPLOY_TEXT = exports.PROJECT_BUILD_TEXT = exports.PROJECT_DEPLOY_STATES = exports.PROJECT_BUILD_STATES = exports.PROJECT_CONFIG_FILE = exports.DEFAULT_POLLING_DELAY = exports.MARKETPLACE_FOLDER = exports.HUBSPOT_FOLDER = exports.FEEDBACK_INTERVAL = exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = void 0;
4
4
  exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = 'HubSpot/hubspot-project-components';
5
5
  exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = 'main';
6
6
  exports.FEEDBACK_INTERVAL = 10;
@@ -57,3 +57,15 @@ exports.PLATFORM_VERSION_ERROR_TYPES = {
57
57
  PLATFORM_VERSION_RETIRED: 'PlatformVersionErrorType.PLATFORM_VERSION_RETIRED',
58
58
  PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST: 'PlatformVersionErrorType.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST',
59
59
  };
60
+ exports.IR_COMPONENT_TYPES = {
61
+ APPLICATION: 'APPLICATION',
62
+ CARD: 'CARD',
63
+ };
64
+ exports.APP_DISTRIBUTION_TYPES = {
65
+ MARKETPLACE: 'marketplace',
66
+ PRIVATE: 'private',
67
+ };
68
+ exports.APP_AUTH_TYPES = {
69
+ OAUTH: 'oauth',
70
+ STATIC: 'static',
71
+ };
package/lib/localDev.d.ts CHANGED
@@ -14,5 +14,5 @@ export declare function createSandboxForLocalDev(accountId: number, accountConfi
14
14
  export declare function createDeveloperTestAccountForLocalDev(accountId: number, accountConfig: CLIAccount, env: Environment): Promise<number>;
15
15
  export declare function useExistingDevTestAccount(env: Environment, account: DeveloperTestAccount): Promise<void>;
16
16
  export declare function createNewProjectForLocalDev(projectConfig: ProjectConfig, targetAccountId: number, shouldCreateWithoutConfirmation: boolean, hasPublicApps: boolean): Promise<Project>;
17
- export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number): Promise<Build>;
17
+ export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number, sendIr?: boolean): Promise<Build>;
18
18
  export declare function getAccountHomeUrl(accountId: number): string;
package/lib/localDev.js CHANGED
@@ -294,8 +294,8 @@ function projectUploadCallback(accountId, projectConfig, tempFile, buildId) {
294
294
  }
295
295
  // Create an initial build if the project was newly created in the account
296
296
  // Return the newly deployed build
297
- async function createInitialBuildForNewProject(projectConfig, projectDir, targetAccountId) {
298
- const { result: initialUploadResult, uploadError } = await (0, upload_1.handleProjectUpload)(targetAccountId, projectConfig, projectDir, projectUploadCallback, (0, lang_1.i18n)(`${i18nKey}.createInitialBuildForNewProject.initialUploadMessage`));
297
+ async function createInitialBuildForNewProject(projectConfig, projectDir, targetAccountId, sendIr) {
298
+ const { result: initialUploadResult, uploadError } = await (0, upload_1.handleProjectUpload)(targetAccountId, projectConfig, projectDir, projectUploadCallback, (0, lang_1.i18n)(`${i18nKey}.createInitialBuildForNewProject.initialUploadMessage`), sendIr);
299
299
  if (uploadError) {
300
300
  if ((0, index_1.isSpecifiedError)(uploadError, {
301
301
  subCategory: constants_1.PROJECT_ERROR_TYPES.PROJECT_LOCKED,
@@ -1,4 +1,6 @@
1
1
  import { ComponentTypes, Component, GenericComponentConfig, PublicAppComponentConfig, PrivateAppComponentConfig, AppCardComponentConfig } from '../../types/Projects';
2
+ import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types';
3
+ import { AppIRNode, CardIRNode } from '../../types/ProjectComponents';
2
4
  export declare const CONFIG_FILES: {
3
5
  [k in ComponentTypes]: string;
4
6
  };
@@ -13,3 +15,5 @@ export declare function getProjectComponentTypes(components: Array<Component>):
13
15
  export declare function getComponentUid(component?: Component | null): string | null;
14
16
  export declare function componentIsApp(component?: Component | null): component is Component<PublicAppComponentConfig | PrivateAppComponentConfig>;
15
17
  export declare function componentIsPublicApp(component?: Component | null): component is Component<PublicAppComponentConfig>;
18
+ export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev): component is AppIRNode;
19
+ export declare function isCardIRNode(component: IntermediateRepresentationNodeLocalDev): component is CardIRNode;
@@ -43,12 +43,15 @@ exports.getProjectComponentTypes = getProjectComponentTypes;
43
43
  exports.getComponentUid = getComponentUid;
44
44
  exports.componentIsApp = componentIsApp;
45
45
  exports.componentIsPublicApp = componentIsPublicApp;
46
+ exports.isAppIRNode = isAppIRNode;
47
+ exports.isCardIRNode = isCardIRNode;
46
48
  const fs = __importStar(require("fs"));
47
49
  const path = __importStar(require("path"));
48
50
  const fs_1 = require("@hubspot/local-dev-lib/fs");
49
51
  const logger_1 = require("@hubspot/local-dev-lib/logger");
50
52
  const index_1 = require("../errorHandlers/index");
51
53
  const Projects_1 = require("../../types/Projects");
54
+ const constants_1 = require("../constants");
52
55
  exports.CONFIG_FILES = {
53
56
  [Projects_1.ComponentTypes.PrivateApp]: 'app.json',
54
57
  [Projects_1.ComponentTypes.PublicApp]: 'public-app.json',
@@ -169,3 +172,9 @@ function componentIsApp(component) {
169
172
  function componentIsPublicApp(component) {
170
173
  return component?.type === Projects_1.ComponentTypes.PublicApp;
171
174
  }
175
+ function isAppIRNode(component) {
176
+ return component.componentType === constants_1.IR_COMPONENT_TYPES.APPLICATION;
177
+ }
178
+ function isCardIRNode(component) {
179
+ return component.componentType === constants_1.IR_COMPONENT_TYPES.CARD;
180
+ }
@@ -1,12 +1,11 @@
1
1
  import { PromptConfig } from '../../types/Prompts';
2
2
  import { AccountType } from '@hubspot/local-dev-lib/types/Accounts';
3
- type AccountNamePromptResponse = {
3
+ export type AccountNamePromptResponse = {
4
4
  name: string;
5
5
  };
6
6
  export declare function getCliAccountNamePromptConfig(defaultName?: string): PromptConfig<AccountNamePromptResponse>;
7
- export declare function cliAccountNamePrompt(defaultName: string): Promise<AccountNamePromptResponse>;
7
+ export declare function cliAccountNamePrompt(defaultName?: string): Promise<AccountNamePromptResponse>;
8
8
  export declare function hubspotAccountNamePrompt({ accountType, currentPortalCount, }: {
9
9
  accountType: AccountType;
10
10
  currentPortalCount?: number;
11
11
  }): Promise<AccountNamePromptResponse>;
12
- export {};
@@ -1,5 +1,6 @@
1
+ import { AccountNamePromptResponse } from './accountNamePrompt';
1
2
  import { PromptConfig } from '../../types/Prompts';
2
- type PersonalAccessKeyPromptResponse = {
3
+ export type PersonalAccessKeyPromptResponse = {
3
4
  personalAccessKey: string;
4
5
  env: string;
5
6
  };
@@ -15,6 +16,7 @@ type ClientSecretPromptResponse = {
15
16
  type ScopesPromptResponse = {
16
17
  scopes: string[];
17
18
  };
19
+ export type OauthPromptResponse = AccountNamePromptResponse & AccountIdPromptResponse & ClientIdPromptResponse & ClientSecretPromptResponse & ScopesPromptResponse;
18
20
  /**
19
21
  * Displays notification to user that we are about to open the browser,
20
22
  * then opens their browser to the personal-access-key shortlink
@@ -23,7 +25,5 @@ export declare function personalAccessKeyPrompt({ env, account, }: {
23
25
  env: string;
24
26
  account?: number;
25
27
  }): Promise<PersonalAccessKeyPromptResponse>;
26
- export declare const OAUTH_FLOW: (PromptConfig<{
27
- name: string;
28
- }> | PromptConfig<AccountIdPromptResponse> | PromptConfig<ClientIdPromptResponse> | PromptConfig<ClientSecretPromptResponse> | PromptConfig<ScopesPromptResponse>)[];
28
+ export declare const OAUTH_FLOW: (PromptConfig<AccountNamePromptResponse> | PromptConfig<AccountIdPromptResponse> | PromptConfig<ClientIdPromptResponse> | PromptConfig<ClientSecretPromptResponse> | PromptConfig<ScopesPromptResponse>)[];
29
29
  export {};
package/lib/ui/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export declare function uiLine(): void;
7
7
  export declare function uiLink(linkText: string, url: string): string;
8
8
  export declare function uiAccountDescription(accountId?: number | null, bold?: boolean): string;
9
9
  export declare function uiInfoSection(title: string, logContent: () => void): void;
10
- export declare function uiCommandReference(command: string): string;
10
+ export declare function uiCommandReference(command: string, withQuotes?: boolean): string;
11
11
  export declare function uiFeatureHighlight(commands: string[], title?: string): void;
12
12
  export declare function uiBetaTag(message: string, log?: boolean): string | undefined;
13
13
  export declare function uiDeprecatedTag(message: string): void;
package/lib/ui/index.js CHANGED
@@ -70,9 +70,9 @@ function uiInfoSection(title, logContent) {
70
70
  logger_1.logger.log('');
71
71
  uiLine();
72
72
  }
73
- function uiCommandReference(command) {
73
+ function uiCommandReference(command, withQuotes = true) {
74
74
  const terminalUISupport = getTerminalUISupport();
75
- const commandReference = `\`${command}\``;
75
+ const commandReference = withQuotes ? `\`${command}\`` : command;
76
76
  return chalk_1.default.bold(terminalUISupport.color
77
77
  ? chalk_1.default.hex(exports.UI_COLORS.MARIGOLD_DARK)(commandReference)
78
78
  : commandReference);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.supportsHyperlinkModule = void 0;
4
- const hasFlag_1 = require("../hasFlag");
4
+ const yargsUtils_1 = require("../yargsUtils");
5
5
  //See https://github.com/jamestalmage/supports-hyperlinks (License: https://github.com/jamestalmage/supports-hyperlinks/blob/master/license)
6
6
  function parseVersion(versionString) {
7
7
  if (/^\d{3,4}$/.test(versionString)) {
@@ -22,7 +22,7 @@ function parseVersion(versionString) {
22
22
  }
23
23
  function supportsHyperlink(stream) {
24
24
  const { env } = process;
25
- if ((0, hasFlag_1.hasFlag)('noHyperlinks')) {
25
+ if ((0, yargsUtils_1.hasFlag)('noHyperlinks')) {
26
26
  return false;
27
27
  }
28
28
  if (stream && !stream.isTTY) {
@@ -7,7 +7,7 @@ exports.supportsColor = void 0;
7
7
  const process_1 = __importDefault(require("process"));
8
8
  const os_1 = __importDefault(require("os"));
9
9
  const tty_1 = __importDefault(require("tty"));
10
- const hasFlag_1 = require("../hasFlag");
10
+ const yargsUtils_1 = require("../yargsUtils");
11
11
  const { env } = process_1.default;
12
12
  function translateLevel(level) {
13
13
  if (level === 0) {
@@ -33,7 +33,7 @@ function _supportsColor(haveStream, { streamIsTTY } = {}) {
33
33
  if (env.TERM === 'dumb') {
34
34
  return min;
35
35
  }
36
- if ((0, hasFlag_1.hasFlag)('noColor')) {
36
+ if ((0, yargsUtils_1.hasFlag)('noColor')) {
37
37
  return 0;
38
38
  }
39
39
  if (process_1.default.platform === 'win32') {
@@ -16,6 +16,6 @@ type Meta = {
16
16
  export declare function trackCommandUsage(command: string, meta?: Meta, accountId?: number): Promise<void>;
17
17
  export declare function trackHelpUsage(command: string): Promise<void>;
18
18
  export declare function trackConvertFieldsUsage(command: string): Promise<void>;
19
- export declare function trackAuthAction(command: string, authType: string, step: string, accountId: number): Promise<void>;
19
+ export declare function trackAuthAction(command: string, authType: string, step: string, accountId?: number): Promise<void>;
20
20
  export declare function trackCommandMetadataUsage(command: string, meta?: Meta, accountId?: number): Promise<void>;
21
21
  export {};