@hubspot/cli 7.7.16-experimental.3 → 7.7.16-experimental.5

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 (33) hide show
  1. package/commands/app/install.d.ts +8 -0
  2. package/commands/app/install.js +127 -0
  3. package/commands/app.js +6 -1
  4. package/commands/getStarted.js +2 -2
  5. package/commands/project/cloneApp.js +4 -4
  6. package/commands/project/create.js +9 -9
  7. package/commands/project/dev/unifiedFlow.js +1 -0
  8. package/commands/testAccount/create.js +11 -4
  9. package/lang/en.d.ts +36 -3
  10. package/lang/en.js +36 -3
  11. package/lib/app/migrate_legacy.js +2 -3
  12. package/lib/polling.d.ts +1 -1
  13. package/lib/polling.js +11 -1
  14. package/lib/projects/add/v3AddComponent.js +4 -0
  15. package/lib/projects/create/index.d.ts +3 -2
  16. package/lib/projects/create/index.js +11 -5
  17. package/lib/projects/create/v3.d.ts +3 -3
  18. package/lib/projects/create/v3.js +2 -2
  19. package/lib/projects/localDev/LocalDevProcess.d.ts +3 -2
  20. package/lib/projects/localDev/LocalDevProcess.js +16 -12
  21. package/lib/projects/localDev/LocalDevState.d.ts +10 -5
  22. package/lib/projects/localDev/LocalDevState.js +18 -20
  23. package/lib/projects/localDev/LocalDevWatcher.js +1 -1
  24. package/lib/projects/structure.d.ts +2 -2
  25. package/lib/projects/upload.d.ts +2 -1
  26. package/lib/projects/upload.js +1 -0
  27. package/lib/prompts/projectNameAndDestPrompt.d.ts +3 -0
  28. package/lib/prompts/projectNameAndDestPrompt.js +60 -0
  29. package/lib/prompts/selectProjectTemplatePrompt.d.ts +26 -0
  30. package/lib/prompts/{createProjectPrompt.js → selectProjectTemplatePrompt.js} +6 -55
  31. package/package.json +2 -2
  32. package/types/LocalDev.d.ts +1 -0
  33. package/lib/prompts/createProjectPrompt.d.ts +0 -28
@@ -0,0 +1,8 @@
1
+ import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule, JSONOutputArgs } from '../../types/Yargs';
2
+ type InstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ appUid?: string;
4
+ projectName?: string;
5
+ testAccountId: number;
6
+ };
7
+ declare const installAppCommand: YargsCommandModule<unknown, InstallAppArgs>;
8
+ export default installAppCommand;
@@ -0,0 +1,127 @@
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 developerTestAccounts_1 = require("@hubspot/local-dev-lib/api/developerTestAccounts");
7
+ const usageTracking_1 = require("../../lib/usageTracking");
8
+ const en_1 = require("../../lang/en");
9
+ const exitCodes_1 = require("../../lib/enums/exitCodes");
10
+ const yargsUtils_1 = require("../../lib/yargsUtils");
11
+ const constants_1 = require("../../lib/constants");
12
+ const logger_1 = require("../../lib/ui/logger");
13
+ const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
14
+ const errorHandlers_1 = require("../../lib/errorHandlers");
15
+ const polling_1 = require("../../lib/polling");
16
+ const config_1 = require("../../lib/projects/config");
17
+ const upload_1 = require("../../lib/projects/upload");
18
+ const structure_1 = require("../../lib/projects/structure");
19
+ const command = 'install <test-account-id>';
20
+ const describe = undefined; // commands.app.subcommands.install.describe;
21
+ async function handler(args) {
22
+ const { derivedAccountId, appUid, projectName, testAccountId, formatOutputAsJson, } = args;
23
+ (0, usageTracking_1.trackCommandUsage)('app-install', {}, derivedAccountId);
24
+ const jsonOutput = {};
25
+ let targetProjectName = projectName;
26
+ let targetAppUid = appUid;
27
+ const { projectConfig, projectDir } = await (0, config_1.getProjectConfig)();
28
+ if (!targetProjectName) {
29
+ (0, config_1.validateProjectConfig)(projectConfig, projectDir);
30
+ targetProjectName = projectConfig?.name;
31
+ }
32
+ if (!targetProjectName) {
33
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.mustSpecifyProjectName);
34
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
35
+ }
36
+ let isAppOauth = true;
37
+ if (!targetAppUid) {
38
+ const intermediateRepresentation = await (0, upload_1.handleTranslate)(projectDir, projectConfig, derivedAccountId, true, undefined);
39
+ if (intermediateRepresentation) {
40
+ Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).forEach(node => {
41
+ if ((0, structure_1.isAppIRNode)(node)) {
42
+ targetAppUid = node.uid;
43
+ isAppOauth = node.config.auth.type === constants_1.APP_AUTH_TYPES.OAUTH;
44
+ }
45
+ });
46
+ }
47
+ }
48
+ if (!targetAppUid) {
49
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.noAppUidFound);
50
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
51
+ }
52
+ if (!isAppOauth) {
53
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.appMustBeOauth);
54
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
55
+ }
56
+ try {
57
+ const { data } = await (0, developerTestAccounts_1.installOauthAppIntoDeveloperTestAccount)(derivedAccountId, testAccountId, targetProjectName, targetAppUid);
58
+ if (data?.authCodes.length > 0) {
59
+ jsonOutput.authCode = data.authCodes[0].authCode;
60
+ }
61
+ }
62
+ catch (err) {
63
+ (0, errorHandlers_1.logError)(err);
64
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
65
+ }
66
+ SpinniesManager_1.default.init({
67
+ succeedColor: 'white',
68
+ });
69
+ SpinniesManager_1.default.add('installApp', {
70
+ text: en_1.commands.app.subcommands.install.polling.start,
71
+ });
72
+ let appInstallSucceeded = false;
73
+ try {
74
+ await (0, polling_1.poll)(() => (0, developerTestAccounts_1.fetchDeveloperTestAccountOauthAppInstallStatus)(derivedAccountId, targetProjectName, targetAppUid), {
75
+ successStates: ['SUCCESS'],
76
+ errorStates: [],
77
+ });
78
+ appInstallSucceeded = true;
79
+ }
80
+ catch (err) {
81
+ SpinniesManager_1.default.fail('installApp');
82
+ (0, errorHandlers_1.logError)(err);
83
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
84
+ }
85
+ if (!appInstallSucceeded) {
86
+ SpinniesManager_1.default.fail('installApp');
87
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
88
+ }
89
+ SpinniesManager_1.default.succeed('installApp', {
90
+ text: en_1.commands.app.subcommands.install.polling.success,
91
+ });
92
+ if (formatOutputAsJson) {
93
+ logger_1.uiLogger.json(jsonOutput);
94
+ }
95
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
96
+ }
97
+ function installAppBuilder(yargs) {
98
+ yargs.positional('test-account-id', {
99
+ describe: en_1.commands.app.subcommands.install.positionals.testAccountId,
100
+ required: true,
101
+ type: 'number',
102
+ });
103
+ yargs.option('app-uid', {
104
+ describe: en_1.commands.app.subcommands.install.options.appUid,
105
+ type: 'string',
106
+ });
107
+ yargs.option('project-name', {
108
+ describe: en_1.commands.app.subcommands.install.options.projectName,
109
+ type: 'string',
110
+ });
111
+ yargs.example('install 1234567890 --app-uid=my-app-uid --project-name=my-project', en_1.commands.app.subcommands.install.example);
112
+ return yargs;
113
+ }
114
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(installAppBuilder, command, en_1.commands.app.subcommands.install.describe, {
115
+ useGlobalOptions: true,
116
+ useAccountOptions: true,
117
+ useConfigOptions: true,
118
+ useEnvironmentOptions: true,
119
+ useJSONOutputOptions: true,
120
+ });
121
+ const installAppCommand = {
122
+ command,
123
+ describe,
124
+ handler,
125
+ builder,
126
+ };
127
+ exports.default = installAppCommand;
package/commands/app.js CHANGED
@@ -5,12 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const migrate_1 = __importDefault(require("./app/migrate"));
7
7
  const secret_1 = __importDefault(require("./app/secret"));
