Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Convert prompts to TypeScript (final part) #1293

Merged
merged 17 commits into from
Dec 11, 2024
2 changes: 2 additions & 0 deletions lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,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 @@ -1253,6 +1254,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(),
kemmerle marked this conversation as resolved.
Show resolved Hide resolved
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
Loading