@hubspot/cli 5.3.1 → 5.4.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/bin/cli.js +24 -5
- package/commands/__tests__/projects.test.js +105 -0
- package/commands/accounts/clean.js +1 -1
- package/commands/cms/convertFields.js +13 -7
- package/commands/project/__tests__/deploy.test.js +1 -1
- package/commands/project/__tests__/installDeps.test.js +168 -0
- package/commands/project/__tests__/logs.test.js +305 -0
- package/commands/project/add.js +24 -12
- package/commands/project/cloneApp.js +13 -21
- package/commands/project/deploy.js +4 -1
- package/commands/project/dev.js +22 -11
- package/commands/project/download.js +6 -3
- package/commands/project/installDeps.js +78 -0
- package/commands/project/logs.js +80 -242
- package/commands/project/migrateApp.js +8 -9
- package/commands/project/upload.js +5 -3
- package/commands/project/watch.js +3 -9
- package/commands/project.js +2 -0
- package/commands/sandbox/create.js +1 -0
- package/commands/sandbox.js +0 -2
- package/lang/en.lyaml +40 -75
- package/lib/LocalDevManager.js +1 -22
- package/lib/__tests__/dependencyManagement.test.js +245 -0
- package/lib/__tests__/projectLogsManager.test.js +210 -0
- package/lib/dependencyManagement.js +157 -0
- package/lib/errorHandlers/apiErrors.js +1 -3
- package/lib/errorHandlers/overrideErrors.js +57 -36
- package/lib/localDev.js +25 -16
- package/lib/projectLogsManager.js +144 -0
- package/lib/projects.js +17 -7
- package/lib/projectsWatch.js +2 -5
- package/lib/prompts/__tests__/projectsLogsPrompt.test.js +46 -0
- package/lib/prompts/createProjectPrompt.js +4 -0
- package/lib/prompts/projectAddPrompt.js +4 -21
- package/lib/prompts/projectDevTargetAccountPrompt.js +16 -25
- package/lib/prompts/projectsLogsPrompt.js +17 -108
- package/lib/sandboxSync.js +13 -15
- package/package.json +6 -6
- package/commands/sandbox/sync.js +0 -225
package/lang/en.lyaml
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
en:
|
|
2
2
|
commands:
|
|
3
3
|
generalErrors:
|
|
4
|
+
updateNotify:
|
|
5
|
+
notifyTitle: "Update available"
|
|
6
|
+
cmsUpdateNotification: "{{#bold}}The CMS CLI is now the HubSpot CLI{{/bold}}\n\nTo upgrade, uninstall {{#bold}}{{ packageName }}{{/bold}}\nand then run {{ updateCommand }}"
|
|
7
|
+
cliUpdateNotification: "HubSpot CLI version {{#cyan}}{{#bold}}{currentVersion}{{/bold}}{{/cyan}} is outdated.\nRun {{ updateCommand }} to upgrade to version {{#cyan}}{{#bold}}{latestVersion}{{/bold}}{{/cyan}}"
|
|
4
8
|
srcIsProject: "\"{{ src }}\" is in a project folder. Did you mean \"hs project {{command}}\"?"
|
|
5
9
|
setDefaultAccountMoved: "This command has moved. Try `hs accounts use` instead"
|
|
6
10
|
accounts:
|
|
@@ -479,6 +483,7 @@ en:
|
|
|
479
483
|
errors:
|
|
480
484
|
noProjectConfig: "No project detected. Please run this command again from a project directory."
|
|
481
485
|
invalidProjectComponents: "Projects cannot contain both private and public apps. Move your apps to separate projects before attempting local development."
|
|
486
|
+
noRunnableComponents: "No supported components were found in this project. Run {{ command }} to see a list of available components and add one to your project."
|
|
482
487
|
parentAccountNotConfigured: "To develop this project locally, run {{ authCommand }} to authenticate the App Developer Account {{ accountId }} associated with {{ accountIdentifier }}."
|
|
483
488
|
examples:
|
|
484
489
|
default: "Start local dev for the current project"
|
|
@@ -550,9 +555,9 @@ en:
|
|
|
550
555
|
describe: "Create a new component within a project"
|
|
551
556
|
options:
|
|
552
557
|
name:
|
|
553
|
-
describe: "
|
|
558
|
+
describe: "The name for your newly created component"
|
|
554
559
|
type:
|
|
555
|
-
describe: "The type
|
|
560
|
+
describe: "The path to the component type's location within the hubspot-project-components Github repo: https://github.com/HubSpot/hubspot-project-components"
|
|
556
561
|
creatingComponent:
|
|
557
562
|
message: "Adding a new component to your project"
|
|
558
563
|
success:
|
|
@@ -561,6 +566,7 @@ en:
|
|
|
561
566
|
locationInProject: "The component location must be within a project folder"
|
|
562
567
|
examples:
|
|
563
568
|
default: "Create a component within your project"
|
|
569
|
+
withFlags: "Use --name and --type flags to bypass the prompt."
|
|
564
570
|
deploy:
|
|
565
571
|
describe: "Deploy a project build"
|
|
566
572
|
debug:
|
|
@@ -586,16 +592,18 @@ en:
|
|
|
586
592
|
logs:
|
|
587
593
|
describe: "Get execution logs for a serverless function within a project"
|
|
588
594
|
errors:
|
|
589
|
-
|
|
595
|
+
noProjectConfig: "No project detected. Run this command again from a project directory."
|
|
596
|
+
failedToFetchProjectDetails: "There was an error fetching project details"
|
|
597
|
+
noFunctionsLinkText: "Visit developer docs"
|
|
598
|
+
noFunctionsInProject: "There aren't any functions in this project\n\t- Run `{{#orange}}hs project logs --help{{/orange}}` to learn more about logs\n\t- {{link}} to learn more about serverless functions"
|
|
599
|
+
noFunctionWithName: "No function with name \"{{ name }}\""
|
|
600
|
+
functionNotDeployed: "The function with name \"{{ name }}\" is not deployed"
|
|
590
601
|
logs:
|
|
591
602
|
showingLogs: "Showing logs for:"
|
|
592
|
-
|
|
593
|
-
hubspotLogsDirectLink: "View logs in HubSpot"
|
|
603
|
+
hubspotLogsDirectLink: "View function logs in HubSpot"
|
|
594
604
|
noLogsFound: "No logs were found for \"{{ name }}\""
|
|
595
605
|
table:
|
|
596
606
|
accountHeader: "Account"
|
|
597
|
-
projectHeader: "Project"
|
|
598
|
-
appHeader: "App"
|
|
599
607
|
functionHeader: "Function"
|
|
600
608
|
endpointHeader: "Endpoint"
|
|
601
609
|
examples:
|
|
@@ -612,12 +620,8 @@ en:
|
|
|
612
620
|
describe: "Retrieve most recent log only"
|
|
613
621
|
limit:
|
|
614
622
|
describe: "Limit the number of logs to output"
|
|
615
|
-
project:
|
|
616
|
-
describe: "Project name"
|
|
617
623
|
function:
|
|
618
624
|
describe: "App function name"
|
|
619
|
-
endpoint:
|
|
620
|
-
describe: "Public endpoint path"
|
|
621
625
|
upload:
|
|
622
626
|
describe: "Upload your project files and create a new build"
|
|
623
627
|
examples:
|
|
@@ -703,6 +707,20 @@ en:
|
|
|
703
707
|
describe: "Open Github issues in your browser to report a bug."
|
|
704
708
|
general:
|
|
705
709
|
describe: "Open Github issues in your browser to give feedback."
|
|
710
|
+
installDeps:
|
|
711
|
+
help:
|
|
712
|
+
describe: "Install the dependencies for your project, or add a dependency to a subcomponent of a project"
|
|
713
|
+
installAppDepsExample: "Install the dependencies for the project"
|
|
714
|
+
addDepToSubComponentExample: "Install the dependencies to one or more project subcomponents"
|
|
715
|
+
installLocationPrompt: "Choose the project components to install the dependencies:"
|
|
716
|
+
installLocationPromptRequired: "You must choose at least one subcomponent"
|
|
717
|
+
installingDependencies: "Installing dependencies in {{directory}}"
|
|
718
|
+
installationSuccessful: "Installed dependencies in {{directory}}"
|
|
719
|
+
addingDependenciesToLocation: "Installing {{dependencies}} in {{directory}}"
|
|
720
|
+
installingDependenciesFailed: "Installing dependencies for {{directory}} failed"
|
|
721
|
+
noProjectConfig: "No project detected. Run this command from a project directory."
|
|
722
|
+
noPackageJsonInProject: "No dependencies to install. The project {{ projectName }} folder might be missing component or subcomponent files. {{ link }}"
|
|
723
|
+
packageManagerNotInstalled: "This command depends on {{ packageManager }}, install {{#bold}}{{ link }}{{/bold}}"
|
|
706
724
|
remove:
|
|
707
725
|
describe: "Delete a file or folder from HubSpot."
|
|
708
726
|
deleted: "Deleted \"{{ path }}\" from account {{ accountId }}"
|
|
@@ -760,30 +778,6 @@ en:
|
|
|
760
778
|
options:
|
|
761
779
|
account:
|
|
762
780
|
describe: "Account name or id to delete"
|
|
763
|
-
sync:
|
|
764
|
-
describe: "Sync to a sandbox account"
|
|
765
|
-
examples:
|
|
766
|
-
default: "Initiates a sync to a sandbox account."
|
|
767
|
-
force: "Skips all confirmation prompts when initiating a sync."
|
|
768
|
-
info:
|
|
769
|
-
developmentSandbox: "This will sync CRM object definitions."
|
|
770
|
-
standardSandbox: "This will sync all supported assets.
|
|
771
|
-
\nTo sync only specific assets, follow this link: {{#bold}}{{ url }}{{/bold}}"
|
|
772
|
-
sync: "\nSync direction:
|
|
773
|
-
\n- Target sandbox: {{#cyan}}{{#bold}}{{ sandboxName }}{{/bold}}{{/cyan}}
|
|
774
|
-
\n- Source account: {{#bold}}{{ parentAccountName }}{{/bold}}
|
|
775
|
-
\n\nRun {{#bold}}hs accounts use{{/bold}} to change your default account and sync to a different target sandbox."
|
|
776
|
-
warning:
|
|
777
|
-
developmentSandbox: "Syncing will update previously synced object definitions and add new ones from production to your development sandbox. Object definitions that were created in your sandbox will stay the same."
|
|
778
|
-
standardSandbox: "Syncing can have a big impact. Updates from your production account may overwrite changes in your standard sandbox. Standard sandboxes are usually shared with other Super Admins."
|
|
779
|
-
confirm:
|
|
780
|
-
developmentSandbox: "Sync CRM object definitions to {{#cyan}}{{#bold}}{{ sandboxName }}{{/bold}}{{/cyan}} from {{#bold}}{{ parentAccountName }}{{/bold}}?"
|
|
781
|
-
standardSandbox: "Sync all supported assets to {{#cyan}}{{#bold}}{{ sandboxName }}{{/bold}}{{/cyan}} from {{#bold}}{{ parentAccountName }}{{/bold}}?"
|
|
782
|
-
failure:
|
|
783
|
-
invalidAccountType: "Sync must be run in a sandbox account. Your default account is a {{ accountType }}. Run {{#bold}}hs auth{{/bold}} to connect your sandbox account to the CLI or {{#bold}}hs accounts use{{/bold}} to change your default account, then try again."
|
|
784
|
-
missingParentPortal: "The production account associated to {{#bold}}{{ sandboxName }}{{/bold}} is not connected to your HubSpot CLI.
|
|
785
|
-
\n- Run {{#bold}}hs auth{{/bold}} to connect that account to your terminal, then try again.
|
|
786
|
-
\n- Run {{#bold}}hs accounts use{{/bold}} to change your default account, if you want to sync to a different sandbox. Then try again.\n"
|
|
787
781
|
secrets:
|
|
788
782
|
describe: "Manage HubSpot secrets."
|
|
789
783
|
subcommands:
|
|
@@ -964,6 +958,10 @@ en:
|
|
|
964
958
|
options:
|
|
965
959
|
options:
|
|
966
960
|
describe: "Options to pass to javascript fields files"
|
|
961
|
+
errors:
|
|
962
|
+
invalidPath: "The path \"{{ path }}\" specified in the \"--src\" flag is not a path to a file or directory"
|
|
963
|
+
missingSrc: "Please specify the path to your javascript fields file or directory with the --src flag."
|
|
964
|
+
|
|
967
965
|
lib:
|
|
968
966
|
process:
|
|
969
967
|
exitDebug: "Attempting to gracefully exit. Triggered by {{ signal }}"
|
|
@@ -975,7 +973,6 @@ en:
|
|
|
975
973
|
failedToInitialize: "Missing required arguments to initialize Local Dev"
|
|
976
974
|
noDeployedBuild: "Your project {{#bold}}{{ projectName }}{{/bold}} exists in {{ accountIdentifier }}, but has no deployed build. Projects must be successfully deployed to be developed locally. Address any build and deploy errors your project may have, then run {{ uploadCommand }} to upload and deploy your project."
|
|
977
975
|
noComponents: "There are no components in this project."
|
|
978
|
-
noRunnableComponents: "No supported components were found under {{#bold}}{{ projectSourceDir }}{{/bold}}. Run {{ command }} to see a list of available components."
|
|
979
976
|
betaMessage: "HubSpot projects local development"
|
|
980
977
|
learnMoreLocalDevServer: "Learn more about the projects local dev server"
|
|
981
978
|
running: "Running {{#bold}}{{ projectName }}{{/bold}} locally on {{ accountIdentifier }}, waiting for changes ..."
|
|
@@ -1009,12 +1006,12 @@ en:
|
|
|
1009
1006
|
localDev:
|
|
1010
1007
|
confirmDefaultAccountIsTarget:
|
|
1011
1008
|
declineDefaultAccountExplanation: "To develop on a different account, run {{ useCommand }} to change your default account, then re-run {{ devCommand }}."
|
|
1012
|
-
checkIfAppDevloperAccount: "This project contains a public app. Local development of public apps is only supported on developer accounts and developer test accounts. Change your default account using {{
|
|
1013
|
-
|
|
1014
|
-
|
|
1009
|
+
checkIfAppDevloperAccount: "This project contains a public app. Local development of public apps is only supported on developer accounts and developer test accounts. Change your default account using {{ useCommand }}, or link a new account with {{ authCommand }}."
|
|
1010
|
+
validateAccountOption:
|
|
1011
|
+
invalidPublicAppAccount: "This project contains a public app. The \"--account\" flag must point to a developer test account to develop this project locally. Alternatively, change your default account to an App Developer Account using {{ useCommand }} and run {{ devCommand }} to set up a new Developer Test Account."
|
|
1012
|
+
invalidPrivateAppAccount: "This project contains a private app. The account specified with the \"--account\" flag points to a developer account, which do not support the local development of private apps. Update the \"--account\" flag to point to a standard, sandbox, or developer test account, or change your default account by running {{ useCommand }}."
|
|
1015
1013
|
nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
|
|
1016
1014
|
publicAppNonDeveloperTestAccountWarning: "Local development of public apps is only supported in {{#bold}}developer test accounts{{/bold}}."
|
|
1017
|
-
privateAppInAppDeveloperAccountError: "Local development of private apps is not supported in {{#bold}}app developer accounts{{/bold}}"
|
|
1018
1015
|
createNewProjectForLocalDev:
|
|
1019
1016
|
projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
|
|
1020
1017
|
publicAppProjectMustExistExplanation: "The project {{ projectName }} does not exist in {{ accountIdentifier}}, the app developer account associated with your target account. This command requires the project to exist in this app developer account."
|
|
@@ -1110,12 +1107,6 @@ en:
|
|
|
1110
1107
|
projectDevCommand:
|
|
1111
1108
|
command: "hs project dev"
|
|
1112
1109
|
message: "Run {{ command }} to set up your test environment and start local development"
|
|
1113
|
-
sandboxSyncDevelopmentCommand:
|
|
1114
|
-
command: "hs sandbox sync"
|
|
1115
|
-
message: "Run {{ command }} to to update CRM object definitions in your sandbox"
|
|
1116
|
-
sandboxSyncStandardCommand:
|
|
1117
|
-
command: "hs sandbox sync"
|
|
1118
|
-
message: "Run {{ command }} to to update all supported assets to your sandbox from production"
|
|
1119
1110
|
sampleProjects:
|
|
1120
1111
|
linkText: "HubSpot's sample projects"
|
|
1121
1112
|
url: "https://developers.hubspot.com/docs/platform/sample-projects?utm_source=cli&utm_content=project_create_whats_next"
|
|
@@ -1160,16 +1151,7 @@ en:
|
|
|
1160
1151
|
confirmDefaultAccount: "Continue testing on {{#bold}}{{ accountName }} ({{ accountType }}){{/bold}}? (Y/n)"
|
|
1161
1152
|
confirmUseExistingDeveloperTestAccount: "Continue with {{ accountName }}? This account isn't currently connected to the HubSpot CLI. By continuing, you'll be prompted to generate a personal access key and connect it."
|
|
1162
1153
|
projectLogsPrompt:
|
|
1163
|
-
projectName
|
|
1164
|
-
message: "[--project] Enter the project name:"
|
|
1165
|
-
error: "Project name is required"
|
|
1166
|
-
logType:
|
|
1167
|
-
message: "Select the type of serverless function"
|
|
1168
|
-
function: "App function"
|
|
1169
|
-
endpoint: "Public endpoint"
|
|
1170
|
-
appName: "[--app] Select the app"
|
|
1171
|
-
functionName: "[--function] Enter the app function name:"
|
|
1172
|
-
endpointName: "[--endpoint] Enter the public endpoint path:"
|
|
1154
|
+
functionName: "[--function] Select function in {{#bold}}{{projectName}}{{/bold}} project"
|
|
1173
1155
|
setAsDefaultAccountPrompt:
|
|
1174
1156
|
setAsDefaultAccountMessage: "Set this account as the default?"
|
|
1175
1157
|
setAsDefaultAccount: "Account \"{{ accountName }}\" set as the default account"
|
|
@@ -1411,8 +1393,7 @@ en:
|
|
|
1411
1393
|
successDevSbInfo: "Initiated sync of object definitions from production to {{ accountName }}. It may take some time. {{ url }}"
|
|
1412
1394
|
failure:
|
|
1413
1395
|
invalidUser: "Couldn't sync {{ accountName }} because your account has been removed from {{ parentAccountName }} or your permission set doesn't allow you to sync the sandbox. To update your permissions, contact a super admin in {{ parentAccountName }}."
|
|
1414
|
-
|
|
1415
|
-
syncInProgress: "Couldn’t run the sync because there’s another sync in progress. Wait for the current sync to finish and then try again. To check the sync status, visit the sync activity log: {{ url }}."
|
|
1396
|
+
syncInProgress: "Couldn't run the sync because there's another sync in progress. Wait for the current sync to finish and then try again. To check the sync status, visit the sync activity log: {{ url }}."
|
|
1416
1397
|
notSuperAdmin: "Couldn't run the sync because you are not a super admin in {{ account }}. Ask the account owner for super admin access to the sandbox."
|
|
1417
1398
|
objectNotFound: "Couldn't sync the sandbox because {{#bold}}{{ account }}{{/bold}} may have been deleted through the UI. Run {{#bold}}hs sandbox delete{{/bold}} to remove this account from the config. "
|
|
1418
1399
|
errorHandlers:
|
|
@@ -1427,21 +1408,6 @@ en:
|
|
|
1427
1408
|
errorOccurred: "An error occurred while {{ fileAction }} {{ filepath }}."
|
|
1428
1409
|
errorExplanation: "This is the result of a system error: {{ errorMessage }}"
|
|
1429
1410
|
apiErrors:
|
|
1430
|
-
messageDetail: "{{ request }} in account {{ accountId }}"
|
|
1431
|
-
unableToUpload: 'Unable to upload "{{ payload }}.'
|
|
1432
|
-
codes:
|
|
1433
|
-
400: "The {{ messageDetail }} was bad."
|
|
1434
|
-
401: "The {{ messageDetail }} was unauthorized."
|
|
1435
|
-
403MissingScope: "Couldn't run the project command because there are scopes missing in your production account. To update scopes, deactivate your current personal access key for {{ accountId }}, and generate a new one. Then run `hs auth` to update the CLI with the new key."
|
|
1436
|
-
403Gating: "The current target account {{ accountId }} does not have access to HubSpot projects. To opt in to the CRM Development Beta and use projects, visit https://app.hubspot.com/l/product-updates/in-beta?update=13899236."
|
|
1437
|
-
403: "The {{ messageDetail }} was forbidden."
|
|
1438
|
-
404Request: 'The {{ action }} failed because "{{ request }}" was not found in account {{ accountId }}.'
|
|
1439
|
-
404: "The {{ messageDetail }} was not found."
|
|
1440
|
-
429: "The {{ messageDetail }} surpassed the rate limit. Retry in one minute."
|
|
1441
|
-
503: "The {{ messageDetail }} could not be handled at this time. Please try again or visit https://help.hubspot.com/ to submit a ticket or contact HubSpot Support if the issue persists."
|
|
1442
|
-
500Generic: "The {{ messageDetail }} failed due to a server error. Please try again or visit https://help.hubspot.com/ to submit a ticket or contact HubSpot Support if the issue persists."
|
|
1443
|
-
400Generic: "The {{ messageDetail }} failed due to a client error."
|
|
1444
|
-
generic: "The {{ messageDetail }} failed."
|
|
1445
1411
|
verifyAccessKeyAndUserAccess:
|
|
1446
1412
|
fetchScopeDataError: "Error verifying access of scopeGroup {{ scopeGroup }}: {{ error }}"
|
|
1447
1413
|
portalMissingScope: "Your account does not have access to this action. Talk to an account admin to request it."
|
|
@@ -1456,5 +1422,4 @@ en:
|
|
|
1456
1422
|
updateProject: "Please update your project to the latest version and try again."
|
|
1457
1423
|
docsLink: "Projects platform versioning (BETA)"
|
|
1458
1424
|
betaLink: "For more info, see {{ docsLink }}."
|
|
1459
|
-
|
|
1460
|
-
|
|
1425
|
+
missingScopeError: "Couldn't execute the {{ request }} because the access key for {{ accountName }} is missing required scopes. To update scopes, run {{ authCommand }}. Then deactivate the existing key and generate a new one that includes the missing scopes."
|
package/lib/LocalDevManager.js
CHANGED
|
@@ -63,7 +63,7 @@ class LocalDevManager {
|
|
|
63
63
|
this.isGithubLinked = options.isGithubLinked;
|
|
64
64
|
this.watcher = null;
|
|
65
65
|
this.uploadWarnings = {};
|
|
66
|
-
this.runnableComponents =
|
|
66
|
+
this.runnableComponents = options.runnableComponents;
|
|
67
67
|
this.activeApp = null;
|
|
68
68
|
this.activePublicAppData = null;
|
|
69
69
|
this.env = options.env;
|
|
@@ -78,27 +78,6 @@ class LocalDevManager {
|
|
|
78
78
|
logger.log(i18n(`${i18nKey}.failedToInitialize`));
|
|
79
79
|
process.exit(EXIT_CODES.ERROR);
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
// The project is empty, there is nothing to run locally
|
|
83
|
-
if (!options.components.length) {
|
|
84
|
-
logger.error(i18n(`${i18nKey}.noComponents`));
|
|
85
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// The project does not contain any components that support local development
|
|
89
|
-
if (!this.runnableComponents.length) {
|
|
90
|
-
logger.error(
|
|
91
|
-
i18n(`${i18nKey}.noRunnableComponents`, {
|
|
92
|
-
projectSourceDir: this.projectSourceDir,
|
|
93
|
-
command: uiCommandReference('hs project add'),
|
|
94
|
-
})
|
|
95
|
-
);
|
|
96
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
getRunnableComponents(components) {
|
|
101
|
-
return components.filter(component => component.runnable);
|
|
102
81
|
}
|
|
103
82
|
|
|
104
83
|
async setActiveApp(appUid) {
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
jest.mock('../projects');
|
|
2
|
+
jest.mock('@hubspot/local-dev-lib/logger');
|
|
3
|
+
jest.mock('@hubspot/local-dev-lib/fs');
|
|
4
|
+
jest.mock('../ui/SpinniesManager');
|
|
5
|
+
jest.mock('fs', () => ({
|
|
6
|
+
...jest.requireActual('fs'),
|
|
7
|
+
existsSync: jest.fn().mockReturnValue(true),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const util = require('util');
|
|
11
|
+
const {
|
|
12
|
+
isGloballyInstalled,
|
|
13
|
+
installPackages,
|
|
14
|
+
getProjectPackageJsonLocations,
|
|
15
|
+
} = require('../dependencyManagement');
|
|
16
|
+
const { walk } = require('@hubspot/local-dev-lib/fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { getProjectConfig } = require('../projects');
|
|
19
|
+
const SpinniesManager = require('../ui/SpinniesManager');
|
|
20
|
+
const { existsSync } = require('fs');
|
|
21
|
+
|
|
22
|
+
describe('cli/lib/dependencyManagement', () => {
|
|
23
|
+
let execMock;
|
|
24
|
+
|
|
25
|
+
const projectDir = path.join('path', 'to', 'project');
|
|
26
|
+
const srcDir = 'src';
|
|
27
|
+
const appDir = path.join(projectDir, srcDir, 'app');
|
|
28
|
+
const appFunctionsDir = path.join(appDir, 'app.functions');
|
|
29
|
+
const extensionsDir = path.join(appDir, 'exensions');
|
|
30
|
+
const projectName = 'super cool test project';
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
execMock = jest.fn();
|
|
34
|
+
util.promisify = jest.fn().mockReturnValue(execMock);
|
|
35
|
+
getProjectConfig.mockResolvedValue({
|
|
36
|
+
projectDir,
|
|
37
|
+
projectConfig: {
|
|
38
|
+
srcDir,
|
|
39
|
+
name: projectName,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('isGloballyInstalled', () => {
|
|
45
|
+
it('should return true when exec is successful', async () => {
|
|
46
|
+
const actual = await isGloballyInstalled('npm');
|
|
47
|
+
expect(actual).toBe(true);
|
|
48
|
+
expect(execMock).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(execMock).toHaveBeenCalledWith('npm --version');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return false when exec is unsuccessful', async () => {
|
|
53
|
+
execMock = jest.fn().mockImplementationOnce(() => {
|
|
54
|
+
throw new Error('unsuccessful');
|
|
55
|
+
});
|
|
56
|
+
util.promisify = jest.fn().mockReturnValueOnce(execMock);
|
|
57
|
+
const actual = await isGloballyInstalled('npm');
|
|
58
|
+
expect(actual).toBe(false);
|
|
59
|
+
expect(execMock).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(execMock).toHaveBeenCalledWith('npm --version');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('installPackages', () => {
|
|
65
|
+
it('should setup a loading spinner', async () => {
|
|
66
|
+
const packages = ['package1', 'package2'];
|
|
67
|
+
const installLocations = ['src/app/app.functions', 'src/app/extensions'];
|
|
68
|
+
await installPackages({ packages, installLocations });
|
|
69
|
+
expect(SpinniesManager.init).toHaveBeenCalledTimes(
|
|
70
|
+
installLocations.length
|
|
71
|
+
);
|
|
72
|
+
expect(SpinniesManager.add).toHaveBeenCalledTimes(
|
|
73
|
+
installLocations.length
|
|
74
|
+
);
|
|
75
|
+
expect(SpinniesManager.succeed).toHaveBeenCalledTimes(
|
|
76
|
+
installLocations.length
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should install the provided packages in all the provided install locations', async () => {
|
|
81
|
+
const packages = ['package1', 'package2'];
|
|
82
|
+
const installLocations = ['src/app/app.functions', 'src/app/extensions'];
|
|
83
|
+
await installPackages({ packages, installLocations });
|
|
84
|
+
|
|
85
|
+
expect(execMock).toHaveBeenCalledTimes(installLocations.length);
|
|
86
|
+
expect(SpinniesManager.add).toHaveBeenCalledTimes(
|
|
87
|
+
installLocations.length
|
|
88
|
+
);
|
|
89
|
+
expect(SpinniesManager.succeed).toHaveBeenCalledTimes(
|
|
90
|
+
installLocations.length
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
for (const location of installLocations) {
|
|
94
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
95
|
+
`npm --prefix=${location} install package1 package2`
|
|
96
|
+
);
|
|
97
|
+
expect(SpinniesManager.add).toHaveBeenCalledWith(
|
|
98
|
+
`installingDependencies-${location}`,
|
|
99
|
+
{
|
|
100
|
+
text: `Installing [package1, package2] in ${location}`,
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
expect(SpinniesManager.succeed).toHaveBeenCalledWith(
|
|
104
|
+
`installingDependencies-${location}`,
|
|
105
|
+
{
|
|
106
|
+
text: `Installed dependencies in ${location}`,
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should use the provided install locations', async () => {
|
|
113
|
+
const installLocations = ['src/app/app.functions', 'src/app/extensions'];
|
|
114
|
+
await installPackages({ installLocations });
|
|
115
|
+
expect(execMock).toHaveBeenCalledTimes(installLocations.length);
|
|
116
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
117
|
+
`npm --prefix=${installLocations[0]} install`
|
|
118
|
+
);
|
|
119
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
120
|
+
`npm --prefix=${installLocations[1]} install`
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should locate the projects package.json files when install locations is not provided', async () => {
|
|
125
|
+
const installLocations = [
|
|
126
|
+
path.join(appFunctionsDir, 'package.json'),
|
|
127
|
+
path.join(extensionsDir, 'package.json'),
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
walk.mockResolvedValue(installLocations);
|
|
131
|
+
|
|
132
|
+
getProjectConfig.mockResolvedValue({
|
|
133
|
+
projectDir,
|
|
134
|
+
projectConfig: {
|
|
135
|
+
srcDir,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await installPackages({});
|
|
140
|
+
// Its called once per each install location, plus once to check if npm installed
|
|
141
|
+
expect(execMock).toHaveBeenCalledTimes(installLocations.length + 1);
|
|
142
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
143
|
+
`npm --prefix=${appFunctionsDir} install`
|
|
144
|
+
);
|
|
145
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
146
|
+
`npm --prefix=${extensionsDir} install`
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should throw an error when installing the dependencies fails', async () => {
|
|
151
|
+
execMock = jest.fn().mockImplementation(command => {
|
|
152
|
+
if (command !== 'npm --version') {
|
|
153
|
+
throw new Error('OH NO');
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
util.promisify = jest.fn().mockReturnValue(execMock);
|
|
158
|
+
|
|
159
|
+
const installLocations = [
|
|
160
|
+
path.join(appFunctionsDir, 'package.json'),
|
|
161
|
+
path.join(extensionsDir, 'package.json'),
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
walk.mockResolvedValue(installLocations);
|
|
165
|
+
|
|
166
|
+
getProjectConfig.mockResolvedValue({
|
|
167
|
+
projectDir,
|
|
168
|
+
projectConfig: {
|
|
169
|
+
srcDir,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await expect(() => installPackages({})).rejects.toThrowError(
|
|
174
|
+
`Installing dependencies for ${appFunctionsDir} failed`
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
expect(SpinniesManager.fail).toHaveBeenCalledTimes(
|
|
178
|
+
installLocations.length
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(SpinniesManager.fail).toHaveBeenCalledWith(
|
|
182
|
+
`installingDependencies-${appFunctionsDir}`,
|
|
183
|
+
{
|
|
184
|
+
text: `Installing dependencies for ${appFunctionsDir} failed`,
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
expect(SpinniesManager.fail).toHaveBeenCalledWith(
|
|
188
|
+
`installingDependencies-${extensionsDir}`,
|
|
189
|
+
{
|
|
190
|
+
text: `Installing dependencies for ${extensionsDir} failed`,
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('getProjectPackageJsonFiles', () => {
|
|
197
|
+
it('should throw an error when ran outside the boundary of a project', async () => {
|
|
198
|
+
getProjectConfig.mockResolvedValue({});
|
|
199
|
+
await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError(
|
|
200
|
+
'No project detected. Run this command from a project directory.'
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should throw an error if npm is not globally installed', async () => {
|
|
205
|
+
execMock = jest.fn().mockImplementation(() => {
|
|
206
|
+
throw new Error('OH NO');
|
|
207
|
+
});
|
|
208
|
+
util.promisify = jest.fn().mockReturnValue(execMock);
|
|
209
|
+
await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError(
|
|
210
|
+
/This command depends on npm, install/
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should throw an error if the project directory does not exist', async () => {
|
|
215
|
+
existsSync.mockReturnValueOnce(false);
|
|
216
|
+
await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError(
|
|
217
|
+
`No dependencies to install. The project ${projectName} folder might be missing component or subcomponent files. Learn how to create a project from scratch.: https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project`
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should ignore package.json files in certain directories', async () => {
|
|
222
|
+
const nodeModulesDir = path.join(appDir, 'node_modules');
|
|
223
|
+
const viteDir = path.join(appDir, '.vite');
|
|
224
|
+
const installLocations = [
|
|
225
|
+
path.join(appFunctionsDir, 'package.json'),
|
|
226
|
+
path.join(extensionsDir, 'package.json'),
|
|
227
|
+
path.join(viteDir, 'package.json'),
|
|
228
|
+
path.join(nodeModulesDir, 'package.json'),
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
walk.mockResolvedValue(installLocations);
|
|
232
|
+
|
|
233
|
+
const actual = await getProjectPackageJsonLocations();
|
|
234
|
+
expect(actual).toEqual([appFunctionsDir, extensionsDir]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should throw an error if no package.json files are found', async () => {
|
|
238
|
+
walk.mockResolvedValue([]);
|
|
239
|
+
|
|
240
|
+
await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError(
|
|
241
|
+
`No dependencies to install. The project ${projectName} folder might be missing component or subcomponent files. Learn how to create a project from scratch.: https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project`
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|