From a068b9e6a7d98d24fa5ecd41c5e2ab4d34343d1c Mon Sep 17 00:00:00 2001 From: James Pacheco Date: Tue, 17 Oct 2023 14:39:49 -0600 Subject: [PATCH] Update execa usage --- core/create-app.tsx | 14 +++--- core/help-text.ts | 4 +- core/types/BaseScaffold.ts | 6 ++- core/utils/fs.ts | 13 ++--- core/utils/renderScaffold.ts | 6 +-- core/utils/scaffold-helpers.ts | 9 ++-- core/utils/templateMappings.ts | 48 +++++++++---------- .../nextjs-dedicated-wallet/scaffold.tsx | 6 +-- .../nextjs-flow-dedicated-wallet/scaffold.tsx | 6 +-- .../nextjs-flow-universal-wallet/scaffold.tsx | 6 +-- .../scaffold.tsx | 6 +-- scaffolds/nextjs-universal-wallet/scaffold.ts | 8 ++-- scaffolds/prompts.ts | 24 ++++++---- 13 files changed, 80 insertions(+), 76 deletions(-) diff --git a/core/create-app.tsx b/core/create-app.tsx index 3d2b5f4..4c56a1b 100644 --- a/core/create-app.tsx +++ b/core/create-app.tsx @@ -6,8 +6,11 @@ import fs from 'fs'; import { URL } from 'url'; import path from 'path'; +import ora, { Ora } from 'ora'; +import prettyTime from 'pretty-time'; import execa from 'execa'; import chalk from 'chalk'; +import { BlockchainNetworkPrompt } from 'scaffolds/prompts'; import { downloadAndExtractRepo, getRepoInfo } from './utils/repo'; import { makeDir } from './utils/make-dir'; import { DEFAULT_CREATE_MAGIC_APP_REPO, GITHUB_BASE_URL } from './config'; @@ -18,10 +21,7 @@ import { parseFlags } from './flags'; import { addShutdownTask } from './utils/shutdown'; import { SharedAnalytics } from './analytics'; import { buildTemplate, mapTemplateToFlags, mapTemplateToScaffold } from './utils/templateMappings'; -import { BlockchainNetworkPrompt } from 'scaffolds/prompts'; -import ora, { Ora } from 'ora'; import { Timer, createTimer } from './utils/timer'; -import prettyTime from 'pretty-time'; import BaseScaffold from './types/BaseScaffold'; import { renderScaffold } from './utils/renderScaffold'; import { ConsoleMessages } from './cli'; @@ -71,7 +71,7 @@ export async function createApp(config: CreateMagicAppConfig) { }; }); - let isChosenTemplateValid = availableScaffolds.map((i) => i.name).includes(config?.template!); + const isChosenTemplateValid = availableScaffolds.map((i) => i.name).includes(config?.template!); if (config?.template && !isChosenTemplateValid) { printWarning(chalk`'{bold ${config.template}}' does not match any templates.`); @@ -122,7 +122,7 @@ export async function createApp(config: CreateMagicAppConfig) { const scaffold = await mapTemplateToScaffold(config.template as string, templateData, spinner, timer); startTimerAndSpinner(timer, spinner, true); - console.log(gray('\n\nRunning scaffold ') + cyan.bold(scaffold.templateName) + '\n'); + console.log(`${gray('\n\nRunning scaffold ') + cyan.bold(scaffold.templateName)}\n`); await renderScaffold(process.cwd(), scaffold, templateData); @@ -160,8 +160,8 @@ function createPostRenderAction(options: { options.cmd == 'installDependenciesCommand' ? options.scaffold.installationCommand : options.scaffold.startCommand; if (getCmd) { - const subprocess = execa(getCmd.join(' '), undefined, { stdio: 'inherit' }); - const bin = getCmd.join(' '); + const subprocess = execa(getCmd.command, getCmd.args, { stdio: 'inherit' }); + const bin = `${getCmd.command} ${getCmd.args.join(' ')}`; return Object.assign(bin, { wait: async () => { diff --git a/core/help-text.ts b/core/help-text.ts index 9406333..b475320 100644 --- a/core/help-text.ts +++ b/core/help-text.ts @@ -1,13 +1,13 @@ /* eslint-disable no-param-reassign */ /* eslint-disable consistent-return */ +import { c } from 'tar'; import chalk from 'chalk'; import decamelize from 'decamelize'; import wrapAnsi from 'wrap-ansi'; import { BINARY } from './config'; import { Flags, Flag } from './flags'; import { mapTemplateToFlags } from './utils/templateMappings'; -import { c } from 'tar'; const styled = { Usage: chalk.bold.inverse(' USAGE '), @@ -48,7 +48,7 @@ export function printHelp(globalOptions: Flags, scaffoldName?: string) { helpSections.push( createHelpSection({ heading: styled.Options + chalk.bold(' ❯ ') + chalk.bold.hex('#b93fff').inverse(` ${scaffoldName} `), - content: createOptionsTable(flags as any), + content: createOptionsTable(flags), }), ); } catch {} diff --git a/core/types/BaseScaffold.ts b/core/types/BaseScaffold.ts index cb9ff84..e9cf91b 100644 --- a/core/types/BaseScaffold.ts +++ b/core/types/BaseScaffold.ts @@ -1,6 +1,8 @@ export default abstract class BaseScaffold { public abstract templateName: string; public abstract source: string | string[]; - public abstract installationCommand: string[]; - public abstract startCommand: string[]; + public abstract installationCommand: ExecaCommand; + public abstract startCommand: ExecaCommand; } + +export type ExecaCommand = { command: string; args: string[] }; diff --git a/core/utils/fs.ts b/core/utils/fs.ts index 651d9df..e68c9c6 100644 --- a/core/utils/fs.ts +++ b/core/utils/fs.ts @@ -1,10 +1,11 @@ -import chalk from 'chalk'; -import { renderFile } from 'ejs'; -const { Confirm } = require('enquirer'); import fs from 'fs'; +import { renderFile } from 'ejs'; import fse from 'fs-extra'; -import { isBinary } from './is-binary'; +import chalk from 'chalk'; import { createPromise } from 'core/utils/create-promise'; +import { isBinary } from './is-binary'; + +const { Confirm } = require('enquirer'); // TODO - rename so that we know it also renders ejs export const copyFileWithEjsData = async (from: string, to: string, data: any) => { @@ -44,13 +45,13 @@ export const readTemplateDirs = ( root: string, done: (err: NodeJS.ErrnoException | null, results: string[]) => void, ): string[] => { - var filePaths: string[] = []; + let filePaths: string[] = []; fs.readdir(root, (err, files) => { if (err) { console.log(err); return; } - var pending = files.length; + let pending = files.length; if (!pending) return done(null, filePaths); files.forEach((file) => { const stats = fs.statSync(`${root}/${file}`); diff --git a/core/utils/renderScaffold.ts b/core/utils/renderScaffold.ts index 44d00c3..ad8a02e 100644 --- a/core/utils/renderScaffold.ts +++ b/core/utils/renderScaffold.ts @@ -1,15 +1,15 @@ +import path from 'path'; +import fs from 'fs'; import BaseScaffold from 'core/types/BaseScaffold'; import { resolveToRoot } from './path-helpers'; import { copyFileWithEjsData, readTemplateDirs } from './fs'; -import path from 'path'; -import fs from 'fs'; // TODO - update templateData type to be more specific export const renderScaffold = async (cwd: string, scaffold: BaseScaffold, templateData: any) => { const basePath = resolveToRoot('scaffolds', scaffold.templateName, 'template'); const allDirFilePaths: string[] = []; // typeof scaffold.source being a string means it's a directory and we should copy all files - if (typeof scaffold.source == 'string') { + if (typeof scaffold.source === 'string') { readTemplateDirs(basePath, async (err, filePaths) => { if (err) { console.log(err); diff --git a/core/utils/scaffold-helpers.ts b/core/utils/scaffold-helpers.ts index ec0b624..9cfe87c 100644 --- a/core/utils/scaffold-helpers.ts +++ b/core/utils/scaffold-helpers.ts @@ -3,10 +3,11 @@ /* eslint-disable import/no-dynamic-require */ /* eslint-disable global-require */ +import fs from 'fs'; import { getAbsoluteTemplatePath, resolveToDist, resolveToRoot } from './path-helpers'; import type { CreateMagicAppData } from '../create-app'; import type { Flags, ValueType } from '../flags'; -import fs from 'fs'; +import { ExecaCommand } from 'core/types/BaseScaffold'; /** * Metadata about the scaffold being defined. @@ -29,12 +30,12 @@ type ScaffoldMetadata = Record> * Provides an optional shell command to install dependencies * required by the scaffolded project. */ - installDependenciesCommand?: string[] | ((data: T & CreateMagicAppData) => string[]); + installDependenciesCommand?: ExecaCommand | ((data: T & CreateMagicAppData) => ExecaCommand); /** * Provides an optional shell command to start the scaffolded project. */ - startCommand?: string[] | ((data: T & CreateMagicAppData) => string[]); + startCommand?: ExecaCommand | ((data: T & CreateMagicAppData) => ExecaCommand); /** * Provides metadata about CLI flags that may be used @@ -63,5 +64,5 @@ export function createProjectDirIfDoesntExists(cwd: string, projectName: string) if (!fs.existsSync(resolveToRoot(cwd, projectName))) { fs.mkdirSync(resolveToRoot(cwd, projectName)); } - process.chdir(projectName as string); + process.chdir(projectName); } diff --git a/core/utils/templateMappings.ts b/core/utils/templateMappings.ts index 0f2051e..5861236 100644 --- a/core/utils/templateMappings.ts +++ b/core/utils/templateMappings.ts @@ -1,3 +1,13 @@ +import { Ora, Spinner } from 'ora'; +import { + AuthTypePrompt, + BlockchainNetworkPrompt, + ConfigurationPrompt, + ProductPrompt, + ProjectNamePrompt, + PublishableApiKeyPrompt, +} from 'scaffolds/prompts'; +import { CreateMagicAppConfig, pauseTimerAndSpinner } from 'core/create-app'; import BaseScaffold from '../types/BaseScaffold'; import DedicatedScaffold, { flags as dedicatedFlags } from '../../scaffolds/nextjs-dedicated-wallet/scaffold'; import FlowDedicatedScaffold, { @@ -10,17 +20,7 @@ import SolanaDedicatedScaffold, { flags as solanaDedicatedFlags, } from '../../scaffolds/nextjs-solana-dedicated-wallet/scaffold'; import UniversalScaffold, { flags as universalFlags } from '../../scaffolds/nextjs-universal-wallet/scaffold'; -import { - AuthTypePrompt, - BlockchainNetworkPrompt, - ConfigurationPrompt, - ProductPrompt, - ProjectNamePrompt, - PublishableApiKeyPrompt, -} from 'scaffolds/prompts'; -import { Ora, Spinner } from 'ora'; import { Timer } from './timer'; -import { CreateMagicAppConfig, pauseTimerAndSpinner } from 'core/create-app'; export type Chain = 'evm' | 'solana' | 'flow'; export type Template = @@ -86,10 +86,8 @@ export async function mapTemplateToScaffold( if (data.isQuickstart) { data.loginMethods = ['Email OTP']; - } else { - if (!data.loginMethods || data.loginMethods.length === 0) { - data.loginMethods = await AuthTypePrompt.loginMethodsPrompt(); - } + } else if (!data.loginMethods || data.loginMethods.length === 0) { + data.loginMethods = await AuthTypePrompt.loginMethodsPrompt(); } return new DedicatedScaffold(data); case 'nextjs-universal-wallet': @@ -192,19 +190,17 @@ export const buildTemplate = async (config: ConfigType): Promise => } else if (config.chain === 'evm') { config.network = await BlockchainNetworkPrompt.evmNetworkPrompt(); } + } else if ( + config.network == 'ethereum' || + config.network == 'ethereum-goerli' || + config.network == 'polygon' || + config.network == 'polygon-mumbai' + ) { + config.chain = 'evm'; + } else if (config.network == 'solana-denvet' || config.network == 'solana-mainnet') { + config.chain = 'solana'; } else { - if ( - config.network == 'ethereum' || - config.network == 'ethereum-goerli' || - config.network == 'polygon' || - config.network == 'polygon-mumbai' - ) { - config.chain = 'evm'; - } else if (config.network == 'solana-denvet' || config.network == 'solana-mainnet') { - config.chain = 'solana'; - } else { - config.chain = 'flow'; - } + config.chain = 'flow'; } if (!config.product) { diff --git a/scaffolds/nextjs-dedicated-wallet/scaffold.tsx b/scaffolds/nextjs-dedicated-wallet/scaffold.tsx index 236ae6e..1445f1c 100644 --- a/scaffolds/nextjs-dedicated-wallet/scaffold.tsx +++ b/scaffolds/nextjs-dedicated-wallet/scaffold.tsx @@ -1,5 +1,5 @@ import { Flags } from 'core/flags'; -import BaseScaffold from 'core/types/BaseScaffold'; +import BaseScaffold, { ExecaCommand } from 'core/types/BaseScaffold'; import { AuthTypePrompt, BlockchainNetworkPrompt, PublishableApiKeyPrompt } from 'scaffolds/prompts'; export type Data = BlockchainNetworkPrompt.Data & PublishableApiKeyPrompt.Data & AuthTypePrompt.Data; @@ -18,8 +18,8 @@ export const definition = { export default class DedicatedScaffold extends BaseScaffold { public templateName = 'nextjs-dedicated-wallet'; private data: Data; - public installationCommand: string[] = ['npm', 'install']; - public startCommand: string[] = ['npm', 'run', 'dev']; + public installationCommand: ExecaCommand = { command: 'npm', args: ['install'] }; + public startCommand: ExecaCommand = { command: 'npm', args: ['run', 'dev'] }; public source: string | string[] = [ './public/favicon.ico', './public/logo.svg', diff --git a/scaffolds/nextjs-flow-dedicated-wallet/scaffold.tsx b/scaffolds/nextjs-flow-dedicated-wallet/scaffold.tsx index 8f1886e..2d93f09 100644 --- a/scaffolds/nextjs-flow-dedicated-wallet/scaffold.tsx +++ b/scaffolds/nextjs-flow-dedicated-wallet/scaffold.tsx @@ -1,5 +1,5 @@ import { Flags } from 'core/flags'; -import BaseScaffold from 'core/types/BaseScaffold'; +import BaseScaffold, { ExecaCommand } from 'core/types/BaseScaffold'; import { AuthTypePrompt, NpmClientPrompt, PublishableApiKeyPrompt } from 'scaffolds/prompts'; export type Data = NpmClientPrompt.Data & PublishableApiKeyPrompt.Data & AuthTypePrompt.Data; @@ -18,8 +18,8 @@ export const definition = { export default class FlowDedicatedScaffold extends BaseScaffold { public templateName = 'nextjs-flow-dedicated-wallet'; private data: Data; - public installationCommand: string[] = ['npm', 'install']; - public startCommand: string[] = ['npm', 'run', 'dev']; + public installationCommand: ExecaCommand = { command: 'npm', args: ['install'] }; + public startCommand: ExecaCommand = { command: 'npm', args: ['run', 'dev'] }; public source: string | string[] = [ './public/favicon.ico', './public/logo.svg', diff --git a/scaffolds/nextjs-flow-universal-wallet/scaffold.tsx b/scaffolds/nextjs-flow-universal-wallet/scaffold.tsx index 9e6d756..07b7d37 100644 --- a/scaffolds/nextjs-flow-universal-wallet/scaffold.tsx +++ b/scaffolds/nextjs-flow-universal-wallet/scaffold.tsx @@ -1,5 +1,5 @@ import { Flags } from 'core/flags'; -import BaseScaffold from 'core/types/BaseScaffold'; +import BaseScaffold, { ExecaCommand } from 'core/types/BaseScaffold'; import { NpmClientPrompt, PublishableApiKeyPrompt } from 'scaffolds/prompts'; export type Data = NpmClientPrompt.Data & PublishableApiKeyPrompt.Data; @@ -17,8 +17,8 @@ export const definition = { export default class FlowUniversalScaffold extends BaseScaffold { public templateName = 'nextjs-flow-universal-wallet'; private data: Data; - public installationCommand: string[] = ['npm', 'install']; - public startCommand: string[] = ['npm', 'run', 'dev']; + public installationCommand: ExecaCommand = { command: 'npm', args: ['install'] }; + public startCommand: ExecaCommand = { command: 'npm', args: ['run', 'dev'] }; public source: string | string[] = './'; constructor(data: Data) { diff --git a/scaffolds/nextjs-solana-dedicated-wallet/scaffold.tsx b/scaffolds/nextjs-solana-dedicated-wallet/scaffold.tsx index aa54317..dbe38b4 100644 --- a/scaffolds/nextjs-solana-dedicated-wallet/scaffold.tsx +++ b/scaffolds/nextjs-solana-dedicated-wallet/scaffold.tsx @@ -1,5 +1,5 @@ import { Flags } from 'core/flags'; -import BaseScaffold from 'core/types/BaseScaffold'; +import BaseScaffold, { ExecaCommand } from 'core/types/BaseScaffold'; import { AuthTypePrompt, NpmClientPrompt, PublishableApiKeyPrompt } from 'scaffolds/prompts'; export type Data = NpmClientPrompt.Data & PublishableApiKeyPrompt.Data & AuthTypePrompt.Data; @@ -18,8 +18,8 @@ export const definition = { export default class SolanaDedicatedScaffold extends BaseScaffold { public templateName = 'nextjs-solana-dedicated-wallet'; private data: Data; - public installationCommand: string[] = ['npm', 'install']; - public startCommand: string[] = ['npm', 'run', 'dev']; + public installationCommand: ExecaCommand = { command: 'npm', args: ['install'] }; + public startCommand: ExecaCommand = { command: 'npm', args: ['run', 'dev'] }; public source: string | string[] = [ './public/favicon.ico', './public/logo.svg', diff --git a/scaffolds/nextjs-universal-wallet/scaffold.ts b/scaffolds/nextjs-universal-wallet/scaffold.ts index 6d4e582..a313653 100644 --- a/scaffolds/nextjs-universal-wallet/scaffold.ts +++ b/scaffolds/nextjs-universal-wallet/scaffold.ts @@ -1,6 +1,6 @@ -import { Flags } from 'core/flags'; -import BaseScaffold from 'core/types/BaseScaffold'; import { Prompt } from 'enquirer'; +import { Flags } from 'core/flags'; +import BaseScaffold, { ExecaCommand } from 'core/types/BaseScaffold'; import { BlockchainNetworkPrompt, PublishableApiKeyPrompt } from 'scaffolds/prompts'; export type Data = BlockchainNetworkPrompt.Data & PublishableApiKeyPrompt.Data; @@ -15,8 +15,8 @@ export const definition = { export default class UniversalScaffold extends BaseScaffold { public templateName = 'nextjs-universal-wallet'; private data: Data; - public installationCommand: string[] = ['npm', 'install']; - public startCommand: string[] = ['npm', 'run', 'dev']; + public installationCommand: ExecaCommand = { command: 'npm', args: ['install'] }; + public startCommand: ExecaCommand = { command: 'npm', args: ['run', 'dev'] }; public source: string | string[] = './'; constructor(data: Data) { diff --git a/scaffolds/prompts.ts b/scaffolds/prompts.ts index 76973c8..4f7ee66 100644 --- a/scaffolds/prompts.ts +++ b/scaffolds/prompts.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +import { Prompt } from 'enquirer'; import type { Flags } from 'core/flags'; import type { ValuesOf } from 'core/types/utility-types'; -import { Prompt } from 'enquirer'; +import { ExecaCommand } from 'core/types/BaseScaffold'; + const { Select, MultiSelect, Input } = require('enquirer'); export namespace ProjectNamePrompt { @@ -11,7 +13,7 @@ export namespace ProjectNamePrompt { }; export const askProjectName = async () => - await new Input({ + new Input({ name: 'projectName', message: 'What is your project named?', initial: 'awesome-magic-app', @@ -82,7 +84,7 @@ export namespace PublishableApiKeyPrompt { : '--publishable-api-key should look like `pk_live_...` or `pk_test_...`'; export const publishableApiKeyPrompt = async () => - await new Input({ + new Input({ message: 'Enter Magic publishable API key from https://dashboard.magic.link:', // @ts-ignore hint: '(leave blank to skip for now)', @@ -116,9 +118,11 @@ export namespace NpmClientPrompt { return data.npmClient === 'yarn' ? ['yarn', 'install'] : ['npm', 'install']; } - export function getStartCommand(packageJsonScript: string) { + export function getStartCommand(packageJsonScript: string): ExecaCommand | ((data: Data) => ExecaCommand) { return (data: Data) => { - return data.npmClient === 'npm' ? ['npm', 'run', packageJsonScript] : ['yarn', packageJsonScript]; + return data.npmClient === 'npm' + ? { command: 'npm', args: ['run', packageJsonScript] } + : { command: 'yarn', args: [packageJsonScript] }; }; } } @@ -129,7 +133,7 @@ export namespace BlockchainNetworkPrompt { }; export const chainPrompt = async () => - await new Select({ + new Select({ name: 'chain', message: 'Which blockchain do you want to use?', choices: [ @@ -140,7 +144,7 @@ export namespace BlockchainNetworkPrompt { }).run(); export const solanaNetworkPrompt = async () => - await new Select({ + new Select({ name: 'network', message: 'Which network would you like to use?', hint: 'We recommend starting with a test network', @@ -151,7 +155,7 @@ export namespace BlockchainNetworkPrompt { }).run(); export const flowNetworkPrompt = async () => - await new Select({ + new Select({ name: 'network', message: 'Which network would you like to use?', hint: 'We recommend starting with a test network', @@ -162,7 +166,7 @@ export namespace BlockchainNetworkPrompt { }).run(); export const evmNetworkPrompt = async () => - await new Select({ + new Select({ name: 'network', message: 'Which network would like to use?', hint: 'We recommend starting with a test network', @@ -199,7 +203,7 @@ export namespace AuthTypePrompt { }; export const loginMethodsPrompt = async () => - await new MultiSelect({ + new MultiSelect({ message: 'How do you want your users to log in to their wallet? See Magic docs for help (https://magic.link/docs/auth/overview)', hint: '( to select, to submit)',