Merge branch 'main' into feat/vercel-adapter

This commit is contained in:
JuanM04 2022-04-02 18:53:00 -03:00
commit a377ac1efc
No known key found for this signature in database
GPG key ID: 0171B712E406271A
306 changed files with 3886 additions and 2318 deletions

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Allow components to return a Response

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
`--experimental-ssr` now is only required when using a 3rd-party adapter

View file

@ -1,16 +0,0 @@
---
"astro": minor
---
Implement RFC [#0017](https://github.com/withastro/rfcs/blob/main/proposals/0017-markdown-content-redesign.md)
- New Markdown API
- New `Astro.glob()` API
- **BREAKING CHANGE:** Removed `Astro.fetchContent()` (replaced by `Astro.glob()`)
```diff
// v0.25
- let allPosts = Astro.fetchContent('./posts/*.md');
// v0.26+
+ let allPosts = await Astro.glob('./posts/*.md');
```

View file

@ -1,7 +0,0 @@
---
'astro': patch
---
Improve `Astro.slots` API to support passing arguments to function-based slots.
This allows for more ergonomic utility components that accept a callback function as a child.

View file

@ -1,5 +0,0 @@
---
'@astrojs/react': minor
---
Add support for React v18

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fixes non-GET API routes in dev with Node 14

View file

@ -1,5 +0,0 @@
---
'astro': minor
---
Implements the Astro.request RFC

View file

@ -1,61 +0,0 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"@example/blog": "0.0.1",
"@example/blog-multiple-authors": "0.0.1",
"@example/component": "0.0.1",
"@example/my-component-demo": "0.0.1",
"@example/my-component": "0.0.1",
"@example/docs": "0.0.1",
"@example/env-vars": "0.0.1",
"@example/framework-alpine": "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/integrations-playground": "0.0.1",
"@example/minimal": "0.0.1",
"@example/non-html-pages": "0.0.1",
"@example/portfolio": "0.0.1",
"@example/ssr": "0.0.1",
"@example/starter": "0.0.1",
"@example/subpath": "0.0.1",
"@example/with-markdown": "0.0.1",
"@example/with-markdown-plugins": "0.0.2",
"@example/with-markdown-shiki": "0.0.1",
"@example/with-nanostores": "0.0.1",
"@example/with-tailwindcss": "0.0.1",
"@example/with-vite-plugin-pwa": "0.0.1",
"astro": "0.25.4",
"@astrojs/prism": "0.4.1",
"@test/custom-element-renderer": "0.1.0",
"@test/static-build-pkg": "0.0.0",
"create-astro": "0.8.0",
"@astrojs/lit": "0.0.2",
"@astrojs/netlify": "0.0.2",
"@astrojs/node": "0.0.2",
"@astrojs/partytown": "0.0.2",
"@astrojs/preact": "0.0.2",
"@astrojs/react": "0.0.2",
"@astrojs/sitemap": "0.0.2",
"@astrojs/solid-js": "0.0.3",
"@astrojs/svelte": "0.0.2",
"@astrojs/tailwind": "0.0.2",
"@astrojs/turbolinks": "0.0.2",
"@astrojs/vue": "0.0.2",
"@astrojs/markdown-remark": "0.7.0",
"@astrojs/renderer-lit": "0.4.0",
"@astrojs/renderer-preact": "0.5.0",
"@astrojs/renderer-react": "0.5.0",
"@astrojs/renderer-solid": "0.4.0",
"@astrojs/renderer-svelte": "0.5.2",
"@astrojs/renderer-vue": "0.4.0",
"@astrojs/webapi": "0.11.0",
"astro-scripts": "0.0.2"
},
"changesets": []
}

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Update CLI error format and style

View file

@ -1,6 +0,0 @@
---
'astro': patch
'@astrojs/deno': patch
---
Add a Deno adapter for SSR

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fix for copying public when using SSR and not client JS

View file

@ -1,2 +1,4 @@
# Switch to tabs (Use Accessible Indentation #2253)
6ddd7678ffb6598ae6e263706813cb5e94535f02
# prettier config update
1335797903a57716e9a02b0ffd8ca636b3883c62

View file

@ -1,5 +1,5 @@
{
"printWidth": 180,
"printWidth": 100,
"semi": true,
"singleQuote": true,
"tabWidth": 2,

View file

@ -1,4 +1,9 @@
{
"recommendations": ["astro-build.astro-vscode", "esbenp.prettier-vscode", "editorconfig.editorconfig", "dbaeumer.vscode-eslint"],
"recommendations": [
"astro-build.astro-vscode",
"esbenp.prettier-vscode",
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
"unwantedRecommendations": []
}

View file

@ -10,10 +10,10 @@
},
"devDependencies": {
"@astrojs/preact": "^0.0.2",
"astro": "^0.25.4",
"sass": "^1.49.9"
"astro": "^0.26.0",
"sass": "^1.49.11"
},
"dependencies": {
"preact": "^10.6.6"
"preact": "^10.7.0"
}
}

View file

@ -10,7 +10,7 @@ export async function getStaticPaths({ paginate, rss }) {
const sortedPosts = allPosts.sort((a, b) => new Date(b.frontmatter.date).valueOf() - new Date(a.frontmatter.date).valueOf());
// Generate an RSS feed from this collection of posts.
// NOTE: This is disabled by default, since it requires `buildOptions.site` to be set in your "astro.config.mjs" file.
// NOTE: This is disabled by default, since it requires `site` to be set in your "astro.config.mjs" file.
// rss({
// title: 'Dons Blog',
// description: 'An example blog on Astro',

View file

@ -4,7 +4,4 @@ import preact from '@astrojs/preact';
// https://astro.build/config
export default defineConfig({
integrations: [preact()],
buildOptions: {
site: 'https://example.com/',
},
});

View file

@ -9,10 +9,10 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4",
"@astrojs/preact": "^0.0.2"
"@astrojs/preact": "^0.0.2",
"astro": "^0.26.0"
},
"dependencies": {
"preact": "^10.6.6"
"preact": "^10.7.0"
}
}

View file

