@hubspot/cli 7.10.0 → 7.10.1-experimental.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 (199) hide show
  1. package/bin/cli.js +5 -4
  2. package/commands/__tests__/getStarted.test.js +10 -0
  3. package/commands/__tests__/project.test.js +3 -0
  4. package/commands/account/__tests__/rename.test.js +10 -3
  5. package/commands/account/auth.js +10 -14
  6. package/commands/account/clean.js +11 -19
  7. package/commands/account/createOverride.js +15 -11
  8. package/commands/account/info.js +8 -5
  9. package/commands/account/list.js +15 -19
  10. package/commands/account/remove.js +23 -22
  11. package/commands/account/removeOverride.js +6 -6
  12. package/commands/account/rename.js +2 -2
  13. package/commands/account/use.js +19 -8
  14. package/commands/app/__tests__/migrate.test.js +8 -4
  15. package/commands/app/migrate.js +2 -2
  16. package/commands/auth.js +18 -14
  17. package/commands/config/migrate.js +5 -5
  18. package/commands/customObject/createSchema.js +2 -3
  19. package/commands/customObject/updateSchema.js +2 -3
  20. package/commands/getStarted.js +2 -3
  21. package/commands/hubdb/__tests__/list.test.js +1 -0
  22. package/commands/hubdb/list.js +2 -2
  23. package/commands/init.js +36 -32
  24. package/commands/project/__tests__/deploy.test.js +10 -5
  25. package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
  26. package/commands/project/__tests__/lint.test.js +709 -0
  27. package/commands/project/__tests__/logs.test.js +4 -0
  28. package/commands/project/__tests__/validate.test.js +286 -28
  29. package/commands/project/cloneApp.js +2 -2
  30. package/commands/project/deploy.js +16 -8
  31. package/commands/project/dev/deprecatedFlow.js +4 -5
  32. package/commands/project/dev/index.js +19 -7
  33. package/commands/project/dev/unifiedFlow.js +4 -5
  34. package/commands/project/lint.d.ts +6 -0
  35. package/commands/project/lint.js +178 -0
  36. package/commands/project/logs.js +2 -3
  37. package/commands/project/migrate.js +4 -13
  38. package/commands/project/profile/add.js +6 -7
  39. package/commands/project/profile/delete.js +2 -2
  40. package/commands/project/upload.js +10 -4
  41. package/commands/project/validate.js +73 -13
  42. package/commands/project.js +2 -0
  43. package/commands/sandbox/__tests__/create.test.js +14 -5
  44. package/commands/sandbox/create.js +4 -5
  45. package/commands/sandbox/delete.js +23 -20
  46. package/commands/testAccount/create.js +2 -2
  47. package/commands/testAccount/delete.js +9 -8
  48. package/lang/en.d.ts +48 -11
  49. package/lang/en.js +58 -15
  50. package/lib/__tests__/buildAccount.test.js +22 -30
  51. package/lib/__tests__/commonOpts.test.js +9 -13
  52. package/lib/__tests__/developerTestAccounts.test.js +29 -17
  53. package/lib/__tests__/importData.test.js +20 -10
  54. package/lib/__tests__/oauth.test.js +19 -8
  55. package/lib/__tests__/projectProfiles.test.js +273 -32
  56. package/lib/__tests__/sandboxSync.test.js +33 -11
  57. package/lib/__tests__/sandboxes.test.js +30 -19
  58. package/lib/__tests__/usageTracking.test.js +10 -10
  59. package/lib/__tests__/validation.test.js +32 -32
  60. package/lib/accountTypes.d.ts +9 -9
  61. package/lib/accountTypes.js +2 -4
  62. package/lib/app/__tests__/migrate.test.js +15 -0
  63. package/lib/app/__tests__/migrate_legacy.test.js +9 -0
  64. package/lib/app/migrate_legacy.d.ts +2 -2
  65. package/lib/buildAccount.d.ts +4 -4
  66. package/lib/buildAccount.js +7 -14
  67. package/lib/commonOpts.js +3 -3
  68. package/lib/configMigrate.d.ts +2 -2
  69. package/lib/configMigrate.js +42 -18
  70. package/lib/configOptions.js +3 -2
  71. package/lib/developerTestAccounts.d.ts +3 -3
  72. package/lib/developerTestAccounts.js +4 -7
  73. package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
  74. package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
  75. package/lib/doctor/Doctor.js +4 -3
  76. package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
  77. package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
  78. package/lib/doctor/__tests__/Doctor.test.js +14 -0
  79. package/lib/importData.js +8 -7
  80. package/lib/links.js +5 -5
  81. package/lib/middleware/{__test__ → __tests__}/commandTargetingUtils.test.js +3 -3
  82. package/lib/middleware/{__test__ → __tests__}/configMiddleware.test.js +23 -22
  83. package/lib/middleware/{__test__ → __tests__}/gitMiddleware.test.js +9 -7
  84. package/lib/middleware/autoUpdateMiddleware.js +34 -23
  85. package/lib/middleware/commandTargetingUtils.js +3 -2
  86. package/lib/middleware/configMiddleware.d.ts +6 -1
  87. package/lib/middleware/configMiddleware.js +36 -15
  88. package/lib/middleware/fireAlarmMiddleware.js +4 -15
  89. package/lib/middleware/gitMiddleware.js +8 -4
  90. package/lib/oauth.d.ts +2 -2
  91. package/lib/oauth.js +8 -10
  92. package/lib/projectProfiles.d.ts +4 -3
  93. package/lib/projectProfiles.js +78 -32
  94. package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
  95. package/lib/projects/__tests__/DevServerManager.test.js +1 -0
  96. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  97. package/lib/projects/__tests__/deploy.test.js +1 -0
  98. package/lib/projects/__tests__/uieLinting.test.js +640 -0
  99. package/lib/projects/create/__tests__/v2.test.js +11 -0
  100. package/lib/projects/localDev/AppDevModeInterface.js +2 -2
  101. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  102. package/lib/projects/localDev/LocalDevLogger.js +4 -4
  103. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
  104. package/lib/projects/localDev/helpers/account.d.ts +10 -10
  105. package/lib/projects/localDev/helpers/account.js +6 -11
  106. package/lib/projects/uieLinting.d.ts +33 -0
  107. package/lib/projects/uieLinting.js +222 -0
  108. package/lib/projects/urls.js +5 -6
  109. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
  110. package/lib/prompts/accountNamePrompt.js +3 -3
  111. package/lib/prompts/accountsPrompt.d.ts +1 -1
  112. package/lib/prompts/accountsPrompt.js +6 -7
  113. package/lib/prompts/confirmImportDataPrompt.js +2 -2
  114. package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
  115. package/lib/prompts/downloadProjectPrompt.js +5 -2
  116. package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
  117. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  118. package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
  119. package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
  120. package/lib/prompts/sandboxesPrompt.js +7 -8
  121. package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
  122. package/lib/sandboxSync.d.ts +2 -2
  123. package/lib/sandboxSync.js +3 -9
  124. package/lib/sandboxes.d.ts +4 -4
  125. package/lib/sandboxes.js +6 -11
  126. package/lib/serverlessLogs.js +2 -2
  127. package/lib/theme/__tests__/migrate.test.js +15 -0
  128. package/lib/ui/index.js +6 -3
  129. package/lib/usageTracking.js +15 -8
  130. package/lib/validation.js +13 -11
  131. package/mcp-server/tools/cms/HsCreateFunctionTool.js +4 -2
  132. package/mcp-server/tools/cms/HsCreateModuleTool.js +4 -2
  133. package/mcp-server/tools/cms/HsCreateTemplateTool.js +4 -2
  134. package/mcp-server/tools/cms/HsFunctionLogsTool.js +4 -2
  135. package/mcp-server/tools/cms/HsListFunctionsTool.js +3 -1
  136. package/mcp-server/tools/cms/HsListTool.js +3 -1
  137. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
  138. package/mcp-server/tools/index.js +4 -0
  139. package/mcp-server/tools/project/AddFeatureToProjectTool.js +4 -2
  140. package/mcp-server/tools/project/CreateProjectTool.js +4 -2
  141. package/mcp-server/tools/project/CreateTestAccountTool.js +17 -7
  142. package/mcp-server/tools/project/DeployProjectTool.js +3 -1
  143. package/mcp-server/tools/project/DocFetchTool.js +6 -4
  144. package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
  145. package/mcp-server/tools/project/DocsSearchTool.js +10 -8
  146. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
  147. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -7
  148. package/mcp-server/tools/project/GetApplicationInfoTool.js +8 -6
  149. package/mcp-server/tools/project/GetBuildLogsTool.d.ts +26 -0
  150. package/mcp-server/tools/project/GetBuildLogsTool.js +125 -0
  151. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
  152. package/mcp-server/tools/project/GetBuildStatusTool.js +166 -0
  153. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
  154. package/mcp-server/tools/project/GetConfigValuesTool.js +9 -7
  155. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
  156. package/mcp-server/tools/project/GuidedWalkthroughTool.js +5 -3
  157. package/mcp-server/tools/project/UploadProjectTools.js +3 -1
  158. package/mcp-server/tools/project/ValidateProjectTool.js +4 -2
  159. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +12 -2
  160. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
  161. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +23 -11
  162. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
  163. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
  164. package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.d.ts +1 -0
  165. package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.js +305 -0
  166. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
  167. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
  168. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
  169. package/mcp-server/utils/__tests__/content.test.js +21 -20
  170. package/mcp-server/utils/__tests__/feedbackTracking.test.js +34 -28
  171. package/mcp-server/utils/config.d.ts +1 -0
  172. package/mcp-server/utils/config.js +10 -0
  173. package/mcp-server/utils/content.d.ts +1 -1
  174. package/mcp-server/utils/content.js +2 -2
  175. package/mcp-server/utils/feedbackTracking.d.ts +1 -1
  176. package/mcp-server/utils/feedbackTracking.js +3 -3
  177. package/mcp-server/utils/toolUsageTracking.js +4 -3
  178. package/package.json +9 -9
  179. package/ui/components/BoxWithTitle.d.ts +2 -1
  180. package/ui/components/BoxWithTitle.js +2 -2
  181. package/ui/components/StatusMessageBoxes.d.ts +5 -4
  182. package/ui/components/StatusMessageBoxes.js +8 -8
  183. package/lib/middleware/__test__/notificationsMiddleware.test.js +0 -8
  184. package/lib/middleware/notificationsMiddleware.d.ts +0 -1
  185. package/lib/middleware/notificationsMiddleware.js +0 -28
  186. package/lib/ui/boxen.d.ts +0 -5
  187. package/lib/ui/boxen.js +0 -26
  188. package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
  189. package/mcp-server/utils/cliConfig.d.ts +0 -1
  190. package/mcp-server/utils/cliConfig.js +0 -12
  191. /package/{lib/middleware/__test__/commandTargetingUtils.test.d.ts → commands/project/__tests__/lint.test.d.ts} +0 -0
  192. /package/lib/middleware/{__test__/configMiddleware.test.d.ts → __tests__/commandTargetingUtils.test.d.ts} +0 -0
  193. /package/lib/middleware/{__test__/gitMiddleware.test.d.ts → __tests__/configMiddleware.test.d.ts} +0 -0
  194. /package/lib/middleware/{__test__/notificationsMiddleware.test.d.ts → __tests__/gitMiddleware.test.d.ts} +0 -0
  195. /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.d.ts +0 -0
  196. /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.js +0 -0
  197. /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.d.ts +0 -0
  198. /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.js +0 -0
  199. /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → lib/projects/__tests__/uieLinting.test.d.ts} +0 -0
