@hubspot/cli 7.10.1-experimental.0 → 7.11.0-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 (32) hide show
  1. package/commands/project/__tests__/deploy.test.js +5 -5
  2. package/commands/project/__tests__/validate.test.js +27 -285
  3. package/commands/project/create.js +20 -14
  4. package/commands/project/deploy.js +6 -14
  5. package/commands/project/dev/index.js +4 -13
  6. package/commands/project/dev/unifiedFlow.js +7 -1
  7. package/commands/project/upload.js +2 -8
  8. package/commands/project/validate.js +12 -72
  9. package/lang/en.d.ts +18 -14
  10. package/lang/en.js +20 -16
  11. package/lib/__tests__/projectProfiles.test.js +32 -273
  12. package/lib/errorHandlers/index.js +8 -3
  13. package/lib/projectProfiles.d.ts +3 -4
  14. package/lib/projectProfiles.js +32 -78
  15. package/lib/projects/__tests__/components.test.js +2 -22
  16. package/lib/projects/__tests__/deploy.test.js +15 -13
  17. package/lib/projects/add/__tests__/legacyAddComponent.test.js +1 -1
  18. package/lib/projects/add/__tests__/v2AddComponent.test.js +30 -4
  19. package/lib/projects/add/legacyAddComponent.js +1 -1
  20. package/lib/projects/add/v2AddComponent.js +16 -5
  21. package/lib/projects/components.d.ts +8 -1
  22. package/lib/projects/components.js +91 -8
  23. package/lib/projects/deploy.js +21 -8
  24. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +9 -1
  25. package/lib/projects/localDev/helpers/process.js +5 -3
  26. package/lib/ui/SpinniesManager.d.ts +5 -7
  27. package/lib/ui/SpinniesManager.js +9 -12
  28. package/lib/ui/__tests__/SpinniesManager.test.d.ts +1 -0
  29. package/lib/ui/__tests__/SpinniesManager.test.js +489 -0
  30. package/mcp-server/utils/config.js +1 -1
  31. package/package.json +4 -4
  32. package/ui/components/BoxWithTitle.js +1 -1
@@ -37,7 +37,7 @@ const getProjectConfigSpy = vi.spyOn(projectUtils, 'getProjectConfig');
37
37
  const projectNamePromptSpy = vi.spyOn(projectNamePrompt, 'projectNamePrompt');
38
38
  const getProjectDetailUrlSpy = vi.spyOn(projectUrlUtils, 'getProjectDetailUrl');
39
39
  const fetchProjectSpy = vi.spyOn(projectApiUtils, 'fetchProject');
40
- const deployProjectSpy = vi.spyOn(projectApiUtils, 'deployProject');
40
+ const deployProjectV1Spy = vi.spyOn(projectApiUtils, 'deployProjectV1');
41
41
  const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
42
42
  const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
43
43
  const processExitSpy = vi.spyOn(process, 'exit');
@@ -144,7 +144,7 @@ describe('commands/project/deploy', () => {
144
144
  env: 'qa',
145
145
  });
146
146
  fetchProjectSpy.mockReturnValue(mockHubSpotHttpResponse(exampleProject));
147
- deployProjectSpy.mockReturnValue(mockHubSpotHttpResponse(deployDetails));
147
+ deployProjectV1Spy.mockReturnValue(mockHubSpotHttpResponse(deployDetails));
148
148
  // Spy on process.exit so our tests don't close when it's called
149
149
  // @ts-expect-error Doesn't match the actual signature because then the linter complains about unused variables
150
150
  processExitSpy.mockImplementation(() => { });
@@ -245,12 +245,12 @@ describe('commands/project/deploy', () => {
245
245
  });
