0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

Merge remote-tracking branch 'origin/main' into next

This commit is contained in:
Emanuele Stoppa 2023-08-10 11:49:52 +01:00
commit 14b0626f3e
32 changed files with 467 additions and 86 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/cloudflare': minor
---
More efficient \_routes.json

View file

@ -1,5 +0,0 @@
---
'create-astro': patch
---
Add support for more Starlight templates

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fixed `EndpointOutput` types with `{ encoding: 'binary' }`

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fix quadratic quote escaping in nested data in island props

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Move hoisted script analysis optimization behind the `experimental.optimizeHoistedScript` option

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^2.10.3"
"astro": "^2.10.4"
}
}

View file

@ -31,18 +31,44 @@ Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sap
## Images
![This is a placeholder image description](/blog-placeholder-1.jpg)
#### Syntax
```markdown
![Alt text](./full/or/relative/path/of/image)
```
#### Output
![blog placeholder](../../../public/blog-placeholder-about.jpg)
## Blockquotes
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
#### Blockquote without attribution
### Blockquote without attribution
#### Syntax
```markdown
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use _Markdown syntax_ within a blockquote.
```
#### Output
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use _Markdown syntax_ within a blockquote.
#### Blockquote with attribution
### Blockquote with attribution
#### Syntax
```markdown
> Don't communicate by sharing memory, share memory by communicating.<br>
> — <cite>Rob Pike[^1]</cite>
```
#### Output
> Don't communicate by sharing memory, share memory by communicating.<br>
> — <cite>Rob Pike[^1]</cite>
@ -51,12 +77,43 @@ The blockquote element represents content that is quoted from another source, op
## Tables
#### Syntax
```markdown
| Italics | Bold | Code |
| --------- | -------- | ------ |
| _italics_ | **bold** | `code` |
```
#### Output
| Italics | Bold | Code |
| --------- | -------- | ------ |
| _italics_ | **bold** | `code` |
## Code Blocks
#### Syntax
we can use 3 backticks ``` in new line and write snippet and close with 3 backticks on new line and to highlight language specific syntac, write one word of language name after first 3 backticks, for eg. html, javascript, css, markdown, typescript, txt, bash
````markdown
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
```
````
Output
```html
<!doctype html>
<html lang="en">
@ -72,19 +129,53 @@ The blockquote element represents content that is quoted from another source, op
## List Types
#### Ordered List
### Ordered List
#### Syntax
```markdown
1. First item
2. Second item
3. Third item
```
#### Output
1. First item
2. Second item
3. Third item
#### Unordered List
### Unordered List
#### Syntax
```markdown
- List item
- Another item
- And another item
```
#### Output
- List item
- Another item
- And another item
#### Nested list
### Nested list
#### Syntax
```markdown
- Fruit
- Apple
- Orange
- Banana
- Dairy
- Milk
- Cheese
```
#### Output
- Fruit
- Apple
@ -96,6 +187,22 @@ The blockquote element represents content that is quoted from another source, op
## Other Elements — abbr, sub, sup, kbd, mark
#### Syntax
```markdown
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
H<sub>2</sub>O
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
```
#### Output
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
H<sub>2</sub>O

View file

@ -219,6 +219,16 @@
- @astrojs/internal-helpers@0.2.0-beta.0
- @astrojs/markdown-remark@3.0.0-beta.0
## 2.10.4
### Patch Changes
- [#8003](https://github.com/withastro/astro/pull/8003) [`16161afb2`](https://github.com/withastro/astro/commit/16161afb2b3a04ca7605fcd16de06efe3fabdef2) Thanks [@JuanM04](https://github.com/JuanM04)! - Fixed `EndpointOutput` types with `{ encoding: 'binary' }`
- [#7995](https://github.com/withastro/astro/pull/7995) [`79376f842`](https://github.com/withastro/astro/commit/79376f842d25edfe4dc2948548e99b59e1c4d24f) Thanks [@belluzj](https://github.com/belluzj)! - Fix quadratic quote escaping in nested data in island props
- [#8007](https://github.com/withastro/astro/pull/8007) [`58b121d42`](https://github.com/withastro/astro/commit/58b121d42a9f58a5a992f0c378b036f37e9715fc) Thanks [@paperdave](https://github.com/paperdave)! - Support Bun by adjusting how `@babel/plugin-transform-react-jsx` is imported.
## 2.10.3
### Patch Changes

View file

@ -1271,6 +1271,28 @@ export interface AstroUserConfig {
* ```
*/
viewTransitions?: boolean;
/**
* @docs
* @name experimental.optimizeHoistedScript
* @type {boolean}
* @default `false`
* @version 2.10.4
* @description
* Prevents unused components' scripts from being included in a page unexpectedly.
* The optimization is best-effort and may inversely miss including the used scripts. Make sure to double-check your built pages
* before publishing.
* Enable hoisted script analysis optimization by adding the experimental flag:
*
* ```js
* {
* experimental: {
* optimizeHoistedScript: true,
* },
* }
* ```
*/
optimizeHoistedScript?: boolean;
};
// Legacy options to be removed