@ -1,8 +1,10 @@
:root {
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
--font-body: 'IBM Plex Sans', var(--font-fallback);
--font-mono: 'IBM Plex Mono', 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-mono: 'IBM Plex Mono', 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;
--color-white: #fff;
--color-black: #000014;

View file

@ -10,6 +10,6 @@
},
"devDependencies": {
"@example/my-component": "workspace:*",
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -7,7 +7,7 @@ import * as Component from '@example/my-component';
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Welcome to Astro</title>
<style global>
<style is:global>
h {
display: block;
font-size: 2em;

View file

@ -3,11 +3,11 @@
"version": "0.0.1",
"private": true,
"scripts": {
"start": "astro --project-root demo dev",
"build": "astro --project-root demo build",
"serve": "astro --project-root demo preview"
"start": "astro --root demo dev",
"build": "astro --root demo build",
"serve": "astro --root demo preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -13,13 +13,13 @@
"@docsearch/css": "^3.0.0",
"@docsearch/react": "^3.0.0",
"@types/react": "^17.0.43",
"preact": "^10.6.6",
"preact": "^10.7.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@astrojs/preact": "^0.0.2",
"@astrojs/react": "^0.0.2",
"astro": "^0.25.4"
"@astrojs/react": "^0.1.0",
"astro": "^0.26.0"
}
}

View file

@ -6,8 +6,19 @@ import { KNOWN_LANGUAGES, langPathRegex } from '../../languages';
const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => {
return (
<div class="language-select-wrapper">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.2em" width="1.2em">
<path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" />
<svg
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 88.6 77.3"
height="1.2em"
width="1.2em"
>
<path
fill="currentColor"
d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z"
/>
<path
fill="currentColor"
d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8 c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z"

View file

@ -41,7 +41,13 @@ export default function Search() {
<>
<button type="button" ref={searchButtonRef} onClick={onOpen} className="search-input">
<svg width="24" height="24" fill="none">
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<span>Search</span>
<span className="search-hint">

View file

@ -15,9 +15,26 @@ const MenuToggle: FunctionalComponent = () => {
}, [sidebarShown]);
return (
<button type="button" aria-pressed={sidebarShown ? 'true' : 'false'} id="menu-toggle" onClick={() => setSidebarShown(!sidebarShown)}>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<button
type="button"
aria-pressed={sidebarShown ? 'true' : 'false'}
id="menu-toggle"
onClick={() => setSidebarShown(!sidebarShown)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<span className="sr-only">Toggle sidebar</span>
</button>

View file

@ -33,7 +33,11 @@ const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = []
{headers
.filter(({ depth }) => depth > 1 && depth < 4)
.map((header) => (
<li class={`header-link depth-${header.depth} ${activeId === header.slug ? 'active' : ''}`.trim()}>
<li
class={`header-link depth-${header.depth} ${
activeId === header.slug ? 'active' : ''
}`.trim()}
>
<a href={`#${header.slug}`}>{header.text}</a>
</li>
))}

View file

@ -6,14 +6,26 @@ import './ThemeToggleButton.css';
const themes = ['light', 'dark'];
const icons = [
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
</svg>,
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>,
];

View file

@ -7,7 +7,9 @@ export const SITE = {
export const OPEN_GRAPH = {
image: {
src: 'https://github.com/withastro/astro/blob/main/assets/social/banner.jpg?raw=true',
alt: 'astro logo on a starry expanse of space,' + ' with a purple saturn-like planet floating in the right foreground',
alt:
'astro logo on a starry expanse of space,' +
' with a purple saturn-like planet floating in the right foreground',
},
twitter: 'astrodotbuild',
};

View file

@ -1,8 +1,10 @@
:root {
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
--font-body: system-ui, var(--font-fallback);
--font-mono: 'IBM Plex Mono', 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-mono: 'IBM Plex Mono', 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;
/*
* Variables with --color-base prefix define

View file

@ -9,6 +9,6 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -9,6 +9,6 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -10,7 +10,7 @@
},
"devDependencies": {
"@astrojs/lit": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"@webcomponents/template-shadowroot": "^0.1.0",

View file

@ -11,20 +11,20 @@
"devDependencies": {
"@astrojs/lit": "^0.0.2",
"@astrojs/preact": "^0.0.2",
"@astrojs/react": "^0.0.2",
"@astrojs/react": "^0.1.0",
"@astrojs/solid-js": "^0.0.3",
"@astrojs/svelte": "^0.0.2",
"@astrojs/vue": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"@webcomponents/template-shadowroot": "^0.1.0",
"lit": "^2.2.1",
"preact": "^10.6.6",
"preact": "^10.7.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"solid-js": "^1.3.13",
"svelte": "^3.46.4",
"svelte": "^3.46.6",
"vue": "^3.2.31"
}
}

View file

@ -10,9 +10,9 @@
},
"devDependencies": {
"@astrojs/preact": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"preact": "^10.6.6"
"preact": "^10.7.0"
}
}

View file

@ -9,8 +9,8 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/react": "^0.0.2",
"astro": "^0.25.4"
"@astrojs/react": "^0.1.0",
"astro": "^0.26.0"
},
"dependencies": {
"react": "^18.0.0",

View file

@ -10,7 +10,7 @@
},
"devDependencies": {
"@astrojs/solid-js": "^0.0.3",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"solid-js": "^1.3.13"

View file

@ -10,9 +10,9 @@
},
"devDependencies": {
"@astrojs/svelte": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"svelte": "^3.46.4"
"svelte": "^3.46.6"
}
}

View file

@ -10,7 +10,7 @@
},
"devDependencies": {
"@astrojs/vue": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"vue": "^3.2.31"

View file

@ -10,21 +10,21 @@
},
"devDependencies": {
"@astrojs/lit": "^0.0.2",
"@astrojs/partytown": "^0.0.2",
"@astrojs/react": "^0.0.2",
"@astrojs/sitemap": "^0.0.2",
"@astrojs/tailwind": "^0.0.2",
"@astrojs/partytown": "^0.1.0",
"@astrojs/react": "^0.1.0",
"@astrojs/sitemap": "^0.1.0",
"@astrojs/tailwind": "^0.1.0",
"@astrojs/turbolinks": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"@webcomponents/template-shadowroot": "^0.1.0",
"lit": "^2.2.1",
"preact": "^10.6.6",
"preact": "^10.7.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"solid-js": "^1.3.13",
"svelte": "^3.46.4",
"svelte": "^3.46.6",
"vue": "^3.2.31"
}
}

View file

@ -9,6 +9,6 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -9,6 +9,6 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -10,10 +10,10 @@
},
"devDependencies": {
"@astrojs/preact": "^0.0.2",
"astro": "^0.25.4",
"sass": "^1.49.9"
"astro": "^0.26.0",
"sass": "^1.49.11"
},
"dependencies": {
"preact": "^10.6.6"
"preact": "^10.7.0"
}
}

View file

@ -24,7 +24,13 @@ function Nav() {
</svg>
</a>
<a className={Styles.social} href="https://dev.to/me">
<svg className={Styles.socialicon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40" style="enable-background:new 0 0 50 40" xmlSpace="preserve">
<svg
className={Styles.socialicon}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 50 40"
style="enable-background:new 0 0 50 40"
xmlSpace="preserve"
>
<path d="M15.7 15.5c-.4-.3-.7-.4-1.1-.4h-1.7v10.1h1.7c.4 0 .8-.1 1.1-.4.4-.3.6-.7.6-1.3v-6.7c0-.6-.2-1-.6-1.3z" />
<path d="M47 0H3C1.3 0 0 1.3 0 3v34c0 1.7 1.3 3 3 3h44c1.7 0 3-1.3 3-3V3c0-1.7-1.3-3-3-3zM19.1 23.5c0 1.3-.4 2.4-1.3 3.2-.8.9-1.9 1.3-3.3 1.3h-4.4V12.3h4.5c1.3 0 2.4.4 3.2 1.3.8.8 1.3 1.9 1.3 3.2v6.7zm9.1-8.4h-5.1v3.6h3.1v2.8h-3.1v3.7h5.1V28h-5.9c-.6 0-1-.2-1.4-.6-.4-.4-.6-.8-.6-1.4V14.2c0-.6.2-1 .6-1.4.4-.4.8-.6 1.4-.6h5.9v2.9zM37.5 26c-.6 1.3-1.3 2-2.2 2-.9 0-1.7-.7-2.2-2l-3.7-13.8h3.1L35.3 23l2.8-10.8h3.1L37.5 26z" />
</svg>

View file

@ -9,15 +9,15 @@
"server": "node server/server.mjs"
},
"devDependencies": {
"@astrojs/svelte": "^0.0.2",
"@astrojs/node": "^0.0.2",
"astro": "^0.25.4",
"concurrently": "^7.0.0",
"@astrojs/svelte": "^0.0.2",
"astro": "^0.26.0",
"concurrently": "^7.1.0",
"lightcookie": "^1.0.25",
"unocss": "^0.15.6",
"vite-imagetools": "^4.0.3"
},
"dependencies": {
"svelte": "^3.46.4"
"svelte": "^3.46.6"
}
}

View file

@ -21,7 +21,11 @@ function getOrigin(request: Request): string {
return new URL(request.url).origin.replace('localhost', '127.0.0.1');
}
async function get<T>(incomingReq: Request, endpoint: string, cb: (response: Response) => Promise<T>): Promise<T> {
async function get<T>(
incomingReq: Request,
endpoint: string,
cb: (response: Response) => Promise<T>
): Promise<T> {
const response = await fetch(`${getOrigin(incomingReq)}${endpoint}`, {
credentials: 'same-origin',
});

View file

@ -9,6 +9,6 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -4,7 +4,8 @@
}
:root {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
font-size: 1rem;
--user-font-scale: 1rem - 16px;
font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);

View file

@ -1,6 +1,7 @@
:root {
--font-mono: 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-mono: 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;
--color-light: #f3f4f6;
}

View file

@ -4,7 +4,5 @@ import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
integrations: [react()],
buildOptions: {
site: 'http://example.com/blog',
},
site: 'http://example.com/blog',
});

View file

@ -9,9 +9,9 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/react": "^0.0.2",
"astro": "^0.25.4",
"sass": "^1.49.9"
"@astrojs/react": "^0.1.0",
"astro": "^0.26.0",
"sass": "^1.49.11"
},
"dependencies": {
"react": "^18.0.0",

View file

@ -1,17 +1,16 @@
import { defineConfig } from 'astro/config';
import astroRemark from '@astrojs/markdown-remark';
import addClasses from './add-classes.mjs';
// https://astro.build/config
export default defineConfig({
// Enable Custom Markdown options, plugins, etc.
markdownOptions: {
render: [
astroRemark,
{
remarkPlugins: ['remark-code-titles'],
rehypePlugins: [['rehype-autolink-headings', { behavior: 'prepend' }], ['rehype-toc', { headings: ['h2', 'h3'] }], [addClasses, { 'h1,h2,h3': 'title' }], 'rehype-slug'],
},
markdown: {
remarkPlugins: ['remark-code-titles'],
rehypePlugins: [
['rehype-autolink-headings', { behavior: 'prepend' }],
['rehype-toc', { headings: ['h2', 'h3'] }],
[addClasses, { 'h1,h2,h3': 'title' }],
'rehype-slug',
],
},
});

View file

@ -9,8 +9,8 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/markdown-remark": "^0.7.0",
"astro": "^0.25.4",
"@astrojs/markdown-remark": "^0.8.0",
"astro": "^0.26.0",
"hast-util-select": "5.0.1",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",

View file

@ -1,20 +1,14 @@
import { defineConfig } from 'astro/config';
import astroRemark from '@astrojs/markdown-remark';
// https://astro.build/config
export default defineConfig({
// Enable Custom Markdown options, plugins, etc.
markdownOptions: {
render: [
astroRemark,
{
syntaxHighlight: 'shiki',
shikiConfig: {
theme: 'dracula',
// Learn more about this configuration here:
// https://docs.astro.build/en/guides/markdown-content/#syntax-highlighting
},
},
],
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: {
theme: 'dracula',
// Learn more about this configuration here:
// https://docs.astro.build/en/guides/markdown-content/#syntax-highlighting
},
},
});

View file

@ -9,7 +9,7 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/markdown-remark": "^0.7.0",
"astro": "^0.25.4"
"@astrojs/markdown-remark": "^0.8.0",
"astro": "^0.26.0"
}
}

View file

@ -9,18 +9,18 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/markdown-remark": "^0.7.0",
"@astrojs/markdown-remark": "^0.8.0",
"@astrojs/preact": "^0.0.2",
"@astrojs/react": "^0.0.2",
"@astrojs/react": "^0.1.0",
"@astrojs/svelte": "^0.0.2",
"@astrojs/vue": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
},
"dependencies": {
"preact": "^10.6.6",
"preact": "^10.7.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"svelte": "^3.46.4",
"svelte": "^3.46.6",
"vue": "^3.2.31"
}
}

View file

@ -13,7 +13,7 @@
"@nanostores/react": "^0.1.5",
"@nanostores/vue": "^0.4.1",
"nanostores": "^0.5.12",
"preact": "^10.6.6",
"preact": "^10.7.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"solid-nanostores": "0.0.6",
@ -21,10 +21,10 @@
},
"devDependencies": {
"@astrojs/preact": "^0.0.2",
"@astrojs/react": "^0.0.2",
"@astrojs/react": "^0.1.0",
"@astrojs/solid-js": "^0.0.3",
"@astrojs/svelte": "^0.0.2",
"@astrojs/vue": "^0.0.2",
"astro": "^0.25.4"
"astro": "^0.26.0"
}
}

View file

@ -4,7 +4,8 @@
}
:root {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
font-size: 1rem;
--user-font-scale: 1rem - 16px;
font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);

View file

@ -1,6 +1,7 @@
:root {
--font-mono: 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-mono: 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;
--color-light: #f3f4f6;
}

View file

@ -9,8 +9,8 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/tailwind": "^0.0.2",
"astro": "^0.25.4",
"@astrojs/tailwind": "^0.1.0",
"astro": "^0.26.0",
"autoprefixer": "^10.4.4",
"canvas-confetti": "^1.5.1",
"postcss": "^8.4.12",

View file

@ -5,7 +5,8 @@
<button class="py-2 px-4 bg-purple-500 text-white font-semibold rounded-lg shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75">
<slot />
</button>
<script hoist>
<script>
import confetti from 'canvas-confetti';
document.body.querySelector('button').addEventListener("click", () => confetti());
</script>
</script>

View file

@ -9,7 +9,7 @@
"preview": "astro preview"
},
"devDependencies": {
"astro": "^0.25.4",
"astro": "^0.26.0",
"vite-plugin-pwa": "0.11.11",
"workbox-window": "^6.5.2"
}

View file

@ -66,7 +66,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"execa": "^6.1.0",
"prettier": "^2.6.1",
"prettier": "^2.6.2",
"pretty-bytes": "^6.0.0",
"tiny-glob": "^0.2.9",
"turbo": "^1.1.10",

View file

@ -8,7 +8,9 @@ export function addAstro(Prism) {
scriptLang = 'typescript';
} else {
scriptLang = 'javascript';
console.warn('Prism TypeScript language not loaded, Astro scripts will be treated as JavaScript.');
console.warn(
'Prism TypeScript language not loaded, Astro scripts will be treated as JavaScript.'
);
}
let script = Prism.util.clone(Prism.languages[scriptLang]);
@ -38,10 +40,14 @@ export function addAstro(Prism) {
spread = re(spread).source;
Prism.languages.astro = Prism.languages.extend('markup', script);
Prism.languages.astro.tag.pattern = re(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source);
Prism.languages.astro.tag.pattern = re(
/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/
.source
);
Prism.languages.astro.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
Prism.languages.astro.tag.inside['attr-value'].pattern = /=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
Prism.languages.astro.tag.inside['attr-value'].pattern =
/=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
Prism.languages.astro.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
Prism.languages.astro.tag.inside['comment'] = script['comment'];
@ -109,7 +115,11 @@ export function addAstro(Prism) {
if (token.content[0].content[0].content === '</') {
// Closing tag
if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
if (
openedTags.length > 0 &&
openedTags[openedTags.length - 1].tagName ===
stringifyToken(token.content[0].content[1])
) {
// Pop matching opening tag
openedTags.pop();
}
@ -127,7 +137,12 @@ export function addAstro(Prism) {
} else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {
// Here we might have entered a Astro context inside a tag
openedTags[openedTags.length - 1].openedBraces++;
} else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
} else if (
openedTags.length > 0 &&
openedTags[openedTags.length - 1].openedBraces > 0 &&
token.type === 'punctuation' &&
token.content === '}'
) {
// Here we might have left a Astro context inside a tag
openedTags[openedTags.length - 1].openedBraces--;
} else {
@ -141,7 +156,10 @@ export function addAstro(Prism) {
let plainText = stringifyToken(token);
// And merge text with adjacent text
if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
if (
i < tokens.length - 1 &&
(typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')
) {
plainText += stringifyToken(tokens[i + 1]);
tokens.splice(i + 1, 1);
}

View file

@ -1,5 +1,51 @@
# astro
## 0.26.0
### Minor Changes
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Implement RFC [#0017](https://github.com/withastro/rfcs/blob/main/proposals/0017-markdown-content-redesign.md)
- New Markdown API
- New `Astro.glob()` API
- **BREAKING CHANGE:** Removed `Astro.fetchContent()` (replaced by `Astro.glob()`)
```diff
// v0.25
- let allPosts = Astro.fetchContent('./posts/*.md');
// v0.26+
+ let allPosts = await Astro.glob('./posts/*.md');
```
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Implement [RFC0016](https://github.com/withastro/rfcs/blob/main/proposals/0016-style-script-defaults.md) which changes the default behavior of `script`, introduces `is:inline`, and changes `<style global>` to `<style is:global>`
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Implements the Astro.request RFC
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Update config options to resepect [RFC0019](https://github.com/withastro/rfcs/blob/main/proposals/0019-config-finalization.md)
### Patch Changes
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Allow components to return a Response
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - `--experimental-ssr` now is only required when using a 3rd-party adapter
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Improve `Astro.slots` API to support passing arguments to function-based slots.
This allows for more ergonomic utility components that accept a callback function as a child.
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Fixes non-GET API routes in dev with Node 14
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Update CLI error format and style
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Update `@astrojs/compiler`, fixing some bugs related to RegExp usage in frontmatter
- [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Add a Deno adapter for SSR
* [`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Fix for copying public when using SSR and not client JS
* Updated dependencies [[`e425f896`](https://github.com/withastro/astro/commit/e425f896b668d98033ad3b998b50c1f28bc7f6ee)]:
- @astrojs/markdown-remark@0.8.0
## 0.25.4
### Patch Changes

View file

@ -9,7 +9,8 @@
const CI_INSTRUCTIONS = {
NETLIFY: 'https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript',
GITHUB_ACTIONS: 'https://docs.github.com/en/actions/guides/building-and-testing-nodejs#specifying-the-nodejs-version',
GITHUB_ACTIONS:
'https://docs.github.com/en/actions/guides/building-and-testing-nodejs#specifying-the-nodejs-version',
VERCEL: 'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
};
@ -17,7 +18,10 @@ const CI_INSTRUCTIONS = {
async function main() {
// Check for ESM support.
// Load the "supports-esm" package in an way that works in both ESM & CJS.
let supportsESM = typeof require !== 'undefined' ? require('supports-esm') : (await import('supports-esm')).default;
let supportsESM =
typeof require !== 'undefined'
? require('supports-esm')
: (await import('supports-esm')).default;
// Check for CJS->ESM named export support.
// "path-to-regexp" is a real-world package that we depend on, that only
@ -79,7 +83,9 @@ Please upgrade Node.js to a supported version: "${engines}"\n`);
break;
}
}
console.log(`${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`);
console.log(
`${ci.name} CI Environment Detected!\nAdditional steps may be needed to set your Node.js version:`
);
console.log(`Documentation: https://docs.astro.build/guides/deploy`);
if (CI_INSTRUCTIONS[platform]) {
console.log(`${ci.name} Documentation: ${CI_INSTRUCTIONS[platform]}`);

View file

@ -1,6 +1,6 @@
{
"name": "astro",
"version": "0.25.4",
"version": "0.26.0",
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
"type": "module",
"author": "withastro",
@ -73,9 +73,9 @@
"test:match": "mocha --timeout 20000 -g"
},
"dependencies": {
"@astrojs/compiler": "^0.13.1",
"@astrojs/language-server": "^0.13.2",
"@astrojs/markdown-remark": "^0.7.0",
"@astrojs/compiler": "^0.14.1",
"@astrojs/language-server": "^0.13.3",
"@astrojs/markdown-remark": "^0.8.0",
"@astrojs/prism": "0.4.1",
"@astrojs/webapi": "^0.11.0",
"@babel/core": "^7.17.8",
@ -130,7 +130,7 @@
"strip-ansi": "^7.0.1",
"supports-esm": "^1.0.0",
"tsconfig-resolver": "^3.0.1",
"vite": "^2.8.6",
"vite": "^2.9.1",
"yargs-parser": "^21.0.1",
"zod": "^3.14.3"
},
@ -159,7 +159,7 @@
"chai": "^4.3.6",
"cheerio": "^1.0.0-rc.10",
"mocha": "^9.2.2",
"sass": "^1.49.9"
"sass": "^1.49.11"
},
"engines": {
"node": "^14.15.0 || >=16.0.0",

View file

@ -2,6 +2,7 @@ import type { AddressInfo } from 'net';
import type * as babel from '@babel/core';
import type * as vite from 'vite';
import { z } from 'zod';
import type { ShikiConfig, Plugin } from '@astrojs/markdown-remark';
import type { AstroConfigSchema } from '../core/config';
import type { AstroComponentFactory, Metadata } from '../runtime/server';
import type { ViteConfigWithSSR } from '../core/create-vite';
@ -24,18 +25,13 @@ export interface AstroComponentMetadata {
/** The flags supported by the Astro CLI */
export interface CLIFlags {
projectRoot?: string;
root?: string;
site?: string;
sitemap?: boolean;
host?: string | boolean;
hostname?: string;
port?: number;
config?: string;
/** @deprecated */
experimentalStaticBuild?: boolean;
experimentalSsr?: boolean;
experimentalIntegrations?: boolean;
legacyBuild?: boolean;
drafts?: boolean;
}
@ -60,7 +56,10 @@ export interface AstroGlobal extends AstroGlobalPartial {
/** get information about this page */
request: Request;
/** see if slots are used */
slots: Record<string, true | undefined> & { has(slotName: string): boolean; render(slotName: string, args?: any[]): Promise<string> };
slots: Record<string, true | undefined> & {
has(slotName: string): boolean;
render(slotName: string, args?: any[]): Promise<string>;
};
}
export interface AstroGlobalPartial {
@ -76,6 +75,32 @@ export interface AstroGlobalPartial {
site: URL;
}
type ServerConfig = {
/**
* @name server.host
* @type {string | boolean}
* @default `false`
* @version 0.24.0
* @description
* Set which network IP addresses the dev server should listen on (i.e. non-localhost IPs).
* - `false` - do not expose on a network IP address
* - `true` - listen on all addresses, including LAN and public addresses
* - `[custom-address]` - expose on a network IP address at `[custom-address]`
*/
host?: string | boolean;
/**
* @name server.port
* @type {number}
* @default `3000`
* @description
* Set which port the dev server should listen on.
*
* If the given port is already in use, Astro will automatically try the next available port.
*/
port?: number;
};
/**
* Astro User Config
* Docs: https://docs.astro.build/reference/configuration-reference/
@ -89,50 +114,50 @@ export interface AstroUserConfig {
/**
* @docs
* @name projectRoot
* @cli --project-root
* @name root
* @cli --root
* @type {string}
* @default `"."` (current working directory)
* @summary Set the project root. The project root is the directory where your Astro project (and all `src`, `public` and `package.json` files) live.
* @description You should only provide this option if you run the `astro` CLI commands in a directory other than the project root directory. Usually, this option is provided via the CLI instead of the `astro.config.js` file, since Astro needs to know your project root before it can locate your config file.
*
* If you provide a relative path (ex: `--project-root: './my-project'`) Astro will resolve it against your current working directory.
* If you provide a relative path (ex: `--root: './my-project'`) Astro will resolve it against your current working directory.
*
* #### Examples
*
* ```js
* {
* projectRoot: './my-project-directory'
* root: './my-project-directory'
* }
* ```
* ```bash
* $ astro build --project-root ./my-project-directory
* $ astro build --root ./my-project-directory
* ```
*/
projectRoot?: string;
root?: string;
/**
* @docs
* @name dist
* @name srcDir
* @type {string}
* @default `"./dist"`
* @description Set the directory that `astro build` writes your final build to.
* @default `"./src"`
* @description Set the directory that Astro will read your site from.
*
* The value can be either an absolute file system path or a path relative to the project root.
*
* ```js
* {
* dist: './my-custom-build-directory'
* srcDir: './www'
* }
* ```
*/
dist?: string;
srcDir?: string;
/**
* @docs
* @name public
* @name publicDir
* @type {string}
* @default `"./public"`
* @default `"./publicDir"`
* @description
* Set the directory for your static assets. Files in this directory are served at `/` during dev and copied to your build directory during build. These files are always served or copied as-is, without transform or bundling.
*
@ -140,36 +165,260 @@ export interface AstroUserConfig {
*
* ```js
* {
* public: './my-custom-public-directory'
* publicDir: './my-custom-publicDir-directory'
* }
* ```
*/
public?: string;
publicDir?: string;
/**
* @docs
* @name integrations
* @type {AstroIntegration[]}
* @default `[]`
* @description
* Add Integrations to your project to extend Astro.
* @name outDir
* @type {string}
* @default `"./outDir"`
* @description Set the directory that `astro build` writes your final build to.
*
* Integrations are your one-stop shop to add new frameworks (like Solid.js), new features (like sitemaps), and new libraries (like Partytown and Turbolinks).
*
* Setting this configuration will disable Astro's default integration, so it is recommended to provide a renderer for every framework that you use:
*
* Note: Integrations are currently under active development, and only first-party integrations are supported. In the future, 3rd-party integrations will be allowed.
* The value can be either an absolute file system path or a path relative to the project root.
*
* ```js
* import react from '@astrojs/react';
* import vue from '@astrojs/vue';
* {
* // Example: Use Astro with Vue + React, and no other frameworks.
* integrations: [react(), vue()]
* outDir: './my-custom-build-directory'
* }
* ```
*/
integrations?: Array<AstroIntegration | AstroIntegration[]>;
outDir?: string;
/**
* @docs
* @name site
* @type {string}
* @description
* Your final, deployed URL. Astro uses this full URL to generate your sitemap and canonical URLs in your final build. It is strongly recommended that you set this configuration to get the most out of Astro.
*
* ```js
* {
* site: 'https://www.my-site.dev'
* }
* ```
*/
site?: string;
/**
* @docs
* @name base
* @type {string}
* @description
* The base path you're deploying to. Astro will match this pathname during development so that your development experience matches your build environment as closely as possible. In the example below, `astro dev` will start your server at `/docs`.
*
* ```js
* {
* base: '/docs'
* }
* ```
*/
base?: string;
/**
* @docs
* @name trailingSlash
* @type {('always' | 'never' | 'ignore')}
* @default `'always'`
* @see buildOptions.pageUrlFormat
* @description
*
* Set the route matching behavior of the dev server. Choose from the following options:
* - `'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
*
* Use this configuration option if your production host has strict handling of how trailing slashes work or do not work.
*
* You can also set this if you prefer to be more strict yourself, so that URLs with or without trailing slashes won't work during development.
*
* ```js
* {
* // Example: Require a trailing slash during development
* trailingSlash: 'always'
* }
* ```
*/
trailingSlash?: 'always' | 'never' | 'ignore';
/**
* @docs
* @kind heading
* @name Build Options
*/
build?: {
/**
* @docs
* @name build.format
* @typeraw {('file' | 'directory')}
* @default `'directory'`
* @description
* Control the output file format of each page.
* - If 'file', Astro will generate an HTML file (ex: "/foo.html") for each page.
* - If 'directory', Astro will generate a directory with a nested `index.html` file (ex: "/foo/index.html") for each page.
*
* ```js
* {
* build: {
* // Example: Generate `page.html` instead of `page/index.html` during build.
* format: 'file'
* }
* }
* ```
*/
format?: 'file' | 'directory';
};
/**
* @docs
* @kind heading
* @name Server Options
* @description
*
* Customize the Astro dev server, used by both `astro dev` and `astro serve`.
*
* ```js
* {
* server: {port: 1234, host: true}
* }
* ```
*
* To set different configuration based on the command run ("dev", "preview") a function can also be passed to this configuration option.
*
* ```js
* {
* // Example: Use the function syntax to customize based on command
* server: (command) => ({port: command === 'dev' ? 3000 : 4000})
* }
* ```
*/
/**
* @docs
* @name server.host
* @type {string | boolean}
* @default `false`
* @version 0.24.0
* @description
* Set which network IP addresses the dev server should listen on (i.e. non-localhost IPs).
* - `false` - do not expose on a network IP address
* - `true` - listen on all addresses, including LAN and public addresses
* - `[custom-address]` - expose on a network IP address at `[custom-address]`
*/
/**
* @docs
* @name server.port
* @type {number}
* @default `3000`
* @description
* Set which port the dev server should listen on.
*
* If the given port is already in use, Astro will automatically try the next available port.
*/
server?: ServerConfig | ((options: { command: 'dev' | 'preview' }) => ServerConfig);
/**
* @docs
* @kind heading
* @name Markdown Options
*/
markdown?: {
/**
* @docs
* @name markdown.drafts
* @type {boolean}
* @default `false`
* @description
* Control if markdown draft pages should be included in the build.
*
* A markdown page is considered a draft if it includes `draft: true` in its front matter. Draft pages are always included & visible during development (`astro dev`) but by default they will not be included in your final build.
*
* ```js
* {
* markdown: {
* // Example: Include all drafts in your final build
* drafts: true,
* }
* }
* ```
*/
drafts?: boolean;
/**
* @docs
* @name markdown.shikiConfig
* @type {ShikiConfig}
* @description
* Shiki configuration options. See [the markdown configuration docs](https://docs.astro.build/en/guides/markdown-content/#shiki-configuration) for usage.
*/
shikiConfig?: ShikiConfig;
/**
* @docs
* @name markdown.syntaxHighlight
* @type {'shiki' | 'prism' | false}
* @default `shiki`
* @description
* Which syntax highlighter to use, if any.
* - `shiki` - use the [Shiki](https://github.com/shikijs/shiki) highlighter
* - `prism` - use the [Prism](https://prismjs.com/) highlighter
* - `false` - do not apply syntax highlighting.
*
* ```js
* {
* markdown: {
* // Example: Switch to use prism for syntax highlighting in Markdown
* syntaxHighlight: 'prism',
* }
* }
* ```
*/
syntaxHighlight?: 'shiki' | 'prism' | false;
/**
* @docs
* @name markdown.remarkPlugins
* @type {Plugin[]}
* @description
* Pass a custom [Remark](https://github.com/remarkjs/remark) plugin to customize how your Markdown is built.
*
* **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired.
*
* ```js
* {
* markdown: {
* // Example: The default set of remark plugins used by Astro
* remarkPlugins: ['remark-code-titles', ['rehype-autolink-headings', { behavior: 'prepend' }]],
* },
* };
* ```
*/
remarkPlugins?: Plugin[];
/**
* @docs
* @name markdown.rehypePlugins
* @type {Plugin[]}
* @description
* Pass a custom [Rehype](https://github.com/remarkjs/remark-rehype) plugin to customize how your Markdown is built.
*
* **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired.
*
* ```js
* {
* markdown: {
* // Example: The default set of rehype plugins used by Astro
* rehypePlugins: [['rehype-toc', { headings: ['h2', 'h3'] }], [addClasses, { 'h1,h2,h3': 'title' }], 'rehype-slug'],
* },
* };
* ```
*/
rehypePlugins?: Plugin[];
};
/**
* @name adapter
@ -180,262 +429,31 @@ export interface AstroUserConfig {
*/
adapter?: AstroIntegration;
/** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
renderers?: string[];
/**
* @docs
* @name markdownOptions
* @type {{render: MarkdownRenderOptions}}
* @see [Markdown guide](/en/guides/markdown-content/)
* @kind heading
* @name Integrations
* @description
* Configure how markdown files (`.md`) are rendered.
*
* Extend Astro with custom integrations. Integrations are your one-stop-shop for adding framework support (like Solid.js), new features (like sitemaps), and new libraries (like Partytown and Turbolinks).
*
* Read our [Integrations Guide](/en/guides/integrations-guide/) for help getting started with Astro Integrations.
*
* ```js
* import { defineConfig } from "astro/config";
* import astroRemark from "@astrojs/markdown-remark";
* import customRehypePlugin from "/path/to/rehypePlugin.mjs";
*
* export default defineConfig({
* // Enable Custom Markdown options, plugins, etc.
* markdownOptions: {
* render: [
* // The Remark parser to parse Markdown content
* astroRemark,
* {
* // Add a Remark plugin to your project.
* remarkPlugins: ["remark-code-titles"],
*
* // Add a Rehype plugin to your project.
* rehypePlugins: [
* "rehype-slug",
* [customRehypePlugin, { configKey: "value" }],
* ["rehype-autolink-headings", { behavior: "prepend" }],
* ],
* },
* ],
* },
* });
* import react from '@astrojs/react';
* import tailwind from '@astrojs/tailwind';
* {
* // Example: Add React + Tailwind support to Astro
* integrations: [react(), tailwind()]
* }
* ```
*/
markdownOptions?: {
render?: MarkdownRenderOptions;
};
integrations?: Array<AstroIntegration | AstroIntegration[]>;
/**
* @docs
* @kind heading
* @name Build Options
*/
buildOptions?: {
/**
* @docs
* @name buildOptions.site
* @type {string}
* @description
* Your final, deployed URL. Astro uses this full URL to generate your sitemap and canonical URLs in your final build. It is strongly recommended that you set this configuration to get the most out of Astro.
*
* Astro will match the site pathname during development so that your development experience matches your build environment as closely as possible. In the example below, `astro dev` will start your server at `http://localhost:3000/docs`.
*
* ```js
* {
* buildOptions: {
* // Example: Tell Astro the final URL of your deployed website.
* site: 'https://www.my-site.dev/docs'
* }
* }
* ```
*/
site?: string;
/**
* @docs
* @name buildOptions.sitemap
* @type {boolean}
* @default `true`
* @description
* Generate a sitemap for your build. Set to false to disable.
*
* Astro will automatically generate a sitemap including all generated pages on your site. If you need more control over your sitemap, consider generating it yourself using a [Non-HTML Page](/en/core-concepts/astro-pages/#non-html-pages).
*
* ```js
* {
* buildOptions: {
* // Example: Disable automatic sitemap generation
* sitemap: false
* }
* }
* ```
*/
sitemap?: boolean;
/**
* @docs
* @name buildOptions.sitemapFilter
* @typeraw {(page: string) => boolean}
* @see buildOptions.sitemap
* @description
* By default, all pages are included in your generated sitemap.
* You can filter included pages by URL using `buildOptions.sitemapFilter`.
*
* The `page` function parameter is the full URL of your rendered page, including your `buildOptions.site` domain.
* Return `true` to include a page in your sitemap, and `false` to remove it.
*
* ```js
* {
* buildOptions: {
* sitemap: true
* sitemapFilter: (page) => page !== 'http://example.com/secret-page')
* }
* }
* ```
*/
sitemapFilter?: (page: string) => boolean;
/**
* @docs
* @name buildOptions.pageUrlFormat
* @type {('file' | 'directory')}
* @default `'directory'`
* @description
* Control the output file format of each page.
* - If 'file', Astro will generate an HTML file (ex: "/foo.html") for each page.
* - If 'directory', Astro will generate a directory with a nested `index.html` file (ex: "/foo/index.html") for each page.
*
* ```js
* {
* buildOptions: {
* // Example: Generate `page.html` instead of `page/index.html` during build.
* pageUrlFormat: 'file'
* }
* }
* ```
*/
pageUrlFormat?: 'file' | 'directory';
/**
* @docs
* @name buildOptions.drafts
* @type {boolean}
* @default `false`
* @description
* Control if markdown draft pages should be included in the build.
*
* A markdown page is considered a draft if it includes `draft: true` in its front matter. Draft pages are always included & visible during development (`astro dev`) but by default they will not be included in your final build.
*
* ```js
* {
* buildOptions: {
* // Example: Include all drafts in your final build
* drafts: true,
* }
* }
* ```
*/
drafts?: boolean;
/**
* Enables "legacy build mode" for compatibility with older Astro versions.
* Default: false
*/
legacyBuild?: boolean;
/**
* @deprecated
* Experimental: Enables "static build mode" for faster builds.
* Default: true
*/
experimentalStaticBuild?: boolean;
/**
* Enable SSR support for 3rd-party adapters.
* Not required when using a built-in adapter.
* Default: false
*/
experimentalSsr?: boolean;
};
/**
* @docs
* @kind heading
* @name Dev Options
*/
devOptions?: {
/**
* @docs
* @name devOptions.host
* @type {string | boolean}
* @default `false`
* @version 0.24.0
* @description
* Set which network IP addresses the dev server should listen on (i.e. non-localhost IPs).
* - `false` - do not expose on a network IP address
* - `true` - listen on all addresses, including LAN and public addresses
* - `[custom-address]` - expose on a network IP address at `[custom-address]`
*/
host?: string | boolean;
/**
* @docs
* @name devOptions.hostname
* @type {string}
* @default `'localhost'`
* @deprecated Use `host` instead
* @description
* > **This option is deprecated.** Consider using `host` instead.
*
* Set which IP addresses the dev server should listen on. Set this to 0.0.0.0 to listen on all addresses, including LAN and public addresses.
*/
hostname?: string;
/**
* @docs
* @name devOptions.port
* @type {number}
* @default `3000`
* @description
* Set which port the dev server should listen on.
*
* If the given port is already in use, Astro will automatically try the next available port.
*/
port?: number;
/**
* @docs
* @name devOptions.trailingSlash
* @type {('always' | 'never' | 'ignore')}
* @default `'always'`
* @see buildOptions.pageUrlFormat
* @description
*
* Set the route matching behavior of the dev server. Choose from the following options:
* - '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
*
* Use this configuration option if your production host has strict handling of how trailing slashes work or do not work.
*
* You can also set this if you prefer to be more strict yourself, so that URLs with or without trailing slashes won't work during development.
*
* ```js
* {
* devOptions: {
* // Example: Require a trailing slash during development
* trailingSlash: 'always'
* }
* }
* ```
*/
trailingSlash?: 'always' | 'never' | 'ignore';
};
/**
* Enable experimental support for 3rd-party integrations.
* Default: false
*/
experimentalIntegrations?: boolean;
/**
* @docs
* @name vite
* @type {vite.UserConfig}
* @name Vite
* @description
*
* Pass additional configuration options to Vite. Useful when Astro doesn't support some advanced configuration that you may need.
@ -447,9 +465,9 @@ export interface AstroUserConfig {
* ```js
* {
* vite: {
* ssr: {
* // Example: Force a broken package to skip SSR processing, if needed
* external: ['broken-npm-package'],
* ssr: {
* // Example: Force a broken package to skip SSR processing, if needed
* external: ['broken-npm-package'],
* }
* }
* }
@ -459,12 +477,51 @@ export interface AstroUserConfig {
* {
* vite: {
* // Example: Add custom vite plugins directly to your Astro project
* plugins: [myPlugin()],
* plugins: [myPlugin()],
* }
* }
* ```
*/
vite?: vite.UserConfig & { ssr?: vite.SSROptions };
experimental?: {
/**
* Enable experimental support for 3rd-party integrations.
* Default: false
*/
integrations?: boolean;
/**
* Enable a build for SSR support.
* Default: false
*/
ssr?: boolean;
};
// Legacy options to be removed
/** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
renderers?: never;
/** @deprecated `projectRoot` has been renamed to `root` */
projectRoot?: never;
/** @deprecated `src` has been renamed to `srcDir` */
src?: never;
/** @deprecated `pages` has been removed. It is no longer configurable. */
pages?: never;
/** @deprecated `public` has been renamed to `publicDir` */
public?: never;
/** @deprecated `dist` has been renamed to `outDir` */
dist?: never;
/** @deprecated `styleOptions` has been renamed to `style` */
styleOptions?: never;
/** @deprecated `markdownOptions` has been renamed to `markdown` */
markdownOptions?: never;
/** @deprecated `buildOptions` has been renamed to `build` */
buildOptions?: never;
/** @deprecated `devOptions` has been renamed to `server` */
devOptions?: never;
/** @deprecated `experimentalIntegrations` has been renamed to `experimental: { integrations: true }` */
experimentalIntegrations?: never;
}
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
@ -506,7 +563,12 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
};
}
export type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>;
export type AsyncRendererComponentFn<U> = (
Component: any,
props: any,
children: string | undefined,
metadata?: AstroComponentMetadata
) => Promise<U>;
/** Generic interface for a component (Astro, Svelte, React, etc.) */
export interface ComponentInstance {
@ -524,7 +586,9 @@ export interface MarkdownInstance<T extends Record<string, any>> {
getHeaders(): Promise<{ depth: number; slug: string; text: string }[]>;
}
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
export type GetHydrateCallback = () => Promise<
(element: Element, innerHTML: string | null) => void
>;
/**
* getStaticPaths() options
@ -552,14 +616,20 @@ export interface JSXTransformConfig {
plugins?: babel.PluginItem[];
}
export type JSXTransformFn = (options: { mode: string; ssr: boolean }) => Promise<JSXTransformConfig>;
export type JSXTransformFn = (options: {
mode: string;
ssr: boolean;
}) => Promise<JSXTransformConfig>;
export interface ManifestData {
routes: RouteData[];
}
export type MarkdownRenderOptions = [string | MarkdownParser, Record<string, any>];
export type MarkdownParser = (contents: string, options?: Record<string, any>) => MarkdownParserResponse | PromiseLike<MarkdownParserResponse>;
export type MarkdownParser = (
contents: string,
options?: Record<string, any>
) => MarkdownParserResponse | PromiseLike<MarkdownParserResponse>;
export interface MarkdownParserResponse {
frontmatter: {
@ -677,13 +747,23 @@ export interface AstroIntegration {
// more generalized. Consider the SSR use-case as well.
// injectElement: (stage: vite.HtmlTagDescriptor, element: string) => void;
}) => void;
'astro:config:done'?: (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void }) => void | Promise<void>;
'astro:config:done'?: (options: {
config: AstroConfig;
setAdapter: (adapter: AstroAdapter) => void;
}) => void | Promise<void>;
'astro:server:setup'?: (options: { server: vite.ViteDevServer }) => void | Promise<void>;
'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise<void>;
'astro:server:done'?: () => void | Promise<void>;
'astro:build:start'?: (options: { buildConfig: BuildConfig }) => void | Promise<void>;
'astro:build:setup'?: (options: { vite: ViteConfigWithSSR; target: 'client' | 'server' }) => void;
'astro:build:done'?: (options: { pages: { pathname: string }[]; dir: URL; routes: RouteData[] }) => void | Promise<void>;
'astro:build:setup'?: (options: {
vite: ViteConfigWithSSR;
target: 'client' | 'server';
}) => void;
'astro:build:done'?: (options: {
pages: { pathname: string }[];
dir: URL;
routes: RouteData[];
}) => void | Promise<void>;
};
}
@ -767,7 +847,11 @@ export interface SSRResult {
styles: Set<SSRElement>;
scripts: Set<SSRElement>;
links: Set<SSRElement>;
createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
createAstro(
Astro: AstroGlobalPartial,
props: Record<string, any>,
slots: Record<string, any> | null
): AstroGlobal;
resolve: (s: string) => Promise<string>;
_metadata: SSRMetadata;
}

View file

@ -8,7 +8,11 @@ import * as path from 'path';
import { pathToFileURL } from 'url';
import * as fs from 'fs';
async function openAllDocuments(workspaceUri: URL, filePathsToIgnore: string[], checker: AstroCheck) {
async function openAllDocuments(
workspaceUri: URL,
filePathsToIgnore: string[],
checker: AstroCheck
) {
const files = await glob('**/*.astro', {
cwd: workspaceUri.pathname,
ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)),
@ -64,7 +68,7 @@ function generateString(str: string, len: number) {
export async function run() {}
export async function check(astroConfig: AstroConfig) {
const root = astroConfig.projectRoot;
const root = astroConfig.root;
let checker = new AstroCheck(root.toString());
await openAllDocuments(root, [], checker);
@ -79,7 +83,11 @@ export async function check(astroConfig: AstroConfig) {
diag.diagnostics.forEach((d) => {
switch (d.severity) {
case DiagnosticSeverity.Error: {
console.error(`${bold(cyan(path.relative(root.pathname, diag.filePath)))}:${bold(yellow(d.range.start.line))}:${bold(yellow(d.range.start.character))} - ${d.message}`);
console.error(
`${bold(cyan(path.relative(root.pathname, diag.filePath)))}:${bold(
yellow(d.range.start.line)
)}:${bold(yellow(d.range.start.character))} - ${d.message}`
);
let startOffset = offsetAt({ line: d.range.start.line, character: 0 }, diag.text);
let endOffset = offsetAt({ line: d.range.start.line + 1, character: 0 }, diag.text);
let str = diag.text.substring(startOffset, endOffset - 1);

View file

@ -36,8 +36,7 @@ function printAstroHelp() {
flags: [
['--host [optional IP]', 'Expose server on network'],
['--config <path>', 'Specify the path to the Astro config file.'],
['--project-root <path>', 'Specify the path to the project root folder.'],
['--no-sitemap', 'Disable sitemap generation (build only).'],
['--root <path>', 'Specify the path to the project root folder.'],
['--legacy-build', 'Use the build strategy prior to 0.24.0'],
['--experimental-ssr', 'Enable SSR compilation fot 3rd-party adapters.'],
['--drafts', 'Include markdown draft pages in the build.'],
@ -74,7 +73,7 @@ function resolveCommand(flags: Arguments): CLICommand {
export async function cli(args: string[]) {
const flags = yargs(args);
const cmd = resolveCommand(flags);
const projectRoot = flags.projectRoot;
const root = flags.root;
switch (cmd) {
case 'help':
@ -101,7 +100,7 @@ export async function cli(args: string[]) {
try {
// Note: ideally, `loadConfig` would return the config AND its filePath
// For now, `add` has to resolve the config again internally
config = await loadConfig({ cwd: projectRoot, flags });
config = await loadConfig({ cwd: root, flags, cmd });
} catch (err) {
return throwAndExit(err);
}
@ -110,7 +109,7 @@ export async function cli(args: string[]) {
case 'add': {
try {
const packages = flags._.slice(3) as string[];
return await add(packages, { cwd: projectRoot, flags, logging });
return await add(packages, { cwd: root, flags, logging });
} catch (err) {
return throwAndExit(err);
}

View file

@ -14,4 +14,5 @@ export async function generate(ast: t.File) {
return code;
}
export const parse = (code: string) => parser.parse(code, { sourceType: 'unambiguous', plugins: ['typescript'] });
export const parse = (code: string) =>
parser.parse(code, { sourceType: 'unambiguous', plugins: ['typescript'] });

View file

@ -50,7 +50,9 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
configURL = await resolveConfigURL({ cwd, flags });
if (configURL?.pathname.endsWith('package.json')) {
throw new Error(`Unable to use astro add with package.json#astro configuration! Try migrating to \`astro.config.mjs\` and try again.`);
throw new Error(
`Unable to use astro add with package.json#astro configuration! Try migrating to \`astro.config.mjs\` and try again.`
);
}
applyPolyfill();
@ -102,7 +104,13 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
debug('add', 'Parsed astro config');
const defineConfig = t.identifier('defineConfig');
ensureImport(ast, t.importDeclaration([t.importSpecifier(defineConfig, defineConfig)], t.stringLiteral('astro/config')));
ensureImport(
ast,
t.importDeclaration(
[t.importSpecifier(defineConfig, defineConfig)],
t.stringLiteral('astro/config')
)
);
wrapDefaultExport(ast, defineConfig);
debug('add', 'Astro config ensured `defineConfig`');
@ -136,9 +144,13 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
case UpdateResult.none: {
const pkgURL = new URL('./package.json', configURL);
if (existsSync(fileURLToPath(pkgURL))) {
const { dependencies = {}, devDependencies = {} } = await fs.readFile(fileURLToPath(pkgURL)).then((res) => JSON.parse(res.toString()));
const { dependencies = {}, devDependencies = {} } = await fs
.readFile(fileURLToPath(pkgURL))
.then((res) => JSON.parse(res.toString()));
const deps = Object.keys(Object.assign(dependencies, devDependencies));
const missingDeps = integrations.filter((integration) => !deps.includes(integration.packageName));
const missingDeps = integrations.filter(
(integration) => !deps.includes(integration.packageName)
);
if (missingDeps.length === 0) {
info(logging, null, msg.success(`Configuration up-to-date.`));
return;
@ -156,7 +168,11 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
case UpdateResult.updated: {
const len = integrations.length;
if (integrations.find((integration) => integration.id === 'tailwind')) {
const possibleConfigFiles = ['./tailwind.config.cjs', './tailwind.config.mjs', './tailwind.config.js'].map((p) => fileURLToPath(new URL(p, configURL)));
const possibleConfigFiles = [
'./tailwind.config.cjs',
'./tailwind.config.mjs',
'./tailwind.config.js',
].map((p) => fileURLToPath(new URL(p, configURL)));
let alreadyConfigured = false;
for (const possibleConfigPath of possibleConfigFiles) {
if (existsSync(possibleConfigPath)) {
@ -165,9 +181,19 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
}
}
if (!alreadyConfigured) {
info(logging, null, `\n ${magenta(`Astro will generate a minimal ${bold('./tailwind.config.cjs')} file.`)}\n`);
info(
logging,
null,
`\n ${magenta(
`Astro will generate a minimal ${bold('./tailwind.config.cjs')} file.`
)}\n`
);
if (await askToContinue({ flags })) {
await fs.writeFile(fileURLToPath(new URL('./tailwind.config.cjs', configURL)), CONSTS.TAILWIND_CONFIG_STUB, { encoding: 'utf-8' });
await fs.writeFile(
fileURLToPath(new URL('./tailwind.config.cjs', configURL)),
CONSTS.TAILWIND_CONFIG_STUB,
{ encoding: 'utf-8' }
);
debug('add', `Generated default ./tailwind.config.cjs file`);
}
} else {
@ -175,11 +201,24 @@ export default async function add(names: string[], { cwd, flags, logging }: AddO
}
}
const list = integrations.map((integration) => ` - ${integration.packageName}`).join('\n');
info(logging, null, msg.success(`Added the following integration${len === 1 ? '' : 's'} to your project:\n${list}`));
info(
logging,
null,
msg.success(
`Added the following integration${len === 1 ? '' : 's'} to your project:\n${list}`
)
);
return;
}
case UpdateResult.cancelled: {
info(logging, null, msg.cancelled(`Dependencies ${bold('NOT')} installed.`, `Be sure to install them manually before continuing!`));
info(
logging,
null,
msg.cancelled(
`Dependencies ${bold('NOT')} installed.`,
`Be sure to install them manually before continuing!`
)
);
return;
}
case UpdateResult.failure: {
@ -193,7 +232,8 @@ async function parseAstroConfig(configURL: URL): Promise<t.File> {
const result = parse(source);
if (!result) throw new Error('Unknown error parsing astro config');
if (result.errors.length > 0) throw new Error('Error parsing astro config: ' + JSON.stringify(result.errors));
if (result.errors.length > 0)
throw new Error('Error parsing astro config: ' + JSON.stringify(result.errors));
return result;
}
@ -217,7 +257,13 @@ Documentation: https://docs.astro.build/en/guides/integrations-guide/`;
async function addIntegration(ast: t.File, integration: IntegrationInfo) {
const integrationId = t.identifier(toIdent(integration.id));
ensureImport(ast, t.importDeclaration([t.importDefaultSpecifier(integrationId)], t.stringLiteral(integration.packageName)));
ensureImport(
ast,
t.importDeclaration(
[t.importDefaultSpecifier(integrationId)],
t.stringLiteral(integration.packageName)
)
);
visit(ast, {
// eslint-disable-next-line @typescript-eslint/no-shadow
@ -241,14 +287,20 @@ async function addIntegration(ast: t.File, integration: IntegrationInfo) {
const integrationCall = t.callExpression(integrationId, []);
if (!integrationsProp) {
configObject.properties.push(t.objectProperty(t.identifier('integrations'), t.arrayExpression([integrationCall])));
configObject.properties.push(
t.objectProperty(t.identifier('integrations'), t.arrayExpression([integrationCall]))
);
return;
}
if (integrationsProp.value.type !== 'ArrayExpression') throw new Error('Unable to parse integrations');
if (integrationsProp.value.type !== 'ArrayExpression')
throw new Error('Unable to parse integrations');
const existingIntegrationCall = integrationsProp.value.elements.find(
(expr) => t.isCallExpression(expr) && t.isIdentifier(expr.callee) && expr.callee.name === integrationId.name
(expr) =>
t.isCallExpression(expr) &&
t.isIdentifier(expr.callee) &&
expr.callee.name === integrationId.name
);
if (existingIntegrationCall) return;
@ -265,7 +317,17 @@ const enum UpdateResult {
failure,
}
async function updateAstroConfig({ configURL, ast, flags, logging }: { configURL: URL; ast: t.File; flags: yargs.Arguments; logging: LogOptions }): Promise<UpdateResult> {
async function updateAstroConfig({
configURL,
ast,
flags,
logging,
}: {
configURL: URL;
ast: t.File;
flags: yargs.Arguments;
logging: LogOptions;
}): Promise<UpdateResult> {
const input = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' });
let output = await generate(ast);
const comment = '// https://astro.build/config';
@ -299,9 +361,18 @@ async function updateAstroConfig({ configURL, ast, flags, logging }: { configURL
diffed = diffed.replace(newContent, coloredOutput);
}
const message = `\n${boxen(diffed, { margin: 0.5, padding: 0.5, borderStyle: 'round', title: configURL.pathname.split('/').pop() })}\n`;
const message = `\n${boxen(diffed, {
margin: 0.5,
padding: 0.5,
borderStyle: 'round',
title: configURL.pathname.split('/').pop(),
})}\n`;
info(logging, null, `\n ${magenta('Astro will make the following changes to your config file:')}\n${message}`);
info(
logging,
null,
`\n ${magenta('Astro will make the following changes to your config file:')}\n${message}`
);
if (await askToContinue({ flags })) {
await fs.writeFile(fileURLToPath(configURL), output, { encoding: 'utf-8' });
@ -318,7 +389,13 @@ interface InstallCommand {
flags: string[];
dependencies: string[];
}
async function getInstallIntegrationsCommand({ integrations, cwd = process.cwd() }: { integrations: IntegrationInfo[]; cwd?: string }): Promise<InstallCommand | null> {
async function getInstallIntegrationsCommand({
integrations,
cwd = process.cwd(),
}: {
integrations: IntegrationInfo[];
cwd?: string;
}): Promise<InstallCommand | null> {
const pm = await preferredPM(cwd);
debug('add', `package manager: ${JSON.stringify(pm)}`);
if (!pm) return null;
@ -359,14 +436,30 @@ async function tryToInstallIntegrations({
info(logging, null);
return UpdateResult.none;
} else {
const coloredOutput = `${bold(installCommand.pm)} ${installCommand.command} ${installCommand.flags.join(' ')} ${cyan(installCommand.dependencies.join(' '))}`;
const message = `\n${boxen(coloredOutput, { margin: 0.5, padding: 0.5, borderStyle: 'round' })}\n`;
info(logging, null, `\n ${magenta('Astro will run the following command:')}\n ${dim('If you skip this step, you can always run it yourself later')}\n${message}`);
const coloredOutput = `${bold(installCommand.pm)} ${
installCommand.command
} ${installCommand.flags.join(' ')} ${cyan(installCommand.dependencies.join(' '))}`;
const message = `\n${boxen(coloredOutput, {
margin: 0.5,
padding: 0.5,
borderStyle: 'round',
})}\n`;
info(
logging,
null,
`\n ${magenta('Astro will run the following command:')}\n ${dim(
'If you skip this step, you can always run it yourself later'
)}\n${message}`
);
if (await askToContinue({ flags })) {
const spinner = ora('Installing dependencies...').start();
try {
await execa(installCommand.pm, [installCommand.command, ...installCommand.flags, ...installCommand.dependencies], { cwd });
await execa(
installCommand.pm,
[installCommand.command, ...installCommand.flags, ...installCommand.dependencies],
{ cwd }
);
spinner.succeed();
return UpdateResult.updated;
} catch (err) {
@ -405,7 +498,9 @@ export async function validateIntegrations(integrations: string[]): Promise<Inte
return res.json();
});
let dependencies: IntegrationInfo['dependencies'] = [[result['name'], `^${result['version']}`]];
let dependencies: IntegrationInfo['dependencies'] = [
[result['name'], `^${result['version']}`],
];
if (result['peerDependencies']) {
for (const peer in result['peerDependencies']) {

View file

@ -4,7 +4,12 @@ export function wrapDefaultExport(ast: t.File, functionIdentifier: t.Identifier)
visit(ast, {
ExportDefaultDeclaration(path) {
if (!t.isExpression(path.node.declaration)) return;
if (t.isCallExpression(path.node.declaration) && t.isIdentifier(path.node.declaration.callee) && path.node.declaration.callee.name === functionIdentifier.name) return;
if (
t.isCallExpression(path.node.declaration) &&
t.isIdentifier(path.node.declaration.callee) &&
path.node.declaration.callee.name === functionIdentifier.name
)
return;
path.node.declaration = t.callExpression(functionIdentifier, [path.node.declaration]);
},
});

View file

@ -1,4 +1,9 @@
import type { ComponentInstance, EndpointHandler, ManifestData, RouteData } from '../../@types/astro';
import type {
ComponentInstance,
EndpointHandler,
ManifestData,
RouteData,
} from '../../@types/astro';
import type { SSRManifest as Manifest, RouteInfo } from './types';
import type { LogOptions } from '../logger/core.js';
@ -9,7 +14,10 @@ import { matchRoute } from '../routing/match.js';
import { render } from '../render/core.js';
import { call as callEndpoint } from '../endpoint/index.js';
import { RouteCache } from '../render/route-cache.js';
import { createLinkStylesheetElementSet, createModuleScriptElementWithSrcSet } from '../render/ssr-element.js';
import {
createLinkStylesheetElementSet,
createModuleScriptElementWithSrcSet,
} from '../render/ssr-element.js';
import { prependForwardSlash } from '../path.js';
import { createRequest } from '../request.js';
@ -58,7 +66,11 @@ export class App {
}
}
async #renderPage(request: Request, routeData: RouteData, mod: ComponentInstance): Promise<Response> {
async #renderPage(
request: Request,
routeData: RouteData,
mod: ComponentInstance
): Promise<Response> {
const url = new URL(request.url);
const manifest = this.#manifest;
const renderers = manifest.renderers;
@ -105,7 +117,11 @@ export class App {
});
}
async #callEndpoint(request: Request, _routeData: RouteData, mod: ComponentInstance): Promise<Response> {
async #callEndpoint(
request: Request,
_routeData: RouteData,
mod: ComponentInstance
): Promise<Response> {
const url = new URL(request.url);
const handler = mod as unknown as EndpointHandler;
const result = await callEndpoint(handler, {

View file

@ -1,4 +1,10 @@
import type { RouteData, SerializedRouteData, MarkdownRenderOptions, ComponentInstance, SSRLoadedRenderer } from '../../@types/astro';
import type {
RouteData,
SerializedRouteData,
MarkdownRenderOptions,
ComponentInstance,
SSRLoadedRenderer,
} from '../../@types/astro';
export type ComponentPath = string;
@ -28,4 +34,7 @@ export type SerializedSSRManifest = Omit<SSRManifest, 'routes'> & {
routes: SerializedRouteInfo[];
};
export type AdapterCreateExports<T = any> = (manifest: SSRManifest, args?: T) => Record<string, any>;
export type AdapterCreateExports<T = any> = (
manifest: SSRManifest,
args?: T
) => Record<string, any>;

View file

@ -5,10 +5,14 @@ import { appendForwardSlash } from '../../core/path.js';
const STATUS_CODE_PAGES = new Set(['/404', '/500']);
function getOutRoot(astroConfig: AstroConfig): URL {
return new URL('./', astroConfig.dist);
return new URL('./', astroConfig.outDir);
}
export function getOutFolder(astroConfig: AstroConfig, pathname: string, routeType: RouteType): URL {
export function getOutFolder(
astroConfig: AstroConfig,
pathname: string,
routeType: RouteType
): URL {
const outRoot = getOutRoot(astroConfig);
// This is the root folder to write to.
@ -16,7 +20,7 @@ export function getOutFolder(astroConfig: AstroConfig, pathname: string, routeTy
case 'endpoint':
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
case 'page':
switch (astroConfig.buildOptions.pageUrlFormat) {
switch (astroConfig.build.format) {
case 'directory': {
if (STATUS_CODE_PAGES.has(pathname)) {
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
@ -30,12 +34,17 @@ export function getOutFolder(astroConfig: AstroConfig, pathname: string, routeTy
}
}
export function getOutFile(astroConfig: AstroConfig, outFolder: URL, pathname: string, routeType: RouteType): URL {
export function getOutFile(
astroConfig: AstroConfig,
outFolder: URL,
pathname: string,
routeType: RouteType
): URL {
switch (routeType) {
case 'endpoint':
return new URL(npath.basename(pathname), outFolder);
case 'page':
switch (astroConfig.buildOptions.pageUrlFormat) {
switch (astroConfig.build.format) {
case 'directory': {
if (STATUS_CODE_PAGES.has(pathname)) {
const baseName = npath.basename(pathname);

View file

@ -1,17 +1,26 @@
import astroRemark from '@astrojs/markdown-remark';
import fs from 'fs';
import { bgGreen, bgMagenta, black, cyan, dim, green, magenta } from 'kleur/colors';
import { bgGreen, black, cyan, dim, green, magenta } from 'kleur/colors';
import npath from 'path';
import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup';
import { fileURLToPath } from 'url';
import type { AstroConfig, ComponentInstance, EndpointHandler, SSRLoadedRenderer } from '../../@types/astro';
import type {
AstroConfig,
ComponentInstance,
EndpointHandler,
SSRLoadedRenderer,
} from '../../@types/astro';
import type { BuildInternals } from '../../core/build/internal.js';
import { debug, info } from '../logger/core.js';
import { appendForwardSlash, prependForwardSlash } from '../../core/path.js';
import { prependForwardSlash } from '../../core/path.js';
import type { RenderOptions } from '../../core/render/core';
import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import { call as callEndpoint } from '../endpoint/index.js';
import { render } from '../render/core.js';
import { createLinkStylesheetElementSet, createModuleScriptElementWithSrcSet } from '../render/ssr-element.js';
import {
createLinkStylesheetElementSet,
createModuleScriptElementWithSrcSet,
} from '../render/ssr-element.js';
import { getOutputFilename, isBuildingToSSR } from '../util.js';
import { getOutFile, getOutFolder } from './common.js';
import { eachPageData, getPageDataByComponent } from './internal.js';
@ -51,29 +60,40 @@ function* throttle(max: number, inPaths: string[]) {
// Gives back a facadeId that is relative to the root.
// ie, src/pages/index.astro instead of /Users/name..../src/pages/index.astro
export function rootRelativeFacadeId(facadeId: string, astroConfig: AstroConfig): string {
return facadeId.slice(fileURLToPath(astroConfig.projectRoot).length);
return facadeId.slice(fileURLToPath(astroConfig.root).length);
}
// Determines of a Rollup chunk is an entrypoint page.
export function chunkIsPage(astroConfig: AstroConfig, output: OutputAsset | OutputChunk, internals: BuildInternals) {
export function chunkIsPage(
astroConfig: AstroConfig,
output: OutputAsset | OutputChunk,
internals: BuildInternals
) {
if (output.type !== 'chunk') {
return false;
}
const chunk = output as OutputChunk;
if (chunk.facadeModuleId) {
const facadeToEntryId = prependForwardSlash(rootRelativeFacadeId(chunk.facadeModuleId, astroConfig));
const facadeToEntryId = prependForwardSlash(
rootRelativeFacadeId(chunk.facadeModuleId, astroConfig)
);
return internals.entrySpecifierToBundleMap.has(facadeToEntryId);
}
return false;
}
export async function generatePages(result: RollupOutput, opts: StaticBuildOptions, internals: BuildInternals, facadeIdToPageDataMap: Map<string, PageBuildData>) {
export async function generatePages(
result: RollupOutput,
opts: StaticBuildOptions,
internals: BuildInternals,
facadeIdToPageDataMap: Map<string, PageBuildData>
) {
const timer = performance.now();
info(opts.logging, null, `\n${bgGreen(black(' generating static routes '))}`);
const ssr = isBuildingToSSR(opts.astroConfig);
const serverEntry = opts.buildConfig.serverEntry;
const outFolder = ssr ? opts.buildConfig.server : opts.astroConfig.dist;
const outFolder = ssr ? opts.buildConfig.server : opts.astroConfig.outDir;
const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder);
const ssrEntry = await import(ssrEntryURL.toString());
@ -100,7 +120,9 @@ async function generatePage(
const pageModule = ssrEntry.pageMap.get(pageData.component);
if (!pageModule) {
throw new Error(`Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.`);
throw new Error(
`Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.`
);
}
const generationOptions: Readonly<GeneratePathOptions> = {
@ -140,7 +162,11 @@ function addPageName(pathname: string, opts: StaticBuildOptions): void {
opts.pageNames.push(pathname.replace(/\/?$/, '/').replace(/^\//, ''));
}
async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: GeneratePathOptions) {
async function generatePath(
pathname: string,
opts: StaticBuildOptions,
gopts: GeneratePathOptions
) {
const { astroConfig, logging, origin, routeCache } = opts;
const { mod, internals, linkIds, hoistedId, pageData, renderers } = gopts;
@ -151,7 +177,7 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
debug('build', `Generating: ${pathname}`);
const site = astroConfig.buildOptions.site;
const site = astroConfig.site;
const links = createLinkStylesheetElementSet(linkIds.reverse(), site);
const scripts = createModuleScriptElementWithSrcSet(hoistedId ? [hoistedId] : [], site);
@ -170,7 +196,7 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
legacyBuild: false,
links,
logging,
markdownRender: astroConfig.markdownOptions.render,
markdownRender: [astroRemark, astroConfig.markdown],
mod,
origin,
pathname,
@ -196,7 +222,9 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
request: createRequest({ url, headers: new Headers(), logging }),
route: pageData.route,
routeCache,
site: astroConfig.buildOptions.site,
site: astroConfig.site
? new URL(astroConfig.base, astroConfig.site).toString()
: astroConfig.site,
ssr: isBuildingToSSR(opts.astroConfig),
};

View file

@ -7,15 +7,25 @@ import { apply as applyPolyfill } from '../polyfill.js';
import { performance } from 'perf_hooks';
import * as vite from 'vite';
import { createVite, ViteConfigWithSSR } from '../create-vite.js';
import { debug, info, levels, timerMessage, warn, warnIfUsingExperimentalSSR } from '../logger/core.js';
import {
debug,
info,
levels,
timerMessage,
warn,
warnIfUsingExperimentalSSR,
} from '../logger/core.js';
import { nodeLogOptions } from '../logger/node.js';
import { createRouteManifest } from '../routing/index.js';
import { generateSitemap } from '../render/sitemap.js';
import { collectPagesData } from './page-data.js';
import { build as scanBasedBuild } from './scan-based-build.js';
import { staticBuild } from './static-build.js';
import { RouteCache } from '../render/route-cache.js';
import { runHookBuildDone, runHookBuildStart, runHookConfigDone, runHookConfigSetup } from '../../integrations/index.js';
import {
runHookBuildDone,
runHookBuildStart,
runHookConfigDone,
runHookConfigSetup,
} from '../../integrations/index.js';
import { getTimeStat } from './util.js';
import { createSafeError, isBuildingToSSR } from '../util.js';
import { fixViteErrorMessage } from '../errors.js';
@ -26,7 +36,10 @@ export interface BuildOptions {
}
/** `astro build` */
export default async function build(config: AstroConfig, options: BuildOptions = { logging: nodeLogOptions }): Promise<void> {
export default async function build(
config: AstroConfig,
options: BuildOptions = { logging: nodeLogOptions }
): Promise<void> {
applyPolyfill();
const builder = new AstroBuilder(config, options);
await builder.run();
@ -42,17 +55,15 @@ class AstroBuilder {
private timer: Record<string, number>;
constructor(config: AstroConfig, options: BuildOptions) {
if (!config.buildOptions.site && config.buildOptions.sitemap !== false) {
warn(options.logging, 'config', `Set "buildOptions.site" to generate correct canonical URLs and sitemap`);
}
if (options.mode) {
this.mode = options.mode;
}
this.config = config;
const port = config.devOptions.port; // no need to save this (dont rely on port in builder)
this.logging = options.logging;
this.routeCache = new RouteCache(this.logging);
this.origin = config.buildOptions.site ? new URL(config.buildOptions.site).origin : `http://localhost:${port}`;
this.origin = config.site
? new URL(config.site).origin
: `http://localhost:${config.server.port}`;
this.manifest = createRouteManifest({ config }, this.logging);
this.timer = {};
}
@ -82,11 +93,17 @@ class AstroBuilder {
}
/** Run the build logic. build() is marked private because usage should go through ".run()" */
private async build({ viteConfig, viteServer }: { viteConfig: ViteConfigWithSSR; viteServer: vite.ViteDevServer }) {
private async build({
viteConfig,
viteServer,
}: {
viteConfig: ViteConfigWithSSR;
viteServer: vite.ViteDevServer;
}) {
const { origin } = this;
const buildConfig: BuildConfig = {
client: new URL('./client/', this.config.dist),
server: new URL('./server/', this.config.dist),
client: new URL('./client/', this.config.outDir),
server: new URL('./server/', this.config.outDir),
serverEntry: 'entry.mjs',
staticMode: undefined,
};
@ -109,7 +126,7 @@ class AstroBuilder {
if ('frontmatter' in data.preload[1]) {
// TODO: add better type inference to data.preload[1]
const frontmatter = (data.preload[1] as any).frontmatter;
if (Boolean(frontmatter.draft) && !this.config.buildOptions.drafts) {
if (Boolean(frontmatter.draft) && !this.config.markdown.drafts) {
debug('build', timerMessage(`Skipping draft page ${page}`, this.timer.loadStart));
delete allPages[page];
}
@ -124,33 +141,23 @@ class AstroBuilder {
// Bundle the assets in your final build: This currently takes the HTML output
// of every page (stored in memory) and bundles the assets pointed to on those pages.
this.timer.buildStart = performance.now();
info(this.logging, 'build', colors.dim(`Completed in ${getTimeStat(this.timer.init, performance.now())}.`));
info(
this.logging,
'build',
colors.dim(`Completed in ${getTimeStat(this.timer.init, performance.now())}.`)
);
// Use the new faster static based build.
if (!this.config.buildOptions.legacyBuild) {
await staticBuild({
allPages,
astroConfig: this.config,
logging: this.logging,
manifest: this.manifest,
origin: this.origin,
pageNames,
routeCache: this.routeCache,
viteConfig,
buildConfig,
});
} else {
await scanBasedBuild({
allPages,
astroConfig: this.config,
logging: this.logging,
origin: this.origin,
pageNames,
routeCache: this.routeCache,
viteConfig,
viteServer,
});
}
await staticBuild({
allPages,
astroConfig: this.config,
logging: this.logging,
manifest: this.manifest,
origin: this.origin,
pageNames,
routeCache: this.routeCache,
viteConfig,
buildConfig,
});
// Write any additionally generated assets to disk.
this.timer.assetsStart = performance.now();
@ -163,27 +170,22 @@ class AstroBuilder {
});
debug('build', timerMessage('Additional assets copied', this.timer.assetsStart));
// Build your final sitemap.
if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
this.timer.sitemapStart = performance.now();
const sitemapFilter = this.config.buildOptions.sitemapFilter ? (this.config.buildOptions.sitemapFilter as (page: string) => boolean) : undefined;
const sitemap = generateSitemap(
pageNames.map((pageName) => new URL(pageName, this.config.buildOptions.site).href),
sitemapFilter
);
const sitemapPath = new URL('./sitemap.xml', this.config.dist);
await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
debug('build', timerMessage('Sitemap built', this.timer.sitemapStart));
}
// You're done! Time to clean up.
await viteServer.close();
await runHookBuildDone({ config: this.config, pages: pageNames, routes: Object.values(allPages).map((pd) => pd.route) });
await runHookBuildDone({
config: this.config,
pages: pageNames,
routes: Object.values(allPages).map((pd) => pd.route),
});
if (this.logging.level && levels[this.logging.level] <= levels['info']) {
const buildMode = isBuildingToSSR(this.config) ? 'ssr' : 'static';
await this.printStats({ logging: this.logging, timeStart: this.timer.init, pageCount: pageNames.length, buildMode });
await this.printStats({
logging: this.logging,
timeStart: this.timer.init,
pageCount: pageNames.length,
buildMode,
});
}
}
@ -199,7 +201,17 @@ class AstroBuilder {
}
/** Stats */
private async printStats({ logging, timeStart, pageCount, buildMode }: { logging: LogOptions; timeStart: number; pageCount: number; buildMode: 'static' | 'ssr' }) {
private async printStats({
logging,
timeStart,
pageCount,
buildMode,
}: {
logging: LogOptions;
timeStart: number;
pageCount: number;
buildMode: 'static' | 'ssr';
}) {
const buildTime = performance.now() - timeStart;
const total = getTimeStat(timeStart, performance.now());

View file

@ -76,13 +76,22 @@ export function createBuildInternals(): BuildInternals {
};
}
export function trackPageData(internals: BuildInternals, component: string, pageData: PageBuildData, componentModuleId: string, componentURL: URL): void {
export function trackPageData(
internals: BuildInternals,
component: string,
pageData: PageBuildData,
componentModuleId: string,
componentURL: URL
): void {
pageData.moduleSpecifier = componentModuleId;
internals.pagesByComponent.set(component, pageData);
internals.pagesByViteID.set(viteID(componentURL), pageData);
}
export function* getPageDatasByChunk(internals: BuildInternals, chunk: RenderedChunk): Generator<PageBuildData, void, unknown> {
export function* getPageDatasByChunk(
internals: BuildInternals,
chunk: RenderedChunk
): Generator<PageBuildData, void, unknown> {
const pagesByViteID = internals.pagesByViteID;
for (const [modulePath] of Object.entries(chunk.modules)) {
if (pagesByViteID.has(modulePath)) {
@ -91,14 +100,20 @@ export function* getPageDatasByChunk(internals: BuildInternals, chunk: RenderedC
}
}
export function getPageDataByComponent(internals: BuildInternals, component: string): PageBuildData | undefined {
export function getPageDataByComponent(
internals: BuildInternals,
component: string
): PageBuildData | undefined {
if (internals.pagesByComponent.has(component)) {
return internals.pagesByComponent.get(component);
}
return undefined;
}
export function getPageDataByViteID(internals: BuildInternals, viteid: ViteID): PageBuildData | undefined {
export function getPageDataByViteID(
internals: BuildInternals,
viteid: ViteID
): PageBuildData | undefined {
if (internals.pagesByViteID.has(viteid)) {
return internals.pagesByViteID.get(viteid);
}

View file

@ -28,7 +28,9 @@ export interface CollectPagesDataResult {
}
// Examines the routes and returns a collection of information about each page.
export async function collectPagesData(opts: CollectPagesDataOptions): Promise<CollectPagesDataResult> {
export async function collectPagesData(
opts: CollectPagesDataOptions
): Promise<CollectPagesDataResult> {
const { astroConfig, logging, manifest, origin, routeCache, viteServer } = opts;
const assets: Record<string, string> = {};
@ -68,14 +70,17 @@ export async function collectPagesData(opts: CollectPagesDataOptions): Promise<C
scripts: new Set(),
preload: await ssrPreload({
astroConfig,
filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
filePath: new URL(`./${route.component}`, astroConfig.root),
viteServer,
})
.then((routes) => {
clearInterval(routeCollectionLogTimeout);
if (buildMode === 'static') {
const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
debug('build', `├── ${colors.bold(colors.green('✔'))} ${route.component}${colors.yellow(html)}`);
debug(
'build',
`├── ${colors.bold(colors.green('✔'))} ${route.component}${colors.yellow(html)}`
);
} else {
debug('build', `├── ${colors.bold(colors.green('✔'))} ${route.component}`);
}
@ -93,29 +98,36 @@ export async function collectPagesData(opts: CollectPagesDataOptions): Promise<C
const result = await getStaticPathsForRoute(opts, route)
.then((_result) => {
const label = _result.staticPaths.length === 1 ? 'page' : 'pages';
debug('build', `├── ${colors.bold(colors.green('✔'))} ${route.component}${colors.magenta(`[${_result.staticPaths.length} ${label}]`)}`);
debug(
'build',
`├── ${colors.bold(colors.green('✔'))} ${route.component}${colors.magenta(
`[${_result.staticPaths.length} ${label}]`
)}`
);
return _result;
})
.catch((err) => {
debug('build', `├── ${colors.bold(colors.red('✗'))} ${route.component}`);
throw err;
});
const rssFn = generateRssFunction(astroConfig.buildOptions.site, route);
const rssFn = generateRssFunction(astroConfig.site, route);
for (const rssCallArg of result.rss) {
const rssResult = rssFn(rssCallArg);
if (rssResult.xml) {
const { url, content } = rssResult.xml;
if (content) {
const rssFile = new URL(url.replace(/^\/?/, './'), astroConfig.dist);
const rssFile = new URL(url.replace(/^\/?/, './'), astroConfig.outDir);
if (assets[fileURLToPath(rssFile)]) {
throw new Error(`[getStaticPaths] RSS feed ${url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`);
throw new Error(
`[getStaticPaths] RSS feed ${url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`
);
}
assets[fileURLToPath(rssFile)] = content;
}
}
if (rssResult.xsl?.content) {
const { url, content } = rssResult.xsl;
const stylesheetFile = new URL(url.replace(/^\/?/, './'), astroConfig.dist);
const stylesheetFile = new URL(url.replace(/^\/?/, './'), astroConfig.outDir);
if (assets[fileURLToPath(stylesheetFile)]) {
throw new Error(
`[getStaticPaths] RSS feed stylesheet ${url} already exists.\nUse \`rss(data, {stylesheet: '...'})\` to choose a unique, custom URL. (${route.component})`
@ -124,7 +136,9 @@ export async function collectPagesData(opts: CollectPagesDataOptions): Promise<C
assets[fileURLToPath(stylesheetFile)] = content;
}
}
const finalPaths = result.staticPaths.map((staticPath) => staticPath.params && route.generate(staticPath.params)).filter(Boolean);
const finalPaths = result.staticPaths
.map((staticPath) => staticPath.params && route.generate(staticPath.params))
.filter(Boolean);
allPages[route.component] = {
component: route.component,
route,
@ -135,7 +149,7 @@ export async function collectPagesData(opts: CollectPagesDataOptions): Promise<C
scripts: new Set(),
preload: await ssrPreload({
astroConfig,
filePath: new URL(`./${route.component}`, astroConfig.projectRoot),
filePath: new URL(`./${route.component}`, astroConfig.root),
viteServer,
}),
};
@ -146,10 +160,13 @@ export async function collectPagesData(opts: CollectPagesDataOptions): Promise<C
return { assets, allPages };
}
async function getStaticPathsForRoute(opts: CollectPagesDataOptions, route: RouteData): Promise<RouteCacheEntry> {
async function getStaticPathsForRoute(
opts: CollectPagesDataOptions,
route: RouteData
): Promise<RouteCacheEntry> {
const { astroConfig, logging, routeCache, ssr, viteServer } = opts;
if (!viteServer) throw new Error(`vite.createServer() not called!`);
const filePath = new URL(`./${route.component}`, astroConfig.projectRoot);
const filePath = new URL(`./${route.component}`, astroConfig.root);
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
const result = await callGetStaticPaths({ mod, route, isValidate: false, logging, ssr });
routeCache.set(route, result);

View file

@ -1,89 +0,0 @@
import type { ViteDevServer } from 'vite';
import type { RollupOutput, RollupWatcher } from 'rollup';
import type { AstroConfig, RouteType } from '../../@types/astro';
import type { AllPagesData, PageBuildData } from './types';
import type { LogOptions } from '../logger/core';
import type { ViteConfigWithSSR } from '../create-vite.js';
import { fileURLToPath } from 'url';
import * as vite from 'vite';
import { createBuildInternals } from '../../core/build/internal.js';
import { rollupPluginAstroScanHTML } from '../../vite-plugin-build-html/index.js';
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
import { RouteCache } from '../render/route-cache.js';
export interface ScanBasedBuildOptions {
allPages: AllPagesData;
astroConfig: AstroConfig;
logging: LogOptions;
origin: string;
pageNames: string[];
routeCache: RouteCache;
viteConfig: ViteConfigWithSSR;
viteServer: ViteDevServer;
}
// Returns a filter predicate to filter AllPagesData entries by RouteType
function entryIsType(type: RouteType) {
return function withPage([_, pageData]: [string, PageBuildData]) {
return pageData.route.type === type;
};
}
// Reducer to combine AllPageData entries back into an object keyed by filepath
function reduceEntries<U>(acc: { [key: string]: U }, [key, value]: [string, U]) {
acc[key] = value;
return acc;
}
// Filters an AllPagesData object to only include routes of a specific RouteType
function routesOfType(type: RouteType, allPages: AllPagesData) {
return Object.entries(allPages).filter(entryIsType(type)).reduce(reduceEntries, {});
}
export async function build(opts: ScanBasedBuildOptions): Promise<RollupOutput | RollupOutput[] | RollupWatcher> {
const { allPages, astroConfig, logging, origin, pageNames, routeCache, viteConfig, viteServer } = opts;
// Internal maps used to coordinate the HTML and CSS plugins.
const internals = createBuildInternals();
return await vite.build({
logLevel: 'warn',
mode: 'production',
build: {
emptyOutDir: true,
minify: 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
outDir: fileURLToPath(astroConfig.dist),
rollupOptions: {
// The `input` will be populated in the build rollup plugin.
input: [],
output: {
format: 'esm',
},
},
target: 'es2020', // must match an esbuild target
},
plugins: [
rollupPluginAstroScanHTML({
astroConfig,
internals,
logging,
origin,
allPages: routesOfType('page', allPages),
pageNames,
routeCache,
viteServer,
}),
rollupPluginAstroBuildCSS({
internals,
legacy: true,
}),
...(viteConfig.plugins || []),
],
publicDir: viteConfig.publicDir,
root: viteConfig.root,
envPrefix: 'PUBLIC_',
server: viteConfig.server,
base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
});
}

View file

@ -44,7 +44,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
timer.buildStart = performance.now();
for (const [component, pageData] of Object.entries(allPages)) {
const astroModuleURL = new URL('./' + component, astroConfig.projectRoot);
const astroModuleURL = new URL('./' + component, astroConfig.root);
const astroModuleId = prependForwardSlash(component);
// Track the page data in internals
@ -64,7 +64,9 @@ export async function staticBuild(opts: StaticBuildOptions) {
// Any hydration directive like astro/client/idle.js
...metadata.hydrationDirectiveSpecifiers(),
// The client path for each renderer
...renderers.filter((renderer) => !!renderer.clientEntrypoint).map((renderer) => renderer.clientEntrypoint!),
...renderers
.filter((renderer) => !!renderer.clientEntrypoint)
.map((renderer) => renderer.clientEntrypoint!),
]);
// Add hoisted scripts
@ -87,7 +89,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
// Empty out the dist folder, if needed. Vite has a config for doing this
// but because we are running 2 vite builds in parallel, that would cause a race
// condition, so we are doing it ourselves
emptyDir(astroConfig.dist, new Set('.git'));
emptyDir(astroConfig.outDir, new Set('.git'));
timer.clientBuild = performance.now();
// Run client build first, so the assets can be fed into the SSR rendered version.
@ -112,7 +114,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
const { astroConfig, viteConfig } = opts;
const ssr = isBuildingToSSR(astroConfig);
const out = ssr ? opts.buildConfig.server : astroConfig.dist;
const out = ssr ? opts.buildConfig.server : astroConfig.outDir;
const viteBuildConfig = {
logLevel: 'error',
@ -149,13 +151,14 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
}),
...(viteConfig.plugins || []),
// SSR needs to be last
isBuildingToSSR(opts.astroConfig) && vitePluginSSR(opts, internals, opts.astroConfig._ctx.adapter!),
isBuildingToSSR(opts.astroConfig) &&
vitePluginSSR(opts, internals, opts.astroConfig._ctx.adapter!),
],
publicDir: ssr ? false : viteConfig.publicDir,
root: viteConfig.root,
envPrefix: 'PUBLIC_',
server: viteConfig.server,
base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
base: astroConfig.site ? new URL(astroConfig.site).pathname : '/',
ssr: viteConfig.ssr,
resolve: viteConfig.resolve,
} as ViteConfigWithSSR;
@ -166,17 +169,21 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
return await vite.build(viteBuildConfig);
}
async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
async function clientBuild(
opts: StaticBuildOptions,
internals: BuildInternals,
input: Set<string>
) {
const { astroConfig, viteConfig } = opts;
const timer = performance.now();
const ssr = isBuildingToSSR(astroConfig);
const out = ssr ? opts.buildConfig.client : astroConfig.dist;
const out = ssr ? opts.buildConfig.client : astroConfig.outDir;
// Nothing to do if there is no client-side JS.
if (!input.size) {
// If SSR, copy public over
if (ssr) {
await copyFiles(astroConfig.public, out);
await copyFiles(astroConfig.publicDir, out);
}
return null;
@ -218,7 +225,7 @@ async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals,
root: viteConfig.root,
envPrefix: 'PUBLIC_',
server: viteConfig.server,
base: appendForwardSlash(astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/'),
base: astroConfig.base,
} as ViteConfigWithSSR;
await runHookBuildSetup({ config: astroConfig, vite: viteBuildConfig, target: 'client' });
@ -231,11 +238,11 @@ async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals,
async function cleanSsrOutput(opts: StaticBuildOptions) {
// The SSR output is all .mjs files, the client output is not.
const files = await glob('**/*.mjs', {
cwd: fileURLToPath(opts.astroConfig.dist),
cwd: fileURLToPath(opts.astroConfig.outDir),
});
await Promise.all(
files.map(async (filename) => {
const url = new URL(filename, opts.astroConfig.dist);
const url = new URL(filename, opts.astroConfig.outDir);
await fs.promises.rm(url);
})
);
@ -260,7 +267,9 @@ async function copyFiles(fromFolder: URL, toFolder: URL) {
async function ssrMoveAssets(opts: StaticBuildOptions) {
info(opts.logging, 'build', 'Rearranging server assets...');
const serverRoot = opts.buildConfig.staticMode ? opts.buildConfig.client : opts.buildConfig.server;
const serverRoot = opts.buildConfig.staticMode
? opts.buildConfig.client
: opts.buildConfig.server;
const clientRoot = opts.buildConfig.client;
const serverAssets = new URL('./assets/', serverRoot);
const clientAssets = new URL('./assets/', clientRoot);

View file

@ -1,5 +1,12 @@
import type { ComponentPreload } from '../render/dev/index';
import type { AstroConfig, BuildConfig, ManifestData, RouteData, ComponentInstance, SSRLoadedRenderer } from '../../@types/astro';
import type {
AstroConfig,
BuildConfig,
ManifestData,
RouteData,
ComponentInstance,
SSRLoadedRenderer,
} from '../../@types/astro';
import type { ViteConfigWithSSR } from '../../create-vite';
import type { LogOptions } from '../../logger';
import type { RouteCache } from '../../render/route-cache.js';

View file

@ -8,7 +8,10 @@ function virtualHoistedEntry(id: string) {
return id.endsWith('.astro/hoisted.js') || id.endsWith('.md/hoisted.js');
}
export function vitePluginHoistedScripts(astroConfig: AstroConfig, internals: BuildInternals): VitePlugin {
export function vitePluginHoistedScripts(
astroConfig: AstroConfig,
internals: BuildInternals
): VitePlugin {
return {
name: '@astro/rollup-plugin-astro-hoisted-scripts',
@ -35,11 +38,15 @@ export function vitePluginHoistedScripts(astroConfig: AstroConfig, internals: Bu
// Find all page entry points and create a map of the entry point to the hashed hoisted script.
// This is used when we render so that we can add the script to the head.
for (const [id, output] of Object.entries(bundle)) {
if (output.type === 'chunk' && output.facadeModuleId && virtualHoistedEntry(output.facadeModuleId)) {
if (
output.type === 'chunk' &&
output.facadeModuleId &&
virtualHoistedEntry(output.facadeModuleId)
) {
const facadeId = output.facadeModuleId!;
const pathname = facadeId.slice(0, facadeId.length - '/hoisted.js'.length);
const vid = viteID(new URL('.' + pathname, astroConfig.projectRoot));
const vid = viteID(new URL('.' + pathname, astroConfig.root));
const pageInfo = getPageDataByViteID(internals, vid);
if (pageInfo) {
pageInfo.hoistedScript = id;

View file

@ -1,4 +1,4 @@
import type { OutputBundle, OutputChunk } from 'rollup';
import astroRemark from '@astrojs/markdown-remark';
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from './internal.js';
import type { AstroAdapter } from '../../@types/astro';
@ -15,7 +15,11 @@ export const virtualModuleId = '@astrojs-ssr-virtual-entry';
const resolvedVirtualModuleId = '\0' + virtualModuleId;
const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
export function vitePluginSSR(buildOpts: StaticBuildOptions, internals: BuildInternals, adapter: AstroAdapter): VitePlugin {
export function vitePluginSSR(
buildOpts: StaticBuildOptions,
internals: BuildInternals,
adapter: AstroAdapter
): VitePlugin {
return {
name: '@astrojs/vite-plugin-astro-ssr',
enforce: 'post',
@ -92,13 +96,14 @@ function buildManifest(opts: StaticBuildOptions, internals: BuildInternals): Ser
// HACK! Patch this special one.
const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
entryModules[BEFORE_HYDRATION_SCRIPT_ID] = 'data:text/javascript;charset=utf-8,//[no before-hydration script]';
entryModules[BEFORE_HYDRATION_SCRIPT_ID] =
'data:text/javascript;charset=utf-8,//[no before-hydration script]';
const ssrManifest: SerializedSSRManifest = {
routes,
site: astroConfig.buildOptions.site,
site: astroConfig.site,
markdown: {
render: astroConfig.markdownOptions.render,
render: [astroRemark, astroConfig.markdown],
},
pageMap: null as any,
renderers: [],

View file

@ -11,6 +11,7 @@ import load from '@proload/core';
import loadTypeScript from '@proload/plugin-tsm';
import postcssrc from 'postcss-load-config';
import { arraify, isObject } from './util.js';
import { appendForwardSlash, trimSlashes } from './path.js';
load.use([loadTypeScript]);
@ -43,40 +44,91 @@ async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<Post
}
}
export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
'projectRoot',
'src',
'pages',
'public',
'dist',
'styleOptions',
'markdownOptions',
'buildOptions',
'devOptions',
'experimentalIntegrations',
]);
export const AstroConfigSchema = z.object({
projectRoot: z
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
root: z
.string()
.optional()
.default('.')
.transform((val) => new URL(val)),
src: z
srcDir: z
.string()
.optional()
.default('./src')
.transform((val) => new URL(val)),
pages: z
.string()
.optional()
.default('./src/pages')
.transform((val) => new URL(val)),
public: z
publicDir: z
.string()
.optional()
.default('./public')
.transform((val) => new URL(val)),
dist: z
outDir: z
.string()
.optional()
.default('./dist')
.transform((val) => new URL(val)),
site: z
.string()
.url()
.optional()
.transform((val) => (val ? appendForwardSlash(val) : val))
.refine((val) => !val || new URL(val).pathname.length <= 1, {
message:
'"site" must be a valid URL origin (ex: "https://example.com") but cannot contain a URL path (ex: "https://example.com/blog"). Use "base" to configure your deployed URL path',
}),
base: z
.string()
.optional()
.default('./')
.transform((val) => (val ? appendForwardSlash(trimSlashes(val)) : val)),
trailingSlash: z
.union([z.literal('always'), z.literal('never'), z.literal('ignore')])
.optional()
.default('ignore'),
build: z
.object({
format: z
.union([z.literal('file'), z.literal('directory')])
.optional()
.default('directory'),
})
.optional()
.default({}),
server: z.preprocess(
// preprocess
// NOTE: Uses the "error" command here because this is overwritten by the
// individualized schema parser with the correct command.
(val) => (typeof val === 'function' ? val({ command: 'error' }) : val),
// validate
z
.object({
host: z.union([z.string(), z.boolean()]).optional().default(false),
port: z.number().optional().default(3000),
})
.optional()
.default({})
),
integrations: z.preprocess(
// preprocess
(val) => (Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val),
// validate
z.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })).default([])
z
.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }))
.default([])
),
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
styleOptions: z
style: z
.object({
postcss: z
.object({
@ -88,50 +140,41 @@ export const AstroConfigSchema = z.object({
})
.optional()
.default({}),
markdownOptions: z
markdown: z
.object({
render: z.any().optional().default(['@astrojs/markdown-remark', {}]),
})
.strict()
.optional()
.default({}),
buildOptions: z
.object({
site: z
.string()
.optional()
.transform((val) => (val ? addTrailingSlash(val) : val)),
sitemapFilter: z.function().optional(),
sitemap: z.boolean().optional().default(true),
pageUrlFormat: z
.union([z.literal('file'), z.literal('directory')])
.optional()
.default('directory'),
legacyBuild: z.boolean().optional().default(false),
experimentalStaticBuild: z.boolean().optional().default(true),
experimentalSsr: z.boolean().optional().default(false),
drafts: z.boolean().optional().default(false),
})
.optional()
.default({}),
devOptions: z
.object({
host: z.union([z.string(), z.boolean()]).optional().default(false),
hostname: z.string().optional().default('localhost'),
port: z.number().optional().default(3000),
trailingSlash: z
.union([z.literal('always'), z.literal('never'), z.literal('ignore')])
mode: z
.union([z.literal('md'), z.literal('mdx')])
.optional()
.default('ignore'),
.default('md'),
syntaxHighlight: z
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
.optional()
.default('shiki'),
// TODO: add better type checking
shikiConfig: z.any().optional().default({}),
remarkPlugins: z.array(z.any()).optional().default([]),
rehypePlugins: z.array(z.any()).optional().default([]),
})
.passthrough()
.optional()
.default({}),
vite: z.any().optional().default({}),
experimental: z
.object({
ssr: z.boolean().optional().default(false),
integrations: z.boolean().optional().default(false),
})
.optional()
.default({}),
experimentalIntegrations: z.boolean().optional().default(false),
vite: z.any().optional().default({}), // TODO: we dont need validation, but can we get better type inference?
});
/** Turn raw config values into normalized values */
export async function validateConfig(userConfig: any, root: string): Promise<AstroConfig> {
export async function validateConfig(
userConfig: any,
root: string,
cmd: string
): Promise<AstroConfig> {
const fileProtocolRoot = pathToFileURL(root + path.sep);
// Manual deprecation checks
/* eslint-disable no-console */
@ -139,8 +182,12 @@ export async function validateConfig(userConfig: any, root: string): Promise<Ast
console.error('Astro "renderers" are now "integrations"!');
console.error('Update your configuration and install new dependencies:');
try {
const rendererKeywords = userConfig.renderers.map((r: string) => r.replace('@astrojs/renderer-', ''));
const rendererImports = rendererKeywords.map((r: string) => ` import ${r} from '@astrojs/${r === 'solid' ? 'solid-js' : r}';`).join('\n');
const rendererKeywords = userConfig.renderers.map((r: string) =>
r.replace('@astrojs/renderer-', '')
);
const rendererImports = rendererKeywords
.map((r: string) => ` import ${r} from '@astrojs/${r === 'solid' ? 'solid-js' : r}';`)
.join('\n');
const rendererIntegrations = rendererKeywords.map((r: string) => ` ${r}(),`).join('\n');
console.error('');
console.error(colors.dim(' // astro.config.js'));
@ -162,32 +209,54 @@ export async function validateConfig(userConfig: any, root: string): Promise<Ast
}
process.exit(1);
}
let oldConfig = false;
for (const key of Object.keys(userConfig)) {
if (LEGACY_ASTRO_CONFIG_KEYS.has(key)) {
oldConfig = true;
break;
}
}
if (oldConfig) {
throw new Error(
`Legacy configuration detected. Please update your configuration to the new format!\nSee https://astro.build/config for more information.`
);
}
/* eslint-enable no-console */
// We need to extend the global schema to add transforms that are relative to root.
// This is type checked against the global schema to make sure we still match.
const AstroConfigRelativeSchema = AstroConfigSchema.extend({
projectRoot: z
root: z
.string()
.default('.')
.transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
src: z
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
srcDir: z
.string()
.default('./src')
.transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
pages: z
.string()
.default('./src/pages')
.transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
public: z
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
publicDir: z
.string()
.default('./public')
.transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
dist: z
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
outDir: z
.string()
.default('./dist')
.transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)),
styleOptions: z
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
server: z.preprocess(
// preprocess
(val) =>
typeof val === 'function' ? val({ command: cmd === 'dev' ? 'dev' : 'preview' }) : val,
// validate
z
.object({
host: z.union([z.string(), z.boolean()]).optional().default(false),
port: z.number().optional().default(3000),
})
.optional()
.default({})
),
style: z
.object({
postcss: z.preprocess(
(val) => resolvePostcssConfig(val, fileProtocolRoot),
@ -209,7 +278,10 @@ export async function validateConfig(userConfig: any, root: string): Promise<Ast
_ctx: { scripts: [], renderers: [], adapter: undefined },
};
// Final-Pass Validation (perform checks that require the full config object)
if (!result.experimentalIntegrations && !result.integrations.every((int) => int.name.startsWith('@astrojs/'))) {
if (
!result.experimental?.integrations &&
!result.integrations.every((int) => int.name.startsWith('@astrojs/'))
) {
throw new Error(
[
`Astro integrations are still experimental.`,
@ -225,51 +297,51 @@ export async function validateConfig(userConfig: any, root: string): Promise<Ast
return result;
}
/** Adds '/' to end of string but doesnt double-up */
function addTrailingSlash(str: string): string {
return str.replace(/\/*$/, '/');
}
/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */
function resolveFlags(flags: Partial<Flags>): CLIFlags {
if (flags.experimentalStaticBuild) {
// eslint-disable-next-line no-console
console.warn(`Passing --experimental-static-build is no longer necessary and is now the default. The flag will be removed in a future version of Astro.`);
}
return {
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
root: typeof flags.root === 'string' ? flags.root : undefined,
site: typeof flags.site === 'string' ? flags.site : undefined,
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
port: typeof flags.port === 'number' ? flags.port : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined,
hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
host: typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
legacyBuild: typeof flags.legacyBuild === 'boolean' ? flags.legacyBuild : false,
host:
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
experimentalSsr: typeof flags.experimentalSsr === 'boolean' ? flags.experimentalSsr : false,
experimentalIntegrations: typeof flags.experimentalIntegrations === 'boolean' ? flags.experimentalIntegrations : false,
experimentalIntegrations:
typeof flags.experimentalIntegrations === 'boolean' ? flags.experimentalIntegrations : false,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false,
};
}
/** Merge CLI flags & user config object (CLI flags take priority) */
function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
astroConfig.buildOptions = astroConfig.buildOptions || {};
astroConfig.devOptions = astroConfig.devOptions || {};
if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
if (typeof flags.host === 'string' || typeof flags.host === 'boolean') astroConfig.devOptions.host = flags.host;
if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
if (typeof flags.legacyBuild === 'boolean') astroConfig.buildOptions.legacyBuild = flags.legacyBuild;
if (typeof flags.experimentalSsr === 'boolean') astroConfig.buildOptions.experimentalSsr = flags.experimentalSsr;
if (typeof flags.experimentalIntegrations === 'boolean') astroConfig.experimentalIntegrations = flags.experimentalIntegrations;
if (typeof flags.drafts === 'boolean') astroConfig.buildOptions.drafts = flags.drafts;
function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags, cmd: string) {
astroConfig.server = astroConfig.server || {};
astroConfig.experimental = astroConfig.experimental || {};
astroConfig.markdown = astroConfig.markdown || {};
if (typeof flags.site === 'string') astroConfig.site = flags.site;
if (typeof flags.experimentalSsr === 'boolean')
astroConfig.experimental.ssr = flags.experimentalSsr;
if (typeof flags.experimentalIntegrations === 'boolean')
astroConfig.experimental.integrations = flags.experimentalIntegrations;
if (typeof flags.drafts === 'boolean') astroConfig.markdown.drafts = flags.drafts;
if (typeof flags.port === 'number') {
// @ts-expect-error astroConfig.server may be a function, but TS doesn't like attaching properties to a function.
// TODO: Come back here and refactor to remove this expected error.
astroConfig.server.port = flags.port;
}
if (typeof flags.host === 'string' || typeof flags.host === 'boolean') {
// @ts-expect-error astroConfig.server may be a function, but TS doesn't like attaching properties to a function.
// TODO: Come back here and refactor to remove this expected error.
astroConfig.server.host = flags.host;
}
return astroConfig;
}
interface LoadConfigOptions {
cwd?: string;
flags?: Flags;
cmd: string;
validate?: boolean;
}
/**
@ -277,7 +349,9 @@ interface LoadConfigOptions {
* Note: currently the same as loadConfig but only returns the `filePath`
* instead of the resolved config
*/
export async function resolveConfigURL(configOptions: LoadConfigOptions): Promise<URL | undefined> {
export async function resolveConfigURL(
configOptions: Pick<LoadConfigOptions, 'cwd' | 'flags'>
): Promise<URL | undefined> {
const root = configOptions.cwd ? path.resolve(configOptions.cwd) : process.cwd();
const flags = resolveFlags(configOptions.flags || {});
let userConfigPath: string | undefined;
@ -311,18 +385,27 @@ export async function loadConfig(configOptions: LoadConfigOptions): Promise<Astr
if (config) {
userConfig = config.value;
}
return resolveConfig(userConfig, root, flags);
return resolveConfig(userConfig, root, flags, configOptions.cmd);
}
/** Attempt to resolve an Astro configuration object. Normalize, validate, and return. */
export async function resolveConfig(userConfig: AstroUserConfig, root: string, flags: CLIFlags = {}): Promise<AstroConfig> {
const mergedConfig = mergeCLIFlags(userConfig, flags);
const validatedConfig = await validateConfig(mergedConfig, root);
export async function resolveConfig(
userConfig: AstroUserConfig,
root: string,
flags: CLIFlags = {},
cmd: string
): Promise<AstroConfig> {
const mergedConfig = mergeCLIFlags(userConfig, flags, cmd);
const validatedConfig = await validateConfig(mergedConfig, root, cmd);
return validatedConfig;
}
function mergeConfigRecursively(defaults: Record<string, any>, overrides: Record<string, any>, rootPath: string) {
function mergeConfigRecursively(
defaults: Record<string, any>,
overrides: Record<string, any>,
rootPath: string
) {
const merged: Record<string, any> = { ...defaults };
for (const key in overrides) {
const value = overrides[key];
@ -357,6 +440,10 @@ function mergeConfigRecursively(defaults: Record<string, any>, overrides: Record
return merged;
}
export function mergeConfig(defaults: Record<string, any>, overrides: Record<string, any>, isRoot = true): Record<string, any> {
export function mergeConfig(
defaults: Record<string, any>,
overrides: Record<string, any>,
isRoot = true
): Record<string, any> {
return mergeConfigRecursively(defaults, overrides, isRoot ? '' : '.');
}

View file

@ -43,12 +43,15 @@ interface CreateViteOptions {
}
/** Return a common starting point for all Vite actions */
export async function createVite(commandConfig: ViteConfigWithSSR, { astroConfig, logging, mode }: CreateViteOptions): Promise<ViteConfigWithSSR> {
export async function createVite(
commandConfig: ViteConfigWithSSR,
{ astroConfig, logging, mode }: CreateViteOptions
): Promise<ViteConfigWithSSR> {
// Scan for any third-party Astro packages. Vite needs these to be passed to `ssr.noExternal`.
const astroPackages = await getAstroPackages(astroConfig);
// Start with the Vite configuration that Astro core needs
const commonConfig: ViteConfigWithSSR = {
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.projectRoot)), // using local caches allows Astro to be used in monorepos, etc.
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.root)), // using local caches allows Astro to be used in monorepos, etc.
clearScreen: false, // we want to control the output, not Vite
logLevel: 'warn', // log warnings and errors only
optimizeDeps: {
@ -67,19 +70,22 @@ export async function createVite(commandConfig: ViteConfigWithSSR, { astroConfig
astroPostprocessVitePlugin({ config: astroConfig }),
astroIntegrationsContainerPlugin({ config: astroConfig }),
],
publicDir: fileURLToPath(astroConfig.public),
root: fileURLToPath(astroConfig.projectRoot),
publicDir: fileURLToPath(astroConfig.publicDir),
root: fileURLToPath(astroConfig.root),
envPrefix: 'PUBLIC_',
server: {
force: true, // force dependency rebuild (TODO: enabled only while next is unstable; eventually only call in "production" mode?)
hmr: process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production' ? false : undefined, // disable HMR for test
hmr:
process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production'
? false
: undefined, // disable HMR for test
// handle Vite URLs
proxy: {
// add proxies here
},
},
css: {
postcss: astroConfig.styleOptions.postcss || {},
postcss: astroConfig.style.postcss || {},
},
resolve: {
alias: {
@ -109,8 +115,8 @@ export async function createVite(commandConfig: ViteConfigWithSSR, { astroConfig
// Scans `projectRoot` for third-party Astro packages that could export an `.astro` file
// `.astro` files need to be built by Vite, so these should use `noExternal`
async function getAstroPackages({ projectRoot }: AstroConfig): Promise<string[]> {
const pkgUrl = new URL('./package.json', projectRoot);
async function getAstroPackages({ root }: AstroConfig): Promise<string[]> {
const pkgUrl = new URL('./package.json', root);
const pkgPath = fileURLToPath(pkgUrl);
if (!fs.existsSync(pkgPath)) return [];
@ -123,11 +129,15 @@ async function getAstroPackages({ projectRoot }: AstroConfig): Promise<string[]>
if (isCommonNotAstro(dep)) return false;
// Attempt: package is named `astro-something`. ✅ Likely a community package
if (/^astro\-/.test(dep)) return true;
const depPkgUrl = new URL(`./node_modules/${dep}/package.json`, projectRoot);
const depPkgUrl = new URL(`./node_modules/${dep}/package.json`, root);
const depPkgPath = fileURLToPath(depPkgUrl);
if (!fs.existsSync(depPkgPath)) return false;
const { dependencies = {}, peerDependencies = {}, keywords = [] } = JSON.parse(fs.readFileSync(depPkgPath, 'utf-8'));
const {
dependencies = {},
peerDependencies = {},
keywords = [],
} = JSON.parse(fs.readFileSync(depPkgPath, 'utf-8'));
// Attempt: package relies on `astro`. ✅ Definitely an Astro package
if (peerDependencies.astro || dependencies.astro) return true;
// Attempt: package is tagged with `astro` or `astro-component`. ✅ Likely a community package
@ -181,7 +191,10 @@ function isCommonNotAstro(dep: string): boolean {
return (
COMMON_DEPENDENCIES_NOT_ASTRO.includes(dep) ||
COMMON_PREFIXES_NOT_ASTRO.some(
(prefix) => (prefix.startsWith('@') ? dep.startsWith(prefix) : dep.substring(dep.lastIndexOf('/') + 1).startsWith(prefix)) // check prefix omitting @scope/
(prefix) =>
prefix.startsWith('@')
? dep.startsWith(prefix)
: dep.substring(dep.lastIndexOf('/') + 1).startsWith(prefix) // check prefix omitting @scope/
)
);
}

View file

@ -2,13 +2,18 @@ import type { AddressInfo } from 'net';
import { performance } from 'perf_hooks';
import * as vite from 'vite';
import type { AstroConfig } from '../../@types/astro';
import { runHookConfigDone, runHookConfigSetup, runHookServerDone, runHookServerSetup, runHookServerStart } from '../../integrations/index.js';
import {
runHookConfigDone,
runHookConfigSetup,
runHookServerDone,
runHookServerSetup,
runHookServerStart,
} from '../../integrations/index.js';
import { createVite } from '../create-vite.js';
import { info, LogOptions, warn, warnIfUsingExperimentalSSR } from '../logger/core.js';
import { nodeLogOptions } from '../logger/node.js';
import * as msg from '../messages.js';
import { apply as applyPolyfill } from '../polyfill.js';
import { getResolvedHostForVite } from '../util.js';
export interface DevOptions {
logging: LogOptions;
@ -20,15 +25,18 @@ export interface DevServer {
}
/** `astro dev` */
export default async function dev(config: AstroConfig, options: DevOptions = { logging: nodeLogOptions }): Promise<DevServer> {
export default async function dev(
config: AstroConfig,
options: DevOptions = { logging: nodeLogOptions }
): Promise<DevServer> {
const devStart = performance.now();
applyPolyfill();
config = await runHookConfigSetup({ config, command: 'dev' });
const { host, port } = config.server;
const viteConfig = await createVite(
{
mode: 'development',
// TODO: remove call once --hostname is baselined
server: { host: getResolvedHostForVite(config) },
server: { host },
},
{ astroConfig: config, logging: options.logging, mode: 'dev' }
);
@ -36,11 +44,21 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l
warnIfUsingExperimentalSSR(options.logging, config);
const viteServer = await vite.createServer(viteConfig);
runHookServerSetup({ config, server: viteServer });
await viteServer.listen(config.devOptions.port);
await viteServer.listen(port);
const devServerAddressInfo = viteServer.httpServer!.address() as AddressInfo;
const site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
info(options.logging, null, msg.devStart({ startupTime: performance.now() - devStart, config, devServerAddressInfo, site, https: !!viteConfig.server?.https }));
const site = config.site ? new URL(config.base, config.site) : undefined;
info(
options.logging,
null,
msg.devStart({
startupTime: performance.now() - devStart,
config,
devServerAddressInfo,
site,
https: !!viteConfig.server?.https,
})
);
const currentVersion = process.env.PACKAGE_VERSION ?? '0.0.0';
if (currentVersion.includes('-')) {

View file

@ -3,7 +3,10 @@ import type { RenderOptions } from '../render/core';
import { renderEndpoint } from '../../runtime/server/index.js';
import { getParamsAndProps, GetParamsAndPropsError } from '../render/core.js';
export type EndpointOptions = Pick<RenderOptions, 'logging' | 'origin' | 'request' | 'route' | 'routeCache' | 'pathname' | 'route' | 'site' | 'ssr'>;
export type EndpointOptions = Pick<
RenderOptions,
'logging' | 'origin' | 'request' | 'route' | 'routeCache' | 'pathname' | 'route' | 'site' | 'ssr'
>;
type EndpointCallResult =
| {
@ -15,11 +18,16 @@ type EndpointCallResult =
response: Response;
};
export async function call(mod: EndpointHandler, opts: EndpointOptions): Promise<EndpointCallResult> {
export async function call(
mod: EndpointHandler,
opts: EndpointOptions
): Promise<EndpointCallResult> {
const paramsAndPropsResp = await getParamsAndProps({ ...opts, mod: mod as any });
if (paramsAndPropsResp === GetParamsAndPropsError.NoMatchingStaticPath) {
throw new Error(`[getStaticPath] route pattern matched, but no matching static path found. (${opts.pathname})`);
throw new Error(
`[getStaticPath] route pattern matched, but no matching static path found. (${opts.pathname})`
);
}
const [params] = paramsAndPropsResp;

View file

@ -47,7 +47,12 @@ export const levels: Record<LoggerLevel, number> = {
};
/** Full logging API */
export function log(opts: LogOptions, level: LoggerLevel, type: string | null, ...args: Array<any>) {
export function log(
opts: LogOptions,
level: LoggerLevel,
type: string | null,
...args: Array<any>
) {
const logLevel = opts.level;
const dest = opts.dest;
const event: LogMessage = {
@ -120,7 +125,8 @@ if (typeof process !== 'undefined') {
/** Print out a timer message for debug() */
export function timerMessage(message: string, startTime: number = Date.now()) {
let timeDiff = Date.now() - startTime;
let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
let timeDisplay =
timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
return `${message} ${dim(timeDisplay)}`;
}

View file

@ -127,5 +127,8 @@ export const logger = {
export function enableVerboseLogging() {
//debugPackage.enable('*,-babel');
debug('cli', '--verbose flag enabled! Enabling: DEBUG="*,-babel"');
debug('cli', 'Tip: Set the DEBUG env variable directly for more control. Example: "DEBUG=astro:*,vite:* astro build".');
debug(
'cli',
'Tip: Set the DEBUG env variable directly for more control. Example: "DEBUG=astro:*,vite:* astro build".'
);
}

View file

@ -2,24 +2,47 @@
* Dev server messages (organized here to prevent clutter)
*/
import { bold, dim, red, green, underline, yellow, bgYellow, cyan, bgGreen, black, bgRed, bgWhite } from 'kleur/colors';
import {
bold,
dim,
red,
green,
underline,
yellow,
bgYellow,
cyan,
bgGreen,
black,
bgRed,
bgWhite,
} from 'kleur/colors';
import os from 'os';
import type { AddressInfo } from 'net';
import type { AstroConfig } from '../@types/astro';
import { collectErrorMetadata, cleanErrorStack } from './errors.js';
import { ZodError } from 'zod';
import { emoji, getLocalAddress, getResolvedHostForVite, padMultilineString } from './util.js';
import { emoji, getLocalAddress, padMultilineString } from './util.js';
const PREFIX_PADDING = 6;
/** Display */
export function req({ url, statusCode, reqTime }: { url: string; statusCode: number; reqTime?: number }): string {
export function req({
url,
statusCode,
reqTime,
}: {
url: string;
statusCode: number;
reqTime?: number;
}): string {
let color = dim;
if (statusCode >= 500) color = red;
else if (statusCode >= 400) color = yellow;
else if (statusCode >= 300) color = dim;
else if (statusCode >= 200) color = green;
return `${bold(color(`${statusCode}`.padStart(PREFIX_PADDING)))} ${url.padStart(40)} ${reqTime ? dim(Math.round(reqTime) + 'ms') : ''}`.trim();
return `${bold(color(`${statusCode}`.padStart(PREFIX_PADDING)))} ${url.padStart(40)} ${
reqTime ? dim(Math.round(reqTime) + 'ms') : ''
}`.trim();
}
export function reload({ file }: { file: string }): string {
@ -51,19 +74,25 @@ export function devStart({
const networkPrefix = `${dim('┃')} Network `;
const { address: networkAddress, port } = devServerAddressInfo;
const localAddress = getLocalAddress(networkAddress, config);
const networkLogging = getNetworkLogging(config);
const toDisplayUrl = (hostname: string) => `${https ? 'https' : 'http'}://${hostname}:${port}${rootPath}`;
const localAddress = getLocalAddress(networkAddress, config.server.host);
const networkLogging = getNetworkLogging(config.server.host);
const toDisplayUrl = (hostname: string) =>
`${https ? 'https' : 'http'}://${hostname}:${port}${rootPath}`;
let addresses = [];
if (networkLogging === 'none') {
addresses = [`${localPrefix}${bold(cyan(toDisplayUrl(localAddress)))}`];
} else if (networkLogging === 'host-to-expose') {
addresses = [`${localPrefix}${bold(cyan(toDisplayUrl(localAddress)))}`, `${networkPrefix}${dim('use --host to expose')}`];
addresses = [
`${localPrefix}${bold(cyan(toDisplayUrl(localAddress)))}`,
`${networkPrefix}${dim('use --host to expose')}`,
];
} else {
addresses = Object.values(os.networkInterfaces())
.flatMap((networkInterface) => networkInterface ?? [])
.filter((networkInterface) => networkInterface?.address && networkInterface?.family === 'IPv4')
.filter(
(networkInterface) => networkInterface?.address && networkInterface?.family === 'IPv4'
)
.map(({ address }) => {
if (address.includes('127.0.0.1')) {
const displayAddress = address.replace('127.0.0.1', localAddress);
@ -76,7 +105,14 @@ export function devStart({
.sort((msg) => (msg.startsWith(localPrefix) ? -1 : 1));
}
const messages = [`${emoji('🚀 ', '')}${bgGreen(black(` astro `))} ${green(`v${version}`)} ${dim(`started in ${Math.round(startupTime)}ms`)}`, '', ...addresses, ''];
const messages = [
`${emoji('🚀 ', '')}${bgGreen(black(` astro `))} ${green(`v${version}`)} ${dim(
`started in ${Math.round(startupTime)}ms`
)}`,
'',
...addresses,
'',
];
return messages.map((msg) => ` ${msg}`).join('\n');
}
@ -125,10 +161,7 @@ export function portInUse({ port }: { port: number }): string {
const LOCAL_IP_HOSTS = new Set(['localhost', '127.0.0.1']);
export function getNetworkLogging(config: AstroConfig): 'none' | 'host-to-expose' | 'visible' {
// TODO: remove once --hostname is baselined
const host = getResolvedHostForVite(config);
export function getNetworkLogging(host: string | boolean): 'none' | 'host-to-expose' | 'visible' {
if (host === false) {
return 'host-to-expose';
} else if (typeof host === 'string' && LOCAL_IP_HOSTS.has(host)) {
@ -139,8 +172,12 @@ export function getNetworkLogging(config: AstroConfig): 'none' | 'host-to-expose
}
export function formatConfigErrorMessage(err: ZodError) {
const errorList = err.issues.map((issue) => ` ! ${bold(issue.path.join('.'))} ${red(issue.message + '.')}`);
return `${red('[config]')} Astro found issue(s) with your configuration:\n${errorList.join('\n')}`;
const errorList = err.issues.map(
(issue) => ` ! ${bold(issue.path.join('.'))} ${red(issue.message + '.')}`
);
return `${red('[config]')} Astro found issue(s) with your configuration:\n${errorList.join(
'\n'
)}`;
}
export function formatErrorMessage(_err: Error, args: string[] = []): string {
@ -199,7 +236,12 @@ export function printHelp({
let message = [];
if (headline) {
message.push(linebreak(), ` ${bgGreen(black(` ${commandName} `))} ${green(`v${process.env.PACKAGE_VERSION ?? ''}`)} ${headline}`);
message.push(
linebreak(),
` ${bgGreen(black(` ${commandName} `))} ${green(
`v${process.env.PACKAGE_VERSION ?? ''}`
)} ${headline}`
);
}
if (usage) {
@ -207,7 +249,11 @@ export function printHelp({
}
if (commands) {
message.push(linebreak(), title('Commands'), table(commands, { padding: 28, prefix: ' astro ' }));
message.push(
linebreak(),
title('Commands'),
table(commands, { padding: 28, prefix: ' astro ' })
);
}
if (flags) {

View file

@ -24,13 +24,16 @@ export interface PreviewServer {
const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/;
/** The primary dev action */
export default async function preview(config: AstroConfig, { logging }: PreviewOptions): Promise<PreviewServer> {
export default async function preview(
config: AstroConfig,
{ logging }: PreviewOptions
): Promise<PreviewServer> {
const startServerTime = performance.now();
const defaultOrigin = 'http://localhost';
const trailingSlash = config.devOptions.trailingSlash;
const trailingSlash = config.trailingSlash;
/** Base request URL. */
let baseURL = new URL(config.buildOptions.site || '/', defaultOrigin);
const staticFileServer = sirv(fileURLToPath(config.dist), {
let baseURL = new URL(config.base, new URL(config.site || '/', defaultOrigin));
const staticFileServer = sirv(fileURLToPath(config.outDir), {
dev: true,
etag: true,
maxAge: 0,
@ -59,10 +62,13 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
switch (true) {
case hasTrailingSlash && trailingSlash == 'never' && !isRoot:
sendError('Not Found (devOptions.trailingSlash is set to "never")');
sendError('Not Found (trailingSlash is set to "never")');
return;
case !hasTrailingSlash && trailingSlash == 'always' && !isRoot && !HAS_FILE_EXTENSION_REGEXP.test(pathname):
sendError('Not Found (devOptions.trailingSlash is set to "always")');
case !hasTrailingSlash &&
trailingSlash == 'always' &&
!isRoot &&
!HAS_FILE_EXTENSION_REGEXP.test(pathname):
sendError('Not Found (trailingSlash is set to "always")');
return;
default: {
// HACK: rewrite req.url so that sirv finds the file
@ -73,8 +79,8 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
}
});
let { port } = config.devOptions;
const host = getResolvedHostForHttpServer(config);
let { port } = config.server;
const host = getResolvedHostForHttpServer(config.server.host);
let httpServer: http.Server;
@ -87,7 +93,17 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
httpServer = server.listen(port, host, async () => {
if (!showedListenMsg) {
const devServerAddressInfo = server.address() as AddressInfo;
info(logging, null, msg.devStart({ startupTime: performance.now() - timerStart, config, devServerAddressInfo, https: false, site: baseURL }));
info(
logging,
null,
msg.devStart({
startupTime: performance.now() - timerStart,
config,
devServerAddressInfo,
https: false,
site: baseURL,
})
);
}
showedListenMsg = true;
resolve();

View file

@ -1,17 +1,13 @@
import type { AstroConfig } from '../../@types/astro';
export function getResolvedHostForHttpServer(config: AstroConfig) {
const { host, hostname } = config.devOptions;
if (host === false && hostname === 'localhost') {
export function getResolvedHostForHttpServer(host: string | boolean) {
if (host === false) {
// Use a secure default
return '127.0.0.1';
} else if (host === true) {
// If passed --host in the CLI without arguments
return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
} else if (typeof host === 'string') {
return host;
} else {
return hostname;
return host;
}
}

View file

@ -1,4 +1,13 @@
import type { ComponentInstance, EndpointHandler, MarkdownRenderOptions, Params, Props, SSRLoadedRenderer, RouteData, SSRElement } from '../../@types/astro';
import type {
ComponentInstance,
EndpointHandler,
MarkdownRenderOptions,
Params,
Props,
SSRLoadedRenderer,
RouteData,
SSRElement,
} from '../../@types/astro';
import type { LogOptions } from '../logger/core.js';
import { renderHead, renderPage } from '../../runtime/server/index.js';
@ -19,7 +28,9 @@ export const enum GetParamsAndPropsError {
NoMatchingStaticPath,
}
export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise<[Params, Props] | GetParamsAndPropsError> {
export async function getParamsAndProps(
opts: GetParamsAndPropsOptions
): Promise<[Params, Props] | GetParamsAndPropsError> {
const { logging, mod, route, routeCache, pathname, ssr } = opts;
// Handle dynamic routes
let params: Params = {};
@ -73,8 +84,26 @@ export interface RenderOptions {
request: Request;
}
export async function render(opts: RenderOptions): Promise<{ type: 'html'; html: string } | { type: 'response'; response: Response }> {
const { legacyBuild, links, logging, origin, markdownRender, mod, pathname, scripts, renderers, request, resolve, route, routeCache, site, ssr } = opts;
export async function render(
opts: RenderOptions
): Promise<{ type: 'html'; html: string } | { type: 'response'; response: Response }> {
const {
legacyBuild,
links,
logging,
origin,
markdownRender,
mod,
pathname,
scripts,
renderers,
request,
resolve,
route,
routeCache,
site,
ssr,
} = opts;
const paramsAndPropsRes = await getParamsAndProps({
logging,
@ -86,14 +115,18 @@ export async function render(opts: RenderOptions): Promise<{ type: 'html'; html:
});
if (paramsAndPropsRes === GetParamsAndPropsError.NoMatchingStaticPath) {
throw new Error(`[getStaticPath] route pattern matched, but no matching static path found. (${pathname})`);
throw new Error(
`[getStaticPath] route pattern matched, but no matching static path found. (${pathname})`
);
}
const [params, pageProps] = paramsAndPropsRes;
// Validate the page component before rendering the page
const Component = await mod.default;
if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
if (!Component)
throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
if (!Component.isAstroComponentFactory)
throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
const result = createResult({
legacyBuild,
@ -126,7 +159,7 @@ export async function render(opts: RenderOptions): Promise<{ type: 'html'; html:
html = html.replace('<!--astro:head:injected-->', '');
// inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
if (!legacyBuild && !/<!doctype html/i.test(html)) {
if (!/<!doctype html/i.test(html)) {
html = '<!DOCTYPE html>\n' + html;
}

View file

@ -98,7 +98,10 @@ function serializeTag({ tag, attrs, children }: vite.HtmlTagDescriptor, indent =
if (unaryTags.has(tag)) {
return `<${tag}${serializeAttrs(attrs)}>`;
} else {
return `<${tag}${serializeAttrs(attrs)}>${serializeTags(children, incrementIndent(indent))}</${tag}>`;
return `<${tag}${serializeAttrs(attrs)}>${serializeTags(
children,
incrementIndent(indent)
)}</${tag}>`;
}
}

View file

@ -1,6 +1,15 @@
import astroRemark from '@astrojs/markdown-remark';
import { fileURLToPath } from 'url';
import type * as vite from 'vite';
import type { AstroConfig, AstroRenderer, ComponentInstance, RouteData, RuntimeMode, SSRElement, SSRLoadedRenderer } from '../../../@types/astro';
import type {
AstroConfig,
AstroRenderer,
ComponentInstance,
RouteData,
RuntimeMode,
SSRElement,
SSRLoadedRenderer,
} from '../../../@types/astro';
import { LogOptions } from '../../logger/core.js';
import { render as coreRender } from '../core.js';
import { prependForwardSlash } from '../../../core/path.js';
@ -20,7 +29,7 @@ export interface SSROptions {
logging: LogOptions;
/** "development" or "production" */
mode: RuntimeMode;
/** production website, needed for some RSS & Sitemap functions */
/** production website, needed for some RSS functions */
origin: string;
/** the web request (needed for dynamic routes) */
pathname: string;
@ -36,24 +45,38 @@ export interface SSROptions {
export type ComponentPreload = [SSRLoadedRenderer[], ComponentInstance];
export type RenderResponse = { type: 'html'; html: string } | { type: 'response'; response: Response };
export type RenderResponse =
| { type: 'html'; html: string }
| { type: 'response'; response: Response };
const svelteStylesRE = /svelte\?svelte&type=style/;
async function loadRenderer(viteServer: vite.ViteDevServer, renderer: AstroRenderer): Promise<SSRLoadedRenderer> {
async function loadRenderer(
viteServer: vite.ViteDevServer,
renderer: AstroRenderer
): Promise<SSRLoadedRenderer> {
// Vite modules can be out-of-date when using an un-resolved url
// We also encountered inconsistencies when using the resolveUrl and resolveId helpers
// We've found that pulling the ID directly from the urlToModuleMap is the most stable!
const id = viteServer.moduleGraph.urlToModuleMap.get(renderer.serverEntrypoint)?.id ?? renderer.serverEntrypoint;
const id =
viteServer.moduleGraph.urlToModuleMap.get(renderer.serverEntrypoint)?.id ??
renderer.serverEntrypoint;
const mod = (await viteServer.ssrLoadModule(id)) as { default: SSRLoadedRenderer['ssr'] };
return { ...renderer, ssr: mod.default };
}
export async function loadRenderers(viteServer: vite.ViteDevServer, astroConfig: AstroConfig): Promise<SSRLoadedRenderer[]> {
export async function loadRenderers(
viteServer: vite.ViteDevServer,
astroConfig: AstroConfig
): Promise<SSRLoadedRenderer[]> {
return Promise.all(astroConfig._ctx.renderers.map((r) => loadRenderer(viteServer, r)));
}
export async function preload({ astroConfig, filePath, viteServer }: Pick<SSROptions, 'astroConfig' | 'filePath' | 'viteServer'>): Promise<ComponentPreload> {
export async function preload({
astroConfig,
filePath,
viteServer,
}: Pick<SSROptions, 'astroConfig' | 'filePath' | 'viteServer'>): Promise<ComponentPreload> {
// Important: This needs to happen first, in case a renderer provides polyfills.
const renderers = await loadRenderers(viteServer, astroConfig);
// Load the module from the Vite SSR Runtime.
@ -63,21 +86,44 @@ export async function preload({ astroConfig, filePath, viteServer }: Pick<SSROpt
}
/** use Vite to SSR */
export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInstance, ssrOpts: SSROptions): Promise<RenderResponse> {
const { astroConfig, filePath, logging, mode, origin, pathname, request, route, routeCache, viteServer } = ssrOpts;
const legacy = astroConfig.buildOptions.legacyBuild;
export async function render(
renderers: SSRLoadedRenderer[],
mod: ComponentInstance,
ssrOpts: SSROptions
): Promise<RenderResponse> {
const {
astroConfig,
filePath,
logging,
mode,
origin,
pathname,
request,
route,
routeCache,
viteServer,
} = ssrOpts;
// TODO: clean up "legacy" flag passed through helper functions
const isLegacyBuild = false;
// Add hoisted script tags
const scripts = createModuleScriptElementWithSrcSet(!legacy && mod.hasOwnProperty('$$metadata') ? Array.from(mod.$$metadata.hoistedScriptPaths()) : []);
const scripts = createModuleScriptElementWithSrcSet(
!isLegacyBuild && mod.hasOwnProperty('$$metadata')
? Array.from(mod.$$metadata.hoistedScriptPaths())
: []
);
// Inject HMR scripts
if (mod.hasOwnProperty('$$metadata') && mode === 'development' && !legacy) {
if (mod.hasOwnProperty('$$metadata') && mode === 'development' && !isLegacyBuild) {
scripts.add({
props: { type: 'module', src: '/@vite/client' },
children: '',
});
scripts.add({
props: { type: 'module', src: new URL('../../../runtime/client/hmr.js', import.meta.url).pathname },
props: {
type: 'module',
src: new URL('../../../runtime/client/hmr.js', import.meta.url).pathname,
},
children: '',
});
}
@ -93,7 +139,7 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
// Pass framework CSS in as link tags to be appended to the page.
let links = new Set<SSRElement>();
if (!legacy) {
if (!isLegacyBuild) {
[...getStylesForURL(filePath, viteServer)].forEach((href) => {
if (mode === 'development' && svelteStylesRE.test(href)) {
scripts.add({
@ -114,10 +160,11 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
}
let content = await coreRender({
legacyBuild: astroConfig.buildOptions.legacyBuild,
// TODO: Remove this flag once legacyBuild support is removed
legacyBuild: isLegacyBuild,
links,
logging,
markdownRender: astroConfig.markdownOptions.render,
markdownRender: [astroRemark, astroConfig.markdown],
mod,
origin,
pathname,
@ -126,28 +173,21 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
// TODO: Can we pass the hydration code more directly through Vite, so that we
// don't need to copy-paste and maintain Vite's import resolution here?
async resolve(s: string) {
// The legacy build needs these to remain unresolved so that vite HTML
// Can do the resolution. Without this condition the build output will be
// broken in the legacy build. This can be removed once the legacy build is removed.
if (!astroConfig.buildOptions.legacyBuild) {
const [resolvedUrl, resolvedPath] = await viteServer.moduleGraph.resolveUrl(s);
if (resolvedPath.includes('node_modules/.vite')) {
return resolvedPath.replace(/.*?node_modules\/\.vite/, '/node_modules/.vite');
}
// NOTE: This matches the same logic that Vite uses to add the `/@id/` prefix.
if (!resolvedUrl.startsWith('.') && !resolvedUrl.startsWith('/')) {
return '/@id' + prependForwardSlash(resolvedUrl);
}
return '/@fs' + prependForwardSlash(resolvedPath);
} else {
return s;
const [resolvedUrl, resolvedPath] = await viteServer.moduleGraph.resolveUrl(s);
if (resolvedPath.includes('node_modules/.vite')) {
return resolvedPath.replace(/.*?node_modules\/\.vite/, '/node_modules/.vite');
}
// NOTE: This matches the same logic that Vite uses to add the `/@id/` prefix.
if (!resolvedUrl.startsWith('.') && !resolvedUrl.startsWith('/')) {
return '/@id' + prependForwardSlash(resolvedUrl);
}
return '/@fs' + prependForwardSlash(resolvedPath);
},
renderers,
request,
route,
routeCache,
site: astroConfig.buildOptions.site,
site: astroConfig.site ? new URL(astroConfig.base, astroConfig.site).toString() : undefined,
ssr: isBuildingToSSR(astroConfig),
});
@ -159,7 +199,7 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
const tags: vite.HtmlTagDescriptor[] = [];
// dev only: inject Astro HMR client
if (mode === 'development' && legacy) {
if (mode === 'development' && isLegacyBuild) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
@ -171,7 +211,7 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
}
// inject CSS
if (legacy) {
if (isLegacyBuild) {
[...getStylesForURL(filePath, viteServer)].forEach((href) => {
if (mode === 'development' && svelteStylesRE.test(href)) {
tags.push({
@ -196,12 +236,6 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
// add injected tags
let html = injectTags(content.html, tags);
// run transformIndexHtml() in dev to run Vite dev transformations
if (mode === 'development' && astroConfig.buildOptions.legacyBuild) {
const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
}
// inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
if (!/<!doctype html/i.test(html)) {
html = '<!DOCTYPE html>\n' + content;
@ -213,7 +247,10 @@ export async function render(renderers: SSRLoadedRenderer[], mod: ComponentInsta
};
}
export async function ssr(preloadedComponent: ComponentPreload, ssrOpts: SSROptions): Promise<RenderResponse> {
export async function ssr(
preloadedComponent: ComponentPreload,
ssrOpts: SSROptions
): Promise<RenderResponse> {
const [renderers, mod] = preloadedComponent;
return await render(renderers, mod, ssrOpts); // NOTE: without "await", errors wont get caught below
}

View file

@ -1,7 +1,17 @@
import { GetStaticPathsResult, Page, PaginateFunction, Params, Props, RouteData } from '../../@types/astro';
import {
GetStaticPathsResult,
Page,
PaginateFunction,
Params,
Props,
RouteData,
} from '../../@types/astro';
export function generatePaginateFunction(routeMatch: RouteData): PaginateFunction {
return function paginateUtility(data: any[], args: { pageSize?: number; params?: Params; props?: Props } = {}) {
return function paginateUtility(
data: any[],
args: { pageSize?: number; params?: Params; props?: Props } = {}
) {
let { pageSize: _pageSize, params: _params, props: _props } = args;
const pageSize = _pageSize || 10;
const paramName = 'page';
@ -41,8 +51,20 @@ export function generatePaginateFunction(routeMatch: RouteData): PaginateFunctio
lastPage: lastPage,
url: {
current: routeMatch.generate({ ...params }),
next: pageNum === lastPage ? undefined : routeMatch.generate({ ...params, page: String(pageNum + 1) }),
prev: pageNum === 1 ? undefined : routeMatch.generate({ ...params, page: !includesFirstPageNumber && pageNum - 1 === 1 ? undefined : String(pageNum - 1) }),
next:
pageNum === lastPage
? undefined
: routeMatch.generate({ ...params, page: String(pageNum + 1) }),
prev:
pageNum === 1
? undefined
: routeMatch.generate({
...params,
page:
!includesFirstPageNumber && pageNum - 1 === 1
? undefined
: String(pageNum - 1),
}),
},
} as Page,
},

View file

@ -1,5 +1,14 @@
import { bold } from 'kleur/colors';
import type { AstroGlobal, AstroGlobalPartial, MarkdownParser, MarkdownRenderOptions, Params, SSRElement, SSRLoadedRenderer, SSRResult } from '../../@types/astro';
import type {
AstroGlobal,
AstroGlobalPartial,
MarkdownParser,
MarkdownRenderOptions,
Params,
SSRElement,
SSRLoadedRenderer,
SSRResult,
} from '../../@types/astro';
import { renderSlot } from '../../runtime/server/index.js';
import { LogOptions, warn } from '../logger/core.js';
import { createCanonicalURL, isCSSRequest } from './util.js';
@ -45,7 +54,9 @@ class Slots {
if (slots) {
for (const key of Object.keys(slots)) {
if ((this as any)[key] !== undefined) {
throw new Error(`Unable to create a slot named "${key}". "${key}" is a reserved slot name!\nPlease update the name of this slot.`);
throw new Error(
`Unable to create a slot named "${key}". "${key}" is a reserved slot name!\nPlease update the name of this slot.`
);
}
Object.defineProperty(this, key, {
get() {
@ -75,10 +86,14 @@ class Slots {
const expression = getFunctionExpression(component);
if (expression) {
const slot = expression(...args);
return await renderSlot(this.#result, slot).then((res) => (res != null ? String(res) : res));
return await renderSlot(this.#result, slot).then((res) =>
res != null ? String(res) : res
);
}
}
const content = await renderSlot(this.#result, this.#slots[name]).then((res) => (res != null ? String(res) : res));
const content = await renderSlot(this.#result, this.#slots[name]).then((res) =>
res != null ? String(res) : res
);
if (cacheable) this.#cache.set(name, content);
return content;
}
@ -98,7 +113,11 @@ export function createResult(args: CreateResultArgs): SSRResult {
scripts: args.scripts ?? new Set<SSRElement>(),
links: args.links ?? new Set<SSRElement>(),
/** This function returns the `Astro` faux-global */
createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
createAstro(
astroGlobal: AstroGlobalPartial,
props: Record<string, any>,
slots: Record<string, any> | null
) {
const astroSlots = new Slots(result, slots);
const Astro = {
@ -122,10 +141,9 @@ export function createResult(args: CreateResultArgs): SSRResult {
let extra = `This can be replaced with a dynamic import like so: await import("${path}")`;
if (isCSSRequest(path)) {
extra = `It looks like you are resolving styles. If you are adding a link tag, replace with this:
<style global>
@import "${path}";
</style>
---
import "${path}";
---
`;
} else if (isScriptRequest(path)) {
extra = `It looks like you are resolving scripts. If you are adding a script tag, replace with this:
@ -134,7 +152,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
or consider make it a module like so:
<script type="module" hoist>
<script>
import MyModule from "${path}";
</script>
`;
@ -143,7 +161,9 @@ or consider make it a module like so:
warn(
args.logging,
`deprecation`,
`${bold('Astro.resolve()')} is deprecated. We see that you are trying to resolve ${path}.
`${bold(
'Astro.resolve()'
)} is deprecated. We see that you are trying to resolve ${path}.
${extra}`
);
// Intentionally return an empty string so that it is not relied upon.
@ -159,6 +179,7 @@ ${extra}`
// Ensure this API is not exposed to users
enumerable: false,
writable: false,
// TODO: remove 1. markdown parser logic 2. update MarkdownRenderOptions to take a function only
// <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
value: async function (content: string, opts: any) {
let [mdRender, renderOpts] = markdownRender;

View file

@ -1,8 +1,19 @@
import type { ComponentInstance, GetStaticPathsItem, GetStaticPathsResult, GetStaticPathsResultKeyed, Params, RouteData, RSS } from '../../@types/astro';
import type {
ComponentInstance,
GetStaticPathsItem,
GetStaticPathsResult,
GetStaticPathsResultKeyed,
Params,
RouteData,
RSS,
} from '../../@types/astro';
import { LogOptions, warn, debug } from '../logger/core.js';
import { generatePaginateFunction } from './paginate.js';
import { validateGetStaticPathsModule, validateGetStaticPathsResult } from '../routing/validation.js';
import {
validateGetStaticPathsModule,
validateGetStaticPathsResult,
} from '../routing/validation.js';
type RSSFn = (...args: any[]) => any;
@ -19,7 +30,13 @@ interface CallGetStaticPathsOptions {
ssr: boolean;
}
export async function callGetStaticPaths({ isValidate, logging, mod, route, ssr }: CallGetStaticPathsOptions): Promise<RouteCacheEntry> {
export async function callGetStaticPaths({
isValidate,
logging,
mod,
route,
ssr,
}: CallGetStaticPathsOptions): Promise<RouteCacheEntry> {
validateGetStaticPathsModule(mod, { ssr });
const resultInProgress = {
rss: [] as RSS[],
@ -80,7 +97,11 @@ export class RouteCache {
// Warn here so that an unexpected double-call of getStaticPaths()
// isn't invisible and developer can track down the issue.
if (this.cache[route.component]) {
warn(this.logging, 'routeCache', `Internal Warning: route cache overwritten. (${route.component})`);
warn(
this.logging,
'routeCache',
`Internal Warning: route cache overwritten. (${route.component})`
);
}
this.cache[route.component] = entry;
}
@ -98,5 +119,7 @@ export function findPathItemByKey(staticPaths: GetStaticPathsResultKeyed, params
}
debug('findPathItemByKey', `Unexpected cache miss looking for ${paramsKey}`);
matchedStaticPath = staticPaths.find(({ params: _params }) => JSON.stringify(_params) === paramsKey);
matchedStaticPath = staticPaths.find(
({ params: _params }) => JSON.stringify(_params) === paramsKey
);
}

Some files were not shown because too many files have changed in this diff Show more