@hubspot/cli 8.0.11-experimental.2 → 8.0.12-experimental.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.
Files changed (75) hide show
  1. package/bin/cli.js +4 -3
  2. package/commands/account/clean.js +2 -0
  3. package/commands/account/createOverride.js +3 -0
  4. package/commands/account/info.js +34 -16
  5. package/commands/account/link.d.ts +4 -0
  6. package/commands/account/link.js +89 -0
  7. package/commands/account/list.js +29 -71
  8. package/commands/account/remove.js +2 -0
  9. package/commands/account/removeOverride.js +3 -0
  10. package/commands/account/unlink.d.ts +4 -0
  11. package/commands/account/unlink.js +70 -0
  12. package/commands/account/use.js +71 -1
  13. package/commands/account.js +4 -0
  14. package/commands/project/appInstallStatus.d.ts +4 -0
  15. package/commands/project/appInstallStatus.js +132 -0
  16. package/commands/project/create.js +8 -0
  17. package/commands/project/dev/deprecatedFlow.js +20 -2
  18. package/commands/project/dev/index.js +6 -0
  19. package/commands/project/dev/unifiedFlow.js +20 -3
  20. package/commands/project/lint.js +20 -2
  21. package/commands/project/upload.d.ts +2 -0
  22. package/commands/project/upload.js +47 -3
  23. package/commands/project.js +2 -0
  24. package/lang/en.d.ts +122 -0
  25. package/lang/en.js +136 -8
  26. package/lib/app/migrate.js +2 -1
  27. package/lib/constants.d.ts +2 -0
  28. package/lib/constants.js +4 -0
  29. package/lib/doctor/Doctor.js +5 -5
  30. package/lib/link/accountTableUtils.d.ts +10 -0
  31. package/lib/link/accountTableUtils.js +39 -0
  32. package/lib/link/index.d.ts +18 -0
  33. package/lib/link/index.js +185 -0
  34. package/lib/link/linkUtils.d.ts +5 -0
  35. package/lib/link/linkUtils.js +49 -0
  36. package/lib/link/prompts.d.ts +7 -0
  37. package/lib/link/prompts.js +126 -0
  38. package/lib/link/renderLinkedAccountsTable.d.ts +2 -0
  39. package/lib/link/renderLinkedAccountsTable.js +14 -0
  40. package/lib/link/warnIfLinkedDirectory.d.ts +1 -0
  41. package/lib/link/warnIfLinkedDirectory.js +9 -0
  42. package/lib/projects/ProjectLogsManager.js +4 -1
  43. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +2 -1
  44. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  45. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +2 -0
  46. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -0
  47. package/lib/projects/preview.d.ts +7 -0
  48. package/lib/projects/preview.js +58 -0
  49. package/lib/projects/uieLinting.d.ts +17 -3
  50. package/lib/projects/uieLinting.js +93 -28
  51. package/lib/projects/upload.d.ts +1 -0
  52. package/lib/projects/upload.js +4 -3
  53. package/lib/prompts/projectsLogsPrompt.js +3 -0
  54. package/lib/prompts/promptUtils.js +1 -0
  55. package/lib/ui/accountTable.d.ts +8 -0
  56. package/lib/ui/accountTable.js +67 -0
  57. package/lib/yargs/parseYargsOrExit.d.ts +4 -0
  58. package/lib/yargs/parseYargsOrExit.js +25 -0
  59. package/mcp-server/server.js +39 -1
  60. package/mcp-server/tools/index.js +2 -0
  61. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  62. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  63. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  64. package/mcp-server/tools/project/FindProjectsTool.d.ts +15 -0
  65. package/mcp-server/tools/project/FindProjectsTool.js +60 -0
  66. package/mcp-server/tools/project/GetBuildLogsTool.js +1 -1
  67. package/mcp-server/tools/project/GetBuildStatusTool.js +1 -1
  68. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  69. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  70. package/package.json +7 -7
  71. package/types/Link.d.ts +32 -0
  72. package/types/Link.js +5 -0
  73. package/types/PackageJson.d.ts +1 -0
  74. package/types/Prompts.d.ts +1 -0
  75. package/types/Yargs.d.ts +1 -0
