@hubspot/cli 7.9.0 → 7.10.0-beta.1
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/commands/__tests__/project.test.js +2 -0
- package/commands/account/__tests__/rename.test.js +35 -0
- package/commands/account/createOverride.js +2 -12
- package/commands/account/removeOverride.js +2 -10
- package/commands/account/rename.d.ts +1 -1
- package/commands/account/rename.js +5 -2
- package/commands/cms/theme/preview.js +1 -4
- package/commands/config/set.js +1 -2
- package/commands/getStarted.js +13 -19
- package/commands/hubdb.d.ts +1 -1
- package/commands/project/__tests__/updateDeps.test.d.ts +1 -0
- package/commands/project/__tests__/updateDeps.test.js +142 -0
- package/commands/project/create.js +0 -1
- package/commands/project/dev/index.js +8 -1
- package/commands/project/listBuilds.js +7 -1
- package/commands/project/updateDeps.d.ts +6 -0
- package/commands/project/updateDeps.js +80 -0
- package/commands/project/upload.js +7 -1
- package/commands/project/validate.js +7 -1
- package/commands/project/watch.js +7 -2
- package/commands/project.js +2 -0
- package/commands/testAccount/__tests__/create.test.js +68 -0
- package/commands/testAccount/create.d.ts +8 -0
- package/commands/testAccount/create.js +134 -44
- package/commands/testAccount/importData.d.ts +1 -1
- package/lang/en.d.ts +3194 -3184
- package/lang/en.js +43 -8
- package/lib/__tests__/dependencyManagement.test.js +273 -1
- package/lib/commonOpts.js +2 -5
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +6 -0
- package/lib/dependencyManagement.d.ts +8 -2
- package/lib/dependencyManagement.js +75 -12
- package/lib/mcp/__tests__/setup.test.d.ts +1 -0
- package/lib/mcp/__tests__/setup.test.js +127 -0
- package/lib/mcp/setup.d.ts +4 -12
- package/lib/mcp/setup.js +34 -1
- package/lib/middleware/autoUpdateMiddleware.d.ts +3 -1
- package/lib/middleware/autoUpdateMiddleware.js +1 -0
- package/lib/npm.d.ts +3 -0
- package/lib/npm.js +6 -0
- package/lib/projects/__tests__/components.test.js +148 -24
- package/lib/projects/__tests__/platformVersion.test.js +5 -1
- package/lib/projects/__tests__/projects.test.js +13 -42
- package/lib/projects/components.js +76 -20
- package/lib/projects/config.js +5 -9
- package/lib/projects/platformVersion.js +1 -1
- package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.js +153 -0
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +5 -0
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +76 -66
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +6 -0
- package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +4 -4
- package/mcp-server/tools/cms/HsCreateModuleTool.js +6 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +6 -0
- package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +4 -4
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +4 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.js +4 -0
- package/mcp-server/tools/cms/HsListTool.js +4 -0
- package/mcp-server/tools/index.js +2 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +6 -0
- package/mcp-server/tools/project/CreateProjectTool.js +6 -0
- package/mcp-server/tools/project/CreateTestAccountTool.d.ts +41 -0
- package/mcp-server/tools/project/CreateTestAccountTool.js +137 -0
- package/mcp-server/tools/project/DeployProjectTool.js +6 -0
- package/mcp-server/tools/project/DocFetchTool.js +4 -0
- package/mcp-server/tools/project/DocsSearchTool.js +4 -0
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +4 -0
- package/mcp-server/tools/project/GetApplicationInfoTool.js +4 -0
- package/mcp-server/tools/project/GetConfigValuesTool.js +4 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +4 -0
- package/mcp-server/tools/project/UploadProjectTools.d.ts +9 -3
- package/mcp-server/tools/project/UploadProjectTools.js +50 -4
- package/mcp-server/tools/project/ValidateProjectTool.js +4 -0
- package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +231 -0
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +56 -4
- package/package.json +2 -2
- package/lang/en.lyaml +0 -1508
- package/lib/lang.d.ts +0 -8
- package/lib/lang.js +0 -72
|
@@ -13,6 +13,7 @@ import migrateApp from '../project/migrateApp.js';
|
|
|
13
13
|
import migrate from '../project/migrate.js';
|
|
14
14
|
import cloneApp from '../project/cloneApp.js';
|
|
15
15
|
import installDeps from '../project/installDeps.js';
|
|
16
|
+
import updateDeps from '../project/updateDeps.js';
|
|
16
17
|
import validate from '../project/validate.js';
|
|
17
18
|
import profileCommands from '../project/profile.js';
|
|
18
19
|
import list from '../project/list.js';
|
|
@@ -72,6 +73,7 @@ describe('commands/project', () => {
|
|
|
72
73
|
migrate,
|
|
73
74
|
cloneApp,
|
|
74
75
|
installDeps,
|
|
76
|
+
updateDeps,
|
|
75
77
|
profileCommands,
|
|
76
78
|
validate,
|
|
77
79
|
list,
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
2
|
import { addConfigOptions, addAccountOptions, } from '../../../lib/commonOpts.js';
|
|
3
3
|
import accountRenameCommand from '../rename.js';
|
|
4
|
+
import * as config from '@hubspot/local-dev-lib/config';
|
|
5
|
+
import { logError } from '../../../lib/errorHandlers/index.js';
|
|
6
|
+
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
4
7
|
vi.mock('../../../lib/commonOpts');
|
|
8
|
+
vi.mock('@hubspot/local-dev-lib/config');
|
|
9
|
+
vi.mock('../../../lib/errorHandlers/index.js');
|
|
5
10
|
const positionalSpy = vi
|
|
6
11
|
.spyOn(yargs, 'positional')
|
|
7
12
|
.mockReturnValue(yargs);
|
|
8
13
|
const exampleSpy = vi
|
|
9
14
|
.spyOn(yargs, 'example')
|
|
10
15
|
.mockReturnValue(yargs);
|
|
16
|
+
const renameAccountSpy = vi.spyOn(config, 'renameAccount');
|
|
17
|
+
const processExitSpy = vi.spyOn(process, 'exit');
|
|
11
18
|
describe('commands/account/rename', () => {
|
|
12
19
|
const yargsMock = yargs;
|
|
13
20
|
describe('command', () => {
|
|
@@ -20,6 +27,34 @@ describe('commands/account/rename', () => {
|
|
|
20
27
|
expect(accountRenameCommand.describe).toBeDefined();
|
|
21
28
|
});
|
|
22
29
|
});
|
|
30
|
+
describe('handler', () => {
|
|
31
|
+
let args;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.clearAllMocks();
|
|
34
|
+
renameAccountSpy.mockResolvedValue(undefined);
|
|
35
|
+
processExitSpy.mockImplementation(() => {
|
|
36
|
+
throw new Error('process.exit called');
|
|
37
|
+
});
|
|
38
|
+
args = {
|
|
39
|
+
accountName: 'myExistingAccountName',
|
|
40
|
+
newName: 'myNewAccountName',
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
it('should rename the account', async () => {
|
|
44
|
+
await expect(accountRenameCommand.handler(args)).rejects.toThrow('process.exit called');
|
|
45
|
+
expect(renameAccountSpy).toHaveBeenCalledWith('myExistingAccountName', 'my-new-account-name');
|
|
46
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.SUCCESS);
|
|
47
|
+
});
|
|
48
|
+
it('should handle errors when renameAccount throws', async () => {
|
|
49
|
+
const error = new Error('Failed to rename account');
|
|
50
|
+
renameAccountSpy.mockRejectedValue(error);
|
|
51
|
+
await expect(accountRenameCommand.handler(args)).rejects.toThrow('process.exit called');
|
|
52
|
+
expect(renameAccountSpy).toHaveBeenCalledWith('myExistingAccountName', 'my-new-account-name');
|
|
53
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
54
|
+
expect(logError).toHaveBeenCalledWith(error);
|
|
55
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
23
58
|
describe('builder', () => {
|
|
24
59
|
it('should support the correct options', () => {
|
|
25
60
|
accountRenameCommand.builder(yargsMock);
|
|
@@ -6,7 +6,7 @@ import { getCWDAccountOverride, getDefaultAccountOverrideFilePath, getConfigPath
|
|
|
6
6
|
import { getGlobalConfig } from '@hubspot/local-dev-lib/config/migrate';
|
|
7
7
|
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
8
8
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
9
|
-
import {
|
|
9
|
+
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
10
10
|
import { selectAccountFromConfig } from '../../lib/prompts/accountsPrompt.js';
|
|
11
11
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
12
12
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
@@ -32,11 +32,6 @@ async function handler(args) {
|
|
|
32
32
|
});
|
|
33
33
|
uiLogger.log('');
|
|
34
34
|
if (!replaceOverrideFile) {
|
|
35
|
-
const accountId = getAccountId(accountOverride) || undefined;
|
|
36
|
-
trackCommandMetadataUsage('account-createOverride', {
|
|
37
|
-
command: 'hs account create-override',
|
|
38
|
-
step: 'Reject overwriting an override via prompt',
|
|
39
|
-
}, accountId);
|
|
40
35
|
process.exit(EXIT_CODES.SUCCESS);
|
|
41
36
|
}
|
|
42
37
|
}
|
|
@@ -48,16 +43,11 @@ async function handler(args) {
|
|
|
48
43
|
overrideDefaultAccount = await selectAccountFromConfig();
|
|
49
44
|
}
|
|
50
45
|
const accountId = getAccountId(overrideDefaultAccount);
|
|
46
|
+
trackCommandUsage('account-createOverride', undefined, accountId);
|
|
51
47
|
try {
|
|
52
48
|
const overrideFilePath = path.join(getCwd(), DEFAULT_ACCOUNT_OVERRIDE_FILE_NAME);
|
|
53
49
|
await fs.writeFile(overrideFilePath, accountId.toString(), 'utf8');
|
|
54
50
|
uiLogger.success(commands.account.subcommands.createOverride.success(overrideFilePath));
|
|
55
|
-
const trackingId = accountId || undefined;
|
|
56
|
-
trackCommandMetadataUsage('config-migrate', {
|
|
57
|
-
command: 'hs config migrate',
|
|
58
|
-
step: 'Confirm overwriting an override via prompt',
|
|
59
|
-
successful: true,
|
|
60
|
-
}, trackingId);
|
|
61
51
|
process.exit(EXIT_CODES.SUCCESS);
|
|
62
52
|
}
|
|
63
53
|
catch (e) {
|
|
@@ -3,7 +3,7 @@ import { getCWDAccountOverride, getDefaultAccountOverrideFilePath, getAccountId,
|
|
|
3
3
|
import { DEFAULT_ACCOUNT_OVERRIDE_FILE_NAME } from '@hubspot/local-dev-lib/constants/config';
|
|
4
4
|
import { getGlobalConfig } from '@hubspot/local-dev-lib/config/migrate';
|
|
5
5
|
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
6
|
-
import {
|
|
6
|
+
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
7
7
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
8
8
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
9
9
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
@@ -22,6 +22,7 @@ async function handler(args) {
|
|
|
22
22
|
const overrideFilePath = getDefaultAccountOverrideFilePath();
|
|
23
23
|
if (accountOverride && overrideFilePath) {
|
|
24
24
|
const accountId = getAccountId(accountOverride) || undefined;
|
|
25
|
+
trackCommandUsage('account-removeOverride', undefined, accountId);
|
|
25
26
|
if (!force) {
|
|
26
27
|
uiLogger.log(commands.account.subcommands.removeOverride.accountOverride(overrideFilePath, accountOverride.toString()));
|
|
27
28
|
const { deleteOverrideFile } = await promptUser({
|
|
@@ -32,21 +33,12 @@ async function handler(args) {
|
|
|
32
33
|
});
|
|
33
34
|
uiLogger.log('');
|
|
34
35
|
if (!deleteOverrideFile) {
|
|
35
|
-
trackCommandMetadataUsage('account-removeOverride', {
|
|
36
|
-
command: 'hs account remove-override',
|
|
37
|
-
step: 'Reject removing override via prompt',
|
|
38
|
-
}, accountId);
|
|
39
36
|
process.exit(EXIT_CODES.SUCCESS);
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
try {
|
|
43
40
|
fs.unlinkSync(overrideFilePath);
|
|
44
41
|
uiLogger.success(commands.account.subcommands.removeOverride.success);
|
|
45
|
-
trackCommandMetadataUsage('account-removeOverride', {
|
|
46
|
-
command: 'hs account remove-override',
|
|
47
|
-
step: 'Confirm removing override file (via prompt/force)',
|
|
48
|
-
successful: true,
|
|
49
|
-
}, accountId);
|
|
50
42
|
process.exit(EXIT_CODES.SUCCESS);
|
|
51
43
|
}
|
|
52
44
|
catch (error) {
|
|
@@ -5,19 +5,22 @@ import { uiLogger } from '../../lib/ui/logger.js';
|
|
|
5
5
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
6
6
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
7
7
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
8
|
+
import { toKebabCase } from '@hubspot/local-dev-lib/text';
|
|
8
9
|
const command = 'rename <account-name> <new-name>';
|
|
9
10
|
const describe = commands.account.subcommands.rename.describe;
|
|
10
11
|
async function handler(args) {
|
|
11
12
|
const { accountName, newName, derivedAccountId } = args;
|
|
12
13
|
trackCommandUsage('accounts-rename', undefined, derivedAccountId);
|
|
14
|
+
const newNameKebabCase = toKebabCase(newName);
|
|
15
|
+
const nameWasSanitized = newNameKebabCase !== newName;
|
|
13
16
|
try {
|
|
14
|
-
await renameAccount(accountName,
|
|
17
|
+
await renameAccount(accountName, newNameKebabCase);
|
|
15
18
|
}
|
|
16
19
|
catch (error) {
|
|
17
20
|
logError(error);
|
|
18
21
|
process.exit(EXIT_CODES.ERROR);
|
|
19
22
|
}
|
|
20
|
-
uiLogger.
|
|
23
|
+
uiLogger.success(commands.account.subcommands.rename.success.renamed(accountName, newNameKebabCase, nameWasSanitized));
|
|
21
24
|
process.exit(EXIT_CODES.SUCCESS);
|
|
22
25
|
}
|
|
23
26
|
function accountRenameBuilder(yargs) {
|
|
@@ -142,14 +142,11 @@ async function handler(args) {
|
|
|
142
142
|
trackCommandUsage('preview', {}, derivedAccountId);
|
|
143
143
|
let createUnifiedDevServer;
|
|
144
144
|
try {
|
|
145
|
-
// @ts-ignore TODO: Remove when we deprecate Node 18
|
|
146
|
-
require.resolve('@hubspot/cms-dev-server');
|
|
147
|
-
// @ts-ignore TODO: Remove when we deprecate Node 18
|
|
148
145
|
const { createDevServer } = await import('@hubspot/cms-dev-server');
|
|
149
146
|
createUnifiedDevServer = createDevServer;
|
|
150
147
|
}
|
|
151
148
|
catch (e) {
|
|
152
|
-
uiLogger.warn('
|
|
149
|
+
uiLogger.warn('Error loading unified dev server. Defaulting to legacy preview.');
|
|
153
150
|
}
|
|
154
151
|
if (createUnifiedDevServer) {
|
|
155
152
|
if (port) {
|
package/commands/config/set.js
CHANGED
|
@@ -22,7 +22,7 @@ async function selectOptions() {
|
|
|
22
22
|
},
|
|
23
23
|
{ name: 'Allow usage tracking', value: { allowUsageTracking: '' } },
|
|
24
24
|
{ name: 'HTTP timeout', value: { httpTimeout: '' } },
|
|
25
|
-
|
|
25
|
+
{ name: 'Allow auto updates', value: { allowAutoUpdates: '' } },
|
|
26
26
|
],
|
|
27
27
|
},
|
|
28
28
|
]);
|
|
@@ -94,7 +94,6 @@ function configSetBuilder(yargs) {
|
|
|
94
94
|
'allow-auto-updates': {
|
|
95
95
|
describe: commands.config.subcommands.set.options.allowAutoUpdates.describe,
|
|
96
96
|
type: 'boolean',
|
|
97
|
-
hidden: true,
|
|
98
97
|
},
|
|
99
98
|
'auto-open-browser': {
|
|
100
99
|
describe: commands.config.subcommands.set.options.autoOpenBrowser.describe,
|
package/commands/getStarted.js
CHANGED
|
@@ -15,7 +15,6 @@ import { debugError, logError } from '../lib/errorHandlers/index.js';
|
|
|
15
15
|
import { handleProjectUpload } from '../lib/projects/upload.js';
|
|
16
16
|
import { PROJECT_CONFIG_FILE, GET_STARTED_OPTIONS, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, } from '../lib/constants.js';
|
|
17
17
|
import { writeProjectConfig, getProjectConfig, validateProjectConfig, } from '../lib/projects/config.js';
|
|
18
|
-
import { getProjectPackageJsonLocations, installPackages, } from '../lib/dependencyManagement.js';
|
|
19
18
|
import { pollProjectBuildAndDeploy } from '../lib/projects/pollProjectBuildAndDeploy.js';
|
|
20
19
|
import { isV2Project } from '../lib/projects/platformVersion.js';
|
|
21
20
|
import { openLink } from '../lib/links.js';
|
|
@@ -135,23 +134,7 @@ async function handler(args) {
|
|
|
135
134
|
successful: true,
|
|
136
135
|
step: 'project-creation',
|
|
137
136
|
}, derivedAccountId);
|
|
138
|
-
// 5.
|
|
139
|
-
const installLocations = await getProjectPackageJsonLocations(projectDest);
|
|
140
|
-
try {
|
|
141
|
-
await installPackages({
|
|
142
|
-
installLocations: installLocations,
|
|
143
|
-
});
|
|
144
|
-
uiLogger.log(' ');
|
|
145
|
-
uiLogger.success(commands.getStarted.logs.dependenciesInstalled);
|
|
146
|
-
uiLogger.log(' ');
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
uiLogger.log(' ');
|
|
150
|
-
uiLogger.error(commands.getStarted.errors.installDepsFailed);
|
|
151
|
-
logError(err);
|
|
152
|
-
uiLogger.log(' ');
|
|
153
|
-
}
|
|
154
|
-
// 6. Ask user if they want to upload the project
|
|
137
|
+
// 5. Ask user if they want to upload the project
|
|
155
138
|
const { shouldUpload } = await promptUser([
|
|
156
139
|
{
|
|
157
140
|
type: 'confirm',
|
|
@@ -179,7 +162,13 @@ async function handler(args) {
|
|
|
179
162
|
uiLogger.error(commands.getStarted.errors.configFileNotFound);
|
|
180
163
|
process.exit(EXIT_CODES.ERROR);
|
|
181
164
|
}
|
|
182
|
-
|
|
165
|
+
try {
|
|
166
|
+
validateProjectConfig(newProjectConfig, newProjectDir);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
logError(error);
|
|
170
|
+
process.exit(EXIT_CODES.ERROR);
|
|
171
|
+
}
|
|
183
172
|
uiLogger.log(' ');
|
|
184
173
|
uiLogger.log(commands.getStarted.logs.uploadingProject);
|
|
185
174
|
uiLogger.log(' ');
|
|
@@ -256,6 +245,11 @@ async function handler(args) {
|
|
|
256
245
|
process.exit(EXIT_CODES.ERROR);
|
|
257
246
|
}
|
|
258
247
|
}
|
|
248
|
+
else {
|
|
249
|
+
uiLogger.log(' ');
|
|
250
|
+
uiFeatureHighlight(['projectUploadCommand', 'projectDevCommand']);
|
|
251
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
252
|
+
}
|
|
259
253
|
}
|
|
260
254
|
// Track successful completion of get-started command
|
|
261
255
|
await trackCommandMetadataUsage('get-started', {
|
package/commands/hubdb.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { YargsCommandModuleBucket } from '../types/Yargs.js';
|
|
2
2
|
export declare const command = "hubdb";
|
|
3
|
-
export declare const describe:
|
|
3
|
+
export declare const describe: string;
|
|
4
4
|
declare const hubdbCommand: YargsCommandModuleBucket;
|
|
5
5
|
export default hubdbCommand;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import yargs from 'yargs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
4
|
+
import * as projectUtils from '../../../lib/projects/config.js';
|
|
5
|
+
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
6
|
+
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
7
|
+
import * as dependencyManagement from '../../../lib/dependencyManagement.js';
|
|
8
|
+
import * as promptUtils from '../../../lib/prompts/promptUtils.js';
|
|
9
|
+
import projectUpdateDepsCommand from '../updateDeps.js';
|
|
10
|
+
vi.mock('../../../lib/ui/logger.js');
|
|
11
|
+
vi.mock('../../../lib/projects/config');
|
|
12
|
+
vi.mock('../../../lib/dependencyManagement');
|
|
13
|
+
vi.mock('../../../lib/prompts/promptUtils');
|
|
14
|
+
vi.mock('../../../lib/usageTracking');
|
|
15
|
+
vi.mock('../../../lib/commonOpts');
|
|
16
|
+
const exampleSpy = vi.spyOn(yargs, 'example');
|
|
17
|
+
const processExitSpy = vi.spyOn(process, 'exit');
|
|
18
|
+
const getProjectConfigSpy = vi.spyOn(projectUtils, 'getProjectConfig');
|
|
19
|
+
const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
|
|
20
|
+
const getProjectPackageJsonLocationsSpy = vi.spyOn(dependencyManagement, 'getProjectPackageJsonLocations');
|
|
21
|
+
const updatePackagesSpy = vi.spyOn(dependencyManagement, 'updatePackages');
|
|
22
|
+
describe('commands/project/updateDeps', () => {
|
|
23
|
+
describe('command', () => {
|
|
24
|
+
it('should have the correct command structure', () => {
|
|
25
|
+
expect(projectUpdateDepsCommand.command).toEqual('update-deps [packages..]');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('describe', () => {
|
|
29
|
+
it('should not provide a description', () => {
|
|
30
|
+
expect(projectUpdateDepsCommand.describe).toEqual(expect.stringMatching(/Update the npm dependencies for your project, or update specific dependencies in a subcomponent of a project/));
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('builder', () => {
|
|
34
|
+
it('should provide examples', () => {
|
|
35
|
+
projectUpdateDepsCommand.builder(yargs);
|
|
36
|
+
expect(exampleSpy).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('handler', () => {
|
|
40
|
+
let args;
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
args = {
|
|
43
|
+
derivedAccountId: 999999,
|
|
44
|
+
};
|
|
45
|
+
// @ts-expect-error Doesn't match the actual signature because then the linter complains about unused variables
|
|
46
|
+
processExitSpy.mockImplementation(() => { });
|
|
47
|
+
});
|
|
48
|
+
it('should track the command usage', async () => {
|
|
49
|
+
await projectUpdateDepsCommand.handler(args);
|
|
50
|
+
expect(trackCommandUsage).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(trackCommandUsage).toHaveBeenCalledWith('project-update-deps', undefined, args.derivedAccountId);
|
|
52
|
+
});
|
|
53
|
+
it('should handle exceptions', async () => {
|
|
54
|
+
const error = new Error('Something went super wrong');
|
|
55
|
+
getProjectConfigSpy.mockImplementationOnce(() => {
|
|
56
|
+
throw error;
|
|
57
|
+
});
|
|
58
|
+
await projectUpdateDepsCommand.handler(args);
|
|
59
|
+
expect(uiLogger.error).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(uiLogger.error).toHaveBeenCalledWith(error.message);
|
|
61
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
62
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
63
|
+
});
|
|
64
|
+
it('should log an error and exit when the project config is not defined', async () => {
|
|
65
|
+
getProjectConfigSpy.mockResolvedValueOnce({
|
|
66
|
+
projectDir: null,
|
|
67
|
+
projectConfig: null,
|
|
68
|
+
});
|
|
69
|
+
await projectUpdateDepsCommand.handler(args);
|
|
70
|
+
expect(uiLogger.error).toHaveBeenCalledTimes(1);
|
|
71
|
+
expect(uiLogger.error).toHaveBeenCalledWith('No project detected. Run this command from a project directory.');
|
|
72
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
73
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
74
|
+
});
|
|
75
|
+
it('should log an error and exit when the project config has no projectDir', async () => {
|
|
76
|
+
getProjectConfigSpy.mockResolvedValueOnce({
|
|
77
|
+
projectDir: null,
|
|
78
|
+
projectConfig: null,
|
|
79
|
+
});
|
|
80
|
+
await projectUpdateDepsCommand.handler(args);
|
|
81
|
+
expect(uiLogger.error).toHaveBeenCalledTimes(1);
|
|
82
|
+
expect(uiLogger.error).toHaveBeenCalledWith('No project detected. Run this command from a project directory.');
|
|
83
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
84
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
85
|
+
});
|
|
86
|
+
it('should prompt for input when packages is defined', async () => {
|
|
87
|
+
const projectDir = 'src';
|
|
88
|
+
getProjectConfigSpy.mockResolvedValue({
|
|
89
|
+
projectDir,
|
|
90
|
+
projectConfig: null,
|
|
91
|
+
});
|
|
92
|
+
const packageJsonLocation = path.join(projectDir, 'directory1');
|
|
93
|
+
promptUserSpy.mockResolvedValueOnce({
|
|
94
|
+
selectedInstallLocations: packageJsonLocation,
|
|
95
|
+
});
|
|
96
|
+
getProjectPackageJsonLocationsSpy.mockResolvedValue([
|
|
97
|
+
packageJsonLocation,
|
|
98
|
+
]);
|
|
99
|
+
await projectUpdateDepsCommand.handler({
|
|
100
|
+
...args,
|
|
101
|
+
packages: ['@hubspot/local-dev-lib'],
|
|
102
|
+
});
|
|
103
|
+
expect(getProjectPackageJsonLocationsSpy).toHaveBeenCalledTimes(1);
|
|
104
|
+
expect(promptUserSpy).toHaveBeenCalledTimes(1);
|
|
105
|
+
expect(promptUserSpy).toHaveBeenCalledWith([
|
|
106
|
+
{
|
|
107
|
+
name: 'selectedInstallLocations',
|
|
108
|
+
type: 'checkbox',
|
|
109
|
+
when: expect.any(Function),
|
|
110
|
+
choices: [
|
|
111
|
+
{
|
|
112
|
+
name: 'directory1',
|
|
113
|
+
value: packageJsonLocation,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
message: 'Choose which project components you would like to update the dependencies for:',
|
|
117
|
+
validate: expect.any(Function),
|
|
118
|
+
},
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
it('should call updatePackages correctly', async () => {
|
|
122
|
+
const projectDir = 'src';
|
|
123
|
+
const packageJsonLocation = path.join(projectDir, 'directory1');
|
|
124
|
+
const installLocations = [packageJsonLocation];
|
|
125
|
+
const packages = ['@hubspot/local-dev-lib'];
|
|
126
|
+
getProjectConfigSpy.mockResolvedValue({
|
|
127
|
+
projectDir,
|
|
128
|
+
projectConfig: null,
|
|
129
|
+
});
|
|
130
|
+
promptUserSpy.mockResolvedValueOnce({
|
|
131
|
+
selectedInstallLocations: packageJsonLocation,
|
|
132
|
+
});
|
|
133
|
+
getProjectPackageJsonLocationsSpy.mockResolvedValue(installLocations);
|
|
134
|
+
await projectUpdateDepsCommand.handler({ ...args, packages });
|
|
135
|
+
expect(updatePackagesSpy).toHaveBeenCalledTimes(1);
|
|
136
|
+
expect(updatePackagesSpy).toHaveBeenCalledWith({
|
|
137
|
+
packages,
|
|
138
|
+
installLocations: packageJsonLocation,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -10,6 +10,7 @@ import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
|
|
|
10
10
|
import { loadProfile, exitIfUsingProfiles, } from '../../../lib/projectProfiles.js';
|
|
11
11
|
import { commands } from '../../../lang/en.js';
|
|
12
12
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
13
|
+
import { logError } from '../../../lib/errorHandlers/index.js';
|
|
13
14
|
const command = 'dev';
|
|
14
15
|
const describe = commands.project.dev.describe;
|
|
15
16
|
function validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV2) {
|
|
@@ -26,7 +27,13 @@ function validateAccountFlags(testingAccount, projectAccount, userProvidedAccoun
|
|
|
26
27
|
async function handler(args) {
|
|
27
28
|
const { derivedAccountId, userProvidedAccount, testingAccount, projectAccount, } = args;
|
|
28
29
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
29
|
-
|
|
30
|
+
try {
|
|
31
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
logError(error);
|
|
35
|
+
process.exit(EXIT_CODES.ERROR);
|
|
36
|
+
}
|
|
30
37
|
const useV2Projects = isV2Project(projectConfig.platformVersion);
|
|
31
38
|
if (!projectDir) {
|
|
32
39
|
uiLogger.error(commands.project.dev.errors.noProjectConfig);
|
|
@@ -68,7 +68,13 @@ async function handler(args) {
|
|
|
68
68
|
let projectName = projectFlagValue;
|
|
69
69
|
if (!projectName) {
|
|
70
70
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
71
|
-
|
|
71
|
+
try {
|
|
72
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logError(error);
|
|
76
|
+
process.exit(EXIT_CODES.ERROR);
|
|
77
|
+
}
|
|
72
78
|
projectName = projectConfig.name;
|
|
73
79
|
}
|
|
74
80
|
try {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CommonArgs, ConfigArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
2
|
+
export type ProjectUpdateDepsArgs = CommonArgs & ConfigArgs & {
|
|
3
|
+
packages?: string[];
|
|
4
|
+
};
|
|
5
|
+
declare const projectUpdateDepsCommand: YargsCommandModule<unknown, ProjectUpdateDepsArgs>;
|
|
6
|
+
export default projectUpdateDepsCommand;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { updatePackages, getProjectPackageJsonLocations, } from '../../lib/dependencyManagement.js';
|
|
2
|
+
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
3
|
+
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
4
|
+
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { commands } from '../../lang/en.js';
|
|
7
|
+
import { uiLogger } from '../../lib/ui/logger.js';
|
|
8
|
+
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
9
|
+
import { logError } from '../../lib/errorHandlers/index.js';
|
|
10
|
+
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
11
|
+
const command = 'update-deps [packages..]';
|
|
12
|
+
const describe = commands.project.updateDeps.help.describe;
|
|
13
|
+
async function handler(args) {
|
|
14
|
+
const { derivedAccountId, packages } = args;
|
|
15
|
+
try {
|
|
16
|
+
trackCommandUsage('project-update-deps', undefined, derivedAccountId);
|
|
17
|
+
const projectConfig = await getProjectConfig();
|
|
18
|
+
if (!projectConfig || !projectConfig.projectDir) {
|
|
19
|
+
uiLogger.error(commands.project.updateDeps.noProjectConfig);
|
|
20
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
21
|
+
}
|
|
22
|
+
const { projectDir } = projectConfig;
|
|
23
|
+
let installLocations = await getProjectPackageJsonLocations();
|
|
24
|
+
if (packages) {
|
|
25
|
+
const { selectedInstallLocations } = await promptUser([
|
|
26
|
+
{
|
|
27
|
+
name: 'selectedInstallLocations',
|
|
28
|
+
type: 'checkbox',
|
|
29
|
+
when: () => packages && packages.length > 0,
|
|
30
|
+
message: commands.project.updateDeps.installLocationPrompt,
|
|
31
|
+
choices: installLocations.map(dir => ({
|
|
32
|
+
name: path.relative(projectDir, dir),
|
|
33
|
+
value: dir,
|
|
34
|
+
})),
|
|
35
|
+
validate: (choices) => {
|
|
36
|
+
if (choices === undefined || choices.length === 0) {
|
|
37
|
+
return commands.project.updateDeps.installLocationPromptRequired;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
if (selectedInstallLocations) {
|
|
44
|
+
installLocations = selectedInstallLocations;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await updatePackages({
|
|
48
|
+
packages,
|
|
49
|
+
installLocations,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
logError(e);
|
|
54
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function projectUpdateDepsBuilder(yargs) {
|
|
58
|
+
yargs.example([
|
|
59
|
+
[
|
|
60
|
+
'$0 project update-deps',
|
|
61
|
+
commands.project.updateDeps.help.updateAppDepsExample,
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
'$0 project update-deps dependency1 dependency2',
|
|
65
|
+
commands.project.updateDeps.help.updateDepToSubComponentExample,
|
|
66
|
+
],
|
|
67
|
+
]);
|
|
68
|
+
return yargs;
|
|
69
|
+
}
|
|
70
|
+
const builder = makeYargsBuilder(projectUpdateDepsBuilder, command, describe, {
|
|
71
|
+
useGlobalOptions: true,
|
|
72
|
+
useConfigOptions: true,
|
|
73
|
+
});
|
|
74
|
+
const projectUpdateDepsCommand = {
|
|
75
|
+
command,
|
|
76
|
+
describe,
|
|
77
|
+
handler,
|
|
78
|
+
builder,
|
|
79
|
+
};
|
|
80
|
+
export default projectUpdateDepsCommand;
|
|
@@ -20,7 +20,13 @@ async function handler(args) {
|
|
|
20
20
|
const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile, } = args;
|
|
21
21
|
const jsonOutput = {};
|
|
22
22
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
23
|
-
|
|
23
|
+
try {
|
|
24
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logError(error);
|
|
28
|
+
process.exit(EXIT_CODES.ERROR);
|
|
29
|
+
}
|
|
24
30
|
let targetAccountId;
|
|
25
31
|
if (isV2Project(projectConfig.platformVersion)) {
|
|
26
32
|
targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
|
|
@@ -23,7 +23,13 @@ async function handler(args) {
|
|
|
23
23
|
uiLogger.error(commands.project.validate.badVersion);
|
|
24
24
|
process.exit(EXIT_CODES.ERROR);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
try {
|
|
27
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
logError(error);
|
|
31
|
+
process.exit(EXIT_CODES.ERROR);
|
|
32
|
+
}
|
|
27
33
|
let targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
|
|
28
34
|
targetAccountId = targetAccountId || derivedAccountId;
|
|
29
35
|
const accountConfig = getAccountConfig(targetAccountId);
|
|
@@ -58,7 +58,13 @@ async function handler(args) {
|
|
|
58
58
|
const { initialUpload, derivedAccountId } = args;
|
|
59
59
|
trackCommandUsage('project-watch', undefined, derivedAccountId);
|
|
60
60
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
61
|
-
|
|
61
|
+
try {
|
|
62
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logError(error);
|
|
66
|
+
process.exit(EXIT_CODES.ERROR);
|
|
67
|
+
}
|
|
62
68
|
if (!projectConfig || !projectDir) {
|
|
63
69
|
uiLogger.error(commands.project.watch.errors.projectConfigNotFound);
|
|
64
70
|
return process.exit(EXIT_CODES.ERROR);
|
|
@@ -67,7 +73,6 @@ async function handler(args) {
|
|
|
67
73
|
uiLogger.error(projectConfig.platformVersion);
|
|
68
74
|
return process.exit(EXIT_CODES.ERROR);
|
|
69
75
|
}
|
|
70
|
-
validateProjectConfig(projectConfig, projectDir);
|
|
71
76
|
try {
|
|
72
77
|
const { data: { results: builds }, } = await fetchProjectBuilds(derivedAccountId, projectConfig.name);
|
|
73
78
|
const hasNoBuilds = !builds || !builds.length;
|
package/commands/project.js
CHANGED
|
@@ -13,6 +13,7 @@ import migrate from './project/migrate.js';
|
|
|
13
13
|
import migrateApp from './project/migrateApp.js';
|
|
14
14
|
import cloneApp from './project/cloneApp.js';
|
|
15
15
|
import installDeps from './project/installDeps.js';
|
|
16
|
+
import updateDeps from './project/updateDeps.js';
|
|
16
17
|
import profile from './project/profile.js';
|
|
17
18
|
import projectValidate from './project/validate.js';
|
|
18
19
|
import list from './project/list.js';
|
|
@@ -36,6 +37,7 @@ function projectBuilder(yargs) {
|
|
|
36
37
|
.command(migrate)
|
|
37
38
|
.command(cloneApp)
|
|
38
39
|
.command(installDeps)
|
|
40
|
+
.command(updateDeps)
|
|
39
41
|
.command(profile)
|
|
40
42
|
.command(projectValidate)
|
|
41
43
|
.demandCommand(1, '');
|