@@ -0,0 +1,178 @@
1
+ import path from 'path';
2
+ import { getProjectPackageJsonLocations, installPackages, } from '../../lib/dependencyManagement.js';
3
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
4
+ import { getProjectConfig } from '../../lib/projects/config.js';
5
+ import { commands } from '../../lang/en.js';
6
+ import { uiLogger } from '../../lib/ui/logger.js';
7
+ import { trackCommandUsage } from '../../lib/usageTracking.js';
8
+ import { logError } from '../../lib/errorHandlers/index.js';
9
+ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
10
+ import { promptUser } from '../../lib/prompts/promptUtils.js';
11
+ import SpinniesManager from '../../lib/ui/SpinniesManager.js';
12
+ import { areAllLintPackagesInstalled, getMissingLintPackages, lintPackages, displayLintResults, hasEslintConfig, hasDeprecatedEslintConfig, getDeprecatedEslintConfigFiles, createEslintConfig, REQUIRED_PACKAGES_AND_MIN_VERSIONS, } from '../../lib/projects/uieLinting.js';
13
+ const command = 'lint';
14
+ const describe = commands.project.lint.help.describe;
15
+ async function handler(args) {
16
+ const { derivedAccountId, installMissingDeps } = args;
17
+ try {
18
+ trackCommandUsage('project-lint', undefined, derivedAccountId);
19
+ const projectConfig = await getProjectConfig();
20
+ if (!projectConfig || !projectConfig.projectDir) {
21
+ uiLogger.error(commands.project.lint.noProjectConfig);
22
+ return process.exit(EXIT_CODES.ERROR);
23
+ }
24
+ SpinniesManager.init({ succeedColor: 'white' });
25
+ SpinniesManager.add('lintCheck', {
26
+ text: commands.project.lint.loading.checking,
27
+ });
28
+ const lintLocations = await getProjectPackageJsonLocations();
29
+ const locationsReadyToLint = [];
30
+ const locationsNeedingPackages = new Map();
31
+ for (const lintLocation of lintLocations) {
32
+ if (areAllLintPackagesInstalled(lintLocation)) {
33
+ locationsReadyToLint.push(lintLocation);
34
+ }
35
+ else {
36
+ const { missingPackages } = getMissingLintPackages(lintLocation);
37
+ if (missingPackages.length > 0) {
38
+ locationsNeedingPackages.set(lintLocation, missingPackages);
39
+ }
40
+ }
41
+ }
42
+ SpinniesManager.succeed('lintCheck');
43
+ if (locationsNeedingPackages.size > 0) {
44
+ const locationsArray = Array.from(locationsNeedingPackages.keys());
45
+ const relativeLocations = locationsArray.map(loc => path.relative(projectConfig.projectDir, loc));
46
+ const allMissingPackages = [];
47
+ for (const packages of locationsNeedingPackages.values()) {
48
+ allMissingPackages.push(...packages);
49
+ }
50
+ let shouldInstallPackages;
51
+ if (installMissingDeps !== undefined) {
52
+ shouldInstallPackages = installMissingDeps;
53
+ }
54
+ else {
55
+ const promptResult = await promptUser([
56
+ {
57
+ name: 'shouldInstallPackages',
58
+ type: 'confirm',
59
+ message: commands.project.lint.installLintPackagesPrompt(relativeLocations, allMissingPackages),
60
+ default: true,
61
+ },
62
+ ]);
63
+ shouldInstallPackages = promptResult.shouldInstallPackages;
64
+ }
65
+ if (shouldInstallPackages) {
66
+ const packagesToInstall = Object.entries(REQUIRED_PACKAGES_AND_MIN_VERSIONS).map(([pkg, version]) => `${pkg}@^${version}`);
67
+ await installPackages({
68
+ packages: packagesToInstall,
69
+ installLocations: locationsArray,
70
+ dev: true,
71
+ });
72
+ // Re-check which locations are now ready
73
+ for (const location of locationsArray) {
74
+ if (areAllLintPackagesInstalled(location)) {
75
+ locationsReadyToLint.push(location);
76
+ }
77
+ }
78
+ }
79
+ else {
80
+ uiLogger.warn(commands.project.lint.skippingDirectoriesWarning(relativeLocations));
81
+ }
82
+ }
83
+ if (locationsReadyToLint.length > 0) {
84
+ // Check for config files and handle deprecated configs
85
+ const deprecatedConfigDetails = [];
86
+ const locationsNeedingConfig = [];
87
+ for (const location of locationsReadyToLint) {
88
+ const hasModernConfig = hasEslintConfig(location);
89
+ const hasDeprecatedConfig = hasDeprecatedEslintConfig(location);
90
+ if (hasDeprecatedConfig) {
91
+ const relativePath = path.relative(projectConfig.projectDir, location);
92
+ const deprecatedFiles = getDeprecatedEslintConfigFiles(location);
93
+ deprecatedConfigDetails.push({
94
+ path: relativePath,
95
+ files: deprecatedFiles,
96
+ });
97
+ }
98
+ if (!hasModernConfig) {
99
+ locationsNeedingConfig.push(location);
100
+ }
101
+ }
102
+ if (deprecatedConfigDetails.length > 0) {
103
+ uiLogger.log('');
104
+ uiLogger.warn(commands.project.lint.deprecatedEslintConfigWarning(deprecatedConfigDetails));
105
+ }
106
+ if (locationsNeedingConfig.length > 0) {
107
+ const relativeLocations = locationsNeedingConfig.map(loc => path.relative(projectConfig.projectDir, loc));
108
+ const { shouldCreateConfig } = await promptUser([
109
+ {
110
+ name: 'shouldCreateConfig',
111
+ type: 'confirm',
112
+ message: commands.project.lint.createEslintConfigPrompt(relativeLocations),
113
+ default: true,
114
+ },
115
+ ]);
116
+ if (shouldCreateConfig) {
117
+ SpinniesManager.add('lintConfigCreate', {
118
+ text: commands.project.lint.loading.creatingConfig,
119
+ });
120
+ const createdConfigs = [];
121
+ for (const location of locationsNeedingConfig) {
122
+ const configPath = createEslintConfig(location);
123
+ createdConfigs.push(configPath);
124
+ }
125
+ SpinniesManager.succeed('lintConfigCreate');
126
+ createdConfigs.forEach(configPath => {
127
+ uiLogger.success(commands.project.lint.eslintConfigCreated(configPath));
128
+ });
129
+ }
130
+ else {
131
+ uiLogger.error(commands.project.lint.eslintConfigRequired);
132
+ return process.exit(EXIT_CODES.ERROR);
133
+ }
134
+ }
135
+ SpinniesManager.add('lintRun', {
136
+ text: commands.project.lint.loading.linting,
137
+ });
138
+ const { success, results } = await lintPackages(locationsReadyToLint, projectConfig.projectDir);
139
+ SpinniesManager.succeed('lintRun');
140
+ displayLintResults(results);
141
+ if (!success) {
142
+ return process.exit(EXIT_CODES.ERROR);
143
+ }
144
+ }
145
+ }
146
+ catch (e) {
147
+ logError(e);
148
+ return process.exit(EXIT_CODES.ERROR);
149
+ }
150
+ }
151
+ function projectLintBuilder(yargs) {
152
+ yargs.example([
153
+ ['$0 project lint', commands.project.lint.help.lintProjectExample],
154
+ [
155
+ '$0 project lint --install-missing-deps',
156
+ commands.project.lint.help.lintProjectWithInstallExample,
157
+ ],
158
+ [
159
+ '$0 project lint --install-missing-deps=false',
160
+ commands.project.lint.help.lintProjectWithoutInstallExample,
161
+ ],
162
+ ]);
163
+ yargs.option('install-missing-deps', {
164
+ type: 'boolean',
165
+ describe: commands.project.lint.help.installMissingDeps,
166
+ });
167
+ return yargs;
168
+ }
169
+ const builder = makeYargsBuilder(projectLintBuilder, command, describe, {
170
+ useGlobalOptions: true,
171
+ });
172
+ const projectLintCommand = {
173
+ command,
174
+ describe,
175
+ handler,
176
+ builder,
177
+ };
178
+ export default projectLintCommand;
@@ -1,6 +1,5 @@
1
- import { getEnv } from '@hubspot/local-dev-lib/config';
1
+ import { getConfigAccountEnvironment } from '@hubspot/local-dev-lib/config';
2
2
  import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