View file

@ -16,7 +16,7 @@ import { pluginSSR, pluginSSRSplit } from './plugin-ssr.js';
export function registerAllPlugins({ internals, options, register }: AstroBuildPluginContainer) {
register(pluginComponentEntry(internals));
register(pluginAliasResolve(internals));
register(pluginAnalyzer(internals));
register(pluginAnalyzer(options, internals));
register(pluginInternals(internals));
register(pluginRenderers(options));
register(pluginMiddleware(options, internals));

View file

@ -6,11 +6,11 @@ import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { ExportDefaultDeclaration, ExportNamedDeclaration, ImportDeclaration } from 'estree';
import { walk } from 'estree-walker';
import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js';
import { prependForwardSlash } from '../../../core/path.js';
import { getTopLevelPages, moduleIsTopLevelPage, walkParentInfos } from '../graph.js';
import { getPageDataByViteID, trackClientOnlyPageDatas } from '../internal.js';
import type { StaticBuildOptions } from '../types.js';
function isPropagatedAsset(id: string) {
try {
@ -31,29 +31,28 @@ async function doesParentImportChild(
): Promise<'no' | 'dynamic' | string[]> {
if (!childInfo || !parentInfo.ast || !childExportNames) return 'no';
// If we're dynamically importing the child, return `dynamic` directly to opt-out of optimization
if (childExportNames === 'dynamic' || parentInfo.dynamicallyImportedIds?.includes(childInfo.id)) {
return 'dynamic';
}
const imports: Array<ImportDeclaration> = [];
const exports: Array<ExportNamedDeclaration | ExportDefaultDeclaration> = [];
walk(parentInfo.ast as ESTreeNode, {
enter(node) {
if (node.type === 'ImportDeclaration') {
imports.push(node as ImportDeclaration);
} else if (
node.type === 'ExportDefaultDeclaration' ||
node.type === 'ExportNamedDeclaration'
) {
exports.push(node as ExportNamedDeclaration | ExportDefaultDeclaration);
}
},
});
// All of the aliases the current component is imported as
for (const node of (parentInfo.ast as any).body) {
if (node.type === 'ImportDeclaration') {
imports.push(node);
} else if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration') {
exports.push(node);
}
}
// All local import names that could be importing the child component
const importNames: string[] = [];
// All of the aliases the child component is exported as
const exportNames: string[] = [];
// Iterate each import, find it they import the child component, if so, check if they
// import the child component name specifically. We can verify this with `childExportNames`.
for (const node of imports) {
const resolved = await this.resolve(node.source.value as string, parentInfo.id);
if (!resolved || resolved.id !== childInfo.id) continue;
@ -68,14 +67,17 @@ async function doesParentImportChild(
}
}
}
// Iterate each export, find it they re-export the child component, and push the exported name to `exportNames`
for (const node of exports) {
if (node.type === 'ExportDefaultDeclaration') {
if (node.declaration.type === 'Identifier' && importNames.includes(node.declaration.name)) {
exportNames.push('default');
// return
}
} else {
// handle `export { x } from 'something';`, where the export and import are in the same node
// Handle:
// export { Component } from './Component.astro'
// export { Component as AliasedComponent } from './Component.astro'
if (node.source) {
const resolved = await this.resolve(node.source.value as string, parentInfo.id);
if (!resolved || resolved.id !== childInfo.id) continue;
@ -86,6 +88,9 @@ async function doesParentImportChild(
}
}
}
// Handle:
// export const AliasedComponent = Component
// export const AliasedComponent = Component, let foo = 'bar'
if (node.declaration) {
if (node.declaration.type !== 'VariableDeclaration') continue;
for (const declarator of node.declaration.declarations) {
@ -96,6 +101,9 @@ async function doesParentImportChild(
}
}
}
// Handle:
// export { Component }
// export { Component as AliasedComponent }
for (const specifier of node.specifiers) {
if (importNames.includes(specifier.local.name)) {
exportNames.push(specifier.exported.name);
@ -116,7 +124,10 @@ async function doesParentImportChild(
return exportNames;
}
export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
export function vitePluginAnalyzer(
options: StaticBuildOptions,
internals: BuildInternals
): VitePlugin {
function hoistedScriptScanner() {
const uniqueHoistedIds = new Map<string, string>();
const pageScripts = new Map<
@ -140,6 +151,7 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
}
if (hoistedScripts.size) {
// These variables are only used for hoisted script analysis optimization
const depthsToChildren = new Map<number, ModuleInfo>();
const depthsToExportNames = new Map<number, string[] | 'dynamic'>();
// The component export from the original component file will always be default.
@ -148,25 +160,28 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
for (const [parentInfo, depth] of walkParentInfos(from, this, function until(importer) {
return isPropagatedAsset(importer);
})) {
depthsToChildren.set(depth, parentInfo);
// If at any point
if (depth > 0) {
// Check if the component is actually imported:
const childInfo = depthsToChildren.get(depth - 1);
const childExportNames = depthsToExportNames.get(depth - 1);
// If hoisted script analysis optimization is enabled, try to analyse and bail early if possible
if (options.settings.config.experimental.optimizeHoistedScript) {
depthsToChildren.set(depth, parentInfo);
// If at any point
if (depth > 0) {
// Check if the component is actually imported:
const childInfo = depthsToChildren.get(depth - 1);
const childExportNames = depthsToExportNames.get(depth - 1);
const doesImport = await doesParentImportChild.call(
this,
parentInfo,
childInfo,
childExportNames
);
const doesImport = await doesParentImportChild.call(
this,
parentInfo,
childInfo,
childExportNames
);
if (doesImport === 'no') {
// Break the search if the parent doesn't import the child.
continue;
if (doesImport === 'no') {
// Break the search if the parent doesn't import the child.
continue;
}
depthsToExportNames.set(depth, doesImport);
}
depthsToExportNames.set(depth, doesImport);
}
if (isPropagatedAsset(parentInfo.id)) {
@ -311,13 +326,16 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
};
}
export function pluginAnalyzer(internals: BuildInternals): AstroBuildPlugin {
export function pluginAnalyzer(
options: StaticBuildOptions,
internals: BuildInternals
): AstroBuildPlugin {
return {
build: 'ssr',
hooks: {
'build:before': () => {
return {
vitePlugin: vitePluginAnalyzer(internals),
vitePlugin: vitePluginAnalyzer(options, internals),
};
},
},

View file

@ -46,6 +46,7 @@ const ASTRO_CONFIG_DEFAULTS = {
experimental: {
assets: false,
viewTransitions: false,
optimizeHoistedScript: false,
},
} satisfies AstroUserConfig & { server: { open: boolean } };
@ -245,6 +246,10 @@ export const AstroConfigSchema = z.object({
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.viewTransitions),
optimizeHoistedScript: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.optimizeHoistedScript),
})
.passthrough()
.refine(

View file

@ -3,10 +3,9 @@ const renderer = {
serverEntrypoint: 'astro/jsx/server.js',
jsxImportSource: 'astro',
jsxTransformOptions: async () => {
const {
default: { default: jsx },
// @ts-expect-error
} = await import('@babel/plugin-transform-react-jsx');
// @ts-expect-error types not found
const plugin = await import('@babel/plugin-transform-react-jsx');
const jsx = plugin.default?.default ?? plugin.default;
const { default: astroJSX } = await import('./babel.js');
return {
plugins: [

View file

@ -8,6 +8,9 @@ describe('Hoisted Imports', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/hoisted-imports/',
experimental: {
optimizeHoistedScript: true,
},
});
});

View file

@ -6,6 +6,12 @@
- [`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 16. The lowest supported version by Astro and all integrations is now v18.14.1. As a reminder, Node 16 will be deprecated on the 11th September 2023.
## 3.1.12
### Patch Changes
- [#7993](https://github.com/withastro/astro/pull/7993) [`315d58f27`](https://github.com/withastro/astro/commit/315d58f27b022c9d4285cf13f445ed18c26c327e) Thanks [@delucis](https://github.com/delucis)! - Add support for more Starlight templates
## 3.1.11
### Patch Changes

View file

@ -106,7 +106,10 @@ Cloudflare has support for adding custom [headers](https://developers.cloudflare
### Custom `_routes.json`
By default, `@astrojs/cloudflare` will generate a `_routes.json` file that lists all files from your `dist/` folder and redirects from the `_redirects` file in the `exclude` array. This will enable Cloudflare to serve files and process static redirects without a function invocation. Creating a custom `_routes.json` will override this automatic optimization and, if not configured manually, cause function invocations that will count against the request limits of your Cloudflare plan.
By default, `@astrojs/cloudflare` will generate a `_routes.json` file with `include` and `exclude` rules based on your applications's dynamic and static routes.
This will enable Cloudflare to serve files and process static redirects without a function invocation. Creating a custom `_routes.json` will override this automatic optimization and, if not configured manually, cause function invocations that will count against the request limits of your Cloudflare plan.
See [Cloudflare's documentation](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) for more details.
## Troubleshooting

View file

@ -59,6 +59,11 @@ const SHIM = `globalThis.process = {
const SERVER_BUILD_FOLDER = '/$server_build/';
/**
* These route types are candiates for being part of the `_routes.json` `include` array.
*/
const potentialFunctionRouteTypes = ['endpoint', 'page'];
export default function createIntegration(args?: Options): AstroIntegration {
let _config: AstroConfig;
let _buildConfig: BuildConfig;
@ -253,6 +258,32 @@ export default function createIntegration(args?: Options): AstroIntegration {
// cloudflare to handle static files and support _redirects configuration
// (without calling the function)
if (!routesExists) {
const functionEndpoints = routes
// Certain route types, when their prerender option is set to false, a run on the server as function invocations
.filter((route) => potentialFunctionRouteTypes.includes(route.type) && !route.prerender)
.map((route) => {
const includePattern =
'/' +
route.segments
.flat()
.map((segment) => (segment.dynamic ? '*' : segment.content))
.join('/');
const regexp = new RegExp(
'^\\/' +
route.segments
.flat()
.map((segment) => (segment.dynamic ? '(.*)' : segment.content))
.join('\\/') +
'$'
);
return {
includePattern,
regexp,
};
});
const staticPathList: Array<string> = (
await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
cwd: fileURLToPath(_config.outDir),
@ -260,7 +291,7 @@ export default function createIntegration(args?: Options): AstroIntegration {
})
)
.filter((file: string) => cloudflareSpecialFiles.indexOf(file) < 0)
.map((file: string) => `/${file}`);
.map((file: string) => `/${file.replace(/\\/g, '/')}`);
for (let page of pages) {
let pagePath = prependForwardSlash(page.pathname);
@ -323,13 +354,41 @@ export default function createIntegration(args?: Options): AstroIntegration {
);
}
staticPathList.push(...routes.filter((r) => r.type === 'redirect').map((r) => r.route));
// In order to product the shortest list of patterns, we first try to
// include all function endpoints, and then exclude all static paths
let include = deduplicatePatterns(
functionEndpoints.map((endpoint) => endpoint.includePattern)
);
let exclude = deduplicatePatterns(
staticPathList.filter((file: string) =>
functionEndpoints.some((endpoint) => endpoint.regexp.test(file))
)
);
// Cloudflare requires at least one include pattern:
// https://developers.cloudflare.com/pages/platform/functions/routing/#limits
// So we add a pattern that we immediately exclude again
if (include.length === 0) {
include = ['/'];
exclude = ['/'];
}
// If using only an exclude list would produce a shorter list of patterns,
// we use that instead
if (include.length + exclude.length > staticPathList.length) {
include = ['/*'];
exclude = deduplicatePatterns(staticPathList);
}
await fs.promises.writeFile(
new URL('./_routes.json', _config.outDir),
JSON.stringify(
{
version: 1,
include: ['/*'],
exclude: staticPathList,
include,
exclude,
},
null,
2
@ -344,3 +403,28 @@ export default function createIntegration(args?: Options): AstroIntegration {
function prependForwardSlash(path: string) {
return path[0] === '/' ? path : '/' + path;
}
/**
* Remove duplicates and redundant patterns from an `include` or `exclude` list.
* Otherwise Cloudflare will throw an error on deployment. Plus, it saves more entries.
* E.g. `['/foo/*', '/foo/*', '/foo/bar'] => ['/foo/*']`
* @param patterns a list of `include` or `exclude` patterns
* @returns a deduplicated list of patterns
*/
function deduplicatePatterns(patterns: string[]) {
const openPatterns: RegExp[] = [];
return [...new Set(patterns)]
.sort((a, b) => a.length - b.length)
.filter((pattern) => {
if (openPatterns.some((p) => p.test(pattern))) {
return false;
}
if (pattern.endsWith('*')) {
openPatterns.push(new RegExp(`^${pattern.replace(/(\*\/)*\*$/g, '.*')}`));
}
return true;
});
}

View file

@ -0,0 +1,11 @@
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
adapter: cloudflare({ mode: 'directory' }),
output: 'hybrid',
redirects: {
'/a/redirect': '/',
},
srcDir: process.env.SRC
});

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-cloudflare-routes-json",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/cloudflare": "workspace:*",
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,5 @@
---
export const prerender=false;
---
ok

View file

@ -0,0 +1,5 @@
---
export const prerender=false;
---
ok

View file

@ -0,0 +1,5 @@
---
export const prerender=false;
---
ok

View file

@ -0,0 +1,5 @@
---
export const prerender=false;
---
ok

View file

@ -0,0 +1 @@
export const prerender = false;

View file

@ -18,13 +18,14 @@ describe('Prerendering', () => {
fixture.clean();
});
it('includes prerendered routes in the routes.json config', async () => {
const foundRoutes = JSON.parse(await fixture.readFile('/_routes.json')).exclude.map((r) =>
r.replace(/\\/g, '/')
);
const expectedExcludedRoutes = ['/_worker.js', '/one/index.html', '/one/'];
it('includes non prerendered routes in the routes.json config', async () => {
const foundRoutes = JSON.parse(await fixture.readFile('/_routes.json'));
expect(foundRoutes.every((element) => expectedExcludedRoutes.includes(element))).to.be.true;
expect(foundRoutes).to.deep.equal({
version: 1,
include: ['/'],
exclude: [],
});
});
});
@ -45,12 +46,13 @@ describe('Hybrid rendering', () => {
delete process.env.PRERENDER;
});
it('includes prerendered routes in the routes.json config', async () => {
const foundRoutes = JSON.parse(await fixture.readFile('/_routes.json')).exclude.map((r) =>
r.replace(/\\/g, '/')
);
const expectedExcludedRoutes = ['/_worker.js', '/index.html', '/'];
it('includes non prerendered routes in the routes.json config', async () => {
const foundRoutes = JSON.parse(await fixture.readFile('/_routes.json'));
expect(foundRoutes.every((element) => expectedExcludedRoutes.includes(element))).to.be.true;
expect(foundRoutes).to.deep.equal({
version: 1,
include: ['/one'],
exclude: [],
});
});
});

View file

@ -0,0 +1,78 @@
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
/** @type {import('./test-utils.js').Fixture} */
describe('_routes.json generation', () => {
after(() => {
delete process.env.SRC;
});
describe('of both functions and static files', () => {
let fixture;
before(async () => {
process.env.SRC = './src/mixed';
fixture = await loadFixture({
root: './fixtures/routesJson/',
});
await fixture.build();
});
it('creates `include` for functions and `exclude` for static files where needed', async () => {
const _routesJson = await fixture.readFile('/_routes.json');
const routes = JSON.parse(_routesJson);
expect(routes).to.deep.equal({
version: 1,
include: ['/a/*'],
exclude: ['/a/', '/a/redirect', '/a/index.html'],
});
});
});
describe('of only functions', () => {
let fixture;
before(async () => {
process.env.SRC = './src/dynamicOnly';
fixture = await loadFixture({
root: './fixtures/routesJson/',
});
await fixture.build();
});
it('creates a wildcard `include` and `exclude` only for the redirect', async () => {
const _routesJson = await fixture.readFile('/_routes.json');
const routes = JSON.parse(_routesJson);
expect(routes).to.deep.equal({
version: 1,
include: ['/*'],
exclude: ['/a/redirect'],
});
});
});
describe('of only static files', () => {
let fixture;
before(async () => {
process.env.SRC = './src/staticOnly';
fixture = await loadFixture({
root: './fixtures/routesJson/',
});
await fixture.build();
});
it('create only one `include` and `exclude` that are supposed to match nothing', async () => {
const _routesJson = await fixture.readFile('/_routes.json');
const routes = JSON.parse(_routesJson);
expect(routes).to.deep.equal({
version: 1,
include: ['/'],
exclude: ['/'],
});
});
});
});

View file

@ -6,6 +6,12 @@
- [`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 16. The lowest supported version by Astro and all integrations is now v18.14.1. As a reminder, Node 16 will be deprecated on the 11th September 2023.
## 2.2.2
### Patch Changes
- [#8007](https://github.com/withastro/astro/pull/8007) [`58b121d42`](https://github.com/withastro/astro/commit/58b121d42a9f58a5a992f0c378b036f37e9715fc) Thanks [@paperdave](https://github.com/paperdave)! - Support Bun by adjusting how `@babel/plugin-transform-react-jsx` is imported.
## 2.2.1
### Patch Changes

View file

@ -7,10 +7,9 @@ function getRenderer(development: boolean): AstroRenderer {
serverEntrypoint: '@astrojs/preact/server.js',
jsxImportSource: 'preact',
jsxTransformOptions: async () => {
const {
default: { default: jsx },
// @ts-expect-error types not found
} = await import('@babel/plugin-transform-react-jsx');
// @ts-expect-error types not found
const plugin = await import('@babel/plugin-transform-react-jsx');
const jsx = plugin.default?.default ?? plugin.default;
return {
plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact' })],
};
@ -25,10 +24,9 @@ function getCompatRenderer(development: boolean): AstroRenderer {
serverEntrypoint: '@astrojs/preact/server.js',
jsxImportSource: 'react',
jsxTransformOptions: async () => {
const {
default: { default: jsx },
// @ts-expect-error types not found
} = await import('@babel/plugin-transform-react-jsx');
// @ts-expect-error types not found
const plugin = await import('@babel/plugin-transform-react-jsx');
const jsx = plugin.default?.default ?? plugin.default;
return {
plugins: [
jsx({}, { runtime: 'automatic', importSource: 'preact/compat' }),