@@ -0,0 +1,132 @@
1
+ import path from 'path';
2
+ import { fetchAppInstallationData } from '@hubspot/local-dev-lib/api/localDevAuth';
3
+ import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
4
+ import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
5
+ import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
6
+ import { translateForLocalDev } from '@hubspot/project-parsing-lib/translate';
7
+ import { makeYargsHandlerWithUsageTracking } from '../../lib/yargs/makeYargsHandlerWithUsageTracking.js';
8
+ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
9
+ import { uiLogger } from '../../lib/ui/logger.js';
10
+ import { ApiErrorContext, debugError, logError, } from '../../lib/errorHandlers/index.js';
11
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
12
+ import { commands } from '../../lang/en.js';
13
+ import { getProjectConfig } from '../../lib/projects/config.js';
14
+ import { isAppIRNode } from '../../lib/projects/structure.js';
15
+ import { APP_AUTH_TYPES } from '../../lib/constants.js';
16
+ const command = 'app-install-status';
17
+ const describe = commands.project.installStatus.describe;
18
+ async function handler(args) {
19
+ const { derivedAccountId, formatOutputAsJson, exit } = args;
20
+ const { projectConfig, projectDir } = await getProjectConfig();
21
+ if (!projectConfig || !projectDir) {
22
+ uiLogger.error(commands.project.installStatus.errors.noProjectConfig);
23
+ return exit(EXIT_CODES.ERROR);
24
+ }
25
+ if (isLegacyProject(projectConfig.platformVersion)) {
26
+ uiLogger.error(commands.project.installStatus.errors.unsupportedPlatformVersion(projectConfig.platformVersion));
27
+ return exit(EXIT_CODES.ERROR);
28
+ }
29
+ let appNode;
30
+ try {
31
+ const { intermediateNodesIndexedByUid } = await translateForLocalDev({
32
+ projectSourceDir: path.join(projectDir, projectConfig.srcDir),
33
+ platformVersion: projectConfig.platformVersion,
34
+ accountId: derivedAccountId,
35
+ }, { skipValidation: true });
36
+ appNode = Object.values(intermediateNodesIndexedByUid).find(isAppIRNode);
37
+ }
38
+ catch (error) {
39
+ debugError(error);
40
+ uiLogger.error(commands.project.installStatus.errors.failedToParseProject);
41
+ return exit(EXIT_CODES.ERROR);
42
+ }
43
+ if (!appNode) {
44
+ uiLogger.error(commands.project.installStatus.errors.noAppInProject);
45
+ return exit(EXIT_CODES.ERROR);
46
+ }
47
+ if (appNode.config.auth.type.toLowerCase() !== APP_AUTH_TYPES.STATIC) {
48
+ uiLogger.error(commands.project.installStatus.errors.unsupportedAuthType(appNode.config.auth.type));
49
+ return exit(EXIT_CODES.ERROR);
50
+ }
51
+ let projectId;
52
+ try {
53
+ const response = await fetchProject(derivedAccountId, projectConfig.name);
54
+ projectId = response.data.id;
55
+ }
56
+ catch (error) {
57
+ logError(error, new ApiErrorContext({
58
+ accountId: derivedAccountId,
59
+ projectName: projectConfig.name,
60
+ }));
61
+ return exit(EXIT_CODES.ERROR);
62
+ }
63
+ let isInstalledWithScopeGroups = false;
64
+ let previouslyAuthorizedScopeGroups = [];
65
+ let appId;
66
+ try {
67
+ const response = await fetchAppInstallationData(derivedAccountId, projectId, appNode.uid, appNode.config.auth.requiredScopes, appNode.config.auth.optionalScopes);
68
+ isInstalledWithScopeGroups = response.data.isInstalledWithScopeGroups;
69
+ previouslyAuthorizedScopeGroups =
70
+ response.data.previouslyAuthorizedScopeGroups;
71
+ appId = response.data.appId;
72
+ }
73
+ catch (error) {
74
+ if (!isHubSpotHttpError(error) || error.status !== 404) {
75
+ logError(error, new ApiErrorContext({
76
+ accountId: derivedAccountId,
77
+ projectName: projectConfig.name,
78
+ }));
79
+ return exit(EXIT_CODES.ERROR);
80
+ }
81
+ }
82
+ const isInstalled = isInstalledWithScopeGroups || previouslyAuthorizedScopeGroups.length > 0;
83
+ if (formatOutputAsJson) {
84
+ uiLogger.json({
85
+ appId,
86
+ appUid: appNode.uid,
87
+ accountId: derivedAccountId,
88
+ projectId,
89
+ isInstalled,
90
+ isInstalledWithCurrentScopes: isInstalledWithScopeGroups,
91
+ previouslyAuthorizedScopeGroups,
92
+ });
93
+ return exit(isInstalled ? EXIT_CODES.SUCCESS : EXIT_CODES.WARNING);
94
+ }
95
+ if (isInstalled) {
96
+ if (isInstalledWithScopeGroups) {
97
+ uiLogger.success(commands.project.installStatus.success.installed(appNode.config.name, derivedAccountId));
98
+ }
99
+ else {
100
+ uiLogger.success(commands.project.installStatus.success.installedWithOutdatedScopes(appNode.config.name, derivedAccountId));
101
+ }
102
+ return exit(EXIT_CODES.SUCCESS);
103
+ }
104
+ uiLogger.log(commands.project.installStatus.notInstalled(appNode.config.name, derivedAccountId));
105
+ return exit(EXIT_CODES.WARNING);
106
+ }
107
+ function projectInstallStatusBuilder(yargs) {
108
+ yargs.example([
109
+ [
110
+ '$0 project app-install-status',
111
+ commands.project.installStatus.examples.default,
112
+ ],
113
+ [
114
+ '$0 project app-install-status --json',
115
+ commands.project.installStatus.examples.json,
116
+ ],
117
+ ]);
118
+ return yargs;
119
+ }
120
+ const builder = makeYargsBuilder(projectInstallStatusBuilder, command, describe, {
121
+ useGlobalOptions: true,
122
+ useConfigOptions: true,
123
+ useAccountOptions: true,
124
+ useJSONOutputOptions: true,
125
+ });
126
+ const projectInstallStatusCommand = {
127
+ command,
128
+ describe,
129
+ handler: makeYargsHandlerWithUsageTracking('project-app-install-status', handler),
130
+ builder,
131
+ };
132
+ export default projectInstallStatusCommand;
@@ -19,8 +19,15 @@ import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/com
19
19
  import SpinniesManager from '../../lib/ui/SpinniesManager.js';
