diff options
Diffstat (limited to 'packages/create-astro/src')
-rw-r--r-- | packages/create-astro/src/actions/dependencies.ts | 18 | ||||
-rw-r--r-- | packages/create-astro/src/actions/git.ts | 8 | ||||
-rw-r--r-- | packages/create-astro/src/messages.ts | 4 | ||||
-rw-r--r-- | packages/create-astro/src/shell.ts | 49 |
4 files changed, 66 insertions, 13 deletions
diff --git a/packages/create-astro/src/actions/dependencies.ts b/packages/create-astro/src/actions/dependencies.ts index ed19fe485..339e3379f 100644 --- a/packages/create-astro/src/actions/dependencies.ts +++ b/packages/create-astro/src/actions/dependencies.ts @@ -1,6 +1,8 @@ import { color } from '@astrojs/cli-kit'; -import { execa } from 'execa'; +import fs from 'node:fs'; +import path from 'node:path'; import { error, info, spinner, title } from '../messages.js'; +import { shell } from '../shell.js'; import type { Context } from './context'; export async function dependencies( @@ -46,10 +48,12 @@ export async function dependencies( } async function install({ pkgManager, cwd }: { pkgManager: string; cwd: string }) { - const installExec = execa(pkgManager, ['install'], { cwd }); - return new Promise<void>((resolve, reject) => { - setTimeout(() => reject(`Request timed out after one minute`), 60_000); - installExec.on('error', (e) => reject(e)); - installExec.on('close', () => resolve()); - }); + if (pkgManager === 'yarn') await ensureYarnLock({ cwd }); + return shell(pkgManager, ['install'], { cwd, timeout: 90_000, stdio: 'ignore' }); +} + +async function ensureYarnLock({ cwd }: { cwd: string }) { + const yarnLock = path.join(cwd, 'yarn.lock'); + if (fs.existsSync(yarnLock)) return; + return fs.promises.writeFile(yarnLock, '', { encoding: 'utf-8' }); } diff --git a/packages/create-astro/src/actions/git.ts b/packages/create-astro/src/actions/git.ts index 00c42dae5..c2a59b0b3 100644 --- a/packages/create-astro/src/actions/git.ts +++ b/packages/create-astro/src/actions/git.ts @@ -3,8 +3,8 @@ import path from 'node:path'; import type { Context } from './context'; import { color } from '@astrojs/cli-kit'; -import { execa } from 'execa'; import { error, info, spinner, title } from '../messages.js'; +import { shell } from '../shell.js'; export async function git(ctx: Pick<Context, 'cwd' | 'git' | 'yes' | 'prompt' | 'dryRun'>) { if (fs.existsSync(path.join(ctx.cwd, '.git'))) { @@ -45,9 +45,9 @@ export async function git(ctx: Pick<Context, 'cwd' | 'git' | 'yes' | 'prompt' | async function init({ cwd }: { cwd: string }) { try { - await execa('git', ['init'], { cwd, stdio: 'ignore' }); - await execa('git', ['add', '-A'], { cwd, stdio: 'ignore' }); - await execa( + await shell('git', ['init'], { cwd, stdio: 'ignore' }); + await shell('git', ['add', '-A'], { cwd, stdio: 'ignore' }); + await shell( 'git', [ 'commit', diff --git a/packages/create-astro/src/messages.ts b/packages/create-astro/src/messages.ts index ee67f3b19..89ccddcdb 100644 --- a/packages/create-astro/src/messages.ts +++ b/packages/create-astro/src/messages.ts @@ -1,11 +1,11 @@ /* eslint no-console: 'off' */ import { color, say as houston, label, spinner as load } from '@astrojs/cli-kit'; import { align, sleep } from '@astrojs/cli-kit/utils'; -import { execa } from 'execa'; import fetch from 'node-fetch-native'; import { exec } from 'node:child_process'; import stripAnsi from 'strip-ansi'; import detectPackageManager from 'which-pm-runs'; +import { shell } from './shell.js'; // Users might lack access to the global npm registry, this function // checks the user's project type and will return the proper npm registry @@ -14,7 +14,7 @@ import detectPackageManager from 'which-pm-runs'; async function getRegistry(): Promise<string> { const packageManager = detectPackageManager()?.name || 'npm'; try { - const { stdout } = await execa(packageManager, ['config', 'get', 'registry']); + const { stdout } = await shell(packageManager, ['config', 'get', 'registry']); return stdout?.trim()?.replace(/\/$/, '') || 'https://registry.npmjs.org'; } catch (e) { return 'https://registry.npmjs.org'; diff --git a/packages/create-astro/src/shell.ts b/packages/create-astro/src/shell.ts new file mode 100644 index 000000000..d2d7ef033 --- /dev/null +++ b/packages/create-astro/src/shell.ts @@ -0,0 +1,49 @@ +// This is an extremely simplified version of [`execa`](https://github.com/sindresorhus/execa) +// intended to keep our dependency size down +import type { StdioOptions } from 'node:child_process'; +import type { Readable } from 'node:stream'; + +import { spawn } from 'node:child_process'; +import { text as textFromStream } from 'node:stream/consumers'; +import { setTimeout as sleep } from 'node:timers/promises'; + +export interface ExecaOptions { + cwd?: string | URL; + stdio?: StdioOptions; + timeout?: number; +} +export interface Output { + stdout: string; + stderr: string; + exitCode: number; +} +const text = (stream: NodeJS.ReadableStream | Readable | null) => + stream ? textFromStream(stream).then((t) => t.trimEnd()) : ''; + +export async function shell( + command: string, + flags: string[], + opts: ExecaOptions = {} +): Promise<Output> { + const controller = opts.timeout ? new AbortController() : undefined; + const child = spawn(command, flags, { + cwd: opts.cwd, + shell: true, + stdio: opts.stdio, + signal: controller?.signal, + }); + const stdout = await text(child.stdout); + const stderr = await text(child.stderr); + if (opts.timeout) { + sleep(opts.timeout).then(() => { + controller!.abort(); + throw { stdout, stderr, exitCode: 1 }; + }); + } + await new Promise((resolve) => child.on('exit', resolve)); + const { exitCode } = child; + if (exitCode !== 0) { + throw { stdout, stderr, exitCode }; + } + return { stdout, stderr, exitCode }; +} |