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:
commit
14b0626f3e
32 changed files with 467 additions and 86 deletions
5
.changeset/breezy-frogs-learn.md
Normal file
5
.changeset/breezy-frogs-learn.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/cloudflare': minor
|
||||
---
|
||||
|
||||
More efficient \_routes.json
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
'create-astro': patch
|
||||
---
|
||||
|
||||
Add support for more Starlight templates
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixed `EndpointOutput` types with `{ encoding: 'binary' }`
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix quadratic quote escaping in nested data in island props
|
5
.changeset/wild-jobs-tan.md
Normal file
5
.changeset/wild-jobs-tan.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Move hoisted script analysis optimization behind the `experimental.optimizeHoistedScript` option
|
|
@ -11,6 +11,6 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^2.10.3"
|
||||
"astro": "^2.10.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -8,6 +8,9 @@ describe('Hoisted Imports', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/hoisted-imports/',
|
||||
experimental: {
|
||||
optimizeHoistedScript: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
11
packages/integrations/cloudflare/test/fixtures/routesJson/astro.config.mjs
vendored
Normal file
11
packages/integrations/cloudflare/test/fixtures/routesJson/astro.config.mjs
vendored
Normal 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
|
||||
});
|
9
packages/integrations/cloudflare/test/fixtures/routesJson/package.json
vendored
Normal file
9
packages/integrations/cloudflare/test/fixtures/routesJson/package.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "@test/astro-cloudflare-routes-json",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
export const prerender=false;
|
||||
---
|
||||
|
||||
ok
|
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/dynamicOnly/pages/index.astro
vendored
Normal file
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/dynamicOnly/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
export const prerender=false;
|
||||
---
|
||||
|
||||
ok
|
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/[...rest].astro
vendored
Normal file
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/[...rest].astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
export const prerender=false;
|
||||
---
|
||||
|
||||
ok
|
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/[id].astro
vendored
Normal file
5
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/[id].astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
export const prerender=false;
|
||||
---
|
||||
|
||||
ok
|
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/endpoint.ts
vendored
Normal file
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/endpoint.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export const prerender = false;
|
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/index.astro
vendored
Normal file
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/a/index.astro
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ok
|
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/b/index.html
vendored
Normal file
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/pages/b/index.html
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ok
|
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/public/public.txt
vendored
Normal file
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/mixed/public/public.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ok
|
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/staticOnly/pages/index.astro
vendored
Normal file
1
packages/integrations/cloudflare/test/fixtures/routesJson/src/staticOnly/pages/index.astro
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ok
|
|
@ -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: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
78
packages/integrations/cloudflare/test/routesJson.js
Normal file
78
packages/integrations/cloudflare/test/routesJson.js
Normal 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: ['/'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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
|
||||
|
|
|
@ -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' }),
|
||||
|
|
Loading…
Reference in a new issue