20
20
  const command = ['create', 'init'];
21
21
  const describe = commands.project.create.describe;
22
+ const BETA_VERSIONS = [
23
+ PLATFORM_VERSIONS.v2026_09_BETA,
24
+ PLATFORM_VERSIONS.v2026_03_BETA,
25
+ ];
22
26
  async function handler(args) {
23
27
  const { platformVersion, templateSource, exit, addUsageMetadata } = args;
28
+ if (BETA_VERSIONS.includes(platformVersion)) {
29
+ uiLogger.warn(commands.project.create.warnings.betaPlatformVersion(platformVersion));
30
+ }
24
31
  if (templateSource && !templateSource.includes('/')) {
25
32
  uiLogger.error(commands.project.create.errors.invalidTemplateSource);
26
33
  return exit(EXIT_CODES.ERROR);
@@ -128,6 +135,7 @@ function projectCreateBuilder(yargs) {
128
135
  PLATFORM_VERSIONS.v2025_2,
129
136
  PLATFORM_VERSIONS.v2026_03_BETA,
130
137
  PLATFORM_VERSIONS.v2026_03,
138
+ PLATFORM_VERSIONS.v2026_09_BETA,
131
139
  ],
132
140
  default: PLATFORM_VERSIONS.v2026_03,
133
141
  },
@@ -1,4 +1,5 @@
1
- import { getConfigAccountById, getAllConfigAccounts, getConfigAccountEnvironment, } from '@hubspot/local-dev-lib/config';
1
+ import { getConfigAccountById, getLinkedOrAllConfigAccounts, getConfigAccountEnvironment, } from '@hubspot/local-dev-lib/config';
2
+ import { getHsSettingsFileIfExists, getHsSettingsFilePath, } from '@hubspot/local-dev-lib/config/hsSettings';
2
3
  import { findProjectComponents, getProjectComponentTypes, } from '../../../lib/projects/structure.js';
3
4
  import { ComponentTypes } from '../../../types/Projects.js';
4
5
  import { commands } from '../../../lang/en.js';
@@ -11,6 +12,7 @@ import { handleExit } from '../../../lib/process.js';
11
12
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
12
13
  import { isSandbox, isDeveloperTestAccount, } from '../../../lib/accountTypes.js';
13
14
  import { ensureProjectExists } from '../../../lib/projects/ensureProjectExists.js';
15
+ import { isDirectoryLinked, addAccountToLinkedSettings, } from '../../../lib/link/linkUtils.js';
14
16
  export async function deprecatedProjectDevFlow({ args, accountId, projectConfig, projectDir, }) {
15
17
  const { userProvidedAccount, derivedAccountId, exit } = args;
16
18
  const env = getConfigAccountEnvironment(derivedAccountId);
@@ -32,7 +34,9 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
32
34
  uiLogger.error(commands.project.dev.errors.invalidProjectComponents);
33
35
  return exit(EXIT_CODES.SUCCESS);
34
36
  }
35
- const accounts = getAllConfigAccounts();
37
+ const hsSettings = getHsSettingsFileIfExists();
38
+ const directoryIsLinked = isDirectoryLinked(hsSettings);
39
+ const accounts = getLinkedOrAllConfigAccounts();
36
40
  if (!accounts) {
37
41
  uiLogger.error(commands.project.dev.errors.noAccountsInConfig);
38
42
  return exit(EXIT_CODES.ERROR);
@@ -69,6 +73,10 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
69
73
  else {
70
74
  await checkIfDefaultAccountIsSupported(accountConfig, hasPublicApps, exit);
71
75
  }
76
+ if (directoryIsLinked) {
77
+ uiLogger.log('');
78
+ uiLogger.info(commands.account.subcommands.link.shared.usingLinkedAccounts(getHsSettingsFilePath()));
79
+ }
72
80
  // The user is targeting an account type that we recommend developing on
73
81
  if (!targetProjectAccountId && bypassRecommendedAccountPrompt) {
74
82
  targetTestingAccountId = derivedAccountId;
@@ -101,6 +109,9 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
101
109
  if (!accountAdded) {
102
110
  return exit(EXIT_CODES.SUCCESS);
103
111
  }
112
+ if (directoryIsLinked) {
113
+ addAccountToLinkedSettings(notInConfigAccount.id);
114
+ }
104
115
  }
105
116
  createNewSandbox = hasPrivateApps && createNestedAccount;
106
117
  createNewDeveloperTestAccount = hasPublicApps && createNestedAccount;
@@ -114,6 +125,9 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
114
125
  }
115
126
  // We will be running our tests against this new sandbox account
116
127
  targetTestingAccountId = targetProjectAccountId;
128
+ if (directoryIsLinked) {
129
+ addAccountToLinkedSettings(targetProjectAccountId);
130
+ }
117
131
  }