8
+ const install_1 = __importDefault(require("./app/install"));
8
9
  const yargsUtils_1 = require("../lib/yargsUtils");
9
10
  const command = ['app', 'apps'];
10
11
  // Keep the command hidden for now
11
12
  const describe = undefined;
12
13
  function appBuilder(yargs) {
13
- yargs.command(migrate_1.default).command(secret_1.default).demandCommand(1, '');
14
+ yargs
15
+ .command(migrate_1.default)
16
+ .command(secret_1.default)
17
+ .command(install_1.default)
18
+ .demandCommand(1, '');
14
19
  return yargs;
15
20
  }
16
21
  const builder = (0, yargsUtils_1.makeYargsBuilder)(appBuilder, command, describe);
@@ -14,7 +14,7 @@ const usageTracking_1 = require("../lib/usageTracking");
14
14
  const exitCodes_1 = require("../lib/enums/exitCodes");
15
15
  const yargsUtils_1 = require("../lib/yargsUtils");
16
16
  const promptUtils_1 = require("../lib/prompts/promptUtils");
17
- const createProjectPrompt_1 = require("../lib/prompts/createProjectPrompt");
17
+ const projectNameAndDestPrompt_1 = require("../lib/prompts/projectNameAndDestPrompt");
18
18
  const ui_1 = require("../lib/ui");
19
19
  const logger_1 = require("../lib/ui/logger");
20
20
  const errorHandlers_1 = require("../lib/errorHandlers");
@@ -76,7 +76,7 @@ async function handler(args) {
76
76
  logger_1.uiLogger.log(en_1.commands.getStarted.prompts.appSelected);
77
77
  // 1. Fetch project templates
78
78
  let latestRepoReleaseTag;
79
- const { dest, name } = await (0, createProjectPrompt_1.createProjectPrompt)(args);
79
+ const { dest, name } = await (0, projectNameAndDestPrompt_1.projectNameAndDestPrompt)(args);
80
80
  // Specific template for get-started command
81
81
  const projectTemplate = {
82
82
  name: 'private-app-get-started-template',
@@ -8,7 +8,7 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const usageTracking_1 = require("../../lib/usageTracking");
9
9
  const lang_1 = require("../../lib/lang");
10
10
  const selectPublicAppForMigrationPrompt_1 = require("../../lib/prompts/selectPublicAppForMigrationPrompt");
11
- const createProjectPrompt_1 = require("../../lib/prompts/createProjectPrompt");
11
+ const projectNameAndDestPrompt_1 = require("../../lib/prompts/projectNameAndDestPrompt");
12
12
  const polling_1 = require("../../lib/polling");
13
13
  const errorHandlers_1 = require("../../lib/errorHandlers");
14
14
  const exitCodes_1 = require("../../lib/enums/exitCodes");
@@ -53,9 +53,9 @@ async function handler(args) {
53
53
  });
54
54
  appId = appIdResponse.appId;
55
55
  }
56
- const createProjectPromptResponse = await (0, createProjectPrompt_1.createProjectPrompt)(args);
57
- projectName = createProjectPromptResponse.name;
58
- projectDest = createProjectPromptResponse.dest;
56
+ const projectNameAndDestPromptResponse = await (0, projectNameAndDestPrompt_1.projectNameAndDestPrompt)(args);
57
+ projectName = projectNameAndDestPromptResponse.name;
58
+ projectDest = projectNameAndDestPromptResponse.dest;
59
59
  }