246
246
  it('should deploy the project', async () => {
247
247
  await projectDeployCommand.handler(args);
248
- expect(deployProjectSpy).toHaveBeenCalledTimes(1);
249
- expect(deployProjectSpy).toHaveBeenCalledWith(args.derivedAccountId, projectNameFromPrompt, args.buildId, undefined, undefined);
248
+ expect(deployProjectV1Spy).toHaveBeenCalledTimes(1);
249
+ expect(deployProjectV1Spy).toHaveBeenCalledWith(args.derivedAccountId, projectNameFromPrompt, args.buildId, undefined);
250
250
  });
251
251
  it('should log an error and exit when the deploy fails', async () => {
252
252
  // @ts-expect-error Testing an edge case where the response is empty
253
- deployProjectSpy.mockResolvedValue({});
253
+ deployProjectV1Spy.mockResolvedValue({});
254
254
  await projectDeployCommand.handler(args);
255
255
  expect(uiLogger.error).toHaveBeenCalledTimes(1);
256
256
  expect(uiLogger.error).toHaveBeenCalledWith(`Deploy error: an unknown error occurred.`);
@@ -5,14 +5,11 @@ import { getProjectConfig, validateProjectConfig, } from '../../../lib/projects/
5
5
  import { uiLogger } from '../../../lib/ui/logger.js';
6
6
  import { commands } from '../../../lang/en.js';
7
7
  import { isV2Project } from '../../../lib/projects/platformVersion.js';
8
- import { validateProjectForProfile } from '../../../lib/projectProfiles.js';
8
+ import { loadAndValidateProfile } from '../../../lib/projectProfiles.js';
9
9
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
10
10
  import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
11
11
  import { handleTranslate } from '../../../lib/projects/upload.js';
12
12
  import projectValidateCommand from '../validate.js';
13
- import { getAllHsProfiles } from '@hubspot/project-parsing-lib';
14
- import SpinniesManager from '../../../lib/ui/SpinniesManager.js';
15
- import { logError } from '../../../lib/errorHandlers/index.js';
16
13
  // Mock dependencies
17
14
  vi.mock('../../../lib/projects/upload.js');
18
15
  vi.mock('../../../lib/projects/config.js');
@@ -22,35 +19,15 @@ vi.mock('../../../lib/projectProfiles.js');
22
19
  vi.mock('../../../lib/errorHandlers/index.js');
23
20
  vi.mock('@hubspot/local-dev-lib/config');
24
21
  vi.mock('../../../lib/projects/platformVersion.js');
25
- vi.mock('@hubspot/project-parsing-lib');
26
- vi.mock('../../../lib/ui/SpinniesManager.js');
27
22
  describe('commands/project/validate', () => {
28
23
  const projectDir = '/test/project';
29
24
  let exitSpy;
30
- const mockProjectConfig = {
31
- name: 'test-project',
32
- srcDir: 'src',
33
- platformVersion: '2025.2',
34
- };
35
- const mockAccountConfig = {
36
- accountType: 'STANDARD',
37
- accountId: 123,
38
- env: 'prod',
39
- };
40
25
  beforeEach(() => {
41
26
  // Mock process.exit to throw to stop execution
42
27
  exitSpy = vi.spyOn(process, 'exit').mockImplementation(code => {
43
28
  throw new Error(`Process exited with code ${code}`);
44
29
  });
45
30
  vi.clearAllMocks();
46
- // Set up default mocks
47
- vi.mocked(getConfigAccountById).mockReturnValue(mockAccountConfig);
48
- vi.mocked(trackCommandUsage);
49
- vi.mocked(SpinniesManager.init);
50
- vi.mocked(SpinniesManager.add);
51
- vi.mocked(SpinniesManager.succeed);
52
- vi.mocked(SpinniesManager.fail);
53
- vi.mocked(validateProjectForProfile).mockResolvedValue([]);
54
31
  });
55
32
  afterEach(() => {
56
33
  exitSpy.mockRestore();
@@ -88,269 +65,34 @@ describe('commands/project/validate', () => {
88
65
  })).rejects.toThrow('Process exited with code 1');
89
66
  expect(uiLogger.error).toHaveBeenCalledWith(commands.project.validate.mustBeRanWithinAProject);
90
67
  });
91
- it('should exit with error for non-V2 projects', async () => {
92
- vi.mocked(getProjectConfig).mockResolvedValue({
93
- projectConfig: {
94
- name: 'test',
95
- srcDir: 'src',
96
- platformVersion: '2024.1',
97
- },
98
- projectDir,
99
- });
100
- vi.mocked(isV2Project).mockReturnValue(false);
101
- await expect(
102
- // @ts-expect-error partial mock
103
- projectValidateCommand.handler({
104
- derivedAccountId: 123,
105
- d: false,
106
- debug: false,
107
- })).rejects.toThrow('Process exited with code 1');
108
- expect(uiLogger.error).toHaveBeenCalledWith(commands.project.validate.badVersion);
109
- });
110
- it('should exit with error when validateProjectConfig throws', async () => {
111
- vi.mocked(getProjectConfig).mockResolvedValue({
112
- projectConfig: mockProjectConfig,
113
- projectDir,
114
- });
115
- vi.mocked(isV2Project).mockReturnValue(true);
116
- const error = new Error('Invalid project config');
117
- vi.mocked(validateProjectConfig).mockImplementation(() => {
118
- throw error;
119
- });
120
- await expect(
121
- // @ts-expect-error partial mock
122
- projectValidateCommand.handler({
123
- derivedAccountId: 123,
124
- d: false,
125
- debug: false,
126
- })).rejects.toThrow('Process exited with code 1');
127
- expect(logError).toHaveBeenCalledWith(error);
128
- });
129
68
  });
130
- describe('profile validation', () => {
131
- describe('when a specific profile is provided', () => {
132
- it('should validate only the specified profile', async () => {
133
- vi.mocked(getProjectConfig).mockResolvedValue({
134
- projectConfig: mockProjectConfig,
135
- projectDir,
136
- });
137
- vi.mocked(isV2Project).mockReturnValue(true);
138
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
139
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod', 'qa']);
140
- vi.mocked(validateProjectForProfile).mockResolvedValue([]);
141
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
142
- await expect(projectValidateCommand.handler({
143
- derivedAccountId: 123,
144
- profile: 'dev',
145
- d: false,
146
- debug: false,
147
- })).rejects.toThrow('Process exited with code 0');
148
- // Should call validateProjectForProfile for the specified profile
149
- expect(validateProjectForProfile).toHaveBeenCalledWith(mockProjectConfig, projectDir, 'dev', 123);
150
- expect(uiLogger.success).toHaveBeenCalledWith(commands.project.validate.success(mockProjectConfig.name));
151
- });
152
- it('should handle profile validation failure', async () => {
153
- vi.mocked(getProjectConfig).mockResolvedValue({
154
- projectConfig: mockProjectConfig,
155
- projectDir,
156
- });
157
- vi.mocked(isV2Project).mockReturnValue(true);
158
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
159
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod']);
160
- const error = new Error('Profile not found');
161
- vi.mocked(validateProjectForProfile).mockResolvedValue([error.message]);
162
- await expect(projectValidateCommand.handler({
163
- derivedAccountId: 123,
164
- profile: 'dev',
165
- d: false,
166
- debug: false,
167
- })).rejects.toThrow('Process exited with code 1');
168
- // The error message is logged as a string, not the Error object
169
- expect(uiLogger.error).toHaveBeenCalledWith(error.message);
170
- });
171
- it('should handle translate failure for a profile', async () => {
172
- vi.mocked(getProjectConfig).mockResolvedValue({
173
- projectConfig: mockProjectConfig,
174
- projectDir,
175
- });
176
- vi.mocked(isV2Project).mockReturnValue(true);
177
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
178
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod']);
179
- const error = new Error('Translation failed');
180
- vi.mocked(validateProjectForProfile).mockResolvedValue([
181
- commands.project.validate.failure(mockProjectConfig.name),
182
- error,
183
- ]);
184
- await expect(projectValidateCommand.handler({
185
- derivedAccountId: 123,
186
- profile: 'dev',
187
- d: false,
188
- debug: false,
189
- })).rejects.toThrow('Process exited with code 1');
190
- // The error object is logged via logError
191
- expect(logError).toHaveBeenCalledWith(error);
192
- });
69
+ it('should call validateSourceDirectory with correct parameters', async () => {
70
+ const mockProjectConfig = {
71
+ name: 'test-project',
72
+ srcDir: 'src',
73
+ platformVersion: '2025.2',
74
+ };
75
+ vi.mocked(getProjectConfig).mockResolvedValue({
76
+ projectConfig: mockProjectConfig,
77
+ projectDir,
193
78
  });
194
- describe('when no profile is provided and project has profiles', () => {
195
- it('should validate all profiles', async () => {
196
- vi.mocked(getProjectConfig).mockResolvedValue({
197
- projectConfig: mockProjectConfig,
198
- projectDir,
199
- });
200
- vi.mocked(isV2Project).mockReturnValue(true);
201
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
202
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod', 'qa']);
203
- vi.mocked(validateProjectForProfile).mockResolvedValue([]);
204
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
205
- await expect(projectValidateCommand.handler({
206
- derivedAccountId: 123,
207
- d: false,
208
- debug: false,
209
- })).rejects.toThrow('Process exited with code 0');
210
- // Should validate all three profiles
211
- expect(validateProjectForProfile).toHaveBeenCalledTimes(3);
212
- expect(validateProjectForProfile).toHaveBeenCalledWith(mockProjectConfig, projectDir, 'dev', 123, true);
213
- expect(validateProjectForProfile).toHaveBeenCalledWith(mockProjectConfig, projectDir, 'prod', 123, true);
214
- expect(validateProjectForProfile).toHaveBeenCalledWith(mockProjectConfig, projectDir, 'qa', 123, true);
215
- // Should show success for all profiles
216
- expect(SpinniesManager.succeed).toHaveBeenCalledWith('validatingAllProfiles', expect.any(Object));
217
- expect(uiLogger.success).toHaveBeenCalledWith(commands.project.validate.success(mockProjectConfig.name));
218
- });
219
- it('should handle failure when validating multiple profiles', async () => {
220
- vi.mocked(getProjectConfig).mockResolvedValue({
221
- projectConfig: mockProjectConfig,
222
- projectDir,
223
- });
224
- vi.mocked(isV2Project).mockReturnValue(true);
225
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
226
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod']);
227
- vi.mocked(validateProjectForProfile)
228
- .mockResolvedValueOnce([]) // dev succeeds
229
- .mockResolvedValueOnce(['Profile not found']); // prod fails
230
- await expect(projectValidateCommand.handler({
231
- derivedAccountId: 123,
232
- d: false,
233
- debug: false,
234
- })).rejects.toThrow('Process exited with code 1');
235
- expect(SpinniesManager.fail).toHaveBeenCalledWith('validatingAllProfiles', expect.any(Object));
236
- });
237
- it('should continue validating remaining profiles after one fails', async () => {
238
- vi.mocked(getProjectConfig).mockResolvedValue({
239
- projectConfig: mockProjectConfig,
240
- projectDir,
241
- });
242
- vi.mocked(isV2Project).mockReturnValue(true);
243
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
244
- vi.mocked(getAllHsProfiles).mockResolvedValue(['dev', 'prod', 'qa']);
245
- vi.mocked(validateProjectForProfile)
246
- .mockResolvedValueOnce([]) // dev succeeds
247
- .mockResolvedValueOnce(['Profile not found']) // prod fails
248
- .mockResolvedValueOnce([]); // qa succeeds
249
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
250
- await expect(projectValidateCommand.handler({
251
- derivedAccountId: 123,
252
- d: false,
253
- debug: false,
254
- })).rejects.toThrow('Process exited with code 1');
255
- // All three profiles should be attempted
256
- expect(validateProjectForProfile).toHaveBeenCalledTimes(3);
257
- });
258
- });
259
- describe('when no profile is provided and project has no profiles', () => {
260
- it('should validate without a profile', async () => {
261
- vi.mocked(getProjectConfig).mockResolvedValue({
262
- projectConfig: mockProjectConfig,
263
- projectDir,
264
- });
265
- vi.mocked(isV2Project).mockReturnValue(true);
266
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
267
- vi.mocked(getAllHsProfiles).mockResolvedValue([]);
268
- vi.mocked(handleTranslate).mockResolvedValue(undefined);
269
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
270
- await expect(projectValidateCommand.handler({
271
- derivedAccountId: 123,
272
- d: false,
273
- debug: false,
274
- })).rejects.toThrow('Process exited with code 0');
275
- // Should call handleTranslate without a profile
276
- expect(handleTranslate).toHaveBeenCalledWith(projectDir, mockProjectConfig, 123, false, undefined);
277
- expect(uiLogger.success).toHaveBeenCalledWith(commands.project.validate.success(mockProjectConfig.name));
278
- });
279
- it('should handle validation failure when no profiles exist', async () => {
280
- vi.mocked(getProjectConfig).mockResolvedValue({
281
- projectConfig: mockProjectConfig,
282
- projectDir,
283
- });
284
- vi.mocked(isV2Project).mockReturnValue(true);
285
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
286
- vi.mocked(getAllHsProfiles).mockResolvedValue([]);
287
- const error = new Error('Translation failed');
288
- vi.mocked(handleTranslate).mockRejectedValue(error);
289
- await expect(projectValidateCommand.handler({
290
- derivedAccountId: 123,
291
- d: false,
292
- debug: false,
293
- })).rejects.toThrow('Process exited with code 1');
294
- expect(uiLogger.error).toHaveBeenCalledWith(commands.project.validate.failure(mockProjectConfig.name));
295
- expect(logError).toHaveBeenCalledWith(error);
296
- });
297
- });
298
- });
299
- describe('source directory validation', () => {
300
- it('should call validateSourceDirectory with correct parameters', async () => {
301
- vi.mocked(getProjectConfig).mockResolvedValue({
302
- projectConfig: mockProjectConfig,
303
- projectDir,
304
- });
305
- vi.mocked(isV2Project).mockReturnValue(true);
306
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
307
- vi.mocked(getAllHsProfiles).mockResolvedValue([]);
308
- vi.mocked(handleTranslate).mockResolvedValue(undefined);
309
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
310
- await expect(projectValidateCommand.handler({
311
- derivedAccountId: 123,
312
- d: false,
313
- debug: false,
314
- })).rejects.toThrow('Process exited with code 0');
315
- const expectedSrcDir = path.resolve(projectDir, mockProjectConfig.srcDir);
316
- expect(validateSourceDirectory).toHaveBeenCalledWith(expectedSrcDir, mockProjectConfig, projectDir);
317
- });
318
- it('should exit with error when validateSourceDirectory throws', async () => {
319
- vi.mocked(getProjectConfig).mockResolvedValue({
320
- projectConfig: mockProjectConfig,
321
- projectDir,
322
- });
323
- vi.mocked(isV2Project).mockReturnValue(true);
324
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
325
- vi.mocked(getAllHsProfiles).mockResolvedValue([]);
326
- vi.mocked(handleTranslate).mockResolvedValue(undefined);
327
- const error = new Error('Invalid source directory');
328
- vi.mocked(validateSourceDirectory).mockRejectedValue(error);
329
- await expect(projectValidateCommand.handler({
330
- derivedAccountId: 123,
331
- d: false,
332
- debug: false,
333
- })).rejects.toThrow('Process exited with code 1');
334
- expect(logError).toHaveBeenCalledWith(error);
335
- });
336
- });
337
- describe('command usage tracking', () => {
338
- it('should track command usage with account type', async () => {
339
- vi.mocked(getProjectConfig).mockResolvedValue({
340
- projectConfig: mockProjectConfig,
341
- projectDir,
342
- });
343
- vi.mocked(isV2Project).mockReturnValue(true);
344
- vi.mocked(validateProjectConfig).mockReturnValue(undefined);
345
- vi.mocked(getAllHsProfiles).mockResolvedValue([]);
346
- vi.mocked(handleTranslate).mockResolvedValue(undefined);
347
- vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
348
- await expect(projectValidateCommand.handler({
349
- derivedAccountId: 123,
350
- d: false,
351
- debug: false,
352
- })).rejects.toThrow('Process exited with code 0');
353
- expect(trackCommandUsage).toHaveBeenCalledWith('project-validate', { type: 'STANDARD' }, 123);
79
+ vi.mocked(isV2Project).mockReturnValue(true);
80
+ vi.mocked(validateProjectConfig).mockReturnValue(undefined);
81
+ vi.mocked(loadAndValidateProfile).mockResolvedValue(123);
82
+ vi.mocked(getConfigAccountById).mockReturnValue({
83
+ accountType: 'STANDARD',
84
+ accountId: 123,
85
+ env: 'prod',
354
86
  });
87
+ vi.mocked(trackCommandUsage);
88
+ vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
89
+ vi.mocked(handleTranslate).mockResolvedValue(undefined);
90
+ await expect(projectValidateCommand.handler({
91
+ derivedAccountId: 123,
92
+ d: false,
93
+ debug: false,
94
+ })).rejects.toThrow('Process exited with code 0');
95
+ const expectedSrcDir = path.resolve(projectDir, mockProjectConfig.srcDir);
96
+ expect(validateSourceDirectory).toHaveBeenCalledWith(expectedSrcDir, mockProjectConfig, projectDir);
355
97
  });
356
98
  });
@@ -7,7 +7,6 @@ import { writeProjectConfig, getProjectConfig, } from '../../lib/projects/config
7
7
  import { EMPTY_PROJECT_TEMPLATE_NAME } from '../../lib/projects/create/legacy.js';
8
8
  import { generateComponentPaths } from '../../lib/projects/create/v2.js';
9
9
  import { PROJECT_WITH_APP, EMPTY_PROJECT } from '../../lib/constants.js';
10
- import { uiFeatureHighlight } from '../../lib/ui/index.js';
11
10
  import { debugError, logError } from '../../lib/errorHandlers/index.js';
12
11
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
13
12
  import { PROJECT_CONFIG_FILE, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, marketplaceDistribution, privateDistribution, oAuth, staticAuth, DEFAULT_PROJECT_TEMPLATE_BRANCH, } from '../../lib/constants.js';
@@ -18,6 +17,7 @@ import { uiLogger } from '../../lib/ui/logger.js';
18
17
  import { handleProjectCreationFlow, } from '../../lib/projects/create/index.js';
19
18
  import { getProjectMetadata, } from '@hubspot/project-parsing-lib/src/lib/project.js';
20
19
  import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/components.js';
20
+ import SpinniesManager from '../../lib/ui/SpinniesManager.js';
21
21
  const command = ['create', 'init'];
22
22
  const describe = commands.project.create.describe;
23
23
  const { v2023_2, v2025_1, v2025_2 } = PLATFORM_VERSIONS;
@@ -61,6 +61,12 @@ async function handler(args) {
61
61
  authType,
62
62
  distribution,
63
63
  });
64
+ const isProjectEmpty = selectProjectTemplatePromptResponse.projectTemplate?.name ===
65
+ EMPTY_PROJECT_TEMPLATE_NAME || projectContents === EMPTY_PROJECT;
66
+ SpinniesManager.init();
67
+ SpinniesManager.add('project-create', {
68
+ text: commands.project.create.creatingComponent(isProjectEmpty, projectNameAndDestPromptResponse.name),
69
+ });
64
70
  try {
65
71
  await cloneGithubRepo(repo, projectDest, {
66
72
  sourceDir: selectProjectTemplatePromptResponse.projectTemplate?.path || components,
@@ -69,6 +75,9 @@ async function handler(args) {
69
75
  });
70
76
  }
71
77
  catch (err) {
78
+ SpinniesManager.fail('project-create', {
79
+ text: commands.project.create.failure(isProjectEmpty, projectNameAndDestPromptResponse.name),
80
+ });
72
81
  debugError(err);
73
82
  uiLogger.error(commands.project.create.errors.failedToDownloadProject);
74
83
  process.exit(EXIT_CODES.ERROR);
@@ -80,23 +89,20 @@ async function handler(args) {
80
89
  ...parsedConfigFile,
81
90
  name: projectName,
82
91
  });
92
+ SpinniesManager.succeed('project-create', {
93
+ text: commands.project.create.success(isProjectEmpty, projectName),
94
+ });
83
95
  const projectMetadata = await getProjectMetadata(path.join(projectDest, parsedConfigFile.srcDir));
84
- updateHsMetaFilesWithAutoGeneratedFields(projectName, projectMetadata.hsMetaFiles);
96
+ await updateHsMetaFilesWithAutoGeneratedFields(projectName, projectMetadata.hsMetaFiles, [], {
97
+ updatedProjectMetadata: projectMetadata,
98
+ showSuccessMessage: true,
99
+ isProjectEmpty,
100
+ projectDest,
101
+ });
85
102
  // If the template is 'no-template', we need to manually create a src directory
86
- if (selectProjectTemplatePromptResponse.projectTemplate?.name ===
87
- EMPTY_PROJECT_TEMPLATE_NAME ||
88
- projectContents === EMPTY_PROJECT) {
103
+ if (isProjectEmpty) {
89
104
  fs.ensureDirSync(path.join(projectDest, 'src'));
90
105
  }
91
- uiLogger.success(commands.project.create.logs.success(projectNameAndDestPromptResponse.name, projectDest));
92
- uiLogger.log(commands.project.create.logs.welcomeMessage);
93
- uiFeatureHighlight([
94
- 'projectCommandTip',
95
- 'projectUploadCommand',
96
- 'projectDevCommand',
97
- 'projectHelpCommand',
98
- 'feedbackCommand',
99
- ]);
100
106
  process.exit(EXIT_CODES.SUCCESS);
101
107
  }
102
108
  function projectCreateBuilder(yargs) {
@@ -7,10 +7,11 @@ import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
7
7
  import { getProjectConfig } from '../../lib/projects/config.js';
8
8
  import { projectNamePrompt } from '../../lib/prompts/projectNamePrompt.js';
9
9
  import { promptUser } from '../../lib/prompts/promptUtils.js';
10
+ import { uiLine } from '../../lib/ui/index.js';
10
11
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
11
12
  import { uiLogger } from '../../lib/ui/logger.js';
12
13
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
13
- import { loadProfile, logProfileFooter, logProfileHeader, enforceProfileUsage, } from '../../lib/projectProfiles.js';
14
+ import { loadProfile, logProfileFooter, logProfileHeader, exitIfUsingProfiles, } from '../../lib/projectProfiles.js';
14
15
  import { PROJECT_DEPLOY_TEXT } from '../../lib/constants.js';
15
16
  import { commands } from '../../lang/en.js';
16
17
  import { handleProjectDeploy, validateBuildIdForDeploy, logDeployErrors, } from '../../lib/projects/deploy.js';
@@ -26,12 +27,9 @@ async function handler(args) {
26
27
  if (isV2Project(projectConfig?.platformVersion)) {
27
28
  if (args.profile) {
28
29
  logProfileHeader(args.profile);
29
- let profile;
30
- try {
31
- profile = loadProfile(projectConfig, projectDir, args.profile);
32
- }
33
- catch (error) {
34
- logError(error);
30
+ const profile = loadProfile(projectConfig, projectDir, args.profile);
31
+ if (!profile) {
32
+ uiLine();
35
33
  process.exit(EXIT_CODES.ERROR);
36
34
  }
37
35
  targetAccountId = profile.accountId;
@@ -39,13 +37,7 @@ async function handler(args) {
39
37
  }
40
38
  else {
41
39
  // A profile must be specified if this project has profiles configured
42
- try {
43
- await enforceProfileUsage(projectConfig, projectDir);
44
- }
45
- catch (error) {
46
- logError(error);
47
- process.exit(EXIT_CODES.ERROR);
48
- }
40
+ await exitIfUsingProfiles(projectConfig, projectDir);
49
41
  }
50
42
  }
51
43
  if (!targetAccountId) {
@@ -7,7 +7,7 @@ import { deprecatedProjectDevFlow } from './deprecatedFlow.js';
7
7
  import { unifiedProjectDevFlow } from './unifiedFlow.js';
8
8
  import { isV2Project } from '../../../lib/projects/platformVersion.js';
9
9
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
- import { loadProfile, enforceProfileUsage, } from '../../../lib/projectProfiles.js';
10
+ import { loadProfile, exitIfUsingProfiles, } from '../../../lib/projectProfiles.js';
11
11
  import { commands } from '../../../lang/en.js';
12
12
  import { uiLogger } from '../../../lib/ui/logger.js';
13
13
  import { logError } from '../../../lib/errorHandlers/index.js';
@@ -64,11 +64,8 @@ async function handler(args) {
64
64
  }
65
65
  if (!targetProjectAccountId && isV2Project(projectConfig.platformVersion)) {
66
66
  if (args.profile) {
67
- try {
68
- profile = loadProfile(projectConfig, projectDir, args.profile);
69
- }
70
- catch (error) {
71
- logError(error);
67
+ profile = loadProfile(projectConfig, projectDir, args.profile);
68
+ if (!profile) {
72
69
  uiLine();
73
70
  process.exit(EXIT_CODES.ERROR);
74
71
  }
@@ -78,13 +75,7 @@ async function handler(args) {
78
75
  }
79
76
  else {
80
77
  // A profile must be specified if this project has profiles configured
81
- try {
82
- await enforceProfileUsage(projectConfig, projectDir);
83
- }
84
- catch (error) {
85
- logError(error);
86
- process.exit(EXIT_CODES.ERROR);
87
- }
78
+ await exitIfUsingProfiles(projectConfig, projectDir);
88
79
  }
89
80
  }
90
81
  if (!targetProjectAccountId) {
@@ -123,7 +123,13 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
123
123
  // Check for missing/outdated dependencies
124
124
  await checkAndInstallDependencies();
125
125
  // End setup, start local dev process
126
- await startPortManagerServer();
126
+ try {
127
+ await startPortManagerServer();
128
+ }
129
+ catch (e) {
130
+ logError(e);
131
+ process.exit(EXIT_CODES.ERROR);
132
+ }
127
133
  const localDevProcess = new LocalDevProcess({
128
134
  initialProjectNodes: projectNodes,
129
135
  initialProjectProfileData: projectProfileData,
@@ -28,14 +28,8 @@ async function handler(args) {
28
28
  process.exit(EXIT_CODES.ERROR);
29
29
  }
30
30
  let targetAccountId;
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);
31
+ if (isV2Project(projectConfig.platformVersion)) {
32
+ targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
39
33
  }
40
34
  targetAccountId = targetAccountId || derivedAccountId;
41
35
  const accountConfig = getConfigAccountById(targetAccountId);