Merge branch 'main' into feat/images

This commit is contained in:
Princesseuh 2023-02-03 15:59:54 +01:00
commit 21416d07f7
No known key found for this signature in database
GPG key ID: 105BBD6D57F2B0C0
81 changed files with 772 additions and 698 deletions

View file

@ -1,5 +0,0 @@
---
'@astrojs/image': patch
---
`getPicture()` return object with the correct image type

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Error overlay will now show the error's `cause` if available.

View file

@ -0,0 +1,5 @@
---
'@astrojs/solid-js': patch
---
Bump vitefu for peerDep warning with Vite 4

View file

@ -1,5 +0,0 @@
---
'@astrojs/image': minor
---
Allow images from outside srcDir

View file

@ -0,0 +1,5 @@
---
'@astrojs/vercel': patch
---
Added second build step through esbuild, to allow framework defined build (vite build) and target defined bundling (esbuilt step)

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -11,8 +11,8 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"@astrojs/mdx": "^0.15.2", "@astrojs/mdx": "^0.16.0",
"@astrojs/rss": "^2.1.0", "@astrojs/rss": "^2.1.0",
"@astrojs/sitemap": "^1.0.1" "@astrojs/sitemap": "^1.0.1"
} }

View file

@ -15,7 +15,7 @@
], ],
"scripts": {}, "scripts": {},
"devDependencies": { "devDependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "^2.0.0-beta.0" "astro": "^2.0.0-beta.0"

View file

@ -10,7 +10,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"@astrojs/deno": "^4.0.0" "@astrojs/deno": "^4.0.0"

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"preact": "^10.7.3", "preact": "^10.7.3",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"alpinejs": "^3.10.2", "alpinejs": "^3.10.2",
"@astrojs/alpinejs": "^0.1.3", "@astrojs/alpinejs": "^0.1.3",
"@types/alpinejs": "^3.7.0" "@types/alpinejs": "^3.7.0"

View file

@ -11,9 +11,9 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"lit": "^2.2.5", "lit": "^2.2.5",
"@astrojs/lit": "^1.1.1", "@astrojs/lit": "^1.1.2",
"@webcomponents/template-shadowroot": "^0.1.0" "@webcomponents/template-shadowroot": "^0.1.0"
} }
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"preact": "^10.7.3", "preact": "^10.7.3",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"preact": "^10.7.3", "preact": "^10.7.3",
"@astrojs/preact": "^2.0.1", "@astrojs/preact": "^2.0.1",
"@preact/signals": "^1.1.0" "@preact/signals": "^1.1.0"

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"@astrojs/react": "^2.0.2", "@astrojs/react": "^2.0.2",

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"solid-js": "^1.4.3", "solid-js": "^1.4.3",
"@astrojs/solid-js": "^2.0.1" "@astrojs/solid-js": "^2.0.1"
} }

View file

@ -13,6 +13,6 @@
"dependencies": { "dependencies": {
"svelte": "^3.48.0", "svelte": "^3.48.0",
"@astrojs/svelte": "^2.0.1", "@astrojs/svelte": "^2.0.1",
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"vue": "^3.2.37", "vue": "^3.2.37",
"@astrojs/vue": "^2.0.1" "@astrojs/vue": "^2.0.1"
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^5.0.1", "@astrojs/node": "^5.0.2",
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -15,7 +15,7 @@
], ],
"scripts": {}, "scripts": {},
"devDependencies": { "devDependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "^2.0.0-beta.0" "astro": "^2.0.0-beta.0"

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -12,10 +12,10 @@
"server": "node dist/server/entry.mjs" "server": "node dist/server/entry.mjs"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"svelte": "^3.48.0", "svelte": "^3.48.0",
"@astrojs/svelte": "^2.0.1", "@astrojs/svelte": "^2.0.1",
"@astrojs/node": "^5.0.1", "@astrojs/node": "^5.0.2",
"concurrently": "^7.2.1", "concurrently": "^7.2.1",
"unocss": "^0.15.6", "unocss": "^0.15.6",
"vite-imagetools": "^4.0.4" "vite-imagetools": "^4.0.4"

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"@astrojs/markdown-remark": "^2.0.1", "@astrojs/markdown-remark": "^2.0.1",
"hast-util-select": "5.0.1", "hast-util-select": "5.0.1",
"rehype-autolink-headings": "^6.1.1", "rehype-autolink-headings": "^6.1.1",

View file

@ -11,6 +11,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4" "astro": "^2.0.5"
} }
} }

View file

@ -11,9 +11,9 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"preact": "^10.6.5", "preact": "^10.6.5",
"@astrojs/preact": "^2.0.1", "@astrojs/preact": "^2.0.1",
"@astrojs/mdx": "^0.15.2" "@astrojs/mdx": "^0.16.0"
} }
} }

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"preact": "^10.7.3", "preact": "^10.7.3",
"@astrojs/preact": "^2.0.1", "@astrojs/preact": "^2.0.1",
"nanostores": "^0.5.12", "nanostores": "^0.5.12",

View file

@ -11,10 +11,10 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^0.15.2", "@astrojs/mdx": "^0.16.0",
"@astrojs/tailwind": "^3.0.1", "@astrojs/tailwind": "^3.0.1",
"@types/canvas-confetti": "^1.4.3", "@types/canvas-confetti": "^1.4.3",
"astro": "^2.0.4", "astro": "^2.0.5",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"canvas-confetti": "^1.5.1", "canvas-confetti": "^1.5.1",
"postcss": "^8.4.14", "postcss": "^8.4.14",

View file

@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"vite-plugin-pwa": "0.11.11", "vite-plugin-pwa": "0.11.11",
"workbox-window": "^6.5.3" "workbox-window": "^6.5.3"
} }

View file

@ -12,7 +12,7 @@
"test": "vitest" "test": "vitest"
}, },
"dependencies": { "dependencies": {
"astro": "^2.0.4", "astro": "^2.0.5",
"vitest": "^0.20.3" "vitest": "^0.20.3"
} }
} }

View file

