diff options
author | 2025-06-05 14:25:23 +0000 | |
---|---|---|
committer | 2025-06-05 14:25:23 +0000 | |
commit | e586d7d704d475afe3373a1de6ae20d504f79d6d (patch) | |
tree | 7e3fa24807cebd48a86bd40f866d792181191ee9 /packages/create-astro | |
download | astro-latest.tar.gz astro-latest.tar.zst astro-latest.zip |
Sync from a8e1c0a7402940e0fc5beef669522b315052df1blatest
Diffstat (limited to 'packages/create-astro')
33 files changed, 2686 insertions, 0 deletions
diff --git a/packages/create-astro/CHANGELOG.md b/packages/create-astro/CHANGELOG.md new file mode 100644 index 000000000..9b307df4b --- /dev/null +++ b/packages/create-astro/CHANGELOG.md @@ -0,0 +1,838 @@ +# create-astro + +## 4.12.1 + +### Patch Changes + +- [#12529](https://github.com/withastro/astro/pull/12529) [`485c2f0`](https://github.com/withastro/astro/commit/485c2f0f1617a79ed997970c2cc439dde89e9be2) Thanks [@apatel369](https://github.com/apatel369)! - Fixes an issue where installing Astro beta using `create-astro` displays the wrong Astro version in the installation messages. + +## 4.12.0 + +### Minor Changes + +- [#13809](https://github.com/withastro/astro/pull/13809) [`3c3b492`](https://github.com/withastro/astro/commit/3c3b492375bd6a63f1fb6cede3685aff999be3c9) Thanks [@ascorbic](https://github.com/ascorbic)! - Increases minimum Node.js version to 18.20.8 + + Node.js 18 has now reached end-of-life and should not be used. For now, Astro will continue to support Node.js 18.20.8, which is the final LTS release of Node.js 18, as well as Node.js 20 and Node.js 22 or later. We will drop support for Node.js 18 in a future release, so we recommend upgrading to Node.js 22 as soon as possible. See Astro's [Node.js support policy](https://docs.astro.build/en/upgrade-astro/#support) for more details. + + :warning: **Important note for users of Cloudflare Pages**: The current build image for Cloudflare Pages uses Node.js 18.17.1 by default, which is no longer supported by Astro. If you are using Cloudflare Pages you should [override the default Node.js version](https://developers.cloudflare.com/pages/configuration/build-image/#override-default-versions) to Node.js 22. This does not affect users of Cloudflare Workers, which uses Node.js 22 by default. + +## 4.11.4 + +### Patch Changes + +- [#13703](https://github.com/withastro/astro/pull/13703) [`659904b`](https://github.com/withastro/astro/commit/659904bd999c6abdd62f18230954b7097dcbb7fe) Thanks [@ascorbic](https://github.com/ascorbic)! - Fixes an issue when installing dependencies with `pnpm`. + +## 4.11.3 + +### Patch Changes + +- [#13591](https://github.com/withastro/astro/pull/13591) [`5dd2d3f`](https://github.com/withastro/astro/commit/5dd2d3fde8a138ed611dedf39ffa5dfeeed315f8) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Removes unused code + +## 4.11.2 + +### Patch Changes + +- [#13636](https://github.com/withastro/astro/pull/13636) [`a9fd03f`](https://github.com/withastro/astro/commit/a9fd03fd82f2ea09c7bec9fd06ef20f5612e0a56) Thanks [@sarah11918](https://github.com/sarah11918)! - Updates the wording for choosing the "minimal" starter template options + +## 4.11.1 + +### Patch Changes + +- [#13257](https://github.com/withastro/astro/pull/13257) [`6012e06`](https://github.com/withastro/astro/commit/6012e061cdd07688be73d5d543ad3a055c373475) Thanks [@ADTC](https://github.com/ADTC)! - Modify the template README file to reflect the correct package manager + +## 4.11.0 + +### Minor Changes + +- [#12539](https://github.com/withastro/astro/pull/12539) [`827093e`](https://github.com/withastro/astro/commit/827093e6175549771f9d93ddf3f2be4c2c60f0b7) Thanks [@bluwy](https://github.com/bluwy)! - Drops node 21 support + +- [#12083](https://github.com/withastro/astro/pull/12083) [`9263e96`](https://github.com/withastro/astro/commit/9263e965932b9a6a116801c063c6b7105c39643e) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Reworks the experience of creating a new Astro project using the `create astro` CLI command. + + - Updates the list of templates to include Starlight and combines the "minimal" and "basics" templates into a new, refreshed "Basics" template to serve as the single, minimal Astro project starter. + - Removes the TypeScript question. Astro is TypeScript-only, so this question was often misleading. The "Strict" preset is now the default, but it can still be changed manually in `tsconfig.json`. + - `astro check` is no longer automatically added to the build script. + - Added a new `--add` flag to install additional integrations after creating a project. For example, `pnpm create astro --add react` will create a new Astro project and install the React integration. + +## 4.11.0-beta.1 + +### Minor Changes + +- [#12539](https://github.com/withastro/astro/pull/12539) [`827093e`](https://github.com/withastro/astro/commit/827093e6175549771f9d93ddf3f2be4c2c60f0b7) Thanks [@bluwy](https://github.com/bluwy)! - Drops node 21 support + +## 4.11.0-beta.0 + +### Minor Changes + +- [#12083](https://github.com/withastro/astro/pull/12083) [`9263e96`](https://github.com/withastro/astro/commit/9263e965932b9a6a116801c063c6b7105c39643e) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Reworks the experience of creating a new Astro project using the `create astro` CLI command. + + - Updates the list of templates to include Starlight and combines the "minimal" and "basics" templates into a new, refreshed "Basics" template to serve as the single, minimal Astro project starter. + - Removes the TypeScript question. Astro is TypeScript-only, so this question was often misleading. The "Strict" preset is now the default, but it can still be changed manually in `tsconfig.json`. + - `astro check` is no longer automatically added to the build script. + - Added a new `--add` flag to install additional integrations after creating a project. For example, `pnpm create astro --add react` will create a new Astro project and install the React integration. + +## 4.10.0 + +### Minor Changes + +- [#12154](https://github.com/withastro/astro/pull/12154) [`9988dd6`](https://github.com/withastro/astro/commit/9988dd67e2e4647c974979470d2e63d80433b611) Thanks [@bluwy](https://github.com/bluwy)! - Improves default template download speed by downloading from a branch containing the template only + +- [#12186](https://github.com/withastro/astro/pull/12186) [`49c4f64`](https://github.com/withastro/astro/commit/49c4f64673390f7035258d662755988f0fb71a94) Thanks [@Terfno](https://github.com/Terfno)! - Ensures new line at the end of the generated `package.json` and `tsconfig.json` files + +## 4.9.2 + +### Patch Changes + +- [#12143](https://github.com/withastro/astro/pull/12143) [`2385d58`](https://github.com/withastro/astro/commit/2385d58389ee975a53f4089f2a7220d97cf3cdff) Thanks [@bluwy](https://github.com/bluwy)! - Uses `@bluwy/giget-core` instead of `giget` for smaller installation size when downloading the CLI + +## 4.9.1 + +### Patch Changes + +- [#12118](https://github.com/withastro/astro/pull/12118) [`f47b347`](https://github.com/withastro/astro/commit/f47b347da899c6e1dcd0b2e7887f7fce6ec8e270) Thanks [@Namchee](https://github.com/Namchee)! - Removes the `strip-ansi` dependency in favor of the native Node API + +## 4.9.0 + +### Minor Changes + +- [#11924](https://github.com/withastro/astro/pull/11924) [`7d70ba3`](https://github.com/withastro/astro/commit/7d70ba317889b9281c7891038779a68fcb8f0778) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Updates the default Astro config with `// @ts-check` if the Typescript preset is `strict` or `strictest` + +## 4.8.4 + +### Patch Changes + +- [#11766](https://github.com/withastro/astro/pull/11766) [`d12dcbf`](https://github.com/withastro/astro/commit/d12dcbff606dd8330075ba77d73ed3cbc79d7421) Thanks [@bluwy](https://github.com/bluwy)! - Fixes initial git commit when initializing git + +## 4.8.3 + +### Patch Changes + +- [#11733](https://github.com/withastro/astro/pull/11733) [`391324d`](https://github.com/withastro/astro/commit/391324df969db71d1c7ca25c2ed14c9eb6eea5ee) Thanks [@bluwy](https://github.com/bluwy)! - Reverts back to `arg` package for CLI argument parsing + +## 4.8.2 + +### Patch Changes + +- [#11645](https://github.com/withastro/astro/pull/11645) [`849e4c6`](https://github.com/withastro/astro/commit/849e4c6c23e61f7fa59f583419048b998bef2475) Thanks [@bluwy](https://github.com/bluwy)! - Refactors internally to use `node:util` `parseArgs` instead of `arg` + +## 4.8.1 + +### Patch Changes + +- [#11567](https://github.com/withastro/astro/pull/11567) [`d27cf6d`](https://github.com/withastro/astro/commit/d27cf6df7bd612642a1e8da5948333b00b70e8bd) Thanks [@ascorbic](https://github.com/ascorbic)! - Logs underlying error when a template cannot be downloaded + +## 4.8.0 + +### Minor Changes + +- [#10689](https://github.com/withastro/astro/pull/10689) [`683d51a5eecafbbfbfed3910a3f1fbf0b3531b99`](https://github.com/withastro/astro/commit/683d51a5eecafbbfbfed3910a3f1fbf0b3531b99) Thanks [@ematipico](https://github.com/ematipico)! - Deprecate support for versions of Node.js older than `v18.17.1` for Node.js 18, older than `v20.0.3` for Node.js 20, and the complete Node.js v19 release line. + + This change is in line with Astro's [Node.js support policy](https://docs.astro.build/en/upgrade-astro/#support). + +## 4.7.5 + +### Patch Changes + +- [#10487](https://github.com/withastro/astro/pull/10487) [`2330f22d6cf8cd150c19ec40359aed4d6b43ddec`](https://github.com/withastro/astro/commit/2330f22d6cf8cd150c19ec40359aed4d6b43ddec) Thanks [@satyarohith](https://github.com/satyarohith)! - Fixes a case where a promise wasn't awaited, causing an issue in Deno. + +## 4.7.4 + +### Patch Changes + +- [#10255](https://github.com/withastro/astro/pull/10255) [`2aec2cdc21f48f9b4f1dd82e2fd16fa3d653ccc5`](https://github.com/withastro/astro/commit/2aec2cdc21f48f9b4f1dd82e2fd16fa3d653ccc5) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fixes an issue where TypeScript and `@astrojs/check` versions would occasionally print as `undefined`. + +## 4.7.3 + +### Patch Changes + +- [#10117](https://github.com/withastro/astro/pull/10117) [`51b6ff7403c1223b1c399e88373075972c82c24c`](https://github.com/withastro/astro/commit/51b6ff7403c1223b1c399e88373075972c82c24c) Thanks [@hippotastic](https://github.com/hippotastic)! - Fixes an issue where `create astro`, `astro add` and `@astrojs/upgrade` would fail due to unexpected package manager CLI output. + +## 4.7.2 + +### Patch Changes + +- [#9813](https://github.com/withastro/astro/pull/9813) [`fecba30a1abb7ca65dfb8f506dde77117fa447d1`](https://github.com/withastro/astro/commit/fecba30a1abb7ca65dfb8f506dde77117fa447d1) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Fixes `@astrojs/check` and `typescript` addition to `package.json` dependencies when the user has decided not to auto-install dependencies + +## 4.7.1 + +### Patch Changes + +- [#9476](https://github.com/withastro/astro/pull/9476) [`651f45b4010ad9b8d9f61fdc748618e220fe5375`](https://github.com/withastro/astro/commit/651f45b4010ad9b8d9f61fdc748618e220fe5375) Thanks [@ElianCodes](https://github.com/ElianCodes)! - Improves seasonal message handling by automatically detecting the local date + +## 4.7.0 + +### Minor Changes + +- [#9470](https://github.com/withastro/astro/pull/9470) [`607303be198931825dac9f3bc97867b4886feaf3`](https://github.com/withastro/astro/commit/607303be198931825dac9f3bc97867b4886feaf3) Thanks [@onsclom](https://github.com/onsclom)! - Improves the `create astro` CLI experience by asking all the questions upfront, then creating your new Astro project based on your responses. + +## 4.6.0 + +### Minor Changes + +- [#9358](https://github.com/withastro/astro/pull/9358) [`35e4c17fe`](https://github.com/withastro/astro/commit/35e4c17fe245179dd88af679a5f0a08785b7bfef) Thanks [@xiBread](https://github.com/xiBread)! - feat: make Houston festive for the holiday season + +## 4.5.2 + +### Patch Changes + +- [#9105](https://github.com/withastro/astro/pull/9105) [`6201bbe96`](https://github.com/withastro/astro/commit/6201bbe96c2a083fb201e4a43a9bd88499821a3e) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Stop clearing the console on start + +## 4.5.2-beta.0 + +### Patch Changes + +- [#9105](https://github.com/withastro/astro/pull/9105) [`6201bbe96`](https://github.com/withastro/astro/commit/6201bbe96c2a083fb201e4a43a9bd88499821a3e) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Stop clearing the console on start + +## 4.5.1 + +### Patch Changes + +- [#9048](https://github.com/withastro/astro/pull/9048) [`1e97708cd`](https://github.com/withastro/astro/commit/1e97708cda779510d638abaefdb4abf707b697e3) Thanks [@skirianov](https://github.com/skirianov)! - Fixes an issue where a successful "Dependencies installed" message is displayed even when installing dependencies fails. + +## 4.5.0 + +### Minor Changes + +- [#8959](https://github.com/withastro/astro/pull/8959) [`6169b6e56`](https://github.com/withastro/astro/commit/6169b6e56152b1ba944416d85551994788cfb887) Thanks [@ElianCodes](https://github.com/ElianCodes)! - Undo the halloween theme and restore `fancy` behaviour + +### Patch Changes + +- [#8939](https://github.com/withastro/astro/pull/8939) [`71455c16c`](https://github.com/withastro/astro/commit/71455c16c371aaca4b5a551b713a77c6820efd78) Thanks [@wktk](https://github.com/wktk)! - Fixes TypeScript installation issue with yarn + +## 4.4.1 + +### Patch Changes + +- [#8911](https://github.com/withastro/astro/pull/8911) [`b236d88ad`](https://github.com/withastro/astro/commit/b236d88addc48d784bd60119fe45750dda900f16) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Ensure an existing template's `package.json` `scripts` are respected when modifying `build`. + +## 4.4.0 + +### Minor Changes + +- [#8853](https://github.com/withastro/astro/pull/8853) [`ce807a2bf`](https://github.com/withastro/astro/commit/ce807a2bfef325683bfdb01065a73c4e2b0a5fe5) Thanks [@rayriffy](https://github.com/rayriffy)! - Automatically installs the required dependencies to run the astro check command when the user indicates they plan to write TypeScript. + +### Patch Changes + +- [#8841](https://github.com/withastro/astro/pull/8841) [`f2dd895d7`](https://github.com/withastro/astro/commit/f2dd895d71e0fccfbc1b98890ceefb69f32524d5) Thanks [@Genteure](https://github.com/Genteure)! - No longer attempts to delete the directory after a template download fails if the path is `.`, `./` or starts with `../`. + +## 4.3.0 + +### Minor Changes + +- [#8846](https://github.com/withastro/astro/pull/8846) [`3baab3d93`](https://github.com/withastro/astro/commit/3baab3d93b8d16517cb089b0fa2c4028f21e780f) Thanks [@ElianCodes](https://github.com/ElianCodes)! - feat: make Houston wear scary hats and say new things for spooky season + +## 4.2.1 + +### Patch Changes + +- [#8634](https://github.com/withastro/astro/pull/8634) [`b64dd45c0`](https://github.com/withastro/astro/commit/b64dd45c0d641f9f2ed997e2cbdf8a6b0193195f) Thanks [@TheOtterlord](https://github.com/TheOtterlord)! - Fix `--yes` behaviour to prevent it overriding `--template` + +## 4.2.0 + +### Minor Changes + +- [#8551](https://github.com/withastro/astro/pull/8551) [`1d5b3f079`](https://github.com/withastro/astro/commit/1d5b3f079d0b4aa5a5c46f97b8b724ab88497fbe) Thanks [@jacobthesheep](https://github.com/jacobthesheep)! - Adds `--yes` and `dry-run` flags to project-name and the `yes` flag to template. + +## 4.1.0 + +### Minor Changes + +- [#8456](https://github.com/withastro/astro/pull/8456) [`ed952b4ce`](https://github.com/withastro/astro/commit/ed952b4cea6f60a4e158a5b20cc36f5e91a6b07f) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve startup performance by removing dependencies, lazily initializing async contextual values + +## 4.0.2 + +### Patch Changes + +- [#8427](https://github.com/withastro/astro/pull/8427) [`b81ff8fce`](https://github.com/withastro/astro/commit/b81ff8fcefe6c30312d7b2050a63b1520d79b25f) Thanks [@aswind7](https://github.com/aswind7)! - trim project name of the user input + +- [#8306](https://github.com/withastro/astro/pull/8306) [`d2f2a11cd`](https://github.com/withastro/astro/commit/d2f2a11cdb42b0de79be21c798eda8e7e7b2a277) Thanks [@jacobthesheep](https://github.com/jacobthesheep)! - Support detecting Bun when logging messages with package manager information. + +## 4.0.1 + +### Patch Changes + +- [#8292](https://github.com/withastro/astro/pull/8292) [`4e88ffd81`](https://github.com/withastro/astro/commit/4e88ffd813a3a9fa37b2ddd1a2eff181d4a99c0f) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Correctly remove new `.codesandbox` folder when copying template + +## 4.0.0 + +### Major Changes + +- [#8188](https://github.com/withastro/astro/pull/8188) [`d0679a666`](https://github.com/withastro/astro/commit/d0679a666f37da0fca396d42b9b32bbb25d29312) Thanks [@ematipico](https://github.com/ematipico)! - 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. + +- [#8179](https://github.com/withastro/astro/pull/8179) [`6011d52d3`](https://github.com/withastro/astro/commit/6011d52d38e43c3e3d52bc3bc41a60e36061b7b7) Thanks [@matthewp](https://github.com/matthewp)! - Astro 3.0 Release Candidate + +## 4.0.0-rc.2 + +### Major Changes + +- [#8179](https://github.com/withastro/astro/pull/8179) [`6011d52d3`](https://github.com/withastro/astro/commit/6011d52d38e43c3e3d52bc3bc41a60e36061b7b7) Thanks [@matthewp](https://github.com/matthewp)! - Astro 3.0 Release Candidate + +## 4.0.0-beta.1 + +### Patch Changes + +- [#7944](https://github.com/withastro/astro/pull/7944) [`dff0f0f8d`](https://github.com/withastro/astro/commit/dff0f0f8ddd531c5d92a90ac00fdb86d71f77509) Thanks [@colinhacks](https://github.com/colinhacks)! - Update 'dev' command for Bun users + +- [#8102](https://github.com/withastro/astro/pull/8102) [`e6e1de4f0`](https://github.com/withastro/astro/commit/e6e1de4f08ddba3a7703136a81f275de1976dc9e) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Verify internet connection and that `--template` exists before continuing + +## 4.0.0-beta.0 + +### Major Changes + +- [`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.2 + +### Patch Changes + +- [#7944](https://github.com/withastro/astro/pull/7944) [`dff0f0f8d`](https://github.com/withastro/astro/commit/dff0f0f8ddd531c5d92a90ac00fdb86d71f77509) Thanks [@colinhacks](https://github.com/colinhacks)! - Update 'dev' command for Bun users + +- [#8102](https://github.com/withastro/astro/pull/8102) [`e6e1de4f0`](https://github.com/withastro/astro/commit/e6e1de4f08ddba3a7703136a81f275de1976dc9e) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Verify internet connection and that `--template` exists before continuing + +## 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 + +- [#8028](https://github.com/withastro/astro/pull/8028) [`8292c4131`](https://github.com/withastro/astro/commit/8292c41311ec41d9d50921fbb2bdeed69e039443) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve yarn berry support + +## 3.1.12 + +### Patch Changes + +- [#7993](https://github.com/withastro/astro/pull/7993) [`315d58f27`](https://github.com/withastro/astro/commit/315d58f27b022c9d4285cf13f445ed18c26c327e) Thanks [@delucis](https://github.com/delucis)! - Add support for more Starlight templates + +## 3.1.11 + +### Patch Changes + +- [#7939](https://github.com/withastro/astro/pull/7939) [`89cd4b877`](https://github.com/withastro/astro/commit/89cd4b877e870ce4a263dd45f42f818fd2c4d5a6) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Handle error state for version number + +## 3.1.10 + +### Patch Changes + +- [#7580](https://github.com/withastro/astro/pull/7580) [`2ca5bdde2`](https://github.com/withastro/astro/commit/2ca5bdde2b1acc2be1586a99686a9a48cdef65dc) Thanks [@sankethchebbi](https://github.com/sankethchebbi)! - Update dependency installation grammar + +## 3.1.9 + +### Patch Changes + +- [#7527](https://github.com/withastro/astro/pull/7527) [`9e2426f75`](https://github.com/withastro/astro/commit/9e2426f75637a6318961f483de90b635f3fdadeb) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Default registry logic to fallback to NPM if registry command fails (sorry, Bun users!) + +- [#7539](https://github.com/withastro/astro/pull/7539) [`1170877b5`](https://github.com/withastro/astro/commit/1170877b51aaa13203e8c488dcf4e39d1b5553ee) Thanks [@jc1144096387](https://github.com/jc1144096387)! - Update registry logic, improving edge cases (http support, redirects, registries ending with '/') + +## 3.1.8 + +### Patch Changes + +- [#7435](https://github.com/withastro/astro/pull/7435) [`3f9f5c117`](https://github.com/withastro/astro/commit/3f9f5c117e4e9e4a0c0a648cb6db9a3073cd5727) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Fix registry failures using unexpected package managers when running create-astro + +## 3.1.7 + +### Patch Changes + +- [#7326](https://github.com/withastro/astro/pull/7326) [`1430ffb47`](https://github.com/withastro/astro/commit/1430ffb4734edbb67cbeaaee7e89a9f78e00473c) Thanks [@calebdwilliams](https://github.com/calebdwilliams)! - Ensure create-astro respects package manager registry configuration + +## 3.1.6 + +### Patch Changes + +- [#7277](https://github.com/withastro/astro/pull/7277) [`229affca4`](https://github.com/withastro/astro/commit/229affca405ce77bf80bcea6a91891f689a3161b) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Add `starlight` template alias + +## 3.1.5 + +### Patch Changes + +- [#7086](https://github.com/withastro/astro/pull/7086) [`c5f1275e9`](https://github.com/withastro/astro/commit/c5f1275e9d2f212a08e56bc25e0b59c7d7e9f11d) Thanks [@MoustaphaDev](https://github.com/MoustaphaDev)! - Fix create astro regression + +## 3.1.4 + +### Patch Changes + +- [#7052](https://github.com/withastro/astro/pull/7052) [`8c14bffbd`](https://github.com/withastro/astro/commit/8c14bffbd9ea63bc4b4e9f9417352fdf4e7e65b4) Thanks [@ematipico](https://github.com/ematipico)! - Don't exit if dependencies fail to install + +## 3.1.3 + +### Patch Changes + +- [#6682](https://github.com/withastro/astro/pull/6682) [`335602344`](https://github.com/withastro/astro/commit/33560234437647f2d768578e7b285c858bff7898) Thanks [@andremralves](https://github.com/andremralves)! - add validation for non-printable characters + +## 3.1.2 + +### Patch Changes + +- [#6677](https://github.com/withastro/astro/pull/6677) [`4a3262060`](https://github.com/withastro/astro/commit/4a32620600966ea89ddb5e1669d89a53e85ccf9a) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Fix: Log an error when passing a `--template` that does not exist + +## 3.1.1 + +### Patch Changes + +- [#6594](https://github.com/withastro/astro/pull/6594) [`a661907b4`](https://github.com/withastro/astro/commit/a661907b40e76aa56e7d7bd7e745bb16456b13e7) Thanks [@btea](https://github.com/btea)! - wrap `projectDir` in quotes if it contains spaces + +## 3.1.0 + +### Minor Changes + +- [#6213](https://github.com/withastro/astro/pull/6213) [`afbbc4d5b`](https://github.com/withastro/astro/commit/afbbc4d5bfafc1779bac00b41c2a1cb1c90f2808) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Updated compilation settings to disable downlevelling for Node 14 + +## 3.0.5 + +### Patch Changes + +- [#6375](https://github.com/withastro/astro/pull/6375) [`754c5ca9a`](https://github.com/withastro/astro/commit/754c5ca9aa93d4e8674059ce79f6b694c147db83) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Respect original `package.json` indentation + +## 3.0.4 + +### Patch Changes + +- [#6352](https://github.com/withastro/astro/pull/6352) [`c87c16cfa`](https://github.com/withastro/astro/commit/c87c16cfaddea3a05af87c3258d57ef1a31516f7) Thanks [@SerekKiri](https://github.com/SerekKiri)! - Add missing flags to help command + +## 3.0.3 + +### Patch Changes + +- [#6314](https://github.com/withastro/astro/pull/6314) [`7f61e8fe3`](https://github.com/withastro/astro/commit/7f61e8fe36b62a1833180c18b6f4304e9a01fce4) Thanks [@MilesPernicious](https://github.com/MilesPernicious)! - Prompt for git initialization last, so all configurations can get added to the initial commit + +- [#6294](https://github.com/withastro/astro/pull/6294) [`d0dbee872`](https://github.com/withastro/astro/commit/d0dbee872fd09800fba644ccbf4011ce01149706) Thanks [@liruifengv](https://github.com/liruifengv)! - `create-astro` help info add `--typescript` flag + +## 3.0.2 + +### Patch Changes + +- [#6278](https://github.com/withastro/astro/pull/6278) [`0f5d122cd`](https://github.com/withastro/astro/commit/0f5d122cd538b65ec7208ddae5e60cfaddaf4b2c) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Revert to giget 1.0.0 until upstream issue is fixed + +## 3.0.1 + +### Patch Changes + +- [#6266](https://github.com/withastro/astro/pull/6266) [`066b4b4ef`](https://github.com/withastro/astro/commit/066b4b4efcde2320d29040c5bd385c67f30c701a) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve error handling during tasks that display a spinner + +## 3.0.0 + +### Major Changes + +- [#6082](https://github.com/withastro/astro/pull/6082) [`8d2187d8b`](https://github.com/withastro/astro/commit/8d2187d8b8587b2a3a0207d9ffa8667c43686436) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Redesigned `create-astro` experience + +## 2.0.2 + +### Patch Changes + +- [#5953](https://github.com/withastro/astro/pull/5953) [`5c64324c0`](https://github.com/withastro/astro/commit/5c64324c0a1b06e836c3d53668940faca4cb517d) Thanks [@ZermattChris](https://github.com/ZermattChris)! - Check for a pre-existing .git directory and if found, skip trying to create a new one. + +## 2.0.1 + +### Patch Changes + +- [#5958](https://github.com/withastro/astro/pull/5958) [`d0d7f6118`](https://github.com/withastro/astro/commit/d0d7f6118299bf328de5abd0b66450d8ac620da3) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix typescript prompt handling + +## 2.0.0 + +### Major Changes + +- [#5782](https://github.com/withastro/astro/pull/5782) [`1f92d64ea`](https://github.com/withastro/astro/commit/1f92d64ea35c03fec43aff64eaf704dc5a9eb30a) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 14. Minimum supported Node version is now >=16.12.0 + +### Patch Changes + +- [#5898](https://github.com/withastro/astro/pull/5898) [`d8919b1a2`](https://github.com/withastro/astro/commit/d8919b1a2197616b70ec57f0fb00b0bde6943e43) Thanks [@TheOtterlord](https://github.com/TheOtterlord)! - Support headless runs with `-y` / `--yes` + +- [#5920](https://github.com/withastro/astro/pull/5920) [`f27bb3d79`](https://github.com/withastro/astro/commit/f27bb3d79f9774f01037e60e656b1f9d8e03367d) Thanks [@delucis](https://github.com/delucis)! - Improve error message for third-party template 404s + +## 2.0.0-beta.1 + +<details> +<summary>See changes in 2.0.0-beta.1</summary> + +### Patch Changes + +- [#5898](https://github.com/withastro/astro/pull/5898) [`d8919b1a2`](https://github.com/withastro/astro/commit/d8919b1a2197616b70ec57f0fb00b0bde6943e43) Thanks [@TheOtterlord](https://github.com/TheOtterlord)! - Support headless runs with `-y` / `--yes` + +- [#5920](https://github.com/withastro/astro/pull/5920) [`f27bb3d79`](https://github.com/withastro/astro/commit/f27bb3d79f9774f01037e60e656b1f9d8e03367d) Thanks [@delucis](https://github.com/delucis)! - Improve error message for third-party template 404s + +</details> + +## 2.0.0-beta.0 + +<details> +<summary>See changes in 2.0.0-beta.0</summary> + +### Major Changes + +- [#5782](https://github.com/withastro/astro/pull/5782) [`1f92d64ea`](https://github.com/withastro/astro/commit/1f92d64ea35c03fec43aff64eaf704dc5a9eb30a) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 14. Minimum supported Node version is now >=16.12.0 + +</details> + +## 1.2.4 + +### Patch Changes + +- [#5579](https://github.com/withastro/astro/pull/5579) [`2c2c65297`](https://github.com/withastro/astro/commit/2c2c65297a18c52691f09621ead55144efd601d4) Thanks [@yuhang-dong](https://github.com/yuhang-dong)! - Upgrade giget to support env proxy config + +- [#5616](https://github.com/withastro/astro/pull/5616) [`61302ab7a`](https://github.com/withastro/astro/commit/61302ab7a09cc4c298c903d725e35355eb069497) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Skip Houston on Windows until we can debug the prompt issue + +## 1.2.3 + +### Patch Changes + +- [#5404](https://github.com/withastro/astro/pull/5404) [`505abfd64`](https://github.com/withastro/astro/commit/505abfd6430b1f71e52d10b02bf9beb5847df8b6) Thanks [@liruifengv](https://github.com/liruifengv)! - fix error when don't have template input + +## 1.2.2 + +### Patch Changes + +- [#5319](https://github.com/withastro/astro/pull/5319) [`b211eadef`](https://github.com/withastro/astro/commit/b211eadeffd6260700254c1492c8e6528d279ad1) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix bug with `setRawMode`. Respect `--skip-houston` in all cases. + +## 1.2.1 + +### Patch Changes + +- [#5240](https://github.com/withastro/astro/pull/5240) [`d9be7e36b`](https://github.com/withastro/astro/commit/d9be7e36b872eb48516dc9d0d5c9d333aac4950b) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve error messages when `create-astro` fails + +- [#5226](https://github.com/withastro/astro/pull/5226) [`641b6d7d5`](https://github.com/withastro/astro/commit/641b6d7d583886fde9529f296846d7e0a50e8624) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Allow Windows users to pass `--fancy` to enable full unicode support + +## 1.2.0 + +### Minor Changes + +- [#5088](https://github.com/withastro/astro/pull/5088) [`040837628`](https://github.com/withastro/astro/commit/04083762810a1a9e078a7e68edab945c8063b1ab) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Introducing your new automated assistant: Houston! ๐ + + ``` + โญโโโโโโฎ Houston: + โ โ โก โ Initiating launch sequence... right... now! + โฐโโโโโโฏ + ``` + + Updates template and TypeScript prompts for clarity and friendliness. + + Migrates template copying from [`degit`](https://github.com/Rich-Harris/degit) (unmaintained) to [`giget`](https://github.com/unjs/giget) for stability. + +## 1.1.0 + +### Minor Changes + +- [#4810](https://github.com/withastro/astro/pull/4810) [`7481ffda0`](https://github.com/withastro/astro/commit/7481ffda028d9028d8e28bc7c6e9960ab80acf0f) Thanks [@mrienstra](https://github.com/mrienstra)! - Always write chosen config to `tsconfig.json`. + + - Before: Only when `strict` & `strictest` was selected + - After: Also when `base` is selected (via "Relaxed" or "I prefer not to use TypeScript") + +## 1.0.2 + +### Patch Changes + +- [#4805](https://github.com/withastro/astro/pull/4805) [`c84d85ba4`](https://github.com/withastro/astro/commit/c84d85ba4d85f250d87bbc98c74665992f6c2768) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Add support for running in cloned empty git repository + +## 1.0.1 + +### Patch Changes + +- [#4439](https://github.com/withastro/astro/pull/4439) [`77ce6be30`](https://github.com/withastro/astro/commit/77ce6be30c9cb8054ebf69a4943b984eed90152e) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Add tsconfig templates for users to extend from + +## 1.0.1-next.0 + +### Patch Changes + +- [#4439](https://github.com/withastro/astro/pull/4439) [`77ce6be30`](https://github.com/withastro/astro/commit/77ce6be30c9cb8054ebf69a4943b984eed90152e) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Add tsconfig templates for users to extend from + +## 1.0.0 + +### Major Changes + +- [`04ad44563`](https://github.com/withastro/astro/commit/04ad445632c67bdd60c1704e1e0dcbcaa27b9308) - > Astro v1.0 is out! Read the [official announcement post](https://astro.build/blog/astro-1/). + + **No breaking changes**. This package is now officially stable and compatible with `astro@1.0.0`! + +## 0.15.1 + +### Patch Changes + +- [#4183](https://github.com/withastro/astro/pull/4183) [`77c018e51`](https://github.com/withastro/astro/commit/77c018e5159e9084304ca650487b6e99c828d3cf) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Fix relaxed and default TypeScript settings not working + +## 0.15.0 + +### Minor Changes + +- [#4179](https://github.com/withastro/astro/pull/4179) [`d344f9e3e`](https://github.com/withastro/astro/commit/d344f9e3ec1f69ad4d7efd433b3523ad5413b726) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Add a step to configure how strict TypeScript should be + +## 0.14.3 + +### Patch Changes + +- [#4075](https://github.com/withastro/astro/pull/4075) [`cc10a5c8e`](https://github.com/withastro/astro/commit/cc10a5c8e03683e64514de75e535169c187ab847) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Added better error handling when cancelling operations, providing bad templates and when there's a degit cache issue + +## 0.14.2 + +### Patch Changes + +- [#3971](https://github.com/withastro/astro/pull/3971) [`e6e216061`](https://github.com/withastro/astro/commit/e6e2160614c9af320419a599c42211d0147760f4) Thanks [@tony-sull](https://github.com/tony-sull)! - Fixes support for using templates from any GitHub repository + +## 0.14.1 + +### Patch Changes + +- [#3937](https://github.com/withastro/astro/pull/3937) [`31f9c0bf0`](https://github.com/withastro/astro/commit/31f9c0bf029ffa4b470e620f2c32e1370643e81e) Thanks [@delucis](https://github.com/delucis)! - Roll back supported Node engines + +## 0.14.0 + +### Minor Changes + +- [#3914](https://github.com/withastro/astro/pull/3914) [`b48767985`](https://github.com/withastro/astro/commit/b48767985359bd359df8071324952ea5f2bc0d86) Thanks [@ran-dall](https://github.com/ran-dall)! - Rollback supported `node@16` version. Minimum versions are now `node@14.20.0` or `node@16.14.0`. + +## 0.13.0 + +### Minor Changes + +- [#3871](https://github.com/withastro/astro/pull/3871) [`1cc5b7890`](https://github.com/withastro/astro/commit/1cc5b78905633608e5b07ad291f916f54e67feb1) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Update supported `node` versions. Minimum versions are now `node@14.20.0` or `node@16.16.0`. + +### Patch Changes + +- [#3886](https://github.com/withastro/astro/pull/3886) [`cb6a97383`](https://github.com/withastro/astro/commit/cb6a973839450dea1705407e1060919c946cca99) Thanks [@QuiiBz](https://github.com/QuiiBz)! - Fix portfolio example JSX error + +## 0.12.5 + +### Patch Changes + +- [#3831](https://github.com/withastro/astro/pull/3831) [`4fb08502`](https://github.com/withastro/astro/commit/4fb08502a99396723b9eb671099482cd619b3564) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Small wording updates + +## 0.12.4 + +### Patch Changes + +- [#3756](https://github.com/withastro/astro/pull/3756) [`507cd5c8`](https://github.com/withastro/astro/commit/507cd5c868448971c6265d97f22e786263dd5a77) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Chore: remove create-astro install step test + +## 0.12.3 + +### Patch Changes + +- [#3748](https://github.com/withastro/astro/pull/3748) [`012f093e`](https://github.com/withastro/astro/commit/012f093eeb771b42b4e9d1e0cbb0d9a9605e0514) Thanks [@delucis](https://github.com/delucis)! - Remove `astro add` step & tweak wording (PR #3715) + +## 0.12.2 + +### Patch Changes + +- [#3391](https://github.com/withastro/astro/pull/3391) [`cf8015ea`](https://github.com/withastro/astro/commit/cf8015eaa2b756f4ec399e8fd7071dee7dfa9ab6) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix [#3309](https://github.com/withastro/astro/issues/3309) default logger locale behavior. + +## 0.12.1 + +### Patch Changes + +- [#3313](https://github.com/withastro/astro/pull/3313) [`1a5335ed`](https://github.com/withastro/astro/commit/1a5335ed9abaef397ee9543a3b4ad7a3fddcf024) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Update "next steps" with more informative text on each CLI command. Oh, and gradients. A lot more gradients. + +## 0.12.0 + +### Minor Changes + +- [#3227](https://github.com/withastro/astro/pull/3227) [`c8f5fa35`](https://github.com/withastro/astro/commit/c8f5fa35c4c3cf08df45e6bd6cb78960782ae08b) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Add "initialize git repository" step to simplify our next steps suggestion. We now give you a one-liner to easily paste in your terminal and start the dev server! + +## 0.11.0 + +### Minor Changes + +- [#3223](https://github.com/withastro/astro/pull/3223) [`b7cd6958`](https://github.com/withastro/astro/commit/b7cd69588453cf874346bf2f14c41accd183129e) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Replace the component framework selector with a new "run astro add" option. This unlocks integrations beyond components during your create-astro setup, including TailwindCSS and Partytown. This also replaces our previous "starter" template with a simplified "Just the basics" option. + +## 0.10.1 + +### Patch Changes + +- [#3212](https://github.com/withastro/astro/pull/3212) [`00fc1326`](https://github.com/withastro/astro/commit/00fc1326ed526974cc4aca9faec410df91b4bcbd) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Simplify logging during welcome message and directory selection + +## 0.10.0 + +### Minor Changes + +- [#3190](https://github.com/withastro/astro/pull/3190) [`38e5e9e9`](https://github.com/withastro/astro/commit/38e5e9e9825876cd0ae14a648b51bdf397e81169) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Feat: add option to install dependencies during setup. This respects the package manager used to run create-astro (ex. "yarn create astro" vs "pnpm create astro@latest"). + +## 0.9.0 + +### Minor Changes + +- [#3168](https://github.com/withastro/astro/pull/3168) [`7c49194c`](https://github.com/withastro/astro/commit/7c49194ca2161a09cc304ba8327533f8176ae0da) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Add prompt to choose a directory, now defaulting to a separate "./my-astro-site" instead of "." (current directory) + +## 0.8.0 + +### Minor Changes + +- [#2843](https://github.com/withastro/astro/pull/2843) [`1fdb63b5`](https://github.com/withastro/astro/commit/1fdb63b5d000d17edca77e870ce721e616a9c64a) Thanks [@JuanM04](https://github.com/JuanM04)! - Automatically add integration `peerDependencies` to scaffolded `package.json` files + +## 0.8.0-next.0 + +### Minor Changes + +- [#2843](https://github.com/withastro/astro/pull/2843) [`1fdb63b5`](https://github.com/withastro/astro/commit/1fdb63b5d000d17edca77e870ce721e616a9c64a) Thanks [@JuanM04](https://github.com/JuanM04)! - Automatically add integration `peerDependencies` to scaffolded `package.json` files + +## 0.7.1 + +### Patch Changes + +- [#2429](https://github.com/withastro/astro/pull/2429) [`fda857eb`](https://github.com/withastro/astro/commit/fda857eb22508f55233e297a887b356ea7b87398) Thanks [@Mikkel-T](https://github.com/Mikkel-T)! - Added an option to create-astro to use verbose logging which should help debug degit issues + +## 0.7.0 + +### Minor Changes + +- [#2202](https://github.com/withastro/astro/pull/2202) [`45cea6ae`](https://github.com/withastro/astro/commit/45cea6aec5a310fed4cb8da0d96670d6b99a2539) Thanks [@jonathantneal](https://github.com/jonathantneal)! - Officially drop support for Node v12. The minimum supported version is now Node v14.15+, + +## 0.6.10 + +### Patch Changes + +- [#2150](https://github.com/withastro/astro/pull/2150) [`d5ebd9d1`](https://github.com/withastro/astro/commit/d5ebd9d178ed4e5d15ef43f32217c16d44f19151) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Fix create-astro export map entry + +## 0.6.9 + +### Patch Changes + +- [#2124](https://github.com/withastro/astro/pull/2124) [`c0f29bcf`](https://github.com/withastro/astro/commit/c0f29bcf8c2b943e4a8101cae4f893b13a4b832c) Thanks [@leosvelperez](https://github.com/leosvelperez)! - Parse --renderers flag correctly when passed to the create-astro cli + +## 0.6.8 + +### Patch Changes + +- 3e1bdb1a: Add a helpful message for the "could not find commit hash for ..." error + +## 0.6.7 + +## 0.6.7-next.1 + +### Patch Changes + +- 6c66d483: Fix issue with v7.x+ versions of npm init, which changed default flag handling + +## 0.6.7-next.0 + +### Patch Changes + +- 6c66d483: Fix issue with v7.x+ versions of npm init, which changed default flag handling + +## 0.6.6 + +### Patch Changes + +- d5fdeefe: Changes create-astro to pull examples from the latest branch + +## 0.6.5 + +### Patch Changes + +- 025f5e3f: Fix to revert change pointing create-astro at the latest branch + +## 0.6.4 + +### Patch Changes + +- 28f00566: Updates create-astro to use the latest branch + +## 0.6.3 + +### Patch Changes + +- 0eeb2534: change rm to unlink for node 12 compatibility + +## 0.6.2 + +### Patch Changes + +- 11a6f884: Added a check to see if the renderers array is empty and only show the message about using the templates default renderers if it isn't + +## 0.6.1 + +### Patch Changes + +- 24dce41c: Adds a new template 'minimal' which does not include a framework + +## 0.6.0 + +### Minor Changes + +- cf4c97cf: forced degit template extraction in case of non empty installation directory + +## 0.5.2 + +### Patch Changes + +- 6c52c92: Add warning when encountering 'zlib: unexpected end of file' error + +## 0.5.1 + +### Patch Changes + +- a7e6666: compile javascript to target Node v12.x +- bd18e14: Add support for [Solid](https://www.solidjs.com/) +- d45431d: create-astro does not fail when removing subdirectories + +## 0.5.1-next.1 + +### Patch Changes + +- bd18e14: Add support for [Solid](https://www.solidjs.com/) + +## 0.5.1-next.0 + +### Patch Changes + +- a7e6666: compile javascript to target Node v12.x +- d45431d: create-astro does not fail when removing subdirectories + +## 0.5.0 + +### Minor Changes + +- 36e104b: Use new client: prefix for component examples + +## 0.4.0 + +### Minor Changes + +- 5d5d67c: Update `create-astro` to handle framework-specific logic based on user preference + +## 0.3.5 + +### Patch Changes + +- d8ceff5: Allows using an external repo as a template + + You can do this with the `--template` flag: + + ```bash + npm init astro my-shopify --template cassidoo/shopify-react-astro + ``` + +## 0.3.4 + +### Patch Changes + +- b0e41ea: fix small output bugs + +## 0.3.3 + +### Patch Changes + +- f9f2da4: Add repository key to package.json for create-astro + +## 0.3.2 + +### Patch Changes + +- ab2972b: Update package.json engines for esm support + +## 0.3.1 + +### Patch Changes + +- d6a7349: fix issue with empty prompt + +## 0.3.0 + +### Minor Changes + +- 6bca7c8: Redesigned create-astro internals +- 6bca7c8: New UI diff --git a/packages/create-astro/README.md b/packages/create-astro/README.md new file mode 100644 index 000000000..260922b0a --- /dev/null +++ b/packages/create-astro/README.md @@ -0,0 +1,63 @@ +# create-astro + +## Scaffolding for Astro projects + +**With NPM:** + +```bash +npm create astro@latest +``` + +**With Yarn:** + +```bash +yarn create astro +``` + +**With PNPM:** + +```bash +pnpm create astro +``` + +`create-astro` automatically runs in _interactive_ mode, but you can also specify your project name and template with command line arguments. + +```bash +# npm +npm create astro@latest my-astro-project -- --template minimal + +# yarn +yarn create astro my-astro-project --template minimal + +# pnpm +pnpm create astro my-astro-project --template minimal +``` + +[Check out the full list][examples] of example templates, available on GitHub. + +You can also use any GitHub repo as a template: + +```bash +npm create astro@latest my-astro-project -- --template cassidoo/shopify-react-astro +``` + +### CLI Flags + +May be provided in place of prompts + +| Name | Description | +| :--------------------------- | :----------------------------------------- | +| `--help` (`-h`) | Display available flags. | +| `--template <name>` | Specify your template. | +| `--install` / `--no-install` | Install dependencies (or not). | +| `--add <integrations>` | Add integrations. | +| `--git` / `--no-git` | Initialize git repo (or not). | +| `--yes` (`-y`) | Skip all prompts by accepting defaults. | +| `--no` (`-n`) | Skip all prompts by declining defaults. | +| `--dry-run` | Walk through steps without executing. | +| `--skip-houston` | Skip Houston animation. | +| `--ref` | Specify an Astro branch (default: latest). | +| `--fancy` | Enable full Unicode support for Windows. | + +[examples]: https://github.com/withastro/astro/tree/main/examples +[typescript]: https://github.com/withastro/astro/tree/main/packages/astro/tsconfigs diff --git a/packages/create-astro/create-astro.mjs b/packages/create-astro/create-astro.mjs new file mode 100755 index 000000000..5e25e5e94 --- /dev/null +++ b/packages/create-astro/create-astro.mjs @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +'use strict'; + +const currentVersion = process.versions.node; +const requiredMajorVersion = parseInt(currentVersion.split('.')[0], 10); +const minimumMajorVersion = 18; + +if (requiredMajorVersion < minimumMajorVersion) { + console.error(`Node.js v${currentVersion} is out of date and unsupported!`); + console.error(`Please use Node.js v${minimumMajorVersion} or higher.`); + process.exit(1); +} + +import('./dist/index.js').then(({ main }) => main()); diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json new file mode 100644 index 000000000..d9a583355 --- /dev/null +++ b/packages/create-astro/package.json @@ -0,0 +1,47 @@ +{ + "name": "create-astro", + "version": "4.12.1", + "type": "module", + "author": "withastro", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/withastro/astro.git", + "directory": "packages/create-astro" + }, + "bugs": "https://github.com/withastro/astro/issues", + "homepage": "https://astro.build", + "exports": { + ".": "./create-astro.mjs" + }, + "main": "./create-astro.mjs", + "bin": { + "create-astro": "./create-astro.mjs" + }, + "scripts": { + "build": "astro-scripts build \"src/index.ts\" --bundle && tsc", + "build:ci": "astro-scripts build \"src/index.ts\" --bundle", + "dev": "astro-scripts dev \"src/**/*.ts\"", + "test": "astro-scripts test \"test/**/*.test.js\"" + }, + "files": [ + "dist", + "create-astro.js" + ], + "//a": "MOST PACKAGES SHOULD GO IN DEV_DEPENDENCIES! THEY WILL BE BUNDLED.", + "//b": "DEPENDENCIES IS FOR UNBUNDLED PACKAGES", + "dependencies": { + "@astrojs/cli-kit": "^0.4.1", + "@bluwy/giget-core": "^0.1.2" + }, + "devDependencies": { + "arg": "^5.0.2", + "astro-scripts": "workspace:*" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + }, + "publishConfig": { + "provenance": true + } +} diff --git a/packages/create-astro/src/actions/context.ts b/packages/create-astro/src/actions/context.ts new file mode 100644 index 000000000..ce4c067fd --- /dev/null +++ b/packages/create-astro/src/actions/context.ts @@ -0,0 +1,146 @@ +import os from 'node:os'; +import { type Task, prompt } from '@astrojs/cli-kit'; +import { random } from '@astrojs/cli-kit/utils'; +import arg from 'arg'; + +import getSeasonalData from '../data/seasonal.js'; +import { getName, getVersion } from '../messages.js'; + +export interface Context { + help: boolean; + prompt: typeof prompt; + cwd: string; + packageManager: string; + username: Promise<string>; + version: Promise<string>; + skipHouston: boolean; + fancy?: boolean; + add?: string[]; + dryRun?: boolean; + yes?: boolean; + projectName?: string; + template?: string; + ref: string; + install?: boolean; + git?: boolean; + typescript?: string; + stdin?: typeof process.stdin; + stdout?: typeof process.stdout; + exit(code: number): never; + welcome?: string; + hat?: string; + tie?: string; + tasks: Task[]; +} + +function getPackageTag(packageSpecifier: string | undefined): string | undefined { + switch (packageSpecifier) { + case 'alpha': + case 'beta': + case 'rc': + return packageSpecifier; + // Will fallback to latest + case undefined: + default: + return undefined; + } +} + +export async function getContext(argv: string[]): Promise<Context> { + const packageSpecifier = argv + .find((argItem) => /^(astro|create-astro)@/.exec(argItem)) + ?.split('@')[1]; + + const flags = arg( + { + '--template': String, + '--ref': String, + '--yes': Boolean, + '--no': Boolean, + '--install': Boolean, + '--no-install': Boolean, + '--git': Boolean, + '--no-git': Boolean, + '--skip-houston': Boolean, + '--dry-run': Boolean, + '--help': Boolean, + '--fancy': Boolean, + '--add': [String], + + '-y': '--yes', + '-n': '--no', + '-h': '--help', + }, + { argv, permissive: true }, + ); + + const packageManager = detectPackageManager() ?? 'npm'; + let cwd = flags['_'][0]; + let { + '--help': help = false, + '--template': template, + '--no': no, + '--yes': yes, + '--install': install, + '--no-install': noInstall, + '--git': git, + '--no-git': noGit, + '--fancy': fancy, + '--skip-houston': skipHouston, + '--dry-run': dryRun, + '--ref': ref, + '--add': add, + } = flags; + let projectName = cwd; + + if (no) { + yes = false; + if (install == undefined) install = false; + if (git == undefined) git = false; + } + + skipHouston = + ((os.platform() === 'win32' && !fancy) || skipHouston) ?? + [yes, no, install, git].some((v) => v !== undefined); + + const { messages, hats, ties } = getSeasonalData({ fancy }); + + const context: Context = { + help, + prompt, + packageManager, + username: getName(), + version: getVersion( + packageManager, + 'astro', + getPackageTag(packageSpecifier), + process.env.ASTRO_VERSION, + ), + skipHouston, + fancy, + add, + dryRun, + projectName, + template, + ref: ref ?? 'latest', + welcome: random(messages), + hat: hats ? random(hats) : undefined, + tie: ties ? random(ties) : undefined, + yes, + install: install ?? (noInstall ? false : undefined), + git: git ?? (noGit ? false : undefined), + cwd, + exit(code) { + process.exit(code); + }, + tasks: [], + }; + return context; +} + +function detectPackageManager() { + if (!process.env.npm_config_user_agent) return; + const specifier = process.env.npm_config_user_agent.split(' ')[0]; + const name = specifier.substring(0, specifier.lastIndexOf('/')); + return name === 'npminstall' ? 'cnpm' : name; +} diff --git a/packages/create-astro/src/actions/dependencies.ts b/packages/create-astro/src/actions/dependencies.ts new file mode 100644 index 000000000..d4990a8fb --- /dev/null +++ b/packages/create-astro/src/actions/dependencies.ts @@ -0,0 +1,109 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { color } from '@astrojs/cli-kit'; +import { error, info, title } from '../messages.js'; +import { shell } from '../shell.js'; +import type { Context } from './context.js'; + +export async function dependencies( + ctx: Pick< + Context, + 'install' | 'yes' | 'prompt' | 'packageManager' | 'cwd' | 'dryRun' | 'tasks' | 'add' + >, +) { + let deps = ctx.install ?? ctx.yes; + if (deps === undefined) { + ({ deps } = await ctx.prompt({ + name: 'deps', + type: 'confirm', + label: title('deps'), + message: `Install dependencies?`, + hint: 'recommended', + initial: true, + })); + ctx.install = deps; + } + + ctx.add = ctx.add?.reduce<string[]>((acc, item) => acc.concat(item.split(',')), []); + + if (ctx.dryRun) { + await info( + '--dry-run', + `Skipping dependency installation${ctx.add ? ` and adding ${ctx.add.join(', ')}` : ''}`, + ); + } else if (deps) { + ctx.tasks.push({ + pending: 'Dependencies', + start: `Dependencies installing with ${ctx.packageManager}...`, + end: 'Dependencies installed', + onError: (e) => { + error('error', e); + error( + 'error', + `Dependencies failed to install, please run ${color.bold( + ctx.packageManager + ' install', + )} to install them manually after setup.`, + ); + }, + while: () => install({ packageManager: ctx.packageManager, cwd: ctx.cwd }), + }); + + let add = ctx.add; + + if (add) { + ctx.tasks.push({ + pending: 'Integrations', + start: `Adding integrations with astro add`, + end: 'Integrations added', + onError: (e) => { + error('error', e); + error( + 'error', + `Failed to add integrations, please run ${color.bold( + `astro add ${add.join(' ')}`, + )} to install them manually after setup.`, + ); + }, + while: () => + astroAdd({ integrations: add, packageManager: ctx.packageManager, cwd: ctx.cwd }), + }); + } + } else { + await info( + ctx.yes === false ? 'deps [skip]' : 'No problem!', + 'Remember to install dependencies after setup.', + ); + } +} + +async function astroAdd({ + integrations, + packageManager, + cwd, +}: { integrations: string[]; packageManager: string; cwd: string }) { + if (packageManager === 'yarn') await ensureYarnLock({ cwd }); + return shell( + packageManager === 'npm' ? 'npx' : `${packageManager} dlx`, + ['astro add', integrations.join(' '), '-y'], + { cwd, timeout: 90_000, stdio: 'ignore' }, + ); +} + +async function install({ packageManager, cwd }: { packageManager: string; cwd: string }) { + if (packageManager === 'yarn') await ensureYarnLock({ cwd }); + return shell(packageManager, ['install'], { cwd, timeout: 90_000, stdio: 'ignore' }); +} + +/** + * Yarn Berry (PnP) versions will throw an error if there isn't an existing `yarn.lock` file + * If a `yarn.lock` file doesn't exist, this function writes an empty `yarn.lock` one. + * Unfortunately this hack is required to run `yarn install`. + * + * The empty `yarn.lock` file is immediately overwritten by the installation process. + * See https://github.com/withastro/astro/pull/8028 + */ +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 new file mode 100644 index 000000000..ebedb8701 --- /dev/null +++ b/packages/create-astro/src/actions/git.ts @@ -0,0 +1,64 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import type { Context } from './context.js'; + +import { color } from '@astrojs/cli-kit'; +import { error, info, title } from '../messages.js'; +import { shell } from '../shell.js'; + +export async function git( + ctx: Pick<Context, 'cwd' | 'git' | 'yes' | 'prompt' | 'dryRun' | 'tasks'>, +) { + if (fs.existsSync(path.join(ctx.cwd, '.git'))) { + await info('Nice!', `Git has already been initialized`); + return; + } + let _git = ctx.git ?? ctx.yes; + if (_git === undefined) { + ({ git: _git } = await ctx.prompt({ + name: 'git', + type: 'confirm', + label: title('git'), + message: `Initialize a new git repository?`, + hint: 'optional', + initial: true, + })); + } + + if (ctx.dryRun) { + await info('--dry-run', `Skipping Git initialization`); + } else if (_git) { + ctx.tasks.push({ + pending: 'Git', + start: 'Git initializing...', + end: 'Git initialized', + while: () => + init({ cwd: ctx.cwd }).catch((e) => { + error('error', e); + process.exit(1); + }), + }); + } else { + await info( + ctx.yes === false ? 'git [skip]' : 'Sounds good!', + `You can always run ${color.reset('git init')}${color.dim(' manually.')}`, + ); + } +} + +async function init({ cwd }: { cwd: string }) { + try { + await shell('git', ['init'], { cwd, stdio: 'ignore' }); + await shell('git', ['add', '-A'], { cwd, stdio: 'ignore' }); + await shell( + 'git', + [ + 'commit', + '-m', + '"Initial commit from Astro"', + '--author="houston[bot] <astrobot-houston@users.noreply.github.com>"', + ], + { cwd, stdio: 'ignore' }, + ); + } catch {} +} diff --git a/packages/create-astro/src/actions/help.ts b/packages/create-astro/src/actions/help.ts new file mode 100644 index 000000000..1d5c7f609 --- /dev/null +++ b/packages/create-astro/src/actions/help.ts @@ -0,0 +1,24 @@ +import { printHelp } from '../messages.js'; + +export function help() { + printHelp({ + commandName: 'create-astro', + usage: '[dir] [...flags]', + headline: 'Scaffold Astro projects.', + tables: { + Flags: [ + ['--help (-h)', 'See all available flags.'], + ['--template <name>', 'Specify your template.'], + ['--install / --no-install', 'Install dependencies (or not).'], + ['--add <integrations>', 'Add integrations.'], + ['--git / --no-git', 'Initialize git repo (or not).'], + ['--yes (-y)', 'Skip all prompts by accepting defaults.'], + ['--no (-n)', 'Skip all prompts by declining defaults.'], + ['--dry-run', 'Walk through steps without executing.'], + ['--skip-houston', 'Skip Houston animation.'], + ['--ref', 'Choose astro branch (default: latest).'], + ['--fancy', 'Enable full Unicode support for Windows.'], + ], + }, + }); +} diff --git a/packages/create-astro/src/actions/intro.ts b/packages/create-astro/src/actions/intro.ts new file mode 100644 index 000000000..0249e63c8 --- /dev/null +++ b/packages/create-astro/src/actions/intro.ts @@ -0,0 +1,29 @@ +import type { Context } from './context.js'; + +import { color, label } from '@astrojs/cli-kit'; +import { banner, say } from '../messages.js'; + +export async function intro( + ctx: Pick<Context, 'skipHouston' | 'welcome' | 'hat' | 'tie' | 'version' | 'username' | 'fancy'>, +) { + banner(); + + if (!ctx.skipHouston) { + const { welcome, hat, tie } = ctx; + await say( + [ + [ + 'Welcome', + 'to', + label('astro', color.bgGreen, color.black), + Promise.resolve(ctx.version).then( + (version) => (version ? color.green(`v${version}`) : '') + ',', + ), + Promise.resolve(ctx.username).then((username) => `${username}!`), + ], + welcome ?? "Let's build something awesome!", + ] as string[], + { clear: true, hat, tie }, + ); + } +} diff --git a/packages/create-astro/src/actions/next-steps.ts b/packages/create-astro/src/actions/next-steps.ts new file mode 100644 index 000000000..91536cc46 --- /dev/null +++ b/packages/create-astro/src/actions/next-steps.ts @@ -0,0 +1,25 @@ +import path from 'node:path'; +import type { Context } from './context.js'; + +import { nextSteps, say } from '../messages.js'; + +export async function next( + ctx: Pick<Context, 'hat' | 'tie' | 'cwd' | 'packageManager' | 'skipHouston'>, +) { + let projectDir = path.relative(process.cwd(), ctx.cwd); + + const commandMap: { [key: string]: string } = { + npm: 'npm run dev', + bun: 'bun run dev', + yarn: 'yarn dev', + pnpm: 'pnpm dev', + }; + + const devCmd = commandMap[ctx.packageManager as keyof typeof commandMap] || 'npm run dev'; + await nextSteps({ projectDir, devCmd }); + + if (!ctx.skipHouston) { + await say(['Good luck out there, astronaut! ๐'], { hat: ctx.hat, tie: ctx.tie }); + } + return; +} diff --git a/packages/create-astro/src/actions/project-name.ts b/packages/create-astro/src/actions/project-name.ts new file mode 100644 index 000000000..26938de80 --- /dev/null +++ b/packages/create-astro/src/actions/project-name.ts @@ -0,0 +1,74 @@ +import type { Context } from './context.js'; + +import path from 'node:path'; +import { color, generateProjectName } from '@astrojs/cli-kit'; +import { info, log, title } from '../messages.js'; + +import { isEmpty, toValidName } from './shared.js'; + +export async function projectName( + ctx: Pick<Context, 'cwd' | 'yes' | 'dryRun' | 'prompt' | 'projectName' | 'exit'>, +) { + await checkCwd(ctx.cwd); + + if (!ctx.cwd || !isEmpty(ctx.cwd)) { + if (!isEmpty(ctx.cwd)) { + await info('Hmm...', `${color.reset(`"${ctx.cwd}"`)}${color.dim(` is not empty!`)}`); + } + + if (ctx.yes) { + ctx.projectName = generateProjectName(); + ctx.cwd = `./${ctx.projectName}`; + await info('dir', `Project created at ./${ctx.projectName}`); + return; + } + + const { name } = await ctx.prompt({ + name: 'name', + type: 'text', + label: title('dir'), + message: 'Where should we create your new project?', + initial: `./${generateProjectName()}`, + validate(value: string) { + if (!isEmpty(value)) { + return `Directory is not empty!`; + } + // Check for non-printable characters + if (value.match(/[^\x20-\x7E]/g) !== null) + return `Invalid non-printable character present!`; + return true; + }, + }); + + ctx.cwd = name!.trim(); + ctx.projectName = toValidName(name!); + if (ctx.dryRun) { + await info('--dry-run', 'Skipping project naming'); + return; + } + } else { + let name = ctx.cwd; + if (name === '.' || name === './') { + const parts = process.cwd().split(path.sep); + name = parts[parts.length - 1]; + } else if (name.startsWith('./') || name.startsWith('../')) { + const parts = name.split('/'); + name = parts[parts.length - 1]; + } + ctx.projectName = toValidName(name); + } + + if (!ctx.cwd) { + ctx.exit(1); + } +} + +async function checkCwd(cwd: string | undefined) { + const empty = cwd && isEmpty(cwd); + if (empty) { + log(''); + await info('dir', `Using ${color.reset(cwd)}${color.dim(' as project directory')}`); + } + + return empty; +} diff --git a/packages/create-astro/src/actions/shared.ts b/packages/create-astro/src/actions/shared.ts new file mode 100644 index 000000000..da19677d0 --- /dev/null +++ b/packages/create-astro/src/actions/shared.ts @@ -0,0 +1,59 @@ +import fs from 'node:fs'; + +// Some existing files and directories can be safely ignored when checking if a directory is a valid project directory. +// https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/create-react-app/createReactApp.js#L907-L934 +const VALID_PROJECT_DIRECTORY_SAFE_LIST = [ + '.DS_Store', + '.git', + '.gitkeep', + '.gitattributes', + '.gitignore', + '.gitlab-ci.yml', + '.hg', + '.hgcheck', + '.hgignore', + '.idea', + '.npmignore', + '.travis.yml', + '.yarn', + '.yarnrc.yml', + 'docs', + 'LICENSE', + 'mkdocs.yml', + 'Thumbs.db', + /\.iml$/, + /^npm-debug\.log/, + /^yarn-debug\.log/, + /^yarn-error\.log/, +]; + +export function isEmpty(dirPath: string) { + if (!fs.existsSync(dirPath)) { + return true; + } + + const conflicts = fs.readdirSync(dirPath).filter((content) => { + return !VALID_PROJECT_DIRECTORY_SAFE_LIST.some((safeContent) => { + return typeof safeContent === 'string' ? content === safeContent : safeContent.test(content); + }); + }); + + return conflicts.length === 0; +} + +function isValidName(projectName: string) { + return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName); +} + +export function toValidName(projectName: string) { + if (isValidName(projectName)) return projectName; + + return projectName + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/^[._]/, '') + .replace(/[^a-z\d\-~]+/g, '-') + .replace(/^-+/, '') + .replace(/-+$/, ''); +} diff --git a/packages/create-astro/src/actions/template.ts b/packages/create-astro/src/actions/template.ts new file mode 100644 index 000000000..cf58d90a8 --- /dev/null +++ b/packages/create-astro/src/actions/template.ts @@ -0,0 +1,169 @@ +import type { Context } from './context.js'; + +import fs from 'node:fs'; +import path from 'node:path'; +import { color } from '@astrojs/cli-kit'; +import { downloadTemplate } from '@bluwy/giget-core'; +import { error, info, title } from '../messages.js'; + +export async function template( + ctx: Pick<Context, 'template' | 'prompt' | 'yes' | 'dryRun' | 'exit' | 'tasks'>, +) { + if (!ctx.template && ctx.yes) ctx.template = 'basics'; + + if (ctx.template) { + await info('tmpl', `Using ${color.reset(ctx.template)}${color.dim(' as project template')}`); + } else { + const { template: tmpl } = await ctx.prompt({ + name: 'template', + type: 'select', + label: title('tmpl'), + message: 'How would you like to start your new project?', + initial: 'basics', + choices: [ + { value: 'basics', label: 'A basic, helpful starter project', hint: '(recommended)' }, + { value: 'blog', label: 'Use blog template' }, + { value: 'starlight', label: 'Use docs (Starlight) template' }, + { value: 'minimal', label: 'Use minimal (empty) template' }, + ], + }); + ctx.template = tmpl; + } + + if (ctx.dryRun) { + await info('--dry-run', `Skipping template copying`); + } else if (ctx.template) { + ctx.tasks.push({ + pending: 'Template', + start: 'Template copying...', + end: 'Template copied', + while: () => + copyTemplate(ctx.template!, ctx as Context).catch((e) => { + if (e instanceof Error) { + error('error', e.message); + process.exit(1); + } else { + error('error', 'Unable to clone template.'); + process.exit(1); + } + }), + }); + } else { + ctx.exit(1); + } +} + +// some files are only needed for online editors when using astro.new. Remove for create-astro installs. +const FILES_TO_REMOVE = ['CHANGELOG.md', '.codesandbox']; +const FILES_TO_UPDATE = { + 'package.json': (file: string, overrides: { name: string }) => + fs.promises.readFile(file, 'utf-8').then((value) => { + // Match first indent in the file or fallback to `\t` + const indent = /(^\s+)/m.exec(value)?.[1] ?? '\t'; + return fs.promises.writeFile( + file, + JSON.stringify( + Object.assign(JSON.parse(value), Object.assign(overrides, { private: undefined })), + null, + indent, + ), + 'utf-8', + ); + }), +}; + +export function getTemplateTarget(tmpl: string, ref = 'latest') { + // Handle Starlight templates + if (tmpl.startsWith('starlight')) { + const [, starter = 'basics'] = tmpl.split('/'); + return `github:withastro/starlight/examples/${starter}`; + } + + // Handle third-party templates + const isThirdParty = tmpl.includes('/'); + if (isThirdParty) return tmpl; + + // Handle Astro templates + if (ref === 'latest') { + // `latest` ref is specially handled to route to a branch specifically + // to allow faster downloads. Otherwise giget has to download the entire + // repo and only copy a sub directory + return `github:withastro/astro#examples/${tmpl}`; + } else { + return `github:withastro/astro/examples/${tmpl}#${ref}`; + } +} + +async function copyTemplate(tmpl: string, ctx: Context) { + const templateTarget = getTemplateTarget(tmpl, ctx.ref); + // Copy + if (!ctx.dryRun) { + try { + await downloadTemplate(templateTarget, { + force: true, + cwd: ctx.cwd, + dir: '.', + }); + + // Modify the README file to reflect the correct package manager + if (ctx.packageManager !== 'npm') { + const readmePath = path.resolve(ctx.cwd, 'README.md'); + const readme = fs.readFileSync(readmePath, 'utf8'); + + // `run` is removed since it's optional in other package managers + const updatedReadme = readme + .replace(/\bnpm run\b/g, ctx.packageManager) + .replace(/\bnpm\b/g, ctx.packageManager); + + fs.writeFileSync(readmePath, updatedReadme); + } + } catch (err: any) { + // Only remove the directory if it's most likely created by us. + if (ctx.cwd !== '.' && ctx.cwd !== './' && !ctx.cwd.startsWith('../')) { + try { + fs.rmdirSync(ctx.cwd); + } catch (_) { + // Ignore any errors from removing the directory, + // make sure we throw and display the original error. + } + } + + if (err.message?.includes('404')) { + throw new Error(`Template ${color.reset(tmpl)} ${color.dim('does not exist!')}`); + } + + if (err.message) { + error('error', err.message); + } + try { + // The underlying error is often buried deep in the `cause` property + // This is in a try/catch block in case of weirdnesses in accessing the `cause` property + if ('cause' in err) { + // This is probably included in err.message, but we can log it just in case it has extra info + error('error', err.cause); + if ('cause' in err.cause) { + // Hopefully the actual fetch error message + error('error', err.cause?.cause); + } + } + } catch {} + throw new Error(`Unable to download template ${color.reset(tmpl)}`); + } + + // Post-process in parallel + const removeFiles = FILES_TO_REMOVE.map(async (file) => { + const fileLoc = path.resolve(path.join(ctx.cwd, file)); + if (fs.existsSync(fileLoc)) { + return fs.promises.rm(fileLoc, { recursive: true }); + } + }); + const updateFiles = Object.entries(FILES_TO_UPDATE).map(async ([file, update]) => { + const fileLoc = path.resolve(path.join(ctx.cwd, file)); + if (fs.existsSync(fileLoc)) { + return update(fileLoc, { name: ctx.projectName! }); + } + }); + + await Promise.all([...removeFiles, ...updateFiles]); + } +} diff --git a/packages/create-astro/src/actions/verify.ts b/packages/create-astro/src/actions/verify.ts new file mode 100644 index 000000000..7f446c869 --- /dev/null +++ b/packages/create-astro/src/actions/verify.ts @@ -0,0 +1,40 @@ +import type { Context } from './context.js'; + +import dns from 'node:dns/promises'; +import { color } from '@astrojs/cli-kit'; +import { verifyTemplate } from '@bluwy/giget-core'; +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 target = getTemplateTarget(ctx.template, ctx.ref); + const ok = await verifyTemplate(target); + 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, + ); +} diff --git a/packages/create-astro/src/data/seasonal.ts b/packages/create-astro/src/data/seasonal.ts new file mode 100644 index 000000000..c333affa0 --- /dev/null +++ b/packages/create-astro/src/data/seasonal.ts @@ -0,0 +1,112 @@ +interface SeasonalHouston { + hats?: string[]; + ties?: string[]; + messages: string[]; +} + +export default function getSeasonalHouston({ fancy }: { fancy?: boolean }): SeasonalHouston { + const season = getSeason(); + switch (season) { + case 'new-year': { + const year = new Date().getFullYear(); + return { + hats: rarity(0.5, ['๐ฉ']), + ties: rarity(0.25, ['๐', '๐', '๐']), + messages: [ + `New year, new Astro site!`, + `Kicking ${year} off with Astro?! What an honor!`, + `Happy ${year}! Let's make something cool.`, + `${year} is your year! Let's build something awesome.`, + `${year} is the year of Astro!`, + `${year} is clearly off to a great start!`, + `Thanks for starting ${year} with Astro!`, + ], + }; + } + case 'spooky': + return { + hats: rarity(0.5, ['๐', '๐ป', 'โ ๏ธ', '๐', '๐ท๏ธ', '๐ฎ']), + ties: rarity(0.25, ['๐ฆด', '๐ฌ', '๐ซ']), + messages: [ + `I'm afraid I can't help you... Just kidding!`, + `Boo! Just kidding. Let's make a website!`, + `Let's haunt the internet. OooOooOOoo!`, + `No tricks here. Seeing you is always treat!`, + `Spiders aren't the only ones building the web!`, + `Let's conjure up some web magic!`, + `Let's harness the power of Astro to build a frightful new site!`, + `We're conjuring up a spooktacular website!`, + `Prepare for a web of spooky wonders to be woven.`, + `Chills and thrills await you on your new project!`, + ], + }; + case 'holiday': + return { + hats: rarity(0.75, ['๐', '๐', '๐ฒ']), + ties: rarity(0.75, ['๐งฃ']), + messages: [ + `'Tis the season to code and create.`, + `Jingle all the way through your web creation journey!`, + `Bells are ringing, and so are your creative ideas!`, + `Let's make the internet our own winter wonderland!`, + `It's time to decorate a brand new website!`, + `Let's unwrap the magic of the web together!`, + `Hope you're enjoying the holiday season!`, + `I'm dreaming of a brand new website!`, + `No better holiday gift than a new site!`, + `Your creativity is the gift that keeps on giving!`, + ], + }; + case undefined: + default: + return { + hats: fancy ? ['๐ฉ', '๐ฉ', '๐ฉ', '๐ฉ', '๐', '๐', '๐งข', '๐ฆ'] : undefined, + ties: fancy ? rarity(0.33, ['๐', '๐งฃ']) : undefined, + messages: [ + `Let's claim your corner of the internet.`, + `I'll be your assistant today.`, + `Let's build something awesome!`, + `Let's build something great!`, + `Let's build something fast!`, + `Let's build the web we want.`, + `Let's make the web weird!`, + `Let's make the web a better place!`, + `Let's create a new project!`, + `Let's create something unique!`, + `Time to build a new website.`, + `Time to build a faster website.`, + `Time to build a sweet new website.`, + `We're glad to have you on board.`, + `Keeping the internet weird since 2021.`, + `Initiating launch sequence...`, + `Initiating launch sequence... right... now!`, + `Awaiting further instructions.`, + ], + }; + } +} + +type Season = 'spooky' | 'holiday' | 'new-year'; +function getSeason(): Season | undefined { + const date = new Date(); + const month = date.getMonth() + 1; + const day = date.getDate() + 1; + + if (month === 1 && day <= 7) { + return 'new-year'; + } + if (month === 10 && day > 7) { + return 'spooky'; + } + if (month === 12 && day > 7 && day < 25) { + return 'holiday'; + } +} + +// Generates an array padded with empty strings to make decorations more rare +function rarity(frequency: number, emoji: string[]) { + if (frequency === 1) return emoji; + if (frequency === 0) return ['']; + const empty = Array.from({ length: Math.round(emoji.length * frequency) }, () => ''); + return [...emoji, ...empty]; +} diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts new file mode 100644 index 000000000..60816f75d --- /dev/null +++ b/packages/create-astro/src/index.ts @@ -0,0 +1,62 @@ +import { getContext } from './actions/context.js'; + +import { tasks } from '@astrojs/cli-kit'; +import { dependencies } from './actions/dependencies.js'; +import { git } from './actions/git.js'; +import { help } from './actions/help.js'; +import { intro } from './actions/intro.js'; +import { next } from './actions/next-steps.js'; +import { projectName } from './actions/project-name.js'; +import { template } from './actions/template.js'; +import { verify } from './actions/verify.js'; +import { setStdout } from './messages.js'; + +const exit = () => process.exit(0); +process.on('SIGINT', exit); +process.on('SIGTERM', exit); + +export async function main() { + // Add some extra spacing from the noisy npm/pnpm init output + // biome-ignore lint/suspicious/noConsoleLog: allowed + console.log(''); + // NOTE: In the v7.x version of npm, the default behavior of `npm init` was changed + // to no longer require `--` to pass args and instead pass `--` directly to us. This + // broke our arg parser, since `--` is a special kind of flag. Filtering for `--` here + // fixes the issue so that create-astro now works on all npm versions. + const cleanArgv = process.argv.slice(2).filter((arg) => arg !== '--'); + const ctx = await getContext(cleanArgv); + if (ctx.help) { + help(); + return; + } + + const steps = [ + verify, + intro, + projectName, + template, + dependencies, + + // Steps which write to files need to go above git + git, + ]; + + for (const step of steps) { + await step(ctx); + } + + // biome-ignore lint/suspicious/noConsoleLog: allowed + console.log(''); + + const labels = { + start: 'Project initializing...', + end: 'Project initialized!', + }; + await tasks(labels, ctx.tasks); + + await next(ctx); + + process.exit(0); +} + +export { dependencies, getContext, git, intro, next, projectName, setStdout, template, verify }; diff --git a/packages/create-astro/src/messages.ts b/packages/create-astro/src/messages.ts new file mode 100644 index 000000000..850d72fd0 --- /dev/null +++ b/packages/create-astro/src/messages.ts @@ -0,0 +1,191 @@ +import { exec } from 'node:child_process'; +import { stripVTControlCharacters } from 'node:util'; +/* eslint no-console: 'off' */ +import { color, say as houston, label } from '@astrojs/cli-kit'; +import { align, sleep } from '@astrojs/cli-kit/utils'; +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 +// +// A copy of this function also exists in the astro package +let _registry: string; +async function getRegistry(packageManager: string): Promise<string> { + if (_registry) return _registry; + const fallback = 'https://registry.npmjs.org'; + try { + const { stdout } = await shell(packageManager, ['config', 'get', 'registry']); + _registry = stdout?.trim()?.replace(/\/$/, '') || fallback; + // Detect cases where the shell command returned a non-URL (e.g. a warning) + if (!new URL(_registry).host) _registry = fallback; + } catch { + _registry = fallback; + } + return _registry; +} + +let stdout = process.stdout; +/** @internal Used to mock `process.stdout.write` for testing purposes */ +export function setStdout(writable: typeof process.stdout) { + stdout = writable; +} + +export async function say(messages: string | string[], { clear = false, hat = '', tie = '' } = {}) { + return houston(messages, { clear, hat, tie, stdout }); +} + +export const title = (text: string) => align(label(text), 'end', 7) + ' '; + +export const getName = () => + new Promise<string>((resolve) => { + exec('git config user.name', { encoding: 'utf-8' }, (_1, gitName) => { + if (gitName.trim()) { + return resolve(gitName.split(' ')[0].trim()); + } + exec('whoami', { encoding: 'utf-8' }, (_3, whoami) => { + if (whoami.trim()) { + return resolve(whoami.split(' ')[0].trim()); + } + return resolve('astronaut'); + }); + }); + }); + +export const getVersion = ( + packageManager: string, + packageName: string, + packageTag = 'latest', + fallback = '', +) => + new Promise<string>(async (resolve) => { + let registry = await getRegistry(packageManager); + const { version } = await fetch(`${registry}/${packageName}/${packageTag}`, { + redirect: 'follow', + }) + .then((res) => res.json()) + .catch(() => ({ version: fallback })); + return resolve(version); + }); + +export const log = (message: string) => stdout.write(message + '\n'); +export const banner = () => { + const prefix = `astro`; + const suffix = `Launch sequence initiated.`; + log(`${label(prefix, color.bgGreen, color.black)} ${suffix}`); +}; + +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) { + log(`${' '.repeat(5)} ${color.cyan('โผ')} ${color.cyan(prefix)}`); + log(`${' '.repeat(9)}${color.dim(text)}`); + } else { + log(`${' '.repeat(5)} ${color.cyan('โผ')} ${color.cyan(prefix)} ${color.dim(text)}`); + } +}; +export const error = async (prefix: string, text: string) => { + if (stdout.columns < 80) { + log(`${' '.repeat(5)} ${color.red('โฒ')} ${color.red(prefix)}`); + log(`${' '.repeat(9)}${color.dim(text)}`); + } else { + log(`${' '.repeat(5)} ${color.red('โฒ')} ${color.red(prefix)} ${color.dim(text)}`); + } +}; + +export const nextSteps = async ({ projectDir, devCmd }: { projectDir: string; devCmd: string }) => { + const max = stdout.columns; + const prefix = max < 80 ? ' ' : ' '.repeat(9); + await sleep(200); + log( + `\n ${color.bgCyan(` ${color.black('next')} `)} ${color.bold( + 'Liftoff confirmed. Explore your project!', + )}`, + ); + + await sleep(100); + if (projectDir !== '') { + projectDir = projectDir.includes(' ') ? `"./${projectDir}"` : `./${projectDir}`; + const enter = [ + `\n${prefix}Enter your project directory using`, + color.cyan(`cd ${projectDir}`, ''), + ]; + const len = enter[0].length + stripVTControlCharacters(enter[1]).length; + log(enter.join(len > max ? '\n' + prefix : ' ')); + } + log( + `${prefix}Run ${color.cyan(devCmd)} to start the dev server. ${color.cyan('CTRL+C')} to stop.`, + ); + await sleep(100); + log( + `${prefix}Add frameworks like ${color.cyan(`react`)} or ${color.cyan( + 'tailwind', + )} using ${color.cyan('astro add')}.`, + ); + await sleep(100); + log(`\n${prefix}Stuck? Join us at ${color.cyan(`https://astro.build/chat`)}`); + await sleep(200); +}; + +export function printHelp({ + commandName, + headline, + usage, + tables, + description, +}: { + commandName: string; + headline?: string; + usage?: string; + tables?: Record<string, [command: string, help: string][]>; + description?: string; +}) { + const linebreak = () => ''; + const table = (rows: [string, string][], { padding }: { padding: number }) => { + const split = stdout.columns < 60; + let raw = ''; + + for (const row of rows) { + if (split) { + raw += ` ${row[0]}\n `; + } else { + raw += `${`${row[0]}`.padStart(padding)}`; + } + raw += ' ' + color.dim(row[1]) + '\n'; + } + + return raw.slice(0, -1); // remove latest \n + }; + + let message = []; + + if (headline) { + message.push( + linebreak(), + `${title(commandName)} ${color.green(`v${process.env.PACKAGE_VERSION ?? ''}`)} ${headline}`, + ); + } + + if (usage) { + message.push(linebreak(), `${color.green(commandName)} ${color.bold(usage)}`); + } + + if (tables) { + function calculateTablePadding(rows: [string, string][]) { + return rows.reduce((val, [first]) => Math.max(val, first.length), 0); + } + const tableEntries = Object.entries(tables); + const padding = Math.max(...tableEntries.map(([, rows]) => calculateTablePadding(rows))); + for (const [, tableRows] of tableEntries) { + message.push(linebreak(), table(tableRows, { padding })); + } + } + + if (description) { + message.push(linebreak(), `${description}`); + } + + log(message.join('\n') + '\n'); +} diff --git a/packages/create-astro/src/shell.ts b/packages/create-astro/src/shell.ts new file mode 100644 index 000000000..253097c4a --- /dev/null +++ b/packages/create-astro/src/shell.ts @@ -0,0 +1,51 @@ +// This is an extremely simplified version of [`execa`](https://github.com/sindresorhus/execa) +// intended to keep our dependency size down +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'; + +interface ExecaOptions { + cwd?: string | URL; + stdio?: StdioOptions; + timeout?: number; +} +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> { + 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 { + throw { stdout, stderr, exitCode: 1 }; + } + const { exitCode } = child; + if (exitCode === null) { + throw new Error('Timeout'); + } + if (exitCode !== 0) { + throw new Error(stderr); + } + return { stdout, stderr, exitCode }; +} diff --git a/packages/create-astro/test/context.test.js b/packages/create-astro/test/context.test.js new file mode 100644 index 000000000..b4e67a8c6 --- /dev/null +++ b/packages/create-astro/test/context.test.js @@ -0,0 +1,73 @@ +import assert from 'node:assert/strict'; +import os from 'node:os'; +import { describe, it } from 'node:test'; +import { getContext } from '../dist/index.js'; +describe('context', () => { + it('no arguments', async () => { + const ctx = await getContext([]); + assert.ok(!ctx.projectName); + assert.ok(!ctx.template); + assert.deepEqual(ctx.skipHouston, os.platform() === 'win32'); + assert.ok(!ctx.dryRun); + }); + + it('project name', async () => { + const ctx = await getContext(['foobar']); + assert.deepEqual(ctx.projectName, 'foobar'); + }); + + it('template', async () => { + const ctx = await getContext(['--template', 'minimal']); + assert.deepEqual(ctx.template, 'minimal'); + }); + + it('skip houston (explicit)', async () => { + const ctx = await getContext(['--skip-houston']); + assert.deepEqual(ctx.skipHouston, true); + }); + + it('skip houston (yes)', async () => { + const ctx = await getContext(['-y']); + assert.deepEqual(ctx.skipHouston, true); + }); + + it('skip houston (no)', async () => { + const ctx = await getContext(['-n']); + assert.deepEqual(ctx.skipHouston, true); + }); + + it('skip houston (install)', async () => { + const ctx = await getContext(['--install']); + assert.deepEqual(ctx.skipHouston, true); + }); + + it('dry run', async () => { + const ctx = await getContext(['--dry-run']); + assert.deepEqual(ctx.dryRun, true); + }); + + it('install', async () => { + const ctx = await getContext(['--install']); + assert.deepEqual(ctx.install, true); + }); + + it('add', async () => { + const ctx = await getContext(['--add', 'node']); + assert.deepEqual(ctx.add, ['node']); + }); + + it('no install', async () => { + const ctx = await getContext(['--no-install']); + assert.deepEqual(ctx.install, false); + }); + + it('git', async () => { + const ctx = await getContext(['--git']); + assert.deepEqual(ctx.git, true); + }); + + it('no git', async () => { + const ctx = await getContext(['--no-git']); + assert.deepEqual(ctx.git, false); + }); +}); diff --git a/packages/create-astro/test/dependencies.test.js b/packages/create-astro/test/dependencies.test.js new file mode 100644 index 000000000..e4eb3d1f4 --- /dev/null +++ b/packages/create-astro/test/dependencies.test.js @@ -0,0 +1,80 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { dependencies } from '../dist/index.js'; +import { setup } from './utils.js'; +describe('dependencies', () => { + const fixture = setup(); + + it('--yes', async () => { + const context = { + cwd: '', + yes: true, + packageManager: 'npm', + dryRun: true, + prompt: () => ({ deps: true }), + }; + + await dependencies(context); + + assert.ok(fixture.hasMessage('Skipping dependency installation')); + }); + + it('prompt yes', async () => { + const context = { + cwd: '', + packageManager: 'npm', + dryRun: true, + prompt: () => ({ deps: true }), + install: undefined, + }; + + await dependencies(context); + + assert.ok(fixture.hasMessage('Skipping dependency installation')); + assert.equal(context.install, true); + }); + + it('prompt no', async () => { + const context = { + cwd: '', + install: true, + packageManager: 'npm', + dryRun: true, + prompt: () => ({ deps: false }), + install: undefined, + }; + + await dependencies(context); + + assert.ok(fixture.hasMessage('Skipping dependency installation')); + assert.equal(context.install, false); + }); + + it('--install', async () => { + const context = { + cwd: '', + install: true, + packageManager: 'npm', + dryRun: true, + prompt: () => ({ deps: false }), + }; + await dependencies(context); + assert.ok(fixture.hasMessage('Skipping dependency installation')); + assert.equal(context.install, true); + }); + + it('--no-install ', async () => { + const context = { + cwd: '', + install: false, + packageManager: 'npm', + dryRun: true, + prompt: () => ({ deps: false }), + }; + + await dependencies(context); + + assert.ok(fixture.hasMessage('Skipping dependency installation')); + assert.equal(context.install, false); + }); +}); diff --git a/packages/create-astro/test/fixtures/empty/.gitkeep b/packages/create-astro/test/fixtures/empty/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/create-astro/test/fixtures/empty/.gitkeep diff --git a/packages/create-astro/test/fixtures/not-empty/git.json b/packages/create-astro/test/fixtures/not-empty/git.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/create-astro/test/fixtures/not-empty/git.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/packages/create-astro/test/fixtures/not-empty/package.json b/packages/create-astro/test/fixtures/not-empty/package.json new file mode 100644 index 000000000..d3f61d640 --- /dev/null +++ b/packages/create-astro/test/fixtures/not-empty/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/create-astro-not-empty", + "private": true, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + } +}
\ No newline at end of file diff --git a/packages/create-astro/test/fixtures/not-empty/tsconfig.json b/packages/create-astro/test/fixtures/not-empty/tsconfig.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/create-astro/test/fixtures/not-empty/tsconfig.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/packages/create-astro/test/git.test.js b/packages/create-astro/test/git.test.js new file mode 100644 index 000000000..85854f0d5 --- /dev/null +++ b/packages/create-astro/test/git.test.js @@ -0,0 +1,57 @@ +import assert from 'node:assert/strict'; +import { rmSync } from 'node:fs'; +import { mkdir, writeFile } from 'node:fs/promises'; +import { after, before, describe, it } from 'node:test'; + +import { git } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('git', () => { + const fixture = setup(); + + it('none', async () => { + const context = { cwd: '', dryRun: true, prompt: () => ({ git: false }) }; + await git(context); + + assert.ok(fixture.hasMessage('Skipping Git initialization')); + }); + + it('yes (--dry-run)', async () => { + const context = { cwd: '', dryRun: true, prompt: () => ({ git: true }) }; + await git(context); + assert.ok(fixture.hasMessage('Skipping Git initialization')); + }); + + it('no (--dry-run)', async () => { + const context = { cwd: '', dryRun: true, prompt: () => ({ git: false }) }; + await git(context); + + assert.ok(fixture.hasMessage('Skipping Git initialization')); + }); +}); + +describe('git initialized', () => { + const fixture = setup(); + const dir = new URL(new URL('./fixtures/not-empty/.git', import.meta.url)); + + before(async () => { + await mkdir(dir, { recursive: true }); + await writeFile(new URL('./git.json', dir), '{}', { encoding: 'utf8' }); + }); + + it('already initialized', async () => { + const context = { + git: true, + cwd: './test/fixtures/not-empty', + dryRun: false, + prompt: () => ({ git: false }), + }; + await git(context); + + assert.ok(fixture.hasMessage('Git has already been initialized')); + }); + + after(() => { + rmSync(dir, { recursive: true, force: true }); + }); +}); diff --git a/packages/create-astro/test/integrations.test.js b/packages/create-astro/test/integrations.test.js new file mode 100644 index 000000000..412285223 --- /dev/null +++ b/packages/create-astro/test/integrations.test.js @@ -0,0 +1,64 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { dependencies } from '../dist/index.js'; +import { setup } from './utils.js'; +describe('integrations', () => { + const fixture = setup(); + + it('--add node', async () => { + const context = { + cwd: '', + yes: true, + packageManager: 'npm', + dryRun: true, + add: ['node'], + }; + + await dependencies(context); + + assert.ok(fixture.hasMessage('--dry-run Skipping dependency installation and adding node')); + }); + + it('--add node --add react', async () => { + const context = { + cwd: '', + yes: true, + packageManager: 'npm', + dryRun: true, + add: ['node', 'react'], + }; + + await dependencies(context); + + assert.ok( + fixture.hasMessage('--dry-run Skipping dependency installation and adding node, react'), + ); + }); + + it('--add node,react', async () => { + const context = { + cwd: '', + yes: true, + packageManager: 'npm', + dryRun: true, + add: ['node,react'], + }; + + await dependencies(context); + + assert.ok( + fixture.hasMessage('--dry-run Skipping dependency installation and adding node, react'), + ); + }); + + it('-y', async () => { + const context = { + cwd: '', + yes: true, + packageManager: 'npm', + dryRun: true, + }; + await dependencies(context); + assert.ok(fixture.hasMessage('--dry-run Skipping dependency installation')); + }); +}); diff --git a/packages/create-astro/test/intro.test.js b/packages/create-astro/test/intro.test.js new file mode 100644 index 000000000..d042dad7f --- /dev/null +++ b/packages/create-astro/test/intro.test.js @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { intro } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('intro', () => { + const fixture = setup(); + + it('no arguments', async () => { + await intro({ skipHouston: false, version: '0.0.0', username: 'user' }); + assert.ok(fixture.hasMessage('Houston:')); + assert.ok(fixture.hasMessage('Welcome to astro v0.0.0')); + }); + it('--skip-houston', async () => { + await intro({ skipHouston: true, version: '0.0.0', username: 'user' }); + assert.equal(fixture.length(), 1); + assert.ok(!fixture.hasMessage('Houston:')); + assert.ok(fixture.hasMessage('Launch sequence initiated')); + }); +}); diff --git a/packages/create-astro/test/next.test.js b/packages/create-astro/test/next.test.js new file mode 100644 index 000000000..5b9b22b30 --- /dev/null +++ b/packages/create-astro/test/next.test.js @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { next } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('next steps', () => { + const fixture = setup(); + + it('no arguments', async () => { + await next({ skipHouston: false, cwd: './it/fixtures/not-empty', packageManager: 'npm' }); + assert.ok(fixture.hasMessage('Liftoff confirmed.')); + assert.ok(fixture.hasMessage('npm run dev')); + assert.ok(fixture.hasMessage('Good luck out there, astronaut!')); + }); + + it('--skip-houston', async () => { + await next({ skipHouston: true, cwd: './it/fixtures/not-empty', packageManager: 'npm' }); + assert.ok(!fixture.hasMessage('Good luck out there, astronaut!')); + }); +}); diff --git a/packages/create-astro/test/project-name.test.js b/packages/create-astro/test/project-name.test.js new file mode 100644 index 000000000..0aebd1c79 --- /dev/null +++ b/packages/create-astro/test/project-name.test.js @@ -0,0 +1,124 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { projectName } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('project name', async () => { + const fixture = setup(); + + it('pass in name', async () => { + const context = { projectName: '', cwd: './foo/bar/baz', prompt: () => {} }; + await projectName(context); + assert.equal(context.cwd, './foo/bar/baz'); + assert.equal(context.projectName, 'baz'); + }); + + it('dot', async () => { + const context = { projectName: '', cwd: '.', prompt: () => ({ name: 'foobar' }) }; + await projectName(context); + assert.ok(fixture.hasMessage('"." is not empty!')); + assert.equal(context.projectName, 'foobar'); + }); + + it('dot slash', async () => { + const context = { projectName: '', cwd: './', prompt: () => ({ name: 'foobar' }) }; + await projectName(context); + assert.ok(fixture.hasMessage('"./" is not empty!')); + assert.equal(context.projectName, 'foobar'); + }); + + it('empty', async () => { + const context = { + projectName: '', + cwd: './test/fixtures/empty', + prompt: () => ({ name: 'foobar' }), + }; + await projectName(context); + assert.ok(!fixture.hasMessage('"./test/fixtures/empty" is not empty!')); + assert.equal(context.projectName, 'empty'); + }); + + it('not empty', async () => { + const context = { + projectName: '', + cwd: './test/fixtures/not-empty', + prompt: () => ({ name: 'foobar' }), + }; + await projectName(context); + assert.ok(fixture.hasMessage('"./test/fixtures/not-empty" is not empty!')); + assert.equal(context.projectName, 'foobar'); + }); + + it('basic', async () => { + const context = { projectName: '', cwd: '', prompt: () => ({ name: 'foobar' }) }; + await projectName(context); + assert.equal(context.cwd, 'foobar'); + assert.equal(context.projectName, 'foobar'); + }); + + it('head and tail blank spaces should be trimmed', async () => { + const context = { projectName: '', cwd: '', prompt: () => ({ name: ' foobar ' }) }; + await projectName(context); + assert.equal(context.cwd, 'foobar'); + assert.equal(context.projectName, 'foobar'); + }); + + it('normalize', async () => { + const context = { projectName: '', cwd: '', prompt: () => ({ name: 'Invalid Name' }) }; + await projectName(context); + assert.equal(context.cwd, 'Invalid Name'); + assert.equal(context.projectName, 'invalid-name'); + }); + + it('remove leading/trailing dashes', async () => { + const context = { projectName: '', cwd: '', prompt: () => ({ name: '(invalid)' }) }; + await projectName(context); + assert.equal(context.projectName, 'invalid'); + }); + + it('handles scoped packages', async () => { + const context = { projectName: '', cwd: '', prompt: () => ({ name: '@astro/site' }) }; + await projectName(context); + assert.equal(context.cwd, '@astro/site'); + assert.equal(context.projectName, '@astro/site'); + }); + + it('--yes', async () => { + const context = { projectName: '', cwd: './foo/bar/baz', yes: true, prompt: () => {} }; + await projectName(context); + assert.equal(context.projectName, 'baz'); + }); + + it('dry run with name', async () => { + const context = { + projectName: '', + cwd: './foo/bar/baz', + dryRun: true, + prompt: () => {}, + }; + await projectName(context); + assert.equal(context.projectName, 'baz'); + }); + + it('dry run with dot', async () => { + const context = { + projectName: '', + cwd: '.', + dryRun: true, + prompt: () => ({ name: 'foobar' }), + }; + await projectName(context); + assert.equal(context.projectName, 'foobar'); + }); + + it('dry run with empty', async () => { + const context = { + projectName: '', + cwd: './test/fixtures/empty', + dryRun: true, + prompt: () => ({ name: 'foobar' }), + }; + await projectName(context); + assert.equal(context.projectName, 'empty'); + }); +}); diff --git a/packages/create-astro/test/template.test.js b/packages/create-astro/test/template.test.js new file mode 100644 index 000000000..821ac9c2e --- /dev/null +++ b/packages/create-astro/test/template.test.js @@ -0,0 +1,39 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { template } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('template', async () => { + const fixture = setup(); + + it('none', async () => { + const context = { template: '', cwd: '', dryRun: true, prompt: () => ({ template: 'blog' }) }; + await template(context); + assert.ok(fixture.hasMessage('Skipping template copying')); + assert.equal(context.template, 'blog'); + }); + + it('minimal (--dry-run)', async () => { + const context = { template: 'minimal', cwd: '', dryRun: true, prompt: () => {} }; + await template(context); + assert.ok(fixture.hasMessage('Using minimal as project template')); + }); + + it('basics (--dry-run)', async () => { + const context = { template: 'basics', cwd: '', dryRun: true, prompt: () => {} }; + await template(context); + assert.ok(fixture.hasMessage('Using basics as project template')); + }); + + it('blog (--dry-run)', async () => { + const context = { template: 'blog', cwd: '', dryRun: true, prompt: () => {} }; + await template(context); + assert.ok(fixture.hasMessage('Using blog as project template')); + }); + + it('minimal (--yes)', async () => { + const context = { template: 'minimal', cwd: '', dryRun: true, yes: true, prompt: () => {} }; + await template(context); + assert.ok(fixture.hasMessage('Using minimal as project template')); + }); +}); diff --git a/packages/create-astro/test/utils.js b/packages/create-astro/test/utils.js new file mode 100644 index 000000000..20063ec53 --- /dev/null +++ b/packages/create-astro/test/utils.js @@ -0,0 +1,32 @@ +import { before, beforeEach } from 'node:test'; +import { stripVTControlCharacters } from 'node:util'; +import { setStdout } from '../dist/index.js'; + +export function setup() { + const ctx = { messages: [] }; + before(() => { + setStdout( + Object.assign({}, process.stdout, { + write(buf) { + ctx.messages.push(stripVTControlCharacters(String(buf)).trim()); + return true; + }, + }), + ); + }); + beforeEach(() => { + ctx.messages = []; + }); + + return { + messages() { + return ctx.messages; + }, + length() { + return ctx.messages.length; + }, + hasMessage(content) { + return !!ctx.messages.find((msg) => msg.includes(content)); + }, + }; +} diff --git a/packages/create-astro/test/verify.test.js b/packages/create-astro/test/verify.test.js new file mode 100644 index 000000000..ff3350145 --- /dev/null +++ b/packages/create-astro/test/verify.test.js @@ -0,0 +1,41 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { verify } from '../dist/index.js'; +import { setup } from './utils.js'; + +describe('verify', async () => { + const fixture = setup(); + const exit = (code) => { + throw code; + }; + + it('basics', async () => { + const context = { template: 'basics', exit }; + await verify(context); + assert.equal(fixture.messages().length, 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; + } + assert.equal(err, 1); + assert.ok(!fixture.hasMessage('Template missing does not exist!')); + }); + + it('starlight', async () => { + const context = { template: 'starlight', exit }; + await verify(context); + assert.equal(fixture.messages().length, 0, 'Did not expect `verify` to log any messages'); + }); + + it('starlight/tailwind', async () => { + const context = { template: 'starlight/tailwind', exit }; + await verify(context); + assert.equal(fixture.messages().length, 0, 'Did not expect `verify` to log any messages'); + }); +}); diff --git a/packages/create-astro/tsconfig.json b/packages/create-astro/tsconfig.json new file mode 100644 index 000000000..18443cddf --- /dev/null +++ b/packages/create-astro/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "./dist" + } +} |