60
60
  catch (error) {
61
61
  (0, errorHandlers_1.logError)(error, new errorHandlers_1.ApiErrorContext({ accountId: derivedAccountId }));
@@ -39,15 +39,15 @@ async function handler(args) {
39
39
  (0, errorHandlers_1.logError)(error);
40
40
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
41
41
  }
42
- const { authType, distribution, repoConfig, projectContents, createProjectPromptResponse, } = handleResult;
42
+ const { authType, distribution, repoConfig, projectContents, selectProjectTemplatePromptResponse, projectNameAndDestPromptResponse, } = handleResult;
43
43
  (0, usageTracking_1.trackCommandUsage)('project-create', {
44
- type: createProjectPromptResponse.projectTemplate?.name ||
45
- (createProjectPromptResponse.componentTemplates || [])
44
+ type: selectProjectTemplatePromptResponse.projectTemplate?.name ||
45
+ (selectProjectTemplatePromptResponse.componentTemplates || [])
46
46
  // @ts-expect-error
47
47
  .map((item) => item.label)
48
48
  .join(','),
49
49
  }, derivedAccountId);
50
- const projectDest = path_1.default.resolve((0, path_2.getCwd)(), createProjectPromptResponse.dest);
50
+ const projectDest = path_1.default.resolve((0, path_2.getCwd)(), projectNameAndDestPromptResponse.dest);
51
51
  const { projectConfig: existingProjectConfig, projectDir: existingProjectDir, } = await (0, config_1.getProjectConfig)(projectDest);
52
52
  // Exit if the target destination is within an existing project
53
53
  if (existingProjectConfig &&
@@ -57,7 +57,7 @@ async function handler(args) {
57
57
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
58
58
  }
59
59
  const components = (0, v3_2.generateComponentPaths)({
60
- createProjectPromptResponse,
60
+ selectProjectTemplatePromptResponse,
61
61
  platformVersion,
62
62
  repoConfig,
63
63
  projectContents,
@@ -66,7 +66,7 @@ async function handler(args) {
66
66
  });
67
67
  try {
68
68
  await (0, github_1.cloneGithubRepo)(repo, projectDest, {
69
- sourceDir: createProjectPromptResponse.projectTemplate?.path || components,
69
+ sourceDir: selectProjectTemplatePromptResponse.projectTemplate?.path || components,
70
70
  hideLogs: true,
71
71
  branch: 'main',
72
72
  });
@@ -80,15 +80,15 @@ async function handler(args) {
80
80
  const parsedConfigFile = JSON.parse(fs_extra_1.default.readFileSync(projectConfigPath).toString());
81
81
  (0, config_1.writeProjectConfig)(projectConfigPath, {
82
82
  ...parsedConfigFile,
83
- name: createProjectPromptResponse.name,
83
+ name: projectNameAndDestPromptResponse.name,
84
84
  });
85
85
  // If the template is 'no-template', we need to manually create a src directory
86
- if (createProjectPromptResponse.projectTemplate?.name ===
86
+ if (selectProjectTemplatePromptResponse.projectTemplate?.name ===
87
87
  legacy_1.EMPTY_PROJECT_TEMPLATE_NAME ||
88
88
  projectContents === v3_1.EMPTY_PROJECT) {
89
89
  fs_extra_1.default.ensureDirSync(path_1.default.join(projectDest, 'src'));
90
90
  }
91
- logger_1.uiLogger.success(en_1.commands.project.create.logs.success(createProjectPromptResponse.name, projectDest));
91
+ logger_1.uiLogger.success(en_1.commands.project.create.logs.success(projectNameAndDestPromptResponse.name, projectDest));
92
92
  logger_1.uiLogger.log(en_1.commands.project.create.logs.welcomeMessage);
93
93
  (0, ui_1.uiFeatureHighlight)([
94
94
  'projectCommandTip',
@@ -121,6 +121,7 @@ async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTar
121
121
  debug: args.debug,
122
122
  deployedBuild,
123
123
  isGithubLinked,
124
+ profile: args.profile,
124
125
  targetProjectAccountId,
125
126
  targetTestingAccountId: targetTestingAccountId,
126
127
  projectConfig,
@@ -92,14 +92,21 @@ async function handler(args) {
92
92
  await (0, polling_1.poll)(() => (0, developerTestAccounts_1.fetchDeveloperTestAccountGateSyncStatus)(derivedAccountId, testAccountId), {
93
93
  successStates: ['SUCCESS'],
94
94
  errorStates: [],
95
+ }, 5000 // 5 seconds
96
+ );
97
+ // HACK: The status endpoint sometimes returns an early success status.
98
+ // Sleep for an extra 5 seconds to make sure the status is actually success.
99
+ await new Promise(resolve => setTimeout(resolve, 5000));
100
+ SpinniesManager_1.default.succeed('createTestAccount', {
101
+ text: en_1.commands.testAccount.create.polling.success(testAccountConfig.accountName, testAccountId),
95
102
  });
96
103
  }
97
104
  catch (err) {
98
- (0, errorHandlers_1.logError)(err);
105
+ (0, errorHandlers_1.debugError)(err);
106
+ SpinniesManager_1.default.fail('createTestAccount', {
107
+ text: en_1.commands.testAccount.create.polling.failure,
108
+ });
99
109
  }
100
- SpinniesManager_1.default.succeed('createTestAccount', {
101
- text: en_1.commands.testAccount.create.polling.success(testAccountConfig.accountName, testAccountId),
102
- });
103
110
  if (formatOutputAsJson) {
104
111
  logger_1.uiLogger.json(jsonOutput);
105
112
  }
package/lang/en.d.ts CHANGED
@@ -1498,6 +1498,28 @@ ${string}`;
1498
1498
  readonly app: {
1499
1499
  readonly describe: "Commands for managing apps.";
1500
1500
  readonly subcommands: {
1501
+ readonly install: {
1502
+ readonly describe: "Install an app.";
1503
+ readonly options: {
1504
+ readonly appUid: "The uid of the app to install";
1505
+ readonly projectName: "The name of the project to install the app to";
1506
+ };
1507
+ readonly positionals: {
1508
+ readonly testAccountId: "The id of the test account to install the app into";
1509
+ };
1510
+ readonly errors: {
1511
+ readonly mustSpecifyProjectName: `You must specify a project name. Use the ${string} flag to specify the project name or run this command from within a project directory.`;
1512
+ readonly noAppUidFound: `No app uid found. Please specify the app uid with the ${string} flag or run this command from within a project that contains an app.`;
1513
+ readonly appMustBeOauth: "This command only supports installing oauth apps. Please specify an app with oauth auth type.";
1514
+ };
1515
+ readonly polling: {
1516
+ readonly start: "Installing app...";
1517
+ readonly success: "App installed successfully";
1518
+ readonly failure: "App installation failed";
1519
+ readonly error: "Error installing app";
1520
+ };
1521
+ readonly example: "Install the app with uid my-app-uid from the project named \"my-project\" into the target account with id 1234567890";
1522
+ };
1501
1523
  readonly secret: {
1502
1524
  readonly describe: "Commands for managing secrets.";
1503
1525
  readonly subcommands: {
@@ -1807,6 +1829,7 @@ ${string}`;
1807
1829
  readonly start: (testAccountName: string) => string;
1808
1830
  readonly syncing: "Test account created! Syncing account data... (may take a few minutes - you can exit and the sync will continue)";
1809
1831
  readonly success: (testAccountName: string, testAccountId: number) => string;
1832
+ readonly failure: "Failed to sync data into test account. The account may not be ready to use.";
1810
1833
  };
1811
1834
  readonly options: {
1812
1835
  readonly configPath: "The path to the test account config";
@@ -2608,6 +2631,9 @@ Run ${string} to upgrade to version ${string}`;
2608
2631
  readonly invalidAuthDistCombo: (authType: string, distribution: string) => string;
2609
2632
  };
2610
2633
  };
2634
+ readonly add: {
2635
+ readonly nothingAdded: "No features added.";
2636
+ };
2611
2637
  readonly validateProjectConfig: {
2612
2638
  readonly configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${string} to create a new project.`;
2613
2639
  readonly configMissingFields: "The project configuration file is missing required fields.";
@@ -2918,16 +2944,20 @@ Run ${string} to upgrade to version ${string}`;
2918
2944
  readonly languageRequired: "Please select API sample app's language";
2919
2945
  };
2920
2946
  };
2921
- readonly createProjectPrompt: {
2947
+ readonly projectNameAndDestPrompt: {
2922
2948
  readonly enterName: "[--name] Give your project a name: ";
2923
2949
  readonly enterDest: "[--dest] Enter the folder to create the project in:";
2924
- readonly selectTemplate: "[--template] Choose a project template: ";
2925
- readonly features: "[--features] Which features would you like your app to include?";
2926
2950
  readonly errors: {
2927
2951
  readonly nameRequired: "A project name is required";
2928
2952
  readonly destRequired: "A project dest is required";
2929
2953
  readonly invalidDest: "There is an existing project at this destination. Please provide a new path for this project.";
2930
2954
  readonly invalidCharacters: "The selected destination contains invalid characters. Please provide a new path and try again.";
2955
+ };
2956
+ };
2957
+ readonly selectProjectTemplatePrompt: {
2958
+ readonly selectTemplate: "[--template] Choose a project template: ";
2959
+ readonly features: "[--features] Which features would you like your app to include?";
2960
+ readonly errors: {
2931
2961
  readonly invalidTemplate: (template: string) => string;
2932
2962
  readonly projectTemplateRequired: "Project template is required when projectTemplates is provided";
2933
2963
  };
@@ -3032,6 +3062,9 @@ Run ${string} to upgrade to version ${string}`;
3032
3062
  };
3033
3063
  };
3034
3064
  };
3065
+ readonly polling: {
3066
+ readonly timeoutError: (timeoutMs: number) => string;
3067
+ };
3035
3068
  readonly convertFields: {
3036
3069
  readonly positionals: {
3037
3070
  readonly src: {
package/lang/en.js CHANGED
@@ -1496,6 +1496,28 @@ exports.commands = {
1496
1496
  app: {
1497
1497
  describe: 'Commands for managing apps.',
1498
1498
  subcommands: {
1499
+ install: {
1500
+ describe: 'Install an app.',
1501
+ options: {
1502
+ appUid: 'The uid of the app to install',
1503
+ projectName: 'The name of the project to install the app to',
1504
+ },
1505
+ positionals: {
1506
+ testAccountId: 'The id of the test account to install the app into',
1507
+ },
1508
+ errors: {
1509
+ mustSpecifyProjectName: `You must specify a project name. Use the ${(0, ui_1.uiCommandReference)('--project-name')} flag to specify the project name or run this command from within a project directory.`,
1510
+ noAppUidFound: `No app uid found. Please specify the app uid with the ${(0, ui_1.uiCommandReference)('--app-uid')} flag or run this command from within a project that contains an app.`,
1511
+ appMustBeOauth: 'This command only supports installing oauth apps. Please specify an app with oauth auth type.',
1512
+ },
1513
+ polling: {
1514
+ start: 'Installing app...',
1515
+ success: 'App installed successfully',
1516
+ failure: 'App installation failed',
1517
+ error: 'Error installing app',
1518
+ },
1519
+ example: 'Install the app with uid my-app-uid from the project named "my-project" into the target account with id 1234567890',
1520
+ },
1499
1521
  secret: {
1500
1522
  describe: 'Commands for managing secrets.',
1501
1523
  subcommands: {
@@ -1805,6 +1827,7 @@ exports.commands = {
1805
1827
  start: (testAccountName) => `Creating test account "${chalk_1.default.bold(testAccountName)}"...`,
1806
1828
  syncing: 'Test account created! Syncing account data... (may take a few minutes - you can exit and the sync will continue)',
1807
1829
  success: (testAccountName, testAccountId) => `Test account "${chalk_1.default.bold(testAccountName)}" successfully created with id: ${chalk_1.default.bold(testAccountId)}`,
1830
+ failure: 'Failed to sync data into test account. The account may not be ready to use.',
1808
1831
  },
1809
1832
  options: {
1810
1833
  configPath: 'The path to the test account config',
@@ -2605,6 +2628,9 @@ exports.lib = {
2605
2628
  invalidAuthDistCombo: (authType, distribution) => `Invalid distribution and auth combination. Apps with distribution '${distribution}' must have auth '${authType}'`,
2606
2629
  },
2607
2630
  },
2631
+ add: {
2632
+ nothingAdded: 'No features added.',
2633
+ },
2608
2634
  validateProjectConfig: {
2609
2635
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${(0, ui_1.uiCommandReference)('hs project create')} to create a new project.`,
2610
2636
  configMissingFields: 'The project configuration file is missing required fields.',
@@ -2913,16 +2939,20 @@ exports.lib = {
2913
2939
  languageRequired: "Please select API sample app's language",
2914
2940
  },
2915
2941
  },
2916
- createProjectPrompt: {
2942
+ projectNameAndDestPrompt: {
2917
2943
  enterName: '[--name] Give your project a name: ',
2918
2944
  enterDest: '[--dest] Enter the folder to create the project in:',
2919
- selectTemplate: '[--template] Choose a project template: ',
2920
- features: '[--features] Which features would you like your app to include?',
2921
2945
  errors: {
2922
2946
  nameRequired: 'A project name is required',
2923
2947
  destRequired: 'A project dest is required',
2924
2948
  invalidDest: 'There is an existing project at this destination. Please provide a new path for this project.',
2925
2949
  invalidCharacters: 'The selected destination contains invalid characters. Please provide a new path and try again.',
2950
+ },
2951
+ },
2952
+ selectProjectTemplatePrompt: {
2953
+ selectTemplate: '[--template] Choose a project template: ',
2954
+ features: '[--features] Which features would you like your app to include?',
2955
+ errors: {
2926
2956
  invalidTemplate: (template) => `[--template] Could not find template "${template}". Please choose an available template:`,
2927
2957
  projectTemplateRequired: 'Project template is required when projectTemplates is provided',
2928
2958
  },
@@ -3027,6 +3057,9 @@ exports.lib = {
3027
3057
  },
3028
3058
  },
3029
3059
  },
3060
+ polling: {
3061
+ timeoutError: (timeoutMs) => `Polling timed out after ${timeoutMs}ms.`,
3062
+ },
3030
3063
  convertFields: {
3031
3064
  positionals: {
3032
3065
  src: {
@@ -18,7 +18,7 @@ const ui_1 = require("../ui");
18
18
  const lang_1 = require("../lang");
19
19
  const accountTypes_1 = require("../accountTypes");
20
20
  const selectPublicAppForMigrationPrompt_1 = require("../prompts/selectPublicAppForMigrationPrompt");
21
- const createProjectPrompt_1 = require("../prompts/createProjectPrompt");
21
+ const projectNameAndDestPrompt_1 = require("../prompts/projectNameAndDestPrompt");
22
22
  const ensureProjectExists_1 = require("../projects/ensureProjectExists");
23
23
  const usageTracking_1 = require("../usageTracking");
24
24
  const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
@@ -58,8 +58,7 @@ async function migrateApp2023_2(derivedAccountId, options, accountConfig) {
58
58
  (0, errorHandlers_1.logError)(error, new errorHandlers_1.ApiErrorContext({ accountId: derivedAccountId }));
59
59
  return process.exit(exitCodes_1.EXIT_CODES.ERROR);
60
60
  }
61
- const createProjectPromptResponse = await (0, createProjectPrompt_1.createProjectPrompt)(options);
62
- const { name: projectName, dest: projectDest } = createProjectPromptResponse;
61
+ const { name: projectName, dest: projectDest } = await (0, projectNameAndDestPrompt_1.projectNameAndDestPrompt)(options);
63
62
  const { projectExists } = await (0, ensureProjectExists_1.ensureProjectExists)(derivedAccountId, projectName, {
64
63
  allowCreate: false,
65
64
  noLogs: true,
package/lib/polling.d.ts CHANGED
@@ -17,5 +17,5 @@ type PollingCallback<T extends GenericPollingResponse> = () => HubSpotPromise<T>
17
17
  export declare function poll<T extends GenericPollingResponse>(callback: PollingCallback<T>, statusLookup?: {
18
18
  successStates: string[];
19
19
  errorStates: string[];
20
- }): Promise<T>;
20
+ }, timeoutMs?: number): Promise<T>;
21
21
  export {};
package/lib/polling.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_POLLING_STATUS_LOOKUP = exports.DEFAULT_POLLING_STATES = void 0;
4
4
  exports.poll = poll;
5
5
  const constants_1 = require("./constants");
6
+ const en_1 = require("../lang/en");
6
7
  exports.DEFAULT_POLLING_STATES = {
7
8
  STARTED: 'STARTED',
8
9
  SUCCESS: 'SUCCESS',
@@ -18,7 +19,8 @@ exports.DEFAULT_POLLING_STATUS_LOOKUP = {
18
19
  exports.DEFAULT_POLLING_STATES.FAILURE,
19
20
  ],
20
21
  };
21
- function poll(callback, statusLookup = exports.DEFAULT_POLLING_STATUS_LOOKUP) {
22
+ function poll(callback, statusLookup = exports.DEFAULT_POLLING_STATUS_LOOKUP, timeoutMs = 60000 // Default 60 second timeout
23
+ ) {
22
24
  return new Promise((resolve, reject) => {
23
25
  const pollInterval = setInterval(async () => {
24
26
  try {
@@ -26,17 +28,25 @@ function poll(callback, statusLookup = exports.DEFAULT_POLLING_STATUS_LOOKUP) {
26
28
  const { status } = pollResp;
27
29
  if (statusLookup.successStates.includes(status)) {
28
30
  clearInterval(pollInterval);
31
+ clearTimeout(timeoutId);
29
32
  resolve(pollResp);
30
33
  }
31
34
  else if (statusLookup.errorStates.includes(status)) {
32
35
  clearInterval(pollInterval);
36
+ clearTimeout(timeoutId);
33
37
  reject(pollResp);
34
38
  }
35
39
  }
36
40
  catch (error) {
37
41
  clearInterval(pollInterval);
42
+ clearTimeout(timeoutId);
38
43
  reject(error);
39
44
  }
40
45
  }, constants_1.DEFAULT_POLLING_DELAY);
46
+ // Set a timeout to stop polling after specified duration
47
+ const timeoutId = setTimeout(() => {
48
+ clearInterval(pollInterval);
49
+ reject(new Error(en_1.lib.polling.timeoutError(timeoutMs)));
50
+ }, timeoutMs);
41
51
  });
42
52
  }
@@ -66,6 +66,10 @@ async function v3AddComponent(args, projectDir, projectConfig) {
66
66
  components.push(path_1.default.join(projectConfig.platformVersion, parentComponent.path));
67
67
  }
68
68
  }
69
+ if (components.length === 0) {
70
+ logger_1.uiLogger.log(en_1.lib.projects.add.nothingAdded);
71
+ return;
72
+ }
69
73
  await (0, github_1.cloneGithubRepo)(constants_1.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, projectDir, {
70
74
  sourceDir: components,
71
75
  hideLogs: true,
@@ -1,6 +1,6 @@
1
1
  import { ArgumentsCamelCase } from 'yargs';
2
2
  import { ProjectTemplateRepoConfig } from '../../../types/Projects';
3
- import { CreateProjectPromptResponse } from '../../prompts/createProjectPrompt';
3
+ import { SelectProjectTemplatePromptResponse, ProjectNameAndDestPromptResponse } from '../../prompts/selectProjectTemplatePrompt';
4
4
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs } from '../../../types/Yargs';
5
5
  import { RepoPath } from '@hubspot/local-dev-lib/types/Github';
6
6
  export type ProjectCreateArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
@@ -19,5 +19,6 @@ export declare function handleProjectCreationFlow(args: ArgumentsCamelCase<Proje
19
19
  distribution?: string;
20
20
  repoConfig?: ProjectTemplateRepoConfig;
21
21
  projectContents?: string;
22
- createProjectPromptResponse: CreateProjectPromptResponse;
22
+ selectProjectTemplatePromptResponse: SelectProjectTemplatePromptResponse;
23
+ projectNameAndDestPromptResponse: ProjectNameAndDestPromptResponse;
23
24
  }>;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleProjectCreationFlow = handleProjectCreationFlow;
4
- const createProjectPrompt_1 = require("../../prompts/createProjectPrompt");
4
+ const selectProjectTemplatePrompt_1 = require("../../prompts/selectProjectTemplatePrompt");
5
+ const projectNameAndDestPrompt_1 = require("../../prompts/projectNameAndDestPrompt");
5
6
  const constants_1 = require("../../constants");
6
7
  const buildAndDeploy_1 = require("../buildAndDeploy");
7
8
  const v3_1 = require("./v3");
@@ -12,15 +13,17 @@ const exitCodes_1 = require("../../enums/exitCodes");
12
13
  async function handleProjectCreationFlow(args) {
13
14
  const { platformVersion, templateSource, projectBase, auth: providedAuth, distribution: providedDistribution, } = args;
14
15
  const repo = templateSource || constants_1.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH;
16
+ const projectNameAndDestPromptResponse = await (0, projectNameAndDestPrompt_1.projectNameAndDestPrompt)(args);
15
17
  if ((0, buildAndDeploy_1.useV3Api)(platformVersion)) {
16
18
  const { componentTemplateChoices, authType, distribution, repoConfig, projectContents, } = await (0, v3_1.v3ComponentFlow)(platformVersion, projectBase, providedAuth, providedDistribution);
17
- const createProjectPromptResponse = await (0, createProjectPrompt_1.createProjectPrompt)(args, undefined, projectContents !== v3_1.EMPTY_PROJECT ? componentTemplateChoices : undefined);
19
+ const selectProjectTemplatePromptResponse = await (0, selectProjectTemplatePrompt_1.selectProjectTemplatePrompt)(args, undefined, projectContents !== v3_1.EMPTY_PROJECT ? componentTemplateChoices : undefined);
18
20
  return {
19
21
  authType,
20
22
  distribution,
21
23
  repoConfig,
22
24
  projectContents,
23
- createProjectPromptResponse,
25
+ selectProjectTemplatePromptResponse,
26
+ projectNameAndDestPromptResponse,
24
27
  };
25
28
  }
26
29
  const projectTemplates = await (0, legacy_1.getProjectTemplateListFromRepo)(repo, 'main');
@@ -28,6 +31,9 @@ async function handleProjectCreationFlow(args) {
28
31
  logger_1.uiLogger.error(en_1.commands.project.create.errors.failedToFetchProjectList);
29
32
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
30
33
  }
31
- const createProjectPromptResponse = await (0, createProjectPrompt_1.createProjectPrompt)(args, projectTemplates);
32
- return { createProjectPromptResponse };
34
+ const selectProjectTemplatePromptResponse = await (0, selectProjectTemplatePrompt_1.selectProjectTemplatePrompt)(args, projectTemplates);
35
+ return {
36
+ selectProjectTemplatePromptResponse,
37
+ projectNameAndDestPromptResponse,
38
+ };
33
39
  }
@@ -1,6 +1,6 @@
1
1
  import { ComponentTemplate, ComponentTemplateChoice, ProjectTemplateRepoConfig } from '../../../types/Projects';
2
2
  import { ProjectMetadata } from '@hubspot/project-parsing-lib/src/lib/project';
3
- import { CreateProjectPromptResponse } from '../../prompts/createProjectPrompt';
3
+ import { SelectProjectTemplatePromptResponse } from '../../prompts/selectProjectTemplatePrompt';
4
4
  export declare const EMPTY_PROJECT = "empty";
5
5
  export declare const PROJECT_WITH_APP = "app";
6
6
  export declare function createV3App(providedAuth: string | undefined, providedDistribution: string | undefined): Promise<{
@@ -16,8 +16,8 @@ type V3ComponentInfo = {
16
16
  componentTemplateChoices?: ComponentTemplateChoice[];
17
17
  };
18
18
  export declare function v3ComponentFlow(platformVersion: string, projectBase: string | undefined, providedAuth: string | undefined, providedDistribution: string | undefined): Promise<V3ComponentInfo>;
19
- export declare function generateComponentPaths({ createProjectPromptResponse, platformVersion, repoConfig, projectContents, authType, distribution, }: {
20
- createProjectPromptResponse: CreateProjectPromptResponse;
19
+ export declare function generateComponentPaths({ selectProjectTemplatePromptResponse, platformVersion, repoConfig, projectContents, authType, distribution, }: {
20
+ selectProjectTemplatePromptResponse: SelectProjectTemplatePromptResponse;
21
21
  platformVersion: string;
22
22
  repoConfig?: ProjectTemplateRepoConfig;
23
23
  projectContents?: string;
@@ -134,11 +134,11 @@ async function v3ComponentFlow(platformVersion, projectBase, providedAuth, provi
134
134
  repoConfig,
135
135
  };
136
136
  }
137
- function generateComponentPaths({ createProjectPromptResponse, platformVersion, repoConfig, projectContents, authType, distribution, }) {
137
+ function generateComponentPaths({ selectProjectTemplatePromptResponse, platformVersion, repoConfig, projectContents, authType, distribution, }) {
138
138
  if (!(0, buildAndDeploy_1.useV3Api)(platformVersion)) {
139
139
  return [];
140
140
  }
141
- const components = createProjectPromptResponse.componentTemplates?.map((componentTemplate) => {
141
+ const components = selectProjectTemplatePromptResponse.componentTemplates?.map((componentTemplate) => {
142
142
  return path_1.default.join(platformVersion, componentTemplate.path);
143
143
  }) || [];
144
144
  if (projectContents && projectContents !== exports.EMPTY_PROJECT) {
@@ -16,15 +16,16 @@ declare class LocalDevProcess {
16
16
  [key: string]: IntermediateRepresentationNodeLocalDev;
17
17
  };
18
18
  get logger(): LocalDevLogger;
19
- get configFilesUpdatedSinceLastUpload(): Set<string>;
20
19
  private setupDevServers;
21
20
  private startDevServers;
22
21
  private cleanupDevServers;
23
22
  private compareLocalProjectToDeployed;
24
23
  private projectConfigValidForUpload;
24
+ private getIntermediateRepresentation;
25
25
  private updateProjectNodes;
26
+ private updateProjectNodesAfterUpload;
26
27
  handleFileChange(filePath: string, event: string): Promise<void>;
27
- handleConfigFileChange(filePath: string, event: string): Promise<void>;
28
+ handleConfigFileChange(): Promise<void>;
28
29
  start(): Promise<void>;
29
30
  stop(showProgress?: boolean): Promise<void>;
30
31
  uploadProject(): Promise<boolean>;
@@ -46,9 +46,6 @@ class LocalDevProcess {
46
46
  get logger() {
47
47
  return this._logger;
48
48
  }
49
- get configFilesUpdatedSinceLastUpload() {
50
- return this.state.configFilesUpdatedSinceLastUpload;
51
- }
52
49
  async setupDevServers() {
53
50
  try {
54
51
  await this.devServerManager.setup();
@@ -105,17 +102,28 @@ class LocalDevProcess {
105
102
  });
106
103
  return true;
107
104
  }
108
- async updateProjectNodes() {
109
- const intermediateRepresentation = await (0, project_parsing_lib_1.translateForLocalDev)({
105
+ getIntermediateRepresentation(projectNodesAtLastUpload) {
106
+ return (0, project_parsing_lib_1.translateForLocalDev)({
110
107
  projectSourceDir: path_1.default.join(this.state.projectDir, this.state.projectConfig.srcDir),
111
108
  platformVersion: this.state.projectConfig.platformVersion,
112
109
  accountId: this.state.targetProjectAccountId,
113
110
  }, {
114
- configFilesUpdatedSinceLastUpload: this.state.configFilesUpdatedSinceLastUpload,
111
+ projectNodesAtLastUpload,
112
+ profile: this.state.profile,
115
113
  });
114
+ }
115
+ async updateProjectNodes() {
116
+ const intermediateRepresentation = await this.getIntermediateRepresentation(this.state.projectNodesAtLastUpload);
116
117
  this.state.projectNodes =
117
118
  intermediateRepresentation.intermediateNodesIndexedByUid;
118
119
  }
120
+ async updateProjectNodesAfterUpload() {
121
+ const intermediateRepresentation = await this.getIntermediateRepresentation();
122
+ this.state.projectNodes =
123
+ intermediateRepresentation.intermediateNodesIndexedByUid;
124
+ this.state.projectNodesAtLastUpload =
125
+ intermediateRepresentation.intermediateNodesIndexedByUid;
126
+ }
119
127
  async handleFileChange(filePath, event) {
120
128
  await this.updateProjectNodes();
121
129
  try {
@@ -125,10 +133,7 @@ class LocalDevProcess {
125
133
  this.logger.fileChangeError(e);
126
134
  }
127
135
  }
128
- async handleConfigFileChange(filePath, event) {
129
- if (event === 'add' || event === 'change') {
130
- this.state.addUpdatedConfigFileUpdatedSinceLastUpload(filePath);
131
- }
136
+ async handleConfigFileChange() {
132
137
  await this.updateProjectNodes();
133
138
  this.logger.uploadWarning();
134
139
  }
@@ -185,8 +190,7 @@ class LocalDevProcess {
185
190
  this.logger.uploadError(uploadError);
186
191
  return false;
187
192
  }
188
- this.state.resetConfigFilesUpdatedSinceLastUpload();
189
- this.updateProjectNodes();
193
+ await this.updateProjectNodesAfterUpload();
190
194
  this.logger.uploadSuccess();
191
195
  this.logger.clearUploadWarnings();
192
196
  return true;
@@ -6,6 +6,7 @@ import { LocalDevStateConstructorOptions, LocalDevStateListener, AppLocalDevData
6
6
  declare class LocalDevState {
7
7
  private _targetProjectAccountId;
8
8
  private _targetTestingAccountId;
9
+ private _profile?;
9
10
  private _projectConfig;
10
11
  private _projectDir;
11
12
  private _projectId;
@@ -14,15 +15,16 @@ declare class LocalDevState {
14
15
  private _deployedBuild?;
15
16
  private _isGithubLinked;
16
17
  private _projectNodes;
18
+ private _projectNodesAtLastUpload;
17
19
  private _env;
18
20
  private _listeners;
19
- private _configFilesUpdatedSinceLastUpload;
20
21
  private _appData;
21
22
  private _devServerMessage;
22
- constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, env, }: LocalDevStateConstructorOptions);
23
+ constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, profile, env, }: LocalDevStateConstructorOptions);
23
24
  private runListeners;
24
25
  get targetProjectAccountId(): number;
25
26
  get targetTestingAccountId(): number;
27
+ get profile(): string | undefined;
26
28
  get projectConfig(): ProjectConfig;
27
29
  get projectDir(): string;
28
30
  get projectId(): number;
@@ -36,10 +38,13 @@ declare class LocalDevState {
36
38
  set projectNodes(nodes: {
37
39
  [key: string]: IntermediateRepresentationNodeLocalDev;
38
40
  });
41
+ get projectNodesAtLastUpload(): {
42
+ [key: string]: IntermediateRepresentationNodeLocalDev;
43
+ };
44
+ set projectNodesAtLastUpload(nodes: {
45
+ [key: string]: IntermediateRepresentationNodeLocalDev;
46
+ });
39
47
  get env(): Environment;
40
- get configFilesUpdatedSinceLastUpload(): Set<string>;
41
- addUpdatedConfigFileUpdatedSinceLastUpload(filePath: string): void;
42
- resetConfigFilesUpdatedSinceLastUpload(): void;
43
48
  get appData(): Record<string, AppLocalDevData>;
44
49
  getAppDataByUid(uid: string): AppLocalDevData | undefined;
45
50
  setAppDataForUid(uid: string, appData: AppLocalDevData): void;
@@ -4,6 +4,7 @@ const constants_1 = require("../../constants");
4
4
  class LocalDevState {
5
5
  _targetProjectAccountId;
6
6
  _targetTestingAccountId;
7
+ _profile;
7
8
  _projectConfig;
8
9
  _projectDir;
9
10
  _projectId;
@@ -12,14 +13,15 @@ class LocalDevState {
12
13
  _deployedBuild;
13
14
  _isGithubLinked;
14
15
  _projectNodes;
16
+ _projectNodesAtLastUpload;
15
17
  _env;
16
18
  _listeners;
17
- _configFilesUpdatedSinceLastUpload;
18
19
  _appData;
19
20
  _devServerMessage;
20
- constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, env, }) {
21
+ constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, profile, env, }) {
21
22
  this._targetProjectAccountId = targetProjectAccountId;
22
23
  this._targetTestingAccountId = targetTestingAccountId;
24
+ this._profile = profile;
23
25
  this._projectConfig = projectConfig;
24
26
  this._projectDir = projectDir;
25
27
  this._projectId = projectId;
@@ -28,8 +30,8 @@ class LocalDevState {
28
30
  this._deployedBuild = deployedBuild;
29
31
  this._isGithubLinked = isGithubLinked;
30
32
  this._projectNodes = initialProjectNodes;
33
+ this._projectNodesAtLastUpload = initialProjectNodes;
31
34
  this._env = env;
32
- this._configFilesUpdatedSinceLastUpload = new Set();
33
35
  this._appData = {};
34
36
  this._devServerMessage = constants_1.LOCAL_DEV_SERVER_MESSAGE_TYPES.INITIAL;
35
37
  this._listeners = {};
@@ -45,10 +47,11 @@ class LocalDevState {
45
47
  get targetTestingAccountId() {
46
48
  return this._targetTestingAccountId;
47
49
  }
50
+ get profile() {
51
+ return this._profile;
52
+ }
48
53
  get projectConfig() {
49
- return {
50
- ...this._projectConfig,
51
- };
54
+ return structuredClone(this._projectConfig);
52
55
  }
53
56
  get projectDir() {
54
57
  return this._projectDir;
@@ -63,34 +66,29 @@ class LocalDevState {
63
66
  return this._debug;
64
67
  }
65
68
  get deployedBuild() {
66
- return (this._deployedBuild && {
67
- ...this._deployedBuild,
68
- });
69
+ return this._deployedBuild && structuredClone(this._deployedBuild);
69
70
  }
70
71
  get isGithubLinked() {
71
72
  return this._isGithubLinked;
72
73
  }
73
74
  get projectNodes() {
74
- return { ...this._projectNodes };
75
+ return structuredClone(this._projectNodes);
75
76
  }
76
77
  set projectNodes(nodes) {
77
78
  this._projectNodes = nodes;
78
79
  this.runListeners('projectNodes');
79
80
  }
80
- get env() {
81
- return this._env;
82
- }
83
- get configFilesUpdatedSinceLastUpload() {
84
- return this._configFilesUpdatedSinceLastUpload;
81
+ get projectNodesAtLastUpload() {
82
+ return structuredClone(this._projectNodesAtLastUpload);
85
83
  }
86
- addUpdatedConfigFileUpdatedSinceLastUpload(filePath) {
87
- this._configFilesUpdatedSinceLastUpload.add(filePath);
84
+ set projectNodesAtLastUpload(nodes) {
85
+ this._projectNodesAtLastUpload = nodes;
88
86
  }
89
- resetConfigFilesUpdatedSinceLastUpload() {
90
- this._configFilesUpdatedSinceLastUpload.clear();
87
+ get env() {
88
+ return this._env;
91
89
  }
92
90
  get appData() {
93
- return { ...this._appData };
91
+ return structuredClone(this._appData);
94
92
  }
95
93
  getAppDataByUid(uid) {
96
94
  return { ...this._appData[uid] };
@@ -21,7 +21,7 @@ class LocalDevWatcher {
21
21
  }
22
22
  handleWatchEvent(filePath, event, configPaths) {
23
23
  if (configPaths.includes(filePath)) {
24
- return this.localDevProcess.handleConfigFileChange(filePath, event);
24
+ return this.localDevProcess.handleConfigFileChange();
25
25
  }
26
26
  return this.localDevProcess.handleFileChange(filePath, event);
27
27
  }
@@ -1,5 +1,5 @@
1
1
  import { ComponentTypes, Component, GenericComponentConfig, PublicAppComponentConfig, PrivateAppComponentConfig, AppCardComponentConfig } from '../../types/Projects';
2
- import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types';
2
+ import { IntermediateRepresentationNode, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types';
3
3
  import { AppIRNode } from '../../types/ProjectComponents';
4
4
  export declare const CONFIG_FILES: {
5
5
  [k in ComponentTypes]: string;
@@ -15,4 +15,4 @@ export declare function getProjectComponentTypes(components: Array<Component>):
15
15
  export declare function getComponentUid(component?: Component | null): string | null;
16
16
  export declare function componentIsApp(component?: Component | null): component is Component<PublicAppComponentConfig | PrivateAppComponentConfig>;
17
17
  export declare function componentIsPublicApp(component?: Component | null): component is Component<PublicAppComponentConfig>;
18
- export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev): component is AppIRNode;
18
+ export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev | IntermediateRepresentationNode): component is AppIRNode;
@@ -1,4 +1,5 @@
1
1
  import { FileResult } from 'tmp';
2
+ import { IntermediateRepresentation } from '@hubspot/project-parsing-lib';
2
3
  import { ProjectConfig } from '../../types/Projects';
3
4
  type ProjectUploadCallbackFunction<T> = (accountId: number, projectConfig: ProjectConfig, tempFile: FileResult, buildId: number) => Promise<T>;
4
5
  type ProjectUploadResult<T> = {
@@ -20,5 +21,5 @@ type HandleProjectUploadArg<T> = {
20
21
  export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
21
22
  export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
22
23
  export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
23
- export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<unknown>;
24
+ export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<IntermediateRepresentation | undefined>;
24
25
  export {};
@@ -149,4 +149,5 @@ async function handleTranslate(projectDir, projectConfig, accountId, skipValidat
149
149
  (0, errorHandlers_1.logError)(e);
150
150
  }
151
151
  }
152
+ return undefined;
152
153
  }
@@ -0,0 +1,3 @@
1
+ import { PromptOptionsArg, ProjectNameAndDestPromptResponse } from './selectProjectTemplatePrompt';
2
+ export declare function projectNameAndDestPrompt(promptOptions: PromptOptionsArg): Promise<ProjectNameAndDestPromptResponse>;
3
+ export declare function validateProjectDirectory(input?: string): string | boolean;
@@ -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.projectNameAndDestPrompt = projectNameAndDestPrompt;
7
+ exports.validateProjectDirectory = validateProjectDirectory;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const path_2 = require("@hubspot/local-dev-lib/path");
11
+ const en_1 = require("../../lang/en");
12
+ const promptUtils_1 = require("./promptUtils");
13
+ const constants_1 = require("../constants");
14
+ async function projectNameAndDestPrompt(promptOptions) {
15
+ const result = await (0, promptUtils_1.promptUser)([
16
+ {
17
+ name: 'name',
18
+ message: en_1.lib.prompts.projectNameAndDestPrompt.enterName,
19
+ when: !promptOptions.name,
20
+ validate: (input) => {
21
+ if (!input) {
22
+ return en_1.lib.prompts.projectNameAndDestPrompt.errors.nameRequired;
23
+ }
24
+ return true;
25
+ },
26
+ },
27
+ {
28
+ name: 'dest',
29
+ message: en_1.lib.prompts.projectNameAndDestPrompt.enterDest,
30
+ when: !promptOptions.dest,
31
+ default: answers => {
32
+ const projectName = (0, path_2.sanitizeFileName)(promptOptions.name || answers.name);
33
+ return path_1.default.resolve((0, path_2.getCwd)(), projectName);
34
+ },
35
+ validate: validateProjectDirectory,
36
+ filter: input => {
37
+ return (0, path_2.untildify)(input);
38
+ },
39
+ },
40
+ ]);
41
+ if (!result.name) {
42
+ result.name = promptOptions.name;
43
+ }
44
+ if (!result.dest) {
45
+ result.dest = promptOptions.dest;
46
+ }
47
+ return result;
48
+ }
49
+ function validateProjectDirectory(input) {
50
+ if (!input) {
51
+ return en_1.lib.prompts.projectNameAndDestPrompt.errors.destRequired;
52
+ }
53
+ if (fs_1.default.existsSync(path_1.default.resolve((0, path_2.getCwd)(), path_1.default.join(input, constants_1.PROJECT_CONFIG_FILE)))) {
54
+ return en_1.lib.prompts.projectNameAndDestPrompt.errors.invalidDest;
55
+ }
56
+ if (!(0, path_2.isValidPath)(input)) {
57
+ return en_1.lib.prompts.projectNameAndDestPrompt.errors.invalidCharacters;
58
+ }
59
+ return true;
60
+ }
@@ -0,0 +1,26 @@
1
+ import { ComponentTemplate, ComponentTemplateChoice, ProjectTemplate } from '../../types/Projects';
2
+ export type SelectProjectTemplatePromptResponse = {
3
+ projectTemplate?: ProjectTemplate;
4
+ componentTemplates?: ComponentTemplate[];
5
+ };
6
+ type SelectProjectTemplatePromptResponseProjectTemplate = {
7
+ projectTemplate: ProjectTemplate;
8
+ componentTemplates: undefined;
9
+ };
10
+ type SelectProjectTemplatePromptResponseComponentTemplates = {
11
+ projectTemplate?: undefined;
12
+ componentTemplates?: ComponentTemplate[];
13
+ };
14
+ export type ProjectNameAndDestPromptResponse = {
15
+ name: string;
16
+ dest: string;
17
+ };
18
+ export type PromptOptionsArg = {
19
+ name?: string;
20
+ dest?: string;
21
+ template?: string;
22
+ features?: string[];
23
+ };
24
+ export declare function selectProjectTemplatePrompt(promptOptions: PromptOptionsArg, projectTemplates?: ProjectTemplate[], componentTemplates?: undefined): Promise<SelectProjectTemplatePromptResponseProjectTemplate>;
25
+ export declare function selectProjectTemplatePrompt(promptOptions: PromptOptionsArg, projectTemplates?: undefined, componentTemplates?: ComponentTemplateChoice[]): Promise<SelectProjectTemplatePromptResponseComponentTemplates>;
26
+ export {};
@@ -1,31 +1,12 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createProjectPrompt = createProjectPrompt;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const path_2 = require("@hubspot/local-dev-lib/path");
3
+ exports.selectProjectTemplatePrompt = selectProjectTemplatePrompt;
10
4
  const promptUtils_1 = require("./promptUtils");
11
- const constants_1 = require("../constants");
12
5
  const en_1 = require("../../lang/en");
13
- function validateProjectDirectory(input) {
14
- if (!input) {
15
- return en_1.lib.prompts.createProjectPrompt.errors.destRequired;
16
- }
17
- if (fs_1.default.existsSync(path_1.default.resolve((0, path_2.getCwd)(), path_1.default.join(input, constants_1.PROJECT_CONFIG_FILE)))) {
18
- return en_1.lib.prompts.createProjectPrompt.errors.invalidDest;
19
- }
20
- if (!(0, path_2.isValidPath)(input)) {
21
- return en_1.lib.prompts.createProjectPrompt.errors.invalidCharacters;
22
- }
23
- return true;
24
- }
25
6
  function findTemplateByNameOrLabel(projectTemplates, templateNameOrLabel) {
26
7
  return projectTemplates.find(t => t.name === templateNameOrLabel || t.label === templateNameOrLabel);
27
8
  }
28
- async function createProjectPrompt(promptOptions, projectTemplates, componentTemplates) {
9
+ async function selectProjectTemplatePrompt(promptOptions, projectTemplates, componentTemplates) {
29
10
  const createProjectFromTemplate = !!projectTemplates && projectTemplates.length > 0;
30
11
  const createProjectFromComponents = Array.isArray(componentTemplates) && componentTemplates?.length > 0;
31
12
  const selectedComponents = [];
@@ -46,36 +27,12 @@ async function createProjectPrompt(promptOptions, projectTemplates, componentTem
46
27
  !!promptOptions.template &&
47
28
  !!findTemplateByNameOrLabel(projectTemplates, promptOptions.template);
48
29
  const result = await (0, promptUtils_1.promptUser)([
49
- {
50
- name: 'name',
51
- message: en_1.lib.prompts.createProjectPrompt.enterName,
52
- when: !promptOptions.name,
53
- validate: (input) => {
54
- if (!input) {
55
- return en_1.lib.prompts.createProjectPrompt.errors.nameRequired;
56
- }
57
- return true;
58
- },
59
- },
60
- {
61
- name: 'dest',
62
- message: en_1.lib.prompts.createProjectPrompt.enterDest,
63
- when: !promptOptions.dest,
64
- default: answers => {
65
- const projectName = (0, path_2.sanitizeFileName)(promptOptions.name || answers.name);
66
- return path_1.default.resolve((0, path_2.getCwd)(), projectName);
67
- },
68
- validate: validateProjectDirectory,
69
- filter: input => {
70
- return (0, path_2.untildify)(input);
71
- },
72
- },
73
30
  {
74
31
  name: 'projectTemplate',
75
32
  message: () => {
76
33
  return promptOptions.template && !providedTemplateIsValid
77
- ? en_1.lib.prompts.createProjectPrompt.errors.invalidTemplate(promptOptions.template)
78
- : en_1.lib.prompts.createProjectPrompt.selectTemplate;
34
+ ? en_1.lib.prompts.selectProjectTemplatePrompt.errors.invalidTemplate(promptOptions.template)
35
+ : en_1.lib.prompts.selectProjectTemplatePrompt.selectTemplate;
79
36
  },
80
37
  when: createProjectFromTemplate && !providedTemplateIsValid,
81
38
  type: 'list',
@@ -90,7 +47,7 @@ async function createProjectPrompt(promptOptions, projectTemplates, componentTem
90
47
  },
91
48
  {
92
49
  name: 'componentTemplates',
93
- message: en_1.lib.prompts.createProjectPrompt.features,
50
+ message: en_1.lib.prompts.selectProjectTemplatePrompt.features,
94
51
  when: !promptOptions.features &&
95
52
  createProjectFromComponents &&
96
53
  selectedComponents.length === 0,
@@ -98,12 +55,6 @@ async function createProjectPrompt(promptOptions, projectTemplates, componentTem
98
55
  choices: componentTemplates,
99
56
  },
100
57
  ]);
101
- if (!result.name) {
102
- result.name = promptOptions.name;
103
- }
104
- if (!result.dest) {
105
- result.dest = promptOptions.dest;
106
- }
107
58
  if (!result.componentTemplates) {
108
59
  result.componentTemplates = selectedComponents;
109
60
  }
@@ -112,7 +63,7 @@ async function createProjectPrompt(promptOptions, projectTemplates, componentTem
112
63
  }
113
64
  if (projectTemplates && projectTemplates.length > 0) {
114
65
  if (!result.projectTemplate) {
115
- throw new Error(en_1.lib.prompts.createProjectPrompt.errors.projectTemplateRequired);
66
+ throw new Error(en_1.lib.prompts.selectProjectTemplatePrompt.errors.projectTemplateRequired);
116
67
  }
117
68
  return result;
118
69
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.7.16-experimental.3",
3
+ "version": "7.7.16-experimental.5",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
7
7
  "dependencies": {
8
8
  "@hubspot/local-dev-lib": "3.11.0",
9
- "@hubspot/project-parsing-lib": "0.3.2",
9
+ "@hubspot/project-parsing-lib": "0.5.0",
10
10
  "@hubspot/serverless-dev-runtime": "7.0.6",
11
11
  "@hubspot/theme-preview-dev-server": "0.0.10",
12
12
  "@hubspot/ui-extensions-dev-server": "0.9.2",
@@ -8,6 +8,7 @@ import { APP_INSTALLATION_STATES, LOCAL_DEV_SERVER_MESSAGE_TYPES } from '../lib/
8
8
  export type LocalDevStateConstructorOptions = {
9
9
  targetProjectAccountId: number;
10
10
  targetTestingAccountId: number;
11
+ profile?: string;
11
12
  projectConfig: ProjectConfig;
12
13
  projectDir: string;
13
14
  projectId: number;
@@ -1,28 +0,0 @@
1
- import { ComponentTemplate, ComponentTemplateChoice, ProjectTemplate } from '../../types/Projects';
2
- export type CreateProjectPromptResponse = {
3
- name: string;
4
- dest: string;
5
- projectTemplate?: ProjectTemplate;
6
- componentTemplates?: ComponentTemplate[];
7
- };
8
- type CreateProjectPromptResponseProjectTemplate = {
9
- name: string;
10
- dest: string;
11
- projectTemplate: ProjectTemplate;
12
- componentTemplates: undefined;
13
- };
14
- type CreateProjectPromptResponseComponentTemplates = {
15
- name: string;
16
- dest: string;
17
- projectTemplate?: undefined;
18
- componentTemplates?: ComponentTemplate[];
19
- };
20
- type PromptOptionsArg = {
21
- name?: string;
22
- dest?: string;
23
- template?: string;
24
- features?: string[];
25
- };
26
- export declare function createProjectPrompt(promptOptions: PromptOptionsArg, projectTemplates?: ProjectTemplate[], componentTemplates?: undefined): Promise<CreateProjectPromptResponseProjectTemplate>;
27
- export declare function createProjectPrompt(promptOptions: PromptOptionsArg, projectTemplates?: undefined, componentTemplates?: ComponentTemplateChoice[]): Promise<CreateProjectPromptResponseComponentTemplates>;
28
- export {};