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
1 change: 1 addition & 0 deletions lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,7 @@ en:
selectProject: "Select a project to download:"
errors:
projectNotFound: "Your project {{ projectName }} could not be found in {{ accountId }}. Please select a valid project:"
accountIdRequired: "An account ID is required to download a project."
projectAddPrompt:
selectType: "[--type] Select your component type:"
enterName: "[--name] Give your component a name: "
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
45 changes: 22 additions & 23 deletions lib/prompts/accountsPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
// @ts-nocheck
const {
import {
getConfigDefaultAccount,
getConfigAccounts,
} = require('@hubspot/local-dev-lib/config');
const {
getAccountIdentifier,
} = require('@hubspot/local-dev-lib/config/getAccountIdentifier');
const { promptUser } = require('./promptUtils');
const { i18n } = require('../lang');
const { uiAccountDescription } = require('../ui');
} from '@hubspot/local-dev-lib/config';
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
import { promptUser } from './promptUtils';
import { i18n } from '../lang';
import { uiAccountDescription } from '../ui';
import { CLIAccount } from '@hubspot/local-dev-lib/types/Accounts';
import { PromptChoices } from '../../types/prompts';

const mapAccountChoices = portals =>
portals.map(p => ({
name: uiAccountDescription(getAccountIdentifier(p), false),
value: p.name || getAccountIdentifier(p),
}));
function mapAccountChoices(
portals: CLIAccount[] | null | undefined
): PromptChoices {
return (
portals?.map(p => ({
name: uiAccountDescription(getAccountIdentifier(p), false),
value: String(p.name || getAccountIdentifier(p)),
})) || []
);
}

const i18nKey = 'commands.account.subcommands.use';

const selectAccountFromConfig = async (prompt = '') => {
export async function selectAccountFromConfig(prompt = ''): Promise<string> {
const accountsList = getConfigAccounts();
const defaultAccount = getConfigDefaultAccount();

const { default: selectedDefault } = await promptUser([
const { default: selectedDefault } = await promptUser<{ default: string }>([
{
type: 'list',
look: false,
name: 'default',
pageSize: 20,
message: prompt || i18n(`${i18nKey}.promptMessage`),
choices: mapAccountChoices(accountsList),
default: defaultAccount,
default: defaultAccount ?? undefined,
},
]);

return selectedDefault;
};

module.exports = {
selectAccountFromConfig,
};
}
39 changes: 20 additions & 19 deletions lib/prompts/cmsFieldPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// @ts-nocheck
const path = require('path');
const fs = require('fs');
const { promptUser } = require('./promptUtils');
const { i18n } = require('../lang');
const escapeRegExp = require('@hubspot/local-dev-lib/escapeRegExp');
import path from 'path';
import fs from 'fs';
import { promptUser } from './promptUtils';
import { i18n } from '../lang';
import { escapeRegExp } from '@hubspot/local-dev-lib/escapeRegExp';

const i18nKey = 'lib.prompts.uploadPrompt';
const FIELDS_FILES = ['fields.json', 'fields.js', 'fields.cjs', 'fields.mjs'];

const fieldsJsPrompt = async (filePath, projectDir, skipFiles = []) => {
export async function fieldsJsPrompt(
filePath: string,
projectDir: string,
skipFiles: string[] = []
): Promise<[string, string[]]> {
const dirName = path.dirname(filePath);

// Get a list of all field files in the directory, resolve their absolute path, and remove the ones that we already skipped.
Expand All @@ -18,34 +22,31 @@ const fieldsJsPrompt = async (filePath, projectDir, skipFiles = []) => {
.filter(file => !skipFiles.includes(file));

if (!fileChoices.length) return [filePath, []];
if (fileChoices.length == 1) return [fileChoices[0], []];
if (fileChoices.length === 1) return [fileChoices[0], []];

// We get the directory above the project one so that relative paths are printed with the root of the project dir attached.
projectDir = projectDir.substring(0, projectDir.lastIndexOf('/'));
const projectDirRegex = new RegExp(`^${escapeRegExp(projectDir)}`);
const fileDir = path.dirname(fileChoices[0]).replace(projectDirRegex, '');

const selection = [];
fileChoices.forEach(fileChoice => {
selection.push({
name: fileChoice.replace(projectDirRegex, ''),
value: fileChoice,
});
});
const promptVal = await promptUser([
const selection = fileChoices.map(fileChoice => ({
name: fileChoice.replace(projectDirRegex, ''),
value: fileChoice,
}));

const promptVal = await promptUser<{ filePathChoice: string }>([
{
message: i18n(`${i18nKey}.fieldsPrompt`, { dir: fileDir }),
type: 'list',
name: 'filePathChoice',
choices: selection,
},
]);

const choice = promptVal.filePathChoice;

// Add the ones that were not picked to skip files array.
const notPicked = fileChoices.filter(item => item !== choice);
skipFiles.push(...notPicked);
return [choice, skipFiles];
};

module.exports = { fieldsJsPrompt };
}
129 changes: 81 additions & 48 deletions lib/prompts/createApiSamplePrompt.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,96 @@
// @ts-nocheck
const { promptUser } = require('./promptUtils');
const { i18n } = require('../lang');
import { promptUser } from './promptUtils';
import { i18n } from '../lang';
import { PromptConfig } from '../../types/prompts';

const i18nKey = 'lib.prompts.createApiSamplePrompt';

const getSampleTypesPrompt = choices => ({
type: 'rawlist',
name: 'sampleType',
message: i18n(`${i18nKey}.selectApiSampleApp`),
choices: choices.map(choice => ({
name: `${choice.name} - ${choice.description}`,
value: choice.id,
})),
validate: input => {
return new Promise(function(resolve, reject) {
if (input.length > 0) {
resolve(true);
}
reject(i18n(`${i18nKey}.errors.apiSampleAppRequired`));
});
},
});

const getLanguagesPrompt = choices => ({
type: 'rawlist',
name: 'sampleLanguage',
message: i18n(`${i18nKey}.selectLanguage`),
choices: choices.map(choice => ({
name: choice,
value: choice,
})),
validate: input => {
return new Promise(function(resolve, reject) {
if (input.length > 0) {
resolve(true);
}
reject(i18n(`${i18nKey}.errors.languageRequired`));
});
},
});

const createApiSamplePrompt = async samplesConfig => {
type SampleChoice = {
name: string;
description: string;
id: string;
languages: string[];
};

type SampleConfig = {
samples: SampleChoice[];
};

type SampleTypePromptResponse = {
sampleType?: string;
};

type LanguagePromptResponse = {
sampleLanguage?: string;
};

type CreateApiSamplePromptResponse = SampleTypePromptResponse &
LanguagePromptResponse;

function getSampleTypesPrompt(
choices: SampleChoice[]
): PromptConfig<SampleTypePromptResponse> {
return {
type: 'rawlist',
name: 'sampleType',
message: i18n(`${i18nKey}.selectApiSampleApp`),
choices: choices.map(choice => ({
name: `${choice.name} - ${choice.description}`,
value: choice.id,
})),
validate: function(input?: string) {
return new Promise<boolean>(function(resolve, reject) {
if (input && input.length > 0) {
resolve(true);
} else {
reject(i18n(`${i18nKey}.errors.apiSampleAppRequired`));
}
});
},
};
}

function getLanguagesPrompt(
choices: string[]
): PromptConfig<LanguagePromptResponse> {
return {
type: 'rawlist',
name: 'sampleLanguage',
message: i18n(`${i18nKey}.selectLanguage`),
choices: choices.map(choice => ({
name: choice,
value: choice,
})),
validate: function(input: string | undefined) {
return new Promise<boolean>(function(resolve, reject) {
if (input && input.length > 0) {
resolve(true);
}
reject(i18n(`${i18nKey}.errors.languageRequired`));
});
},
};
}

export async function createApiSamplePrompt(
samplesConfig: SampleConfig
): Promise<CreateApiSamplePromptResponse> {
try {
const { samples } = samplesConfig;
const sampleTypeAnswer = await promptUser(getSampleTypesPrompt(samples));
const sampleTypeAnswer = await promptUser<SampleTypePromptResponse>(
getSampleTypesPrompt(samples)
);
const chosenSample = samples.find(
sample => sample.id === sampleTypeAnswer.sampleType
);
const { languages } = chosenSample;
const languagesAnswer = await promptUser(getLanguagesPrompt(languages));
const { languages } = chosenSample!;
const languagesAnswer = await promptUser<LanguagePromptResponse>(
getLanguagesPrompt(languages)
);
return {
...sampleTypeAnswer,
...languagesAnswer,
};
} catch (e) {
return {};
}
};

module.exports = {
createApiSamplePrompt,
};
}
Loading
Loading