@ -1,5 +1,17 @@
# astro # astro
## 2.0.5
### Patch Changes
- [#6052](https://github.com/withastro/astro/pull/6052) [`9793f19ec`](https://github.com/withastro/astro/commit/9793f19ecd4e64cbf3140454fe52aeee2c22c8c9) Thanks [@mayank99](https://github.com/mayank99)! - Error overlay will now show the error's `cause` if available.
- [#6070](https://github.com/withastro/astro/pull/6070) [`f91615f5c`](https://github.com/withastro/astro/commit/f91615f5c04fde36f115dad9110dd75254efd61d) Thanks [@AirBorne04](https://github.com/AirBorne04)! - \* safe guard against TextEncode.encode(HTMLString) [errors on vercel edge]
- safe guard against html.replace when html is undefined
- [#6064](https://github.com/withastro/astro/pull/6064) [`2fb72c887`](https://github.com/withastro/astro/commit/2fb72c887f71c0a69ab512870d65b8c867774766) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Apply MDX `components` export when rendering as a content collection entry
## 2.0.4 ## 2.0.4
### Patch Changes ### Patch Changes

View file

@ -0,0 +1,35 @@
import { LitElement, html } from 'lit';
export default class NonDeferredCounter extends LitElement {
static get properties() {
return {
count: {
type: Number,
// All set properties are reflected to attributes so its hydration is
// not deferred.
reflect: true,
},
};
}
constructor() {
super();
this.count = 0;
}
increment() {
this.count++;
}
render() {
return html`
<div>
<p>Count: ${this.count}</p>
<button type="button" @click=${this.increment}>Increment</button>
</div>
`;
}
}
customElements.define('non-deferred-counter', NonDeferredCounter);

View file

@ -1,8 +1,9 @@
--- ---
import MyCounter from '../components/Counter.js'; import MyCounter from '../components/Counter.js';
import NonDeferredCounter from '../components/NonDeferredCounter.js';
const someProps = { const someProps = {
count: 0, count: 10,
}; };
--- ---
@ -15,6 +16,9 @@ const someProps = {
<h1>Hello, client:idle!</h1> <h1>Hello, client:idle!</h1>
</MyCounter> </MyCounter>
<NonDeferredCounter id="non-deferred" client:idle {...someProps}>
</NonDeferredCounter>
<MyCounter id="client-load" {...someProps} client:load> <MyCounter id="client-load" {...someProps} client:load>
<h1>Hello, client:load!</h1> <h1>Hello, client:load!</h1>
</MyCounter> </MyCounter>

View file

@ -2,7 +2,7 @@
import MyCounter from '../components/Counter.js'; import MyCounter from '../components/Counter.js';
const someProps = { const someProps = {
count: 0, count: 10,
}; };
--- ---

View file

@ -2,7 +2,7 @@
import MyCounter from '../components/Counter.js'; import MyCounter from '../components/Counter.js';
const someProps = { const someProps = {
count: 0, count: 10,
}; };
--- ---

View file

@ -32,12 +32,25 @@ test.describe('Lit components', () => {
await expect(counter).toHaveCount(1); await expect(counter).toHaveCount(1);
const count = counter.locator('p'); const count = counter.locator('p');
await expect(count, 'initial count is 0').toHaveText('Count: 0'); await expect(count, 'initial count is 10').toHaveText('Count: 10');
const inc = counter.locator('button'); const inc = counter.locator('button');
await inc.click(); await inc.click();
await expect(count, 'count incremented by 1').toHaveText('Count: 1'); await expect(count, 'count incremented by 1').toHaveText('Count: 11');
});
t('non-deferred attribute serialization', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
const counter = page.locator('#non-deferred');
const count = counter.locator('p');
await expect(count, 'initial count is 10').toHaveText('Count: 10');
const inc = counter.locator('button');
await inc.click();
await expect(count, 'count incremented by 1').toHaveText('Count: 11');
}); });
t('client:load', async ({ page, astro }) => { t('client:load', async ({ page, astro }) => {
@ -47,12 +60,12 @@ test.describe('Lit components', () => {
await expect(counter, 'component is visible').toBeVisible(); await expect(counter, 'component is visible').toBeVisible();
const count = counter.locator('p'); const count = counter.locator('p');
await expect(count, 'initial count is 0').toHaveText('Count: 0'); await expect(count, 'initial count is 10').toHaveText('Count: 10');
const inc = counter.locator('button'); const inc = counter.locator('button');
await inc.click(); await inc.click();
await expect(count, 'count incremented by 1').toHaveText('Count: 1'); await expect(count, 'count incremented by 1').toHaveText('Count: 11');
}); });
t('client:visible', async ({ page, astro }) => { t('client:visible', async ({ page, astro }) => {
@ -64,12 +77,12 @@ test.describe('Lit components', () => {
await expect(counter, 'component is visible').toBeVisible(); await expect(counter, 'component is visible').toBeVisible();
const count = counter.locator('p'); const count = counter.locator('p');
await expect(count, 'initial count is 0').toHaveText('Count: 0'); await expect(count, 'initial count is 10').toHaveText('Count: 10');
const inc = counter.locator('button'); const inc = counter.locator('button');
await inc.click(); await inc.click();
await expect(count, 'count incremented by 1').toHaveText('Count: 1'); await expect(count, 'count incremented by 1').toHaveText('Count: 11');
}); });
t('client:media', async ({ page, astro }) => { t('client:media', async ({ page, astro }) => {
@ -79,18 +92,18 @@ test.describe('Lit components', () => {
await expect(counter, 'component is visible').toBeVisible(); await expect(counter, 'component is visible').toBeVisible();
const count = counter.locator('p'); const count = counter.locator('p');
await expect(count, 'initial count is 0').toHaveText('Count: 0'); await expect(count, 'initial count is 10').toHaveText('Count: 10');
const inc = counter.locator('button'); const inc = counter.locator('button');
await inc.click(); await inc.click();
await expect(count, 'component not hydrated yet').toHaveText('Count: 0'); await expect(count, 'component not hydrated yet').toHaveText('Count: 10');
// Reset the viewport to hydrate the component (max-width: 50rem) // Reset the viewport to hydrate the component (max-width: 50rem)
await page.setViewportSize({ width: 414, height: 1124 }); await page.setViewportSize({ width: 414, height: 1124 });
await inc.click(); await inc.click();
await expect(count, 'count incremented by 1').toHaveText('Count: 1'); await expect(count, 'count incremented by 1').toHaveText('Count: 11');
}); });
t.skip('HMR', async ({ page, astro }) => { t.skip('HMR', async ({ page, astro }) => {

View file

@ -1,6 +1,6 @@
{ {
"name": "astro", "name": "astro",
"version": "2.0.4", "version": "2.0.5",
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
"type": "module", "type": "module",
"author": "withastro", "author": "withastro",

View file

@ -1,3 +1,4 @@
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { prependForwardSlash } from '../core/path.js'; import { prependForwardSlash } from '../core/path.js';
import { import {
@ -120,21 +121,32 @@ async function render({
id: string; id: string;
collectionToRenderEntryMap: CollectionToEntryMap; collectionToRenderEntryMap: CollectionToEntryMap;
}) { }) {
const lazyImport = collectionToRenderEntryMap[collection]?.[id]; const UnexpectedRenderError = new AstroError({
if (!lazyImport) throw new Error(`${String(collection)}${String(id)} does not exist.`); ...AstroErrorData.UnknownContentCollectionError,
message: `Unexpected error while rendering ${String(collection)}${String(id)}.`,
});
const mod = await lazyImport(); const lazyImport = collectionToRenderEntryMap[collection]?.[id];
if (typeof lazyImport !== 'function') throw UnexpectedRenderError;
const baseMod = await lazyImport();
if (baseMod == null || typeof baseMod !== 'object') throw UnexpectedRenderError;
const { collectedStyles, collectedLinks, collectedScripts, getMod } = baseMod;
if (typeof getMod !== 'function') throw UnexpectedRenderError;
const mod = await getMod();
if (mod == null || typeof mod !== 'object') throw UnexpectedRenderError;
const Content = createComponent({ const Content = createComponent({
factory(result, props, slots) { factory(result, baseProps, slots) {
let styles = '', let styles = '',
links = '', links = '',
scripts = ''; scripts = '';
if (Array.isArray(mod?.collectedStyles)) { if (Array.isArray(collectedStyles)) {
styles = mod.collectedStyles.map((style: any) => renderStyleElement(style)).join(''); styles = collectedStyles.map((style: any) => renderStyleElement(style)).join('');
} }
if (Array.isArray(mod?.collectedLinks)) { if (Array.isArray(collectedLinks)) {
links = mod.collectedLinks links = collectedLinks
.map((link: any) => { .map((link: any) => {
return renderUniqueStylesheet(result, { return renderUniqueStylesheet(result, {
href: prependForwardSlash(link), href: prependForwardSlash(link),
@ -142,8 +154,17 @@ async function render({
}) })
.join(''); .join('');
} }
if (Array.isArray(mod?.collectedScripts)) { if (Array.isArray(collectedScripts)) {
scripts = mod.collectedScripts.map((script: any) => renderScriptElement(script)).join(''); scripts = collectedScripts.map((script: any) => renderScriptElement(script)).join('');
}
let props = baseProps;
// Auto-apply MDX components export
if (id.endsWith('mdx')) {
props = {
components: mod.components ?? {},
...baseProps,
};
} }
return createHeadAndContent( return createHeadAndContent(

View file

@ -36,7 +36,9 @@ export function astroContentAssetPropagationPlugin({ mode }: { mode: string }):
if (isPropagatedAsset(id)) { if (isPropagatedAsset(id)) {
const basePath = id.split('?')[0]; const basePath = id.split('?')[0];
const code = ` const code = `
export { Content, getHeadings, frontmatter } from ${JSON.stringify(basePath)}; export async function getMod() {
return import(${JSON.stringify(basePath)});
}
export const collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)}; export const collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)};
export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)}; export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)}; export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)};

View file

@ -419,7 +419,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati
* @docs * @docs
* @message * @message
* **Example error messages:**<br/> * **Example error messages:**<br/>
* InvalidComponentArgs: Invalid arguments passed to <MyAstroComponent> component. * InvalidComponentArgs: Invalid arguments passed to `<MyAstroComponent>` component.
* @description * @description
* Astro components cannot be rendered manually via a function call, such as `Component()` or `{items.map(Component)}`. Prefer the component syntax `<Component />` or `{items.map(item => <Component {...item} />)}`. * Astro components cannot be rendered manually via a function call, such as `Component()` or `{items.map(Component)}`. Prefer the component syntax `<Component />` or `{items.map(item => <Component {...item} />)}`.
*/ */
@ -635,6 +635,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati
}, },
/** /**
* @docs * @docs
* @message `COLLECTION_NAME` `ENTRY_ID` has an invalid slug. `slug` must be a string.
* @see * @see
* - [The reserved entry `slug` field](https://docs.astro.build/en/guides/content-collections/) * - [The reserved entry `slug` field](https://docs.astro.build/en/guides/content-collections/)
* @description * @description
@ -652,6 +653,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati
}, },
/** /**
* @docs * @docs
* @message A content collection schema should not contain `slug` since it is reserved for slug generation. Remove this from your `COLLECTION_NAME` collection schema.
* @see * @see
* - [The reserved entry `slug` field](https://docs.astro.build/en/guides/content-collections/) * - [The reserved entry `slug` field](https://docs.astro.build/en/guides/content-collections/)
* @description * @description

View file

@ -93,5 +93,7 @@ export function chunkToByteArray(
if (chunk instanceof Uint8Array) { if (chunk instanceof Uint8Array) {
return chunk as Uint8Array; return chunk as Uint8Array;
} }
return encoder.encode(stringifyChunk(result, chunk)); // stringify chunk might return a HTMLString
let stringified = stringifyChunk(result, chunk);
return encoder.encode(stringified.toString());
} }

View file

@ -261,8 +261,10 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
if (isPage || renderer?.name === 'astro:jsx') { if (isPage || renderer?.name === 'astro:jsx') {
yield html; yield html;
} else { } else if (html && html.length > 0) {
yield markHTMLString(html.replace(/\<\/?astro-slot\>/g, '')); yield markHTMLString(html.replace(/\<\/?astro-slot\>/g, ''));
} else {
yield '';
} }
})(); })();
} }

View file

@ -71,6 +71,15 @@ describe('Content Collections - render()', () => {
'`WithScripts.astro` hoisted script included unexpectedly.' '`WithScripts.astro` hoisted script included unexpectedly.'
).to.be.undefined; ).to.be.undefined;
}); });
it('Applies MDX components export', async () => {
const html = await fixture.readFile('/launch-week-components-export/index.html');
const $ = cheerio.load(html);
const h2 = $('h2');
expect(h2).to.have.a.lengthOf(1);
expect(h2.attr('data-components-export-applied')).to.equal('true');
});
}); });
describe('Build - SSR', () => { describe('Build - SSR', () => {
@ -110,6 +119,18 @@ describe('Content Collections - render()', () => {
// Includes styles // Includes styles
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0); expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
}); });
it('Applies MDX components export', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/launch-week-components-export');
const response = await app.render(request);
const html = await response.text();
const $ = cheerio.load(html);
const h2 = $('h2');
expect(h2).to.have.a.lengthOf(1);
expect(h2.attr('data-components-export-applied')).to.equal('true');
});
}); });
describe('Dev - SSG', () => { describe('Dev - SSG', () => {
@ -162,5 +183,17 @@ describe('Content Collections - render()', () => {
// Includes inline script // Includes inline script
expect($('script[data-is-inline]')).to.have.a.lengthOf(1); expect($('script[data-is-inline]')).to.have.a.lengthOf(1);
}); });
it('Applies MDX components export', async () => {
const response = await fixture.fetch('/launch-week-components-export', { method: 'GET' });
expect(response.status).to.equal(200);
const html = await response.text();
const $ = cheerio.load(html);
const h2 = $('h2');
expect(h2).to.have.a.lengthOf(1);
expect(h2.attr('data-components-export-applied')).to.equal('true');
});
}); });
}); });

View file

@ -23,7 +23,7 @@ describe('Custom Elements', () => {
expect($('my-element')).to.have.lengthOf(1); expect($('my-element')).to.have.lengthOf(1);
// test 2: shadow rendered // test 2: shadow rendered
expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1); expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
}); });
it('Works with exported tagName', async () => { it('Works with exported tagName', async () => {
@ -34,7 +34,7 @@ describe('Custom Elements', () => {
expect($('my-element')).to.have.lengthOf(1); expect($('my-element')).to.have.lengthOf(1);
// test 2: shadow rendered // test 2: shadow rendered
expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1); expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
}); });
it.skip('Hydration works with exported tagName', async () => { it.skip('Hydration works with exported tagName', async () => {
@ -46,7 +46,7 @@ describe('Custom Elements', () => {
expect($('my-element')).to.have.lengthOf(1); expect($('my-element')).to.have.lengthOf(1);
// test 2: shadow rendered // test 2: shadow rendered
expect($('my-element template[shadowroot=open]')).to.have.lengthOf(1); expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
// Hydration // Hydration
// test 3: Component and polyfill scripts bundled separately // test 3: Component and polyfill scripts bundled separately

View file

@ -0,0 +1,4 @@
---
---
<h2 data-components-export-applied="true"><slot /></h2>

View file

@ -0,0 +1,18 @@
---
title: 'Launch week!'
description: 'Join us for the exciting launch of SPACE BLOG'
publishedDate: 'Sat May 21 2022 00:00:00 GMT-0400 (Eastern Daylight Time)'
tags: ['announcement']
---
import H2 from '../../../components/H2.astro';
export const components = { h2: H2 };
Join us for the space blog launch!
## Details
- THIS THURSDAY
- Houston, TX
- Dress code: **interstellar casual** ✨

View file

@ -0,0 +1,14 @@
---
import { getEntryBySlug } from 'astro:content';
const entry = await getEntryBySlug('blog', 'promo/launch-week-components-export');
const { Content } = await entry.render();
---
<html>
<head>
<title>Launch Week</title>
</head>
<body>
<Content />
</body>
</html>

View file

@ -18,7 +18,7 @@ function renderToStaticMarkup(component, props, innerHTML) {
const Component = getConstructor(component); const Component = getConstructor(component);
const el = new Component(); const el = new Component();
el.connectedCallback(); el.connectedCallback();
const html = `<${el.localName}><template shadowroot="open">${el.shadowRoot.innerHTML}</template>${el.innerHTML}</${el.localName}>` const html = `<${el.localName}><template shadowroot="open" shadowrootmode="open">${el.shadowRoot.innerHTML}</template>${el.innerHTML}</${el.localName}>`
return { return {
html html
}; };

View file

@ -0,0 +1,23 @@
import { LitElement, html } from 'lit';
import { property, customElement } from 'lit/decorators.js';
@customElement('non-deferred-counter')
export class NonDeferredCounter extends LitElement {
// All set properties are reflected to attributes so its hydration is not
// hydration-deferred should always be set.
@property({ type: Number, reflect: true }) count = 0;
increment() {
this.count++;
}
render() {
return html`
<div>
<p>Count: ${this.count}</p>
<button type="button" @click=${this.increment}>Increment</button>
</div>
`;
}
}

View file

@ -1,5 +1,6 @@
--- ---
import {MyElement} from '../components/my-element.js'; import {MyElement} from '../components/my-element.js';
import {NonDeferredCounter} from '../components/non-deferred-element.js';
--- ---
<html> <html>
@ -8,11 +9,17 @@ import {MyElement} from '../components/my-element.js';
</head> </head>
<body> <body>
<MyElement <MyElement
id="default"
foo="bar" foo="bar"
str-attr={'initialized'} str-attr={'initialized'}
bool={false} bool={false}
obj={{data: 1}} obj={{data: 1}}
reflectedStrProp={'initialized reflected'}> reflectedStrProp={'initialized reflected'}>
</MyElement> </MyElement>
<NonDeferredCounter
id="non-deferred"
count={10}
foo="bar">
</NonDeferredCounter>
</body> </body>
</html> </html>

View file

@ -30,36 +30,57 @@ describe('LitElement test', function () {
const $ = cheerio.load(html); const $ = cheerio.load(html);
// test 1: attributes rendered non reactive properties // test 1: attributes rendered non reactive properties
expect($('my-element').attr('foo')).to.equal('bar'); expect($('#default').attr('foo')).to.equal('bar');
// test 2: shadow rendered // test 2: shadow rendered
expect($('my-element').html()).to.include(`<div>Testing...</div>`); expect($('#default').html()).to.include(`<div>Testing...</div>`);
// test 3: string reactive property set // test 3: string reactive property set
expect(stripExpressionMarkers($('my-element').html())).to.include( expect(stripExpressionMarkers($('#default').html())).to.include(
`<div id="str">initialized</div>` `<div id="str">initialized</div>`
); );
// test 4: boolean reactive property correctly set // test 4: boolean reactive property correctly set
// <my-element bool="false"> Lit will equate to true because it uses // <my-element bool="false"> Lit will equate to true because it uses
// this.hasAttribute to determine its value // this.hasAttribute to determine its value
expect(stripExpressionMarkers($('my-element').html())).to.include(`<div id="bool">B</div>`); expect(stripExpressionMarkers($('#default').html())).to.include(`<div id="bool">B</div>`);
// test 5: object reactive property set // test 5: object reactive property set
// by default objects will be stringified to [object Object] // by default objects will be stringified to [object Object]
expect(stripExpressionMarkers($('my-element').html())).to.include( expect(stripExpressionMarkers($('#default').html())).to.include(`<div id="data">data: 1</div>`);
`<div id="data">data: 1</div>`
);
// test 6: reactive properties are not rendered as attributes // test 6: reactive properties are not rendered as attributes
expect($('my-element').attr('obj')).to.equal(undefined); expect($('#default').attr('obj')).to.equal(undefined);
expect($('my-element').attr('bool')).to.equal(undefined); expect($('#default').attr('bool')).to.equal(undefined);
expect($('my-element').attr('str')).to.equal(undefined); expect($('#default').attr('str')).to.equal(undefined);
// test 7: reflected reactive props are rendered as attributes // test 7: reflected reactive props are rendered as attributes
expect($('my-element').attr('reflectedbool')).to.equal(''); expect($('#default').attr('reflectedbool')).to.equal('');
expect($('my-element').attr('reflected-str')).to.equal('default reflected string'); expect($('#default').attr('reflected-str')).to.equal('default reflected string');
expect($('my-element').attr('reflected-str-prop')).to.equal('initialized reflected'); expect($('#default').attr('reflected-str-prop')).to.equal('initialized reflected');
});
it('Sets defer-hydration on element only when necessary', async () => {
// @lit-labs/ssr/ requires Node 13.9 or higher
if (NODE_VERSION < 13.9) {
return;
}
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
// test 1: reflected reactive props are rendered as attributes
expect($('#non-deferred').attr('count')).to.equal('10');
// test 2: non-reactive props are set as attributes
expect($('#non-deferred').attr('foo')).to.equal('bar');
// test 3: components with only reflected reactive props set are not
// deferred because their state can be completely serialized via attributes
expect($('#non-deferred').attr('defer-hydration')).to.equal(undefined);
// test 4: components with non-reflected reactive props set are deferred because
// their state needs to be synced with the server on the client.
expect($('#default').attr('defer-hydration')).to.equal('');
}); });
it('Correctly passes child slots', async () => { it('Correctly passes child slots', async () => {
@ -74,7 +95,7 @@ describe('LitElement test', function () {
const $slottedMyElement = $('#slotted'); const $slottedMyElement = $('#slotted');
const $slottedSlottedMyElement = $('#slotted-slotted'); const $slottedSlottedMyElement = $('#slotted-slotted');
expect($('my-element').length).to.equal(3); expect($('#default').length).to.equal(3);
// Root my-element // Root my-element
expect($rootMyElement.children('.default').length).to.equal(2); expect($rootMyElement.children('.default').length).to.equal(2);

View file

@ -38,7 +38,7 @@
"tiny-glob": "^0.2.9" "tiny-glob": "^0.2.9"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"astro": "workspace:*", "astro": "workspace:*",

View file

@ -32,7 +32,7 @@
"esbuild": "^0.15.18" "esbuild": "^0.15.18"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"astro": "workspace:*", "astro": "workspace:*",

View file

@ -1,5 +1,18 @@
# @astrojs/image # @astrojs/image
## 0.14.0
### Minor Changes
- [#5932](https://github.com/withastro/astro/pull/5932) [`b3e65991f`](https://github.com/withastro/astro/commit/b3e65991f731f5320ba5826c731934a8e8482493) Thanks [@rasendubi](https://github.com/rasendubi)! - Allow images from outside srcDir
### Patch Changes
- [#5894](https://github.com/withastro/astro/pull/5894) [`ca91976ed`](https://github.com/withastro/astro/commit/ca91976edbfd34adbb31096516a266f31d8f6216) Thanks [@ralacerda](https://github.com/ralacerda)! - `getPicture()` return object with the correct image type
- Updated dependencies [[`9793f19ec`](https://github.com/withastro/astro/commit/9793f19ecd4e64cbf3140454fe52aeee2c22c8c9), [`f91615f5c`](https://github.com/withastro/astro/commit/f91615f5c04fde36f115dad9110dd75254efd61d), [`2fb72c887`](https://github.com/withastro/astro/commit/2fb72c887f71c0a69ab512870d65b8c867774766)]:
- astro@2.0.5
## 0.13.1 ## 0.13.1
### Patch Changes ### Patch Changes

View file

@ -259,7 +259,7 @@ color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`,
**Default:** `'cover'` **Default:** `'cover'`
</p> </p>
> This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. > This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. Read more about [how `sharp` resizes images](https://sharp.pixelplumbing.com/api-resize).
How the image should be resized to fit both `height` and `width`. How the image should be resized to fit both `height` and `width`.
@ -271,7 +271,7 @@ How the image should be resized to fit both `height` and `width`.
**Default:** `'centre'` **Default:** `'centre'`
</p> </p>
> This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. > This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. Read more about [how `sharp` resizes images](https://sharp.pixelplumbing.com/api-resize).
Position of the crop when fit is `cover` or `contain`. Position of the crop when fit is `cover` or `contain`.
@ -333,7 +333,7 @@ The list of sizes that should be built for responsive images. This is combined w
```astro ```astro
// Builds three images: 400x400, 800x800, and 1200x1200 // Builds three images: 400x400, 800x800, and 1200x1200
<Picture src={...} widths={[400, 800, 1200]} aspectRatio="1:1" /> <Picture src={...} widths={[400, 800, 1200]} aspectRatio="1:1" alt="descriptive text" />
``` ```
#### aspectRatio #### aspectRatio
@ -392,7 +392,7 @@ color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`,
**Default:** `'cover'` **Default:** `'cover'`
</p> </p>
> This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. > This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. Read more about [how `sharp` resizes images](https://sharp.pixelplumbing.com/api-resize).
How the image should be resized to fit both `height` and `width`. How the image should be resized to fit both `height` and `width`.
@ -406,7 +406,7 @@ How the image should be resized to fit both `height` and `width`.
**Default:** `'centre'` **Default:** `'centre'`
</p> </p>
> This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. > This is not supported by the default Squoosh service. See the [installation section](#installing-sharp-optional) for details on using the `sharp` service instead. Read more about [how `sharp` resizes images](https://sharp.pixelplumbing.com/api-resize).
Position of the crop when fit is `cover` or `contain`. Position of the crop when fit is `cover` or `contain`.
@ -422,7 +422,10 @@ This can be helpful if you need to add preload links to a page's `<head>`.
--- ---
import { getImage } from '@astrojs/image'; import { getImage } from '@astrojs/image';
const { src } = await getImage({src: '../assets/hero.png'}); const { src } = await getImage({
src: import('../assets/hero.png'),
alt: "My hero image"
});
--- ---
<html> <html>

View file

@ -1,7 +1,7 @@
{ {
"name": "@astrojs/image", "name": "@astrojs/image",
"description": "Load and transform images in your Astro site.", "description": "Load and transform images in your Astro site.",
"version": "0.13.1", "version": "0.14.0",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"author": "withastro", "author": "withastro",
@ -63,7 +63,7 @@
"vite": "^4.0.3" "vite": "^4.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4", "astro": "workspace:^2.0.5",
"sharp": ">=0.31.0" "sharp": ">=0.31.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {

View file

@ -1,5 +1,13 @@
# @astrojs/lit # @astrojs/lit
## 1.1.2
### Patch Changes
- [#6080](https://github.com/withastro/astro/pull/6080) [`0db220415`](https://github.com/withastro/astro/commit/0db22041531d981a813a07f4c4e00cfb7ebddd51) Thanks [@e111077](https://github.com/e111077)! - Fixes Lit hydration not having the same reactive values as server (losing state upon hydration)
- [#6055](https://github.com/withastro/astro/pull/6055) [`2567aa48b`](https://github.com/withastro/astro/commit/2567aa48bba8751cf7e10429555f1e85830c9169) Thanks [@e111077](https://github.com/e111077)! - Add forwards compatibility for streaming Declarative Shadow DOM
## 1.1.1 ## 1.1.1
### Patch Changes ### Patch Changes

View file

@ -8,9 +8,13 @@ async function polyfill() {
} }
const polyfillCheckEl = new DOMParser() const polyfillCheckEl = new DOMParser()
.parseFromString(`<p><template shadowroot="open"></template></p>`, 'text/html', { .parseFromString(
`<p><template shadowroot="open" shadowrootmode="open"></template></p>`,
'text/html',
{
includeShadowRoots: true, includeShadowRoots: true,
}) }
)
.querySelector('p'); .querySelector('p');
if (!polyfillCheckEl || !polyfillCheckEl.shadowRoot) { if (!polyfillCheckEl || !polyfillCheckEl.shadowRoot) {

View file

@ -8,7 +8,7 @@ var b = (t, n) => {
function s() { function s() {
if (d === void 0) { if (d === void 0) {
let t = document.createElement('div'); let t = document.createElement('div');
(t.innerHTML = '<div><template shadowroot="open"></template></div>'), (t.innerHTML = '<div><template shadowroot="open" shadowrootmode="open"></template></div>'),
(d = !!t.firstElementChild.shadowRoot); (d = !!t.firstElementChild.shadowRoot);
} }
return d; return d;
@ -80,8 +80,12 @@ async function g() {
window.addEventListener('DOMContentLoaded', () => t(document.body), { once: true }); window.addEventListener('DOMContentLoaded', () => t(document.body), { once: true });
} }
var x = new DOMParser() var x = new DOMParser()
.parseFromString('<p><template shadowroot="open"></template></p>', 'text/html', { .parseFromString(
'<p><template shadowroot="open" shadowrootmode="open"></template></p>',
'text/html',
{
includeShadowRoots: !0, includeShadowRoots: !0,
}) }
)
.querySelector('p'); .querySelector('p');
(!x || !x.shadowRoot) && g(); (!x || !x.shadowRoot) && g();

View file

@ -1,6 +1,6 @@
{ {
"name": "@astrojs/lit", "name": "@astrojs/lit",
"version": "1.1.1", "version": "1.1.2",
"description": "Use Lit components within Astro", "description": "Use Lit components within Astro",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -23,6 +23,7 @@
".": "./dist/index.js", ".": "./dist/index.js",
"./server.js": "./server.js", "./server.js": "./server.js",
"./client-shim.js": "./client-shim.js", "./client-shim.js": "./client-shim.js",
"./dist/client.js": "./dist/client.js",
"./hydration-support.js": "./hydration-support.js", "./hydration-support.js": "./hydration-support.js",
"./package.json": "./package.json" "./package.json": "./package.json"
}, },

View file

@ -36,10 +36,18 @@ function* render(Component, attrs, slots) {
// LitElementRenderer creates a new element instance, so copy over. // LitElementRenderer creates a new element instance, so copy over.
const Ctr = getCustomElementConstructor(tagName); const Ctr = getCustomElementConstructor(tagName);
let shouldDeferHydration = false;
if (attrs) { if (attrs) {
for (let [name, value] of Object.entries(attrs)) { for (let [name, value] of Object.entries(attrs)) {
// check if this is a reactive property const isReactiveProperty = name in Ctr.prototype;
if (name in Ctr.prototype) { const isReflectedReactiveProperty = Ctr.elementProperties.get(name)?.reflect;
// Only defer hydration if we are setting a reactive property that cannot
// be reflected / serialized as a property.
shouldDeferHydration ||= isReactiveProperty && !isReflectedReactiveProperty;
if (isReactiveProperty) {
instance.setProperty(name, value); instance.setProperty(name, value);
} else { } else {
instance.setAttribute(name, value); instance.setAttribute(name, value);
@ -49,12 +57,12 @@ function* render(Component, attrs, slots) {
instance.connectedCallback(); instance.connectedCallback();
yield `<${tagName}`; yield `<${tagName}${shouldDeferHydration ? ' defer-hydration' : ''}`;
yield* instance.renderAttributes(); yield* instance.renderAttributes();
yield `>`; yield `>`;
const shadowContents = instance.renderShadow({}); const shadowContents = instance.renderShadow({});
if (shadowContents !== undefined) { if (shadowContents !== undefined) {
yield '<template shadowroot="open">'; yield '<template shadowroot="open" shadowrootmode="open">';
yield* shadowContents; yield* shadowContents;
yield '</template>'; yield '</template>';
} }

View file

@ -0,0 +1,21 @@
export default (element: HTMLElement) => async (Component: any, props: Record<string, any>) => {
// Get the LitElement element instance (may or may not be upgraded).
const component = element.children[0] as HTMLElement;
// If there is no deferral of hydration, then all reactive properties are
// already serialzied as reflected attributes, or no reactive props were set
if (!component || !component.hasAttribute('defer-hydration')) {
return;
}
// Set properties on the LitElement instance for resuming hydration.
for (let [name, value] of Object.entries(props)) {
// Check if reactive property or class property.
if (name in Component.prototype) {
(component as any)[name] = value;
}
}
// Tell LitElement to resume hydration.
component.removeAttribute('defer-hydration');
};

View file

@ -5,6 +5,7 @@ function getViteConfiguration() {
return { return {
optimizeDeps: { optimizeDeps: {
include: [ include: [
'@astrojs/lit/dist/client.js',
'@astrojs/lit/client-shim.js', '@astrojs/lit/client-shim.js',
'@astrojs/lit/hydration-support.js', '@astrojs/lit/hydration-support.js',
'@webcomponents/template-shadowroot/template-shadowroot.js', '@webcomponents/template-shadowroot/template-shadowroot.js',
@ -34,6 +35,7 @@ export default function (): AstroIntegration {
addRenderer({ addRenderer({
name: '@astrojs/lit', name: '@astrojs/lit',
serverEntrypoint: '@astrojs/lit/server.js', serverEntrypoint: '@astrojs/lit/server.js',
clientEntrypoint: '@astrojs/lit/dist/client.js',
}); });
// Update the vite configuration. // Update the vite configuration.
updateConfig({ updateConfig({

View file

@ -38,7 +38,7 @@ describe('renderToStaticMarkup', () => {
customElements.define(tagName, class extends LitElement {}); customElements.define(tagName, class extends LitElement {});
const render = await renderToStaticMarkup(tagName); const render = await renderToStaticMarkup(tagName);
expect(render).to.deep.equal({ expect(render).to.deep.equal({
html: `<${tagName}><template shadowroot="open"><!--lit-part--><!--/lit-part--></template></${tagName}>`, html: `<${tagName}><template shadowroot="open" shadowrootmode="open"><!--lit-part--><!--/lit-part--></template></${tagName}>`,
}); });
}); });

View file

@ -1,5 +1,15 @@
# @astrojs/mdx # @astrojs/mdx
## 0.16.0
### Minor Changes
- [#6050](https://github.com/withastro/astro/pull/6050) [`2ab32b59e`](https://github.com/withastro/astro/commit/2ab32b59ef0a28d34757f2c2adb9cf2baa86855e) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Fix: load syntax highlighters after MDX remark plugins. This keeps MDX consistent with Astro's markdown behavior.
### Patch Changes
- [#6062](https://github.com/withastro/astro/pull/6062) [`c6cf847bd`](https://github.com/withastro/astro/commit/c6cf847bd0b6bef3c51a5710fba5ca43b11e46f9) Thanks [@delucis](https://github.com/delucis)! - Update MDX README
## 0.15.2 ## 0.15.2
### Patch Changes ### Patch Changes

View file

@ -103,47 +103,19 @@ import rehypeMinifyHtml from 'rehype-minify-html';
export default defineConfig({ export default defineConfig({
integrations: [ integrations: [
mdx({ mdx({
remarkPlugins: [exampleRemarkPlugin], syntaxHighlight: 'shiki',
}), shikiConfig: { theme: 'dracula' },
], remarkPlugins: [remarkToc],
}); rehypePlugins: [rehypeMinifyHtml],
remarkRehype: { footnoteLabel: 'Footnotes' },
gfm: false,
})
]
})
``` ```
…every MDX file will have `customProperty` in its frontmatter! See [our Markdown documentation](https://docs.astro.build/en/guides/markdown-content/#example-injecting-frontmatter) for more usage instructions and a [reading time plugin example](https://docs.astro.build/en/guides/markdown-content/#example-calculate-reading-time). :::caution
MDX does not support passing remark and rehype plugins as a string. You should install, import, and apply the plugin function instead.
### Layouts
Layouts can be applied [in the same way as standard Astro Markdown](https://docs.astro.build/en/guides/markdown-content/#frontmatter-layout). You can add a `layout` to [your frontmatter](#frontmatter) like so:
```yaml
---
layout: '../layouts/BaseLayout.astro'
title: 'My Blog Post'
---
```
Then, you can retrieve all other frontmatter properties from your layout via the `frontmatter` property, and render your MDX using the default [`<slot />`](https://docs.astro.build/en/core-concepts/astro-components/#slots). See [layout props](#layout-props) for a complete list of props available.
```astro title="src/layouts/BaseLayout.astro"
---
const { frontmatter, url } = Astro.props;
---
<html>
<head>
<meta rel="canonical" href={new URL(url, Astro.site).pathname}>
<title>{frontmatter.title}</title>
</head>
<body>
<h1>{frontmatter.title}</h1>
<!-- Rendered MDX will be passed into the default slot. -->
<slot />
</body>
</html>
```
You can set a layouts [`Props` type](/en/guides/typescript/#component-props) with the `MDXLayoutProps` helper.
:::note
`MDXLayoutProps` is the same as the `MarkdownLayoutProps` utility type with `rawContent()` and `compiledContent()` removed (since these are not available for `.mdx` files). Feel free to **use `MarkdownLayoutProps` instead** when sharing a layout across `.md` and `.mdx` files.
::: :::
📚 See the [Markdown Options reference](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) for a complete list of options. 📚 See the [Markdown Options reference](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) for a complete list of options.
@ -157,77 +129,6 @@ MDX will extend [your project's existing Markdown configuration](https://docs.as
For example, say you need to disable GitHub-Flavored Markdown and apply a different set of remark plugins for MDX files. You can apply these options like so, with `extendMarkdownConfig` enabled by default: For example, say you need to disable GitHub-Flavored Markdown and apply a different set of remark plugins for MDX files. You can apply these options like so, with `extendMarkdownConfig` enabled by default:
```html
<blockquote>
<p>A blockquote with <em>some</em> emphasis.</p>
</blockquote>
```
But what if you want to specify your own markup for these blockquotes? In the above example, you could create a custom `<Blockquote />` component (in any language) that either has a `<slot />` component or accepts a `children` prop.
```astro title="src/components/Blockquote.astro"
---
const props = Astro.props;
---
<blockquote {...props} class="bg-blue-50 p-4">
<span class="text-4xl text-blue-600 mb-2"></span>
<slot />
</blockquote>
```
Then in the MDX file you import the component and export it to the `components` export.
```mdx title="src/pages/posts/post-1.mdx" {2}
import Blockquote from '../components/Blockquote.astro';
export const components = { blockquote: Blockquote };
```
Now, writing the standard Markdown blockquote syntax (`>`) will use your custom `<Blockquote />` component instead. No need to use a component in Markdown, or write a remark/rehype plugin! Visit the [MDX website](https://mdxjs.com/table-of-components/) for a full list of HTML elements that can be overwritten as custom components.
#### Custom components with imported `mdx`
When rendering imported MDX content, custom components can be passed via the `components` prop.
Note: An MDX file's exported components will _not_ be used unless you manually import and pass them via the `components` property. See the example below:
```astro title="src/pages/page.astro" "components={{...components, h1: Heading }}"
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---
<Content components={{...components, h1: Heading }} />
```
### Syntax highlighting
The MDX integration respects [your project's `markdown.syntaxHighlight` configuration](https://docs.astro.build/en/guides/markdown-content/#syntax-highlighting).
We will highlight your code blocks with [Shiki](https://github.com/shikijs/shiki) by default. You can customize this highlighter using the `markdown.shikiConfig` option in your `astro.config`. For example, you can apply a different built-in theme like so:
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
shikiConfig: {
theme: 'dracula',
},
},
integrations: [mdx()],
});
```
Visit [our Shiki configuration docs](https://docs.astro.build/en/guides/markdown-content/#shiki-configuration) for more on using Shiki with Astro.
#### Switch to Prism
You can also use the [Prism](https://prismjs.com/) syntax highlighter by setting `markdown.syntaxHighlight` to `'prism'` in your `astro.config` like so:
__`astro.config.mjs`__ __`astro.config.mjs`__
```js ```js
@ -240,108 +141,17 @@ export default defineConfig({
remarkPlugins: [remarkPlugin1], remarkPlugins: [remarkPlugin1],
gfm: true, gfm: true,
}, },
integrations: [mdx()],
});
```
This applies a minimal Prism renderer with added support for `astro` code blocks. Visit [our "Prism configuration" docs](https://docs.astro.build/en/guides/markdown-content/#prism-configuration) for more on using Prism with Astro.
#### Switch to a custom syntax highlighter
You may want to apply your own syntax highlighter too. If your highlighter offers a remark or rehype plugin, you can flip off our syntax highlighting by setting `markdown.syntaxHighlight: false` and wiring up your plugin. For example, say you want to apply [Shiki Twoslash's remark plugin](https://www.npmjs.com/package/remark-shiki-twoslash):
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import shikiTwoslash from 'remark-shiki-twoslash';
export default defineConfig({
markdown: {
syntaxHighlight: false,
},
integrations: [ integrations: [
mdx({ mdx({
remarkPlugins: [shikiTwoslash, { /* Shiki Twoslash config */ }], // `syntaxHighlight` inherited from Markdown
// Markdown `remarkPlugins` ignored,
// only `remarkPlugin2` applied.
remarkPlugins: [remarkPlugin2],
// `gfm` overridden to `false`
gfm: false,
}) })
], ]
});
```
## Configuration
### remarkPlugins
[Remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) allow you to extend your Markdown with new capabilities. This includes [auto-generating a table of contents](https://github.com/remarkjs/remark-toc), [applying accessible emoji labels](https://github.com/florianeckerstorfer/remark-a11y-emoji), and more. We encourage you to browse [awesome-remark](https://github.com/remarkjs/awesome-remark) for a full curated list!
This example applies the [`remark-toc`](https://github.com/remarkjs/remark-toc) plugin to `.mdx` files. To customize plugin inheritance from your Markdown config or Astro's defaults, [see the `extendPlugins` option](#extendplugins).
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
export default defineConfig({
integrations: [mdx({
remarkPlugins: [remarkToc],
})],
});
```
### rehypePlugins
[Rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) allow you to transform the HTML that your Markdown generates. We encourage you to browse [awesome-rehype](https://github.com/rehypejs/awesome-rehype) for a full curated list of plugins!
We apply our own (non-removable) [`collect-headings`](https://github.com/withastro/astro/blob/main/packages/integrations/mdx/src/rehype-collect-headings.ts) plugin. This applies IDs to all headings (i.e. `h1 -> h6`) in your MDX files to [link to headings via anchor tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#linking_to_an_element_on_the_same_page).
This example applies the [`rehype-minify`](https://github.com/rehypejs/rehype-minify) plugin to `.mdx` files. To customize plugin inheritance from your Markdown config or Astro's defaults, [see the `extendPlugins` option](#extendplugins).
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import rehypeMinifyHtml from 'rehype-minify';
export default defineConfig({
integrations: [mdx({
rehypePlugins: [rehypeMinifyHtml],
})],
});
```
### extendPlugins
**Type:** `'markdown' | 'astroDefaults' | false`
**Default:** `'markdown'`
#### `markdown` (default)
By default, Astro inherits all [remark](#remarkplugins) and [rehype](#rehypeplugins) plugins from [the `markdown` option in your Astro config](https://docs.astro.build/en/guides/markdown-content/#markdown-plugins). This also respects the [`markdown.extendDefaultPlugins`](https://docs.astro.build/en/reference/configuration-reference/#markdownextenddefaultplugins) option to extend Astro's defaults. Any additional plugins you apply in your MDX config will be applied _after_ your configured Markdown plugins.
This example applies [`remark-toc`](https://github.com/remarkjs/remark-toc) to Markdown _and_ MDX, and [`rehype-minify`](https://github.com/rehypejs/rehype-minify) to MDX alone:
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
import rehypeMinify from 'rehype-minify';
export default defineConfig({
markdown: {
// Applied to .md and .mdx files
remarkPlugins: [remarkToc],
},
integrations: [mdx({
// Applied to .mdx files only
rehypePlugins: [rehypeMinify],
})],
}); });
``` ```
@ -349,40 +159,21 @@ You may also need to disable `markdown` config extension in MDX. For this, set `
__`astro.config.mjs`__ __`astro.config.mjs`__
```js "extendPlugins: 'astroDefaults'" ```js
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx'; import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
export default defineConfig({ export default defineConfig({
markdown: { markdown: {
remarkPlugins: [remarkPlugin1], remarkPlugins: [remarkPlugin1],
}, },
integrations: [mdx({ integrations: [
remarkPlugins: [remarkToc], mdx({
// Astro defaults applied // Markdown config now ignored
extendPlugins: 'astroDefaults', extendMarkdownConfig: false,
})], // No `remarkPlugins` applied
}); })
``` ]
#### `false`
If you don't want to extend any plugins, set `extendPlugins` to `false`:
__`astro.config.mjs`__
```js "extendPlugins: false"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
export default defineConfig({
integrations: [mdx({
remarkPlugins: [remarkToc],
// Astro defaults not applied
extendPlugins: false,
})],
}); });
``` ```
@ -392,30 +183,6 @@ These are plugins that modify the output [estree](https://github.com/estree/estr
We suggest [using AST Explorer](https://astexplorer.net/) to play with estree outputs, and trying [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) for searching across JavaScript nodes. We suggest [using AST Explorer](https://astexplorer.net/) to play with estree outputs, and trying [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) for searching across JavaScript nodes.
### remarkRehype
Markdown content is transformed into HTML through remark-rehype which has [a number of options](https://github.com/remarkjs/remark-rehype#options).
You can use remark-rehype options in your MDX integration config file like so:
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [mdx({
remarkRehype: {
footnoteLabel: 'Catatan kaki',
footnoteBackLabel: 'Kembali ke konten',
},
})],
});
```
This inherits the configuration of `markdown.remarkRehype`. This behavior can be changed by configuring `extendPlugins`.
## Examples ## Examples
* The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project. * The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project.

View file

@ -1,7 +1,7 @@
{ {
"name": "@astrojs/mdx", "name": "@astrojs/mdx",
"description": "Use MDX within Astro", "description": "Use MDX within Astro",
"version": "0.15.2", "version": "0.16.0",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"author": "withastro", "author": "withastro",

View file

@ -129,12 +129,7 @@ export async function getRemarkPlugins(
config: AstroConfig config: AstroConfig
): Promise<MdxRollupPluginOptions['remarkPlugins']> { ): Promise<MdxRollupPluginOptions['remarkPlugins']> {
let remarkPlugins: PluggableList = []; let remarkPlugins: PluggableList = [];
if (mdxOptions.syntaxHighlight === 'shiki') {
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
}
if (mdxOptions.syntaxHighlight === 'prism') {
remarkPlugins.push(remarkPrism);
}
if (mdxOptions.gfm) { if (mdxOptions.gfm) {
remarkPlugins.push(remarkGfm); remarkPlugins.push(remarkGfm);
} }
@ -144,6 +139,14 @@ export async function getRemarkPlugins(
remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)]; remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)];
// Apply syntax highlighters after user plugins to match `markdown/remark` behavior
if (mdxOptions.syntaxHighlight === 'shiki') {
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
}
if (mdxOptions.syntaxHighlight === 'prism') {
remarkPlugins.push(remarkPrism);
}
// Apply last in case user plugins resolve relative image paths // Apply last in case user plugins resolve relative image paths
remarkPlugins.push(toRemarkContentRelImageError(config)); remarkPlugins.push(toRemarkContentRelImageError(config));

View file

@ -1,5 +1,11 @@
# @astrojs/netlify # @astrojs/netlify
## 2.1.1
### Patch Changes
- [#6090](https://github.com/withastro/astro/pull/6090) [`97a97196f`](https://github.com/withastro/astro/commit/97a97196fc4d2dd8ced838ddbca17a587cfa0957) Thanks [@matthewp](https://github.com/matthewp)! - Fix Netlify Function regression
## 2.1.0 ## 2.1.0
### Minor Changes ### Minor Changes

View file

@ -1,7 +1,7 @@
{ {
"name": "@astrojs/netlify", "name": "@astrojs/netlify",
"description": "Deploy your site to Netlify", "description": "Deploy your site to Netlify",
"version": "2.1.0", "version": "2.1.1",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"author": "withastro", "author": "withastro",
@ -35,14 +35,14 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/webapi": "^2.0.0", "@astrojs/webapi": "^2.0.0",
"@netlify/functions": "^1.0.0",
"esbuild": "^0.15.18" "esbuild": "^0.15.18"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"@netlify/edge-handler-types": "^0.34.1", "@netlify/edge-handler-types": "^0.34.1",
"@netlify/functions": "^1.0.0",
"@types/node": "^14.18.20", "@types/node": "^14.18.20",
"astro": "workspace:*", "astro": "workspace:*",
"astro-scripts": "workspace:*", "astro-scripts": "workspace:*",

View file

@ -1,5 +1,11 @@
# @astrojs/node # @astrojs/node
## 5.0.2
### Patch Changes
- [#6088](https://github.com/withastro/astro/pull/6088) [`6a03649f0`](https://github.com/withastro/astro/commit/6a03649f0084f0df6738236d4a86c9936325cee7) Thanks [@QingXia-Ela](https://github.com/QingXia-Ela)! - fix incorrent encoded when path has other language characters
## 5.0.1 ## 5.0.1
### Patch Changes ### Patch Changes

View file

@ -1,7 +1,7 @@
{ {
"name": "@astrojs/node", "name": "@astrojs/node",
"description": "Deploy your site to a Node.js server", "description": "Deploy your site to a Node.js server",
"version": "5.0.1", "version": "5.0.2",
"type": "module", "type": "module",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"author": "withastro", "author": "withastro",
@ -34,7 +34,7 @@
"send": "^0.18.0" "send": "^0.18.0"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"@types/send": "^0.17.1", "@types/send": "^0.17.1",

View file

@ -19,7 +19,7 @@ export function createServer(
if (req.url) { if (req.url) {
let pathname = removeBase(req.url); let pathname = removeBase(req.url);
pathname = pathname[0] === '/' ? pathname : '/' + pathname; pathname = pathname[0] === '/' ? pathname : '/' + pathname;
const stream = send(req, encodeURI(pathname), { const stream = send(req, encodeURI(decodeURI(pathname)), {
root: fileURLToPath(client), root: fileURLToPath(client),
dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny', dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny',
}); });

View file

@ -33,7 +33,7 @@
}, },
"dependencies": { "dependencies": {
"babel-preset-solid": "^1.4.2", "babel-preset-solid": "^1.4.2",
"vitefu": "^0.2.1" "vitefu": "^0.2.4"
}, },
"devDependencies": { "devDependencies": {
"astro": "workspace:*", "astro": "workspace:*",

View file

@ -44,7 +44,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"svelte": "^3.54.0", "svelte": "^3.54.0",
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"engines": { "engines": {
"node": ">=16.12.0" "node": ">=16.12.0"

View file

@ -41,7 +41,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"tailwindcss": "^3.0.24", "tailwindcss": "^3.0.24",
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {

View file

@ -50,7 +50,7 @@
"set-cookie-parser": "^2.5.1" "set-cookie-parser": "^2.5.1"
}, },
"peerDependencies": { "peerDependencies": {
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"devDependencies": { "devDependencies": {
"@types/set-cookie-parser": "^2.4.2", "@types/set-cookie-parser": "^2.4.2",

View file

@ -1,4 +1,5 @@
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro'; import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
import esbuild from 'esbuild';
import { relative as relativePath } from 'node:path'; import { relative as relativePath } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@ -74,18 +75,25 @@ export default function vercelEdge({ includeFiles = [] }: VercelEdgeConfig = {})
} }
} }
vite.ssr = { vite.ssr ||= {};
target: 'webworker', vite.ssr.target ||= 'webworker';
noExternal: true,
};
vite.build ||= {};
vite.build.minify = true;
} }
}, },
'astro:build:done': async ({ routes }) => { 'astro:build:done': async ({ routes }) => {
const entry = new URL(serverEntry, buildTempFolder); const entry = new URL(serverEntry, buildTempFolder);
const generatedFiles = await getFilesFromFolder(buildTempFolder); const generatedFiles = await getFilesFromFolder(buildTempFolder);
const entryPath = fileURLToPath(entry);
await esbuild.build({
target: 'es2020',
platform: 'browser',
entryPoints: [entryPath],
outfile: entryPath,
allowOverwrite: true,
format: 'esm',
bundle: true,
minify: true,
});
// Copy entry and other server files // Copy entry and other server files
const commonAncestor = await copyFilesToFunction( const commonAncestor = await copyFilesToFunction(
@ -100,7 +108,7 @@ export default function vercelEdge({ includeFiles = [] }: VercelEdgeConfig = {})
// https://vercel.com/docs/build-output-api/v3#vercel-primitives/edge-functions/configuration // https://vercel.com/docs/build-output-api/v3#vercel-primitives/edge-functions/configuration
await writeJson(new URL(`./.vc-config.json`, functionFolder), { await writeJson(new URL(`./.vc-config.json`, functionFolder), {
runtime: 'edge', runtime: 'edge',
entrypoint: relativePath(commonAncestor, fileURLToPath(entry)), entrypoint: relativePath(commonAncestor, entryPath),
}); });
// Output configuration // Output configuration

View file

@ -51,7 +51,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.2.30", "vue": "^3.2.30",
"astro": "workspace:^2.0.4" "astro": "workspace:^2.0.5"
}, },
"engines": { "engines": {
"node": ">=16.12.0" "node": ">=16.12.0"

File diff suppressed because it is too large Load diff