summaryrefslogtreecommitdiff
path: root/packages/create-astro
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@skypack.dev> 2023-08-17 08:54:28 -0400
committerGravatar Matthew Phillips <matthew@skypack.dev> 2023-08-17 08:54:28 -0400
commitcbb77af978bd0dcee08ad2dcadadb032abc44dc1 (patch)
tree94b7f35fd4214bbcdb1d36393583c5332bc5ff24 /packages/create-astro
parent2484dc4080e5cd84b9a53648a1de426d7c907be2 (diff)
parentd6b4943764989c0e89df2d6875cd19691566dfb3 (diff)
downloadastro-cbb77af978bd0dcee08ad2dcadadb032abc44dc1.tar.gz
astro-cbb77af978bd0dcee08ad2dcadadb032abc44dc1.tar.zst
astro-cbb77af978bd0dcee08ad2dcadadb032abc44dc1.zip
Merge branch 'main' into next
Diffstat (limited to 'packages/create-astro')
-rw-r--r--packages/create-astro/CHANGELOG.md12
-rw-r--r--packages/create-astro/src/actions/next-steps.ts7
-rw-r--r--packages/create-astro/src/actions/template.ts2
-rw-r--r--packages/create-astro/src/actions/verify.ts93
-rw-r--r--packages/create-astro/src/index.ts3
-rw-r--r--packages/create-astro/src/messages.ts7
-rw-r--r--packages/create-astro/src/shell.ts36
-rw-r--r--packages/create-astro/test/verify.test.js41
8 files changed, 180 insertions, 21 deletions
diff --git a/packages/create-astro/CHANGELOG.md b/packages/create-astro/CHANGELOG.md
index 519133c71..be4257b58 100644
--- a/packages/create-astro/CHANGELOG.md
+++ b/packages/create-astro/CHANGELOG.md
@@ -6,6 +6,18 @@
- [`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 16. The lowest supported version by Astro and all integrations is now v18.14.1. As a reminder, Node 16 will be deprecated on the 11th September 2023.
+## 3.2.1
+
+### Patch Changes
+
+- [#8089](https://github.com/withastro/astro/pull/8089) [`04755e846`](https://github.com/withastro/astro/commit/04755e84658ea10914a09f3d07f302267326d610) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix install step to avoid uncaught errors
+
+## 3.2.0
+
+### Minor Changes
+
+- [#8077](https://github.com/withastro/astro/pull/8077) [`44cf30a25`](https://github.com/withastro/astro/commit/44cf30a25209b331e6e8a95a4b40a768ede3604a) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Reduce dependency installation size, swap `execa` for light `node:child_process` wrapper
+
## 3.1.13
### Patch Changes
diff --git a/packages/create-astro/src/actions/next-steps.ts b/packages/create-astro/src/actions/next-steps.ts
index 1b3a0db28..01c1963d9 100644
--- a/packages/create-astro/src/actions/next-steps.ts
+++ b/packages/create-astro/src/actions/next-steps.ts
@@ -5,7 +5,12 @@ import { nextSteps, say } from '../messages.js';
export async function next(ctx: Pick<Context, 'cwd' | 'pkgManager' | 'skipHouston'>) {
let projectDir = path.relative(process.cwd(), ctx.cwd);
- const devCmd = ctx.pkgManager === 'npm' ? 'npm run dev' : `${ctx.pkgManager} dev`;
+ const devCmd =
+ ctx.pkgManager === 'npm'
+ ? 'npm run dev'
+ : ctx.pkgManager === 'bun'
+ ? 'bun run dev'
+ : `${ctx.pkgManager} dev`;
await nextSteps({ projectDir, devCmd });
if (!ctx.skipHouston) {
diff --git a/packages/create-astro/src/actions/template.ts b/packages/create-astro/src/actions/template.ts
index ca041642b..887ba69f5 100644
--- a/packages/create-astro/src/actions/template.ts
+++ b/packages/create-astro/src/actions/template.ts
@@ -66,7 +66,7 @@ const FILES_TO_UPDATE = {
}),
};
-function getTemplateTarget(tmpl: string, ref = 'latest') {
+export function getTemplateTarget(tmpl: string, ref = 'latest') {
if (tmpl.startsWith('starlight')) {
const [, starter = 'basics'] = tmpl.split('/');
return `withastro/starlight/examples/${starter}`;
diff --git a/packages/create-astro/src/actions/verify.ts b/packages/create-astro/src/actions/verify.ts
new file mode 100644
index 000000000..73359142b
--- /dev/null
+++ b/packages/create-astro/src/actions/verify.ts
@@ -0,0 +1,93 @@
+import type { Context } from './context';
+
+import { color } from '@astrojs/cli-kit';
+import fetch from 'node-fetch-native';
+import dns from 'node:dns/promises';
+import { bannerAbort, error, info, log } from '../messages.js';
+import { getTemplateTarget } from './template.js';
+
+export async function verify(
+ ctx: Pick<Context, 'version' | 'dryRun' | 'template' | 'ref' | 'exit'>
+) {
+ if (!ctx.dryRun) {
+ const online = await isOnline();
+ if (!online) {
+ bannerAbort();
+ log('');
+ error('error', `Unable to connect to the internet.`);
+ ctx.exit(1);
+ }
+ }
+
+ if (ctx.template) {
+ const ok = await verifyTemplate(ctx.template, ctx.ref);
+ if (!ok) {
+ bannerAbort();
+ log('');
+ error('error', `Template ${color.reset(ctx.template)} ${color.dim('could not be found!')}`);
+ await info('check', 'https://astro.build/examples');
+ ctx.exit(1);
+ }
+ }
+}
+
+function isOnline(): Promise<boolean> {
+ return dns.lookup('github.com').then(
+ () => true,
+ () => false
+ );
+}
+
+async function verifyTemplate(tmpl: string, ref?: string) {
+ const target = getTemplateTarget(tmpl, ref);
+ const { repo, subdir, ref: branch } = parseGitURI(target.replace('github:', ''));
+ const url = new URL(`/repos/${repo}/contents${subdir}?ref=${branch}`, 'https://api.github.com/');
+
+ let res = await fetch(url.toString(), {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ 'X-GitHub-Api-Version': '2022-11-28',
+ },
+ });
+
+ // If users hit a ratelimit, fallback to the GitHub website
+ if (res.status === 403) {
+ res = await fetch(`https://github.com/${repo}/tree/${branch}${subdir}`);
+ }
+
+ return res.status === 200;
+}
+
+// Adapted from https://github.com/unjs/giget/blob/main/src/_utils.ts
+// MIT License
+
+// Copyright (c) Pooya Parsa <pooya@pi0.io>
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+const GIT_RE = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w.-]+)?/;
+
+function parseGitURI(input: string) {
+ const m = input.match(GIT_RE)?.groups;
+ if (!m) throw new Error(`Unable to parse "${input}"`);
+ return {
+ repo: m.repo,
+ subdir: m.subdir || '/',
+ ref: m.ref ? m.ref.slice(1) : 'main',
+ };
+}
diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts
index b99aa1cc3..a33e11bff 100644
--- a/packages/create-astro/src/index.ts
+++ b/packages/create-astro/src/index.ts
@@ -8,6 +8,7 @@ import { next } from './actions/next-steps.js';
import { projectName } from './actions/project-name.js';
import { template } from './actions/template.js';
import { setupTypeScript, typescript } from './actions/typescript.js';
+import { verify } from './actions/verify.js';
import { setStdout } from './messages.js';
const exit = () => process.exit(0);
@@ -30,6 +31,7 @@ export async function main() {
}
const steps = [
+ verify,
intro,
projectName,
template,
@@ -58,4 +60,5 @@ export {
setupTypeScript,
template,
typescript,
+ verify,
};
diff --git a/packages/create-astro/src/messages.ts b/packages/create-astro/src/messages.ts
index 89ccddcdb..fbf276794 100644
--- a/packages/create-astro/src/messages.ts
+++ b/packages/create-astro/src/messages.ts
@@ -93,11 +93,14 @@ export const getVersion = () =>
export const log = (message: string) => stdout.write(message + '\n');
export const banner = async (version: string) =>
log(
- `\n${label('astro', color.bgGreen, color.black)} ${
- version ? color.green(color.bold(`v${version}`)) : ''
+ `\n${label('astro', color.bgGreen, color.black)}${
+ version ? ' ' + color.green(color.bold(`v${version}`)) : ''
} ${color.bold('Launch sequence initiated.')}`
);
+export const bannerAbort = () =>
+ log(`\n${label('astro', color.bgRed)} ${color.bold('Launch sequence aborted.')}`);
+
export const info = async (prefix: string, text: string) => {
await sleep(100);
if (stdout.columns < 80) {
diff --git a/packages/create-astro/src/shell.ts b/packages/create-astro/src/shell.ts
index d2d7ef033..e6f1295ea 100644
--- a/packages/create-astro/src/shell.ts
+++ b/packages/create-astro/src/shell.ts
@@ -1,11 +1,10 @@
// 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 { ChildProcess, 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;
@@ -25,25 +24,28 @@ export async function shell(
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 };
+ let child: ChildProcess;
+ let stdout = '';
+ let stderr = '';
+ try {
+ child = spawn(command, flags, {
+ cwd: opts.cwd,
+ shell: true,
+ stdio: opts.stdio,
+ timeout: opts.timeout,
});
+ const done = new Promise((resolve) => child.on('close', resolve));
+ [stdout, stderr] = await Promise.all([text(child.stdout), text(child.stderr)]);
+ await done;
+ } catch (e) {
+ throw { stdout, stderr, exitCode: 1 };
}
- await new Promise((resolve) => child.on('exit', resolve));
const { exitCode } = child;
+ if (exitCode === null) {
+ throw new Error('Timeout');
+ }
if (exitCode !== 0) {
- throw { stdout, stderr, exitCode };
+ throw new Error(stderr);
}
return { stdout, stderr, exitCode };
}
diff --git a/packages/create-astro/test/verify.test.js b/packages/create-astro/test/verify.test.js
new file mode 100644
index 000000000..ecfaba727
--- /dev/null
+++ b/packages/create-astro/test/verify.test.js
@@ -0,0 +1,41 @@
+import { expect } from 'chai';
+
+import { verify } from '../dist/index.js';
+import { setup } from './utils.js';
+
+describe('verify', () => {
+ const fixture = setup();
+ const exit = (code) => {
+ throw code;
+ };
+
+ it('basics', async () => {
+ const context = { template: 'basics', exit };
+ await verify(context);
+ expect(fixture.messages().length).to.equal(0, 'Did not expect `verify` to log any messages');
+ });
+
+ it('missing', async () => {
+ const context = { template: 'missing', exit };
+ let err = null;
+ try {
+ await verify(context);
+ } catch (e) {
+ err = e;
+ }
+ expect(err).to.eq(1);
+ expect(fixture.hasMessage('Template missing does not exist!'));
+ });
+
+ it('starlight', async () => {
+ const context = { template: 'starlight', exit };
+ await verify(context);
+ expect(fixture.messages().length).to.equal(0, 'Did not expect `verify` to log any messages');
+ });
+
+ it('starlight/tailwind', async () => {
+ const context = { template: 'starlight/tailwind', exit };
+ await verify(context);
+ expect(fixture.messages().length).to.equal(0, 'Did not expect `verify` to log any messages');
+ });
+});