118
132
  if (createNewDeveloperTestAccount) {
119
133
  try {
@@ -123,6 +137,9 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
123
137
  return exit(EXIT_CODES.ERROR);
124
138
  }
125
139
  targetProjectAccountId = derivedAccountId;
140
+ if (directoryIsLinked) {
141
+ addAccountToLinkedSettings(targetTestingAccountId);
142
+ }
126
143
  }
127
144
  if (!targetProjectAccountId || !targetTestingAccountId) {
128
145
  uiLogger.error(commands.project.dev.errors.noAccount(accountId));
@@ -156,6 +173,7 @@ export async function deprecatedProjectDevFlow({ args, accountId, projectConfig,
156
173
  targetAccountId: targetTestingAccountId,
157
174
  env,
158
175
  exit,
176
+ port: args.port,
159
177
  });
160
178
  await LocalDev.start();
161
179
  handleExit(({ isSIGHUP }) => LocalDev.stop(!isSIGHUP));
@@ -13,6 +13,7 @@ import { uiLogger } from '../../../lib/ui/logger.js';
13
13
  import { logError } from '../../../lib/errorHandlers/index.js';
14
14
  import { projectProfilePrompt } from '../../../lib/prompts/projectProfilePrompt.js';
15
15
  import { isPromptExitError } from '../../../lib/errors/PromptExitError.js';
16
+ import { LOCAL_DEV_DEFAULT_PORT } from '../../../lib/constants.js';
16
17
  const command = 'dev';
17
18
  const describe = commands.project.dev.describe;
18
19
  function validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV2) {
@@ -149,6 +150,11 @@ function projectDevBuilder(yargs) {
149
150
  type: 'string',
150
151
  description: commands.project.dev.options.account,
151
152
  });
153
+ yargs.option('port', {
154
+ type: 'number',
155
+ description: commands.project.dev.options.port,
156
+ default: LOCAL_DEV_DEFAULT_PORT,
157
+ });
152
158
  yargs.example([['$0 project dev', commands.project.dev.examples.default]]);
