🚀 Astro Next (0.21.0) (#1406)
* make astro-root uids unique * Move Astro to Vite * Update tests * More test improvements * fred fixes * Update compiler, improve tests * Fix runtime, improve code frame * Add Markdown support * Tycho fixes * Fred fixes part 2 * Throw Error for WIP Features * Improve testing suite * Allow users to pass config to Vite * Fix npm install (#1407) * Automate publish on merge (#1408) * Add NPM_TOKEN to publish script (#1409) * Create .npmrc * Clean up astro deps (#1411) * Use new renderers (#1412) * feat: update compiler (#1421) * Try mocha/chai test runners (#1418) * Try mocha/chai test runners * Disable failing smoke test for now Will revert when next can build docs * Enable mocha in parallel mode * Remove warning * Update docs * Fix Windows bug * Fix internal imports * Fix styles * Fix CI release on merge to next (#1427) * Fix logger locale parsing (#1439) * fix(logger): locale parsing * Fixed issue of compiler crash when "c" locale was encountered * Return default locale if parsed locale is less than 2 chars long * chore: add changeset * Apply changes from #1387 * Add back in support for children (#1486) * Add back in support for children * Be more careful * Enables most slot tests (#1494) * Enables most slot tests * Use spreadAttributes * Add hydration to Solid renderer (#1479) (#1495) * feat: add hydration to Solid renderer * fix: intersection observer, move script to the end Co-authored-by: Ryan Carniato <ryansolid@gmail.com> * [next] support Astro.slots API (#1516) * [next] Support for custom elements (#1528) * [next] Support for custom elements * Fix eslint errors * eslint again * [next] Fix Astro.fetchContent (#1480) * fix Astro.fetchContent * fix(fetchContent): cast type Co-authored-by: Nate Moore <nate@skypack.dev> * Move hydration to the compiler (#1547) * Move hydration to the compiler * Move extracting url, export to util fn * Brings back astro-dynamic tests (#1548) * Implements top-level Astro + Astro.resolve (#1556) * Implements top-level Astro + Astro.resolve * Fix linting * [next] Update renderers (#1509) * chore: update vite * fix(renderers): point renderers to resolved server/client entrypoints * Chore: Enable more tests with new compiler changes (#1558) * [Next] `fetch` support (#1563) * fix: polyfill fetch in every ssr scenario * test(fetch): update fetch tests * docs: update data fetching guide to remove caveats about `fetch` and isomorphic usage * refactor: update regex for clarity * Restructure (#1569) * Upgrade to @astrojs/compiler 0.2.0 (#1584) * Use Vite fork (#1585) * Use Vite fork * Fix linting * Move Vite to vendor/ and add a license * Fix linting * Include the dist folder * Update files config * Markdown compilation (#1593) * Markdown compilation * remove debugger * Gets lit hydration working (#1595) * Gets Astro.fetchContent compilation to work (#1596) * Gets Astro.fetchContent compilation to work This fixes Astro.fetchContent so that we handle esbuild transforming the name of the nested Astro call. * Remove debugging * Update the tests * Remove another debugger * Update Vite to latest (#1597) * Add Prism syntax highlighting (#1598) * Scoped styles with markdown (#1599) * Bugfix: fix getStaticPaths() cache miss (#1602) * Fix build order (#1609) * Bugfix: restore build to get all paths earlier, when build. Same as main. * Also re-add timings * [next] blog example fully working (#1610) * Add environment variables docs (Closes #873) (#1587) * Added environment variables docs (Closes #873) * Fixed prefix * Remove numbered comments (#1611) * Chore: remove numbered comments * Clean up block comments * comment style fixes (#1614) * [next] Upgrade compiler (#1619) * [next] Upgrade compiler * Upgrade to latest compiler * Fix the path to global css * Removed debugger * feat: add fragment support to vite-plugin-astro (#1600) * [next] fix `.tsx` handling (#1620) * fix: support tsx in JSX plugin * fix: preserve JSX via esbuild, only use Babel for JSX compilation * fix: handle upcoming Vite API for `ssr` flag * [next] Add CSS preprocessing (#1589) * Add concept for style support in Astro * Update style preprocessor to use new compiler * fix: massage preprocessStyle type * fix: @astrojs/compiler types Co-authored-by: Nate Moore <nate@skypack.dev> * fix issues in blog-multiple-authors (#1621) * Move Sass to deps (#1622) * Update renderer API for Vite (#1623) * Update renderer API for Vite * Fix lit-element tests * Clean up comments * Throw friendly error if renderer provides viteConfig in a bad format * Fix changesets (#1628) * Remove cheerio scanning from build stats (#1629) * Minor change to jsxTransformOptions, update Renderer API docs (#1630) * [next] docs example fully working (#1627) * [next] docs example fully working * Upgrade compiler to unlock docs * Add `class:list` directive (#1612) * Add support for class:list directive The `class:list` directive serializes an expression of css class names. For React components, `className:list` is also supported. * Remove `className` support and React tests * Add tests for the absence of omitted classes * fix: `define:vars` scoping for styles (#1632) * feat: fix Debug component (#1633) * [next] Fix `<Markdown>` component (#1631) * fix: cleanup issues with <Markdown> component * fix: fix `content` usage with Markdown * [next] Fix `<Code>` component (#1635) * fix: enable Code component * test: update expect to chai format * Fixes solid (#1634) * Fixes solid * Rename the test * Rebase with next * Skip solid test for now * Add support for markdown plugins (#1650) * Fix broken next release (#1652) * Prevent passing to Svelte components * Prevent passing class to Vue components * Add CSS injection, fix portfolio example (#1648) * Fix portfolio example * Add .pcss extension * Update load ssr opts * Update packages/astro/src/runtime/server/index.ts Co-authored-by: Jonathan Neal <jonathantneal@hotmail.com> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com> Co-authored-by: Jonathan Neal <jonathantneal@hotmail.com> * Fixes external HMR (#1654) * Upgrade compiler version (#1655) Fixes docs and blog examples * Resolve renderers relative to the projectRoot (#1659) * Template fixes (#1656) * fix: dedupe hashes for identical islands (#1660) * fix: scope `define:vars` to `:root` for `<style global>` (#1663) * chore: update compiler to latest (#1664) * [next] fix island hydration inside of `<Markdown>` (#1665) * fix: create rehype plugin to smooth over island hydration bugs * refactor: remove debug code * chore: explain need for `rehypeIslands` * Bugfix: renderer-lit missing files on npm (#1669) * Force Vite to rebuild dependencies (#1670) * [next] Add `preact/compat` renderer (#1668) * feat: add preact/compat entry for `@astrojs/renderer-preact` * Update index.js * Bugfix: plugin-astro-fetch tries to append node-fetch to node-fetch (#1671) * Fix Vite race condition (#1674) * Fix with-nanostore deps (#1675) Adds missing Solid renderer * [next] Fix `resolveDependency` on Windows (#1666) * fix: Windows issue with resolveDependency util * chore: add comment * Update CONTRIBUTING.md (#1677) * Prevent scanning a user's deps (#1678) * Prevent scanning a user's deps * Remove unused things * remove unused util * Adding a changeset for the remark plugin * Config changes needed for stater template (#1680) This does 2 things: 1. Adds prismjs as a dep. 2. Adds shiki as an external. * Next bugs (#1681) * fix(#1679): hoisted <script> rendering * fix(#1679): do not print global for styles, but do for scripts * fix: update ObjectSet implementation * fix: dedupe elements in sets * [next] update compiler (#1683) * chore: update compiler * chore: update compiler (again) * Fix Astro HMR bottleneck (#1684) * Bugfix: JSX renderers can be declared in any order (#1686) * chore: update compiler (#1690) * Exclude lit-server from being optimized (#1691) This should get the lit example working from `npm`. * fix: exclude all renderer server entrypoints (#1692) * chore: update compiler (#1705) * fix: do not crash when Markdown has no content (#1702) * feat: improve support for third-party React packages (#1701) * Remove prism warning when no language is provided (#1703) * Remove prism warning when no language is provided * Add the plaintext language instead * retry deploy * chore: enter prerelease mode under `next` (#1707) * Updates to the changesets (#1708) * Updates to the changesets * Adds a changeset for astro-prism Co-authored-by: Fred K. Schott <fkschott@gmail.com> Co-authored-by: Nate Moore <nate@skypack.dev> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com> Co-authored-by: Pranav Karawale <52596591+obnoxiousnerd@users.noreply.github.com> Co-authored-by: Matthew Phillips <matthew@skypack.dev> Co-authored-by: Matthew Phillips <matthew@matthewphillips.info> Co-authored-by: Ryan Carniato <ryansolid@gmail.com> Co-authored-by: AsyncBanana <58297401+AsyncBanana@users.noreply.github.com> Co-authored-by: Jonathan Neal <jonathantneal@hotmail.com>
This commit is contained in:
parent
61e6bff5b1
commit
d84bfe719a
456 changed files with 191362 additions and 11926 deletions
5
.changeset/dull-bikes-doubt.md
Normal file
5
.changeset/dull-bikes-doubt.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/prism': minor
|
||||
---
|
||||
|
||||
Adds typings for the main entrypoint
|
11
.changeset/pink-trainers-learn.md
Normal file
11
.changeset/pink-trainers-learn.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Astro 0.21 Beta release! This introduces the new version of Astro that includes:
|
||||
|
||||
- A new, faster, Go-based compiler
|
||||
- A runtime backed by Vite, with faster dev experience
|
||||
- New features
|
||||
|
||||
See more at https://astro.build/blog/astro-021-preview/
|
40
.changeset/pre.json
Normal file
40
.changeset/pre.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"docs": "0.0.7",
|
||||
"@example/blog": "0.0.1",
|
||||
"@example/blog-multiple-authors": "0.0.1",
|
||||
"@example/docs": "0.0.1",
|
||||
"@example/framework-lit": "0.0.1",
|
||||
"@example/framework-multiple": "0.0.1",
|
||||
"@example/framework-preact": "0.0.1",
|
||||
"@example/framework-react": "0.0.1",
|
||||
"@example/framework-solid": "0.0.1",
|
||||
"@example/framework-svelte": "0.0.1",
|
||||
"@example/framework-vue": "0.0.1",
|
||||
"@example/minimal": "0.0.1",
|
||||
"@example/portfolio": "0.0.1",
|
||||
"@example/starter": "0.0.1",
|
||||
"@example/with-markdown": "0.0.1",
|
||||
"@example/with-markdown-plugins": "0.0.2",
|
||||
"@example/with-nanostores": "0.0.1",
|
||||
"@example/with-tailwindcss": "0.0.1",
|
||||
"astro": "0.20.12",
|
||||
"@astrojs/parser": "0.20.2",
|
||||
"@astrojs/prism": "0.2.2",
|
||||
"@astrojs/astro-test-builtins-dep": "0.0.1",
|
||||
"@astrojs/test-custom-element-renderer": "0.0.1",
|
||||
"create-astro": "0.6.6",
|
||||
"@astrojs/markdown-remark": "0.3.1",
|
||||
"@astrojs/renderer-lit": "0.1.2",
|
||||
"@astrojs/renderer-preact": "0.2.2",
|
||||
"@astrojs/renderer-react": "0.2.2",
|
||||
"@astrojs/renderer-solid": "0.1.1",
|
||||
"@astrojs/renderer-svelte": "0.1.2",
|
||||
"@astrojs/renderer-vue": "0.1.9",
|
||||
"astro-scripts": "0.0.1",
|
||||
"www": "1.1.0"
|
||||
},
|
||||
"changesets": []
|
||||
}
|
5
.changeset/silly-apples-build.md
Normal file
5
.changeset/silly-apples-build.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/markdown-remark': minor
|
||||
---
|
||||
|
||||
Adds prism support within the Markdown plugin.
|
10
.changeset/tiny-bulldogs-lie.md
Normal file
10
.changeset/tiny-bulldogs-lie.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
'@astrojs/renderer-lit': minor
|
||||
'@astrojs/renderer-preact': minor
|
||||
'@astrojs/renderer-react': minor
|
||||
'@astrojs/renderer-solid': minor
|
||||
'@astrojs/renderer-svelte': minor
|
||||
'@astrojs/renderer-vue': minor
|
||||
---
|
||||
|
||||
Updates the renderers to confirm to the new renderer API.
|
|
@ -3,3 +3,4 @@
|
|||
!packages/astro/**/*.js
|
||||
!packages/astro/**/*.ts
|
||||
packages/astro/test/**/*.js
|
||||
packages/astro/vendor/vite/**/*
|
|
@ -8,13 +8,13 @@ module.exports = {
|
|||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'no-console': 'warn',
|
||||
'no-shadow': 'error',
|
||||
'prefer-const': 'off',
|
||||
'prefer-rest-params': 'off',
|
||||
'require-jsdoc': 'off',
|
||||
// 'require-jsdoc': 'error', // re-enable this to enforce JSDoc for all functions
|
||||
},
|
||||
};
|
||||
|
|
59
.github/workflows/ci.yml
vendored
59
.github/workflows/ci.yml
vendored
|
@ -18,11 +18,13 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node_version: [12, 14, 16]
|
||||
include:
|
||||
- os: windows-latest
|
||||
node_version: 14
|
||||
# TODO: uncomment this (Vite has trouble resolving imports on Windows)
|
||||
# include:
|
||||
# - os: windows-latest
|
||||
# node_version: 14
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
LANG: en-us
|
||||
name: 'Test: node-${{ matrix.node_version }}, ${{ matrix.os }}'
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
@ -72,33 +74,34 @@ jobs:
|
|||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
smoke:
|
||||
runs-on: ubuntu-latest
|
||||
name: 'Smoke: node-14, ubuntu-latest'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# NOTE: temporarily disabled until `next` branch can build docs again
|
||||
# smoke:
|
||||
# runs-on: ubuntu-latest
|
||||
# name: 'Smoke: node-14, ubuntu-latest'
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
|
||||
- name: Set node version to 14
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
cache: 'yarn'
|
||||
# - name: Set node version to 14
|
||||
# uses: actions/setup-node@v2
|
||||
# with:
|
||||
# node-version: 14
|
||||
# cache: 'yarn'
|
||||
|
||||
- name: Debug
|
||||
run: yarn versions
|
||||
# - name: Debug
|
||||
# run: yarn versions
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --ignore-engines
|
||||
# - name: Install dependencies
|
||||
# run: yarn install --frozen-lockfile --ignore-engines
|
||||
|
||||
- name: Build
|
||||
run: yarn build:all
|
||||
# - name: Build
|
||||
# run: yarn build:all
|
||||
|
||||
- name: "Smoke Test: Build 'docs'"
|
||||
run: yarn build
|
||||
working-directory: ./docs
|
||||
# - name: "Smoke Test: Build 'docs'"
|
||||
# run: yarn build
|
||||
# working-directory: ./docs
|
||||
|
||||
- name: "Smoke Test: Build 'www'"
|
||||
run: yarn build
|
||||
working-directory: ./www
|
||||
# - name: "Smoke Test: Build 'www'"
|
||||
# run: yarn build
|
||||
# working-directory: ./www
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -12,3 +12,5 @@ package-lock.json
|
|||
|
||||
# do not commit .env files or any files that end with `.env`
|
||||
*.env
|
||||
|
||||
!packages/astro/vendor/vite/dist
|
|
@ -5,7 +5,9 @@ We welcome contributions of any size and skill level. As an open source project,
|
|||
> **Tip for new contributors:**
|
||||
> Take a look at [https://github.com/firstcontributions/first-contributions](https://github.com/firstcontributions/first-contributions) for helpful information on contributing
|
||||
|
||||
## Prerequisite
|
||||
## Quick Guide
|
||||
|
||||
### Prerequisite
|
||||
|
||||
```shell
|
||||
node: "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
|
@ -13,7 +15,7 @@ yarn: "^1.22.10"
|
|||
# otherwise, your build will fail
|
||||
```
|
||||
|
||||
## Setting up your local repo
|
||||
### Setting up your local repo
|
||||
|
||||
Astro uses yarn workspaces, so you should **always run `yarn install` from the top-level project directory.** running `yarn install` in the top-level project root will install dependencies for `astro`, `www`, `docs`, and every package in the repo.
|
||||
|
||||
|
@ -23,7 +25,7 @@ yarn install
|
|||
yarn build:all
|
||||
```
|
||||
|
||||
## Development
|
||||
### Development
|
||||
|
||||
```shell
|
||||
# starts a file-watching, live-reloading dev script for active development
|
||||
|
@ -32,17 +34,26 @@ yarn dev
|
|||
yarn build
|
||||
```
|
||||
|
||||
## Running tests
|
||||
#### Debugging Vite
|
||||
|
||||
You can debug vite by prefixing any command with `DEBUG` like so:
|
||||
|
||||
```
|
||||
DEBUG=vite:* astro dev # debug everything in Vite
|
||||
DEBUG=vite:[name] astro dev # debug specific process, e.g. "vite:deps" or "vite:transform"
|
||||
```
|
||||
|
||||
### Running tests
|
||||
|
||||
```shell
|
||||
# run this in the top-level project root to run all tests
|
||||
yarn test
|
||||
# run only a few tests, great for working on a single feature
|
||||
# (example - `yarn test rss` runs `astro-rss.test.js` tests)
|
||||
yarn test $STRING_MATCH
|
||||
# (example - `yarn test -g "RSS"` runs `astro-rss.test.js`)
|
||||
yarn test -g "$STRING_MATCH"
|
||||
```
|
||||
|
||||
## Other useful commands
|
||||
### Other useful commands
|
||||
|
||||
```shell
|
||||
# auto-format the entire project
|
||||
|
@ -56,7 +67,7 @@ yarn format
|
|||
yarn lint
|
||||
```
|
||||
|
||||
## Making a Pull Request
|
||||
### Making a Pull Request
|
||||
|
||||
When making a pull request, be sure to add a changeset when something has changed with Astro. Non-packages (`examples/*`, `docs/*`, and `www/*`) do not need changesets.
|
||||
|
||||
|
@ -64,7 +75,7 @@ When making a pull request, be sure to add a changeset when something has change
|
|||
yarn changeset
|
||||
```
|
||||
|
||||
## Running benchmarks
|
||||
### Running benchmarks
|
||||
|
||||
We have benchmarks to keep performance under control. You can run these by running (from the project root):
|
||||
|
||||
|
@ -83,7 +94,31 @@ node test/benchmark/dev.bench.js --save
|
|||
|
||||
Which will update the build and dev benchmarks.
|
||||
|
||||
# Releasing Astro
|
||||
## Code Structure
|
||||
|
||||
Server-side rendering (SSR) can be complicated. The Astro package (`packages/astro`) is structured in a way to help think about the different systems.
|
||||
|
||||
- `components/`: Built-in components to use in your project (e.g. `import Code from 'astro/components/Code.astro'`)
|
||||
- `src/`: Astro source
|
||||
- `@types/`: TypeScript types. These are centralized to cut down on circular dependencies
|
||||
- `cli/`: Code that powers the `astro` CLI command
|
||||
- `core/`: Code that executes **in the top-level scope** (in Node). Within, you’ll find code that powers the `astro build` and `astro dev` commands, as well as top-level SSR code.
|
||||
- `runtime/`: Code that executes **in different scopes** (i.e. not in a pure Node context). You’ll have to think about code differently here.
|
||||
- `client/`: Code that executes **in the browser.** Astro’s partial hydration code lives here, and only browser-compatible code can be used.
|
||||
- `server/`: Code that executes **inside Vite’s SSR.** Though this is a Node environment inside, this will be executed independently from `core/` and may have to be structured differently.
|
||||
- `vite-plugin-*/`: Any Vite plugins that Astro needs to run. For the most part, these also execute within Vite similar to `src/runtime/server/`, but it’s also helpful to think about them as independent modules. _Note: at the moment these are internal while they’re in development_
|
||||
|
||||
### Thinking about SSR
|
||||
|
||||
There are 3 contexts in which code executes:
|
||||
|
||||
- **Node.js**: this code lives in `src/core/`.
|
||||
- **Inside Vite**: this code lives in `src/runtime/server/`.
|
||||
- **In the browser**: this code lives in `src/runtime/client/`.
|
||||
|
||||
Understanding in which environment code runs, and at which stage in the process, can help clarify thinking about what Astro is doing. It also helps with debugging, for instance, if you’re working within `src/core/`, you know that your code isn’t executing within Vite, so you don’t have to debug Vite’s setup. But you will have to debug vite inside `runtime/server/`.
|
||||
|
||||
## Releasing Astro
|
||||
|
||||
_Note: Only priviledged contributors (L3+) can release new versions of Astro._
|
||||
|
||||
|
@ -91,7 +126,7 @@ The repo is set up with automatic releases, using the changeset GitHub action &
|
|||
|
||||
To release a new version of Astro, find the `Version Packages` PR, read it over, and merge it.
|
||||
|
||||
## Releasing PR preview snapshots
|
||||
### Releasing PR preview snapshots
|
||||
|
||||
Our release tool `changeset` has a feature for releasing "snapshot" releases from a PR or custom branch. These are npm package publishes that live temporarily, so that you can give users a way to test a PR before merging. This can be a great way to get early user feedback while still in the PR review process.
|
||||
|
||||
|
@ -112,7 +147,7 @@ git reset --hard
|
|||
|
||||
Full documentation: https://github.com/atlassian/changesets/blob/main/docs/snapshot-releases.md
|
||||
|
||||
## Releasing `astro@next` (aka "prerelease mode")
|
||||
### Releasing `astro@next` (aka "prerelease mode")
|
||||
|
||||
Sometimes, the repo will enter into "prerelease mode". In prerelease mode, our normal release process will publish npm versions under the `next` dist-tag, instead of the default `latest` tag. We do this from time-to-time to test large features before sharing them with the larger Astro audience.
|
||||
|
||||
|
@ -154,7 +189,7 @@ When in prerelease mode, the automatic PR release process will no longer release
|
|||
1. Go to https://github.com/snowpackjs/astro/releases/new and create a new release. Copy the new changelog entry from https://github.com/snowpackjs/astro/blob/latest/packages/astro/CHANGELOG.md.
|
||||
1. Post in Discord #announcements channel, if needed!
|
||||
|
||||
# Translations
|
||||
## Translations
|
||||
|
||||
Help us translate [docs.astro.build](https://docs.astro.build/) into as many languages as possible! This can be a great way to get involved with open source development without having to code.
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"broken-link-checker": "^0.7.8",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pa11y-ci": "^2.4.2",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier": "^2.4.1",
|
||||
"start-server-and-test": "^1.12.6"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -24,6 +24,7 @@ export const SIDEBAR = {
|
|||
{ text: 'RSS', link: 'guides/rss' },
|
||||
{ text: 'Supported Imports', link: 'guides/imports' },
|
||||
{ text: 'Aliases', link: 'guides/aliases' },
|
||||
{ text: 'Environment Variables', link: 'guides/environment-variables' },
|
||||
{ text: 'Deploy to the web', link: 'guides/deploy' },
|
||||
{ text: 'Publish to npm', link: 'guides/publish-to-npm' },
|
||||
|
||||
|
|
|
@ -32,11 +32,10 @@ console.log(data);
|
|||
|
||||
## Using `fetch()` outside of Astro Components
|
||||
|
||||
If you want to use `fetch()` in a non-astro component, use the [`node-fetch`](https://github.com/node-fetch/node-fetch) library:
|
||||
If you want to use `fetch()` in a non-astro component, it is also globally available:
|
||||
|
||||
```tsx
|
||||
// Movies.tsx
|
||||
import fetch from 'node-fetch';
|
||||
import type { FunctionalComponent } from 'preact';
|
||||
import { h } from 'preact';
|
||||
|
||||
|
@ -55,11 +54,3 @@ const Movies: FunctionalComponent = () => {
|
|||
|
||||
export default Movies;
|
||||
```
|
||||
|
||||
If you load a component using `node-fetch` [interactively](/core-concepts/component-hydration), with `client:load`, `client:visible`, etc., you'll need to either not use `node-fetch` or switch to an [isomorphic](https://en.wikipedia.org/wiki/Isomorphic_JavaScript) library that will run both at build time and on the client, as the [`node-fetch` README.md](https://github.com/node-fetch/node-fetch#motivation) recommends:
|
||||
|
||||
> Instead of implementing XMLHttpRequest in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native http to fetch API directly? Hence, node-fetch, minimal code for a window.fetch compatible API on Node.js runtime.
|
||||
>
|
||||
> See Jason Miller's [isomorphic-unfetch](https://www.npmjs.com/package/isomorphic-unfetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports node-fetch for server-side, whatwg-fetch for client-side).
|
||||
|
||||
> Quoted from https://github.com/node-fetch/node-fetch#motivation
|
||||
|
|
29
docs/src/pages/guides/environment-variables.md
Normal file
29
docs/src/pages/guides/environment-variables.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
layout: ~/layouts/MainLayout.astro
|
||||
title: Using environment variables
|
||||
description: Learn how to use environment variables in an Astro project.
|
||||
---
|
||||
|
||||
Astro uses Vite for environment variables, and allows you to use any of its methods to get and set environment variables. Note that all environment variables must be prefixed with `VITE_` to be accessible by client side code.
|
||||
|
||||
## Setting environment variables
|
||||
|
||||
Vite includes `dotenv` by default, allowing you to easily set environment variables with no configuration in Astro projects. You can also attach a mode (either `production` or `development`) to the filename, like `.env.production` or `.env.development`, which makes the environment variables only take effect in that mode.
|
||||
|
||||
Just create a `.env` file in the project directory and add some variables to it.
|
||||
|
||||
```bash
|
||||
# .env
|
||||
VITE_POKEAPI="https://pokeapi.co/api/v2"
|
||||
```
|
||||
|
||||
## Getting environment variables
|
||||
|
||||
Instead of using `process.env`, with Vite you use `import.meta.env`, which uses the `import.meta` feature added in ES2020 (don't worry about browser support though, Vite replaces all `import.meta.env` mentions with static values). For example, to get the `VITE_POKEAPI` environment variable, you could use `import.meta.env.VITE_POKEAPI`.
|
||||
|
||||
```js
|
||||
fetch(`${import.meta.env.VITE_POKEAPI}/pokemon/squirtle`
|
||||
```
|
||||
|
||||
> ⚠️WARNING⚠️:
|
||||
> Because Vite statically replaces `import.meta.env`, you cannot access it with dynamic keys like `import.meta.env[key]`.
|
|
@ -6,62 +6,140 @@ description: Learn how to style components with Astro.
|
|||
|
||||
Astro includes special handling to make writing CSS as easy as possible. Styling inside of Astro components is done by adding a `<style>` tag anywhere.
|
||||
|
||||
By default, all Astro component styles are **scoped**, meaning they only apply to the current component. These styles are automatically extracted and optimized for you in the final build, so that you don't need to worry about style loading.
|
||||
## Astro component styles
|
||||
|
||||
To create global styles, add a `:global()` wrapper around a selector (the same as if you were using [CSS Modules][css-modules]).
|
||||
By default, all Astro component styles are **scoped**, meaning they only apply to the current component. This can be very easy to work with, as you only have to worry about what’s in your current document at any given time.
|
||||
|
||||
```html
|
||||
<!-- src/components/MyComponent.astro -->
|
||||
<style>
|
||||
/* Scoped class selector within the component */
|
||||
.scoped {
|
||||
font-weight: bold;
|
||||
.text {
|
||||
font-family: cursive;
|
||||
}
|
||||
/* Scoped element selector within the component */
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
/* Global style */
|
||||
</style>
|
||||
|
||||
<h1>I’m a scoped style and I’m red!</h1>
|
||||
<p class="text">I'm a scoped style and I’m cursive!</p>
|
||||
```
|
||||
|
||||
Note that the `h1` selector won’t bleed out of the current component! These styles won’t apply any other `h1` tags outside this document. Not even child components.
|
||||
|
||||
_Tip: even though you can use element selectors, using classnames is preferred. This is not only slightly more performant, but is also easier to read, especially in a large document._
|
||||
|
||||
### Global styles
|
||||
|
||||
Of course, the real power of CSS is being able to reuse as much as possible! The preferred method of loading global styles is by using a standard `<link>` tag like you’re used to. It can even be used in conjunction with Astro’s scoped `<style>` tag:
|
||||
|
||||
```html
|
||||
<!-- src/pages/index.astro -->
|
||||
<head>
|
||||
<!-- load styles from src/styles/utils.css using Astro.resolve() -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href={Astro.resolve('../styles/utils.css')} />
|
||||
</head>
|
||||
<body>
|
||||
<!-- scoped Astro styles that apply only to the current page (not to children or other components) -->
|
||||
<style>
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- the ".title" class is scoped, but we can also use our global "align-center" and "margin top: 4" utility classes from utils.css -->
|
||||
<h1 class="title align-center mt4">Scoped Page Title</h1>
|
||||
</body>
|
||||
```
|
||||
|
||||
_Note: `Astro.resolve()` is a handy utility that helps resolve files from anywhere ([docs][astro-resolve])_
|
||||
|
||||
#### Styling children
|
||||
|
||||
If you’d like scoped styles to apply to children, you can use the special `:global()` function borrowed from [CSS Modules][css-modules]:
|
||||
|
||||
```astro
|
||||
<!-- src/components/MyComponent.astro -->
|
||||
---
|
||||
import PostContent from './Post.astro';
|
||||
---
|
||||
<style>
|
||||
/* Scoped to current component only */
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* Scoped to all descendents of the scoped .blog-post class */
|
||||
.blog-post :global(h1) {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Title</h1>
|
||||
<article class="blog-post">
|
||||
<PostContent />
|
||||
</article>
|
||||
```
|
||||
|
||||
This is a great way to style things like blog posts, or documents with CMS-powered content where the contents live outside of Astro. But be careful when styling children unconditionally, as it breaks component encapsulation. Components that appear different based on whether or not they have a certain parent component can become unwieldy quickly.
|
||||
|
||||
#### Global styles within style tag
|
||||
|
||||
If you’d like to use global styles but you don’t want to use a normal `<link>` tag (recommended), there is a `<style global>` escape hatch:
|
||||
|
||||
```html
|
||||
<style global>
|
||||
/* Applies to all h1 tags in your entire site */
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Globally-styled</h1>
|
||||
```
|
||||
|
||||
You can achieve the same by using the `:global()` function at the root of a selector:
|
||||
|
||||
```html
|
||||
<style>
|
||||
/* Applies to all h1 tags in your entire site */
|
||||
:global(h1) {
|
||||
font-size: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scoped">I'm a scoped style and only apply to this component</div>
|
||||
<h1>I have both scoped and global styles</h1>
|
||||
/* normal scoped h1 that applies to this file only */
|
||||
h1 {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
To include every selector in a `<style>` as global styles, use `<style global>`. It's best to avoid using this escape hatch if possible, but it can be useful if you find yourself repeating `:global()` multiple times in the same `<style>`.
|
||||
It’s recommended to only use this in scenarios where a `<link>` tag won’t work. It’s harder to track down errant global styles when they’re scattered around and not in a central CSS file.
|
||||
|
||||
```html
|
||||
<!-- src/components/MyComponent.astro -->
|
||||
<style>
|
||||
/* Scoped class selector within the component */
|
||||
.scoped {
|
||||
font-weight: bold;
|
||||
}
|
||||
/* Scoped element selector within the component */
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
📚 Read our full guide on [Astro component syntax][astro-component] to learn more about using the `<style>` tag.
|
||||
|
||||
<style global>
|
||||
/* Global style */
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
</style>
|
||||
## Autoprefixer
|
||||
|
||||
<div class="scoped">I'm a scoped style and only apply to this component</div>
|
||||
<h1>I have both scoped and global styles</h1>
|
||||
[Autoprefixer][autoprefixer] takes care of cross-browser CSS compatibility for you. Use it in astro by installing it (`npm install --save-dev autoprefixer`) and adding a `postcss.config.cjs` file to the root of your project:
|
||||
|
||||
```js
|
||||
// postcss.config.cjs
|
||||
module.exports = {
|
||||
autoprefixer: {
|
||||
/* (optional) autoprefixer settings */
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
📚 Read our full guide on [Astro component syntax](/core-concepts/astro-components#css-styles) to learn more about using the `<style>` tag.
|
||||
_Note: Astro v0.21 and later requires this manual setup for autoprefixer. Previous versions ran this automatically._
|
||||
|
||||
## Cross-Browser Compatibility
|
||||
## PostCSS
|
||||
|
||||
We also automatically add browser prefixes using [Autoprefixer][autoprefixer]. By default, Astro loads the [Browserslist defaults][browserslist-defaults], but you may also specify your own by placing a [Browserslist][browserslist] file in your project root.
|
||||
You can use any PostCSS plugin by adding a `postcss.config.cjs` file to the root of your project. Follow the documentation for the plugin you’re trying to install for configuration and setup.
|
||||
|
||||
---
|
||||
|
||||
|
@ -78,7 +156,7 @@ Styling in Astro is meant to be as flexible as you'd like it to be! The followin
|
|||
|
||||
¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don't need dynamic values)_
|
||||
|
||||
All styles in Astro are automatically [**autoprefixed**](#cross-browser-compatibility), minified and bundled, so you can just write CSS and we'll handle the rest ✨.
|
||||
All styles in Astro are automatically minified and bundled, so you can just write CSS and we'll handle the rest ✨.
|
||||
|
||||
---
|
||||
|
||||
|
@ -104,33 +182,36 @@ Vue in Astro supports the same methods as `vue-loader` does:
|
|||
|
||||
Svelte in Astro also works exactly as expected: [Svelte Styling Docs][svelte-style].
|
||||
|
||||
### 👓 Sass
|
||||
### 🎨 CSS Preprocessors (Sass, Stylus, etc.)
|
||||
|
||||
Astro also supports [Sass][sass] out-of-the-box. To enable for each framework:
|
||||
Astro supports CSS preprocessors such as [Sass][sass], [Stylus][stylus], and [Less][less] through [Vite][vite-preprocessors]. It can be enabled via the following:
|
||||
|
||||
- **Sass**: Run `npm install -D sass` and use `<style lang="scss">` or `<style lang="sass">` (indented) in `.astro` files
|
||||
- **Stylus**: Run `npm install -D stylus` and use `<style lang="styl">` or `<style lang="stylus">` in `.astro` files
|
||||
- **Less**: Run `npm install -D less` and use `<style lang="less">` in `.astro` files.
|
||||
|
||||
You can also use all of the above within JS frameworks as well! Simply follow the patterns each framework recommends:
|
||||
|
||||
- **Astro**: `<style lang="scss">` or `<style lang="sass">`
|
||||
- **React** / **Preact**: `import Styles from './styles.module.scss'`;
|
||||
- **Vue**: `<style lang="scss">` or `<style lang="sass">`
|
||||
- **Svelte**: `<style lang="scss">` or `<style lang="sass">`
|
||||
- **Vue**: `<style lang="scss">`
|
||||
- **Svelte**: `<style lang="scss">`
|
||||
|
||||
💁 Sass is great! If you haven't used Sass in a while, please give it another try. The new and improved [Sass Modules][sass-use] are a great fit with modern web development, and it's blazing-fast since being rewritten in Dart. And the best part? **You know it already!** Use `.scss` to write familiar CSS syntax you're used to, and only sprinkle in Sass features if/when you need them.'
|
||||
Additionally, [PostCSS](#-postcss) is supported, but the setup is [slightly different](#-postcss).
|
||||
|
||||
**Note**: If you use .scss files rather than .css files, your stylesheet links should still point to .css files because of Astro’s auto-compilation process. When Astro “needs” the styling files, it’ll be “looking for” the final .css file(s) that it compiles from the .scss file(s). For example, if you have a .scss file at `./src/styles/global.scss`, use this link: `<link rel="stylesheet" href="{Astro.resolve('../styles/global.css')}">` — **not** `<link rel="stylesheet" href="{Astro.resolve('../styles/global.scss')}">`.
|
||||
_Note: CSS inside `public/` will **not** be transformed! Place it within `src/` instead._
|
||||
|
||||
### 🍃 Tailwind
|
||||
|
||||
> Note that Astro's Tailwind support _only_ works with Tailwind JIT mode.
|
||||
|
||||
Astro can be configured to use [Tailwind][tailwind] easily! Install the dependencies:
|
||||
|
||||
```
|
||||
npm install --save-dev tailwindcss
|
||||
```
|
||||
|
||||
And also create a `tailwind.config.js` in your project root:
|
||||
And create 2 files in your project root: `tailwind.config.cjs` and `postcss.config.cjs`:
|
||||
|
||||
```js
|
||||
// tailwind.config.js
|
||||
// tailwind.config.cjs
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: ['./public/**/*.html', './src/**/*.{astro,js,jsx,svelte,ts,tsx,vue}'],
|
||||
|
@ -138,14 +219,10 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
Be sure to add the config path to `astro.config.mjs`, so that Astro enables JIT support in the dev server.
|
||||
|
||||
```diff
|
||||
// astro.config.mjs
|
||||
export default {
|
||||
+ devOptions: {
|
||||
+ tailwindConfig: './tailwind.config.js',
|
||||
+ },
|
||||
```js
|
||||
// postcss.config.cjs
|
||||
module.exports = {
|
||||
tailwind: {},
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -162,7 +239,7 @@ As an alternative to `src/styles/global.css`, You may also add Tailwind utilitie
|
|||
|
||||
#### Migrating from v0.19
|
||||
|
||||
As of [version 0.20.0](https://github.com/snowpackjs/astro/releases/tag/astro%400.20.0), Astro will no longer bundle, build and process `public/` files. Previously, we'd recommended putting your tailwind files in the `public/` directory. If you started a project with this pattern, you should move any Tailwind styles into the `src` directory and import them in your template using [Astro.resolve()](/reference/api-reference#astroresolve):
|
||||
As of [version 0.20.0](https://github.com/snowpackjs/astro/releases/tag/astro%400.20.0), Astro will no longer bundle, build and process `public/` files. Previously, we'd recommended putting your tailwind files in the `public/` directory. If you started a project with this pattern, you should move any Tailwind styles into the `src` directory and import them in your template using [Astro.resolve()][astro-resolve]:
|
||||
|
||||
```astro
|
||||
<link
|
||||
|
@ -171,44 +248,14 @@ As of [version 0.20.0](https://github.com/snowpackjs/astro/releases/tag/astro%40
|
|||
>
|
||||
```
|
||||
|
||||
### Importing from npm
|
||||
|
||||
If you want to import third-party libraries into an Astro component, you can use a `<style lang="scss">` tag to enable [Sass][sass] and use the [@use][sass-use] rule.
|
||||
|
||||
```html
|
||||
<!-- Loads Boostrap -->
|
||||
<style lang="scss">
|
||||
@use "bootstrap/scss/bootstrap";
|
||||
</style>
|
||||
```
|
||||
|
||||
### 🎭 PostCSS
|
||||
|
||||
[PostCSS](https://postcss.org/) is a popular CSS transpiler with support for [a huge ecosystem of plugins.](https://github.com/postcss/postcss#plugins)
|
||||
|
||||
**To use PostCSS with Snowpack:** add the [@snowpack/plugin-postcss](https://www.npmjs.com/package/@snowpack/plugin-postcss) plugin to your project.
|
||||
|
||||
```diff
|
||||
// snowpack.config.js
|
||||
"plugins": [
|
||||
+ "@snowpack/plugin-postcss"
|
||||
]
|
||||
```
|
||||
|
||||
PostCSS requires a [`postcss.config.js`](https://github.com/postcss/postcss#usage) file in your project. By default, the plugin looks in the root directory of your project, but you can customize this yourself with the `config` option. See [the plugin README](https://www.npmjs.com/package/@snowpack/plugin-postcss) for all available options.
|
||||
|
||||
```js
|
||||
// postcss.config.js
|
||||
// Example (empty) postcss config file
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// ...
|
||||
],
|
||||
};
|
||||
```
|
||||
Using PostCSS is as simple as placing a [`postcss.config.cjs`](https://github.com/postcss/postcss#usage) file in the root of your project.
|
||||
|
||||
Be aware that this plugin will run on all CSS in your project, including any files that compiled to CSS (like `.scss` Sass files, for example).
|
||||
|
||||
_Note: CSS in `public/` **will not be transformed!** Instead, place it within `src/` if you’d like PostCSS to run over your styles._
|
||||
|
||||
## Bundling
|
||||
|
||||
All CSS is minified and bundled automatically for you in running `astro build`. Without getting too in the weeds, the general rules are:
|
||||
|
@ -550,6 +597,8 @@ This guide wouldn't be possible without the following blog posts, which expand o
|
|||
Also please check out the [Stylelint][stylelint] project to whip your styles into shape. You lint your JS, why not your CSS?
|
||||
|
||||
[autoprefixer]: https://github.com/postcss/autoprefixer
|
||||
[astro-component]: /core-concepts/astro-components#css-styles
|
||||
[astro-resolve]: /reference/api-reference#astroresolve
|
||||
[bem]: http://getbem.com/introduction/
|
||||
[box-model]: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model
|
||||
[browserslist]: https://github.com/browserslist/browserslist
|
||||
|
@ -560,6 +609,7 @@ Also please check out the [Stylelint][stylelint] project to whip your styles int
|
|||
[css-treeshaking]: https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/
|
||||
[fouc]: https://en.wikipedia.org/wiki/Flash_of_unstyled_content
|
||||
[layout-isolated]: https://web.archive.org/web/20210227162315/https://visly.app/blogposts/layout-isolated-components
|
||||
[less]: https://lesscss.org/
|
||||
[issues]: https://github.com/snowpackjs/astro/issues
|
||||
[magic-number]: https://css-tricks.com/magic-numbers-in-css/
|
||||
[material-ui]: https://material.io/components
|
||||
|
@ -568,11 +618,13 @@ Also please check out the [Stylelint][stylelint] project to whip your styles int
|
|||
[sass-use]: https://sass-lang.com/documentation/at-rules/use
|
||||
[smacss]: http://smacss.com/
|
||||
[styled-components]: https://styled-components.com/
|
||||
[stylus]: https://stylus-lang.com/
|
||||
[styled-jsx]: https://github.com/vercel/styled-jsx
|
||||
[stylelint]: https://stylelint.io/
|
||||
[svelte-style]: https://svelte.dev/docs#style
|
||||
[tailwind]: https://tailwindcss.com
|
||||
[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css
|
||||
[utility-css]: https://frontstuff.io/in-defense-of-utility-first-css
|
||||
[vite-preprocessors]: https://vitejs.dev/guide/features.html#css-pre-processors
|
||||
[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html
|
||||
[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html
|
||||
|
|
|
@ -22,7 +22,3 @@ export default /** @type {import('astro').AstroUserConfig} */ (
|
|||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Snowpack Config
|
||||
|
||||
Astro is powered internally by Snowpack. You can configure Snowpack directly by creating a `snowpack.config.mjs` file. See [snowpack.dev](https://www.snowpack.dev/reference/configuration) for full documentation on this file.
|
||||
|
|
|
@ -38,8 +38,8 @@ A renderer should include any framework dependencies as package dependencies. Fo
|
|||
// package.json
|
||||
"name": "@astrojs/renderer-react",
|
||||
"dependencies": {
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -49,22 +49,32 @@ This means that Astro users don't need to install the UI framework packages them
|
|||
|
||||
The main entrypoint of a renderer is a simple JS file which exports a manifest for the renderer. The required values are `name`, `server`, and `client`.
|
||||
|
||||
Additionally, this entrypoint can define a [Snowpack plugin](https://www.snowpack.dev/guides/plugins) that should be used to load non-JavaScript files.
|
||||
Additionally, this entrypoint can define a [Vite config object](https://vitejs.dev/config/) that should be used to load non-JavaScript files.
|
||||
|
||||
```js
|
||||
import myVitePlugin from 'vite-plugin-myplugin';
|
||||
|
||||
export default {
|
||||
name: '@astrojs/renderer-xxx', // the renderer name
|
||||
client: './client.js', // relative path to the client entrypoint
|
||||
server: './server.js', // optional, relative path to the server entrypoint
|
||||
snowpackPlugin: '@snowpack/plugin-xxx', // optional, the name of a snowpack plugin to inject
|
||||
snowpackPluginOptions: { example: true }, // optional, any options to be forwarded to the snowpack plugin
|
||||
knownEntrypoints: ['framework'], // optional, entrypoint modules that will be used by compiled source
|
||||
external: ['dep'], // optional, dependencies that should not be built by snowpack
|
||||
polyfills: ['./shadow-dom-polyfill.js'], // optional, module scripts that should be loaded before client hydration.
|
||||
hydrationPolyfills: ['./hydrate-framework.js'], // optional, polyfills that need to run before hydration ever occurs.
|
||||
jsxImportSource: 'preact', // optional, the name of the library from which JSX is imported
|
||||
jsxTransformOptions: async () => {
|
||||
// optional, a function to transform JSX files
|
||||
server: './server.js', // (optional) relative path to the server entrypoint
|
||||
viteConfig(options = { mode: 'development', command: 'serve' }) {
|
||||
// (optional) return config object for Vite (https://vitejs.dev/config/)
|
||||
return {
|
||||
plugins: [myVitePlugin()], // tip: usually this will depend on a Vite plugin
|
||||
optimizeDeps: {
|
||||
include: ['my-client-dep'], // tip: it’s always a good idea to specify any client-side hydration deps here
|
||||
},
|
||||
ssr: {
|
||||
external: ['my-client-dep/node/server.js'], // tip: use ssr.external in case you encounter code meant only for Node
|
||||
},
|
||||
};
|
||||
},
|
||||
polyfills: ['./shadow-dom-polyfill.js'], // (optional) scripts that should be injected on the page for the component
|
||||
hydrationPolyfills: ['./hydrate-framework.js'], // (optional) polyfills that need to run before hydration ever occurs
|
||||
jsxImportSource: 'preact', // (optional) the name of the library from which JSX is imported ("react", "preact", "solid-js", etc.)
|
||||
jsxTransformOptions: async (options = { mode: 'development', ssr: true }) => {
|
||||
// (optional) a function to transform JSX files
|
||||
const {
|
||||
default: { default: jsx },
|
||||
} = await import('@babel/plugin-transform-react-jsx');
|
||||
|
@ -91,19 +101,15 @@ This is an `async` function that returns information about how to transform matc
|
|||
|
||||
> Keep in mind that this transform doesn't need to handle TSX separately from JSX, Astro handles that for you!
|
||||
|
||||
The arguments passed to `jsxTransformOptions` follow Snowpack's `load()` plugin hook. These allow you to pass separate Babel configurations for various conditions, like if your files should be compiled differently in SSR mode.
|
||||
`jsxTransformOptions` receives context about whether it’s running in `development` or `production` mode, as well as whether or not it’s running in SSR or client hydration. These allow you to pass separate Babel configurations for various conditions, like if your files should be compiled differently in SSR mode.
|
||||
|
||||
```ts
|
||||
export interface JSXTransformOptions {
|
||||
(context: {
|
||||
/** True if builder is in dev mode (`astro dev`) */
|
||||
isDev: boolean;
|
||||
/** True if HMR is enabled (add any HMR code to the output here). */
|
||||
isHmrEnabled: boolean;
|
||||
/** "development" or "production" */
|
||||
mode: string;
|
||||
/** True if builder is in SSR mode */
|
||||
isSSR: boolean;
|
||||
/** True if file being transformed is inside of a package. */
|
||||
isPackage: boolean;
|
||||
ssr: boolean;
|
||||
}) => {
|
||||
plugins?: any[];
|
||||
presets?: any[];
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"don": {
|
||||
"name": "Don Quixote",
|
||||
"image": "/authors/don.jpg"
|
||||
"image": "/images/don.jpg"
|
||||
},
|
||||
"sancho": {
|
||||
"name": "Sancho Panza",
|
||||
"image": "/authors/sancho.jpg"
|
||||
"image": "/images/sancho.jpg"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
---
|
||||
|
||||
import MainHead from '../../components/MainHead.astro';
|
||||
import Nav from '../../components/Nav.astro';
|
||||
import PostPreview from '../../components/PostPreview.astro';
|
||||
import Pagination from '../../components/Pagination.astro';
|
||||
|
||||
// page
|
||||
let title = 'Don’s Blog';
|
||||
let description = 'An example blog on Astro';
|
||||
let canonicalURL = Astro.request.canonicalURL;
|
||||
|
||||
// collection
|
||||
import authorData from '../../data/authors.json';
|
||||
|
||||
export function getStaticPaths() {
|
||||
const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
|
||||
let allAuthorsUnique = [...new Set(allPosts.map(p => p.author))];
|
||||
return allAuthorsUnique.map(author => ({params: {author}, props: {allPosts}}));
|
||||
}
|
||||
|
||||
interface MarkdownFrontmatter {
|
||||
date: number;
|
||||
description: string;
|
||||
|
@ -19,14 +19,10 @@ interface MarkdownFrontmatter {
|
|||
author: string;
|
||||
}
|
||||
|
||||
export function getStaticPaths() {
|
||||
const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
|
||||
let allAuthorsUnique = [...new Set(allPosts.map(p => p.author))];
|
||||
return allAuthorsUnique.map(author => ({params: {author}, props: {allPosts}}));
|
||||
}
|
||||
|
||||
const { allPosts } = Astro.props;
|
||||
const { params } = Astro.request;
|
||||
const { params, canonicalURL } = Astro.request;
|
||||
const title = 'Don’s Blog';
|
||||
const description = 'An example blog on Astro';
|
||||
|
||||
/** filter posts by author, sort by date */
|
||||
const posts = allPosts
|
||||
|
|
|
@ -3,20 +3,8 @@ import MainHead from '../../components/MainHead.astro';
|
|||
import Nav from '../../components/Nav.astro';
|
||||
import PostPreview from '../../components/PostPreview.astro';
|
||||
import Pagination from '../../components/Pagination.astro';
|
||||
|
||||
// page
|
||||
let title = 'Don’s Blog';
|
||||
let description = 'An example blog on Astro';
|
||||
let canonicalURL = Astro.request.canonicalURL;
|
||||
|
||||
// collection
|
||||
interface MarkdownFrontmatter {
|
||||
date: number;
|
||||
description: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
import authorData from '../../data/authors.json';
|
||||
|
||||
export async function getStaticPaths({paginate, rss}) {
|
||||
const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
|
||||
const sortedPosts = allPosts.sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
|
||||
|
@ -39,6 +27,19 @@ export async function getStaticPaths({paginate, rss}) {
|
|||
return paginate(sortedPosts, {pageSize: 1});
|
||||
}
|
||||
|
||||
// page
|
||||
let title = 'Don’s Blog';
|
||||
let description = 'An example blog on Astro';
|
||||
let canonicalURL = Astro.request.canonicalURL;
|
||||
|
||||
// collection
|
||||
interface MarkdownFrontmatter {
|
||||
date: number;
|
||||
description: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
||||
const { page } = Astro.props;
|
||||
---
|
||||
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,31 +90,19 @@ body {
|
|||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
font-family: var(--font-body);
|
||||
font-size: 1rem;
|
||||
font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
body {
|
||||
.wrapper {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 65em;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
--gutter: 0.5rem;
|
||||
--doc-padding: 2rem;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: minmax(var(--gutter), 1fr) minmax(0, var(--max-width)) minmax(var(--gutter), 1fr);
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.layout > :is(main, article) {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
|
|
|
@ -1,36 +1,3 @@
|
|||
<header class="layout">
|
||||
<article>
|
||||
<h1>
|
||||
<a href="/">
|
||||
<svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
#flame {
|
||||
fill: #ff5d01;
|
||||
}
|
||||
#a {
|
||||
fill: #000014;
|
||||
}
|
||||
</style>
|
||||
<title>Logo</title>
|
||||
<path
|
||||
id="a"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||
/>
|
||||
<path
|
||||
id="flame"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||
/>
|
||||
</svg>
|
||||
<span>My Blog</span>
|
||||
</a>
|
||||
</h1>
|
||||
</article>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
padding-top: 1rem;
|
||||
|
@ -88,3 +55,36 @@ h1 a {
|
|||
display: inline-flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<header class="wrapper">
|
||||
<article>
|
||||
<h1>
|
||||
<a href="/">
|
||||
<svg class="logo" width="32" height="32" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
#flame {
|
||||
fill: #ff5d01;
|
||||
}
|
||||
#a {
|
||||
fill: #000014;
|
||||
}
|
||||
</style>
|
||||
<title>Logo</title>
|
||||
<path
|
||||
id="a"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||
/>
|
||||
<path
|
||||
id="flame"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||
/>
|
||||
</svg>
|
||||
<span>My Blog</span>
|
||||
</a>
|
||||
</h1>
|
||||
</article>
|
||||
</header>
|
||||
|
|
|
@ -26,13 +26,12 @@ const { post } = Astro.props;
|
|||
}
|
||||
|
||||
header {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
padding-bottom: 2rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.title,
|
||||
|
|
9
examples/blog/src/components/Heading.astro
Normal file
9
examples/blog/src/components/Heading.astro
Normal file
|
@ -0,0 +1,9 @@
|
|||
<h1>
|
||||
<slot/>
|
||||
</h1>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,4 @@
|
|||
---
|
||||
import { Markdown } from 'astro/components';
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import BlogHeader from '../components/BlogHeader.astro';
|
||||
import BlogPost from '../components/BlogPost.astro';
|
||||
|
@ -7,6 +6,7 @@ import BlogPost from '../components/BlogPost.astro';
|
|||
const {content} = Astro.props;
|
||||
const {title, description, publishDate, author, heroImage, permalink, alt} = content;
|
||||
---
|
||||
|
||||
<html lang={ content.lang || 'en' }>
|
||||
<head>
|
||||
<BaseHead title={title} description={description} permalink={permalink} />
|
||||
|
@ -15,9 +15,11 @@ const {title, description, publishDate, author, heroImage, permalink, alt} = con
|
|||
|
||||
<body>
|
||||
<BlogHeader />
|
||||
<div class="wrapper">
|
||||
<BlogPost title={title} author={author} heroImage={heroImage} publishDate={publishDate} alt={alt}>
|
||||
<slot />
|
||||
</BlogPost>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ let permalink = 'https://example.com/';
|
|||
|
||||
// Data Fetching: List all Markdown posts in the repo.
|
||||
|
||||
let allPosts = Astro.fetchContent<MarkdownFrontmatter>('./posts/*.md');
|
||||
let allPosts = await Astro.fetchContent('./posts/*.md');
|
||||
allPosts = allPosts.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
|
@ -30,14 +30,6 @@ allPosts = allPosts.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(
|
|||
<link rel="stylesheet" href="/blog.css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 3.5rem 1fr;
|
||||
--gutter: 0.5rem;
|
||||
--doc-padding: 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -75,7 +67,7 @@ allPosts = allPosts.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(
|
|||
|
||||
<body>
|
||||
<BlogHeader />
|
||||
<div class="layout">
|
||||
<div class="wrapper">
|
||||
<main class="content">
|
||||
<section class="intro">
|
||||
<h1 class="latest">{title}</h1>
|
||||
|
|
16
examples/blog/src/pages/posts/index.md
Normal file
16
examples/blog/src/pages/posts/index.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
setup: |
|
||||
import Layout from '../../layouts/BlogPost.astro'
|
||||
import Cool from '../../components/Author.astro'
|
||||
title: Hello world!
|
||||
publishDate: 12 Sep 2021
|
||||
name: Nate Moore
|
||||
value: 128
|
||||
description: Just a Hello World Post!
|
||||
---
|
||||
|
||||
<Cool name={frontmatter.name} href="https://twitter.com/n_moore" client:load />
|
||||
|
||||
This is so cool!
|
||||
|
||||
Do variables work {frontmatter.value * 2}?
|
|
@ -1,86 +0,0 @@
|
|||
---
|
||||
title: 'Introducing Astro: Ship Less JavaScript'
|
||||
description: "We're excited to announce Astro as a new way to build static websites and deliver lightning-fast performance without sacrificing a modern developer experience."
|
||||
publishDate: 'Tuesday, June 8 2021'
|
||||
author: 'fred'
|
||||
heroImage: '/social.jpg'
|
||||
alt: 'Astro'
|
||||
layout: '../../layouts/BlogPost.astro'
|
||||
---
|
||||
|
||||
There's a simple secret to building a faster website — _just ship less_.
|
||||
|
||||
Unfortunately, modern web development has been trending in the opposite direction—towards _more._ More JavaScript, more features, more moving parts, and ultimately more complexity needed to keep it all running smoothly.
|
||||
|
||||
Today I'm excited to publicly share Astro: a new kind of static site builder that delivers lightning-fast performance with a modern developer experience. To design Astro, we borrowed the best parts of our favorite tools and then added a few innovations of our own, including:
|
||||
|
||||
- **Bring Your Own Framework (BYOF):** Build your site using React, Svelte, Vue, Preact, web components, or just plain ol' HTML + JavaScript.
|
||||
- **100% Static HTML, No JS:** Astro renders your entire page to static HTML, removing all JavaScript from your final build by default.
|
||||
- **On-Demand Components:** Need some JS? Astro can automatically hydrate interactive components when they become visible on the page. If the user never sees it, they never load it.
|
||||
- **Fully-Featured:** Astro supports TypeScript, Scoped CSS, CSS Modules, Sass, Tailwind, Markdown, MDX, and any of your favorite npm packages.
|
||||
- **SEO Enabled:** Automatic sitemaps, RSS feeds, pagination and collections take the pain out of SEO and syndication.
|
||||
|
||||
This post marks the first public beta release of Astro. **Missing features and bugs are still to be expected at this early stage.** There are still some months to go before an official 1.0 release, but there are already several fast sites built with Astro in production today. We would love your early feedback as we move towards a v1.0 release later this year.
|
||||
|
||||
> To learn more about Astro and start building your first site, check out [the project README.](https://github.com/snowpackjs/astro#-guides).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Starting a new project in Astro is easy:
|
||||
|
||||
```shell
|
||||
# create your project
|
||||
mkdir new-project-directory
|
||||
cd new-project-directory
|
||||
npm init astro
|
||||
|
||||
# install your dependencies
|
||||
npm install
|
||||
|
||||
# start the dev server and open your browser
|
||||
npm run dev
|
||||
```
|
||||
|
||||
> To learn more about Astro and start building your first site, check out [the project README.](https://github.com/snowpackjs/astro#-guides).
|
||||
|
||||
## How Astro Works
|
||||
|
||||
Astro works a lot like a static site generator. If you have ever used Eleventy, Hugo, or Jekyll (or even a server-side web framework like Rails, Laravel, or Django) then you should feel right at home with Astro.
|
||||
|
||||
In Astro, you compose your website using UI components from your favorite JavaScript web framework (React, Svelte, Vue, etc). Astro renders your entire site to static HTML during the build. The result is a fully static website with all JavaScript removed from the final page. No monolithic JavaScript application required, just static HTML that loads as fast as possible in the browser regardless of how many UI components you used to generate it.
|
||||
|
||||
Of course, sometimes client-side JavaScript is inevitable. Image carousels, shopping carts, and auto-complete search bars are just a few examples of things that require some JavaScript to run in the browser. This is where Astro really shines: When a component needs some JavaScript, Astro only loads that one component (and any dependencies). The rest of your site continues to exist as static, lightweight HTML.
|
||||
|
||||
In other full-stack web frameworks this level of per-component optimization would be impossible without loading the entire page in JavaScript, delaying interactivity. In Astro, this kind of [partial hydration](https://addyosmani.com/blog/rehydration/) is built into the tool itself.
|
||||
|
||||
You can even [automatically defer components](https://codepen.io/jonneal/full/ZELvMvw) to only load once they become visible on the page with the `client:visible` directive.
|
||||
|
||||
This new approach to web architecture is called [islands architecture](https://jasonformat.com/islands-architecture/). We didn't coin the term, but Astro may have perfected the technique. We are confident that an HTML-first, JavaScript-only-as-needed approach is the best solution for the majority of content-based websites.
|
||||
|
||||
> To learn more about Astro and start building your first site, check out [the project README.](https://github.com/snowpackjs/astro#-guides)
|
||||
|
||||
## Embracing the Pit of Success
|
||||
|
||||
> A well-designed system makes it easy to do the right things and annoying (but not impossible) to do the wrong things<div class="source"><p>– Jeff Atwood</p>[Falling Into The Pit of Success](https://blog.codinghorror.com/falling-into-the-pit-of-success/)</div>
|
||||
|
||||
Poor performance is often framed as a failure of the developer, but we respectfully disagree. In many cases, poor performance is a failure of tooling. It should be difficult to build a slow website.
|
||||
|
||||
Astro's main design principle is to lead developers into what [Rico Mariani](https://twitter.com/ricomariani) dubbed "the pit of success". It is our goal to build every site "fast by default" while also delivering a familiar, modern developer experience.
|
||||
|
||||
By building your site to static HTML by default, Astro makes it difficult (but never impossible 😉) to build a slow site.
|
||||
|
||||
## Long-Term Sustainability
|
||||
|
||||
Astro is built by the team of open source developers behind [Snowpack](https://snowpack.dev) and [Skypack](https://skypack.dev), with additional contributions from the community.
|
||||
|
||||
**Astro is and always will be free.** It is an open source project released under the [MIT license](https://github.com/snowpackjs/astro/blob/main/LICENSE).
|
||||
|
||||
We care deeply about building a more sustainable future for open source software. At the same time, we need to support Astro's development long-term. This requires money (donations alone aren't enough.)
|
||||
|
||||
We're inspired by the early success of projects like [Tailwind](https://tailwindcss.com/), [Rome](https://rome.tools/), [Remix](https://remix.run/), [Ionic](https://ionicframework.com/), and others who are experimenting with long-term financial sustainability on top of Open Source. Over the next year we'll be exploring how we can create a sustainable business to support a 100% free, open source Astro for years to come.
|
||||
|
||||
If your company is as excited about Astro as we are, [we'd love to hear from you.](https://astro.build/chat)
|
||||
|
||||
Finally, I'd like to give a **HUGE** thanks to the 300+ developers who joined our earliest private beta. Your feedback has been essential in shaping Astro into the tool it is today. If you're interested in getting involved (or just following along with development) please [join us on Discord.](https://astro.build/chat)
|
||||
|
||||
> To learn more about Astro and start building your first site, check out [the project README.](https://github.com/snowpackjs/astro#-guides)
|
|
@ -14,15 +14,5 @@
|
|||
"devDependencies": {
|
||||
"astro": "^0.20.12",
|
||||
"@snowpack/plugin-dotenv": "^2.1.0"
|
||||
},
|
||||
"snowpack": {
|
||||
"alias": {
|
||||
"components": "./src/components",
|
||||
"~": "./src"
|
||||
},
|
||||
"plugins": [
|
||||
"@snowpack/plugin-dotenv"
|
||||
],
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<!-- Global Metadata -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
|
|
@ -4,8 +4,8 @@ import * as CONFIG from '../../config.ts';
|
|||
import AstroLogo from './AstroLogo.astro';
|
||||
import SkipToContent from './SkipToContent.astro';
|
||||
import SidebarToggle from './SidebarToggle.tsx';
|
||||
import LanguageSelect from './LanguageSelect.jsx';
|
||||
import Search from "./Search.jsx";
|
||||
import LanguageSelect from './LanguageSelect.tsx';
|
||||
import Search from "./Search.tsx";
|
||||
|
||||
const {currentPage} = Astro.props;
|
||||
const lang = currentPage && getLanguageFromURL(currentPage);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { useState, useCallback, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { DocSearchModal, useDocSearchKeyboardEvents } from '@docsearch/react';
|
||||
import * as CONFIG from '../../config.js';
|
||||
import * as CONFIG from '../../config';
|
||||
import '@docsearch/css/dist/style.css';
|
||||
import './Search.css';
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
---
|
||||
import MoreMenu from '../RightSidebar/MoreMenu.astro';
|
||||
import TableOfContents from '../RightSidebar/TableOfContents.tsx';
|
||||
|
||||
const {content, githubEditUrl} = Astro.props;
|
||||
const title = content.title;
|
||||
const headers = content.astro.headers;
|
||||
import MoreMenu from '../RightSidebar/MoreMenu.astro';
|
||||
import TableOfContents from '../RightSidebar/TableOfContents.tsx';
|
||||
---
|
||||
<style>
|
||||
.content {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import ThemeToggleButton from './ThemeToggleButton.jsx';
|
||||
import * as CONFIG from '../../config.js';
|
||||
import ThemeToggleButton from './ThemeToggleButton.tsx';
|
||||
import * as CONFIG from '../../config';
|
||||
const {editHref} = Astro.props;
|
||||
const showMoreSection = (CONFIG.COMMUNITY_INVITE_URL || editHref);
|
||||
---
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
import TableOfContents from './TableOfContents.jsx';
|
||||
import TableOfContents from './TableOfContents.tsx';
|
||||
import MoreMenu from './MoreMenu.astro';
|
||||
const {content, githubEditUrl} = Astro.props;
|
||||
const headers = content.astro.headers;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { KNOWN_LANGUAGES } from './config.js';
|
||||
import { KNOWN_LANGUAGES } from './config';
|
||||
|
||||
export { KNOWN_LANGUAGES }
|
||||
export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES);
|
||||
export const langPathRegex = /\/([a-z]{2}-?[A-Z]{0,2})\//;
|
||||
|
||||
export function getLanguageFromURL(pathname: string) {
|
||||
const langCodeMatch = pathname.match(/\/([a-z]{2}-?[A-Z]{0,2})\//);
|
||||
const langCodeMatch = pathname.match(langPathRegex);
|
||||
return langCodeMatch ? langCodeMatch[1] : 'en';
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Footer from '../components/Footer/Footer.astro';
|
|||
import PageContent from '../components/PageContent/PageContent.astro';
|
||||
import LeftSidebar from '../components/LeftSidebar/LeftSidebar.astro';
|
||||
import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
|
||||
import * as CONFIG from "../config.ts";
|
||||
import * as CONFIG from "../config";
|
||||
|
||||
const { content = {} } = Astro.props;
|
||||
const currentPage = Astro.request.url.pathname;
|
||||
|
@ -14,7 +14,7 @@ const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`;
|
|||
const githubEditUrl = CONFIG.GITHUB_EDIT_URL && (CONFIG.GITHUB_EDIT_URL + currentFile);
|
||||
---
|
||||
|
||||
<html dir="{content.dir ?? 'ltr'}" lang="{content.lang ?? 'en-us'}" class="initial">
|
||||
<html dir={content.dir ?? 'ltr'} lang={content.lang ?? 'en-us'} class="initial">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<HeadSEO {content} canonicalURL={Astro.request.canonicalURL} />
|
||||
|
|
|
@ -11,8 +11,5 @@
|
|||
"devDependencies": {
|
||||
"@astrojs/renderer-lit": "^0.1.2",
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,12 @@ import '../components/Counter.js';
|
|||
<html lang="en">
|
||||
<head>
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<title>Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test app</h1>
|
||||
|
||||
<my-counter client:load />
|
||||
|
||||
<Lorem />
|
||||
|
||||
<calc-add num={33} />
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
"preview": "astro preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12",
|
||||
"@astrojs/renderer-solid": "^0.1.1"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
"@astrojs/renderer-solid": "^0.1.1",
|
||||
"astro": "^0.20.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
// Component Imports
|
||||
import Counter from '../components/Counter.jsx'
|
||||
|
||||
import Counter from '../components/Counter.tsx'
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
// https://docs.astro.build/core-concepts/astro-components/
|
||||
|
@ -9,13 +8,8 @@ import Counter from '../components/Counter.jsx'
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width"
|
||||
/>
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<style>
|
||||
:global(:root) {
|
||||
font-family: system-ui;
|
||||
|
@ -23,22 +17,22 @@ import Counter from '../components/Counter.jsx'
|
|||
}
|
||||
:global(.counter) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
place-items: center;
|
||||
font-size: 2em;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
:global(.children) {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<Counter client:visible>
|
||||
<h1>Hello Preact!</h1>
|
||||
<h1>Hello, Preact!</h1>
|
||||
</Counter>
|
||||
</main>
|
||||
</body>
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,8 @@ const someProps = {
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width"
|
||||
/>
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<style>
|
||||
:global(:root) {
|
||||
font-family: system-ui;
|
||||
|
@ -25,22 +20,22 @@ const someProps = {
|
|||
}
|
||||
:global(.counter) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
place-items: center;
|
||||
font-size: 2em;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
:global(.children) {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<Counter {...someProps} client:visible>
|
||||
<h1>Hello React!</h1>
|
||||
<h1>Hello, React!</h1>
|
||||
</Counter>
|
||||
</main>
|
||||
</body>
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
"preview": "astro preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12",
|
||||
"@astrojs/renderer-solid": "0.1.1"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
"@astrojs/renderer-solid": "^0.1.1",
|
||||
"astro": "^0.20.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { createSignal } from 'solid-js';
|
||||
|
||||
/** */
|
||||
export default function SolidCounter({ children }) {
|
||||
export default function Counter({ children }) {
|
||||
const [count, setCount] = createSignal(0);
|
||||
const add = () => setCount(count() + 1);
|
||||
const subtract = () => setCount(count() - 1);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
import Counter from '../components/Counter.jsx';
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
@ -13,10 +12,10 @@ import Counter from '../components/Counter.jsx';
|
|||
}
|
||||
.counter {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
place-items: center;
|
||||
font-size: 2em;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: 3em;
|
||||
place-items: center;
|
||||
}
|
||||
.counter-message {
|
||||
text-align: center;
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,8 @@ import Counter from '../components/Counter.svelte'
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width"
|
||||
/>
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<style>
|
||||
:global(:root) {
|
||||
font-family: system-ui;
|
||||
|
@ -23,22 +18,22 @@ import Counter from '../components/Counter.svelte'
|
|||
}
|
||||
:global(.counter) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
place-items: center;
|
||||
font-size: 2em;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
:global(.children) {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<Counter client:visible>
|
||||
<h1>Hello Svelte!</h1>
|
||||
<h1>Hello, Svelte!</h1>
|
||||
</Counter>
|
||||
</main>
|
||||
</body>
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,8 @@ import Counter from '../components/Counter.vue'
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width"
|
||||
/>
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<style>
|
||||
:global(:root) {
|
||||
font-family: system-ui;
|
||||
|
@ -23,22 +18,22 @@ import Counter from '../components/Counter.vue'
|
|||
}
|
||||
:global(.counter) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
place-items: center;
|
||||
font-size: 2em;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
:global(.children) {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 2em;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<Counter client:visible>
|
||||
<h1>Hello Vue!</h1>
|
||||
<h1>Hello, Vue!</h1>
|
||||
</Counter>
|
||||
</main>
|
||||
</body>
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@ const { title = 'Jeanine White: Personal Site', description = 'The personal site
|
|||
<title>{title}</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="/_astro/src/scss/global.css">
|
||||
<link rel="stylesheet" type="text/css" href="/src/scss/global.scss">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap" rel="stylesheet">
|
||||
|
|
|
@ -10,11 +10,12 @@ import Nav from '../components/Nav/index.jsx';
|
|||
<MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
|
||||
<style lang="scss">
|
||||
.heroImg {
|
||||
max-height: 24rem;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import Footer from '../components/Footer/index.jsx';
|
|||
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
|
||||
|
||||
// Data Fetching: List all Markdown posts in the repo.
|
||||
const projects = Astro.fetchContent('./project/**/*.md');
|
||||
const projects = Astro.fetchContent("./project/**/*.md");
|
||||
const featuredProject = projects[0];
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
|
@ -190,28 +190,19 @@ const featuredProject = projects[0];
|
|||
height="1131"
|
||||
class="img"
|
||||
src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
|
||||
srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,
|
||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,
|
||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,
|
||||
https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w,"
|
||||
srcSet="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w"
|
||||
sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
|
||||
/>
|
||||
<div class="gradient" />
|
||||
<div class="gradient2" />
|
||||
>
|
||||
<div class="gradient"></div>
|
||||
<div class="gradient2"></div>
|
||||
<div class="overlay">
|
||||
<h1 class="title">
|
||||
<small class="subtitle">The personal site of </small>Jeanine White
|
||||
</h1>
|
||||
<div>
|
||||
<span class="role">
|
||||
👩💻 Developer <span class="invert">👩💻 Developer</span>
|
||||
</span>
|
||||
<span class="role">
|
||||
🎤 Speaker <span class="invert">🎤 Speaker</span>
|
||||
</span>
|
||||
<span class="role">
|
||||
✏️ Writer <span class="invert">✏️ Writer</span>
|
||||
</span>
|
||||
<span class="role">👩💻 Developer <span class="invert">👩💻 Developer</span></span>
|
||||
<span class="role">🎤 Speaker <span class="invert">🎤 Speaker</span></span>
|
||||
<span class="role">✏️ Writer <span class="invert">✏️ Writer</span></span>
|
||||
</div>
|
||||
<p class="desc">Lover of dogs, roadtrips, and poetry.</p>
|
||||
</div>
|
||||
|
@ -233,8 +224,7 @@ const featuredProject = projects[0];
|
|||
<span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
|
||||
<a href="https://github.com/snowpackjs/astro" target="_blank" rel="nofollow">
|
||||
Astro
|
||||
</a>
|
||||
, a new way to build static sites. This is just an example template for you to modify.
|
||||
</a>, a new way to build static sites. This is just an example template for you to modify.
|
||||
</p>
|
||||
<p>
|
||||
<a href="/about">Read more</a>
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,19 @@
|
|||
// VSCode and other TypeScript-enabled text editors will provide auto-completion,
|
||||
// helpful tooltips, and warnings if your exported object is invalid.
|
||||
// You can disable this by removing "@ts-check" and `@type` comments below.
|
||||
import astroRemark from '@astrojs/markdown-remark';
|
||||
|
||||
// @ts-check
|
||||
export default /** @type {import('astro').AstroUserConfig} */ ({
|
||||
// Enable Custom Markdown options, plugins, etc.
|
||||
markdownOptions: {
|
||||
render: [astroRemark, {
|
||||
remarkPlugins: ['remark-code-titles', 'remark-slug'],
|
||||
rehypePlugins: [
|
||||
['rehype-autolink-headings', { behavior: 'prepend' }],
|
||||
['rehype-toc', { headings: ['h2', 'h3'] }],
|
||||
['rehype-add-classes', { 'h1,h2,h3': 'title' }],
|
||||
],
|
||||
]
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
|
|
@ -15,8 +15,5 @@
|
|||
"rehype-toc": "^3.0.2",
|
||||
"remark-code-titles": "^0.1.2",
|
||||
"remark-slug": "^6.0.0"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,5 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,11 @@ const items = ['A', 'B', 'C'];
|
|||
## Oh yeah...
|
||||
|
||||
<ReactCounter client:visible>
|
||||
|
||||
🤯 It's also _recursive_!
|
||||
|
||||
### Markdown can be embedded in any child component
|
||||
|
||||
</ReactCounter>
|
||||
|
||||
## Code
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanostores": "^0.3.3"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
"devDependencies": {
|
||||
"@astrojs/renderer-solid": "^0.1.1",
|
||||
"astro": "^0.20.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
|
||||
// @ts-check
|
||||
export default /** @type {import('astro').AstroUserConfig} */ ({
|
||||
// Enable Tailwind by telling Astro where your Tailwind config file lives.
|
||||
devOptions: {
|
||||
tailwindConfig: './tailwind.config.js',
|
||||
},
|
||||
// Enable the Preact renderer to support Preact JSX components.
|
||||
renderers: ['@astrojs/renderer-preact'],
|
||||
});
|
||||
|
|
|
@ -9,10 +9,8 @@
|
|||
"preview": "astro preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^2.1.2",
|
||||
"astro": "^0.20.12"
|
||||
},
|
||||
"snowpack": {
|
||||
"workspaceRoot": "../.."
|
||||
"astro": "^0.20.12",
|
||||
"autoprefixer": "^10.3.7",
|
||||
"tailwindcss": "^2.2.17"
|
||||
}
|
||||
}
|
||||
|
|
6
examples/with-tailwindcss/postcss.config.js
Normal file
6
examples/with-tailwindcss/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: ['./public/**/*.html', './src/**/*.{astro,js,jsx,ts,tsx,vue,svelte}'],
|
||||
purge: ['**/*.{astro,html,js,jsx,svelte,ts,tsx,vue}'],
|
||||
};
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
{
|
||||
"ignoreChanges": ["**/test/**", "**/*.md"],
|
||||
"useWorkspaces": true,
|
||||
"version": "4.0.0"
|
||||
"version": "4.0.0",
|
||||
"command": {
|
||||
"run": {
|
||||
"npmClient": "yarn"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
34
package.json
34
package.json
|
@ -12,17 +12,19 @@
|
|||
"build": "yarn build:core",
|
||||
"build:one": "lerna run build --scope",
|
||||
"build:all": "lerna run build --scope \"{astro,@astrojs/*}\"",
|
||||
"build:core": "lerna run build --scope \"{astro,@astrojs/parser,@astrojs/markdown-support}\"",
|
||||
"build:core": "lerna run build --scope \"{astro,@astrojs/parser,@astrojs/markdown-remark}\"",
|
||||
"dev": "yarn dev:core --parallel --stream",
|
||||
"dev:one": "lerna run dev --scope --parallel --stream",
|
||||
"dev:all": "lerna run dev --scope \"{astro,@astrojs/*}\" --parallel --stream",
|
||||
"dev:core": "lerna run dev --scope \"{astro,@astrojs/parser,@astrojs/markdown-support}\" --parallel --stream",
|
||||
"dev:core": "lerna run dev --scope \"{astro,@astrojs/parser,@astrojs/markdown-remark}\" --parallel --stream",
|
||||
"format": "prettier -w .",
|
||||
"lint": "eslint \"packages/**/*.ts\"",
|
||||
"test": "yarn workspace astro run test",
|
||||
"test:templates": "lerna run test --scope create-astro --stream"
|
||||
},
|
||||
"workspaces": [
|
||||
"compiled/*",
|
||||
"packages/markdown/*",
|
||||
"packages/renderers/*",
|
||||
"packages/*",
|
||||
"examples/*",
|
||||
|
@ -34,36 +36,26 @@
|
|||
"packages/astro/test/fixtures/custom-elements/my-component-lib"
|
||||
],
|
||||
"volta": {
|
||||
"node": "14.16.1",
|
||||
"node": "14.17.0",
|
||||
"npm": "7.11.2",
|
||||
"yarn": "1.22.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"camel-case": "^4.1.2"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.16.0",
|
||||
"@octokit/action": "^3.15.4",
|
||||
"@snowpack/plugin-postcss": "^1.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"cheerio": "^1.0.0-rc.6",
|
||||
"cheerio-select-tmp": "^0.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"del": "^6.0.0",
|
||||
"esbuild": "^0.11.17",
|
||||
"eslint": "^7.25.0",
|
||||
"esbuild": "^0.13.7",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"execa": "^5.0.0",
|
||||
"lerna": "^4.0.0",
|
||||
"postcss": "^8.2.15",
|
||||
"postcss-icss-keyframes": "^0.2.1",
|
||||
"prettier": "^2.2.1",
|
||||
"svelte": "^3.38.0",
|
||||
"prettier": "^2.4.1",
|
||||
"tiny-glob": "^0.2.8",
|
||||
"typescript": "^4.2.4",
|
||||
"uvu": "^0.5.1"
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
|
|
1
packages/astro-prism/index.d.ts
vendored
Normal file
1
packages/astro-prism/index.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export function addAstro(Prism: any): void;
|
|
@ -15,6 +15,7 @@
|
|||
"exports": {
|
||||
".": "./index.mjs"
|
||||
},
|
||||
"types": "./index.d.ts",
|
||||
"keywords": [],
|
||||
"author": "Skypack",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -13,6 +13,7 @@ const CI_INSTRUCTIONS = {
|
|||
VERCEL: 'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
|
||||
};
|
||||
|
||||
/** `astro *` */
|
||||
async function main() {
|
||||
// Check for ESM support.
|
||||
// Load the "supports-esm" package in an way that works in both ESM & CJS.
|
||||
|
@ -29,7 +30,7 @@ async function main() {
|
|||
|
||||
// Preflight check complete. Enjoy! ✨
|
||||
if (supportsESM) {
|
||||
return import('./dist/cli.js')
|
||||
return import('./dist/cli/index.js')
|
||||
.then(({ cli }) => cli(process.argv))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
|
|
@ -1,144 +1,6 @@
|
|||
---
|
||||
const key = Object.keys(Astro.props)[0];
|
||||
const value = Astro.props[key];
|
||||
|
||||
const getType = (node: unknown) => {
|
||||
if (Array.isArray(node)) return 'array';
|
||||
if (node === null) return 'null';
|
||||
if (typeof node === 'object') {
|
||||
if ((node as any).then) return 'promise';
|
||||
}
|
||||
return typeof node;
|
||||
};
|
||||
|
||||
const getSummary = (node: any, key: string, className: string) => {
|
||||
const type = getType(node);
|
||||
let value;
|
||||
let open;
|
||||
let close;
|
||||
|
||||
if (type === 'function') {
|
||||
return <>
|
||||
{(key || !key && key === 0) && <><span class={`${className} key`}>{key}</span><span class={`${className} sep`}>:</span></>}
|
||||
<span class={`${className} value value-function`}>{node.name}<span class={`${className} punc`}>()</span></span>
|
||||
</>
|
||||
}
|
||||
|
||||
if (type === 'promise') {
|
||||
return <>
|
||||
{(key || !key && key === 0) && <><span class={`${className} key`}>{key}</span><span class={`${className} sep`}>:</span></>}
|
||||
<span class={`${className} value value-promise`}>Promise</span>
|
||||
</>
|
||||
}
|
||||
|
||||
if (type === 'array') {
|
||||
value = node.length;
|
||||
open = <><span class={`${className} none`}>Array</span>{'['}</>;
|
||||
close = ']';
|
||||
} else if (type === 'object') {
|
||||
const keys = Object.keys(node);
|
||||
if (keys.length === 0) {
|
||||
value = 'Empty';
|
||||
} else if (keys.length > 3) {
|
||||
value = '…';
|
||||
} else {
|
||||
value = keys.slice(0, 3).join(',');
|
||||
}
|
||||
open = '{';
|
||||
close = '}';
|
||||
};
|
||||
|
||||
return <>
|
||||
{key && <><span class={`${className} key`}>{key}</span>: </>}
|
||||
{open && <span class={`${className} punc`}>{open}</span>}
|
||||
<span class={`${className} hide`}>
|
||||
<span class={`${className} len`}>{value}</span>
|
||||
{close && <span class={`${className} punc`}>{close}</span>}
|
||||
</span>
|
||||
</>;
|
||||
};
|
||||
|
||||
const Details = ({ node, key, children, class: className }) => {
|
||||
const type = getType(node);
|
||||
const props = {};
|
||||
|
||||
if (type === 'array' || type === 'object') {
|
||||
props['data-char'] = type === 'array' ? ']' : '}'
|
||||
props.open = !key && type === 'object' ? '' : undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<details {...props} class={className}>
|
||||
<Summary node={node} key={key} class={className} />
|
||||
{children}
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
const Summary = ({ node, key, class: className }) => {
|
||||
return (
|
||||
<summary class={className}>{getSummary(node, key, className)}</summary>
|
||||
);
|
||||
}
|
||||
|
||||
const Empty = Symbol('Empty');
|
||||
|
||||
const KeyValue = ({ key, value, dim, class: className }) => {
|
||||
let type = key === '__proto__' ? 'prototype' : getType(value);
|
||||
if (type === 'null') {
|
||||
value = 'null';
|
||||
} else if (type === 'undefined') {
|
||||
value = 'undefined';
|
||||
} else if (value === Empty) {
|
||||
type = 'empty';
|
||||
value = 'Empty';
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={`${className} line`}>
|
||||
{(key || !key && key === 0) && <><span class={`${className} key ${dim ? 'key-dim' : ''}`.trim()}>{key}</span><span class={`${className} sep`}>:</span></>}
|
||||
<span class={`${className} value value-${type}`}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Node = ({ node, key, class: className, ...props }) => {
|
||||
const type = getType(node);
|
||||
className = className.replace(/debug-value/g, '');
|
||||
|
||||
if (type === 'array' || type === 'object') {
|
||||
let children = [];
|
||||
if (type === 'array' && node.length > 0 && Object.entries(node).length === 0) {
|
||||
children = Array.from({ length: node.length }, (_, key) => <Node node={Empty} key={key} class={className} />);
|
||||
} else {
|
||||
children = Object.entries(node).map(([key, value]) => <Node node={value} key={key} class={className} />);
|
||||
}
|
||||
return (
|
||||
<Details node={node} key={key} children={children} class={className} />
|
||||
);
|
||||
} else if (type === 'function') {
|
||||
return (
|
||||
<Details node={node} key={key} class={className} children={
|
||||
<>
|
||||
<KeyValue key="name" value={node.name} dim={true} class={className} />
|
||||
<KeyValue key="__proto__" value="Function" dim={true} class={className} />
|
||||
</>
|
||||
}/>
|
||||
);
|
||||
} else if (type === 'promise') {
|
||||
return (
|
||||
<Details node={node} key={key} class={className} children={
|
||||
<KeyValue key="__proto__" value="Promise" dim={true} />
|
||||
} />
|
||||
);
|
||||
}
|
||||
|
||||
return <KeyValue key={key} value={node} class={className} />;
|
||||
}
|
||||
---
|
||||
|
||||
<div class="debug">
|
||||
|
@ -146,21 +8,32 @@ const Node = ({ node, key, class: className, ...props }) => {
|
|||
<h2 class="debug-title"><span class="debug-label">Debug</span> <span class="debug-name">"{key}"</span></h2>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<Node node={value} class="debug-value" />
|
||||
</main>
|
||||
<pre>{JSON.stringify(value, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.debug-header {
|
||||
background: #FF1639;
|
||||
<style>
|
||||
.debug {
|
||||
font-size: 14px;
|
||||
padding: 1rem 1.5rem;
|
||||
background: white;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.debug-header, pre {
|
||||
margin: -1rem -1.5rem 1rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
}
|
||||
|
||||
.debug-header {
|
||||
background: #FF1639;
|
||||
border-radius: 4px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.debug-title {
|
||||
font-size: 1em;
|
||||
color: #fff;
|
||||
color: white;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
|
@ -170,137 +43,12 @@ const Node = ({ node, key, class: className, ...props }) => {
|
|||
margin-right: 0.75em;
|
||||
}
|
||||
|
||||
.debug {
|
||||
all: initial;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 1.5rem;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #FFCFD6;
|
||||
|
||||
background: #FFF;
|
||||
font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.44;
|
||||
color: #6b7280;
|
||||
white-space: pre;
|
||||
|
||||
}
|
||||
|
||||
details[open] > summary span.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
details[open] > summary span.none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details > details {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.line {
|
||||
padding-left: 1.125em;
|
||||
}
|
||||
|
||||
details[open]::after {
|
||||
content: attr(data-char);
|
||||
}
|
||||
|
||||
.sep {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
|
||||
details > summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details:hover > summary::before,
|
||||
details:focus > summary::before {
|
||||
transform: translate(0.25em, -0.25em);
|
||||
}
|
||||
|
||||
details > summary::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13 8L2.60769 14L2.6077 2L13 8Z' fill='%236B7280' /%3E%3C/svg%3E%0A");
|
||||
background-size: 1em;
|
||||
|
||||
display: inline-flex;
|
||||
font-size: 0.5em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 1.25em;
|
||||
margin-left: -2em;
|
||||
transform: translate(0, -0.25em);
|
||||
line-height: 1em;
|
||||
transition: transform 120ms ease-in;
|
||||
}
|
||||
|
||||
details[open] > summary::before {
|
||||
transform: translate(0.25em, -0.25em) rotate(90deg);
|
||||
}
|
||||
|
||||
.debug :global(::marker) {
|
||||
content: '';
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.key {
|
||||
color: #882de7;
|
||||
}
|
||||
|
||||
.key-dim {
|
||||
color: #B881F1;
|
||||
}
|
||||
|
||||
.len {
|
||||
color: #B881F1;
|
||||
}
|
||||
|
||||
details:hover > summary,
|
||||
details:focus > summary,
|
||||
details:hover > summary .punc,
|
||||
details:focus > summary .punc,
|
||||
details:hover[open]::after,
|
||||
details:focus[open]::after {
|
||||
color: #000012;
|
||||
}
|
||||
|
||||
details:hover > summary .len,
|
||||
details:focus > summary .len {
|
||||
color: #882DE7;
|
||||
}
|
||||
|
||||
.punc {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.value-string {
|
||||
color: #17c083;
|
||||
}
|
||||
|
||||
.value-function::before {
|
||||
content: 'ƒ ';
|
||||
color: #3894ff;
|
||||
}
|
||||
.value-function {
|
||||
color: #5076f9;
|
||||
}
|
||||
|
||||
.value-number {
|
||||
color: #ff5d01;
|
||||
}
|
||||
|
||||
.value-null,
|
||||
.value-undefined {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
main > .line {
|
||||
margin-left: -0.75em;
|
||||
padding-left: 0;
|
||||
pre {
|
||||
border: 1px solid #eee;
|
||||
padding: 1rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,32 +1,39 @@
|
|||
---
|
||||
import { renderMarkdown } from '@astrojs/markdown-support';
|
||||
|
||||
export interface Props {
|
||||
content?: string;
|
||||
}
|
||||
|
||||
const dedent = (str: string) => str.split('\n').map(ln => ln.trimStart()).join('\n');
|
||||
|
||||
// Internal props that should not be part of the external interface.
|
||||
interface InternalProps extends Props {
|
||||
$scope: string;
|
||||
}
|
||||
|
||||
const { content, $scope } = Astro.props as InternalProps;
|
||||
let { content, class: className } = Astro.props as InternalProps;
|
||||
let html = null;
|
||||
let htmlContent = '';
|
||||
|
||||
const { privateRenderMarkdownDoNotUse: renderMarkdown } = (Astro as any);
|
||||
|
||||
// If no content prop provided, use the slot.
|
||||
if (!content) {
|
||||
const { privateRenderSlotDoNotUse: renderSlot } = (Astro as any);
|
||||
content = await renderSlot('default');
|
||||
if (content.trim().length > 0) {
|
||||
content = dedent(content);
|
||||
}
|
||||
}
|
||||
|
||||
// This flow is only triggered if a user passes `<Markdown content={content} />`
|
||||
if (content) {
|
||||
const { content: htmlContent } = await renderMarkdown(content, {
|
||||
htmlContent = await renderMarkdown(content, {
|
||||
mode: 'md',
|
||||
$: {
|
||||
scopedClassName: $scope
|
||||
scopedClassName: className
|
||||
}
|
||||
});
|
||||
html = htmlContent;
|
||||
}
|
||||
|
||||
/*
|
||||
If we have rendered `html` for `content`, render that
|
||||
Otherwise, just render the slotted content
|
||||
*/
|
||||
html = htmlContent;
|
||||
---
|
||||
{html ? html : <slot />}
|
||||
|
|
27
packages/astro/internal.d.ts
vendored
27
packages/astro/internal.d.ts
vendored
|
@ -1,27 +0,0 @@
|
|||
declare module '#astro/compiler' {
|
||||
export * from 'astro/dist/types/compiler';
|
||||
}
|
||||
declare module '#astro/ast' {
|
||||
export * from 'astro/dist/types/ast';
|
||||
}
|
||||
declare module '#astro/build' {
|
||||
export * from 'astro/dist/types/build';
|
||||
}
|
||||
declare module '#astro/cli' {
|
||||
export * from 'astro/dist/types/cli';
|
||||
}
|
||||
declare module '#astro/config' {
|
||||
export * from 'astro/dist/types/config';
|
||||
}
|
||||
declare module '#astro/dev' {
|
||||
export * from 'astro/dist/types/dev';
|
||||
}
|
||||
declare module '#astro/logger' {
|
||||
export * from 'astro/dist/types/logger';
|
||||
}
|
||||
declare module '#astro/runtime' {
|
||||
export * from 'astro/dist/types/runtime';
|
||||
}
|
||||
declare module '#astro/search' {
|
||||
export * from 'astro/dist/types/search';
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
"author": "Skypack",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"types": "./dist/types/@types/public.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/snowpackjs/astro.git",
|
||||
|
@ -12,18 +11,27 @@
|
|||
},
|
||||
"exports": {
|
||||
".": "./astro.js",
|
||||
"./package.json": "./package.json",
|
||||
"./snowpack-plugin": "./snowpack-plugin.cjs",
|
||||
"./snowpack-plugin-jsx": "./snowpack-plugin-jsx.cjs",
|
||||
"./client/*": "./dist/runtime/client/*",
|
||||
"./components": "./components/index.js",
|
||||
"./debug": "./components/Debug.astro",
|
||||
"./components/*": "./components/*",
|
||||
"./runtime/svelte": "./dist/frontend/runtime/svelte.js",
|
||||
"./internal/*": "./dist/internal/*",
|
||||
"./dist/internal/*": "./dist/internal/*"
|
||||
"./debug": "./components/Debug.astro",
|
||||
"./internal": "./dist/runtime/server/index.js",
|
||||
"./internal/*": "./dist/runtime/server/*",
|
||||
"./package.json": "./package.json",
|
||||
"./runtime/*": "./dist/runtime/*",
|
||||
"./server/*": "./dist/runtime/server/*",
|
||||
"./vite-plugin-astro": "./dist/vite-plugin-astro/index.js",
|
||||
"./vite-plugin-astro/*": "./dist/vite-plugin-astro/*",
|
||||
"./vite-plugin-astro-postprocess": "./dist/vite-plugin-astro-postprocess/index.js",
|
||||
"./vite-plugin-astro-postprocess/*": "./dist/vite-plugin-astro-postprocess/*",
|
||||
"./vite-plugin-fetch": "./dist/vite-plugin-fetch/index.js",
|
||||
"./vite-plugin-fetch/*": "./dist/vite-plugin-fetch/*",
|
||||
"./vite-plugin-jsx/*": "./dist/vite-plugin-jsx/*",
|
||||
"./vite-plugin-jsx": "./dist/vite-plugin-jsx/index.js",
|
||||
"./vite-plugin-markdown": "./dist/vite-plugin-markdown/index.js",
|
||||
"./vite-plugin-markdown/*": "./dist/vite-plugin-markdown/*"
|
||||
},
|
||||
"imports": {
|
||||
"#astro/compiler": "./dist/compiler/index.js",
|
||||
"#astro/*": "./dist/*.js"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -32,93 +40,79 @@
|
|||
"files": [
|
||||
"components",
|
||||
"dist",
|
||||
"snowpack-plugin-jsx.cjs",
|
||||
"snowpack-plugin.cjs",
|
||||
"astro.js"
|
||||
"astro.js",
|
||||
"README.md",
|
||||
"vendor"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
"dev": "astro-scripts dev \"src/**/*.ts\"",
|
||||
"postbuild": "astro-scripts copy \"src/**/*.astro\"",
|
||||
"benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js",
|
||||
"test": "uvu test -i fixtures -i benchmark -i test-utils.js"
|
||||
"test": "mocha --parallel --timeout 15000"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^0.2.23",
|
||||
"@astrojs/language-server": "^0.7.16",
|
||||
"@astrojs/markdown-remark": "^0.3.1",
|
||||
"@astrojs/markdown-support": "0.3.1",
|
||||
"@astrojs/parser": "0.20.2",
|
||||
"@astrojs/prism": "0.2.2",
|
||||
"@astrojs/renderer-preact": "0.2.2",
|
||||
"@astrojs/renderer-preact": "^0.2.2",
|
||||
"@astrojs/renderer-react": "0.2.2",
|
||||
"@astrojs/renderer-svelte": "0.1.2",
|
||||
"@astrojs/renderer-vue": "0.1.9",
|
||||
"@babel/code-frame": "^7.12.13",
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/generator": "^7.13.9",
|
||||
"@babel/parser": "^7.13.15",
|
||||
"@babel/traverse": "^7.13.15",
|
||||
"@snowpack/plugin-postcss": "^1.4.3",
|
||||
"@snowpack/plugin-sass": "^1.4.0",
|
||||
"@types/send": "^0.17.1",
|
||||
"acorn": "^7.4.0",
|
||||
"astring": "^1.7.4",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"camel-case": "^4.1.2",
|
||||
"cheerio": "^1.0.0-rc.6",
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/traverse": "^7.15.4",
|
||||
"@web/rollup-plugin-html": "^1.10.1",
|
||||
"astring": "^1.7.5",
|
||||
"ci-info": "^3.2.0",
|
||||
"del": "^6.0.0",
|
||||
"es-module-lexer": "^0.4.1",
|
||||
"esbuild": "^0.12.12",
|
||||
"connect": "^3.7.0",
|
||||
"es-module-lexer": "^0.7.1",
|
||||
"esbuild": "^0.13.6",
|
||||
"estree-util-value-to-estree": "^1.2.0",
|
||||
"estree-walker": "^3.0.0",
|
||||
"fast-xml-parser": "^3.19.0",
|
||||
"fdir": "^5.0.0",
|
||||
"find-up": "^5.0.0",
|
||||
"get-port": "^5.1.1",
|
||||
"gzip-size": "^6.0.0",
|
||||
"html-entities": "^2.3.2",
|
||||
"htmlparser2": "^7.1.2",
|
||||
"kleur": "^4.1.4",
|
||||
"magic-string": "^0.25.3",
|
||||
"mime": "^2.5.2",
|
||||
"moize": "^6.0.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"morphdom": "^2.6.1",
|
||||
"node-fetch": "^2.6.5",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"picomatch": "^2.2.3",
|
||||
"postcss": "^8.2.15",
|
||||
"postcss-icss-keyframes": "^0.2.1",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"prismjs": "^1.23.0",
|
||||
"prismjs": "^1.25.0",
|
||||
"remark-slug": "^7.0.0",
|
||||
"resolve": "^1.20.0",
|
||||
"rollup": "^2.43.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sass": "^1.32.13",
|
||||
"sass": "^1.43.3",
|
||||
"semver": "^7.3.5",
|
||||
"send": "^0.17.1",
|
||||
"shiki": "^0.9.10",
|
||||
"shorthash": "^0.0.2",
|
||||
"slash": "^4.0.0",
|
||||
"snowpack": "^3.8.6",
|
||||
"srcset-parse": "^1.1.0",
|
||||
"sourcemap-codec": "^1.4.8",
|
||||
"string-width": "^5.0.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-indent": "^4.0.0",
|
||||
"supports-esm": "^1.0.0",
|
||||
"tiny-glob": "^0.2.8",
|
||||
"yargs-parser": "^20.2.7",
|
||||
"vite": "^2.6.10",
|
||||
"yargs-parser": "^20.2.9",
|
||||
"zod": "^3.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.14.0",
|
||||
"@types/babel__code-frame": "^7.0.2",
|
||||
"@types/babel__generator": "^7.6.2",
|
||||
"@types/babel__parser": "^7.1.1",
|
||||
"@types/babel__traverse": "^7.11.1",
|
||||
"@types/estree": "0.0.46",
|
||||
"@astrojs/parser": "^0.20.2",
|
||||
"@babel/types": "^7.15.6",
|
||||
"@types/babel__core": "^7.1.15",
|
||||
"@types/chai": "^4.2.22",
|
||||
"@types/connect": "^3.4.35",
|
||||
"@types/mime": "^2.0.3",
|
||||
"@types/node": "^14.14.31",
|
||||
"@types/sass": "^1.16.0",
|
||||
"@types/yargs-parser": "^20.2.0",
|
||||
"astro-scripts": "0.0.1",
|
||||
"is-windows": "^1.0.2",
|
||||
"strip-ansi": "^7.0.0"
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/resolve": "^1.20.1",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/send": "^0.17.1",
|
||||
"@types/yargs-parser": "^20.2.1",
|
||||
"chai": "^4.3.4",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"mocha": "^9.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0",
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
const esbuild = require('esbuild');
|
||||
const colors = require('kleur/colors');
|
||||
const loggerPromise = import('./dist/logger.js');
|
||||
const { promises: fs } = require('fs');
|
||||
|
||||
const babel = require('@babel/core');
|
||||
const eslexer = require('es-module-lexer');
|
||||
let error = (...args) => {};
|
||||
|
||||
/**
|
||||
* @typedef {Object} PluginOptions - creates a new type named 'SpecialType'
|
||||
* @prop {import('./src/config_manager').ConfigManager} configManager
|
||||
* @prop {'development' | 'production'} mode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns esbuild loader for a given file
|
||||
* @param filePath {string}
|
||||
* @returns {import('esbuild').Loader}
|
||||
*/
|
||||
function getLoader(fileExt) {
|
||||
/** @type {any} */
|
||||
return fileExt.substr(1);
|
||||
}
|
||||
|
||||
// The `tsx` loader in esbuild will remove unused imports, so we need to
|
||||
// be careful about esbuild not treating h, React, Fragment, etc. as unused.
|
||||
const PREVENT_UNUSED_IMPORTS = ';;(React,Fragment,h);';
|
||||
|
||||
/**
|
||||
* @type {import('snowpack').SnowpackPluginFactory<PluginOptions>}
|
||||
*/
|
||||
module.exports = function jsxPlugin(config, options = {}) {
|
||||
const { configManager, logging } = options;
|
||||
|
||||
let didInit = false;
|
||||
return {
|
||||
name: '@astrojs/snowpack-plugin-jsx',
|
||||
resolve: {
|
||||
input: ['.jsx', '.tsx'],
|
||||
output: ['.js'],
|
||||
},
|
||||
async load({ filePath, fileExt, ...transformContext }) {
|
||||
if (!didInit) {
|
||||
const logger = await loggerPromise;
|
||||
error = logger.error;
|
||||
await eslexer.init;
|
||||
didInit = true;
|
||||
}
|
||||
|
||||
const contents = await fs.readFile(filePath, 'utf8');
|
||||
const loader = getLoader(fileExt);
|
||||
|
||||
const { code, warnings } = await esbuild.transform(contents, {
|
||||
loader,
|
||||
jsx: 'preserve',
|
||||
sourcefile: filePath,
|
||||
sourcemap: config.buildOptions.sourcemap ? 'inline' : undefined,
|
||||
charset: 'utf8',
|
||||
sourcesContent: config.mode !== 'production',
|
||||
});
|
||||
for (const warning of warnings) {
|
||||
error(
|
||||
logging,
|
||||
'renderer',
|
||||
`${colors.bold('!')} ${filePath}
|
||||
${warning.text}`
|
||||
);
|
||||
}
|
||||
|
||||
let renderers = await configManager.getRenderers();
|
||||
const importSources = new Set(renderers.map(({ jsxImportSource }) => jsxImportSource).filter((i) => i));
|
||||
const getRenderer = (importSource) => renderers.find(({ jsxImportSource }) => jsxImportSource === importSource);
|
||||
const getTransformOptions = async (importSource) => {
|
||||
const { name } = getRenderer(importSource);
|
||||
const { default: renderer } = await import(name);
|
||||
return renderer.jsxTransformOptions(transformContext);
|
||||
};
|
||||
|
||||
if (importSources.size === 0) {
|
||||
throw new Error(`${colors.yellow(filePath)}
|
||||
Unable to resolve a renderer that handles JSX transforms! Please include a \`renderer\` plugin which supports JSX in your \`astro.config.mjs\` file.`);
|
||||
}
|
||||
|
||||
// If we only have a single renderer, we can skip a bunch of work!
|
||||
if (importSources.size === 1) {
|
||||
const result = transform(code, filePath, await getTransformOptions(Array.from(importSources)[0]));
|
||||
|
||||
return {
|
||||
'.js': {
|
||||
code: result.code || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// we need valid JS here, so we can use `h` and `Fragment` as placeholders
|
||||
// NOTE(fks, matthewp): Make sure that you're transforming the original contents here.
|
||||
const { code: codeToScan } = await esbuild.transform(contents + PREVENT_UNUSED_IMPORTS, {
|
||||
loader,
|
||||
jsx: 'transform',
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
});
|
||||
|
||||
let imports = [];
|
||||
if (/import/.test(codeToScan)) {
|
||||
let [i] = eslexer.parse(codeToScan);
|
||||
// @ts-ignore
|
||||
imports = i;
|
||||
}
|
||||
|
||||
let importSource;
|
||||
|
||||
if (imports.length > 0) {
|
||||
for (let { n: name } of imports) {
|
||||
if (name.indexOf('/') > -1) name = name.split('/')[0];
|
||||
if (importSources.has(name)) {
|
||||
importSource = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!importSource) {
|
||||
const multiline = contents.match(/\/\*\*[\S\s]*\*\//gm) || [];
|
||||
|
||||
for (const comment of multiline) {
|
||||
const [_, lib] = comment.match(/@jsxImportSource\s*(\S+)/) || [];
|
||||
if (lib) {
|
||||
importSource = lib;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!importSource) {
|
||||
const importStatements = {
|
||||
react: "import React from 'react'",
|
||||
preact: "import { h } from 'preact'",
|
||||
'solid-js': "import 'solid-js/web'",
|
||||
};
|
||||
if (importSources.size > 1) {
|
||||
const defaultRenderer = Array.from(importSources)[0];
|
||||
error(
|
||||
logging,
|
||||
'renderer',
|
||||
`${colors.yellow(filePath)}
|
||||
Unable to resolve a renderer that handles this file! With more than one renderer enabled, you should include an import or use a pragma comment.
|
||||
Add ${colors.cyan(importStatements[defaultRenderer] || `import '${defaultRenderer}';`)} or ${colors.cyan(`/* jsxImportSource: ${defaultRenderer} */`)} to this file.
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
'.js': {
|
||||
code: contents,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const result = transform(code, filePath, await getTransformOptions(importSource));
|
||||
|
||||
return {
|
||||
'.js': {
|
||||
code: result.code || '',
|
||||
},
|
||||
};
|
||||
},
|
||||
cleanup() {},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param code {string}
|
||||
* @param id {string}
|
||||
* @param opts {{ plugins?: import('@babel/core').PluginItem[], presets?: import('@babel/core').PluginItem[] }|undefined}
|
||||
*/
|
||||
const transform = (code, id, { alias, plugins = [], presets = [] } = {}) =>
|
||||
babel.transformSync(code, {
|
||||
presets,
|
||||
plugins: [...plugins, alias ? ['babel-plugin-module-resolver', { root: process.cwd(), alias }] : undefined].filter((v) => v),
|
||||
cwd: process.cwd(),
|
||||
filename: id,
|
||||
ast: false,
|
||||
compact: false,
|
||||
sourceMaps: false,
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
const { readFile } = require('fs').promises;
|
||||
const getPort = require('get-port');
|
||||
// Snowpack plugins must be CommonJS :(
|
||||
const transformPromise = import('./dist/compiler/index.js');
|
||||
|
||||
const DEFAULT_HMR_PORT = 12321;
|
||||
|
||||
/**
|
||||
* @typedef {Object} PluginOptions - creates a new type named 'SpecialType'
|
||||
* @prop {import('./src/config_manager').ConfigManager} configManager
|
||||
* @prop {'development' | 'production'} mode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {import('snowpack').SnowpackPluginFactory<PluginOptions>}
|
||||
*/
|
||||
module.exports = (snowpackConfig, options = {}) => {
|
||||
const { resolvePackageUrl, astroConfig, configManager, mode } = options;
|
||||
let hmrPort = DEFAULT_HMR_PORT;
|
||||
return {
|
||||
name: 'snowpack-astro',
|
||||
knownEntrypoints: ['astro/dist/internal/h.js', 'astro/components/Prism.astro', 'shorthash', 'estree-util-value-to-estree', 'astring'],
|
||||
resolve: {
|
||||
input: ['.astro', '.md'],
|
||||
output: ['.js', '.css'],
|
||||
},
|
||||
async transform({ contents, id, fileExt }) {
|
||||
if (configManager.isConfigModule(fileExt, id)) {
|
||||
configManager.configModuleId = id;
|
||||
const source = await configManager.buildSource(contents);
|
||||
return source;
|
||||
}
|
||||
},
|
||||
onChange({ filePath }) {
|
||||
// If the astro.config.mjs file changes, mark the generated config module as changed.
|
||||
if (configManager.isAstroConfig(filePath) && configManager.configModuleId) {
|
||||
this.markChanged(configManager.configModuleId);
|
||||
configManager.markDirty();
|
||||
}
|
||||
},
|
||||
async config(snowpackConfig) {
|
||||
if (!isNaN(snowpackConfig.devOptions.hmrPort)) {
|
||||
hmrPort = snowpackConfig.devOptions.hmrPort;
|
||||
} else {
|
||||
hmrPort = await getPort({ port: DEFAULT_HMR_PORT, host: snowpackConfig.devOptions.hostname });
|
||||
snowpackConfig.devOptions.hmrPort = hmrPort;
|
||||
}
|
||||
},
|
||||
async load({ filePath }) {
|
||||
const { compileComponent } = await transformPromise;
|
||||
const projectRoot = snowpackConfig.root;
|
||||
const contents = await readFile(filePath, 'utf-8');
|
||||
|
||||
/** @type {import('./src/@types/compiler').CompileOptions} */
|
||||
const compileOptions = {
|
||||
astroConfig,
|
||||
hmrPort,
|
||||
mode,
|
||||
resolvePackageUrl,
|
||||
};
|
||||
const result = await compileComponent(contents, { compileOptions, filename: filePath, projectRoot });
|
||||
const output = {
|
||||
'.js': { code: result.contents },
|
||||
};
|
||||
if (result.css) output['.css'] = result.css;
|
||||
return output;
|
||||
},
|
||||
};
|
||||
};
|
5
packages/astro/src/@types/README.md
Normal file
5
packages/astro/src/@types/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# `@types/`
|
||||
|
||||
TypeScript definitions and types for untyped modules.
|
||||
|
||||
[See CONTRIBUTING.md](../../../../CONTRIBUTING.md) for a code overview.
|
323
packages/astro/src/@types/astro-core.ts
Normal file
323
packages/astro/src/@types/astro-core.ts
Normal file
|
@ -0,0 +1,323 @@
|
|||
import type babel from '@babel/core';
|
||||
import type { z } from 'zod';
|
||||
import type { AstroConfigSchema } from '../core/config';
|
||||
import type { AstroComponentFactory } from '../runtime/server';
|
||||
import type vite from '../../vendor/vite';
|
||||
|
||||
export interface AstroComponentMetadata {
|
||||
displayName: string;
|
||||
hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only';
|
||||
hydrateArgs?: any;
|
||||
componentUrl?: string;
|
||||
componentExport?: { value: string; namespace?: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
* The Astro User Config Format:
|
||||
* This is the type interface for your astro.config.mjs default export.
|
||||
*/
|
||||
export interface AstroUserConfig {
|
||||
/**
|
||||
* Where to resolve all URLs relative to. Useful if you have a monorepo project.
|
||||
* Default: '.' (current working directory)
|
||||
*/
|
||||
projectRoot?: string;
|
||||
/**
|
||||
* Path to the `astro build` output.
|
||||
* Default: './dist'
|
||||
*/
|
||||
dist?: string;
|
||||
/**
|
||||
* Path to all of your Astro components, pages, and data.
|
||||
* Default: './src'
|
||||
*/
|
||||
src?: string;
|
||||
/**
|
||||
* Path to your Astro/Markdown pages. Each file in this directory
|
||||
* becomes a page in your final build.
|
||||
* Default: './src/pages'
|
||||
*/
|
||||
pages?: string;
|
||||
/**
|
||||
* Path to your public files. These are copied over into your build directory, untouched.
|
||||
* Useful for favicons, images, and other files that don't need processing.
|
||||
* Default: './public'
|
||||
*/
|
||||
public?: string;
|
||||
/**
|
||||
* Framework component renderers enable UI framework rendering (static and dynamic).
|
||||
* When you define this in your configuration, all other defaults are disabled.
|
||||
* Default: [
|
||||
* '@astrojs/renderer-svelte',
|
||||
* '@astrojs/renderer-vue',
|
||||
* '@astrojs/renderer-react',
|
||||
* '@astrojs/renderer-preact',
|
||||
* ],
|
||||
*/
|
||||
renderers?: string[];
|
||||
/** Options for rendering markdown content */
|
||||
markdownOptions?: {
|
||||
render?: [string, Record<string, any>];
|
||||
};
|
||||
/** Options specific to `astro build` */
|
||||
buildOptions?: {
|
||||
/** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
|
||||
site?: string;
|
||||
/**
|
||||
* Generate an automatically-generated sitemap for your build.
|
||||
* Default: true
|
||||
*/
|
||||
sitemap?: boolean;
|
||||
/**
|
||||
* Control the output file URL format of each page.
|
||||
* If 'file', Astro will generate a matching HTML file (ex: "/foo.html") instead of a directory.
|
||||
* If 'directory', Astro will generate a directory with a nested index.html (ex: "/foo/index.html") for each page.
|
||||
* Default: 'directory'
|
||||
*/
|
||||
pageUrlFormat?: 'file' | 'directory';
|
||||
};
|
||||
/** Options for the development server run with `astro dev`. */
|
||||
devOptions?: {
|
||||
hostname?: string;
|
||||
/** The port to run the dev server on. */
|
||||
port?: number;
|
||||
/**
|
||||
* Configure The trailing slash behavior of URL route matching:
|
||||
* 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
|
||||
* 'never' - Never match URLs that include a trailing slash (ex: "/foo")
|
||||
* 'ignore' - Match URLs regardless of whether a trailing "/" exists
|
||||
* Default: 'always'
|
||||
*/
|
||||
trailingSlash?: 'always' | 'never' | 'ignore';
|
||||
};
|
||||
/** Pass configuration options to Vite */
|
||||
vite?: vite.InlineConfig;
|
||||
}
|
||||
|
||||
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
|
||||
// we can add JSDoc-style documentation and link to the definition file in our repo.
|
||||
// However, Zod comes with the ability to auto-generate AstroConfig from the schema
|
||||
// above. If we ever get to the point where we no longer need the dedicated
|
||||
// @types/config.ts file, consider replacing it with the following lines:
|
||||
//
|
||||
// export interface AstroUserConfig extends z.input<typeof AstroConfigSchema> {
|
||||
// }
|
||||
|
||||
export type AstroConfig = z.output<typeof AstroConfigSchema>;
|
||||
|
||||
export type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>;
|
||||
|
||||
export interface CollectionRSS {
|
||||
/** (required) Title of the RSS Feed */
|
||||
title: string;
|
||||
/** (required) Description of the RSS Feed */
|
||||
description: string;
|
||||
/** Specify arbitrary metadata on opening <xml> tag */
|
||||
xmlns?: Record<string, string>;
|
||||
/** Specify custom data in opening of file */
|
||||
customData?: string;
|
||||
/**
|
||||
* Specify where the RSS xml file should be written.
|
||||
* Relative to final build directory. Example: '/foo/bar.xml'
|
||||
* Defaults to '/rss.xml'.
|
||||
*/
|
||||
dest?: string;
|
||||
/** Return data about each item */
|
||||
items: {
|
||||
/** (required) Title of item */
|
||||
title: string;
|
||||
/** (required) Link to item */
|
||||
link: string;
|
||||
/** Publication date of item */
|
||||
pubDate?: Date;
|
||||
/** Item description */
|
||||
description?: string;
|
||||
/** Append some other XML-valid data to this item */
|
||||
customData?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/** Generic interface for a component (Astro, Svelte, React, etc.) */
|
||||
export interface ComponentInstance {
|
||||
default: AstroComponentFactory;
|
||||
css?: string[];
|
||||
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
|
||||
}
|
||||
|
||||
export type GetStaticPathsArgs = { paginate: PaginateFunction; rss: RSSFunction };
|
||||
|
||||
export interface GetStaticPathsOptions {
|
||||
paginate?: PaginateFunction;
|
||||
rss?: (...args: any[]) => any;
|
||||
}
|
||||
|
||||
export type GetStaticPathsResult = { params: Params; props?: Props }[] | { params: Params; props?: Props }[];
|
||||
|
||||
export interface JSXTransformConfig {
|
||||
/** Babel presets */
|
||||
presets?: babel.PluginItem[];
|
||||
/** Babel plugins */
|
||||
plugins?: babel.PluginItem[];
|
||||
}
|
||||
|
||||
export type JSXTransformFn = (options: { mode: string; ssr: boolean }) => Promise<JSXTransformConfig>;
|
||||
|
||||
export interface ManifestData {
|
||||
routes: RouteData[];
|
||||
}
|
||||
|
||||
export interface PaginatedCollectionProp<T = any> {
|
||||
/** result */
|
||||
data: T[];
|
||||
/** metadata */
|
||||
/** the count of the first item on the page, starting from 0 */
|
||||
start: number;
|
||||
/** the count of the last item on the page, starting from 0 */
|
||||
end: number;
|
||||
/** total number of results */
|
||||
total: number;
|
||||
/** the current page number, starting from 1 */
|
||||
currentPage: number;
|
||||
/** number of items per page (default: 25) */
|
||||
size: number;
|
||||
/** number of last page */
|
||||
lastPage: number;
|
||||
url: {
|
||||
/** url of the current page */
|
||||
current: string;
|
||||
/** url of the previous page (if there is one) */
|
||||
prev: string | undefined;
|
||||
/** url of the next page (if there is one) */
|
||||
next: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PaginatedCollectionResult<T = any> {
|
||||
/** result */
|
||||
data: T[];
|
||||
/** metadata */
|
||||
/** the count of the first item on the page, starting from 0 */
|
||||
start: number;
|
||||
/** the count of the last item on the page, starting from 0 */
|
||||
end: number;
|
||||
/** total number of results */
|
||||
total: number;
|
||||
/** the current page number, starting from 1 */
|
||||
currentPage: number;
|
||||
/** number of items per page (default: 25) */
|
||||
size: number;
|
||||
/** number of last page */
|
||||
lastPage: number;
|
||||
url: {
|
||||
/** url of the current page */
|
||||
current: string;
|
||||
/** url of the previous page (if there is one) */
|
||||
prev: string | undefined;
|
||||
/** url of the next page (if there is one) */
|
||||
next: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export type PaginateFunction = (data: [], args?: { pageSize?: number; params?: Params; props?: Props }) => GetStaticPathsResult;
|
||||
|
||||
export type Params = Record<string, string | undefined>;
|
||||
|
||||
export type Props = Record<string, unknown>;
|
||||
|
||||
export interface RenderPageOptions {
|
||||
request: {
|
||||
params?: Params;
|
||||
url: URL;
|
||||
canonicalURL: URL;
|
||||
};
|
||||
children: any[];
|
||||
props: Props;
|
||||
css?: string[];
|
||||
}
|
||||
|
||||
export interface Renderer {
|
||||
/** Name of the renderer (required) */
|
||||
name: string;
|
||||
/** Import statement for renderer */
|
||||
source?: string;
|
||||
/** Scripts to be injected before component */
|
||||
polyfills?: string[];
|
||||
/** Polyfills that need to run before hydration ever occurs */
|
||||
hydrationPolyfills?: string[];
|
||||
/** JSX identifier (e.g. 'react' or 'solid-js') */
|
||||
jsxImportSource?: string;
|
||||
/** Babel transform options */
|
||||
jsxTransformOptions?: JSXTransformFn;
|
||||
/** Utilies for server-side rendering */
|
||||
ssr: {
|
||||
check: AsyncRendererComponentFn<boolean>;
|
||||
renderToStaticMarkup: AsyncRendererComponentFn<{
|
||||
html: string;
|
||||
}>;
|
||||
};
|
||||
/** Return configuration object for Vite ("options" should match https://vitejs.dev/guide/api-plugin.html#config) */
|
||||
viteConfig?: (options: { mode: 'string'; command: 'build' | 'serve' }) => Promise<vite.InlineConfig>;
|
||||
/** @deprecated Don’t try and build these dependencies for client (deprecated in 0.21) */
|
||||
external?: string[];
|
||||
/** @deprecated Clientside requirements (deprecated in 0.21) */
|
||||
knownEntrypoints?: string[];
|
||||
}
|
||||
|
||||
export interface RouteData {
|
||||
component: string;
|
||||
generate: (data?: any) => string;
|
||||
params: string[];
|
||||
pathname?: string;
|
||||
pattern: RegExp;
|
||||
type: 'page';
|
||||
}
|
||||
|
||||
export type RouteCache = Record<string, GetStaticPathsResult>;
|
||||
|
||||
export type RuntimeMode = 'development' | 'production';
|
||||
|
||||
export type RSSFunction = (args: RSSFunctionArgs) => void;
|
||||
|
||||
export interface RSSFunctionArgs {
|
||||
/** (required) Title of the RSS Feed */
|
||||
title: string;
|
||||
/** (required) Description of the RSS Feed */
|
||||
description: string;
|
||||
/** Specify arbitrary metadata on opening <xml> tag */
|
||||
xmlns?: Record<string, string>;
|
||||
/** Specify custom data in opening of file */
|
||||
customData?: string;
|
||||
/**
|
||||
* Specify where the RSS xml file should be written.
|
||||
* Relative to final build directory. Example: '/foo/bar.xml'
|
||||
* Defaults to '/rss.xml'.
|
||||
*/
|
||||
dest?: string;
|
||||
/** Return data about each item */
|
||||
items: {
|
||||
/** (required) Title of item */
|
||||
title: string;
|
||||
/** (required) Link to item */
|
||||
link: string;
|
||||
/** Publication date of item */
|
||||
pubDate?: Date;
|
||||
/** Item description */
|
||||
description?: string;
|
||||
/** Append some other XML-valid data to this item */
|
||||
customData?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type RSSResult = { url: string; xml?: string };
|
||||
|
||||
export type ScriptInfo = ScriptInfoInline | ScriptInfoExternal;
|
||||
|
||||
export type SSRError = Error & vite.ErrorPayload['err'];
|
||||
|
||||
export interface ScriptInfoInline {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ScriptInfoExternal {
|
||||
src: string;
|
||||
}
|
64
packages/astro/src/@types/astro-runtime.ts
Normal file
64
packages/astro/src/@types/astro-runtime.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import type { Renderer } from './astro-core';
|
||||
|
||||
export interface AstroBuiltinProps {
|
||||
'client:load'?: boolean;
|
||||
'client:idle'?: boolean;
|
||||
'client:media'?: string;
|
||||
'client:visible'?: boolean;
|
||||
}
|
||||
|
||||
export interface AstroGlobal extends TopLevelAstro {
|
||||
props: Record<string, number | string | any>;
|
||||
request: AstroPageRequest;
|
||||
slots: Record<string, true | undefined>;
|
||||
}
|
||||
|
||||
interface AstroPageRequest {
|
||||
url: URL;
|
||||
canonicalURL: URL;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
type AstroRenderedHTML = string;
|
||||
|
||||
export type FetchContentResultBase = {
|
||||
astro: {
|
||||
headers: string[];
|
||||
source: string;
|
||||
html: AstroRenderedHTML;
|
||||
};
|
||||
url: URL;
|
||||
};
|
||||
|
||||
export type FetchContentResult<T> = FetchContentResultBase & T;
|
||||
|
||||
export interface HydrateOptions {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
|
||||
|
||||
export type Params = Record<string, string | undefined>;
|
||||
|
||||
export interface TopLevelAstro {
|
||||
isPage: boolean;
|
||||
fetchContent<T = any>(globStr: string): Promise<FetchContentResult<T>[]>;
|
||||
resolve: (path: string) => string;
|
||||
site: URL;
|
||||
}
|
||||
|
||||
export interface SSRMetadata {
|
||||
renderers: Renderer[];
|
||||
}
|
||||
|
||||
export interface SSRResult {
|
||||
styles: Set<SSRElement>;
|
||||
scripts: Set<SSRElement>;
|
||||
createAstro(Astro: TopLevelAstro, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
|
||||
_metadata: SSRMetadata;
|
||||
}
|
||||
|
||||
export interface SSRElement {
|
||||
props: Record<string, any>;
|
||||
children: string;
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types';
|
||||
import type { AstroUserConfig, AstroConfig } from './config';
|
||||
|
||||
export { AstroUserConfig, AstroConfig };
|
||||
export interface RouteData {
|
||||
type: 'page';
|
||||
pattern: RegExp;
|
||||
params: string[];
|
||||
path: string | null;
|
||||
component: string;
|
||||
generate: (data?: any) => string;
|
||||
}
|
||||
|
||||
export interface ManifestData {
|
||||
routes: RouteData[];
|
||||
}
|
||||
|
||||
export interface JsxItem {
|
||||
name: string;
|
||||
jsx: string;
|
||||
}
|
||||
|
||||
export interface InlineScriptInfo {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ExternalScriptInfo {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export type ScriptInfo = InlineScriptInfo | ExternalScriptInfo;
|
||||
|
||||
export interface TransformResult {
|
||||
script: string;
|
||||
imports: string[];
|
||||
exports: string[];
|
||||
components: string[];
|
||||
html: string;
|
||||
css?: string;
|
||||
hoistedScripts: ScriptInfo[];
|
||||
getStaticPaths?: string;
|
||||
hasCustomElements: boolean;
|
||||
customElementCandidates: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface CompileResult {
|
||||
result: TransformResult;
|
||||
contents: string;
|
||||
css?: string;
|
||||
}
|
||||
|
||||
export type RuntimeMode = 'development' | 'production';
|
||||
|
||||
export type Params = Record<string, string | undefined>;
|
||||
export type Props = Record<string, any>;
|
||||
|
||||
/** Entire output of `astro build`, stored in memory */
|
||||
export interface BuildOutput {
|
||||
[dist: string]: BuildFile;
|
||||
}
|
||||
|
||||
export interface BuildFile {
|
||||
/** The original location. Needed for code frame errors. */
|
||||
srcPath: URL;
|
||||
/** File contents */
|
||||
contents: string | Buffer;
|
||||
/** File content type (to determine encoding, etc) */
|
||||
contentType: string;
|
||||
/** Encoding */
|
||||
encoding?: 'utf8';
|
||||
/** Extracted scripts */
|
||||
hoistedScripts?: ScriptInfo[];
|
||||
}
|
||||
|
||||
/** Mapping of every URL and its required assets. All URLs are absolute relative to the project. */
|
||||
export type BundleMap = {
|
||||
[pageUrl: string]: PageDependencies;
|
||||
};
|
||||
|
||||
export interface PageDependencies {
|
||||
/** JavaScript files needed for page. No distinction between blocking/non-blocking or sync/async. */
|
||||
js: Set<string>;
|
||||
/** CSS needed for page, whether imported via <link>, JS, or Astro component. */
|
||||
css: Set<string>;
|
||||
/** Images needed for page. Can be loaded via CSS, <link>, or otherwise. */
|
||||
images: Set<string>;
|
||||
/** Async hoisted Javascript */
|
||||
hoistedJS: Map<string, ScriptInfo>;
|
||||
}
|
||||
|
||||
export interface RSSFunctionArgs {
|
||||
/** (required) Title of the RSS Feed */
|
||||
title: string;
|
||||
/** (required) Description of the RSS Feed */
|
||||
description: string;
|
||||
/** Specify arbitrary metadata on opening <xml> tag */
|
||||
xmlns?: Record<string, string>;
|
||||
/** Specify custom data in opening of file */
|
||||
customData?: string;
|
||||
/**
|
||||
* Specify where the RSS xml file should be written.
|
||||
* Relative to final build directory. Example: '/foo/bar.xml'
|
||||
* Defaults to '/rss.xml'.
|
||||
*/
|
||||
dest?: string;
|
||||
/** Return data about each item */
|
||||
items: {
|
||||
/** (required) Title of item */
|
||||
title: string;
|
||||
/** (required) Link to item */
|
||||
link: string;
|
||||
/** Publication date of item */
|
||||
pubDate?: Date;
|
||||
/** Item description */
|
||||
description?: string;
|
||||
/** Append some other XML-valid data to this item */
|
||||
customData?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface PaginatedCollectionProp<T = any> {
|
||||
/** result */
|
||||
data: T[];
|
||||
/** metadata */
|
||||
/** the count of the first item on the page, starting from 0 */
|
||||
start: number;
|
||||
/** the count of the last item on the page, starting from 0 */
|
||||
end: number;
|
||||
/** total number of results */
|
||||
total: number;
|
||||
/** the current page number, starting from 1 */
|
||||
currentPage: number;
|
||||
/** number of items per page (default: 25) */
|
||||
size: number;
|
||||
/** number of last page */
|
||||
lastPage: number;
|
||||
url: {
|
||||
/** url of the current page */
|
||||
current: string;
|
||||
/** url of the previous page (if there is one) */
|
||||
prev: string | undefined;
|
||||
/** url of the next page (if there is one) */
|
||||
next: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export type RSSFunction = (args: RSSFunctionArgs) => void;
|
||||
export type PaginateFunction = (data: [], args?: { pageSize?: number; params?: Params; props?: Props }) => GetStaticPathsResult;
|
||||
export type GetStaticPathsArgs = { paginate: PaginateFunction; rss: RSSFunction };
|
||||
export type GetStaticPathsResult = { params: Params; props?: Props }[] | { params: Params; props?: Props }[];
|
||||
|
||||
export interface ComponentInfo {
|
||||
url: string;
|
||||
importSpecifier: ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier;
|
||||
}
|
||||
|
||||
export type Components = Map<string, ComponentInfo>;
|
||||
|
||||
export interface AstroComponentMetadata {
|
||||
displayName: string;
|
||||
hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only';
|
||||
componentUrl?: string;
|
||||
componentExport?: { value: string; namespace?: boolean };
|
||||
value?: undefined | string;
|
||||
}
|
||||
|
||||
type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>;
|
||||
|
||||
export interface Renderer {
|
||||
check: AsyncRendererComponentFn<boolean>;
|
||||
renderToStaticMarkup: AsyncRendererComponentFn<{
|
||||
html: string;
|
||||
}>;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import type { LogOptions } from '../logger';
|
||||
import type { AstroConfig, RuntimeMode } from './astro';
|
||||
|
||||
export interface CompileOptions {
|
||||
logging: LogOptions;
|
||||
resolvePackageUrl: (p: string) => Promise<string>;
|
||||
astroConfig: AstroConfig;
|
||||
hmrPort?: number;
|
||||
mode: RuntimeMode;
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
import type { AstroMarkdownOptions } from '@astrojs/markdown-support';
|
||||
import type { AstroConfigSchema } from '../config';
|
||||
import type { z } from 'zod';
|
||||
|
||||
/**
|
||||
* The Astro User Config Format:
|
||||
* This is the type interface for your astro.config.mjs default export.
|
||||
*/
|
||||
export interface AstroUserConfig {
|
||||
/**
|
||||
* Where to resolve all URLs relative to. Useful if you have a monorepo project.
|
||||
* Default: '.' (current working directory)
|
||||
*/
|
||||
projectRoot?: string;
|
||||
/**
|
||||
* Path to the `astro build` output.
|
||||
* Default: './dist'
|
||||
*/
|
||||
dist?: string;
|
||||
/**
|
||||
* Path to all of your Astro components, pages, and data.
|
||||
* Default: './src'
|
||||
*/
|
||||
src?: string;
|
||||
/**
|
||||
* Path to your Astro/Markdown pages. Each file in this directory
|
||||
* becomes a page in your final build.
|
||||
* Default: './src/pages'
|
||||
*/
|
||||
pages?: string;
|
||||
/**
|
||||
* Path to your public files. These are copied over into your build directory, untouched.
|
||||
* Useful for favicons, images, and other files that don't need processing.
|
||||
* Default: './public'
|
||||
*/
|
||||
public?: string;
|
||||
/**
|
||||
* Framework component renderers enable UI framework rendering (static and dynamic).
|
||||
* When you define this in your configuration, all other defaults are disabled.
|
||||
* Default: [
|
||||
* '@astrojs/renderer-svelte',
|
||||
* '@astrojs/renderer-vue',
|
||||
* '@astrojs/renderer-react',
|
||||
* '@astrojs/renderer-preact',
|
||||
* ],
|
||||
*/
|
||||
renderers?: string[];
|
||||
/** Options for rendering markdown content */
|
||||
markdownOptions?: Partial<AstroMarkdownOptions>;
|
||||
/** Options specific to `astro build` */
|
||||
buildOptions?: {
|
||||
/** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
|
||||
site?: string;
|
||||
/** Generate an automatically-generated sitemap for your build.
|
||||
* Default: true
|
||||
*/
|
||||
sitemap?: boolean;
|
||||
/**
|
||||
* Control the output file URL format of each page.
|
||||
* If 'file', Astro will generate a matching HTML file (ex: "/foo.html") instead of a directory.
|
||||
* If 'directory', Astro will generate a directory with a nested index.html (ex: "/foo/index.html") for each page.
|
||||
* Default: 'directory'
|
||||
*/
|
||||
pageUrlFormat?: 'file' | 'directory';
|
||||
};
|
||||
/** Options for the development server run with `astro dev`. */
|
||||
devOptions?: {
|
||||
hostname?: string;
|
||||
/** The port to run the dev server on. */
|
||||
port?: number;
|
||||
/** Path to tailwind.config.js, if used */
|
||||
tailwindConfig?: string;
|
||||
/**
|
||||
* Configure The trailing slash behavior of URL route matching:
|
||||
* 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
|
||||
* 'never' - Never match URLs that include a trailing slash (ex: "/foo")
|
||||
* 'ignore' - Match URLs regardless of whether a trailing "/" exists
|
||||
* Default: 'always'
|
||||
*/
|
||||
trailingSlash?: 'always' | 'never' | 'ignore';
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
|
||||
// we can add JSDoc-style documentation and link to the definition file in our repo.
|
||||
// However, Zod comes with the ability to auto-generate AstroConfig from the schema
|
||||
// above. If we ever get to the point where we no longer need the dedicated
|
||||
// @types/config.ts file, consider replacing it with the following lines:
|
||||
//
|
||||
// export interface AstroUserConfig extends z.input<typeof AstroConfigSchema> {
|
||||
// markdownOptions?: Partial<AstroMarkdownOptions>;
|
||||
// }
|
||||
export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
|
||||
markdownOptions: Partial<AstroMarkdownOptions>;
|
||||
}
|
25
packages/astro/src/@types/estree-walker.d.ts
vendored
25
packages/astro/src/@types/estree-walker.d.ts
vendored
|
@ -1,25 +0,0 @@
|
|||
import { BaseNode } from 'estree-walker';
|
||||
|
||||
declare module 'estree-walker' {
|
||||
export function walk<T = BaseNode>(
|
||||
ast: T,
|
||||
{
|
||||
enter,
|
||||
leave,
|
||||
}: {
|
||||
enter?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
|
||||
leave?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
|
||||
}
|
||||
): T;
|
||||
|
||||
export function asyncWalk<T = BaseNode>(
|
||||
ast: T,
|
||||
{
|
||||
enter,
|
||||
leave,
|
||||
}: {
|
||||
enter?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
|
||||
leave?: (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, key: string, index: number) => void;
|
||||
}
|
||||
): T;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
|
||||
|
||||
export interface HydrateOptions {
|
||||
value?: string;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
// TODO: add types (if helpful)
|
||||
declare module 'micromark-extension-gfm';
|
||||
declare module 'micromark-extension-gfm/html.js';
|
|
@ -1,14 +0,0 @@
|
|||
export interface MicromarkExtensionContext {
|
||||
sliceSerialize(node: any): string;
|
||||
raw(value: string): void;
|
||||
tag(value: string): void;
|
||||
data(value: string): void;
|
||||
resume(): any;
|
||||
}
|
||||
|
||||
export type MicromarkExtensionCallback = (this: MicromarkExtensionContext, node: any) => void;
|
||||
|
||||
export interface MicromarkExtension {
|
||||
enter?: Record<string, MicromarkExtensionCallback>;
|
||||
exit?: Record<string, MicromarkExtensionCallback>;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
declare module 'postcss-icss-keyframes' {
|
||||
import type { Plugin } from 'postcss';
|
||||
|
||||
export default function (options: { generateScopedName(keyframesName: string, filepath: string, css: string): string }): Plugin;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { AstroConfig, AstroUserConfig } from './config';
|
1
packages/astro/src/@types/resolve.d.ts
vendored
1
packages/astro/src/@types/resolve.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
declare module 'resolve';
|
2
packages/astro/src/@types/tailwind.d.ts
vendored
2
packages/astro/src/@types/tailwind.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
// we shouldn‘t have this as a dependency for Astro, but we may dynamically import it if a user requests it, so let TS know about it
|
||||
declare module 'tailwindcss';
|
|
@ -1,23 +0,0 @@
|
|||
import type { TemplateNode } from '@astrojs/parser';
|
||||
import type { CompileOptions } from './compiler';
|
||||
|
||||
export type VisitorFn<T = TemplateNode> = (this: { skip: () => void; remove: () => void; replace: (node: T) => void }, node: T, parent: T, type: string, index: number) => void;
|
||||
|
||||
export interface NodeVisitor {
|
||||
enter?: VisitorFn;
|
||||
leave?: VisitorFn;
|
||||
}
|
||||
|
||||
export interface Transformer {
|
||||
visitors?: {
|
||||
html?: Record<string, NodeVisitor>;
|
||||
css?: Record<string, NodeVisitor>;
|
||||
};
|
||||
finalize: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface TransformOptions {
|
||||
compileOptions: CompileOptions;
|
||||
filename: string;
|
||||
fileID: string;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import type { Attribute } from '@astrojs/parser';
|
||||
|
||||
// AST utility functions
|
||||
|
||||
/** Get TemplateNode attribute from name */
|
||||
export function getAttr(attributes: Attribute[], name: string): Attribute | undefined {
|
||||
const attr = attributes.find((a) => a.name === name);
|
||||
return attr;
|
||||
}
|
||||
|
||||
/** Get TemplateNode attribute by value */
|
||||
export function getAttrValue(attributes: Attribute[], name: string): string | undefined {
|
||||
if (attributes.length === 0) return '';
|
||||
const attr = getAttr(attributes, name);
|
||||
if (attr) {
|
||||
return attr.value[0]?.data;
|
||||
}
|
||||
}
|
||||
|
||||
/** Set TemplateNode attribute value */
|
||||
export function setAttrValue(attributes: Attribute[], name: string, value: string): void {
|
||||
const attr = attributes.find((a) => a.name === name);
|
||||
if (attr && attr.value[0]) {
|
||||
attr.value[0].data = value;
|
||||
attr.value[0].raw = value;
|
||||
}
|
||||
}
|
|
@ -1,365 +0,0 @@
|
|||
import cheerio from 'cheerio';
|
||||
import del from 'del';
|
||||
import eslexer from 'es-module-lexer';
|
||||
import fs from 'fs';
|
||||
import { bold, green, red, underline, yellow } from 'kleur/colors';
|
||||
import mime from 'mime';
|
||||
import path from 'path';
|
||||
import { performance } from 'perf_hooks';
|
||||
import glob from 'tiny-glob';
|
||||
import hash from 'shorthash';
|
||||
import srcsetParse from 'srcset-parse';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { AstroConfig, BuildOutput, BundleMap, PageDependencies, RouteData, RuntimeMode, ScriptInfo } from './@types/astro';
|
||||
import { bundleCSS } from './build/bundle/css.js';
|
||||
import { bundleJS, bundleHoistedJS, collectJSImports } from './build/bundle/js.js';
|
||||
import { buildStaticPage, getStaticPathsForPage } from './build/page.js';
|
||||
import { generateSitemap } from './build/sitemap.js';
|
||||
import { collectBundleStats, logURLStats, mapBundleStatsToURLStats } from './build/stats.js';
|
||||
import { getDistPath, stopTimer } from './build/util.js';
|
||||
import type { LogOptions } from './logger';
|
||||
import { debug, defaultLogDestination, defaultLogLevel, error, info, warn } from './logger.js';
|
||||
import { createRuntime, LoadResult } from './runtime.js';
|
||||
|
||||
// This package isn't real ESM, so have to coerce it
|
||||
const matchSrcset: typeof srcsetParse = (srcsetParse as any).default;
|
||||
|
||||
const defaultLogging: LogOptions = {
|
||||
level: defaultLogLevel,
|
||||
dest: defaultLogDestination,
|
||||
};
|
||||
|
||||
/** Is this URL remote or embedded? */
|
||||
function isRemoteOrEmbedded(url: string) {
|
||||
return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//') || url.startsWith('data:');
|
||||
}
|
||||
|
||||
/** The primary build action */
|
||||
export async function build(astroConfig: AstroConfig, logging: LogOptions = defaultLogging): Promise<0 | 1> {
|
||||
const { projectRoot } = astroConfig;
|
||||
const buildState: BuildOutput = {};
|
||||
const depTree: BundleMap = {};
|
||||
const timer: Record<string, number> = {};
|
||||
|
||||
const runtimeLogging: LogOptions = {
|
||||
level: 'error',
|
||||
dest: defaultLogDestination,
|
||||
};
|
||||
|
||||
// warn users if missing config item in build that may result in broken SEO (can’t disable, as they should provide this)
|
||||
if (!astroConfig.buildOptions.site) {
|
||||
warn(logging, 'config', `Set "buildOptions.site" to generate correct canonical URLs and sitemap`);
|
||||
}
|
||||
|
||||
const mode: RuntimeMode = 'production';
|
||||
const astroRuntime = await createRuntime(astroConfig, { mode, logging: runtimeLogging });
|
||||
const { runtimeConfig } = astroRuntime;
|
||||
const { snowpackRuntime } = runtimeConfig;
|
||||
|
||||
try {
|
||||
// 0. erase build directory
|
||||
await del(fileURLToPath(astroConfig.dist));
|
||||
|
||||
/**
|
||||
* 1. Build Pages
|
||||
* Source files are built in parallel and stored in memory. Most assets are also gathered here, too.
|
||||
*/
|
||||
timer.build = performance.now();
|
||||
info(logging, 'build', yellow('! building pages...'));
|
||||
const allRoutesAndPaths = await Promise.all(
|
||||
runtimeConfig.manifest.routes.map(async (route): Promise<[RouteData, string[]]> => {
|
||||
if (route.path) {
|
||||
return [route, [route.path]];
|
||||
} else {
|
||||
const result = await getStaticPathsForPage({
|
||||
astroConfig,
|
||||
astroRuntime,
|
||||
route,
|
||||
snowpackRuntime,
|
||||
logging,
|
||||
});
|
||||
if (result.rss.xml) {
|
||||
if (buildState[result.rss.url]) {
|
||||
throw new Error(`[getStaticPaths] RSS feed ${result.rss.url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`);
|
||||
}
|
||||
buildState[result.rss.url] = {
|
||||
srcPath: new URL(result.rss.url, projectRoot),
|
||||
contents: result.rss.xml,
|
||||
contentType: 'text/xml',
|
||||
encoding: 'utf8',
|
||||
};
|
||||
}
|
||||
return [route, result.paths];
|
||||
}
|
||||
})
|
||||
);
|
||||
try {
|
||||
await Promise.all(
|
||||
allRoutesAndPaths.map(async ([route, paths]: [RouteData, string[]]) => {
|
||||
for (const p of paths) {
|
||||
await buildStaticPage({
|
||||
astroConfig,
|
||||
buildState,
|
||||
route,
|
||||
path: p,
|
||||
astroRuntime,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.filename) {
|
||||
let stack = e.stack
|
||||
.replace(/Object\.__render \(/gm, '')
|
||||
.replace(/\/_astro\/(.+)\.astro\.js\:\d+\:\d+\)/gm, (_: string, $1: string) => 'file://' + fileURLToPath(projectRoot) + $1 + '.astro')
|
||||
.split('\n');
|
||||
stack.splice(1, 0, ` at file://${e.filename}`);
|
||||
stack = stack.join('\n');
|
||||
error(
|
||||
logging,
|
||||
'build',
|
||||
`${red(`Unable to render ${underline(e.filename.replace(fileURLToPath(projectRoot), ''))}`)}
|
||||
|
||||
${stack}
|
||||
`
|
||||
);
|
||||
} else {
|
||||
error(logging, 'build', e.message);
|
||||
}
|
||||
error(logging, 'build', red('✕ building pages failed!'));
|
||||
|
||||
await astroRuntime.shutdown();
|
||||
return 1;
|
||||
}
|
||||
info(logging, 'build', green('✔'), 'pages built.');
|
||||
debug(logging, 'build', `built pages [${stopTimer(timer.build)}]`);
|
||||
|
||||
// after pages are built, build depTree
|
||||
timer.deps = performance.now();
|
||||
const scanPromises: Promise<void>[] = [];
|
||||
|
||||
await eslexer.init;
|
||||
for (const id of Object.keys(buildState)) {
|
||||
if (buildState[id].contentType !== 'text/html') continue; // only scan HTML files
|
||||
const pageDeps = findDeps(buildState[id].contents as string, {
|
||||
astroConfig,
|
||||
srcPath: buildState[id].srcPath,
|
||||
id,
|
||||
});
|
||||
depTree[id] = pageDeps;
|
||||
|
||||
// while scanning we will find some unbuilt files; make sure those are all built while scanning
|
||||
for (const url of [...pageDeps.js, ...pageDeps.css, ...pageDeps.images]) {
|
||||
if (!buildState[url])
|
||||
scanPromises.push(
|
||||
astroRuntime.load(url).then((result: LoadResult) => {
|
||||
if (result.statusCode === 404) {
|
||||
if (url.startsWith('/_astro/')) {
|
||||
throw new Error(`${buildState[id].srcPath.href}: could not find file "${url}".`);
|
||||
}
|
||||
warn(logging, 'build', `${buildState[id].srcPath.href}: could not find file "${url}". Marked as external.`);
|
||||
return;
|
||||
}
|
||||
if (result.statusCode !== 200) {
|
||||
// there shouldn’t be a build error here
|
||||
throw (result as any).error || new Error(`unexpected ${result.statusCode} response from "${url}".`);
|
||||
}
|
||||
buildState[url] = {
|
||||
srcPath: new URL(url, projectRoot),
|
||||
contents: result.contents,
|
||||
contentType: result.contentType || mime.getType(url) || '',
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
await Promise.all(scanPromises);
|
||||
debug(logging, 'build', `scanned deps [${stopTimer(timer.deps)}]`);
|
||||
|
||||
/**
|
||||
* 2. Bundling 1st Pass: In-memory
|
||||
* Bundle CSS, and anything else that can happen in memory (for now, JS bundling happens after writing to disk)
|
||||
*/
|
||||
info(logging, 'build', yellow('! optimizing css...'));
|
||||
timer.prebundleCSS = performance.now();
|
||||
await Promise.all([
|
||||
bundleCSS({ buildState, astroConfig, logging, depTree }).then(() => {
|
||||
debug(logging, 'build', `bundled CSS [${stopTimer(timer.prebundleCSS)}]`);
|
||||
}),
|
||||
bundleHoistedJS({ buildState, astroConfig, logging, depTree, runtime: astroRuntime, dist: astroConfig.dist }),
|
||||
// TODO: optimize images?
|
||||
]);
|
||||
// TODO: minify HTML?
|
||||
info(logging, 'build', green('✔'), 'css optimized.');
|
||||
|
||||
/**
|
||||
* 3. Write to disk
|
||||
* Also clear in-memory bundle
|
||||
*/
|
||||
// collect stats output
|
||||
const urlStats = await collectBundleStats(buildState, depTree);
|
||||
|
||||
// collect JS imports for bundling
|
||||
const jsImports = await collectJSImports(buildState);
|
||||
|
||||
// write sitemap
|
||||
if (astroConfig.buildOptions.sitemap && astroConfig.buildOptions.site) {
|
||||
timer.sitemap = performance.now();
|
||||
info(logging, 'build', yellow('! creating sitemap...'));
|
||||
const sitemap = generateSitemap(buildState, astroConfig.buildOptions.site);
|
||||
const sitemapPath = new URL('sitemap.xml', astroConfig.dist);
|
||||
await fs.promises.mkdir(path.dirname(fileURLToPath(sitemapPath)), { recursive: true });
|
||||
await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
|
||||
info(logging, 'build', green('✔'), 'sitemap built.');
|
||||
debug(logging, 'build', `built sitemap [${stopTimer(timer.sitemap)}]`);
|
||||
}
|
||||
|
||||
// write to disk and free up memory
|
||||
timer.write = performance.now();
|
||||
for (const id of Object.keys(buildState)) {
|
||||
const outPath = new URL(`.${id}`, astroConfig.dist);
|
||||
const parentDir = path.dirname(fileURLToPath(outPath));
|
||||
await fs.promises.mkdir(parentDir, { recursive: true });
|
||||
const handle = await fs.promises.open(outPath, 'w');
|
||||
await fs.promises.writeFile(handle, buildState[id].contents, buildState[id].encoding);
|
||||
|
||||
// Ensure the file handle is not left hanging which will
|
||||
// result in the garbage collector loggin errors in the console
|
||||
// when it eventually has to close them.
|
||||
await handle.close();
|
||||
|
||||
delete buildState[id];
|
||||
delete depTree[id];
|
||||
}
|
||||
debug(logging, 'build', `wrote files to disk [${stopTimer(timer.write)}]`);
|
||||
|
||||
/**
|
||||
* 4. Copy Public Assets
|
||||
*/
|
||||
if (fs.existsSync(astroConfig.public)) {
|
||||
info(logging, 'build', yellow(`! copying public folder...`));
|
||||
timer.public = performance.now();
|
||||
const cwd = fileURLToPath(astroConfig.public);
|
||||
const publicFiles = await glob('**/*', { cwd, filesOnly: true });
|
||||
await Promise.all(
|
||||
publicFiles.map(async (filepath) => {
|
||||
const srcPath = new URL(filepath, astroConfig.public);
|
||||
const distPath = new URL(filepath, astroConfig.dist);
|
||||
await fs.promises.mkdir(path.dirname(fileURLToPath(distPath)), { recursive: true });
|
||||
await fs.promises.copyFile(srcPath, distPath);
|
||||
})
|
||||
);
|
||||
debug(logging, 'build', `copied public folder [${stopTimer(timer.public)}]`);
|
||||
info(logging, 'build', green('✔'), 'public folder copied.');
|
||||
} else {
|
||||
if (path.basename(astroConfig.public.toString()) !== 'public') {
|
||||
info(logging, 'tip', yellow(`! no public folder ${astroConfig.public} found...`));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. Bundling 2nd Pass: On disk
|
||||
* Bundle JS, which requires hard files to optimize
|
||||
*/
|
||||
info(logging, 'build', yellow(`! bundling...`));
|
||||
if (jsImports.size > 0) {
|
||||
timer.bundleJS = performance.now();
|
||||
const jsStats = await bundleJS(jsImports, { dist: astroConfig.dist, astroRuntime });
|
||||
mapBundleStatsToURLStats({ urlStats, depTree, bundleStats: jsStats });
|
||||
debug(logging, 'build', `bundled JS [${stopTimer(timer.bundleJS)}]`);
|
||||
info(logging, 'build', green(`✔`), 'bundling complete.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 6. Print stats
|
||||
*/
|
||||
logURLStats(logging, urlStats);
|
||||
await astroRuntime.shutdown();
|
||||
info(logging, 'build', bold(green('▶ Build Complete!')));
|
||||
return 0;
|
||||
} catch (err) {
|
||||
error(logging, 'build', err.message);
|
||||
await astroRuntime.shutdown();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Given an HTML string, collect <link> and <img> tags */
|
||||
export function findDeps(html: string, { astroConfig, srcPath }: { astroConfig: AstroConfig; srcPath: URL; id: string }): PageDependencies {
|
||||
const pageDeps: PageDependencies = {
|
||||
js: new Set<string>(),
|
||||
css: new Set<string>(),
|
||||
images: new Set<string>(),
|
||||
hoistedJS: new Map<string, ScriptInfo>(),
|
||||
};
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
$('script').each((_i, el) => {
|
||||
const src = $(el).attr('src');
|
||||
const hoist = $(el).attr('data-astro') === 'hoist';
|
||||
if (hoist) {
|
||||
if (src) {
|
||||
pageDeps.hoistedJS.set(src, {
|
||||
src,
|
||||
});
|
||||
} else {
|
||||
let content = $(el).html() || '';
|
||||
pageDeps.hoistedJS.set(`astro-virtual:${hash.unique(content)}`, {
|
||||
content,
|
||||
});
|
||||
}
|
||||
} else if (src) {
|
||||
if (isRemoteOrEmbedded(src)) return;
|
||||
pageDeps.js.add(getDistPath(src, { astroConfig, srcPath }));
|
||||
} else {
|
||||
const text = $(el).html();
|
||||
if (!text) return;
|
||||
const [imports] = eslexer.parse(text);
|
||||
for (const spec of imports) {
|
||||
const importSrc = spec.n;
|
||||
if (importSrc && !isRemoteOrEmbedded(importSrc)) {
|
||||
pageDeps.js.add(getDistPath(importSrc, { astroConfig, srcPath }));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('link[href]').each((_i, el) => {
|
||||
const href = $(el).attr('href');
|
||||
if (href && !isRemoteOrEmbedded(href) && ($(el).attr('rel') === 'stylesheet' || $(el).attr('type') === 'text/css' || href.endsWith('.css'))) {
|
||||
const dist = getDistPath(href, { astroConfig, srcPath });
|
||||
pageDeps.css.add(dist);
|
||||
}
|
||||
});
|
||||
|
||||
$('img[src]').each((_i, el) => {
|
||||
const src = $(el).attr('src');
|
||||
if (src && !isRemoteOrEmbedded(src)) {
|
||||
pageDeps.images.add(getDistPath(src, { astroConfig, srcPath }));
|
||||
}
|
||||
});
|
||||
|
||||
$('img[srcset]').each((_i, el) => {
|
||||
const srcset = $(el).attr('srcset') || '';
|
||||
for (const src of matchSrcset(srcset)) {
|
||||
if (!isRemoteOrEmbedded(src.url)) {
|
||||
pageDeps.images.add(getDistPath(src.url, { astroConfig, srcPath }));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add in srcset check for <source>
|
||||
$('source[srcset]').each((_i, el) => {
|
||||
const srcset = $(el).attr('srcset') || '';
|
||||
for (const src of matchSrcset(srcset)) {
|
||||
if (!isRemoteOrEmbedded(src.url)) {
|
||||
pageDeps.images.add(getDistPath(src.url, { astroConfig, srcPath }));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// important: preserve the scan order of deps! order matters on pages
|
||||
|
||||
return pageDeps;
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
import type { AstroConfig, BuildOutput, BundleMap } from '../../@types/astro';
|
||||
import type { LogOptions } from '../../logger.js';
|
||||
|
||||
import { performance } from 'perf_hooks';
|
||||
import shorthash from 'shorthash';
|
||||
import cheerio from 'cheerio';
|
||||
import esbuild from 'esbuild';
|
||||
import { getDistPath, getSrcPath, IS_ASTRO_FILE_URL, stopTimer } from '../util.js';
|
||||
import { debug } from '../../logger.js';
|
||||
|
||||
// config
|
||||
const COMMON_URL = `/_astro/common-[HASH].css`; // [HASH] will be replaced
|
||||
|
||||
/**
|
||||
* Bundle CSS
|
||||
* For files within dep tree, find ways to combine them.
|
||||
* Current logic:
|
||||
* - If CSS appears across multiple pages, combine into `/_astro/common.css` bundle
|
||||
* - Otherwise, combine page CSS into one request as `/_astro/[page].css` bundle
|
||||
*
|
||||
* This operation _should_ be relatively-safe to do in parallel with other bundling,
|
||||
* assuming other bundling steps don’t touch CSS. While this step does modify HTML,
|
||||
* it doesn’t keep anything in local memory so other processes may modify HTML too.
|
||||
*
|
||||
* This operation mutates the original references of the buildOutput not only for
|
||||
* safety (prevents possible conflicts), but for efficiency.
|
||||
*/
|
||||
export async function bundleCSS({
|
||||
astroConfig,
|
||||
buildState,
|
||||
logging,
|
||||
depTree,
|
||||
}: {
|
||||
astroConfig: AstroConfig;
|
||||
buildState: BuildOutput;
|
||||
logging: LogOptions;
|
||||
depTree: BundleMap;
|
||||
}): Promise<void> {
|
||||
const timer: Record<string, number> = {};
|
||||
const cssMap = new Map<string, string>();
|
||||
|
||||
// 1. organize CSS into common or page-specific CSS
|
||||
timer.bundle = performance.now();
|
||||
const sortedPages = Object.keys(depTree); // these were scanned in parallel; sort to create somewhat deterministic order
|
||||
sortedPages.sort((a, b) => a.localeCompare(b, 'en', { numeric: true }));
|
||||
for (const pageUrl of sortedPages) {
|
||||
const { css } = depTree[pageUrl];
|
||||
for (const cssUrl of css.keys()) {
|
||||
if (!IS_ASTRO_FILE_URL.test(cssUrl)) {
|
||||
// do not add to cssMap, leave as-is.
|
||||
} else if (cssMap.has(cssUrl)) {
|
||||
// scenario 1: if multiple URLs require this CSS, upgrade to common chunk
|
||||
cssMap.set(cssUrl, COMMON_URL);
|
||||
} else {
|
||||
// scenario 2: otherwise, assume this CSS is page-specific
|
||||
cssMap.set(cssUrl, '/_astro' + pageUrl.replace(/.html$/, '').replace(/^\./, '') + '-[HASH].css');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. bundle (note: assume cssMap keys are in specific, correct order; assume buildState[] keys are in different order each time)
|
||||
timer.bundle = performance.now();
|
||||
// note: don’t parallelize here otherwise CSS may end up in random order
|
||||
for (const id of cssMap.keys()) {
|
||||
const newUrl = cssMap.get(id) as string;
|
||||
|
||||
// if new bundle, create
|
||||
if (!buildState[newUrl]) {
|
||||
buildState[newUrl] = {
|
||||
srcPath: getSrcPath(id, { astroConfig }), // this isn’t accurate, but we can at least reference a file in the bundle
|
||||
contents: '',
|
||||
contentType: 'text/css',
|
||||
encoding: 'utf8',
|
||||
};
|
||||
}
|
||||
|
||||
// append to bundle, delete old file
|
||||
(buildState[newUrl] as any).contents += Buffer.isBuffer(buildState[id].contents) ? buildState[id].contents.toString('utf8') : buildState[id].contents;
|
||||
delete buildState[id];
|
||||
}
|
||||
debug(logging, 'css', `bundled [${stopTimer(timer.bundle)}]`);
|
||||
|
||||
// 3. minify
|
||||
timer.minify = performance.now();
|
||||
await Promise.all(
|
||||
Object.keys(buildState).map(async (id) => {
|
||||
if (buildState[id].contentType !== 'text/css') return;
|
||||
const { code } = await esbuild.transform(buildState[id].contents.toString(), {
|
||||
loader: 'css',
|
||||
minify: true,
|
||||
});
|
||||
buildState[id].contents = code;
|
||||
})
|
||||
);
|
||||
debug(logging, 'css', `minified [${stopTimer(timer.minify)}]`);
|
||||
|
||||
// 4. determine hashes based on CSS content (deterministic), and update HTML <link> tags with final hashed URLs
|
||||
timer.hashes = performance.now();
|
||||
const cssHashes = new Map<string, string>();
|
||||
for (const id of Object.keys(buildState)) {
|
||||
if (!id.includes('[HASH].css')) continue; // iterate through buildState, looking to replace [HASH]
|
||||
|
||||
const hash = shorthash.unique(buildState[id].contents as string);
|
||||
const newID = id.replace(/\[HASH\]/, hash);
|
||||
cssHashes.set(id, newID);
|
||||
buildState[newID] = buildState[id]; // copy ref without cloning to save memory
|
||||
delete buildState[id]; // delete old ref
|
||||
}
|
||||
debug(logging, 'css', `built hashes [${stopTimer(timer.hashes)}]`);
|
||||
|
||||
// 5. update HTML <link> tags with final hashed URLs
|
||||
timer.html = performance.now();
|
||||
await Promise.all(
|
||||
Object.keys(buildState).map(async (id) => {
|
||||
if (buildState[id].contentType !== 'text/html') return;
|
||||
|
||||
const $ = cheerio.load(buildState[id].contents);
|
||||
const stylesheets = new Set<string>(); // keep track of page-specific CSS so we remove dupes
|
||||
const preloads = new Set<string>(); // list of stylesheets preloads, to remove dupes
|
||||
|
||||
$('link[href]').each((i, el) => {
|
||||
const srcPath = getSrcPath(id, { astroConfig });
|
||||
const oldHref = getDistPath($(el).attr('href') || '', { astroConfig, srcPath }); // note: this may be a relative URL; transform to absolute to find a buildOutput match
|
||||
const newHref = cssMap.get(oldHref);
|
||||
|
||||
if (!newHref) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.attribs?.rel === 'preload') {
|
||||
if (preloads.has(newHref)) {
|
||||
$(el).remove();
|
||||
} else {
|
||||
$(el).attr('href', cssHashes.get(newHref) || '');
|
||||
preloads.add(newHref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (stylesheets.has(newHref)) {
|
||||
$(el).remove(); // this is a dupe; remove
|
||||
} else {
|
||||
$(el).attr('href', cssHashes.get(newHref) || ''); // new CSS; update href (important! use cssHashes, not cssMap)
|
||||
|
||||
// bonus: add [rel] and [type]. not necessary, but why not?
|
||||
$(el).attr('rel', 'stylesheet');
|
||||
$(el).attr('type', 'text/css');
|
||||
|
||||
stylesheets.add(newHref);
|
||||
}
|
||||
});
|
||||
(buildState[id] as any).contents = $.html(); // save updated HTML in global buildState
|
||||
})
|
||||
);
|
||||
debug(logging, 'css', `parsed html [${stopTimer(timer.html)}]`);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue