@hubspot/cli 8.9.0-beta.1 → 8.9.1-beta.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/lang/en.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
2
+ import { LocallyChangedComponents } from '../types/Projects.js';
2
3
  export declare const commands: {
3
4
  generalErrors: {
4
5
  srcIsProject: (src: string, command: string) => string;
@@ -3394,7 +3395,9 @@ export declare const lib: {
3394
3395
  checking: string;
3395
3396
  upToDate: string;
3396
3397
  notUpToDate: string;
3398
+ changedFiles: (changed: LocallyChangedComponents) => string;
3397
3399
  notUpToDateExplanation: (profile?: string) => string;
3400
+ unableToCompare: string;
3398
3401
  };
3399
3402
  createNewProjectForLocalDev: {
3400
3403
  projectMustExistExplanation: (projectName: string, accountId: number) => string;
package/lang/en.js CHANGED
@@ -3419,7 +3419,21 @@ export const lib = {
3419
3419
  checking: 'Checking if your deployed build is up to date...',
3420
3420
  upToDate: 'Deployed build is up to date.',
3421
3421
  notUpToDate: `Your project contains undeployed local changes.`,
3422
+ changedFiles: (changed) => {
3423
+ const sections = [];
3424
+ if (changed.added.length > 0) {
3425
+ sections.push(`${chalk.bold('Features Added:')}\n${changed.added.map(f => ` - ${f}`).join('\n')}`);
3426
+ }
3427
+ if (changed.updated.length > 0) {
3428
+ sections.push(`${chalk.bold('Features Modified:')}\n${changed.updated.map(f => ` - ${f}`).join('\n')}`);
3429
+ }
3430
+ if (changed.removed.length > 0) {
3431
+ sections.push(`${chalk.bold('Features Removed:')}\n${changed.removed.map(f => ` - ${f}`).join('\n')}`);
3432
+ }
3433
+ return sections.join('\n\n');
3434
+ },
3422
3435
  notUpToDateExplanation: (profile) => `Run ${uiCommandReference(`hs project upload${profile ? ` --profile ${profile}` : ''}`)} to upload these changes to HubSpot, then re-run ${uiCommandReference(`hs project dev${profile ? ` --profile ${profile}` : ''}`)} to continue local development.`,
3436
+ unableToCompare: `Unable to check if local *-hsmeta.json files match the deployed build. Run ${uiCommandReference('hs project upload')} to upload any local changes before continuing.`,
3423
3437
  },
3424
3438
  createNewProjectForLocalDev: {
3425
3439
  projectMustExistExplanation: (projectName, accountId) => `The project ${projectName} does not exist in the target account ${uiAccountDescription(accountId)}. This command requires the project to exist in the target account.`,
@@ -20,11 +20,13 @@ class UIExtensionsDevModeInterface {
20
20
  });
21
21
  }
22
22
  async start() {
23
+ const appId = Object.values(this.localDevState.appData)[0]?.id;
23
24
  return UIEDevModeInterface.start({
24
25
  accountId: this.localDevState.targetTestingAccountId,
25
26
  // @ts-expect-error TODO: reconcile types between CLI and UIE Dev Server
26
27
  projectConfig: this.localDevState.projectConfig,
27
28
  requestPorts,
29
+ appId,
28
30
  });
29
31
  }
30
32
  async fileChange(filePath, event) {
@@ -1,7 +1,7 @@
1
1
  import { type IntermediateRepresentationNode, type IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/translate';
2
2
  import { Build } from '@hubspot/local-dev-lib/types/Build';
3
3
  import { Project } from '@hubspot/local-dev-lib/types/Project';
4
- import { ProjectConfig } from '../../../../types/Projects.js';
4
+ import { LocallyChangedComponents, ProjectConfig } from '../../../../types/Projects.js';
5
5
  import { ExitFunction } from '../../../../types/Yargs.js';
6
6
  export declare function createNewProjectForLocalDev(projectConfig: ProjectConfig, targetAccountId: number, shouldCreateWithoutConfirmation: boolean, hasPublicApps: boolean, exit: ExitFunction): Promise<Project>;
7
7
  export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number, exit: ExitFunction, sendIR?: boolean, profile?: string): Promise<Build>;
@@ -11,7 +11,8 @@ export declare function compareLocalProjectToDeployed(projectConfig: ProjectConf
11
11
  export declare function getDeployedProjectNodes(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, profile?: string): Promise<{
12
12
  [key: string]: IntermediateRepresentationNode;
13
13
  }>;
14
- export declare function isDeployedProjectUpToDateWithLocal(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, localProjectNodes: {
14
+ export declare function hasLocalComponentChanges(changed: LocallyChangedComponents): boolean;
15
+ export declare function getLocallyChangedComponents(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, localProjectNodes: {
15
16
  [key: string]: IntermediateRepresentationNodeLocalDev;
16
- }, profile?: string): Promise<boolean>;
17
+ }, profile?: string): Promise<LocallyChangedComponents>;
17
18
  export declare function checkAndInstallDependencies(): Promise<void>;
@@ -1,25 +1,24 @@
1
1
  import fs from 'fs-extra';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
- import { createProject } from '@hubspot/local-dev-lib/api/projects';
5
- import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
4
+ import { createProject, downloadProject, } from '@hubspot/local-dev-lib/api/projects';
6
5
  import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
7
6
  import { sanitizeFileName } from '@hubspot/local-dev-lib/path';
8
7
  import { isDeepEqual } from '@hubspot/local-dev-lib/isDeepEqual';
9
8
  import { translate, } from '@hubspot/project-parsing-lib/translate';
9
+ import { AUTO_GENERATED_COMPONENT_TYPES } from '@hubspot/project-parsing-lib/constants';
10
+ import { mapToUserFacingType } from '@hubspot/project-parsing-lib/transform';
10
11
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
11
- import { PROJECT_ERROR_TYPES, PROJECT_BUILD_TEXT, PROJECT_DEPLOY_TEXT, PROJECT_CONFIG_FILE, } from '../../../constants.js';
12
+ import { PROJECT_BUILD_TEXT, PROJECT_CONFIG_FILE, PROJECT_DEPLOY_TEXT, PROJECT_ERROR_TYPES, } from '../../../constants.js';
12
13
  import { lib } from '../../../../lang/en.js';
13
14
  import { uiLogger } from '../../../ui/logger.js';
14
- import { uiLine } from '../../../ui/index.js';
15
+ import { uiAccountDescription, uiLine } from '../../../ui/index.js';
15
16
  import { confirmPrompt } from '../../../prompts/promptUtils.js';
16
- import { uiAccountDescription } from '../../../ui/index.js';
17
17
  import SpinniesManager from '../../../ui/SpinniesManager.js';
18
18
  import { EXIT_CODES } from '../../../enums/exitCodes.js';
19
19
  import { handleProjectUpload } from '../../upload.js';
20
20
  import { pollProjectBuildAndDeploy } from '../../pollProjectBuildAndDeploy.js';
21
- import { debugError, logError } from '../../../errorHandlers/index.js';
22
- import { ApiErrorContext } from '../../../errorHandlers/index.js';
21
+ import { ApiErrorContext, debugError, logError, } from '../../../errorHandlers/index.js';
23
22
  import { getProjectPackageJsonLocations, hasMissingPackages, installPackages, } from '../../../dependencyManagement.js';
24
23
  // Prompt the user to create a new project if one doesn't exist on their target account
25
24
  export async function createNewProjectForLocalDev(projectConfig, targetAccountId, shouldCreateWithoutConfirmation, hasPublicApps, exit) {
@@ -133,8 +132,8 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
133
132
  SpinniesManager.add('compareLocalProjectToDeployed', {
134
133
  text: lib.localDevHelpers.project.compareLocalProjectToDeployed.checking,
135
134
  });
136
- const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile);
137
- if (isUpToDate) {
135
+ const changedComponents = await getLocallyChangedComponents(projectConfig, accountId, deployedBuildId, localProjectNodes, profile);
136
+ if (!hasLocalComponentChanges(changedComponents)) {
138
137
  SpinniesManager.succeed('compareLocalProjectToDeployed', {
139
138
  text: lib.localDevHelpers.project.compareLocalProjectToDeployed.upToDate,
140
139
  });
@@ -145,6 +144,8 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
145
144
  .notUpToDate,
146
145
  });
147
146
  uiLogger.log('');
147
+ uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed.changedFiles(changedComponents));
148
+ uiLogger.log('');
148
149
  uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed.notUpToDateExplanation(profile));
149
150
  return exit(EXIT_CODES.SUCCESS);
150
151
  }
@@ -185,14 +186,47 @@ export async function getDeployedProjectNodes(projectConfig, accountId, deployed
185
186
  }
186
187
  }