153
159
  yargs.conflicts('profile', 'account');
154
160
  yargs.conflicts('profile', 'testing-account');
@@ -3,7 +3,7 @@ import util from 'util';
3
3
  import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
4
4
  import { startPortManagerServer, stopPortManagerServer, } from '@hubspot/local-dev-lib/portManager';
5
5
  import { isTranslationError, translateForLocalDev, } from '@hubspot/project-parsing-lib/translate';
6
- import { getConfigAccountEnvironment, getAllConfigAccounts, getConfigAccountById, } from '@hubspot/local-dev-lib/config';
6
+ import { getConfigAccountEnvironment, getLinkedOrAllConfigAccounts, getConfigAccountById, } from '@hubspot/local-dev-lib/config';
7
7
  import { logError } from '../../../lib/errorHandlers/index.js';
8
8
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
9
9
  import { ensureProjectExists } from '../../../lib/projects/ensureProjectExists.js';
@@ -18,6 +18,8 @@ import { uiLogger } from '../../../lib/ui/logger.js';
18
18
  import { commands } from '../../../lang/en.js';
19
19
  import LocalDevWebsocketServer from '../../../lib/projects/localDev/LocalDevWebsocketServer.js';
20
20
  import { isLocalDevRunning } from '../../../lib/projects/localDev/helpers/process.js';
21
+ import { getHsSettingsFileIfExists, getHsSettingsFilePath, } from '@hubspot/local-dev-lib/config/hsSettings';
22
+ import { isDirectoryLinked, addAccountToLinkedSettings, } from '../../../lib/link/linkUtils.js';
21
23
  export async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir, }) {
22
24
  const { exit } = args;
23
25
  if (await isLocalDevRunning()) {
@@ -56,13 +58,19 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
56
58
  uiLogger.error(commands.project.dev.errors.noAccount(targetProjectAccountId));
57
59
  return exit(EXIT_CODES.ERROR);
58
60
  }
59
- const accounts = getAllConfigAccounts();
61
+ const hsSettings = getHsSettingsFileIfExists();
62
+ const directoryIsLinked = isDirectoryLinked(hsSettings);
63
+ const accounts = getLinkedOrAllConfigAccounts();
60
64
  const accountIsCombined = await isUnifiedAccount(targetProjectAccountConfig);
61
65
  const targetProjectAccountIsTestAccountOrSandbox = isTestAccountOrSandbox(targetProjectAccountConfig);
62
66
  if (!accountIsCombined) {
63
67
  uiLogger.error(commands.project.dev.errors.accountNotCombined);
64
68
  return exit(EXIT_CODES.ERROR);
65
69
  }
70
+ if (directoryIsLinked && !providedTargetTestingAccountId) {
71
+ uiLogger.log('');
72
+ uiLogger.info(commands.account.subcommands.link.shared.usingLinkedAccounts(getHsSettingsFilePath()));
73
+ }
66
74
  let targetTestingAccountId = providedTargetTestingAccountId;
67
75
  // Temporarily removing logic to use profile account as testing account
68
76
  // if (profileConfig) {
@@ -90,6 +98,9 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
90
98
  if (!accountAdded) {
91
99
  return exit(EXIT_CODES.SUCCESS);
92
100
  }
101
+ if (directoryIsLinked) {
102
+ addAccountToLinkedSettings(devAccountPromptResponse.notInConfigAccount.id);
103
+ }
93
104
  }
94
105
  else if (devAccountPromptResponse.createNestedAccount) {
95
106
  // Create a new developer test account and automatically add it to the CLI config
@@ -99,6 +110,9 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
99
110
  catch {
100
111
  return exit(EXIT_CODES.ERROR);
101
112
  }
113
+ if (directoryIsLinked) {
114
+ addAccountToLinkedSettings(targetTestingAccountId);
115
+ }
102
116
  }
103
117
  }
104
118
  else if (accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX) {
@@ -112,6 +126,9 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
112
126
  catch {
113
127
  return exit(EXIT_CODES.ERROR);
114
128
  }
129
+ if (directoryIsLinked) {
130
+ addAccountToLinkedSettings(targetTestingAccountId);
131
+ }
115
132
  }
116
133
  }
117
134
  else {
@@ -143,7 +160,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
143
160
  }
