@hubspot/cli 8.0.10-experimental.1 → 8.0.10-experimental.3

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 (111) hide show
  1. package/commands/account/auth.js +15 -5
  2. package/commands/account/use.js +14 -4
  3. package/commands/auth.js +10 -6
  4. package/commands/cms/__tests__/watch.test.js +0 -8
  5. package/commands/cms/function/logs.js +1 -0
  6. package/commands/cms/theme/preview.js +2 -4
  7. package/commands/cms/watch.d.ts +0 -1
  8. package/commands/cms/watch.js +2 -8
  9. package/commands/feedback.js +1 -1
  10. package/commands/hubdb/clear.js +4 -0
  11. package/commands/hubdb/delete.js +4 -0
  12. package/commands/hubdb/fetch.js +4 -0
  13. package/commands/init.js +4 -0
  14. package/commands/mcp/__tests__/start.test.js +8 -1
  15. package/commands/mcp/setup.js +1 -9
  16. package/commands/mcp/start.js +0 -1
  17. package/commands/project/__tests__/create.test.js +1 -1
  18. package/commands/project/create.js +2 -2
  19. package/commands/project/dev/index.js +29 -19
  20. package/commands/project/download.js +5 -1
  21. package/commands/project/watch.js +15 -2
  22. package/commands/sandbox/__tests__/create.test.js +1 -48
  23. package/commands/sandbox/create.js +3 -30
  24. package/commands/testAccount/create.js +4 -0
  25. package/lang/en.d.ts +13 -6
  26. package/lang/en.js +13 -6
  27. package/lib/__tests__/buildAccount.test.js +1 -52
  28. package/lib/__tests__/sandboxes.test.js +1 -29
  29. package/lib/__tests__/serverlessLogs.test.js +79 -64
  30. package/lib/accountAuth.js +4 -0
  31. package/lib/buildAccount.d.ts +1 -6
  32. package/lib/buildAccount.js +9 -42
  33. package/lib/constants.d.ts +1 -3
  34. package/lib/constants.js +1 -3
  35. package/lib/errors/PromptExitError.d.ts +4 -0
  36. package/lib/errors/PromptExitError.js +8 -0
  37. package/lib/generateSelectors.js +1 -2
  38. package/lib/mcp/__tests__/setup.test.js +357 -28
  39. package/lib/mcp/setup.d.ts +1 -0
  40. package/lib/mcp/setup.js +77 -30
  41. package/lib/projects/__tests__/components.test.js +14 -0
  42. package/lib/projects/components.js +12 -2
  43. package/lib/projects/create/__tests__/legacy.test.js +6 -24
  44. package/lib/projects/create/index.js +1 -4
  45. package/lib/projects/create/legacy.js +3 -8
  46. package/lib/projects/create/v2.js +1 -9
  47. package/lib/projects/ensureProjectExists.js +1 -2
  48. package/lib/projects/localDev/AppDevModeInterface.js +4 -0
  49. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +4 -0
  50. package/lib/projects/localDev/helpers/account.js +5 -11
  51. package/lib/projects/pollProjectBuildAndDeploy.js +90 -85
  52. package/lib/projects/upload.d.ts +1 -0
  53. package/lib/projects/upload.js +37 -46
  54. package/lib/projects/watch.d.ts +2 -1
  55. package/lib/projects/watch.js +32 -24
  56. package/lib/prompts/downloadProjectPrompt.js +11 -10
  57. package/lib/prompts/installAppPrompt.js +3 -2
  58. package/lib/prompts/personalAccessKeyPrompt.js +3 -2
  59. package/lib/prompts/projectDevTargetAccountPrompt.js +13 -16
  60. package/lib/prompts/selectHubDBTablePrompt.js +8 -4
  61. package/lib/prompts/selectPublicAppForMigrationPrompt.js +12 -6
  62. package/lib/sandboxes.d.ts +1 -9
  63. package/lib/sandboxes.js +0 -21
  64. package/lib/serverlessLogs.js +50 -44
  65. package/lib/{cms/devServerProcess.d.ts → theme/cmsDevServerProcess.d.ts} +2 -3
  66. package/lib/theme/cmsDevServerProcess.js +148 -0
  67. package/lib/theme/cmsDevServerRunner.d.ts +14 -0
  68. package/lib/theme/cmsDevServerRunner.js +90 -0
  69. package/lib/usageTracking.js +8 -5
  70. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  71. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  72. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  73. package/mcp-server/tools/cms/HsFunctionLogsTool.js +1 -1
  74. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  75. package/mcp-server/tools/cms/HsListTool.js +1 -1
  76. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -2
  77. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -2
  78. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -2
  79. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +1 -2
  80. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -2
  81. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -2
  82. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  83. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  84. package/mcp-server/tools/project/CreateProjectTool.d.ts +1 -1
  85. package/mcp-server/tools/project/CreateProjectTool.js +1 -1
  86. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  87. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  88. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  89. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  90. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -2
  91. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -2
  92. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +1 -2
  93. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -2
  94. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +10 -2
  95. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +2 -2
  96. package/mcp-server/tools/project/constants.d.ts +1 -1
  97. package/mcp-server/utils/__tests__/command.test.js +233 -3
  98. package/mcp-server/utils/__tests__/feedbackTracking.test.js +9 -64
  99. package/mcp-server/utils/command.d.ts +5 -0
  100. package/mcp-server/utils/command.js +24 -0
  101. package/mcp-server/utils/feedbackTracking.js +2 -17
  102. package/package.json +4 -5
  103. package/lib/__tests__/sandboxSync.test.d.ts +0 -1
  104. package/lib/__tests__/sandboxSync.test.js +0 -147
  105. package/lib/cms/devServerProcess.js +0 -200
  106. package/lib/sandboxSync.d.ts +0 -4
  107. package/lib/sandboxSync.js +0 -102
  108. package/mcp-server/utils/__tests__/project.test.d.ts +0 -1
  109. package/mcp-server/utils/__tests__/project.test.js +0 -140
  110. package/mcp-server/utils/project.d.ts +0 -5
  111. package/mcp-server/utils/project.js +0 -18
