@hubspot/cli 5.4.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/commands/__tests__/projects.test.js +105 -0
  2. package/commands/accounts/clean.js +1 -1
  3. package/commands/project/__tests__/deploy.test.js +1 -1
  4. package/commands/project/__tests__/installDeps.test.js +168 -0
  5. package/commands/project/__tests__/logs.test.js +305 -0
  6. package/commands/project/cloneApp.js +3 -16
  7. package/commands/project/deploy.js +4 -1
  8. package/commands/project/dev.js +15 -6
  9. package/commands/project/download.js +4 -1
  10. package/commands/project/installDeps.js +78 -0
  11. package/commands/project/logs.js +80 -242
  12. package/commands/project/migrateApp.js +3 -6
  13. package/commands/project/upload.js +5 -3
  14. package/commands/project/watch.js +3 -9
  15. package/commands/project.js +2 -0
  16. package/commands/sandbox/create.js +1 -0
  17. package/commands/sandbox.js +0 -2
  18. package/lang/en.lyaml +25 -70
  19. package/lib/LocalDevManager.js +1 -22
  20. package/lib/__tests__/dependencyManagement.test.js +245 -0
  21. package/lib/__tests__/projectLogsManager.test.js +210 -0
  22. package/lib/dependencyManagement.js +157 -0
  23. package/lib/errorHandlers/apiErrors.js +1 -3
  24. package/lib/errorHandlers/overrideErrors.js +57 -36
  25. package/lib/localDev.js +1 -7
  26. package/lib/projectLogsManager.js +144 -0
  27. package/lib/projects.js +3 -6
  28. package/lib/projectsWatch.js +2 -5
  29. package/lib/prompts/__tests__/projectsLogsPrompt.test.js +46 -0
  30. package/lib/prompts/createProjectPrompt.js +4 -0
  31. package/lib/prompts/projectDevTargetAccountPrompt.js +16 -25
  32. package/lib/prompts/projectsLogsPrompt.js +17 -108
  33. package/lib/sandboxSync.js +13 -15
  34. package/package.json +6 -6
  35. package/commands/sandbox/sync.js +0 -225