144
161
  // End setup, start local dev process
145
162
  try {
146
- await startPortManagerServer();
163
+ await startPortManagerServer(args.port);
147
164
  }
148
165
  catch (e) {
149
166
  logError(e);
@@ -10,7 +10,7 @@ import { logError } from '../../lib/errorHandlers/index.js';
10
10
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
11
11
  import { promptUser } from '../../lib/prompts/promptUtils.js';
12
12
  import SpinniesManager from '../../lib/ui/SpinniesManager.js';
13
- import { areAllLintPackagesInstalled, getMissingLintPackages, lintPackages, displayLintResults, hasEslintConfig, hasDeprecatedEslintConfig, getDeprecatedEslintConfigFiles, createEslintConfig, REQUIRED_PACKAGES_AND_MIN_VERSIONS, } from '../../lib/projects/uieLinting.js';
13
+ import { areAllLintPackagesInstalled, getMissingLintPackages, getMissingLintScripts, addLintScriptsToPackageJson, lintPackages, displayLintResults, hasEslintConfig, hasDeprecatedEslintConfig, getDeprecatedEslintConfigFiles, createEslintConfig, REQUIRED_PACKAGES_AND_MIN_VERSIONS, } from '../../lib/projects/uieLinting.js';
14
14
  const command = 'lint';
15
15
  const describe = commands.project.lint.help.describe;
16
16
  async function handler(args) {
@@ -117,9 +117,10 @@ async function handler(args) {
117
117
  SpinniesManager.add('lintConfigCreate', {
118
118
  text: commands.project.lint.loading.creatingConfig,
119
119
  });
120
+ const platformVersion = projectConfig.projectConfig?.platformVersion ?? null;
120
121
  const createdConfigs = [];
121
122
  for (const location of locationsNeedingConfig) {
122
- const configPath = createEslintConfig(location);
123
+ const configPath = await createEslintConfig(location, platformVersion);
123
124
  createdConfigs.push(configPath);
124
125
  }
125
126
  SpinniesManager.succeed('lintConfigCreate');
@@ -132,6 +133,23 @@ async function handler(args) {
132
133
  return exit(EXIT_CODES.ERROR);
133
134
  }
134
135
  }
136
+ const locationsNeedingScripts = locationsReadyToLint.filter(location => getMissingLintScripts(location).length > 0);
137
+ if (locationsNeedingScripts.length > 0) {
138
+ SpinniesManager.add('lintScriptsAdd', {
139
+ text: commands.project.lint.loading.addingLintScripts,
140
+ });
141
+ const addedResults = [];
142
+ for (const location of locationsNeedingScripts) {
143
+ const result = addLintScriptsToPackageJson(location);
144
+ if (result.added.length > 0) {
145
+ addedResults.push(result);
146
+ }
147
+ }
148
+ SpinniesManager.succeed('lintScriptsAdd');
149
+ addedResults.forEach(({ added, relativePath }) => {
150
+ uiLogger.success(commands.project.lint.lintScriptsAdded(added, relativePath));
151
+ });
152
+ }
135
153
  SpinniesManager.add('lintRun', {
136
154
  text: commands.project.lint.loading.linting,
137
155
  });
@@ -5,6 +5,8 @@ export type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
5
5
  m: string;
6
6
  skipValidation: boolean;
7
7
  profile?: string;
8
+ preview: boolean;
9
+ target?: number;
8
10
  };
9
11
  declare const projectUploadCommand: YargsCommandModule<unknown, ProjectUploadArgs>;
10
12
  export default projectUploadCommand;
@@ -8,7 +8,8 @@ import { logFeedbackMessage } from '../../lib/projects/ui.js';
8
8
  import { handleProjectUpload } from '../../lib/projects/upload.js';
9
9
  import { loadAndValidateProfile } from '../../lib/projects/projectProfiles.js';
10
10
  import { displayWarnLogs, pollProjectBuildAndDeploy, } from '../../lib/projects/pollProjectBuildAndDeploy.js';
11
- import { commands } from '../../lang/en.js';
11
+ import { triggerAndPollPreview } from '../../lib/projects/preview.js';
12
+ import { commands, lib } from '../../lang/en.js';
12
13
  import { PROJECT_ERROR_TYPES } from '../../lib/constants.js';
13
14
  import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
14
15
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
@@ -17,8 +18,22 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
17
18
  import { projectProfilePrompt } from '../../lib/prompts/projectProfilePrompt.js';
18
19
  const command = 'upload';
19
20
  const describe = commands.project.upload.describe;
21
+ async function handlePreview(accountId, projectId, buildId, targetPortalId) {
22
+ if (!projectId) {
23
+ uiLogger.warn(lib.projectPreview.missingProjectId);
24
+ return;
25
+ }
26
+ const previewResult = await triggerAndPollPreview(accountId, projectId, buildId, targetPortalId);
27
+ if (!previewResult.succeeded) {
28
+ uiLogger.warn(lib.projectPreview.warning);
29
+ }
30
+ return {
31
+ releaseTag: previewResult.releaseTag,
32
+ succeeded: previewResult.succeeded,
33
+ };
34
+ }
20
35
  async function handler(args) {
21
- const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, exit, addUsageMetadata, } = args;
36
+ const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, } = args;
22
37
  const jsonOutput = {};