3
- import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
4
3
  import { trackCommandUsage } from '../../lib/usageTracking.js';
5
4
  import { getTableContents, getTableHeader } from '../../lib/ui/table.js';
6
5
  import { logError } from '../../lib/errorHandlers/index.js';
@@ -12,7 +11,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
12
11
  import { ProjectLogsManager } from '../../lib/projects/ProjectLogsManager.js';
13
12
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
14
13
  function getPrivateAppsUrl(accountId) {
15
- const baseUrl = getHubSpotWebsiteOrigin(getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
14
+ const baseUrl = getHubSpotWebsiteOrigin(getConfigAccountEnvironment(accountId));
16
15
  return `${baseUrl}/private-apps/${accountId}`;
17
16
  }
18
17
  function logTable(tableHeader, logsInfo) {
@@ -7,7 +7,6 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
7
7
  import { uiCommandReference } from '../../lib/ui/index.js';
8
8
  import { commands, lib } from '../../lang/en.js';
9
9
  import { uiLogger } from '../../lib/ui/logger.js';
10
- import { logInBox } from '../../lib/ui/boxen.js';
11
10
  import { renderInline } from '../../ui/index.js';
12
11
  import { getWarningBox } from '../../ui/components/StatusMessageBoxes.js';
13
12
  import { getHasMigratableThemes, migrateThemes2025_2, } from '../../lib/theme/migrate.js';
@@ -26,18 +25,10 @@ async function handler(args) {
26
25
  return process.exit(EXIT_CODES.ERROR);
27
26
  }
28
27
  if (projectConfig?.projectConfig) {
29
- if (!process.env.HUBSPOT_ENABLE_INK) {
30
- await logInBox({
31
- contents: lib.migrate.projectMigrationWarning,
32
- options: { title: lib.migrate.projectMigrationWarningTitle },
33
- });
34
- }
35
- else {
36
- await renderInline(getWarningBox({
37
- title: lib.migrate.projectMigrationWarningTitle,
38
- message: lib.migrate.projectMigrationWarning,
39
- }));
40
- }
28
+ await renderInline(getWarningBox({
29
+ title: lib.migrate.projectMigrationWarningTitle,
30
+ message: lib.migrate.projectMigrationWarning,
31
+ }));
41
32
  }
42
33
  try {
43
34
  const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
@@ -1,7 +1,6 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
- import { getAccountId, getConfigAccounts } from '@hubspot/local-dev-lib/config';
4
- import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
3
+ import { getConfigAccountIfExists, getAllConfigAccounts, } from '@hubspot/local-dev-lib/config';
5
4
  import { getAllHsProfiles, getHsProfileFilename, loadHsProfileFile, } from '@hubspot/project-parsing-lib';
6
5
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
7
6
  import { getProjectConfig } from '../../../lib/projects/config.js';
@@ -102,9 +101,9 @@ async function handler(args) {
102
101
  profileName = promptResponse.name;
103
102
  }
104
103
  if (targetAccountId) {
105
- const accountId = getAccountId(targetAccountId);
106
- if (accountId) {
107
- targetAccountId = accountId;
104
+ const account = getConfigAccountIfExists(targetAccountId);
105
+ if (account) {
106
+ targetAccountId = account.accountId;
108
107
  }
109
108
  else {
110
109
  uiLogger.error(commands.project.profile.add.errors.invalidTargetAccount);
@@ -113,14 +112,14 @@ async function handler(args) {
113
112
  }
114
113
  }
115
114
  if (!targetAccountId) {
116
- const configuredAccounts = getConfigAccounts();
115
+ const configuredAccounts = getAllConfigAccounts();
117
116
  if (!configuredAccounts || !configuredAccounts.length) {
118
117
  uiLogger.error(commands.project.profile.add.errors.noAccountsConfigured);
119
118
  process.exit(EXIT_CODES.ERROR);
120
119
  }
121
120
  const promptResponse = await listPrompt(commands.project.profile.add.prompts.targetAccountPrompt, {
122
121
  choices: configuredAccounts.map(account => {
123
- const accountId = getAccountIdentifier(account);
122
+ const accountId = account.accountId;
124
123
  return {
125
124
  name: uiAccountDescription(accountId),
126
125
  value: accountId,
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { getAllHsProfiles, getHsProfileFilename, loadHsProfileFile, } from '@hubspot/project-parsing-lib';
4
4
  import { fetchProject, deleteProject, } from '@hubspot/local-dev-lib/api/projects';
5
- import { getAccountConfig } from '@hubspot/local-dev-lib/config';
5
+ import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
6
6
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
7
7
  import { getProjectConfig } from '../../../lib/projects/config.js';
8
8
  import { uiLogger } from '../../../lib/ui/logger.js';
@@ -80,7 +80,7 @@ async function handler(args) {
80
80
  catch (err) {
81
81
  debugError(err);
82
82
  }
83
- const targetAccountConfig = getAccountConfig(targetAccountId);
83
+ const targetAccountConfig = getConfigAccountById(targetAccountId);
84
84
  if (projectExists &&
85
85
  targetAccountConfig &&
86
86
  (isDeveloperTestAccount(targetAccountConfig) ||
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { uiLogger } from '../../lib/ui/logger.js';
3
- import { getAccountConfig } from '@hubspot/local-dev-lib/config';
3
+ import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
4
4
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
5
5
  import { isV2Project } from '../../lib/projects/platformVersion.js';
6
6
  import { trackCommandUsage } from '../../lib/usageTracking.js';
@@ -28,11 +28,17 @@ async function handler(args) {
28
28
  process.exit(EXIT_CODES.ERROR);
29
29
  }
30
30
  let targetAccountId;
31
- if (isV2Project(projectConfig.platformVersion)) {
32
- targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
31
+ try {
32
+ if (isV2Project(projectConfig.platformVersion)) {
33
+ targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
34
+ }
35
+ }
36
+ catch (err) {
37
+ logError(err);
38
+ process.exit(EXIT_CODES.ERROR);
33
39
  }
34
40
  targetAccountId = targetAccountId || derivedAccountId;
35
- const accountConfig = getAccountConfig(targetAccountId);
41
+ const accountConfig = getConfigAccountById(targetAccountId);
36
42
  const accountType = accountConfig && accountConfig.accountType;
37
43
  trackCommandUsage('project-upload', { type: accountType, assetType: projectConfig.platformVersion }, targetAccountId);
38
44
  try {
@@ -1,5 +1,5 @@
1
1
  import path from 'path';
2
- import { getAccountConfig } from '@hubspot/local-dev-lib/config';
2
+ import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
3
3
  import { isV2Project } from '../../lib/projects/platformVersion.js';
4
4
  import { trackCommandUsage } from '../../lib/usageTracking.js';
5
5
  import { uiLogger } from '../../lib/ui/logger.js';
@@ -8,13 +8,19 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
8
8
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
9
9
  import { validateSourceDirectory, handleTranslate, } from '../../lib/projects/upload.js';
10
10
  import { commands } from '../../lang/en.js';
11
- import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
11
+ import { validateProjectForProfile } from '../../lib/projectProfiles.js';
12
12
  import { logError } from '../../lib/errorHandlers/index.js';
13
+ import { getAllHsProfiles } from '@hubspot/project-parsing-lib';
14
+ import SpinniesManager from '../../lib/ui/SpinniesManager.js';
13
15
  const command = 'validate';
14
16
  const describe = commands.project.validate.describe;
15
17
  async function handler(args) {
18
+ SpinniesManager.init();
16
19
  const { derivedAccountId, profile } = args;
17
20
  const { projectConfig, projectDir } = await getProjectConfig();
21
+ const accountConfig = getConfigAccountById(derivedAccountId);
22
+ const accountType = accountConfig && accountConfig.accountType;
23
+ trackCommandUsage('project-validate', { type: accountType }, derivedAccountId);
18
24
  if (!projectConfig || !projectDir) {
19
25
  uiLogger.error(commands.project.validate.mustBeRanWithinAProject);
20
26
  process.exit(EXIT_CODES.ERROR);
@@ -30,25 +36,79 @@ async function handler(args) {
30
36
  logError(error);
31
37
  process.exit(EXIT_CODES.ERROR);
32
38
  }
33
- let targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
34
- targetAccountId = targetAccountId || derivedAccountId;
35
- const accountConfig = getAccountConfig(targetAccountId);
36
- const accountType = accountConfig && accountConfig.accountType;
37
- trackCommandUsage('project-validate', { type: accountType }, targetAccountId);
39
+ let validationSucceeded = true;
38
40
  const srcDir = path.resolve(projectDir, projectConfig.srcDir);
39
- try {
40
- await validateSourceDirectory(srcDir, projectConfig, projectDir);
41
+ // Get all of the profiles except the provided profile
42
+ const profiles = (await getAllHsProfiles(path.join(projectDir, projectConfig.srcDir))).filter(profileName => profileName !== profile);
43
+ // If a profile is specified, only validate that profile
44
+ if (profile) {
45
+ const validationErrors = await validateProjectForProfile(projectConfig, projectDir, profile, derivedAccountId);
46
+ if (validationErrors.length) {
47
+ validationErrors.forEach(error => {
48
+ uiLogger.log('');
49
+ if (error instanceof Error) {
50
+ logError(error);
51
+ }
52
+ else {
53
+ uiLogger.error(error);
54
+ }
55
+ });
56
+ validationSucceeded = false;
57
+ }
41
58
  }
42
- catch (e) {
43
- logError(e);
59
+ else if (profiles.length > 0) {
60
+ // If no profile was specified and the project has profiles, validate all of them
61
+ SpinniesManager.add('validatingAllProfiles', {
62
+ text: commands.project.validate.spinners.validatingAllProfiles,
63
+ });
64
+ const errors = [];
65
+ for (const profileName of profiles) {
66
+ const validationErrors = await validateProjectForProfile(projectConfig, projectDir, profileName, derivedAccountId, true);
67
+ if (validationErrors.length) {
68
+ errors.push(...validationErrors);
69
+ validationSucceeded = false;
70
+ }
71
+ }
72
+ if (validationSucceeded) {
73
+ SpinniesManager.succeed('validatingAllProfiles', {
74
+ text: commands.project.validate.spinners.allProfilesValidationSucceeded,
75
+ });
76
+ }
77
+ else {
78
+ SpinniesManager.fail('validatingAllProfiles', {
79
+ text: commands.project.validate.spinners.allProfilesValidationFailed,
80
+ });
81
+ }
82
+ errors.forEach(error => {
83
+ uiLogger.log('');
84
+ if (error instanceof Error) {
85
+ logError(error);
86
+ }
87
+ else {
88
+ uiLogger.error(error);
89
+ }
90
+ });
91
+ }
92
+ else if (profiles.length === 0) {
93
+ // If the project has no profiles, validate the project without a profile
94
+ try {
95
+ await handleTranslate(projectDir, projectConfig, derivedAccountId, false, undefined);
96
+ }
97
+ catch (e) {
98
+ uiLogger.error(commands.project.validate.failure(projectConfig.name));
99
+ logError(e);
100
+ validationSucceeded = false;
101
+ uiLogger.log('');
102
+ }
103
+ }
104
+ if (!validationSucceeded) {
44
105
  process.exit(EXIT_CODES.ERROR);
45
106
  }
46
107
  try {
47
- await handleTranslate(projectDir, projectConfig, targetAccountId, false, profile);
108
+ await validateSourceDirectory(srcDir, projectConfig, projectDir);
48
109
  }
49
110
  catch (e) {
50
111
  logError(e);
51
- uiLogger.error(commands.project.validate.failure(projectConfig.name));
52
112
  process.exit(EXIT_CODES.ERROR);
53
113
  }
54
114
  uiLogger.success(commands.project.validate.success(projectConfig.name));
@@ -13,6 +13,7 @@ import migrate from './project/migrate.js';
13
13
  import migrateApp from './project/migrateApp.js';
14
14
  import cloneApp from './project/cloneApp.js';
15
15
  import installDeps from './project/installDeps.js';
16
+ import lint from './project/lint.js';
16
17
  import updateDeps from './project/updateDeps.js';
17
18
  import profile from './project/profile.js';
18
19
  import projectValidate from './project/validate.js';
@@ -37,6 +38,7 @@ function projectBuilder(yargs) {
37
38
  .command(migrate)
38
39
  .command(cloneApp)
39
40
  .command(installDeps)
41
+ .command(lint)
40
42
  .command(updateDeps)
41
43
  .command(profile)
42
44
  .command(projectValidate)
@@ -14,6 +14,7 @@ import { uiLogger } from '../../../lib/ui/logger.js';
14
14
  import * as sandboxesLib from '../../../lib/sandboxes.js';
15
15
  import * as sandboxSync from '../../../lib/sandboxSync.js';
16
16
  import { vi } from 'vitest';
17
+ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
17
18
  vi.mock('../../../lib/ui/logger.js');
18
19
  vi.mock('@hubspot/local-dev-lib/config');
19
20
  vi.mock('../../../lib/commonOpts');
@@ -26,12 +27,13 @@ vi.mock('../../../lib/usageTracking');
26
27
  vi.mock('../../../lib/buildAccount');
27
28
  vi.mock('../../../lib/sandboxes');
28
29
  vi.mock('../../../lib/commonOpts');
29
- const getAccountConfigSpy = vi.spyOn(configUtils, 'getAccountConfig');
30
+ const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
30
31
  const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
31
32
  const sandboxTypePromptSpy = vi.spyOn(sandboxPrompts, 'sandboxTypePrompt');
32
33
  const processExitSpy = vi.spyOn(process, 'exit');
33
34
  const buildSandboxSpy = vi.spyOn(buildAccount, 'buildSandbox');
34
35
  const buildV2SandboxSpy = vi.spyOn(buildAccount, 'buildV2Sandbox');
36
+ const getConfigAccountEnvironmentSpy = vi.spyOn(configUtils, 'getConfigAccountEnvironment');
35
37
  const getAvailableSyncTypesSpy = vi.spyOn(sandboxesLib, 'getAvailableSyncTypes');
36
38
  const syncSandboxSpy = vi.spyOn(sandboxSync, 'syncSandbox');
37
39
  const validateSandboxUsageLimitsSpy = vi.spyOn(sandboxesLib, 'validateSandboxUsageLimits');
@@ -88,7 +90,8 @@ describe('commands/sandbox/create', () => {
88
90
  args = {
89
91
  derivedAccountId: 1234567890,
90
92
  };
91
- getAccountConfigSpy.mockReturnValue({
93
+ getConfigAccountByIdSpy.mockReturnValue({
94
+ accountId: 1234567890,
92
95
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
93
96
  env: 'prod',
94
97
  });
@@ -104,6 +107,7 @@ describe('commands/sandbox/create', () => {
104
107
  validateSandboxUsageLimitsSpy.mockResolvedValue(undefined);
105
108
  mockedHasFeatureV2Sandboxes.mockResolvedValue(false);
106
109
  mockedHasFeatureV2Cli.mockResolvedValue(false);
110
+ getConfigAccountEnvironmentSpy.mockReturnValue(ENVIRONMENTS.PROD);
107
111
  buildSandboxSpy.mockResolvedValue({
108
112
  sandbox: mockSandbox,
109
113
  personalAccessKey: 'mock-personal-access-key',
@@ -123,8 +127,8 @@ describe('commands/sandbox/create', () => {
123
127
  });
124
128
  it('should load the account config for the correct account id', async () => {
125
129
  await sandboxCreateCommand.handler(args);
126
- expect(getAccountConfigSpy).toHaveBeenCalledTimes(2); // 1st is for parent account, 2nd is for sandbox account
127
- expect(getAccountConfigSpy).toHaveBeenCalledWith(args.derivedAccountId);
130
+ expect(getConfigAccountByIdSpy).toHaveBeenCalledTimes(2); // 1st is for parent account, 2nd is for sandbox account
131
+ expect(getConfigAccountByIdSpy).toHaveBeenCalledWith(args.derivedAccountId);
128
132
  });
129
133
  it('should track the command usage', async () => {
130
134
  await sandboxCreateCommand.handler(args);
@@ -135,6 +139,7 @@ describe('commands/sandbox/create', () => {
135
139
  await sandboxCreateCommand.handler(args);
136
140
  expect(validateSandboxUsageLimitsSpy).toHaveBeenCalledTimes(1);
137
141
  expect(validateSandboxUsageLimitsSpy).toHaveBeenCalledWith({
142
+ accountId: 1234567890,
138
143
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
139
144
  env: 'prod',
140
145
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod');
@@ -168,6 +173,7 @@ describe('commands/sandbox/create', () => {
168
173
  await sandboxCreateCommand.handler(args);
169
174
  expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
170
175
  expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
176
+ accountId: 1234567890,
171
177
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
172
178
  env: 'prod',
173
179
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
@@ -181,6 +187,7 @@ describe('commands/sandbox/create', () => {
181
187
  await sandboxCreateCommand.handler(args);
182
188
  expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
183
189
  expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
190
+ accountId: 1234567890,
184
191
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
185
192
  env: 'prod',
186
193
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
@@ -194,6 +201,7 @@ describe('commands/sandbox/create', () => {
194
201
  await sandboxCreateCommand.handler(args);
195
202
  expect(buildV2SandboxSpy).toHaveBeenCalledTimes(1);
196
203
  expect(buildV2SandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
204
+ accountId: 1234567890,
197
205
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
198
206
  env: 'prod',
199
207
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, false, // syncObjectRecords
@@ -222,7 +230,8 @@ describe('commands/sandbox/create', () => {
222
230
  expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
223
231
  });
224
232
  it('should error out if the default account type is not standard', async () => {
225
- getAccountConfigSpy.mockReturnValue({
233
+ getConfigAccountByIdSpy.mockReturnValue({
234
+ accountId: 1234567890,
226
235
  accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
227
236
  env: 'prod',
228
237
  });
@@ -1,9 +1,8 @@
1
- import { getAccountConfig, getEnv } from '@hubspot/local-dev-lib/config';
1
+ import { getConfigAccountById, getConfigAccountEnvironment, } from '@hubspot/local-dev-lib/config';
2
2
  import { uiLogger } from '../../lib/ui/logger.js';
3
3
  import { isMissingScopeError } from '@hubspot/local-dev-lib/errors/index';
4
4
  import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
5
5
  import { HUBSPOT_ACCOUNT_TYPES, HUBSPOT_ACCOUNT_TYPE_STRINGS, } from '@hubspot/local-dev-lib/constants/config';
6
- import { getValidEnv } from '@hubspot/local-dev-lib/environment';
7
6
  import { commands, lib } from '../../lang/en.js';
8
7
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
9
8
  import { uiFeatureHighlight, uiBetaTag } from '../../lib/ui/index.js';
@@ -22,8 +21,8 @@ const command = 'create';
22
21
  const describe = uiBetaTag(commands.sandbox.subcommands.create.describe, false);
23
22
  async function handler(args) {
24
23
  const { name, type, force, derivedAccountId } = args;
25
- const accountConfig = getAccountConfig(derivedAccountId);
26
- const env = getValidEnv(getEnv(derivedAccountId));
24
+ const accountConfig = getConfigAccountById(derivedAccountId);
25
+ const env = getConfigAccountEnvironment(derivedAccountId);
27
26
  trackCommandUsage('sandbox-create', {}, derivedAccountId);
28
27
  // Check if account config exists
29
28
  if (!accountConfig) {
@@ -111,7 +110,7 @@ async function handler(args) {
111
110
  else {
112
111
  result = await buildSandbox(sandboxName, accountConfig, sandboxType, env, force);
113
112
  }
114
- const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
113
+ const sandboxAccountConfig = getConfigAccountById(result.sandbox.sandboxHubId);
115
114
  // Check if sandbox account config exists
116
115
  if (!sandboxAccountConfig) {
117
116
  uiLogger.error(commands.sandbox.subcommands.create.failure.noSandboxAccountConfig(result.sandbox.sandboxHubId));