@@ -0,0 +1,157 @@
1
+ const { logger } = require('@hubspot/local-dev-lib/logger');
2
+ const { getProjectConfig } = require('./projects');
3
+ const { exec: execAsync } = require('child_process');
4
+ const { walk } = require('@hubspot/local-dev-lib/fs');
5
+ const path = require('path');
6
+ const { uiLink } = require('./ui');
7
+ const util = require('util');
8
+ const { i18n } = require('./lang');
9
+ const SpinniesManager = require('./ui/SpinniesManager');
10
+ const fs = require('fs');
11
+
12
+ const DEFAULT_PACKAGE_MANAGER = 'npm';
13
+
14
+ const i18nKey = `commands.project.subcommands.installDeps`;
15
+
16
+ class NoPackageJsonFilesError extends Error {
17
+ constructor(projectName) {
18
+ super(
19
+ i18n(`${i18nKey}.noPackageJsonInProject`, {
20
+ projectName,
21
+ link: uiLink(
22
+ 'Learn how to create a project from scratch.',
23
+ 'https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project'
24
+ ),
25
+ })
26
+ );
27
+ }
28
+ }
29
+
30
+ async function isGloballyInstalled(command) {
31
+ const exec = util.promisify(execAsync);
32
+ try {
33
+ await exec(`${command} --version`);
34
+ return true;
35
+ } catch (e) {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async function installPackages({ packages, installLocations }) {
41
+ const installDirs =
42
+ installLocations || (await getProjectPackageJsonLocations());
43
+ await Promise.all(
44
+ installDirs.map(async dir => {
45
+ await installPackagesInDirectory(packages, dir);
46
+ })
47
+ );
48
+ }
49
+
50
+ async function installPackagesInDirectory(packages, directory) {
51
+ const spinner = `installingDependencies-${directory}`;
52
+ const relativeDir = path.relative(process.cwd(), directory);
53
+ SpinniesManager.init();
54
+ SpinniesManager.add(spinner, {
55
+ text:
56
+ packages && packages.length
57
+ ? i18n(`${i18nKey}.addingDependenciesToLocation`, {
58
+ dependencies: `[${packages.join(', ')}]`,
59
+ directory: relativeDir,
60
+ })
61
+ : i18n(`${i18nKey}.installingDependencies`, {
62
+ directory: relativeDir,
63
+ }),
64
+ });
65
+ let installCommand = `${DEFAULT_PACKAGE_MANAGER} --prefix=${directory} install`;
66
+
67
+ if (packages) {
68
+ installCommand = `${installCommand} ${packages.join(' ')}`;
69
+ }
70
+
71
+ logger.debug(`Running ${installCommand}`);
72
+ try {
73
+ const exec = util.promisify(execAsync);
74
+ await exec(installCommand);
75
+ SpinniesManager.succeed(spinner, {
76
+ text: i18n(`${i18nKey}.installationSuccessful`, {
77
+ directory: relativeDir,
78
+ }),
79
+ });
80
+ } catch (e) {
81
+ SpinniesManager.fail(spinner, {
82
+ text: i18n(`${i18nKey}.installingDependenciesFailed`, {
83
+ directory: relativeDir,
84
+ }),
85
+ });
86
+ throw new Error(
87
+ i18n(`${i18nKey}.installingDependenciesFailed`, {
88
+ directory: relativeDir,
89
+ }),
90
+ {
91
+ cause: e,
92
+ }
93
+ );
94
+ }
95
+ }
96
+
97
+ async function getProjectPackageJsonLocations() {
98
+ const projectConfig = await getProjectConfig();
99
+
100
+ if (
101
+ !projectConfig ||
102
+ !projectConfig.projectDir ||
103
+ !projectConfig.projectConfig
104
+ ) {
105
+ throw new Error(i18n(`${i18nKey}.noProjectConfig`));
106
+ }
107
+
108
+ const {
109
+ projectDir,
110
+ projectConfig: { srcDir, name },
111
+ } = projectConfig;
112
+
113
+ if (!(await isGloballyInstalled(DEFAULT_PACKAGE_MANAGER))) {
114
+ throw new Error(
115
+ i18n(`${i18nKey}.packageManagerNotInstalled`, {
116
+ packageManager: DEFAULT_PACKAGE_MANAGER,
117
+ link: uiLink(
118
+ DEFAULT_PACKAGE_MANAGER,
119
+ 'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm'
120
+ ),
121
+ })
122
+ );
123
+ }
124
+
125
+ if (
126
+ !fs.existsSync(projectConfig.projectDir) ||
127
+ !fs.existsSync(path.join(projectDir, srcDir))
128
+ ) {
129
+ throw new NoPackageJsonFilesError(name);
130
+ }
131
+
132
+ const packageJsonFiles = (await walk(path.join(projectDir, srcDir))).filter(
133
+ file =>
134
+ file.includes('package.json') &&
135
+ !file.includes('node_modules') &&
136
+ !file.includes('.vite')
137
+ );
138
+
139
+ if (packageJsonFiles.length === 0) {
140
+ throw new NoPackageJsonFilesError(name);
141
+ }
142
+
143
+ const packageParentDirs = [];
144
+ packageJsonFiles.forEach(packageJsonFile => {
145
+ const parentDir = path.dirname(packageJsonFile);
146
+ packageParentDirs.push(parentDir);
147
+ });
148
+
149
+ return packageParentDirs;
150
+ }
151
+
152
+ module.exports = {
153
+ isGloballyInstalled,
154
+ installPackages,
155
+ DEFAULT_PACKAGE_MANAGER,
156
+ getProjectPackageJsonLocations,
157
+ };
@@ -28,8 +28,6 @@ class ApiErrorContext extends ErrorContext {
28
28
  this.request = props.request || '';
29
29
  /** @type {string} */
30
30
  this.payload = props.payload || '';
31
- /** @type {string} */
32
- this.projectName = props.projectName || '';
33
31
  }
34
32
  }
35
33
 
@@ -54,7 +52,7 @@ function logValidationErrors(error, context) {
54
52
  */
55
53
  function logApiErrorInstance(error, context) {
56
54
  if (error.isAxiosError) {
57
- if (overrideErrors(error)) return;
55
+ if (overrideErrors(error, context)) return;
58
56
  const errorWithContext = getAxiosErrorWithContext(error, context);
59
57
  logger.error(errorWithContext.message);
60
58
  return;
@@ -1,38 +1,43 @@
1
- const { isSpecifiedError } = require('@hubspot/local-dev-lib/errors/apiErrors');
1
+ const {
2
+ isSpecifiedError,
3
+ isMissingScopeError,
4
+ } = require('@hubspot/local-dev-lib/errors/apiErrors');
2
5
  const { logger } = require('@hubspot/local-dev-lib/logger');
3
-
4
6
  const { PLATFORM_VERSION_ERROR_TYPES } = require('../constants');
5
7
  const { i18n } = require('../lang');
6
- const { uiLine, uiLink } = require('../ui');
8
+ const {
9
+ uiAccountDescription,
10
+ uiLine,
11
+ uiLink,
12
+ uiCommandReference,
13
+ } = require('../ui');
7
14
 
8
15
  const i18nKey = 'lib.errorHandlers.overrideErrors';
9
16
 
10
- function createPlatformVersionError(subCategory, errData) {
11
- const docsLink = uiLink(
12
- i18n(`${i18nKey}.platformVersionErrors.docsLink`),
13
- 'https://developers.hubspot.com/docs/platform/platform-versioning'
14
- );
15
-
16
- const platformVersionKey = {
17
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED]:
18
- 'unspecified platformVersion',
19
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
20
- errData.context.RETIRED_PLATFORM_VERSION,
21
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST]:
22
- errData.context.PLATFORM_VERSION,
23
- };
17
+ function createPlatformVersionError(err, subCategory) {
18
+ let translationKey = 'unspecifiedPlatformVersion';
19
+ let platformVersion = 'unspecified platformVersion';
20
+ const errorContext =
21
+ err.response && err.response.data && err.response.data.context;
24
22
 
25
- const errorTypeToTranslationKey = {
26
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED]:
27
- 'unspecifiedPlatformVersion',
28
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
29
- 'platformVersionRetired',
30
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST]:
31
- 'nonExistentPlatformVersion',
32
- };
33
-
34
- const platformVersion = platformVersionKey[subCategory] || '';
35
- const translationKey = errorTypeToTranslationKey[subCategory];
23
+ switch (subCategory) {
24
+ case [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
25
+ translationKey = 'platformVersionRetired';
26
+ if (errorContext && errorContext[subCategory]) {
27
+ platformVersion = errorContext[subCategory];
28
+ }
29
+ break;
30
+ case [
31
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST,
32
+ ]:
33
+ translationKey = 'nonExistentPlatformVersion';
34
+ if (errorContext && errorContext[subCategory]) {
35
+ platformVersion = errorContext[subCategory];
36
+ }
37
+ break;
38
+ default:
39
+ break;
40
+ }
36
41
 
37
42
  uiLine();
38
43
  logger.error(i18n(`${i18nKey}.platformVersionErrors.header`));
@@ -44,21 +49,37 @@ function createPlatformVersionError(subCategory, errData) {
44
49
  logger.log(i18n(`${i18nKey}.platformVersionErrors.updateProject`));
45
50
  logger.log(
46
51
  i18n(`${i18nKey}.platformVersionErrors.betaLink`, {
47
- docsLink,
52
+ docsLink: uiLink(
53
+ i18n(`${i18nKey}.platformVersionErrors.docsLink`),
54
+ 'https://developers.hubspot.com/docs/platform/platform-versioning'
55
+ ),
48
56
  })
49
57
  );
50
58
  uiLine();
51
59
  }
52
60
 
53
- function overrideErrors(err) {
61
+ function overrideErrors(err, context) {
62
+ if (isMissingScopeError(err)) {
63
+ logger.error(
64
+ i18n(`${i18nKey}.missingScopeError`, {
65
+ accountName: context.accountId
66
+ ? uiAccountDescription(context.accountId)
67
+ : '',
68
+ request: context.request || 'request',
69
+ authCommand: uiCommandReference('hs auth'),
70
+ })
71
+ );
72
+ return true;
73
+ }
74
+
54
75
  if (
55
76
  isSpecifiedError(err, {
56
77
  subCategory: PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED,
57
78
  })
58
79
  ) {
59
80
  createPlatformVersionError(
60
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED,
61
- err.response.data
81
+ err,
82
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED
62
83
  );
63
84
  return true;
64
85
  }
@@ -69,8 +90,8 @@ function overrideErrors(err) {
69
90
  })
70
91
  ) {
71
92
  createPlatformVersionError(
72
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED,
73
- err.response.data
93
+ err,
94
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED
74
95
  );
75
96
  return true;
76
97
  }
@@ -82,8 +103,8 @@ function overrideErrors(err) {
82
103
  })
83
104
  ) {
84
105
  createPlatformVersionError(
85
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST,
86
- err.response.data
106
+ err,
107
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST
87
108
  );
88
109
  return true;
89
110
  }
package/lib/localDev.js CHANGED
@@ -129,13 +129,6 @@ const suggestRecommendedNestedAccount = async (
129
129
  `${i18nKey}.validateAccountOption.publicAppNonDeveloperTestAccountWarning`
130
130
  )
131
131
  );
