@hubspot/cli 7.10.0-beta.1 → 7.10.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.
- package/bin/cli.js +5 -4
- package/commands/__tests__/getStarted.test.js +10 -0
- package/commands/__tests__/project.test.js +3 -0
- package/commands/account/__tests__/rename.test.js +10 -3
- package/commands/account/auth.js +10 -14
- package/commands/account/clean.js +11 -19
- package/commands/account/createOverride.js +15 -11
- package/commands/account/info.js +8 -5
- package/commands/account/list.js +15 -19
- package/commands/account/remove.js +23 -22
- package/commands/account/removeOverride.js +6 -6
- package/commands/account/rename.js +2 -2
- package/commands/account/use.js +19 -8
- package/commands/app/__tests__/migrate.test.js +8 -4
- package/commands/app/migrate.js +2 -2
- package/commands/auth.js +18 -14
- package/commands/config/migrate.js +5 -5
- package/commands/customObject/createSchema.js +2 -3
- package/commands/customObject/updateSchema.js +2 -3
- package/commands/getStarted.js +2 -3
- package/commands/hubdb/__tests__/list.test.js +1 -0
- package/commands/hubdb/list.js +2 -2
- package/commands/init.js +36 -32
- package/commands/project/__tests__/deploy.test.js +10 -5
- package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
- package/commands/project/__tests__/lint.test.js +709 -0
- package/commands/project/__tests__/logs.test.js +4 -0
- package/commands/project/__tests__/validate.test.js +2 -2
- package/commands/project/cloneApp.js +2 -2
- package/commands/project/deploy.js +2 -2
- package/commands/project/dev/deprecatedFlow.js +4 -5
- package/commands/project/dev/index.js +6 -3
- package/commands/project/dev/unifiedFlow.js +4 -5
- package/commands/project/lint.d.ts +6 -0
- package/commands/project/lint.js +178 -0
- package/commands/project/logs.js +2 -3
- package/commands/project/profile/add.js +6 -7
- package/commands/project/profile/delete.js +2 -2
- package/commands/project/upload.js +2 -2
- package/commands/project/validate.js +2 -2
- package/commands/project.js +2 -0
- package/commands/sandbox/__tests__/create.test.js +14 -5
- package/commands/sandbox/create.js +4 -5
- package/commands/sandbox/delete.js +23 -20
- package/commands/testAccount/__tests__/create.test.js +5 -5
- package/commands/testAccount/create.js +2 -2
- package/commands/testAccount/delete.js +9 -8
- package/lang/en.d.ts +40 -6
- package/lang/en.js +54 -14
- package/lib/__tests__/buildAccount.test.js +22 -30
- package/lib/__tests__/commonOpts.test.js +9 -13
- package/lib/__tests__/developerTestAccounts.test.js +29 -17
- package/lib/__tests__/importData.test.js +20 -10
- package/lib/__tests__/oauth.test.js +19 -8
- package/lib/__tests__/sandboxSync.test.js +33 -11
- package/lib/__tests__/sandboxes.test.js +30 -19
- package/lib/__tests__/usageTracking.test.js +10 -10
- package/lib/__tests__/validation.test.js +32 -32
- package/lib/accountTypes.d.ts +9 -9
- package/lib/accountTypes.js +2 -4
- package/lib/app/__tests__/migrate.test.js +15 -0
- package/lib/app/__tests__/migrate_legacy.test.js +9 -0
- package/lib/app/migrate_legacy.d.ts +2 -2
- package/lib/buildAccount.d.ts +4 -4
- package/lib/buildAccount.js +7 -14
- package/lib/commonOpts.js +3 -3
- package/lib/configMigrate.d.ts +2 -2
- package/lib/configMigrate.js +42 -18
- package/lib/configOptions.js +3 -2
- package/lib/developerTestAccounts.d.ts +3 -3
- package/lib/developerTestAccounts.js +4 -7
- package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
- package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
- package/lib/doctor/Doctor.js +4 -3
- package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
- package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
- package/lib/doctor/__tests__/Doctor.test.js +14 -0
- package/lib/importData.js +8 -7
- package/lib/links.js +5 -5
- package/lib/middleware/{__test__ → __tests__}/commandTargetingUtils.test.js +3 -3
- package/lib/middleware/{__test__ → __tests__}/configMiddleware.test.js +23 -22
- package/lib/middleware/{__test__ → __tests__}/gitMiddleware.test.js +9 -7
- package/lib/middleware/autoUpdateMiddleware.js +12 -4
- package/lib/middleware/commandTargetingUtils.js +3 -2
- package/lib/middleware/configMiddleware.d.ts +6 -1
- package/lib/middleware/configMiddleware.js +36 -15
- package/lib/middleware/gitMiddleware.js +8 -4
- package/lib/oauth.d.ts +2 -2
- package/lib/oauth.js +8 -10
- package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
- package/lib/projects/__tests__/DevServerManager.test.js +1 -0
- package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
- package/lib/projects/__tests__/components.test.js +1 -1
- package/lib/projects/__tests__/deploy.test.js +1 -0
- package/lib/projects/__tests__/uieLinting.test.js +640 -0
- package/lib/projects/components.js +1 -1
- package/lib/projects/create/__tests__/v2.test.js +11 -0
- package/lib/projects/localDev/AppDevModeInterface.js +2 -2
- package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
- package/lib/projects/localDev/DevSessionManager.d.ts +17 -0
- package/lib/projects/localDev/DevSessionManager.js +56 -0
- package/lib/projects/localDev/LocalDevLogger.d.ts +3 -0
- package/lib/projects/localDev/LocalDevLogger.js +13 -4
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
- package/lib/projects/localDev/LocalDevProcess.d.ts +1 -0
- package/lib/projects/localDev/LocalDevProcess.js +12 -1
- package/lib/projects/localDev/LocalDevState.d.ts +3 -0
- package/lib/projects/localDev/LocalDevState.js +9 -0
- package/lib/projects/localDev/helpers/account.d.ts +10 -10
- package/lib/projects/localDev/helpers/account.js +6 -11
- package/lib/projects/localDev/helpers/devSessionsApi.d.ts +9 -0
- package/lib/projects/localDev/helpers/devSessionsApi.js +19 -0
- package/lib/projects/uieLinting.d.ts +33 -0
- package/lib/projects/uieLinting.js +222 -0
- package/lib/projects/urls.js +5 -6
- package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
- package/lib/prompts/accountNamePrompt.js +3 -3
- package/lib/prompts/accountsPrompt.d.ts +1 -1
- package/lib/prompts/accountsPrompt.js +6 -7
- package/lib/prompts/confirmImportDataPrompt.js +2 -2
- package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
- package/lib/prompts/downloadProjectPrompt.js +5 -2
- package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
- package/lib/prompts/personalAccessKeyPrompt.js +2 -2
- package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
- package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
- package/lib/prompts/sandboxesPrompt.js +7 -8
- package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
- package/lib/sandboxSync.d.ts +2 -2
- package/lib/sandboxSync.js +3 -9
- package/lib/sandboxes.d.ts +4 -4
- package/lib/sandboxes.js +6 -11
- package/lib/serverlessLogs.js +2 -2
- package/lib/theme/__tests__/migrate.test.js +15 -0
- package/lib/ui/index.js +6 -3
- package/lib/usageTracking.js +15 -8
- package/lib/validation.js +13 -11
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +4 -2
- package/mcp-server/tools/cms/HsCreateModuleTool.js +4 -2
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +4 -2
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +4 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +3 -1
- package/mcp-server/tools/cms/HsListTool.js +3 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
- package/mcp-server/tools/index.js +4 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +4 -2
- package/mcp-server/tools/project/CreateProjectTool.js +4 -2
- package/mcp-server/tools/project/CreateTestAccountTool.js +42 -19
- package/mcp-server/tools/project/DeployProjectTool.js +3 -1
- package/mcp-server/tools/project/DocFetchTool.js +6 -4
- package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
- package/mcp-server/tools/project/DocsSearchTool.js +10 -8
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -7
- package/mcp-server/tools/project/GetApplicationInfoTool.js +8 -6
- package/mcp-server/tools/project/GetBuildLogsTool.d.ts +26 -0
- package/mcp-server/tools/project/GetBuildLogsTool.js +125 -0
- package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
- package/mcp-server/tools/project/GetBuildStatusTool.js +166 -0
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +9 -7
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +5 -3
- package/mcp-server/tools/project/UploadProjectTools.js +3 -1
- package/mcp-server/tools/project/ValidateProjectTool.js +4 -2
- package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +226 -3
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +23 -11
- package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
- package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
- package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.js +305 -0
- package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
- package/mcp-server/utils/__tests__/content.test.js +21 -20
- package/mcp-server/utils/__tests__/feedbackTracking.test.js +34 -28
- package/mcp-server/utils/config.d.ts +1 -0
- package/mcp-server/utils/config.js +10 -0
- package/mcp-server/utils/content.d.ts +1 -1
- package/mcp-server/utils/content.js +2 -2
- package/mcp-server/utils/feedbackTracking.d.ts +1 -1
- package/mcp-server/utils/feedbackTracking.js +3 -3
- package/mcp-server/utils/toolUsageTracking.js +4 -3
- package/package.json +8 -7
- package/types/LocalDev.d.ts +1 -0
- package/lib/middleware/__test__/notificationsMiddleware.test.js +0 -8
- package/lib/middleware/notificationsMiddleware.d.ts +0 -1
- package/lib/middleware/notificationsMiddleware.js +0 -28
- package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
- package/mcp-server/utils/cliConfig.d.ts +0 -1
- package/mcp-server/utils/cliConfig.js +0 -12
- /package/{lib/middleware/__test__/commandTargetingUtils.test.d.ts → commands/project/__tests__/lint.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/configMiddleware.test.d.ts → __tests__/commandTargetingUtils.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/gitMiddleware.test.d.ts → __tests__/configMiddleware.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/notificationsMiddleware.test.d.ts → __tests__/gitMiddleware.test.d.ts} +0 -0
- /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.d.ts +0 -0
- /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.js +0 -0
- /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.d.ts +0 -0
- /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.js +0 -0
- /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → lib/projects/__tests__/uieLinting.test.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fetchAppInstallationData } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
2
2
|
import { fetchAppMetadataByUid, fetchPublicAppProductionInstallCounts, installStaticAuthAppOnTestAccount, } from '@hubspot/local-dev-lib/api/appsDev';
|
|
3
|
-
import {
|
|
3
|
+
import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
|
|
4
4
|
import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, APP_INSTALLATION_STATES, LOCAL_DEV_SERVER_MESSAGE_TYPES, } from '../../constants.js';
|
|
5
5
|
import { EXIT_CODES } from '../../enums/exitCodes.js';
|
|
6
6
|
import { isAppIRNode } from '../../projects/structure.js';
|
|
@@ -67,7 +67,7 @@ class AppDevModeInterface {
|
|
|
67
67
|
return (this.appNode?.config.auth.type.toLowerCase() === APP_AUTH_TYPES.OAUTH);
|
|
68
68
|
}
|
|
69
69
|
isAutomaticallyInstallable() {
|
|
70
|
-
const targetTestingAccount =
|
|
70
|
+
const targetTestingAccount = getConfigAccountById(this.localDevState.targetTestingAccountId);
|
|
71
71
|
if (!targetTestingAccount) {
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
@@ -2,7 +2,7 @@ import { logger } from '@hubspot/local-dev-lib/logger';
|
|
|
2
2
|
import { DevModeInterface as UIEDevModeInterface } from '@hubspot/ui-extensions-dev-server';
|
|
3
3
|
import { startPortManagerServer, stopPortManagerServer, requestPorts, } from '@hubspot/local-dev-lib/portManager';
|
|
4
4
|
import { getHubSpotApiOrigin, getHubSpotWebsiteOrigin, } from '@hubspot/local-dev-lib/urls';
|
|
5
|
-
import {
|
|
5
|
+
import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
|
|
6
6
|
import { ComponentTypes, } from '../../../types/Projects.js';
|
|
7
7
|
import { lib } from '../../../lang/en.js';
|
|
8
8
|
import { uiLogger } from '../../ui/logger.js';
|
|
@@ -58,7 +58,7 @@ class DevServerManager_DEPRECATED {
|
|
|
58
58
|
async setup({ components, onUploadRequired, accountId, setActiveApp, }) {
|
|
59
59
|
this.componentsByType = this.arrangeComponentsByType(components);
|
|
60
60
|
let env;
|
|
61
|
-
const accountConfig =
|
|
61
|
+
const accountConfig = getConfigAccountById(accountId);
|
|
62
62
|
if (accountConfig) {
|
|
63
63
|
env = accountConfig.env;
|
|
64
64
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import LocalDevState from './LocalDevState.js';
|
|
2
|
+
import LocalDevLogger from './LocalDevLogger.js';
|
|
3
|
+
type DevSessionManagerConstructorOptions = {
|
|
4
|
+
localDevState: LocalDevState;
|
|
5
|
+
localDevLogger: LocalDevLogger;
|
|
6
|
+
};
|
|
7
|
+
declare class DevSessionManager {
|
|
8
|
+
localDevState: LocalDevState;
|
|
9
|
+
localDevLogger: LocalDevLogger;
|
|
10
|
+
private _devSessionId;
|
|
11
|
+
private _heartbeatInterval;
|
|
12
|
+
constructor(options: DevSessionManagerConstructorOptions);
|
|
13
|
+
registerDevSession(): Promise<boolean>;
|
|
14
|
+
private initializeHeartbeat;
|
|
15
|
+
deleteDevSession(): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
export default DevSessionManager;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getActiveServers } from '@hubspot/local-dev-lib/portManager';
|
|
2
|
+
import { devSessionHeartbeat, registerDevSession, deleteDevSession, } from './helpers/devSessionsApi.js';
|
|
3
|
+
class DevSessionManager {
|
|
4
|
+
localDevState;
|
|
5
|
+
localDevLogger;
|
|
6
|
+
_devSessionId;
|
|
7
|
+
_heartbeatInterval;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.localDevState = options.localDevState;
|
|
10
|
+
this.localDevLogger = options.localDevLogger;
|
|
11
|
+
this._devSessionId = undefined;
|
|
12
|
+
this._heartbeatInterval = undefined;
|
|
13
|
+
}
|
|
14
|
+
async registerDevSession() {
|
|
15
|
+
try {
|
|
16
|
+
const activeServers = await getActiveServers();
|
|
17
|
+
const portData = Object.entries(activeServers).map(([serverId, port]) => ({ serverId, port }));
|
|
18
|
+
const registerDevSessionResponse = await registerDevSession(this.localDevState.targetTestingAccountId, portData);
|
|
19
|
+
this._devSessionId = registerDevSessionResponse.data.sessionId;
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
this.localDevLogger.devSessionRegistrationError(e);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
this.initializeHeartbeat();
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
initializeHeartbeat() {
|
|
29
|
+
this._heartbeatInterval = setInterval(() => {
|
|
30
|
+
if (!this._devSessionId) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
devSessionHeartbeat(this.localDevState.targetTestingAccountId, this._devSessionId);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
// TODO: What do we do if the heartbeat fails?
|
|
38
|
+
this.localDevLogger.devSessionHeartbeatError(e);
|
|
39
|
+
}
|
|
40
|
+
}, 30000);
|
|
41
|
+
}
|
|
42
|
+
async deleteDevSession() {
|
|
43
|
+
if (this._devSessionId) {
|
|
44
|
+
clearInterval(this._heartbeatInterval);
|
|
45
|
+
try {
|
|
46
|
+
await deleteDevSession(this.localDevState.targetProjectAccountId, this._devSessionId);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
this.localDevLogger.devSessionDeletionError(e);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export default DevSessionManager;
|
|
@@ -12,6 +12,9 @@ declare class LocalDevLogger {
|
|
|
12
12
|
devServerSetupError(e: unknown): void;
|
|
13
13
|
devServerStartError(e: unknown): void;
|
|
14
14
|
devServerCleanupError(e: unknown): void;
|
|
15
|
+
devSessionRegistrationError(e: unknown): void;
|
|
16
|
+
devSessionHeartbeatError(e: unknown): void;
|
|
17
|
+
devSessionDeletionError(e: unknown): void;
|
|
15
18
|
resetSpinnies(): void;
|
|
16
19
|
startupMessage(): void;
|
|
17
20
|
cleanupStart(): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { hasLocalStateFlag } from '@hubspot/local-dev-lib/config';
|
|
2
|
+
import { getConfigDefaultAccountIfExists } from '@hubspot/local-dev-lib/config';
|
|
3
3
|
import { uiLogger } from '../../ui/logger.js';
|
|
4
4
|
import { uiLine, uiAccountDescription, uiCommandReference, } from '../../ui/index.js';
|
|
5
5
|
import { lib } from '../../../lang/en.js';
|
|
@@ -30,9 +30,9 @@ class LocalDevLogger {
|
|
|
30
30
|
uiLogger.error(langFunction(e instanceof Error ? e.message : ''));
|
|
31
31
|
}
|
|
32
32
|
getUploadCommand() {
|
|
33
|
-
const currentDefaultAccount =
|
|
33
|
+
const currentDefaultAccount = getConfigDefaultAccountIfExists();
|
|
34
34
|
return this.state.targetProjectAccountId !==
|
|
35
|
-
|
|
35
|
+
currentDefaultAccount?.accountId
|
|
36
36
|
? uiCommandReference(`hs project upload --account=${this.state.targetProjectAccountId}`)
|
|
37
37
|
: uiCommandReference('hs project upload');
|
|
38
38
|
}
|
|
@@ -67,6 +67,15 @@ class LocalDevLogger {
|
|
|
67
67
|
devServerCleanupError(e) {
|
|
68
68
|
this.handleError(e, lib.LocalDevManager.devServer.cleanupError);
|
|
69
69
|
}
|
|
70
|
+
devSessionRegistrationError(e) {
|
|
71
|
+
this.handleError(e, lib.LocalDevManager.devSession.registrationError);
|
|
72
|
+
}
|
|
73
|
+
devSessionHeartbeatError(e) {
|
|
74
|
+
this.handleError(e, lib.LocalDevManager.devSession.heartbeatError);
|
|
75
|
+
}
|
|
76
|
+
devSessionDeletionError(e) {
|
|
77
|
+
this.handleError(e, lib.LocalDevManager.devSession.deletionError);
|
|
78
|
+
}
|
|
70
79
|
resetSpinnies() {
|
|
71
80
|
SpinniesManager.stopAll();
|
|
72
81
|
SpinniesManager.init();
|
|
@@ -3,7 +3,7 @@ import chokidar from 'chokidar';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { fetchAppInstallationData } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
5
5
|
import { fetchPublicAppsForPortal, fetchPublicAppProductionInstallCounts, } from '@hubspot/local-dev-lib/api/appsDev';
|
|
6
|
-
import {
|
|
6
|
+
import { getConfigDefaultAccountIfExists } from '@hubspot/local-dev-lib/config';
|
|
7
7
|
import { PROJECT_CONFIG_FILE } from '../../constants.js';
|
|
8
8
|
import SpinniesManager from '../../ui/SpinniesManager.js';
|
|
9
9
|
import DevServerManager_DEPRECATED from './DevServerManager_DEPRECATED.js';
|
|
@@ -200,8 +200,8 @@ class LocalDevManager {
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
getUploadCommand() {
|
|
203
|
-
const currentDefaultAccount =
|
|
204
|
-
return this.targetProjectAccountId !==
|
|
203
|
+
const currentDefaultAccount = getConfigDefaultAccountIfExists();
|
|
204
|
+
return this.targetProjectAccountId !== currentDefaultAccount?.accountId
|
|
205
205
|
? uiCommandReference(`hs project upload --account=${this.targetProjectAccountId}`)
|
|
206
206
|
: uiCommandReference('hs project upload');
|
|
207
207
|
}
|
|
@@ -6,6 +6,7 @@ import open from 'open';
|
|
|
6
6
|
import LocalDevState from './LocalDevState.js';
|
|
7
7
|
import LocalDevLogger from './LocalDevLogger.js';
|
|
8
8
|
import DevServerManager from './DevServerManager.js';
|
|
9
|
+
import DevSessionManager from './DevSessionManager.js';
|
|
9
10
|
import { EXIT_CODES } from '../../enums/exitCodes.js';
|
|
10
11
|
import { getProjectConfig } from '../config.js';
|
|
11
12
|
import { handleProjectUpload } from '../upload.js';
|
|
@@ -20,6 +21,7 @@ class LocalDevProcess {
|
|
|
20
21
|
state;
|
|
21
22
|
_logger;
|
|
22
23
|
devServerManager;
|
|
24
|
+
devSessionManager;
|
|
23
25
|
constructor(options) {
|
|
24
26
|
this.state = new LocalDevState(options);
|
|
25
27
|
this._logger = new LocalDevLogger(this.state);
|
|
@@ -27,6 +29,10 @@ class LocalDevProcess {
|
|
|
27
29
|
localDevState: this.state,
|
|
28
30
|
logger: this._logger,
|
|
29
31
|
});
|
|
32
|
+
this.devSessionManager = new DevSessionManager({
|
|
33
|
+
localDevState: this.state,
|
|
34
|
+
localDevLogger: this._logger,
|
|
35
|
+
});
|
|
30
36
|
}
|
|
31
37
|
get projectDir() {
|
|
32
38
|
return this.state.projectDir;
|
|
@@ -149,6 +155,10 @@ class LocalDevProcess {
|
|
|
149
155
|
this.openLocalDevUi();
|
|
150
156
|
}
|
|
151
157
|
await this.startDevServers();
|
|
158
|
+
const devSessionRegistered = await this.devSessionManager.registerDevSession();
|
|
159
|
+
if (!devSessionRegistered) {
|
|
160
|
+
process.exit(EXIT_CODES.ERROR);
|
|
161
|
+
}
|
|
152
162
|
this.state.devServersStarted = true;
|
|
153
163
|
this.logger.monitorConsoleOutput();
|
|
154
164
|
}
|
|
@@ -156,7 +166,8 @@ class LocalDevProcess {
|
|
|
156
166
|
if (showProgress) {
|
|
157
167
|
this.logger.cleanupStart();
|
|
158
168
|
}
|
|
159
|
-
|
|
169
|
+
let cleanupSucceeded = await this.cleanupDevServers();
|
|
170
|
+
cleanupSucceeded = await this.devSessionManager.deleteDevSession();
|
|
160
171
|
if (!cleanupSucceeded) {
|
|
161
172
|
if (showProgress) {
|
|
162
173
|
this.logger.cleanupError();
|
|
@@ -20,6 +20,7 @@ declare class LocalDevState {
|
|
|
20
20
|
private _devServerMessage;
|
|
21
21
|
private _uploadWarnings;
|
|
22
22
|
private _devServersStarted;
|
|
23
|
+
private _devSessionId;
|
|
23
24
|
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectData, debug, initialProjectNodes, initialProjectProfileData, profile, env, }: LocalDevStateConstructorOptions);
|
|
24
25
|
private runListeners;
|
|
25
26
|
get targetProjectAccountId(): number;
|
|
@@ -54,6 +55,8 @@ declare class LocalDevState {
|
|
|
54
55
|
get uploadWarnings(): Set<string>;
|
|
55
56
|
get devServersStarted(): boolean;
|
|
56
57
|
set devServersStarted(started: boolean);
|
|
58
|
+
get devSessionId(): number | undefined;
|
|
59
|
+
set devSessionId(sessionId: number | undefined);
|
|
57
60
|
addUploadWarning(warning: string): void;
|
|
58
61
|
clearUploadWarnings(): void;
|
|
59
62
|
addListener<K extends keyof LocalDevState>(key: K, listener: LocalDevStateListener<K>): void;
|
|
@@ -16,6 +16,7 @@ class LocalDevState {
|
|
|
16
16
|
_devServerMessage;
|
|
17
17
|
_uploadWarnings;
|
|
18
18
|
_devServersStarted;
|
|
19
|
+
_devSessionId;
|
|
19
20
|
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectData, debug, initialProjectNodes, initialProjectProfileData, profile, env, }) {
|
|
20
21
|
this._targetProjectAccountId = targetProjectAccountId;
|
|
21
22
|
this._targetTestingAccountId = targetTestingAccountId;
|
|
@@ -32,6 +33,7 @@ class LocalDevState {
|
|
|
32
33
|
this._devServerMessage = LOCAL_DEV_SERVER_MESSAGE_TYPES.INITIAL;
|
|
33
34
|
this._uploadWarnings = new Set();
|
|
34
35
|
this._devServersStarted = false;
|
|
36
|
+
this._devSessionId = undefined;
|
|
35
37
|
this._listeners = {};
|
|
36
38
|
}
|
|
37
39
|
runListeners(key) {
|
|
@@ -115,6 +117,13 @@ class LocalDevState {
|
|
|
115
117
|
this._devServersStarted = started;
|
|
116
118
|
this.runListeners('devServersStarted');
|
|
117
119
|
}
|
|
120
|
+
get devSessionId() {
|
|
121
|
+
return this._devSessionId;
|
|
122
|
+
}
|
|
123
|
+
set devSessionId(sessionId) {
|
|
124
|
+
this._devSessionId = sessionId;
|
|
125
|
+
this.runListeners('devSessionId');
|
|
126
|
+
}
|
|
118
127
|
addUploadWarning(warning) {
|
|
119
128
|
this.uploadWarnings.add(warning);
|
|
120
129
|
this.runListeners('uploadWarnings');
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
|
|
2
2
|
import { Environment } from '@hubspot/local-dev-lib/types/Config';
|
|
3
3
|
import { DeveloperTestAccount } from '@hubspot/local-dev-lib/types/developerTestAccounts.js';
|
|
4
4
|
import { ProjectDevTargetAccountPromptResponse } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
5
|
-
export declare function confirmDefaultAccountIsTarget(accountConfig:
|
|
6
|
-
export declare function checkIfDefaultAccountIsSupported(accountConfig:
|
|
7
|
-
export declare function checkIfParentAccountIsAuthed(accountConfig:
|
|
8
|
-
export declare function checkIfAccountFlagIsSupported(accountConfig:
|
|
9
|
-
export declare function suggestRecommendedNestedAccount(accounts:
|
|
10
|
-
export declare function createSandboxForLocalDev(accountId: number, accountConfig:
|
|
11
|
-
export declare function createDeveloperTestAccountForLocalDev(accountId: number, accountConfig:
|
|
5
|
+
export declare function confirmDefaultAccountIsTarget(accountConfig: HubSpotConfigAccount): Promise<void>;
|
|
6
|
+
export declare function checkIfDefaultAccountIsSupported(accountConfig: HubSpotConfigAccount, hasPublicApps: boolean): Promise<void>;
|
|
7
|
+
export declare function checkIfParentAccountIsAuthed(accountConfig: HubSpotConfigAccount): void;
|
|
8
|
+
export declare function checkIfAccountFlagIsSupported(accountConfig: HubSpotConfigAccount, hasPublicApps: boolean): void;
|
|
9
|
+
export declare function suggestRecommendedNestedAccount(accounts: HubSpotConfigAccount[], accountConfig: HubSpotConfigAccount, hasPublicApps: boolean): Promise<ProjectDevTargetAccountPromptResponse>;
|
|
10
|
+
export declare function createSandboxForLocalDev(accountId: number, accountConfig: HubSpotConfigAccount, env: Environment): Promise<number>;
|
|
11
|
+
export declare function createDeveloperTestAccountForLocalDev(accountId: number, accountConfig: HubSpotConfigAccount, env: Environment, useV2?: boolean): Promise<number>;
|
|
12
12
|
export declare function useExistingDevTestAccount(env: Environment, account: DeveloperTestAccount): Promise<void>;
|
|
13
|
-
export declare function hasSandboxes(account:
|
|
14
|
-
export declare function selectAccountTypePrompt(accountConfig:
|
|
13
|
+
export declare function hasSandboxes(account: HubSpotConfigAccount): Promise<boolean>;
|
|
14
|
+
export declare function selectAccountTypePrompt(accountConfig: HubSpotConfigAccount): Promise<string | null>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { HUBSPOT_ACCOUNT_TYPE_STRINGS } from '@hubspot/local-dev-lib/constants/config';
|
|
2
|
-
import {
|
|
3
|
-
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
2
|
+
import { getConfigAccountById, getConfigAccountIfExists, } from '@hubspot/local-dev-lib/config';
|
|
4
3
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
5
4
|
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
|
|
6
5
|
import { isMissingScopeError } from '@hubspot/local-dev-lib/errors/index';
|
|
@@ -60,8 +59,8 @@ export async function checkIfDefaultAccountIsSupported(accountConfig, hasPublicA
|
|
|
60
59
|
}
|
|
61
60
|
export function checkIfParentAccountIsAuthed(accountConfig) {
|
|
62
61
|
if (!accountConfig.parentAccountId ||
|
|
63
|
-
!
|
|
64
|
-
uiLogger.error(lib.localDevHelpers.account.checkIfParentAccountIsAuthed.notAuthedError(accountConfig.parentAccountId || '', uiAccountDescription(
|
|
62
|
+
!getConfigAccountIfExists(accountConfig.parentAccountId)?.accountId) {
|
|
63
|
+
uiLogger.error(lib.localDevHelpers.account.checkIfParentAccountIsAuthed.notAuthedError(accountConfig.parentAccountId || '', uiAccountDescription(accountConfig.accountId)));
|
|
65
64
|
process.exit(EXIT_CODES.SUCCESS);
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -122,11 +121,7 @@ export async function createSandboxForLocalDev(accountId, accountConfig, env) {
|
|
|
122
121
|
trackCommandMetadataUsage('sandbox-create', { step: 'project-dev' }, accountId);
|
|
123
122
|
const result = await buildSandbox(name, accountConfig, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, env);
|
|
124
123
|
const targetAccountId = result.sandbox.sandboxHubId;
|
|
125
|
-
const sandboxAccountConfig =
|
|
126
|
-
if (!sandboxAccountConfig) {
|
|
127
|
-
uiLogger.error(lib.sandbox.create.developer.failure.generic);
|
|
128
|
-
process.exit(EXIT_CODES.ERROR);
|
|
129
|
-
}
|
|
124
|
+
const sandboxAccountConfig = getConfigAccountById(result.sandbox.sandboxHubId);
|
|
130
125
|
const syncTasks = await getAvailableSyncTypes(accountConfig, sandboxAccountConfig);
|
|
131
126
|
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
|
|
132
127
|
await syncSandbox(sandboxAccountConfig, accountConfig, env, syncTasks, true);
|
|
@@ -190,7 +185,7 @@ export async function useExistingDevTestAccount(env, account) {
|
|
|
190
185
|
uiLogger.success(lib.developerTestAccount.create.success.configFileUpdated(devTestAcctConfigName, PERSONAL_ACCESS_KEY_AUTH_METHOD.name));
|
|
191
186
|
}
|
|
192
187
|
export async function hasSandboxes(account) {
|
|
193
|
-
const accountId =
|
|
188
|
+
const accountId = account.accountId;
|
|
194
189
|
if (!accountId) {
|
|
195
190
|
return false;
|
|
196
191
|
}
|
|
@@ -206,7 +201,7 @@ export async function hasSandboxes(account) {
|
|
|
206
201
|
// Top level prompt to choose the type of account to test on
|
|
207
202
|
export async function selectAccountTypePrompt(accountConfig) {
|
|
208
203
|
const hasAccessToSandboxes = await hasSandboxes(accountConfig);
|
|
209
|
-
const accountId =
|
|
204
|
+
const accountId = accountConfig.accountId;
|
|
210
205
|
const result = await listPrompt(lib.localDevHelpers.account.selectAccountTypePrompt.message, {
|
|
211
206
|
choices: [
|
|
212
207
|
{
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HubSpotPromise } from '@hubspot/local-dev-lib/types/Http';
|
|
2
|
+
export declare function registerDevSession(accountId: number, ports: {
|
|
3
|
+
serverId: string;
|
|
4
|
+
port: number;
|
|
5
|
+
}[], force?: boolean): HubSpotPromise<{
|
|
6
|
+
sessionId: number;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function devSessionHeartbeat(accountId: number, sessionId: number): HubSpotPromise<void>;
|
|
9
|
+
export declare function deleteDevSession(accountId: number, sessionId: number): HubSpotPromise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { http } from '@hubspot/local-dev-lib/http';
|
|
2
|
+
const DEV_SESSIONS_API_PATH = 'projects-localdev/2025-09/dev-sessions';
|
|
3
|
+
export async function registerDevSession(accountId, ports, force) {
|
|
4
|
+
return http.post(accountId, {
|
|
5
|
+
url: `${DEV_SESSIONS_API_PATH}/register${force ? '?force=true' : ''}`,
|
|
6
|
+
data: { ports },
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export async function devSessionHeartbeat(accountId, sessionId) {
|
|
10
|
+
return http.post(accountId, {
|
|
11
|
+
url: `${DEV_SESSIONS_API_PATH}/${sessionId}/heartbeat`,
|
|
12
|
+
data: {},
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export async function deleteDevSession(accountId, sessionId) {
|
|
16
|
+
return http.delete(accountId, {
|
|
17
|
+
url: `${DEV_SESSIONS_API_PATH}/${sessionId}`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const REQUIRED_PACKAGES_AND_MIN_VERSIONS: {
|
|
2
|
+
readonly eslint: "9.0.0";
|
|
3
|
+
readonly '@typescript-eslint/eslint-plugin': "8.46.4";
|
|
4
|
+
readonly '@typescript-eslint/parser': "8.46.4";
|
|
5
|
+
readonly 'typescript-eslint': "8.46.4";
|
|
6
|
+
readonly jiti: "2.6.1";
|
|
7
|
+
};
|
|
8
|
+
export declare function isEslintInstalled(directory: string): boolean;
|
|
9
|
+
export declare function areAllLintPackagesInstalled(directory: string): boolean;
|
|
10
|
+
export declare function getMissingLintPackages(directory: string): {
|
|
11
|
+
missingPackages: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function hasEslintConfig(directory: string): boolean;
|
|
14
|
+
export declare function hasDeprecatedEslintConfig(directory: string): boolean;
|
|
15
|
+
export declare function getDeprecatedEslintConfigFiles(directory: string): string[];
|
|
16
|
+
export declare function createEslintConfig(directory: string): string;
|
|
17
|
+
export declare function lintPackagesInDirectory(directory: string, projectDir?: string): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
output: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function lintPackages(lintLocations?: string[], projectDir?: string): Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
results: Array<{
|
|
24
|
+
location: string;
|
|
25
|
+
success: boolean;
|
|
26
|
+
output: string;
|
|
27
|
+
}>;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function displayLintResults(results: Array<{
|
|
30
|
+
location: string;
|
|
31
|
+
success: boolean;
|
|
32
|
+
output: string;
|
|
33
|
+
}>): void;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import util from 'util';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
import { exec as execAsync } from 'node:child_process';
|
|
6
|
+
import { getProjectPackageJsonLocations, isPackageInstalled, } from '../dependencyManagement.js';
|
|
7
|
+
import { commands } from '../../lang/en.js';
|
|
8
|
+
import { uiLogger } from '../ui/logger.js';
|
|
9
|
+
export const REQUIRED_PACKAGES_AND_MIN_VERSIONS = {
|
|
10
|
+
eslint: '9.0.0',
|
|
11
|
+
'@typescript-eslint/eslint-plugin': '8.46.4',
|
|
12
|
+
'@typescript-eslint/parser': '8.46.4',
|
|
13
|
+
'typescript-eslint': '8.46.4',
|
|
14
|
+
jiti: '2.6.1',
|
|
15
|
+
};
|
|
16
|
+
const ESLINT_CONFIG_FILES = [
|
|
17
|
+
'eslint.config.mts',
|
|
18
|
+
'eslint.config.ts',
|
|
19
|
+
'eslint.config.cts',
|
|
20
|
+
'eslint.config.js',
|
|
21
|
+
'eslint.config.mjs',
|
|
22
|
+
'eslint.config.cjs',
|
|
23
|
+
];
|
|
24
|
+
const DEPRECATED_ESLINT_CONFIG_FILES = [
|
|
25
|
+
'.eslintrc.js',
|
|
26
|
+
'.eslintrc.cjs',
|
|
27
|
+
'.eslintrc.yaml',
|
|
28
|
+
'.eslintrc.yml',
|
|
29
|
+
'.eslintrc.json',
|
|
30
|
+
'.eslintrc',
|
|
31
|
+
];
|
|
32
|
+
const ESLINT_CONFIG_TEMPLATE = `import { defineConfig } from "eslint/config";
|
|
33
|
+
import tsParser from "@typescript-eslint/parser";
|
|
34
|
+
|
|
35
|
+
export default defineConfig([
|
|
36
|
+
{
|
|
37
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
|
38
|
+
languageOptions: {
|
|
39
|
+
parser: tsParser,
|
|
40
|
+
parserOptions: {
|
|
41
|
+
ecmaVersion: "latest",
|
|
42
|
+
sourceType: "module",
|
|
43
|
+
ecmaFeatures: {
|
|
44
|
+
jsx: true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
rules: {
|
|
49
|
+
"no-console": ["warn", { allow: ["warn", "error"] }]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
`;
|
|
54
|
+
function getPackageVersionFromPackageJson(directory, packageName) {
|
|
55
|
+
const packageJsonPath = path.join(directory, 'package.json');
|
|
56
|
+
try {
|
|
57
|
+
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
58
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
59
|
+
const version = packageJson.dependencies?.[packageName] ||
|
|
60
|
+
packageJson.devDependencies?.[packageName];
|
|
61
|
+
return version || null;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function isPackageVersionValid(directory, packageName) {
|
|
68
|
+
const versionRange = getPackageVersionFromPackageJson(directory, packageName);
|
|
69
|
+
if (!versionRange) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const minimumVersion = REQUIRED_PACKAGES_AND_MIN_VERSIONS[packageName];
|
|
73
|
+
if (!minimumVersion) {
|
|
74
|
+
// If no minimum version specified, any version is valid
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
// Check if the version range satisfies the minimum version
|
|
79
|
+
// For ranges like ^9.0.0, ~8.5.0, >=9.0.0, etc.
|
|
80
|
+
const minVersionSatisfied = semver.satisfies(minimumVersion, versionRange, {
|
|
81
|
+
includePrerelease: true,
|
|
82
|
+
});
|
|
83
|
+
const coercedVersion = semver.minVersion(versionRange);
|
|
84
|
+
if (coercedVersion) {
|
|
85
|
+
return semver.gte(coercedVersion, minimumVersion);
|
|
86
|
+
}
|
|
87
|
+
return minVersionSatisfied;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function isEslintInstalled(directory) {
|
|
94
|
+
return isPackageInstalled(directory, 'eslint');
|
|
95
|
+
}
|
|
96
|
+
export function areAllLintPackagesInstalled(directory) {
|
|
97
|
+
return Object.keys(REQUIRED_PACKAGES_AND_MIN_VERSIONS).every(pkg => {
|
|
98
|
+
const installed = isPackageInstalled(directory, pkg);
|
|
99
|
+
const validVersion = isPackageVersionValid(directory, pkg);
|
|
100
|
+
return installed && validVersion;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export function getMissingLintPackages(directory) {
|
|
104
|
+
const missingPackages = [];
|
|
105
|
+
for (const packageName of Object.keys(REQUIRED_PACKAGES_AND_MIN_VERSIONS)) {
|
|
106
|
+
const isInstalled = isPackageInstalled(directory, packageName);
|
|
107
|
+
const isValidVersion = isPackageVersionValid(directory, packageName);
|
|
108
|
+
if (!isInstalled || !isValidVersion) {
|
|
109
|
+
missingPackages.push(packageName);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { missingPackages };
|
|
113
|
+
}
|
|
114
|
+
export function hasEslintConfig(directory) {
|
|
115
|
+
return ESLINT_CONFIG_FILES.some(configFile => {
|
|
116
|
+
const configPath = path.join(directory, configFile);
|
|
117
|
+
return fs.existsSync(configPath);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
export function hasDeprecatedEslintConfig(directory) {
|
|
121
|
+
return DEPRECATED_ESLINT_CONFIG_FILES.some(configFile => {
|
|
122
|
+
const configPath = path.join(directory, configFile);
|
|
123
|
+
return fs.existsSync(configPath);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
export function getDeprecatedEslintConfigFiles(directory) {
|
|
127
|
+
return DEPRECATED_ESLINT_CONFIG_FILES.filter(configFile => {
|
|
128
|
+
const configPath = path.join(directory, configFile);
|
|
129
|
+
return fs.existsSync(configPath);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export function createEslintConfig(directory) {
|
|
133
|
+
const configPath = path.join(directory, 'eslint.config.mts');
|
|
134
|
+
try {
|
|
135
|
+
fs.writeFileSync(configPath, ESLINT_CONFIG_TEMPLATE, 'utf-8');
|
|
136
|
+
return path.relative(process.cwd(), configPath);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
uiLogger.error(commands.project.lint.failedToCreateEslintConfig(configPath));
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export async function lintPackagesInDirectory(directory, projectDir) {
|
|
144
|
+
const displayPath = projectDir
|
|
145
|
+
? path.relative(projectDir, directory)
|
|
146
|
+
: directory;
|
|
147
|
+
const exec = util.promisify(execAsync);
|
|
148
|
+
const lintCommand = 'npx eslint . --color';
|
|
149
|
+
try {
|
|
150
|
+
const { stdout, stderr } = await exec(lintCommand, {
|
|
151
|
+
cwd: directory,
|
|
152
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer for large outputs
|
|
153
|
+
});
|
|
154
|
+
let output = `\n${displayPath}:\n`;
|
|
155
|
+
if (stdout && stdout.trim()) {
|
|
156
|
+
output += stdout;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
output += ' No linting issues found.\n';
|
|
160
|
+
}
|
|
161
|
+
if (stderr && stderr.trim()) {
|
|
162
|
+
output += stderr;
|
|
163
|
+
}
|
|
164
|
+
return { success: true, output };
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// ESLint returns exit code 1 when there are linting errors
|
|
168
|
+
// but still provides useful output in stdout/stderr
|
|
169
|
+
let output = `\n${displayPath}:\n`;
|
|
170
|
+
if (error && typeof error === 'object' && 'stdout' in error) {
|
|
171
|
+
const execError = error;
|
|
172
|
+
if (execError.stdout) {
|
|
173
|
+
output += execError.stdout;
|
|
174
|
+
}
|
|
175
|
+
if (execError.stderr) {
|
|
176
|
+
output += execError.stderr;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { success: false, output };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export async function lintPackages(lintLocations, projectDir) {
|
|
183
|
+
const locations = lintLocations || (await getProjectPackageJsonLocations());
|
|
184
|
+
const results = [];
|
|
185
|
+
for (const location of locations) {
|
|
186
|
+
const result = await lintPackagesInDirectory(location, projectDir);
|
|
187
|
+
const displayPath = projectDir
|
|
188
|
+
? path.relative(projectDir, location)
|
|
189
|
+
: location;
|
|
190
|
+
results.push({ location: displayPath, ...result });
|
|
191
|
+
}
|
|
192
|
+
const failedLocations = results.filter(r => !r.success);
|
|
193
|
+
return {
|
|
194
|
+
success: failedLocations.length === 0,
|
|
195
|
+
results,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function displayLintResults(results) {
|
|
199
|
+
// Display all output
|
|
200
|
+
results.forEach(r => {
|
|
201
|
+
uiLogger.log(r.output);
|
|
202
|
+
});
|
|
203
|
+
// Summary
|
|
204
|
+
const failedLocations = results.filter(r => !r.success);
|
|
205
|
+
const passedLocations = results.filter(r => r.success);
|
|
206
|
+
uiLogger.log('\n' + '='.repeat(50));
|
|
207
|
+
if (passedLocations.length > 0) {
|
|
208
|
+
uiLogger.success(`Linting passed in ${passedLocations.length} ${passedLocations.length === 1 ? 'directory' : 'directories'}:`);
|
|
209
|
+
passedLocations.forEach(r => {
|
|
210
|
+
uiLogger.log(` ✓ ${r.location}`);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (failedLocations.length > 0) {
|
|
214
|
+
if (passedLocations.length > 0) {
|
|
215
|
+
uiLogger.log('');
|
|
216
|
+
}
|
|
217
|
+
uiLogger.error(`Linting failed in ${failedLocations.length} ${failedLocations.length === 1 ? 'directory' : 'directories'}:`);
|
|
218
|
+
failedLocations.forEach(r => {
|
|
219
|
+
uiLogger.log(` ✗ ${r.location}`);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|