@hubspot/cli 7.8.0-experimental.0 → 7.8.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.
- package/bin/cli.js +0 -2
- package/commands/getStarted.d.ts +1 -1
- package/commands/getStarted.js +64 -16
- package/commands/mcp/setup.js +8 -0
- package/commands/project/dev/unifiedFlow.js +1 -1
- package/commands/project/migrate.js +30 -21
- package/lang/en.d.ts +4 -1
- package/lang/en.js +5 -1
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/app/__tests__/migrate.test.js +14 -51
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +5 -80
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +3 -0
- package/lib/dependencyManagement.d.ts +0 -5
- package/lib/dependencyManagement.js +0 -9
- package/lib/hasFeature.js +6 -0
- package/lib/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.js +1 -1
- package/lib/projects/create/v3.js +3 -2
- package/lib/projects/localDev/helpers/project.d.ts +2 -2
- package/lib/projects/localDev/helpers/project.js +5 -6
- package/lib/theme/__tests__/migrate.test.d.ts +1 -0
- package/lib/theme/__tests__/migrate.test.js +233 -0
- package/lib/theme/migrate.d.ts +13 -0
- package/lib/theme/migrate.js +90 -0
- package/lib/ui/SpinniesManager.js +105 -8
- package/lib/usageTracking.js +2 -2
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
- package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/CreateProjectTool.js +5 -5
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/DocFetchTool.js +2 -2
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +7 -7
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +11 -5
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +2 -2
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
- package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
- package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/tools/project/constants.js +9 -3
- package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
- package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
- package/mcp-server/utils/cliConfig.d.ts +1 -0
- package/mcp-server/utils/cliConfig.js +12 -0
- package/package.json +2 -7
- package/ui/components/HorizontalSelectPrompt.js +1 -1
- package/commands/getStartedV2.d.ts +0 -9
- package/commands/getStartedV2.js +0 -39
- package/ui/components/Ascii.d.ts +0 -10
- package/ui/components/Ascii.js +0 -11
- package/ui/views/GetStarted.d.ts +0 -7
- package/ui/views/GetStarted.js +0 -157
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
2
2
|
import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
|
-
import { validateUid
|
|
4
|
+
import { validateUid } from '@hubspot/project-parsing-lib';
|
|
5
5
|
import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
6
6
|
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
|
|
7
7
|
import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
@@ -12,7 +12,7 @@ import { poll } from '../../polling.js';
|
|
|
12
12
|
import { CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, listAppsForMigration, } from '../../../api/migrate.js';
|
|
13
13
|
import { lib } from '../../../lang/en.js';
|
|
14
14
|
import { hasUnfiedAppsAccess } from '../../hasFeature.js';
|
|
15
|
-
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup,
|
|
15
|
+
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationApps, } from '../migrate.js';
|
|
16
16
|
vi.mock('@hubspot/local-dev-lib/logger');
|
|
17
17
|
vi.mock('@hubspot/local-dev-lib/path');
|
|
18
18
|
vi.mock('@hubspot/local-dev-lib/archive');
|
|
@@ -40,7 +40,6 @@ const mockedListPrompt = listPrompt;
|
|
|
40
40
|
const mockedEnsureProjectExists = ensureProjectExists;
|
|
41
41
|
const mockedPoll = poll;
|
|
42
42
|
const mockedListAppsForMigration = listAppsForMigration;
|
|
43
|
-
const mockedGetProjectThemeDetails = getProjectThemeDetails;
|
|
44
43
|
const mockedInitializeAppMigration = initializeAppMigration;
|
|
45
44
|
const mockedContinueAppMigration = continueAppMigration;
|
|
46
45
|
const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
|
|
@@ -205,58 +204,31 @@ describe('lib/app/migrate', () => {
|
|
|
205
204
|
expect(result.migratableApps[0].projectName).toBe(PROJECT_NAME);
|
|
206
205
|
});
|
|
207
206
|
});
|
|
208
|
-
describe('
|
|
209
|
-
it('should return false when no projectConfig is provided', async () => {
|
|
210
|
-
const result = await getHasMigratableThemes();
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
hasMigratableThemes: false,
|
|
213
|
-
migratableThemesCount: 0,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
it('should return true when there are migratable themes', async () => {
|
|
217
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
218
|
-
legacyThemeDetails: [
|
|
219
|
-
{
|
|
220
|
-
configFilepath: 'src/theme.json',
|
|
221
|
-
themePath: 'src/theme',
|
|
222
|
-
themeConfig: {
|
|
223
|
-
secret_names: ['my-secret'],
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
legacyReactThemeDetails: [],
|
|
228
|
-
});
|
|
229
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
230
|
-
const result = await getHasMigratableThemes(projectConfig);
|
|
231
|
-
expect(result).toEqual({
|
|
232
|
-
hasMigratableThemes: true,
|
|
233
|
-
migratableThemesCount: 1,
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
describe('validateMigrationAppsAndThemes', () => {
|
|
207
|
+
describe('validateMigrationApps', () => {
|
|
238
208
|
const mockMigratableApp1 = createMockMigratableApp(1, 'App 1', PROJECT_NAME);
|
|
239
209
|
const mockMigratableApp2 = createMockMigratableApp(2, 'App 2', PROJECT_NAME);
|
|
240
210
|
it('should throw an error when multiple apps are found for a project', async () => {
|
|
241
211
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
242
|
-
await expect(
|
|
212
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
243
213
|
migratableApps: [mockMigratableApp1, mockMigratableApp2],
|
|
244
214
|
unmigratableApps: [],
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
it('should throw an error when cms themes are found for a project', async () => {
|
|
248
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
249
|
-
await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [mockMigratableApp1], unmigratableApps: [] }, true, projectConfig)).rejects.toThrow(lib.migrate.errors.project.themesAndAppsNotAllowed);
|
|
215
|
+
}, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
|
|
250
216
|
});
|
|
251
217
|
it('should throw an error when no apps are found for a project', async () => {
|
|
252
218
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
253
|
-
await expect(
|
|
219
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
|
|
254
220
|
});
|
|
255
221
|
it('should throw an error when no migratable apps are found', async () => {
|
|
256
|
-
await expect(
|
|
222
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
223
|
+
migratableApps: [],
|
|
224
|
+
unmigratableApps: mockUnmigratableApps,
|
|
225
|
+
})).rejects.toThrow(/No apps in account/);
|
|
257
226
|
});
|
|
258
227
|
it('should throw an error when appId is provided but not found', async () => {
|
|
259
|
-
await expect(
|
|
228
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
229
|
+
migratableApps: [],
|
|
230
|
+
unmigratableApps: [],
|
|
231
|
+
})).rejects.toThrow(/No apps in account/);
|
|
260
232
|
});
|
|
261
233
|
});
|
|
262
234
|
describe('promptForAppToMigrate', () => {
|
|
@@ -330,10 +302,6 @@ describe('lib/app/migrate', () => {
|
|
|
330
302
|
unmigratableApps: [],
|
|
331
303
|
},
|
|
332
304
|
});
|
|
333
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
334
|
-
legacyThemeDetails: [],
|
|
335
|
-
legacyReactThemeDetails: [],
|
|
336
|
-
});
|
|
337
305
|
mockedListPrompt.mockResolvedValue({ appId: 1 });
|
|
338
306
|
mockedConfirmPrompt.mockResolvedValue(true);
|
|
339
307
|
mockedEnsureProjectExists.mockResolvedValue({ projectExists: false });
|
|
@@ -352,16 +320,11 @@ describe('lib/app/migrate', () => {
|
|
|
352
320
|
unmigratableApps: [],
|
|
353
321
|
},
|
|
354
322
|
});
|
|
355
|
-
mockedGetProjectThemeDetails.mockResolvedValueOnce({
|
|
356
|
-
legacyThemeDetails: [],
|
|
357
|
-
legacyReactThemeDetails: [],
|
|
358
|
-
});
|
|
359
323
|
const result = await handleMigrationSetup(ACCOUNT_ID, defaultOptions, projectConfig);
|
|
360
324
|
expect(result).toEqual({
|
|
361
325
|
appIdToMigrate: 1,
|
|
362
326
|
projectName: PROJECT_NAME,
|
|
363
327
|
projectDest: MOCK_PROJECT_DIR,
|
|
364
|
-
isThemesMigration: false,
|
|
365
328
|
});
|
|
366
329
|
});
|
|
367
330
|
it('should prompt for project name when not provided', async () => {
|
package/lib/app/migrate.d.ts
CHANGED
|
@@ -12,18 +12,14 @@ export type MigrateAppArgs = CommonArgs & AccountArgs & EnvironmentArgs & Config
|
|
|
12
12
|
export declare function getUnmigratableReason(reasonCode: string, projectName: string | undefined, accountId: number): string;
|
|
13
13
|
export declare function generateFilterAppsByProjectNameFunction(projectConfig?: LoadedProjectConfig): (app: MigrationApp) => boolean;
|
|
14
14
|
export declare function buildErrorMessageFromMigrationStatus(error: MigrationFailed): string;
|
|
15
|
-
export declare function getHasMigratableThemes(projectConfig?: LoadedProjectConfig): Promise<{
|
|
16
|
-
hasMigratableThemes: boolean;
|
|
17
|
-
migratableThemesCount: number;
|
|
18
|
-
}>;
|
|
19
15
|
export declare function fetchMigrationApps(derivedAccountId: number, platformVersion: string, projectConfig?: LoadedProjectConfig): Promise<{
|
|
20
16
|
migratableApps: MigratableApp[];
|
|
21
17
|
unmigratableApps: UnmigratableApp[];
|
|
22
18
|
}>;
|
|
23
|
-
export declare function
|
|
19
|
+
export declare function validateMigrationApps(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
|
|
24
20
|
migratableApps: MigratableApp[];
|
|
25
21
|
unmigratableApps: UnmigratableApp[];
|
|
26
|
-
},
|
|
22
|
+
}, projectConfig?: LoadedProjectConfig): Promise<void>;
|
|
27
23
|
export declare function promptForAppToMigrate(allApps: MigrationApp[], derivedAccountId: number): Promise<number>;
|
|
28
24
|
export declare function selectAppToMigrate(allApps: MigrationApp[], derivedAccountId: number, appId?: number): Promise<{
|
|
29
25
|
proceed: boolean;
|
|
@@ -33,9 +29,7 @@ export declare function handleMigrationSetup(derivedAccountId: number, options:
|
|
|
33
29
|
appIdToMigrate?: number | undefined;
|
|
34
30
|
projectName?: string;
|
|
35
31
|
projectDest?: string;
|
|
36
|
-
isThemesMigration?: boolean;
|
|
37
32
|
}>;
|
|
38
|
-
export declare function handleThemesMigration(projectConfig: LoadedProjectConfig, platformVersion: string): Promise<void>;
|
|
39
33
|
export declare function beginAppMigration(derivedAccountId: number, appId: number, platformVersion: string): Promise<{
|
|
40
34
|
migrationId: number;
|
|
41
35
|
uidMap: Record<string, string>;
|
package/lib/app/migrate.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import { validateUid
|
|
5
|
+
import { validateUid } from '@hubspot/project-parsing-lib';
|
|
6
6
|
import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
7
7
|
import { mapToUserFacingType } from '@hubspot/project-parsing-lib/src/lib/transform.js';
|
|
8
8
|
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
|
|
@@ -10,19 +10,15 @@ import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
|
10
10
|
import { Separator } from '@inquirer/prompts';
|
|
11
11
|
import { confirmPrompt, inputPrompt, listPrompt, } from '../prompts/promptUtils.js';
|
|
12
12
|
import { uiAccountDescription, uiCommandReference, uiLine, uiLink, } from '../ui/index.js';
|
|
13
|
-
import { writeProjectConfig } from '../projects/config.js';
|
|
14
13
|
import { ensureProjectExists } from '../projects/ensureProjectExists.js';
|
|
15
14
|
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
16
15
|
import { DEFAULT_POLLING_STATUS_LOOKUP, poll } from '../polling.js';
|
|
17
16
|
import { checkMigrationStatusV2, CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, isMigrationStatus, listAppsForMigration, } from '../../api/migrate.js';
|
|
18
17
|
import fs from 'fs';
|
|
19
18
|
import { lib } from '../../lang/en.js';
|
|
20
|
-
import { PROJECT_CONFIG_FILE } from '../constants.js';
|
|
21
19
|
import { hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
22
20
|
import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
|
|
23
21
|
import { uiLogger } from '../ui/logger.js';
|
|
24
|
-
import { debugError } from '../errorHandlers/index.js';
|
|
25
|
-
import { useV3Api } from '../projects/platformVersion.js';
|
|
26
22
|
export function getUnmigratableReason(reasonCode, projectName, accountId) {
|
|
27
23
|
switch (reasonCode) {
|
|
28
24
|
case UNMIGRATABLE_REASONS.UP_TO_DATE:
|
|
@@ -59,17 +55,6 @@ export function buildErrorMessageFromMigrationStatus(error) {
|
|
|
59
55
|
})
|
|
60
56
|
.join('\n\t- ')}`;
|
|
61
57
|
}
|
|
62
|
-
export async function getHasMigratableThemes(projectConfig) {
|
|
63
|
-
if (!projectConfig?.projectConfig?.name || !projectConfig?.projectDir) {
|
|
64
|
-
return { hasMigratableThemes: false, migratableThemesCount: 0 };
|
|
65
|
-
}
|
|
66
|
-
const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
67
|
-
const { legacyThemeDetails, legacyReactThemeDetails } = await getProjectThemeDetails(projectSrcDir);
|
|
68
|
-
return {
|
|
69
|
-
hasMigratableThemes: legacyThemeDetails.length > 0 || legacyReactThemeDetails.length > 0,
|
|
70
|
-
migratableThemesCount: legacyThemeDetails.length + legacyReactThemeDetails.length,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
58
|
export async function fetchMigrationApps(derivedAccountId, platformVersion, projectConfig) {
|
|
74
59
|
const { data: { migratableApps, unmigratableApps }, } = await listAppsForMigration(derivedAccountId, platformVersion);
|
|
75
60
|
const filteredMigratableApps = migratableApps.filter(generateFilterAppsByProjectNameFunction(projectConfig));
|
|
@@ -79,23 +64,11 @@ export async function fetchMigrationApps(derivedAccountId, platformVersion, proj
|
|
|
79
64
|
unmigratableApps: filteredUnmigratableApps,
|
|
80
65
|
};
|
|
81
66
|
}
|
|
82
|
-
export async function
|
|
67
|
+
export async function validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps, }, projectConfig) {
|
|
83
68
|
const allApps = [...migratableApps, ...unmigratableApps];
|
|
84
69
|
if (allApps.length > 1 && projectConfig) {
|
|
85
70
|
throw new Error(lib.migrate.errors.project.multipleApps);
|
|
86
71
|
}
|
|
87
|
-
if (hasMigratableThemes) {
|
|
88
|
-
if (useV3Api(projectConfig?.projectConfig?.platformVersion)) {
|
|
89
|
-
throw new Error(lib.migrate.errors.project.themesAlreadyMigrated);
|
|
90
|
-
}
|
|
91
|
-
if (allApps.length > 0 && projectConfig) {
|
|
92
|
-
throw new Error(lib.migrate.errors.project.themesAndAppsNotAllowed);
|
|
93
|
-
}
|
|
94
|
-
if (!projectConfig) {
|
|
95
|
-
throw new Error(lib.migrate.errors.project.noProjectForThemesMigration);
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
72
|
if (!projectConfig?.projectConfig) {
|
|
100
73
|
allApps.forEach(app => {
|
|
101
74
|
if (app.projectName) {
|
|
@@ -178,35 +151,20 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
|
|
|
178
151
|
text: lib.migrate.spinners.checkingForMigratableComponents,
|
|
179
152
|
});
|
|
180
153
|
const { name, dest, appId } = options;
|
|
181
|
-
const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
|
|
182
154
|
const { migratableApps, unmigratableApps } = await fetchMigrationApps(derivedAccountId, options.platformVersion, projectConfig);
|
|
183
155
|
SpinniesManager.remove('checkingForMigratableComponents');
|
|
184
|
-
await
|
|
156
|
+
await validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps }, projectConfig);
|
|
185
157
|
const allApps = [...migratableApps, ...unmigratableApps];
|
|
186
|
-
|
|
187
|
-
let appIdToMigrate;
|
|
188
|
-
if (hasMigratableThemes) {
|
|
189
|
-
uiLogger.log(lib.migrate.prompt.themesMigration(migratableThemesCount));
|
|
190
|
-
proceed = await confirmPrompt(lib.migrate.prompt.proceed, {
|
|
191
|
-
defaultAnswer: false,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
const appSelectionPrompt = await selectAppToMigrate(allApps, derivedAccountId, appId);
|
|
196
|
-
appIdToMigrate = appSelectionPrompt.appIdToMigrate;
|
|
197
|
-
proceed = appSelectionPrompt.proceed;
|
|
198
|
-
}
|
|
158
|
+
const { proceed, appIdToMigrate } = await selectAppToMigrate(allApps, derivedAccountId, appId);
|
|
199
159
|
if (!proceed) {
|
|
200
160
|
return {};
|
|
201
161
|
}
|
|
202
162
|
// If it's a project we don't want to prompt for dest and name, so just return early
|
|
203
|
-
// Theme migrations can only be initiated from the project directory
|
|
204
163
|
if (projectConfig &&
|
|
205
164
|
projectConfig?.projectConfig &&
|
|
206
165
|
projectConfig?.projectDir) {
|
|
207
166
|
return {
|
|
208
167
|
appIdToMigrate,
|
|
209
|
-
isThemesMigration: hasMigratableThemes,
|
|
210
168
|
projectName: projectConfig.projectConfig.name,
|
|
211
169
|
projectDest: projectConfig.projectDir,
|
|
212
170
|
};
|
|
@@ -231,35 +189,6 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
|
|
|
231
189
|
}));
|
|
232
190
|
return { appIdToMigrate, projectName, projectDest };
|
|
233
191
|
}
|
|
234
|
-
export async function handleThemesMigration(projectConfig, platformVersion) {
|
|
235
|
-
if (!projectConfig?.projectDir || !projectConfig?.projectConfig?.srcDir) {
|
|
236
|
-
throw new Error(lib.migrate.errors.project.invalidConfig);
|
|
237
|
-
}
|
|
238
|
-
const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
239
|
-
let migrated = false;
|
|
240
|
-
let failureReason;
|
|
241
|
-
try {
|
|
242
|
-
const migrationResult = await migrateThemes(projectConfig.projectDir, projectSrcDir);
|
|
243
|
-
migrated = migrationResult.migrated;
|
|
244
|
-
failureReason = migrationResult.failureReason;
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
debugError(error);
|
|
248
|
-
throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
|
|
249
|
-
}
|
|
250
|
-
if (!migrated) {
|
|
251
|
-
throw new Error(failureReason || lib.migrate.errors.project.failedToMigrateThemes);
|
|
252
|
-
}
|
|
253
|
-
const newProjectConfig = { ...projectConfig.projectConfig };
|
|
254
|
-
newProjectConfig.platformVersion = platformVersion;
|
|
255
|
-
const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
|
|
256
|
-
const success = writeProjectConfig(projectConfigPath, newProjectConfig);
|
|
257
|
-
if (!success) {
|
|
258
|
-
throw new Error(lib.migrate.errors.project.failedToUpdateProjectConfig);
|
|
259
|
-
}
|
|
260
|
-
uiLogger.log('');
|
|
261
|
-
uiLogger.log(lib.migrate.success.themesMigrationSuccess(platformVersion));
|
|
262
|
-
}
|
|
263
192
|
export async function beginAppMigration(derivedAccountId, appId, platformVersion) {
|
|
264
193
|
SpinniesManager.add('beginningMigration', {
|
|
265
194
|
text: lib.migrate.spinners.beginningMigration,
|
|
@@ -412,11 +341,7 @@ export async function migrateApp2025_2(derivedAccountId, options, projectConfig)
|
|
|
412
341
|
throw new Error(lib.migrate.errors.project.doesNotExist(derivedAccountId));
|
|
413
342
|
}
|
|
414
343
|
}
|
|
415
|
-
const { appIdToMigrate, projectName, projectDest
|
|
416
|
-
if (isThemesMigration) {
|
|
417
|
-
await handleThemesMigration(projectConfig, options.platformVersion);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
344
|
+
const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
|
|
420
345
|
if (!appIdToMigrate || !projectName || !projectDest) {
|
|
421
346
|
return;
|
|
422
347
|
}
|
package/lib/constants.d.ts
CHANGED
|
@@ -81,6 +81,9 @@ export declare const FEATURES: {
|
|
|
81
81
|
readonly SANDBOXES_V2: "sandboxes:v2:enabled";
|
|
82
82
|
readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
|
|
83
83
|
readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
|
|
84
|
+
readonly APPS_HOME: "UIE:AppHome";
|
|
85
|
+
readonly MCP_ACCESS: "Developers:CLIMCPAccess";
|
|
86
|
+
readonly THEME_MIGRATION_2025_2: "Developers:ProjectThemeMigrations:2025.2";
|
|
84
87
|
};
|
|
85
88
|
export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
|
|
86
89
|
UPLOAD_SUCCESS: string;
|
package/lib/constants.js
CHANGED
|
@@ -73,6 +73,9 @@ export const FEATURES = {
|
|
|
73
73
|
SANDBOXES_V2: 'sandboxes:v2:enabled',
|
|
74
74
|
SANDBOXES_V2_CLI: 'sandboxes:v2:cliEnabled',
|
|
75
75
|
APP_EVENTS: 'Developers:UnifiedApps:AppEventsAccess',
|
|
76
|
+
APPS_HOME: 'UIE:AppHome',
|
|
77
|
+
MCP_ACCESS: 'Developers:CLIMCPAccess',
|
|
78
|
+
THEME_MIGRATION_2025_2: 'Developers:ProjectThemeMigrations:2025.2',
|
|
76
79
|
};
|
|
77
80
|
export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
|
|
78
81
|
UPLOAD_SUCCESS: 'server:uploadSuccess',
|
|
@@ -2,10 +2,5 @@ export declare function installPackages({ packages, installLocations, }: {
|
|
|
2
2
|
packages?: string[];
|
|
3
3
|
installLocations?: string[];
|
|
4
4
|
}): Promise<void>;
|
|
5
|
-
export declare function installPackagesV2({ packages, installLocations, }: {
|
|
6
|
-
packages?: string[];
|
|
7
|
-
installLocations?: string[];
|
|
8
|
-
}): Promise<void>;
|
|
9
|
-
export declare function installPackagesInDirectoryV2(directory: string, packages?: string[]): Promise<void>;
|
|
10
5
|
export declare function getProjectPackageJsonLocations(dir?: string): Promise<string[]>;
|
|
11
6
|
export declare function hasMissingPackages(directory: string): Promise<boolean>;
|
|
@@ -22,15 +22,6 @@ export async function installPackages({ packages, installLocations, }) {
|
|
|
22
22
|
await installPackagesInDirectory(dir, packages);
|
|
23
23
|
}));
|
|
24
24
|
}
|
|
25
|
-
export async function installPackagesV2({ packages, installLocations, }) {
|
|
26
|
-
const installDirs = installLocations || (await getProjectPackageJsonLocations());
|
|
27
|
-
await Promise.all(installDirs.map(async (dir) => {
|
|
28
|
-
await installPackagesInDirectoryV2(dir, packages);
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
export async function installPackagesInDirectoryV2(directory, packages) {
|
|
32
|
-
await executeInstall(packages, null, { cwd: directory });
|
|
33
|
-
}
|
|
34
25
|
async function installPackagesInDirectory(directory, packages) {
|
|
35
26
|
const spinner = `installingDependencies-${directory}`;
|
|
36
27
|
const relativeDir = path.relative(process.cwd(), directory);
|
package/lib/hasFeature.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { http } from '@hubspot/local-dev-lib/http';
|
|
2
2
|
import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
3
|
+
import { FEATURES } from './constants.js';
|
|
4
|
+
const FEATURES_THAT_DEFAULT_ON = [FEATURES.APPS_HOME];
|
|
3
5
|
export async function hasFeature(accountId, feature) {
|
|
4
6
|
const { data: { enabledFeatures }, } = await fetchEnabledFeatures(accountId);
|
|
7
|
+
if (enabledFeatures[feature] === undefined &&
|
|
8
|
+
FEATURES_THAT_DEFAULT_ON.includes(feature)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
5
11
|
return Boolean(enabledFeatures[feature]);
|
|
6
12
|
}
|
|
7
13
|
export async function hasUnfiedAppsAccess(accountId) {
|
package/lib/links.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ type SiteLink = {
|
|
|
7
7
|
export declare function getSiteLinksAsArray(accountId: number): SiteLink[];
|
|
8
8
|
export declare function logSiteLinks(accountId: number): void;
|
|
9
9
|
export declare function openLink(accountId: number, shortcut: string): void;
|
|
10
|
+
export declare function getProductUpdatesUrl(rolloutId: string, accountId?: number): string;
|
|
10
11
|
export {};
|
package/lib/links.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
|
4
4
|
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
|
|
5
5
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
6
6
|
import { getTableContents, getTableHeader } from './ui/table.js';
|
|
7
|
-
const
|
|
7
|
+
const COMMON_SITE_LINKS = {
|
|
8
8
|
APPS_MARKETPLACE: {
|
|
9
9
|
shortcut: 'apps-marketplace',
|
|
10
10
|
alias: 'apm',
|
|
@@ -76,7 +76,7 @@ const SITE_LINKS = {
|
|
|
76
76
|
};
|
|
77
77
|
export function getSiteLinksAsArray(accountId) {
|
|
78
78
|
const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
79
|
-
return Object.values(
|
|
79
|
+
return Object.values(COMMON_SITE_LINKS)
|
|
80
80
|
.sort((a, b) => (a.shortcut < b.shortcut ? -1 : 1))
|
|
81
81
|
.map(l => ({ ...l, url: l.getUrl(accountId, baseUrl) }));
|
|
82
82
|
}
|
|
@@ -90,7 +90,7 @@ export function logSiteLinks(accountId) {
|
|
|
90
90
|
logger.log(getTableContents(linksAsArray));
|
|
91
91
|
}
|
|
92
92
|
export function openLink(accountId, shortcut) {
|
|
93
|
-
const match = Object.values(
|
|
93
|
+
const match = Object.values(COMMON_SITE_LINKS).find(l => l.shortcut === shortcut || (l.alias && l.alias === shortcut));
|
|
94
94
|
if (!match) {
|
|
95
95
|
logger.error(`We couldn't find a shortcut matching ${shortcut}. Type 'hs open list' to see a list of available shortcuts`);
|
|
96
96
|
return;
|
|
@@ -99,3 +99,10 @@ export function openLink(accountId, shortcut) {
|
|
|
99
99
|
open(match.getUrl(accountId, baseUrl), { url: true });
|
|
100
100
|
logger.success(`We opened ${match.getUrl(accountId, baseUrl)} in your browser`);
|
|
101
101
|
}
|
|
102
|
+
export function getProductUpdatesUrl(rolloutId, accountId) {
|
|
103
|
+
const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
104
|
+
if (accountId) {
|
|
105
|
+
return `${baseUrl}/product-updates/${accountId}/in-beta?rollout=${rolloutId}`;
|
|
106
|
+
}
|
|
107
|
+
return `${baseUrl}/l/product-updates/in-beta?rollout=${rolloutId}`;
|
|
108
|
+
}
|
package/lib/mcp/setup.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from 'path';
|
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
10
|
import { existsSync } from 'fs';
|
|
11
|
-
const mcpServerName = '
|
|
11
|
+
const mcpServerName = 'HubSpot';
|
|
12
12
|
const claudeCode = 'claude';
|
|
13
13
|
const windsurf = 'windsurf';
|
|
14
14
|
const cursor = 'cursor';
|
|
@@ -9,7 +9,7 @@ import { getConfigForPlatformVersion } from './legacy.js';
|
|
|
9
9
|
import { logError } from '../../errorHandlers/index.js';
|
|
10
10
|
import { EXIT_CODES } from '../../enums/exitCodes.js';
|
|
11
11
|
import { hasFeature } from '../../hasFeature.js';
|
|
12
|
-
import { AppEventsKey } from '@hubspot/project-parsing-lib/src/lib/constants.js';
|
|
12
|
+
import { AppEventsKey, PagesKey, } from '@hubspot/project-parsing-lib/src/lib/constants.js';
|
|
13
13
|
export async function createV3App(providedAuth, providedDistribution) {
|
|
14
14
|
let authType;
|
|
15
15
|
if (providedAuth &&
|
|
@@ -51,6 +51,7 @@ export async function createV3App(providedAuth, providedDistribution) {
|
|
|
51
51
|
}
|
|
52
52
|
const componentTypeToGateMap = {
|
|
53
53
|
[AppEventsKey]: FEATURES.APP_EVENTS,
|
|
54
|
+
[PagesKey]: FEATURES.APPS_HOME,
|
|
54
55
|
};
|
|
55
56
|
export async function calculateComponentTemplateChoices(components, authType, distribution, accountId, projectMetadata) {
|
|
56
57
|
const enabledComponents = [];
|
|
@@ -88,7 +89,7 @@ export async function calculateComponentTemplateChoices(components, authType, di
|
|
|
88
89
|
}
|
|
89
90
|
if (disabledReasons.length > 0) {
|
|
90
91
|
disabledComponents.push({
|
|
91
|
-
name: `[${chalk.yellow('DISABLED')}] ${template.label}
|
|
92
|
+
name: `[${chalk.yellow('DISABLED')}] ${template.label} -`,
|
|
92
93
|
value: template,
|
|
93
94
|
disabled: disabledReasons.join(' '),
|
|
94
95
|
});
|
|
@@ -6,7 +6,7 @@ export declare function createNewProjectForLocalDev(projectConfig: ProjectConfig
|
|
|
6
6
|
export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number, sendIR?: boolean, profile?: string): Promise<Build>;
|
|
7
7
|
export declare function compareLocalProjectToDeployed(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number | undefined, localProjectNodes: {
|
|
8
8
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
9
|
-
}): Promise<void>;
|
|
9
|
+
}, profile?: string): Promise<void>;
|
|
10
10
|
export declare function isDeployedProjectUpToDateWithLocal(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, localProjectNodes: {
|
|
11
11
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
12
|
-
}): Promise<boolean>;
|
|
12
|
+
}, profile?: string): Promise<boolean>;
|
|
@@ -123,7 +123,7 @@ export async function createInitialBuildForNewProject(projectConfig, projectDir,
|
|
|
123
123
|
}
|
|
124
124
|
return initialUploadResult.buildResult;
|
|
125
125
|
}
|
|
126
|
-
export async function compareLocalProjectToDeployed(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
126
|
+
export async function compareLocalProjectToDeployed(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
|
|
127
127
|
uiLogger.log('');
|
|
128
128
|
if (!deployedBuildId) {
|
|
129
129
|
uiLogger.error(lib.localDevHelpers.project.compareLocalProjectToDeployed.noDeployedBuild(projectConfig.name, uiAccountDescription(accountId)));
|
|
@@ -132,7 +132,7 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
|
|
|
132
132
|
SpinniesManager.add('compareLocalProjectToDeployed', {
|
|
133
133
|
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.checking,
|
|
134
134
|
});
|
|
135
|
-
const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes);
|
|
135
|
+
const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile);
|
|
136
136
|
if (isUpToDate) {
|
|
137
137
|
SpinniesManager.succeed('compareLocalProjectToDeployed', {
|
|
138
138
|
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.upToDate,
|
|
@@ -144,12 +144,11 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
|
|
|
144
144
|
.notUpToDate,
|
|
145
145
|
});
|
|
146
146
|
uiLogger.log('');
|
|
147
|
-
uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed
|
|
148
|
-
.notUpToDateExplanation);
|
|
147
|
+
uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed.notUpToDateExplanation(profile));
|
|
149
148
|
process.exit(EXIT_CODES.SUCCESS);
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
|
-
export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
151
|
+
export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
|
|
153
152
|
let tempDir = null;
|
|
154
153
|
try {
|
|
155
154
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hubspot-project-compare-'));
|
|
@@ -161,7 +160,7 @@ export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountI
|
|
|
161
160
|
projectSourceDir: deployedProjectSourceDir,
|
|
162
161
|
platformVersion: projectConfig.platformVersion,
|
|
163
162
|
accountId: accountId,
|
|
164
|
-
}, {});
|
|
163
|
+
}, { profile });
|
|
165
164
|
return isDeepEqual(localProjectNodes, deployedProjectNodes, ['localDev']);
|
|
166
165
|
}
|
|
167
166
|
finally {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|