132
- } else if (isAppDeveloperAccount(accountConfig)) {
133
- logger.error(
134
- i18n(
135
- `${i18nKey}.validateAccountOption.privateAppInAppDeveloperAccountError`
136
- )
137
- );
138
- process.exit(EXIT_CODES.ERROR);
139
132
  } else {
140
133
  logger.log(i18n(`${i18nKey}.validateAccountOption.nonSandboxWarning`));
141
134
  }
@@ -202,6 +195,7 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
202
195
  accountConfig,
203
196
  sandboxAccountConfig
204
197
  );
198
+ // For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
205
199
  await syncSandbox({
206
200
  accountConfig: sandboxAccountConfig,
207
201
  parentAccountConfig: accountConfig,
@@ -0,0 +1,144 @@
1
+ const { getProjectConfig, ensureProjectExists } = require('./projects');
2
+ const {
3
+ fetchProjectComponentsMetadata,
4
+ } = require('@hubspot/local-dev-lib/api/projects');
5
+ const { i18n } = require('./lang');
6
+ const { uiLink } = require('./ui');
7
+
8
+ const i18nKey = 'commands.project.subcommands.logs';
9
+
10
+ class ProjectLogsManager {
11
+ reset() {
12
+ Object.keys(this).forEach(key => {
13
+ if (Object.hasOwn(this, key)) {
14
+ this[key] = undefined;
15
+ }
16
+ });
17
+ }
18
+
19
+ async init(accountId) {
20
+ const { projectConfig } = await getProjectConfig();
21
+
22
+ if (!projectConfig || !projectConfig.name) {
23
+ throw new Error(i18n(`${i18nKey}.errors.noProjectConfig`));
24
+ }
25
+
26
+ const { name: projectName } = projectConfig;
27
+
28
+ this.projectName = projectName;
29
+ this.accountId = accountId;
30
+ this.functions = [];
31
+
32
+ const { project } = await ensureProjectExists(
33
+ this.accountId,
34
+ this.projectName,
35
+ {
36
+ allowCreate: false,
37
+ }
38
+ );
39
+
40
+ if (
41
+ !project ||
42
+ !project.deployedBuild ||
43
+ !project.deployedBuild.subbuildStatuses
44
+ ) {
45
+ throw new Error(i18n(`${i18nKey}.errors.failedToFetchProjectDetails`));
46
+ }
47
+
48
+ this.projectId = project.id;
49
+ await this.fetchFunctionDetails();
50
+ }
51
+
52
+ async fetchFunctionDetails() {
53
+ if (!this.projectId) {
54
+ throw new Error(i18n(`${i18nKey}.errors.noProjectConfig`));
55
+ }
56
+
57
+ const { topLevelComponentMetadata } = await fetchProjectComponentsMetadata(
58
+ this.accountId,
59
+ this.projectId
60
+ );
61
+
62
+ const apps = topLevelComponentMetadata.filter(componentMetadata => {
63
+ const { type } = componentMetadata;
64
+ return type && type.name === 'PRIVATE_APP';
65
+ });
66
+
67
+ if (!this.functions) {
68
+ this.functions = [];
69
+ }
70
+
71
+ apps.forEach(app => {
72
+ this.functions.push(
73
+ ...app.featureComponents.filter(
74
+ component => component.type.name === 'APP_FUNCTION'
75
+ )
76
+ );
77
+ });
78
+
79
+ if (this.functions.length === 0) {
80
+ throw new Error(
81
+ i18n(`${i18nKey}.errors.noFunctionsInProject`, {
82
+ link: uiLink(
83
+ i18n(`${i18nKey}.errors.noFunctionsLinkText`),
84
+ 'https://developers.hubspot.com/docs/platform/serverless-functions'
85
+ ),
86
+ })
87
+ );
88
+ }
89
+ }
90
+
91
+ getFunctionNames() {
92
+ if (!this.functions) {
93
+ return [];
94
+ }
95
+ return this.functions.map(
96
+ serverlessFunction => serverlessFunction.componentName
97
+ );
98
+ }
99
+
100
+ setFunction(functionName) {
101
+ if (!this.functions) {
102
+ throw new Error(
103
+ i18n(`${i18nKey}.errors.noFunctionsInProject`, {
104
+ link: uiLink(
105
+ i18n(`${i18nKey}.errors.noFunctionsLinkText`),
106
+ 'https://developers.hubspot.com/docs/platform/serverless-functions'
107
+ ),
108
+ }),
109
+ {
110
+ projectName: this.projectName,
111
+ }
112
+ );
113
+ }
114
+
115
+ this.selectedFunction = this.functions.find(
116
+ serverlessFunction => serverlessFunction.componentName === functionName
117
+ );
118
+
119
+ if (!this.selectedFunction) {
120
+ throw new Error(
121
+ i18n(`${i18nKey}.errors.noFunctionWithName`, { name: functionName })
122
+ );
123
+ }
124
+
125
+ this.functionName = functionName;
126
+
127
+ if (!this.selectedFunction.deployOutput) {
128
+ throw new Error(
129
+ i18n(`${i18nKey}.errors.functionNotDeployed`, { name: functionName })
130
+ );
131
+ }
132
+ this.appId = this.selectedFunction.deployOutput.appId;
133
+
134
+ if (this.selectedFunction.deployOutput.endpoint) {
135
+ this.endpointName = this.selectedFunction.deployOutput.endpoint.path;
136
+ this.method = this.selectedFunction.deployOutput.endpoint.method;
137
+ this.isPublicFunction = true;
138
+ } else {
139
+ this.isPublicFunction = false;
140
+ }
141
+ }
142
+ }
143
+
144
+ module.exports = new ProjectLogsManager();
package/lib/projects.js CHANGED
@@ -287,10 +287,7 @@ const ensureProjectExists = async (
287
287
  );
288
288
  return { projectExists: true, project };
289
289
  } catch (err) {
290
- return logApiErrorInstance(
291
- err,
292
- new ApiErrorContext({ accountId, projectName })
293
- );
290
+ return logApiErrorInstance(err, new ApiErrorContext({ accountId }));
294
291
  }
295
292
  } else {
296
293
  if (!noLogs) {
@@ -312,7 +309,7 @@ const ensureProjectExists = async (
312
309
  logger.error(err.message);
313
310
  process.exit(EXIT_CODES.ERROR);
314
311
  }
315
- logApiErrorInstance(err, new ApiErrorContext({ accountId, projectName }));
312
+ logApiErrorInstance(err, new ApiErrorContext({ accountId }));
316
313
  process.exit(EXIT_CODES.ERROR);
317
314
  }
318
315
  };