@@ -1,200 +0,0 @@
1
- import { spawn, execSync } from 'child_process';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import os from 'os';
5
- import { fileURLToPath } from 'url';
6
- import { getConfigFilePath } from '@hubspot/local-dev-lib/config';
7
- /**
8
- * Ensures cms-dev-server is installed in an isolated cache directory.
9
- * This prevents React version conflicts with the CLI.
10
- */
11
- function ensureCmsDevServerCache(targetVersion) {
12
- const cacheDir = path.join(os.homedir(), '.hubspot', 'cms-dev-server-cache');
13
- const packageJsonPath = path.join(cacheDir, 'node_modules', '@hubspot', 'cms-dev-server', 'package.json');
14
- // Check if already installed with correct version
15
- let needsInstall = true;
16
- if (fs.existsSync(packageJsonPath)) {
17
- try {
18
- const installedPackage = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
19
- if (installedPackage.version === targetVersion) {
20
- needsInstall = false;
21
- }
22
- }
23
- catch (e) {
24
- // If we can't read the package.json, reinstall
25
- needsInstall = true;
26
- }
27
- }
28
- if (needsInstall) {
29
- console.log(`Setting up isolated cms-dev-server ${targetVersion}...`);
30
- // Create cache directory
31
- fs.mkdirSync(cacheDir, { recursive: true });
32
- // Clear old installation if exists
33
- const nodeModulesDir = path.join(cacheDir, 'node_modules');
34
- if (fs.existsSync(nodeModulesDir)) {
35
- fs.rmSync(nodeModulesDir, { recursive: true, force: true });
36
- }
37
- // Install cms-dev-server with production dependencies only
38
- try {
39
- execSync(`npm install @hubspot/cms-dev-server@${targetVersion} --production --no-save --loglevel=error`, {
40
- cwd: cacheDir,
41
- stdio: 'ignore', // Suppress npm output
42
- });
43
- console.log('✓ cms-dev-server setup complete');
44
- }
45
- catch (e) {
46
- console.error('Failed to install cms-dev-server:', e);
47
- process.exit(1);
48
- }
49
- }
50
- return cacheDir;
51
- }
52
- export function spawnDevServer(options) {
53
- const { absoluteSrc, accountName, noSsl, port, generateFieldsTypes, resetSession, dest, } = options;
54
- // Get the version from CLI's package.json
55
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
56
- const cliPackageJsonPath = path.join(__dirname, '../../package.json');
57
- const cliPackageJson = JSON.parse(fs.readFileSync(cliPackageJsonPath, 'utf8'));
58
- const targetVersion = cliPackageJson.dependencies['@hubspot/cms-dev-server'];
59
- if (!targetVersion) {
60
- console.error('Could not determine cms-dev-server version from package.json');
61
- process.exit(1);
62
- }
63
- // Ensure cms-dev-server is installed in isolated cache
64
- const cacheDir = ensureCmsDevServerCache(targetVersion);
65
- // Build a Node script that imports and calls createDevServer
66
- // This runs in the isolated cache directory, completely separate from CLI's node_modules
67
- const script = `
68
- // Suppress library deprecation warnings (e.g., body-parser)
69
- process.noDeprecation = true;
70
-
71
- const { createDevServer } = await import('@hubspot/cms-dev-server');
72
- const { walk } = await import('@hubspot/local-dev-lib/fs');
73
- const { createIgnoreFilter } = await import('@hubspot/local-dev-lib/ignoreRules');
74
- const { isAllowedExtension } = await import('@hubspot/local-dev-lib/path');
75
- const { FILE_UPLOAD_RESULT_TYPES } = await import('@hubspot/local-dev-lib/constants/files');
76
- const cliProgress = (await import('cli-progress')).default;
77
-
78
- ${dest
79
- ? `
80
- // Get uploadable files for preview
81
- let filePaths = [];
82
- try {
83
- filePaths = await walk(${JSON.stringify(absoluteSrc)});
84
- } catch (e) {
85
- console.error('Error walking directory:', e);
86
- }
87
- filePaths = filePaths
88
- .filter(file => isAllowedExtension(file))
89
- .filter(createIgnoreFilter(false));
90
-
91
- // Create progress bar for initial upload
92
- function startProgressBar(numFiles) {
93
- const initialUploadProgressBar = new cliProgress.SingleBar(
94
- {
95
- gracefulExit: true,
96
- format: '[{bar}] {percentage}% | {value}/{total} | {label}',
97
- hideCursor: true,
98
- },
99
- cliProgress.Presets.rect
100
- );
101
- initialUploadProgressBar.start(numFiles, 0, {
102
- label: 'Preparing upload...',
103
- });
104
- let uploadsHaveStarted = false;
105
- return {
106
- onAttemptCallback: () => {},
107
- onSuccessCallback: () => {
108
- initialUploadProgressBar.increment();
109
- if (!uploadsHaveStarted) {
110
- uploadsHaveStarted = true;
111
- initialUploadProgressBar.update(0, {
112
- label: 'Uploading files...',
113
- });
114
- }
115
- },
116
- onFirstErrorCallback: () => {},
117
- onRetryCallback: () => {},
118
- onFinalErrorCallback: () => initialUploadProgressBar.increment(),
119
- onFinishCallback: (results) => {
120
- initialUploadProgressBar.update(numFiles, {
121
- label: 'Upload complete',
122
- });
123
- initialUploadProgressBar.stop();
124
- results.forEach(result => {
125
- if (result.resultType == FILE_UPLOAD_RESULT_TYPES.FAILURE) {
126
- console.error(\`Failed to upload \${result.file}\`);
127
- }
128
- });
129
- },
130
- };
131
- }
132
-
133
- const themePreviewOptions = {
134
- filePaths,
135
- startProgressBar,
136
- resetSession: ${Boolean(resetSession)},
137
- dest: ${JSON.stringify(dest)},
138
- };
139
- `
140
- : 'const themePreviewOptions = undefined;'}
141
-
142
- createDevServer(
143
- ${JSON.stringify(absoluteSrc)},
144
- false, // storybook
145
- '', // config (uses env var HUBSPOT_CONFIG_PATH)
146
- ${JSON.stringify(accountName || '')},
147
- ${!noSsl}, // sslEnabled
148
- ${Boolean(generateFieldsTypes)}, // fieldGenEnabled
149
- themePreviewOptions
150
- );
151
- `;
152
- // Set environment variables
153
- const env = { ...process.env };
154
- if (port) {
155
- env.PORT = port.toString();
156
- }
157
- // Suppress Node.js deprecation warnings
158
- env.NODE_NO_WARNINGS = '1';
159
- // Set config path so cms-dev-server can find hubspot.config.yaml
160
- // Use existing env var if set, otherwise get the default config path
161
- if (!env.HUBSPOT_CONFIG_PATH) {
162
- try {
163
- env.HUBSPOT_CONFIG_PATH = getConfigFilePath();
164
- }
165
- catch (e) {
166
- // Config file doesn't exist - cms-dev-server will handle this gracefully
167
- }
168
- }
169
- console.log(env.HUBSPOT_CONFIG_PATH);
170
- // Spawn Node with the inline script from the isolated cache directory
171
- // This ensures complete isolation from CLI's React 19
172
- const devServer = spawn('node', ['--input-type=module', '-e', script], {
173
- stdio: 'inherit',
174
- env,
175
- cwd: cacheDir,
176
- });
177
- // Handle process events
178
- devServer.on('error', error => {
179
- console.error('Failed to start dev server:', error);
180
- process.exit(1);
181
- });
182
- devServer.on('exit', (code, signal) => {
183
- if (code !== 0 && code !== null) {
184
- console.error(`Dev server exited with code ${code}`);
185
- process.exit(code);
186
- }
187
- if (signal) {
188
- console.error(`Dev server killed with signal ${signal}`);
189
- process.exit(1);
190
- }
191
- });
192
- // Handle CLI termination
193
- process.on('SIGINT', () => {
194
- devServer.kill('SIGINT');
195
- });
196
- process.on('SIGTERM', () => {
197
- devServer.kill('SIGTERM');
198
- });
199
- return devServer;
200
- }
@@ -1,4 +0,0 @@
1
- import { HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
2
- import { Environment } from '@hubspot/local-dev-lib/types/Accounts';
3
- import { SandboxSyncTask } from '../types/Sandboxes.js';
4
- export declare function syncSandbox(accountConfig: HubSpotConfigAccount, parentAccountConfig: HubSpotConfigAccount, env: Environment, syncTasks: Array<SandboxSyncTask>, slimInfoMessage?: boolean): Promise<void>;
@@ -1,102 +0,0 @@
1
- import SpinniesManager from './ui/SpinniesManager.js';
2
- import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
3
- import { uiLogger } from './ui/logger.js';
4
- import { initiateSync } from '@hubspot/local-dev-lib/api/sandboxSync';
5
- import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
6
- import { lib } from '../lang/en.js';
7
- import { getAvailableSyncTypes, getSandboxTypeAsString } from './sandboxes.js';
8
- import { debugError, logError, ApiErrorContext, } from './errorHandlers/index.js';
9
- import { uiAccountDescription, uiLine, uiCommandDisabledBanner, } from './ui/index.js';
10
- import { isDevelopmentSandbox } from './accountTypes.js';
11
- export async function syncSandbox(accountConfig, parentAccountConfig, env, syncTasks, slimInfoMessage = false) {
12
- const accountId = accountConfig.accountId;
13
- const parentAccountId = parentAccountConfig.accountId;
14
- const isDevSandbox = isDevelopmentSandbox(accountConfig);
15
- if (!accountId || !parentAccountId) {
16
- throw new Error(lib.sandbox.sync.failure.invalidUser(uiAccountDescription(accountId), uiAccountDescription(parentAccountId)));
17
- }
18
- SpinniesManager.init({
19
- succeedColor: 'white',
20
- });
21
- let availableSyncTasks = syncTasks;
22
- const baseUrl = getHubSpotWebsiteOrigin(env);
23
- const syncStatusUrl = `${baseUrl}/sandboxes-developer/${parentAccountId}/${getSandboxTypeAsString(accountConfig.accountType)}`;
24
- try {
25
- // If no sync tasks exist, fetch sync types based on default account. Parent account required for fetch
26
- if (!availableSyncTasks ||
27
- (typeof availableSyncTasks === 'object' &&
28
- availableSyncTasks.length === 0)) {
29
- availableSyncTasks = await getAvailableSyncTypes(parentAccountConfig, accountConfig);
30
- }
31
- SpinniesManager.add('sandboxSync', {
32
- text: lib.sandbox.sync.loading.startSync,
33
- });
34
- await initiateSync(parentAccountId, accountId, availableSyncTasks, accountId);
35
- const spinniesText = isDevSandbox
36
- ? lib.sandbox.sync.loading.succeedDevSb(accountId)
37
- : lib.sandbox.sync.loading.succeed(accountId);
38
- SpinniesManager.succeed('sandboxSync', {
39
- text: slimInfoMessage
40
- ? lib.sandbox.sync.loading.successDevSbInfo(accountId, syncStatusUrl)
41
- : spinniesText,
42
- });
43
- }
44
- catch (err) {
45
- debugError(err);
46
- SpinniesManager.fail('sandboxSync', {
47
- text: lib.sandbox.sync.loading.fail(accountId),
48
- });
49
- uiLogger.log('');
50
- if (isSpecifiedError(err, {
51
- statusCode: 403,
52
- category: 'BANNED',
53
- subCategory: 'sandboxes-sync-api.SYNC_NOT_ALLOWED_INVALID_USER',
54
- })) {
55
- uiLogger.error(lib.sandbox.sync.failure.invalidUser(uiAccountDescription(accountId), uiAccountDescription(parentAccountId)));
56
- }
57
- else if (isSpecifiedError(err, {
58
- statusCode: 429,
59
- category: 'RATE_LIMITS',
60
- subCategory: 'sandboxes-sync-api.SYNC_IN_PROGRESS',
61
- })) {
62
- uiLogger.error(lib.sandbox.sync.failure.syncInProgress(`${baseUrl}/sandboxes-developer/${parentAccountId}/syncactivitylog`));
63
- }
64
- else if (isSpecifiedError(err, {
65
- statusCode: 403,
66
- category: 'BANNED',
67
- subCategory: 'sandboxes-sync-api.SYNC_NOT_ALLOWED_INVALID_USERID',
68
- })) {
69
- // This will only trigger if a user is not a super admin of the target account.
70
- uiLogger.error(lib.sandbox.sync.failure.notSuperAdmin(accountId));
71
- }
72
- else if (isSpecifiedError(err, {
73
- statusCode: 404,
74
- category: 'OBJECT_NOT_FOUND',
75
- subCategory: 'SandboxErrors.SANDBOX_NOT_FOUND',
76
- })) {
77
- uiLogger.error(lib.sandbox.sync.failure.objectNotFound(accountId));
78
- }
79
- else if (isSpecifiedError(err, {
80
- statusCode: 404,
81
- })) {
82
- uiCommandDisabledBanner('hs sandbox sync', 'https://developers.hubspot.com/docs/developer-tooling/local-development/hubspot-cli/project-commands');
83
- }
84
- else {
85
- logError(err, new ApiErrorContext({
86
- accountId: parentAccountId,
87
- request: 'sandbox sync',
88
- }));
89
- }
90
- uiLogger.log('');
91
- throw err;
92
- }
93
- if (!slimInfoMessage) {
94
- uiLogger.log('');
95
- uiLine();
96
- uiLogger.info(isDevSandbox
97
- ? lib.sandbox.sync.info.syncMessageDevSb(syncStatusUrl)
98
- : lib.sandbox.sync.info.syncMessage(syncStatusUrl));
99
- uiLine();
100
- uiLogger.log('');
101
- }
102
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,140 +0,0 @@
1
- import { runCommandInDir } from '../project.js';
2
- import { execAsync } from '../command.js';
3
- import fs from 'fs';
4
- import path from 'path';
5
- vi.mock('../command', () => ({
6
- execAsync: vi.fn(),
7
- addFlag: vi.fn((command, flagName, value) => {
8
- if (Array.isArray(value)) {
9
- return `${command} --${flagName} ${value.map(item => `"${item}"`).join(' ')}`;
10
- }
11
- return `${command} --${flagName} "${value}"`;
12
- }),
13
- }));
14
- vi.mock('fs');
15
- vi.mock('path');
16
- const mockExecAsync = execAsync;
17
- const mockExistsSync = fs.existsSync;
18
- const mockMkdirSync = fs.mkdirSync;
19
- const mockResolve = path.resolve;
20
- describe('mcp-server/utils/project', () => {
21
- describe('runCommandInDir', () => {
22
- const mockDirectory = '/test/directory';
23
- const mockCommand = 'npm install';
24
- const mockResolvedPath = '/resolved/test/directory';
25
- beforeEach(() => {
26
- mockResolve.mockReturnValue(mockResolvedPath);
27
- });
28
- it('should run command in existing directory', async () => {
29
- const expectedResult = {
30
- stdout: 'command output',
31
- stderr: '',
32
- };
33
- mockExistsSync.mockReturnValue(true);
34
- mockExecAsync.mockResolvedValue(expectedResult);
35
- const result = await runCommandInDir(mockDirectory, mockCommand);
36
- expect(mockExistsSync).toHaveBeenCalledWith(mockDirectory);
37
- expect(mockMkdirSync).not.toHaveBeenCalled();
38
- expect(mockResolve).toHaveBeenCalledWith(mockDirectory);
39
- expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
40
- cwd: mockResolvedPath,
41
- env: expect.any(Object),
42
- }));
43
- expect(result).toEqual(expectedResult);
44
- });
45
- it('should create directory if it does not exist', async () => {
46
- const expectedResult = {
47
- stdout: 'command output',
48
- stderr: '',
49
- };
50
- mockExistsSync.mockReturnValue(false);
51
- mockExecAsync.mockResolvedValue(expectedResult);
52
- const result = await runCommandInDir(mockDirectory, mockCommand);
53
- expect(mockExistsSync).toHaveBeenCalledWith(mockDirectory);
54
- expect(mockMkdirSync).toHaveBeenCalledWith(mockDirectory);
55
- expect(mockResolve).toHaveBeenCalledWith(mockDirectory);
56
- expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
57
- cwd: mockResolvedPath,
58
- env: expect.any(Object),
59
- }));
60
- expect(result).toEqual(expectedResult);
61
- });
62
- it('should propagate execAsync errors', async () => {
63
- const error = new Error('Command failed');
64
- mockExistsSync.mockReturnValue(true);
65
- mockExecAsync.mockRejectedValue(error);
66
- await expect(runCommandInDir(mockDirectory, mockCommand)).rejects.toThrow('Command failed');
67
- expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
68
- cwd: mockResolvedPath,
69
- env: expect.any(Object),
70
- }));
71
- });
72
- it('should handle stderr in results', async () => {
73
- const expectedResult = {
74
- stdout: 'some output',
75
- stderr: 'warning message',
76
- };
77
- mockExistsSync.mockReturnValue(true);
78
- mockExecAsync.mockResolvedValue(expectedResult);
79
- const result = await runCommandInDir(mockDirectory, mockCommand);
80
- expect(result.stdout).toBe('some output');
81
- expect(result.stderr).toBe('warning message');
82
- });
83
- it('should add --disable-usage-tracking flag to hs commands', async () => {
84
- const hsCommand = 'hs project upload';
85
- const expectedResult = {
86
- stdout: 'success',
87
- stderr: '',
88
- };
89
- mockExistsSync.mockReturnValue(true);
90
- mockExecAsync.mockResolvedValue(expectedResult);
91
- await runCommandInDir(mockDirectory, hsCommand);
92
- expect(mockExecAsync).toHaveBeenCalledWith('hs project upload --disable-usage-tracking "true"', expect.objectContaining({
93
- cwd: mockResolvedPath,
94
- env: expect.any(Object),
95
- }));
96
- });
97
- it('should not add --disable-usage-tracking flag to non-hs commands', async () => {
98
- const nonHsCommand = 'npm install';
99
- const expectedResult = {
100
- stdout: 'success',
101
- stderr: '',
102
- };
103
- mockExistsSync.mockReturnValue(true);
104
- mockExecAsync.mockResolvedValue(expectedResult);
105
- await runCommandInDir(mockDirectory, nonHsCommand);
106
- expect(mockExecAsync).toHaveBeenCalledWith('npm install', expect.objectContaining({
107
- cwd: mockResolvedPath,
108
- env: expect.any(Object),
109
- }));
110
- });
111
- it('should add --disable-usage-tracking flag to hs commands with existing flags', async () => {
112
- const hsCommand = 'hs project upload --profile prod';
113
- const expectedResult = {
114
- stdout: 'success',
115
- stderr: '',
116
- };
117
- mockExistsSync.mockReturnValue(true);
118
- mockExecAsync.mockResolvedValue(expectedResult);
119
- await runCommandInDir(mockDirectory, hsCommand);
120
- expect(mockExecAsync).toHaveBeenCalledWith('hs project upload --profile prod --disable-usage-tracking "true"', expect.objectContaining({
121
- cwd: mockResolvedPath,
122
- env: expect.any(Object),
123
- }));
124
- });
125
- it('should handle hs commands that start with whitespace', async () => {
126
- const hsCommand = 'hs init';
127
- const expectedResult = {
128
- stdout: 'success',
129
- stderr: '',
130
- };
131
- mockExistsSync.mockReturnValue(true);
132
- mockExecAsync.mockResolvedValue(expectedResult);
133
- await runCommandInDir(mockDirectory, hsCommand);
134
- expect(mockExecAsync).toHaveBeenCalledWith('hs init --disable-usage-tracking "true"', expect.objectContaining({
135
- cwd: mockResolvedPath,
136
- env: expect.any(Object),
137
- }));
138
- });
139
- });
140
- });
@@ -1,5 +0,0 @@
1
- export interface CommandResults {
2
- stderr: string;
3
- stdout: string;
4
- }
5
- export declare function runCommandInDir(directory: string, command: string): Promise<CommandResults>;
@@ -1,18 +0,0 @@
1
- import { addFlag, execAsync } from './command.js';
2
- import path from 'path';
3
- import fs from 'fs';
4
- export async function runCommandInDir(directory, command) {
5
- if (!fs.existsSync(directory)) {
6
- fs.mkdirSync(directory);
7
- }
8
- let finalCommand = command;
9
- if (command.startsWith('hs ')) {
10
- finalCommand = addFlag(finalCommand, 'disable-usage-tracking', true);
11
- }
12
- return execAsync(finalCommand, {
13
- cwd: path.resolve(directory),
14
- env: {
15
- ...process.env,
16
- },
17
- });
18
- }