summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Emanuele Stoppa <my.burning@gmail.com> 2023-08-09 16:28:14 +0100
committerGravatar Emanuele Stoppa <my.burning@gmail.com> 2023-08-09 16:28:14 +0100
commit670ef109dbbc07f53c7758bad6098186719fdeb2 (patch)
tree2f5df138879e62b605f846346303032e798eae2c
parent65c354969e6fe0ef6d622e8f4c545e2f717ce8c6 (diff)
parent79376f842d25edfe4dc2948548e99b59e1c4d24f (diff)
downloadastro-670ef109dbbc07f53c7758bad6098186719fdeb2.tar.gz
astro-670ef109dbbc07f53c7758bad6098186719fdeb2.tar.zst
astro-670ef109dbbc07f53c7758bad6098186719fdeb2.zip
Merge remote-tracking branch 'origin/main' into next
Diffstat (limited to '')
-rw-r--r--.changeset/dry-pandas-flash.md5
-rw-r--r--.changeset/funny-glasses-bathe.md5
-rw-r--r--.changeset/great-icons-turn.md5
-rw-r--r--packages/astro/src/@types/astro.ts13
-rw-r--r--packages/astro/src/core/app/index.ts4
-rw-r--r--packages/astro/src/core/endpoint/index.ts9
-rw-r--r--packages/astro/src/runtime/server/astro-island.ts27
-rw-r--r--packages/astro/src/runtime/server/serialize.ts20
-rw-r--r--packages/astro/src/vite-plugin-astro-server/route.ts15
-rw-r--r--packages/astro/test/serialize.test.js18
-rw-r--r--packages/create-astro/src/actions/template.ts5
-rw-r--r--packages/integrations/markdoc/README.md2
12 files changed, 79 insertions, 49 deletions
diff --git a/.changeset/dry-pandas-flash.md b/.changeset/dry-pandas-flash.md
new file mode 100644
index 000000000..fb18de65d
--- /dev/null
+++ b/.changeset/dry-pandas-flash.md
@@ -0,0 +1,5 @@
+---
+'create-astro': patch
+---
+
+Add support for more Starlight templates
diff --git a/.changeset/funny-glasses-bathe.md b/.changeset/funny-glasses-bathe.md
new file mode 100644
index 000000000..28db2f746
--- /dev/null
+++ b/.changeset/funny-glasses-bathe.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fixed `EndpointOutput` types with `{ encoding: 'binary' }`
diff --git a/.changeset/great-icons-turn.md b/.changeset/great-icons-turn.md
new file mode 100644
index 000000000..c3d937f91
--- /dev/null
+++ b/.changeset/great-icons-turn.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix quadratic quote escaping in nested data in island props
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 4491dc48c..f7b07dbfe 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -1870,10 +1870,15 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
locals: App.Locals;
}
-export interface EndpointOutput {
- body: Body;
- encoding?: BufferEncoding;
-}
+export type EndpointOutput =
+ | {
+ body: Body;
+ encoding?: Exclude<BufferEncoding, 'binary'>;
+ }
+ | {
+ body: Uint8Array;
+ encoding: 'binary';
+ };
export type APIRoute<Props extends Record<string, any> = Record<string, any>> = (
context: APIContext<Props>
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index 4496f0098..43f7a3926 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -194,7 +194,6 @@ export class App {
}
return response.response;
} else {
- const body = response.body;
const headers = new Headers();
const mimeType = mime.getType(url.pathname);
if (mimeType) {
@@ -202,7 +201,8 @@ export class App {
} else {
headers.set('Content-Type', 'text/plain;charset=utf-8');
}
- const bytes = this.#encoder.encode(body);
+ const bytes =
+ response.encoding !== 'binary' ? this.#encoder.encode(response.body) : response.body;
headers.set('Content-Length', bytes.byteLength.toString());
const newResponse = new Response(bytes, {
diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts
index b35895197..97b26e380 100644
--- a/packages/astro/src/core/endpoint/index.ts
+++ b/packages/astro/src/core/endpoint/index.ts
@@ -18,12 +18,10 @@ const clientAddressSymbol = Symbol.for('astro.clientAddress');
const clientLocalsSymbol = Symbol.for('astro.locals');
export type EndpointCallResult =
- | {
+ | (EndpointOutput & {
type: 'simple';
- body: string;
- encoding?: BufferEncoding;
cookies: AstroCookies;
- }
+ })
| {
type: 'response';
response: Response;
@@ -153,9 +151,8 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
}
return {
+ ...response,
type: 'simple',
- body: response.body,
- encoding: response.encoding,
cookies: context.cookies,
};
}
diff --git a/packages/astro/src/runtime/server/astro-island.ts b/packages/astro/src/runtime/server/astro-island.ts
index a108044ac..7be630d06 100644
--- a/packages/astro/src/runtime/server/astro-island.ts
+++ b/packages/astro/src/runtime/server/astro-island.ts
@@ -18,25 +18,32 @@ declare const Astro: {
}
const propTypes: PropTypeSelector = {
- 0: (value) => value,
- 1: (value) => JSON.parse(value, reviver),
+ 0: (value) => reviveObject(value),
+ 1: (value) => reviveArray(value),
2: (value) => new RegExp(value),
3: (value) => new Date(value),
- 4: (value) => new Map(JSON.parse(value, reviver)),
- 5: (value) => new Set(JSON.parse(value, reviver)),
+ 4: (value) => new Map(reviveArray(value)),
+ 5: (value) => new Set(reviveArray(value)),
6: (value) => BigInt(value),
7: (value) => new URL(value),
- 8: (value) => new Uint8Array(JSON.parse(value)),
- 9: (value) => new Uint16Array(JSON.parse(value)),
- 10: (value) => new Uint32Array(JSON.parse(value)),
+ 8: (value) => new Uint8Array(value),
+ 9: (value) => new Uint16Array(value),
+ 10: (value) => new Uint32Array(value),
};
- const reviver = (propKey: string, raw: string): any => {
- if (propKey === '' || !Array.isArray(raw)) return raw;
+ // Not using JSON.parse reviver because it's bottom-up but we want top-down
+ const reviveTuple = (raw: any): any => {
const [type, value] = raw;
return type in propTypes ? propTypes[type](value) : undefined;
};
+ const reviveArray = (raw: any): any => (raw as Array<any>).map(reviveTuple);
+
+ const reviveObject = (raw: any): any => {
+ if (typeof raw !== 'object' || raw === null) return raw;
+ return Object.fromEntries(Object.entries(raw).map(([key, value]) => [key, reviveTuple(value)]));
+ };
+
if (!customElements.get('astro-island')) {
customElements.define(
'astro-island',
@@ -132,7 +139,7 @@ declare const Astro: {
try {
props = this.hasAttribute('props')
- ? JSON.parse(this.getAttribute('props')!, reviver)
+ ? reviveObject(JSON.parse(this.getAttribute('props')!))
: {};
} catch (e) {
let componentName: string = this.getAttribute('component-url') || '<unknown>';
diff --git a/packages/astro/src/runtime/server/serialize.ts b/packages/astro/src/runtime/server/serialize.ts
index 7c0d46dee..479552260 100644
--- a/packages/astro/src/runtime/server/serialize.ts
+++ b/packages/astro/src/runtime/server/serialize.ts
@@ -4,7 +4,7 @@ type ValueOf<T> = T[keyof T];
const PROP_TYPE = {
Value: 0,
- JSON: 1,
+ JSON: 1, // Actually means Array
RegExp: 2,
Date: 3,
Map: 4,
@@ -68,16 +68,10 @@ function convertToSerializedForm(
return [PROP_TYPE.RegExp, (value as RegExp).source];
}
case '[object Map]': {
- return [
- PROP_TYPE.Map,
- JSON.stringify(serializeArray(Array.from(value as Map<any, any>), metadata, parents)),
- ];
+ return [PROP_TYPE.Map, serializeArray(Array.from(value as Map<any, any>), metadata, parents)];
}
case '[object Set]': {
- return [
- PROP_TYPE.Set,
- JSON.stringify(serializeArray(Array.from(value as Set<any>), metadata, parents)),
- ];
+ return [PROP_TYPE.Set, serializeArray(Array.from(value as Set<any>), metadata, parents)];
}
case '[object BigInt]': {
return [PROP_TYPE.BigInt, (value as bigint).toString()];
@@ -86,16 +80,16 @@ function convertToSerializedForm(
return [PROP_TYPE.URL, (value as URL).toString()];
}
case '[object Array]': {
- return [PROP_TYPE.JSON, JSON.stringify(serializeArray(value, metadata, parents))];
+ return [PROP_TYPE.JSON, serializeArray(value, metadata, parents)];
}
case '[object Uint8Array]': {
- return [PROP_TYPE.Uint8Array, JSON.stringify(Array.from(value as Uint8Array))];
+ return [PROP_TYPE.Uint8Array, Array.from(value as Uint8Array)];
}
case '[object Uint16Array]': {
- return [PROP_TYPE.Uint16Array, JSON.stringify(Array.from(value as Uint16Array))];
+ return [PROP_TYPE.Uint16Array, Array.from(value as Uint16Array)];
}
case '[object Uint32Array]': {
- return [PROP_TYPE.Uint32Array, JSON.stringify(Array.from(value as Uint32Array))];
+ return [PROP_TYPE.Uint32Array, Array.from(value as Uint32Array)];
}
default: {
if (value !== null && typeof value === 'object') {
diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts
index ff926bca2..f58d248a3 100644
--- a/packages/astro/src/vite-plugin-astro-server/route.ts
+++ b/packages/astro/src/vite-plugin-astro-server/route.ts
@@ -246,12 +246,15 @@ export async function handleRoute({
if (computedMimeType) {
contentType = computedMimeType;
}
- const response = new Response(Buffer.from(result.body, result.encoding), {
- status: 200,
- headers: {
- 'Content-Type': `${contentType};charset=utf-8`,
- },
- });
+ const response = new Response(
+ result.encoding !== 'binary' ? Buffer.from(result.body, result.encoding) : result.body,
+ {
+ status: 200,
+ headers: {
+ 'Content-Type': `${contentType};charset=utf-8`,
+ },
+ }
+ );
attachToResponse(response, result.cookies);
await writeWebResponse(incomingResponse, response);
}
diff --git a/packages/astro/test/serialize.test.js b/packages/astro/test/serialize.test.js
index f6838be19..3a370b8af 100644
--- a/packages/astro/test/serialize.test.js
+++ b/packages/astro/test/serialize.test.js
@@ -34,7 +34,13 @@ describe('serialize', () => {
});
it('serializes an array', () => {
const input = { a: [0] };
- const output = `{"a":[1,"[[0,0]]"]}`;
+ const output = `{"a":[1,[[0,0]]]}`;
+ expect(serializeProps(input)).to.equal(output);
+ });
+ it('can serialize deeply nested data without quadratic quote escaping', () => {
+ const input = { a: [{ b: [{ c: [{ d: [{ e: [{ f: [{ g: ['leaf'] }] }] }] }] }] }] };
+ const output =
+ '{"a":[1,[[0,{"b":[1,[[0,{"c":[1,[[0,{"d":[1,[[0,{"e":[1,[[0,{"f":[1,[[0,{"g":[1,[[0,"leaf"]]]}]]]}]]]}]]]}]]]}]]]}]]]}';
expect(serializeProps(input)).to.equal(output);
});
it('serializes a regular expression', () => {
@@ -49,12 +55,12 @@ describe('serialize', () => {
});
it('serializes a Map', () => {
const input = { a: new Map([[0, 1]]) };
- const output = `{"a":[4,"[[1,\\"[[0,0],[0,1]]\\"]]"]}`;
+ const output = `{"a":[4,[[1,[[0,0],[0,1]]]]]}`;
expect(serializeProps(input)).to.equal(output);
});
it('serializes a Set', () => {
const input = { a: new Set([0, 1, 2, 3]) };
- const output = `{"a":[5,"[[0,0],[0,1],[0,2],[0,3]]"]}`;
+ const output = `{"a":[5,[[0,0],[0,1],[0,2],[0,3]]]}`;
expect(serializeProps(input)).to.equal(output);
});
it('serializes a BigInt', () => {
@@ -69,17 +75,17 @@ describe('serialize', () => {
});
it('serializes a Uint8Array', () => {
const input = { a: new Uint8Array([1, 2, 3]) };
- const output = `{"a":[8,"[1,2,3]"]}`;
+ const output = `{"a":[8,[1,2,3]]}`;
expect(serializeProps(input)).to.equal(output);
});
it('serializes a Uint16Array', () => {
const input = { a: new Uint16Array([1, 2, 3]) };
- const output = `{"a":[9,"[1,2,3]"]}`;
+ const output = `{"a":[9,[1,2,3]]}`;
expect(serializeProps(input)).to.equal(output);
});
it('serializes a Uint32Array', () => {
const input = { a: new Uint32Array([1, 2, 3]) };
- const output = `{"a":[10,"[1,2,3]"]}`;
+ const output = `{"a":[10,[1,2,3]]}`;
expect(serializeProps(input)).to.equal(output);
});
it('cannot serialize a cyclic reference', () => {
diff --git a/packages/create-astro/src/actions/template.ts b/packages/create-astro/src/actions/template.ts
index f762b264f..ca041642b 100644
--- a/packages/create-astro/src/actions/template.ts
+++ b/packages/create-astro/src/actions/template.ts
@@ -67,9 +67,12 @@ const FILES_TO_UPDATE = {
};
function getTemplateTarget(tmpl: string, ref = 'latest') {
+ if (tmpl.startsWith('starlight')) {
+ const [, starter = 'basics'] = tmpl.split('/');
+ return `withastro/starlight/examples/${starter}`;
+ }
const isThirdParty = tmpl.includes('/');
if (isThirdParty) return tmpl;
- if (tmpl === 'starlight') return `withastro/starlight/examples/basics`;
return `github:withastro/astro/examples/${tmpl}#${ref}`;
}
diff --git a/packages/integrations/markdoc/README.md b/packages/integrations/markdoc/README.md
index 4b9772bc7..780b8de9a 100644
--- a/packages/integrations/markdoc/README.md
+++ b/packages/integrations/markdoc/README.md
@@ -278,7 +278,7 @@ export default defineMarkdocConfig({
### Use client-side UI components
-Tags and nodes are restricted to `.astro` files. To embed client-side UI components in Markdoc, [use a wrapper `.astro` component that renders a framework component](/en/core-concepts/framework-components/#nesting-framework-components) with your desired `client:` directive.
+Tags and nodes are restricted to `.astro` files. To embed client-side UI components in Markdoc, [use a wrapper `.astro` component that renders a framework component](https://docs.astro.build/en/core-concepts/framework-components/#nesting-framework-components) with your desired `client:` directive.
This example wraps a React `Aside.tsx` component with a `ClientAside.astro` component: