diff --git a/packages/cli/src/commands/connector/add.ts b/packages/cli/src/commands/connector/add.ts index 25f530334..0e59c6ffd 100644 --- a/packages/cli/src/commands/connector/add.ts +++ b/packages/cli/src/commands/connector/add.ts @@ -1,13 +1,7 @@ -import chalk from 'chalk'; import { CommandModule } from 'yargs'; -import { oraPromise } from '../../utilities'; -import { - addConnectors, - fetchOfficialConnectorList, - inquireInstancePath, - normalizePackageName, -} from './utils'; +import { log } from '../../utilities'; +import { addConnectors, addOfficialConnectors, inquireInstancePath } from './utils'; const add: CommandModule = { command: ['add [packages...]', 'a', 'install', 'i'], @@ -32,14 +26,13 @@ const add: CommandModule { const instancePath = await inquireInstancePath(path); - const packages = official - ? await oraPromise(fetchOfficialConnectorList(), { - text: 'Fetch official connector list', - prefixText: chalk.blue('[info]'), - }) - : packageNames.map((name) => normalizePackageName(name)); + if (official) { + await addOfficialConnectors(instancePath); + } - await addConnectors(instancePath, packages); + await addConnectors(instancePath, packageNames); + + log.info('Restart your Logto instance to get the changes reflected.'); }, }; diff --git a/packages/cli/src/commands/connector/utils.ts b/packages/cli/src/commands/connector/utils.ts index 0c99d60d0..9fbe72fdf 100644 --- a/packages/cli/src/commands/connector/utils.ts +++ b/packages/cli/src/commands/connector/utils.ts @@ -13,7 +13,7 @@ import tar from 'tar'; import { z } from 'zod'; import { connectorDirectory } from '../../constants'; -import { log } from '../../utilities'; +import { log, oraPromise } from '../../utilities'; import { defaultPath } from '../install/utils'; const coreDirectory = 'packages/core'; @@ -96,38 +96,40 @@ export const addConnectors = async (instancePath: string, packageNames: string[] log.info('Fetch connector metadata'); const results = await Promise.all( - packageNames.map(async (packageName) => { - const run = async () => { - const { stdout } = await execPromise(`npm pack ${packageName} --json`, { cwd }); - const result = npmPackResultGuard.parse(JSON.parse(stdout)); + packageNames + .map((name) => normalizePackageName(name)) + .map(async (packageName) => { + const run = async () => { + const { stdout } = await execPromise(`npm pack ${packageName} --json`, { cwd }); + const result = npmPackResultGuard.parse(JSON.parse(stdout)); - if (!result[0]) { - throw new Error( - `Unable to execute ${chalk.green('npm pack')} on package ${chalk.green(packageName)}` - ); + if (!result[0]) { + throw new Error( + `Unable to execute ${chalk.green('npm pack')} on package ${chalk.green(packageName)}` + ); + } + + const { filename, name } = result[0]; + const escapedFilename = filename.replace(/\//g, '-').replace(/@/g, ''); + const tarPath = path.join(cwd, escapedFilename); + const packageDirectory = path.join(cwd, name.replace(/\//g, '-')); + + await remove(packageDirectory); + await ensureDir(packageDirectory); + await tar.extract({ cwd: packageDirectory, file: tarPath, strip: 1 }); + await unlink(tarPath); + + log.succeed(`Added ${chalk.green(name)}`); + }; + + try { + await pRetry(run, { retries: 2 }); + } catch (error: unknown) { + console.warn(`[${packageName}]`, error); + + return packageName; } - - const { filename, name } = result[0]; - const escapedFilename = filename.replace(/\//g, '-').replace(/@/g, ''); - const tarPath = path.join(cwd, escapedFilename); - const packageDirectory = path.join(cwd, name.replace(/\//g, '-')); - - await remove(packageDirectory); - await ensureDir(packageDirectory); - await tar.extract({ cwd: packageDirectory, file: tarPath, strip: 1 }); - await unlink(tarPath); - - log.succeed(`Added ${chalk.green(name)}`); - }; - - try { - await pRetry(run, { retries: 2 }); - } catch (error: unknown) { - console.warn(`[${packageName}]`, error); - - return packageName; - } - }) + }) ); const errorPackages = results.filter(Boolean); @@ -146,7 +148,7 @@ export const addConnectors = async (instancePath: string, packageNames: string[] const officialConnectorPrefix = '@logto/connector-'; -export const fetchOfficialConnectorList = async () => { +const fetchOfficialConnectorList = async () => { const { stdout } = await execPromise(`npm search ${officialConnectorPrefix} --json`); const packages = z .object({ name: z.string() }) @@ -160,3 +162,11 @@ export const fetchOfficialConnectorList = async () => { ) ); }; + +export const addOfficialConnectors = async (instancePath: string) => { + const packages = await oraPromise(fetchOfficialConnectorList(), { + text: 'Fetch official connector list', + prefixText: chalk.blue('[info]'), + }); + await addConnectors(instancePath, packages); +}; diff --git a/packages/cli/src/commands/install/index.ts b/packages/cli/src/commands/install/index.ts index 3d08fbac2..42aec162f 100644 --- a/packages/cli/src/commands/install/index.ts +++ b/packages/cli/src/commands/install/index.ts @@ -3,6 +3,7 @@ import { CommandModule } from 'yargs'; import { getDatabaseUrlFromConfig } from '../../database'; import { log } from '../../utilities'; +import { addOfficialConnectors } from '../connector/utils'; import { validateNodeVersion, inquireInstancePath, @@ -12,14 +13,16 @@ import { createEnv, logFinale, decompress, + inquireOfficialConnectors, } from './utils'; export type InstallArgs = { path?: string; skipSeed: boolean; + officialConnectors?: boolean; }; -const installLogto = async ({ path, skipSeed }: InstallArgs) => { +const installLogto = async ({ path, skipSeed, officialConnectors }: InstallArgs) => { validateNodeVersion(); // Get instance path @@ -34,7 +37,11 @@ const installLogto = async ({ path, skipSeed }: InstallArgs) => { // Seed database if (skipSeed) { - log.info(`You can use ${chalk.green('db seed')} command to seed database when ready.`); + log.info( + `Skipped database seeding.\n\n' + ' You can use the ${chalk.green( + 'db seed' + )} command to seed database when ready.\n` + ); } else { await seedDatabase(instancePath); } @@ -42,11 +49,21 @@ const installLogto = async ({ path, skipSeed }: InstallArgs) => { // Save to dot env await createEnv(instancePath, await getDatabaseUrlFromConfig()); + // Add official connectors + if (await inquireOfficialConnectors(officialConnectors)) { + await addOfficialConnectors(instancePath); + } else { + log.info( + 'Skipped adding official connectors.\n\n' + + ` You can use the ${chalk.green('connector add')} command to add connectors at any time.\n` + ); + } + // Finale logFinale(instancePath); }; -const install: CommandModule = { +const install: CommandModule = { command: ['init', 'i', 'install'], describe: 'Download and run the latest Logto release', builder: (yargs) => @@ -62,9 +79,14 @@ const install: CommandModule = { type: 'boolean', default: false, }, + officialConnectors: { + alias: 'oc', + describe: 'Add official connectors after downloading Logto', + type: 'boolean', + }, }), - handler: async ({ path, skipSeed }) => { - await installLogto({ path, skipSeed }); + handler: async ({ path, skipSeed, officialConnectors }) => { + await installLogto({ path, skipSeed, officialConnectors }); }, }; diff --git a/packages/cli/src/commands/install/utils.ts b/packages/cli/src/commands/install/utils.ts index d120d694c..5e295b11d 100644 --- a/packages/cli/src/commands/install/utils.ts +++ b/packages/cli/src/commands/install/utils.ts @@ -152,3 +152,17 @@ export const logFinale = (instancePath: string) => { `Use the command below to start Logto. Happy hacking!\n\n ${chalk.green(startCommand)}` ); }; + +export const inquireOfficialConnectors = async (initialAnswer?: boolean) => { + const { value } = await inquirer.prompt<{ value: boolean }>( + { + name: 'value', + message: 'Do you want to add official connectors?', + type: 'confirm', + default: true, + }, + { value: initialAnswer } + ); + + return value; +};