@@ -572,7 +569,7 @@ const handleProjectUpload = async (
572
569
  buildId
573
570
  );
574
571
  }
575
- resolve(uploadResult);
572
+ resolve(uploadResult || {});
576
573
  })
577
574
  );
578
575
 
@@ -85,10 +85,7 @@ const debounceQueueBuild = (accountId, projectName, platformVersion) => {
85
85
  logger.log(i18n(`${i18nKey}.logs.watchCancelledFromUi`));
86
86
  process.exit(0);
87
87
  } else {
88
- logApiErrorInstance(
89
- err,
90
- new ApiErrorContext({ accountId, projectName })
91
- );
88
+ logApiErrorInstance(err, new ApiErrorContext({ accountId }));
92
89
  }
93
90
 
94
91
  return;
@@ -158,7 +155,7 @@ const createNewBuild = async (accountId, projectName, platformVersion) => {
158
155
  );
159
156
  return buildId;
160
157
  } catch (err) {
161
- logApiErrorInstance(err, new ApiErrorContext({ accountId, projectName }));
158
+ logApiErrorInstance(err, new ApiErrorContext({ accountId }));
162
159
  if (
163
160
  isSpecifiedError(err, { subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED })
164
161
  ) {
@@ -0,0 +1,46 @@
1
+ const { projectLogsPrompt } = require('../projectsLogsPrompt');
2
+
3
+ jest.mock('../promptUtils');
4
+ const { promptUser } = require('../promptUtils');
5
+ const chalk = require('chalk');
6
+
7
+ describe('prompts/projectsLogsPrompt', () => {
8
+ it('should return undefined functionName when functionChoices is nullable', async () => {
9
+ const actual = await projectLogsPrompt({ functionChoices: null });
10
+ expect(actual).toEqual({});
11
+ expect(promptUser).not.toHaveBeenCalled();
12
+ });
13
+
14
+ it('should return the functionName without prompting when there is only one functionChoice', async () => {
15
+ const functionChoice = 'this-is-the-only-function';
16
+ const { functionName } = await projectLogsPrompt({
17
+ functionChoices: [functionChoice],
18
+ });
19
+ expect(functionName).toEqual(functionChoice);
20
+ expect(promptUser).not.toHaveBeenCalled();
21
+ });
22
+
23
+ it('should prompt the user if there is more than one choice', async () => {
24
+ const functionChoices = ['choice 1', 'choice 2'];
25
+ const projectName = 'my cool project';
26
+ await projectLogsPrompt({
27
+ functionChoices,
28
+ projectName,
29
+ });
30
+
31
+ expect(promptUser).toHaveBeenCalledTimes(1);
32
+ expect(promptUser).toHaveBeenLastCalledWith(
33
+ expect.arrayContaining([
34
+ expect.objectContaining({
35
+ name: 'functionName',
36
+ type: 'list',
37
+ message: `[--function] Select function in ${chalk.bold(
38
+ projectName
39
+ )} project`,
40
+ when: expect.any(Function),
41
+ choices: functionChoices,
42
+ }),
43
+ ])
44
+ );
45
+ });
46
+ });
@@ -4,6 +4,7 @@ const {
4
4
  getCwd,
5
5
  sanitizeFileName,
6
6
  isValidPath,
7
+ untildify,
7
8
  } = require('@hubspot/local-dev-lib/path');
8
9
  const { PROJECT_COMPONENT_TYPES } = require('../../lib/constants');
9
10
  const { promptUser } = require('./promptUtils');
@@ -100,6 +101,9 @@ const createProjectPrompt = async (
100
101
  }
101
102
  return true;
102
103
  },
104
+ filter: input => {
105
+ return untildify(input);
106
+ },
103
107
  },
104
108
  {
105
109
  name: 'template',
@@ -1,7 +1,7 @@
1
1
  const { promptUser } = require('./promptUtils');
2
2
  const { i18n } = require('../lang');
3
3
  const { uiAccountDescription, uiCommandReference } = require('../ui');
4
- const { isSandbox, isDeveloperTestAccount } = require('../accountTypes');
4
+ const { isSandbox } = require('../accountTypes');
5
5
  const { getAccountId } = require('@hubspot/local-dev-lib/config');
6
6
  const { getSandboxUsageLimits } = require('@hubspot/local-dev-lib/sandboxes');
7
7
  const {
@@ -99,7 +99,6 @@ const selectDeveloperTestTargetAccountPrompt = async (
99
99
  defaultAccountConfig
100
100
  ) => {
101
101
  const defaultAccountId = getAccountId(defaultAccountConfig.name);
102
- let choices = [];
103
102
  let devTestAccountsResponse = undefined;
104
103
  try {
105
104
  devTestAccountsResponse = await fetchDeveloperTestAccounts(
@@ -109,13 +108,6 @@ const selectDeveloperTestTargetAccountPrompt = async (
109
108
  logger.debug('Unable to fetch developer test account usage limits: ', err);
110
109
  }
111
110
 
112
- const devTestAccounts = accounts
113
- .reverse()
114
- .filter(
115
- config =>
116
- isDeveloperTestAccount(config) &&
117
- config.parentAccountId === defaultAccountId
118
- );
119
111
  let disabledMessage = false;
120
112
  if (
121
113
  devTestAccountsResponse &&
@@ -128,27 +120,26 @@ const selectDeveloperTestTargetAccountPrompt = async (
128
120
  });
129
121
  }
130
122
 
131
- let devTestAccountsNotInConfig = [];
123
+ const devTestAccounts = [];
132
124
  if (devTestAccountsResponse && devTestAccountsResponse.results) {
133
- const inConfigIds = devTestAccounts.map(d => d.portalId);
125
+ const accountIds = accounts.map(account => account.portalId);
126
+
134
127
  devTestAccountsResponse.results.forEach(acct => {
135
- if (inConfigIds.indexOf(acct.id) < 0) {
136
- devTestAccountsNotInConfig.push({
137
- name: getNonConfigDeveloperTestAccountName(acct),
138
- value: {
139
- targetAccountId: acct.id,
140
- createdNestedAccount: false,
141
- parentAccountId: defaultAccountId,
142
- notInConfigAccount: acct,
143
- },
144
- });
145
- }
128
+ const inConfig = accountIds.includes(acct.id);
129
+ devTestAccounts.push({
130
+ name: getNonConfigDeveloperTestAccountName(acct),
131
+ value: {
132
+ targetAccountId: acct.id,
133
+ createdNestedAccount: false,
134
+ parentAccountId: defaultAccountId,
135
+ notInConfigAccount: inConfig ? null : acct,
136
+ },
137
+ });
146
138
  });
147
139
  }
148
140
 
149
- choices = [
150
- ...devTestAccounts.map(mapNestedAccount),
151
- ...devTestAccountsNotInConfig,
141
+ const choices = [
142
+ ...devTestAccounts,
152
143
  {
153
144
  name: i18n(`${i18nKey}.createNewDeveloperTestAccountOption`),
154
145
  value: {