Skip to content

Commit

Permalink
chore: Convert prompts to TypeScript (final part) (#1293)
Browse files Browse the repository at this point in the history
  • Loading branch information
kemmerle authored Dec 11, 2024
1 parent ac847e2 commit e34c6dc
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 237 deletions.
2 changes: 2 additions & 0 deletions lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ en:
developerTestAccountLimit: "Your account reached the limit of {{ limit }} developer test accounts."
confirmDefaultAccount: "Continue testing on {{#bold}}{{ accountName }} ({{ accountType }}){{/bold}}? (Y/n)"
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."
noAccountId: "No account ID found for the selected account. Please try again."
projectLogsPrompt:
functionName: "[--function] Select function in {{#bold}}{{projectName}}{{/bold}} project"
setAsDefaultAccountPrompt:
Expand Down Expand Up @@ -1307,6 +1308,7 @@ en:
selectAppIdMigrate: "[--appId] Choose an app under {{ accountName }} to migrate:"
selectAppIdClone: "[--appId] Choose an app under {{ accountName }} to clone:"
errors:
noAccountId: "An account ID is required to select an app."
noAppsMigration: "{{#bold}}No apps to migrate{{/bold}}"
noAppsClone: "{{#bold}}No apps to clone{{/bold}}"
noAppsMigrationMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be migrated to the projects framework."
Expand Down
2 changes: 1 addition & 1 deletion lib/prompts/accountNamePrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type AccountNamePromptResponse = {
};

export function getCliAccountNamePromptConfig(
defaultName: string
defaultName?: string
): PromptConfig<AccountNamePromptResponse> {
return {
name: 'name',
Expand Down
4 changes: 2 additions & 2 deletions lib/prompts/createTemplatePrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { PromptChoices, PromptConfig } from '../../types/prompts';

const i18nKey = 'lib.prompts.createTemplatePrompt';

const templateTypeChoices: PromptChoices = [
const templateTypeChoices = [
{ name: 'page', value: 'page-template' },
{ name: 'email', value: 'email-template' },
{ name: 'partial', value: 'partial' },
{ name: 'global partial', value: 'global-partial' },
{ name: 'blog listing', value: 'blog-listing-template' },
{ name: 'blog post', value: 'blog-post-template' },
{ name: 'search results', value: 'search-template' },
] as const;
] satisfies PromptChoices;

interface CreateTemplatePromptResponse {
templateType: typeof templateTypeChoices[number]['value'];
Expand Down
110 changes: 66 additions & 44 deletions lib/prompts/personalAccessKeyPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,56 @@
// @ts-nocheck
const open = require('open');
const {
import open from 'open';
import {
OAUTH_SCOPES,
DEFAULT_OAUTH_SCOPES,
} = require('@hubspot/local-dev-lib/constants/auth');
const { deleteEmptyConfigFile } = require('@hubspot/local-dev-lib/config');
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
const { logger } = require('@hubspot/local-dev-lib/logger');
const { promptUser } = require('./promptUtils');
const { getCliAccountNamePromptConfig } = require('./accountNamePrompt');
const { i18n } = require('../lang');
const { uiInfoSection } = require('../ui');
const { EXIT_CODES } = require('../enums/exitCodes');
} from '@hubspot/local-dev-lib/constants/auth';
import { deleteEmptyConfigFile } from '@hubspot/local-dev-lib/config';
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
import { logger } from '@hubspot/local-dev-lib/logger';
import { promptUser } from './promptUtils';
import { getCliAccountNamePromptConfig } from './accountNamePrompt';
import { i18n } from '../lang';
import { uiInfoSection } from '../ui';
import { EXIT_CODES } from '../enums/exitCodes';
import { PromptConfig } from '../../types/prompts';

const i18nKey = 'lib.prompts.personalAccessKeyPrompt';

type PersonalAccessKeyPromptResponse = {
personalAccessKey: string;
env: string;
};

type AccountIdPromptResponse = {
accountId: number;
};

type ClientIdPromptResponse = {
clientId: string;
};

type ClientSecretPromptResponse = {
clientSecret: string;
};

type PersonalAccessKeyBrowserOpenPrepResponse = {
personalAcessKeyBrowserOpenPrep: boolean;
};

type ScopesPromptResponse = {
scopes: string[];
};

/**
* Displays notification to user that we are about to open the browser,
* then opens their browser to the personal-access-key shortlink
*/
const personalAccessKeyPrompt = async ({ env, account } = {}) => {
export async function personalAccessKeyPrompt({
env,
account,
}: {
env: string;
account?: string;
}): Promise<PersonalAccessKeyPromptResponse> {
const websiteOrigin = getHubSpotWebsiteOrigin(env);
let url = `${websiteOrigin}/l/personal-access-key`;
if (process.env.BROWSER !== 'none') {
Expand All @@ -29,9 +60,9 @@ const personalAccessKeyPrompt = async ({ env, account } = {}) => {
if (account) {
url = `${websiteOrigin}/personal-access-key/${account}`;
}
const { personalAcessKeyBrowserOpenPrep: shouldOpen } = await promptUser([
PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP,
]);
const { personalAcessKeyBrowserOpenPrep: shouldOpen } = await promptUser<
PersonalAccessKeyBrowserOpenPrepResponse
>([PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP]);
if (shouldOpen) {
open(url, { url: true });
} else {
Expand All @@ -41,30 +72,32 @@ const personalAccessKeyPrompt = async ({ env, account } = {}) => {
}

logger.log(i18n(`${i18nKey}.logs.openingWebBrowser`, { url }));
const { personalAccessKey } = await promptUser(PERSONAL_ACCESS_KEY);
const { personalAccessKey } = await promptUser<
PersonalAccessKeyPromptResponse
>(PERSONAL_ACCESS_KEY);

return {
personalAccessKey,
env,
};
};
}

const ACCOUNT_ID = {
const ACCOUNT_ID: PromptConfig<AccountIdPromptResponse> = {
name: 'accountId',
message: i18n(`${i18nKey}.enterAccountId`),
type: 'number',
validate(val) {
if (!Number.isNaN(val) && val > 0) {
validate(val?: number) {
if (!Number.isNaN(val) && val !== undefined && val > 0) {
return true;
}
return i18n(`${i18nKey}.errors.invalidAccountId`);
},
};

const CLIENT_ID = {
const CLIENT_ID: PromptConfig<ClientIdPromptResponse> = {
name: 'clientId',
message: i18n(`${i18nKey}.enterClientId`),
validate(val) {
validate(val?: string) {
if (typeof val !== 'string') {
return i18n(`${i18nKey}.errors.invalidOauthClientId`);
} else if (val.length !== 36) {
Expand All @@ -74,10 +107,10 @@ const CLIENT_ID = {
},
};

const CLIENT_SECRET = {
const CLIENT_SECRET: PromptConfig<ClientSecretPromptResponse> = {
name: 'clientSecret',
message: i18n(`${i18nKey}.enterClientSecret`),
validate(val) {
validate(val?: string) {
if (typeof val !== 'string') {
return i18n(`${i18nKey}.errors.invalidOauthClientSecret`);
} else if (val.length !== 36) {
Expand All @@ -89,24 +122,24 @@ const CLIENT_SECRET = {
},
};

const PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP = {
const PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP: PromptConfig<PersonalAccessKeyBrowserOpenPrepResponse> = {
name: 'personalAcessKeyBrowserOpenPrep',
type: 'confirm',
message: i18n(`${i18nKey}.personalAccessKeyBrowserOpenPrompt`),
};

const PERSONAL_ACCESS_KEY = {
const PERSONAL_ACCESS_KEY: PromptConfig<PersonalAccessKeyPromptResponse> = {
name: 'personalAccessKey',
message: i18n(`${i18nKey}.enterPersonalAccessKey`),
transformer: val => {
transformer: (val?: string) => {
if (!val) return val;
let res = '';
for (let i = 0; i < val.length; i++) {
res += '*';
}
return res;
},
validate(val) {
validate(val?: string) {
if (!val || typeof val !== 'string') {
return i18n(`${i18nKey}.errors.invalidPersonalAccessKey`);
} else if (val[0] === '•') {
Expand All @@ -116,29 +149,18 @@ const PERSONAL_ACCESS_KEY = {
},
};

const SCOPES = {
const SCOPES: PromptConfig<ScopesPromptResponse> = {
type: 'checkbox',
name: 'scopes',
message: i18n(`${i18nKey}.selectScopes`),
default: DEFAULT_OAUTH_SCOPES,
choices: OAUTH_SCOPES,
default: [...DEFAULT_OAUTH_SCOPES],
choices: [...OAUTH_SCOPES],
};

const OAUTH_FLOW = [
export const OAUTH_FLOW = [
getCliAccountNamePromptConfig(),
ACCOUNT_ID,
CLIENT_ID,
CLIENT_SECRET,
SCOPES,
];

module.exports = {
personalAccessKeyPrompt,
CLIENT_ID,
CLIENT_SECRET,
ACCOUNT_ID,
SCOPES,
PERSONAL_ACCESS_KEY,
// Flows
OAUTH_FLOW,
};
43 changes: 25 additions & 18 deletions lib/prompts/previewPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
// @ts-nocheck
const path = require('path');
const { getCwd } = require('@hubspot/local-dev-lib/path');
const { promptUser } = require('./promptUtils');
const { i18n } = require('../lang');
import path from 'path';
import { getCwd } from '@hubspot/local-dev-lib/path';
import { promptUser } from './promptUtils';
import { i18n } from '../lang';

const i18nKey = 'lib.prompts.previewPrompt';

const previewPrompt = (promptOptions = {}) => {
return promptUser([
type PreviewPromptResponse = {
src: string;
dest: string;
};

type PreviewProjectPromptResponse = {
themeComponentPath: string;
};

export async function previewPrompt(
promptOptions: { src?: string; dest?: string } = {}
): Promise<PreviewPromptResponse> {
return promptUser<PreviewPromptResponse>([
{
name: 'src',
message: i18n(`${i18nKey}.enterSrc`),
when: !promptOptions.src,
default: '.',
validate: input => {
validate: (input?: string) => {
if (!input) {
return i18n(`${i18nKey}.errors.srcRequired`);
}
Expand All @@ -25,18 +35,20 @@ const previewPrompt = (promptOptions = {}) => {
message: i18n(`${i18nKey}.enterDest`),
when: !promptOptions.dest,
default: path.basename(getCwd()),
validate: input => {
validate: (input?: string) => {
if (!input) {
return i18n(`${i18nKey}.errors.destRequired`);
}
return true;
},
},
]);
};
}

const previewProjectPrompt = async themeComponents => {
return promptUser([
export async function previewProjectPrompt(
themeComponents: { path: string }[]
): Promise<PreviewProjectPromptResponse> {
return promptUser<PreviewProjectPromptResponse>([
{
name: 'themeComponentPath',
message: i18n(`${i18nKey}.themeProjectSelect`),
Expand All @@ -50,9 +62,4 @@ const previewProjectPrompt = async themeComponents => {
}),
},
]);
};

module.exports = {
previewPrompt,
previewProjectPrompt,
};
}
31 changes: 20 additions & 11 deletions lib/prompts/projectAddPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
// @ts-nocheck
const { promptUser } = require('./promptUtils');
const { i18n } = require('../lang');
import { promptUser } from './promptUtils';
import { i18n } from '../lang';

const i18nKey = 'lib.prompts.projectAddPrompt';

const projectAddPrompt = async (components, promptOptions = {}) => {
return promptUser([
type Component = {
path: string;
label: string;
insertPath: string;
};

type ProjectAddPromptResponse = {
component: Component;
name: string;
};

export async function projectAddPrompt(
components: Component[],
promptOptions: { name?: string; type?: string } = {}
): Promise<ProjectAddPromptResponse> {
return promptUser<ProjectAddPromptResponse>([
{
name: 'component',
message: () => {
Expand All @@ -31,16 +44,12 @@ const projectAddPrompt = async (components, promptOptions = {}) => {
name: 'name',
message: i18n(`${i18nKey}.enterName`),
when: !promptOptions.name,
validate: input => {
validate: (input?: string) => {
if (!input) {
return i18n(`${i18nKey}.errors.nameRequired`);
}
return true;
},
},
]);
};

module.exports = {
projectAddPrompt,
};
}
Loading

0 comments on commit e34c6dc

Please sign in to comment.