diff --git a/packages/cli/commands/fetch.js b/packages/cli/commands/fetch.js index 7b8c5da42..0322ac30f 100644 --- a/packages/cli/commands/fetch.js +++ b/packages/cli/commands/fetch.js @@ -17,6 +17,11 @@ const { i18n } = require('../lib/lang'); const i18nKey = 'commands.fetch'; const { EXIT_CODES } = require('../lib/enums/exitCodes'); const { logError } = require('../lib/errorHandlers/index'); +const { getIsInProject } = require('../lib/projects'); +const { + fetchProjectAssetPrompt, +} = require('../lib/prompts/fetchProjectAssetPrompt'); +const { uiFeatureHighlight } = require('../lib/ui'); exports.command = 'fetch [dest]'; exports.describe = i18n(`${i18nKey}.describe`); @@ -35,6 +40,23 @@ exports.handler = async options => { process.exit(EXIT_CODES.ERROR); } + // If fetching the Elevate theme, check if the destination is in a project. + if ('@hubspot/elevate' === src) { + const inProject = getIsInProject(resolveLocalPath(dest)); + // If not in a project, alert the user. Otherwise proceed with the fetch. + if (!inProject) { + const fetchAnyway = await fetchProjectAssetPrompt(src); + if (!fetchAnyway.continue) { + logger.log(''); + uiFeatureHighlight( + ['projectCreateCommand'], + i18n(`${i18nKey}.info.createElevateProjectTitle`) + ); + process.exit(EXIT_CODES.SUCCESS); + } + } + } + const accountId = getAccountId(options); const mode = getMode(options); diff --git a/packages/cli/commands/project/create.js b/packages/cli/commands/project/create.js index 422cdd8c8..da88903dd 100644 --- a/packages/cli/commands/project/create.js +++ b/packages/cli/commands/project/create.js @@ -54,7 +54,8 @@ exports.handler = async options => { options.name || name, template, options.templateSource, - githubRef + githubRef, + accountId ); logger.log(''); diff --git a/packages/cli/lang/en.lyaml b/packages/cli/lang/en.lyaml index 5d945b8c8..e3c1dacb6 100644 --- a/packages/cli/lang/en.lyaml +++ b/packages/cli/lang/en.lyaml @@ -278,6 +278,8 @@ en: describe: "Fetch a file, directory or module from HubSpot and write to a path on your computer." errors: sourceRequired: "A source to fetch is required." + info: + createElevateProjectTitle: "Create a project and select the \"Elevate\" theme" options: staging: describe: "Retrieve staged changes for project" @@ -1063,6 +1065,8 @@ en: logFeedbackMessage: feedbackHeader: "We'd love to hear your feedback!" feedbackMessage: "How are you liking the new projects and developer tools? \n > Run `{{#yellow}}hs feedback{{/yellow}}` to let us know what you think!\n" + errors: + downloadFileOrFolder: "Not all files in folder {{#bold}}\"{{ src }}\"{{/bold}} were successfully fetched. Run {{ command }} again to finish download" ui: betaTag: "{{#bold}}[BETA]{{/bold}}" betaWarning: @@ -1195,6 +1199,8 @@ en: invalidPersonalAccessKeyCopy: "Please copy the actual access key rather than the bullets that mask it." folderOverwritePrompt: overwriteConfirm: "The folder with name \"{{ folderName }}\" already exists. Overwrite?" + fetchProjectAssetPrompt: + fetchConfirm: "\"{{ assetPath }}\" requires a HubSpot project to work. Would you like to continue fetching without creating a project?" createTemplatePrompt: selectTemplate: "Select the type of template to create" createModulePrompt: diff --git a/packages/cli/lib/projects.js b/packages/cli/lib/projects.js index 72fc1f90a..bc8096333 100644 --- a/packages/cli/lib/projects.js +++ b/packages/cli/lib/projects.js @@ -39,10 +39,17 @@ const { getCwd, getAbsoluteFilePath } = require('@hubspot/local-dev-lib/path'); const { downloadGithubRepoContents } = require('@hubspot/local-dev-lib/github'); const { promptUser } = require('./prompts/promptUtils'); const { EXIT_CODES } = require('./enums/exitCodes'); -const { uiLine, uiLink, uiAccountDescription } = require('../lib/ui'); +const { + uiLine, + uiLink, + uiAccountDescription, + uiCommandReference, +} = require('../lib/ui'); const { i18n } = require('./lang'); const SpinniesManager = require('./ui/SpinniesManager'); const { logError, ApiErrorContext } = require('./errorHandlers/index'); +const { downloadFileOrFolder } = require('@hubspot/local-dev-lib/fileMapper'); +const { DEFAULT_MODE } = require('@hubspot/local-dev-lib/constants/files'); const i18nKey = 'lib.projects'; @@ -101,7 +108,8 @@ const createProjectConfig = async ( projectName, template, templateSource, - githubRef + githubRef, + accountId ) => { const { projectConfig, projectDir } = await getProjectConfig(projectPath); @@ -140,12 +148,25 @@ const createProjectConfig = async ( const hasCustomTemplateSource = Boolean(templateSource); - await downloadGithubRepoContents( - templateSource || HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, - template.path, - projectPath, - hasCustomTemplateSource ? undefined : githubRef - ); + const { isHubSpotAsset } = template; + + // if template config is marked `isHubSpotAsset` download no-template project to fetch the @hubspot asset into it. + if (isHubSpotAsset) { + await downloadGithubRepoContents( + HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, + 'projects/no-template', + projectPath, + githubRef + ); + } else { + await downloadGithubRepoContents( + templateSource || HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, + template.path, + projectPath, + hasCustomTemplateSource ? undefined : githubRef + ); + } + const _config = JSON.parse(fs.readFileSync(projectConfigPath)); writeProjectConfig(projectConfigPath, { ..._config, @@ -156,6 +177,36 @@ const createProjectConfig = async ( fs.ensureDirSync(path.join(projectPath, 'src')); } + if (isHubSpotAsset) { + const assetPath = template.path; + const destinationPath = path.join( + projectPath, + template.insertPath, + template.name + ); + // fetch the @hubspot asset and place it in the new project folder + try { + // Fetch and write file/folder. + await downloadFileOrFolder( + accountId, + assetPath, + destinationPath, + DEFAULT_MODE, + false + ); + } catch (err) { + logger.error( + i18n(`${i18nKey}.errors.downloadFileOrFolder`, { + src: template.path, + command: uiCommandReference( + `hs fetch ${assetPath} ${destinationPath}` + ), + }) + ); + process.exit(EXIT_CODES.ERROR); + } + } + return true; }; diff --git a/packages/cli/lib/prompts/fetchProjectAssetPrompt.js b/packages/cli/lib/prompts/fetchProjectAssetPrompt.js new file mode 100644 index 000000000..8cfbae5a0 --- /dev/null +++ b/packages/cli/lib/prompts/fetchProjectAssetPrompt.js @@ -0,0 +1,17 @@ +const { promptUser } = require('./promptUtils'); +const { i18n } = require('../lang'); + +const i18nKey = 'lib.prompts.fetchProjectAssetPrompt'; + +const fetchProjectAssetPrompt = assetPath => { + return promptUser({ + type: 'confirm', + name: 'continue', + message: i18n(`${i18nKey}.fetchConfirm`, { assetPath }), + default: false, + }); +}; + +module.exports = { + fetchProjectAssetPrompt, +};