187
188
  }
188
- export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
189
+ function filterAutoGeneratedNodes(nodes) {
190
+ return Object.fromEntries(Object.entries(nodes).filter(([, node]) => !AUTO_GENERATED_COMPONENT_TYPES.includes(mapToUserFacingType(node.componentType))));
191
+ }
192
+ export function hasLocalComponentChanges(changed) {
193
+ return (changed.added.length > 0 ||
194
+ changed.updated.length > 0 ||
195
+ changed.removed.length > 0);
196
+ }
197
+ export async function getLocallyChangedComponents(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
198
+ const result = {
199
+ added: [],
200
+ updated: [],
201
+ removed: [],
202
+ };
189
203
  try {
190
204
  const deployedProjectNodes = await getDeployedProjectNodes(projectConfig, accountId, deployedBuildId, profile);
191
- return isDeepEqual(localProjectNodes, deployedProjectNodes, ['localDev']);
205
+ const filteredLocal = filterAutoGeneratedNodes(localProjectNodes);
206
+ const filteredDeployed = filterAutoGeneratedNodes(deployedProjectNodes);
207
+ const allUids = new Set([
208
+ ...Object.keys(filteredLocal),
209
+ ...Object.keys(filteredDeployed),
210
+ ]);
211
+ for (const uid of allUids) {
212
+ const local = filteredLocal[uid];
213
+ const deployed = filteredDeployed[uid];
214
+ if (!local) {
215
+ result.removed.push(path.join(projectConfig.srcDir, deployed.metaFilePath));
216
+ }
217
+ else if (!deployed) {
218
+ result.added.push(path.join(projectConfig.srcDir, local.metaFilePath));
219
+ }
220
+ else if (!isDeepEqual(local, deployed, ['localDev', 'componentDeps'])) {
221
+ result.updated.push(path.join(projectConfig.srcDir, local.metaFilePath));
222
+ }
223
+ }
224
+ return result;
192
225
  }
193
226
  catch (err) {
194
227
  debugError(err);
195
- return false;
228
+ uiLogger.warn(lib.localDevHelpers.project.compareLocalProjectToDeployed.unableToCompare);
229
+ return result;
196
230
  }
197
231
  }
198
232
  export async function checkAndInstallDependencies() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "8.9.0-beta.1",
3
+ "version": "8.9.1-beta.0",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
@@ -10,10 +10,10 @@
10
10
  "!**/__tests__/**"
11
11
  ],
12
12
  "dependencies": {
13
- "@hubspot/local-dev-lib": "5.8.0",
13
+ "@hubspot/local-dev-lib": "5.8.1",
14
14
  "@hubspot/project-parsing-lib": "0.17.0",
15
15
  "@hubspot/serverless-dev-runtime": "7.0.7",
16
- "@hubspot/ui-extensions-dev-server": "2.0.8",
16
+ "@hubspot/ui-extensions-dev-server": "2.0.9",
17
17
  "@inquirer/prompts": "7.1.0",
18
18
  "@modelcontextprotocol/sdk": "1.29.0",
19
19
  "archiver": "7.0.1",
@@ -147,3 +147,8 @@ export type Component<T = GenericComponentConfig> = {
147
147
  runnable: boolean;
148
148
  path: string;
149
149
  };
150
+ export type LocallyChangedComponents = {
151
+ added: string[];
152
+ updated: string[];
153
+ removed: string[];
154
+ };