summaryrefslogtreecommitdiff
path: root/packages/create-astro/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/create-astro/src')
-rw-r--r--packages/create-astro/src/actions/dependencies.ts18
-rw-r--r--packages/create-astro/src/actions/git.ts8
-rw-r--r--packages/create-astro/src/messages.ts4
-rw-r--r--packages/create-astro/src/shell.ts49
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 };
+}