23
38
  const { projectConfig, projectDir } = await getProjectConfig();
24
39
  try {
@@ -56,7 +71,7 @@ async function handler(args) {
56
71
  assetType: projectConfig.platformVersion,
57
72
  });
58
73
  try {
59
- const { result, uploadError } = await handleProjectUpload({
74
+ const { result, uploadError, projectId } = await handleProjectUpload({
60
75
  accountId: targetAccountId,
61
76
  projectConfig,
62
77
  projectDir,
@@ -90,6 +105,12 @@ async function handler(args) {
90
105
  logFeedbackMessage(result.buildId);
91
106
  await displayWarnLogs(targetAccountId, projectConfig.name, result.buildId);
92
107
  }
108
+ if (result && result.succeeded && preview && targetPortalId) {
109
+ const previewJson = await handlePreview(targetAccountId, projectId, result.buildId, targetPortalId);
110
+ if (previewJson && formatOutputAsJson) {
111
+ jsonOutput.preview = previewJson;
112
+ }
113
+ }
93
114
  if (result && result.succeeded && formatOutputAsJson) {
94
115
  jsonOutput.buildId = result.buildId;
95
116
  if (result.deployResult) {
@@ -132,6 +153,25 @@ function projectUploadBuilder(yargs) {
132
153
  alias: 'p',
133
154
  describe: commands.project.upload.options.profile.describe,
134
155
  },
156
+ preview: {
157
+ describe: commands.project.upload.options.preview.describe,
158
+ type: 'boolean',
159
+ default: false,
160
+ },
161
+ target: {
162
+ describe: commands.project.upload.options.target.describe,
163
+ type: 'number',
164
+ requiresArg: true,
165
+ },
166
+ });
167
+ yargs.check(argv => {
168
+ if (argv.preview && argv.target == null) {
169
+ throw new Error(commands.project.upload.errors.previewRequiresTarget);
170
+ }
171
+ if (argv.target != null && !argv.preview) {
172
+ throw new Error(commands.project.upload.errors.targetRequiresPreview);
173
+ }
174
+ return true;
135
175
  });
136
176
  yargs.conflicts('profile', 'account');
137
177
  yargs.example([
@@ -140,6 +180,10 @@ function projectUploadBuilder(yargs) {
140
180
  '$0 project upload --profile=profileName',
141
181
  commands.project.upload.examples.withProfile,
142
182
  ],
183
+ [
184
+ '$0 project upload --preview --target=12345',
185
+ commands.project.upload.examples.withPreview,
186
+ ],
143
187
  ]);
144
188
  return yargs;
145
189
  }
@@ -19,6 +19,7 @@ import projectValidate from './project/validate.js';
19
19
  import list from './project/list.js';
20
20
  import info from './project/info.js';
21
21
  import deleteProject from './project/delete.js';
22
+ import appInstallStatus from './project/appInstallStatus.js';
22
23
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
23
24
  import { getProjectConfig } from '../lib/projects/config.js';
24
25
  import { isSupportedPlatformVersion, LATEST_SUPPORTED_PLATFORM_VERSION, } from '@hubspot/project-parsing-lib/projects';
@@ -66,6 +67,7 @@ function projectBuilder(yargs) {
66
67
  .command(updateDeps)
67
68
  .command(profile)
68
69
  .command(projectValidate)
70
+ .command(appInstallStatus)
69
71
  .demandCommand(1, '');
70
72
  return yargs;
71
73
  }