diff --git a/packages/cli/lang/en.lyaml b/packages/cli/lang/en.lyaml index 42693fe2c..7542e89ca 100644 --- a/packages/cli/lang/en.lyaml +++ b/packages/cli/lang/en.lyaml @@ -719,7 +719,7 @@ en: addingDependenciesToLocation: "Installing {{dependencies}} in {{directory}}" installingDependenciesFailed: "Installing dependencies for {{directory}} failed" noProjectConfig: "No project detected. Run this command from a project directory." - noPackageJsonInProject: "Could not find any package.json files in the project" + noPackageJsonInProject: "No dependencies to install. The project {{ projectName }} folder might be missing component or subcomponent files. {{ link }}" packageManagerNotInstalled: "This command depends on {{ packageManager }}, install {{#bold}}{{ link }}{{/bold}}" remove: describe: "Delete a file or folder from HubSpot." diff --git a/packages/cli/lib/__tests__/dependencyManagement.test.js b/packages/cli/lib/__tests__/dependencyManagement.test.js index 7bbe60c37..f77b35d08 100644 --- a/packages/cli/lib/__tests__/dependencyManagement.test.js +++ b/packages/cli/lib/__tests__/dependencyManagement.test.js @@ -2,6 +2,10 @@ jest.mock('../projects'); jest.mock('@hubspot/local-dev-lib/logger'); jest.mock('@hubspot/local-dev-lib/fs'); jest.mock('../ui/SpinniesManager'); +jest.mock('fs', () => ({ + ...jest.requireActual('fs'), + existsSync: jest.fn().mockReturnValue(true), +})); const util = require('util'); const { @@ -9,10 +13,11 @@ const { installPackages, getProjectPackageJsonLocations, } = require('../dependencyManagement'); -const fs = require('@hubspot/local-dev-lib/fs'); +const { walk } = require('@hubspot/local-dev-lib/fs'); const path = require('path'); const { getProjectConfig } = require('../projects'); const SpinniesManager = require('../ui/SpinniesManager'); +const { existsSync } = require('fs'); describe('cli/lib/dependencyManagement', () => { let execMock; @@ -120,7 +125,7 @@ describe('cli/lib/dependencyManagement', () => { path.join(extensionsDir, 'package.json'), ]; - fs.walk.mockResolvedValue(installLocations); + walk.mockResolvedValue(installLocations); getProjectConfig.mockResolvedValue({ projectDir, @@ -154,7 +159,7 @@ describe('cli/lib/dependencyManagement', () => { path.join(extensionsDir, 'package.json'), ]; - fs.walk.mockResolvedValue(installLocations); + walk.mockResolvedValue(installLocations); getProjectConfig.mockResolvedValue({ projectDir, @@ -204,6 +209,13 @@ describe('cli/lib/dependencyManagement', () => { ); }); + it('should throw an error if the project directory does not exist', async () => { + existsSync.mockReturnValueOnce(false); + await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError( + 'No dependencies to install. The project 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' + ); + }); + it('should ignore package.json files in certain directories', async () => { const nodeModulesDir = path.join(appDir, 'node_modules'); const viteDir = path.join(appDir, '.vite'); @@ -214,17 +226,17 @@ describe('cli/lib/dependencyManagement', () => { path.join(nodeModulesDir, 'package.json'), ]; - fs.walk.mockResolvedValue(installLocations); + walk.mockResolvedValue(installLocations); const actual = await getProjectPackageJsonLocations(); expect(actual).toEqual([appFunctionsDir, extensionsDir]); }); it('should throw an error if no package.json files are found', async () => { - fs.walk.mockResolvedValue([]); + walk.mockResolvedValue([]); await expect(() => getProjectPackageJsonLocations()).rejects.toThrowError( - 'Could not find any package.json files in the project' + 'No dependencies to install. The project 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' ); }); }); diff --git a/packages/cli/lib/dependencyManagement.js b/packages/cli/lib/dependencyManagement.js index 4f89bd932..ee5dcb479 100644 --- a/packages/cli/lib/dependencyManagement.js +++ b/packages/cli/lib/dependencyManagement.js @@ -7,11 +7,26 @@ const { uiLink } = require('./ui'); const util = require('util'); const { i18n } = require('./lang'); const SpinniesManager = require('./ui/SpinniesManager'); +const fs = require('fs'); const DEFAULT_PACKAGE_MANAGER = 'npm'; const i18nKey = `commands.project.subcommands.installDeps`; +class NoPackageJsonFilesError extends Error { + constructor(projectName) { + super( + i18n(`${i18nKey}.noPackageJsonInProject`, { + projectName, + link: uiLink( + 'Learn how to create a project from scratch.', + 'https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project' + ), + }) + ); + } +} + async function isGloballyInstalled(command) { const exec = util.promisify(execAsync); try { @@ -92,7 +107,7 @@ async function getProjectPackageJsonLocations() { const { projectDir, - projectConfig: { srcDir }, + projectConfig: { srcDir, name }, } = projectConfig; if (!(await isGloballyInstalled(DEFAULT_PACKAGE_MANAGER))) { @@ -107,6 +122,13 @@ async function getProjectPackageJsonLocations() { ); } + if ( + !fs.existsSync(projectConfig.projectDir) || + !fs.existsSync(path.join(projectDir, srcDir)) + ) { + throw new NoPackageJsonFilesError(name); + } + const packageJsonFiles = (await walk(path.join(projectDir, srcDir))).filter( file => file.includes('package.json') && @@ -115,7 +137,7 @@ async function getProjectPackageJsonLocations() { ); if (packageJsonFiles.length === 0) { - throw new Error(i18n(`${i18nKey}.noPackageJsonInProject`)); + throw new NoPackageJsonFilesError(name); } const packageParentDirs = [];