Skip to content

Commit

Permalink
Merge pull request #1035 from HubSpot/js/theme-project-dev
Browse files Browse the repository at this point in the history
Theme project dev
  • Loading branch information
jsines authored Apr 22, 2024
2 parents 201cb50 + b141450 commit e81b6f7
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
62 changes: 48 additions & 14 deletions packages/cli/commands/theme/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,28 @@ const { preview } = require('@hubspot/theme-preview-dev-server');
const { getUploadableFileList } = require('../../lib/upload');
const { trackCommandUsage } = require('../../lib/usageTracking');
const { loadAndValidateOptions } = require('../../lib/validation');
const { previewPrompt } = require('../../lib/prompts/previewPrompt');
const {
previewPrompt,
previewProjectPrompt,
} = require('../../lib/prompts/previewPrompt');
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
const {
FILE_UPLOAD_RESULT_TYPES,
} = require('@hubspot/local-dev-lib/constants/files');
const i18nKey = 'cli.commands.preview';
const cliProgress = require('cli-progress');
const {
ApiErrorContext,
logApiUploadErrorInstance,
} = require('../../lib/errorHandlers/apiErrors');
const { handleExit, handleKeypress } = require('../../lib/process');
const { getThemeJSONPath } = require('@hubspot/local-dev-lib/cms/themes');
const { getProjectConfig } = require('../../lib/projects');
const {
findProjectComponents,
COMPONENT_TYPES,
} = require('../../lib/projectStructure');

const i18nKey = 'cli.commands.preview';
exports.command = 'preview [--src] [--dest]';
exports.describe = false; // i18n(`${i18nKey}.describe`) - Hiding command

Expand Down Expand Up @@ -63,25 +72,50 @@ const handleUserInput = () => {
});
};

const determineSrcAndDest = async options => {
let absoluteSrc;
let dest;
const { projectDir, projectConfig } = await getProjectConfig();
if (!(projectDir && projectConfig)) {
// Not in a project, prompt for src and dest of traditional theme
const previewPromptAnswers = await previewPrompt(options);
const src = options.src || previewPromptAnswers.src;
dest = options.dest || previewPromptAnswers.dest;
absoluteSrc = path.resolve(getCwd(), src);
if (!dest || !validateSrcPath(absoluteSrc)) {
process.exit(EXIT_CODES.ERROR);
}
} else {
// In a project
let themeJsonPath = getThemeJSONPath();
if (!themeJsonPath) {
const projectComponents = await findProjectComponents(projectDir);
const themeComponents = projectComponents.filter(
c => c.type === COMPONENT_TYPES.hublTheme
);
if (themeComponents.length === 0) {
logger.error(i18n(`${i18nKey}.errors.noThemeComponents`));
process.exit(EXIT_CODES.ERROR);
}
const answer = await previewProjectPrompt(themeComponents);
themeJsonPath = `${answer.themeComponentPath}/theme.json`;
}
const { dir: themeDir } = path.parse(themeJsonPath);
absoluteSrc = themeDir;
const { base: themeName } = path.parse(themeDir);
dest = `@projects/${projectConfig.name}/${themeName}`;
}
return { absoluteSrc, dest };
};

exports.handler = async options => {
const { notify, skipUpload, noSsl, port, debug } = options;

await loadAndValidateOptions(options);

const accountId = getAccountId(options);

const previewPromptAnswers = await previewPrompt(options);
const src = options.src || previewPromptAnswers.src;
let dest = options.dest || previewPromptAnswers.dest;
if (!dest) {
logger.error(i18n(`${i18nKey}.errors.destinationRequired`));
return;
}

const absoluteSrc = path.resolve(getCwd(), src);
if (!validateSrcPath(absoluteSrc)) {
process.exit(EXIT_CODES.ERROR);
}
const { absoluteSrc, dest } = await determineSrcAndDest(options);

const filePaths = await getUploadableFileList(absoluteSrc, false);

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ en:
describe: "Upload and watch a theme directory on your computer for changes and start a local development server to preview theme changes on a site"
errors:
invalidPath: "The path \"{{ path }}\" is not a path to a directory"
noThemeComponents: "Your project has no theme components available to preview."
options:
src:
describe: "Path to the local directory your theme is in, relative to your current working directory"
Expand Down Expand Up @@ -1222,6 +1223,7 @@ en:
previewPrompt:
enterSrc: "[--src] Enter a local theme directory to preview."
enterDest: "[--dest] Enter the destination path for the src theme in HubSpot Design Tools."
themeProjectSelect: "[--theme] Select which theme to preview."
errors:
srcRequired: "You must specify a source directory."
destRequired: "You must specify a destination directory."
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/lib/projectStructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ const { logErrorInstance } = require('./errorHandlers/standardErrors');
const COMPONENT_TYPES = Object.freeze({
privateApp: 'private-app',
publicApp: 'public-app',
hublTheme: 'hubl-theme',
});

const CONFIG_FILES = {
[COMPONENT_TYPES.privateApp]: 'app.json',
[COMPONENT_TYPES.publicApp]: 'public-app.json',
[COMPONENT_TYPES.hublTheme]: 'theme.json',
};

function getTypeFromConfigFile(configFile) {
Expand Down Expand Up @@ -102,13 +104,14 @@ async function findProjectComponents(projectSourceDir) {
if (Object.values(CONFIG_FILES).includes(base)) {
const parsedAppConfig = loadConfigFile(projectFile);

if (parsedAppConfig && parsedAppConfig.name) {
if (parsedAppConfig) {
const isLegacy = getIsLegacyApp(parsedAppConfig, dir);
const isHublTheme = base === CONFIG_FILES[COMPONENT_TYPES.hublTheme];

components.push({
type: getTypeFromConfigFile(base),
config: parsedAppConfig,
runnable: !isLegacy,
runnable: !isLegacy && !isHublTheme,
path: dir,
});
}
Expand Down
18 changes: 18 additions & 0 deletions packages/cli/lib/prompts/previewPrompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ const previewPrompt = (promptOptions = {}) => {
]);
};

const previewProjectPrompt = async themeComponents => {
return promptUser([
{
name: 'themeComponentPath',
message: i18n(`${i18nKey}.themeProjectSelect`),
type: 'list',
choices: themeComponents.map(t => {
const themeName = path.basename(t.path);
return {
name: themeName,
value: t.path,
};
}),
},
]);
};

module.exports = {
previewPrompt,
previewProjectPrompt,
};

0 comments on commit e81b6f7

Please sign in to comment.