mirror of
https://github.com/withastro/astro.git
synced 2025-02-03 22:29:08 -05:00
Merge branch 'main' into next
This commit is contained in:
commit
953e6e0f23
7 changed files with 94 additions and 4 deletions
5
.changeset/ninety-monkeys-complain.md
Normal file
5
.changeset/ninety-monkeys-complain.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Clear content layer cache when astro version changes
|
5
.changeset/swift-snakes-hope.md
Normal file
5
.changeset/swift-snakes-hope.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Allows special characters in Action names
|
|
@ -11,7 +11,10 @@ import type { ActionAccept, ActionClient } from './server.js';
|
||||||
export async function getAction(
|
export async function getAction(
|
||||||
path: string,
|
path: string,
|
||||||
): Promise<ActionClient<unknown, ActionAccept, ZodType>> {
|
): Promise<ActionClient<unknown, ActionAccept, ZodType>> {
|
||||||
const pathKeys = path.replace('/_actions/', '').split('.');
|
const pathKeys = path
|
||||||
|
.replace('/_actions/', '')
|
||||||
|
.split('.')
|
||||||
|
.map((key) => decodeURIComponent(key));
|
||||||
// @ts-expect-error virtual module
|
// @ts-expect-error virtual module
|
||||||
let { server: actionLookup } = await import('astro:internal-actions');
|
let { server: actionLookup } = await import('astro:internal-actions');
|
||||||
|
|
||||||
|
|
|
@ -146,13 +146,27 @@ export class ContentLayer {
|
||||||
const { digest: currentConfigDigest } = contentConfig.config;
|
const { digest: currentConfigDigest } = contentConfig.config;
|
||||||
this.#lastConfigDigest = currentConfigDigest;
|
this.#lastConfigDigest = currentConfigDigest;
|
||||||
|
|
||||||
|
let shouldClear = false;
|
||||||
const previousConfigDigest = await this.#store.metaStore().get('config-digest');
|
const previousConfigDigest = await this.#store.metaStore().get('config-digest');
|
||||||
|
const previousAstroVersion = await this.#store.metaStore().get('astro-version');
|
||||||
if (currentConfigDigest && previousConfigDigest !== currentConfigDigest) {
|
if (currentConfigDigest && previousConfigDigest !== currentConfigDigest) {
|
||||||
logger.info('Content config changed, clearing cache');
|
logger.info('Content config changed');
|
||||||
|
shouldClear = true;
|
||||||
|
}
|
||||||
|
if (process.env.ASTRO_VERSION && previousAstroVersion !== process.env.ASTRO_VERSION) {
|
||||||
|
logger.info('Astro version changed');
|
||||||
|
shouldClear = true;
|
||||||
|
}
|
||||||
|
if (shouldClear) {
|
||||||
|
logger.info('Clearing content store');
|
||||||
this.#store.clearAll();
|
this.#store.clearAll();
|
||||||
|
}
|
||||||
|
if (process.env.ASTRO_VERSION) {
|
||||||
|
await this.#store.metaStore().set('astro-version', process.env.ASTRO_VERSION);
|
||||||
|
}
|
||||||
|
if (currentConfigDigest) {
|
||||||
await this.#store.metaStore().set('config-digest', currentConfigDigest);
|
await this.#store.metaStore().set('config-digest', currentConfigDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.entries(contentConfig.config.collections).map(async ([name, collection]) => {
|
Object.entries(contentConfig.config.collections).map(async ([name, collection]) => {
|
||||||
if (collection.type !== CONTENT_LAYER_TYPE) {
|
if (collection.type !== CONTENT_LAYER_TYPE) {
|
||||||
|
|
|
@ -5,13 +5,17 @@ import {
|
||||||
getActionQueryString,
|
getActionQueryString,
|
||||||
} from 'astro:actions';
|
} from 'astro:actions';
|
||||||
|
|
||||||
|
const ENCODED_DOT = '%2E';
|
||||||
|
|
||||||
function toActionProxy(actionCallback = {}, aggregatedPath = '') {
|
function toActionProxy(actionCallback = {}, aggregatedPath = '') {
|
||||||
return new Proxy(actionCallback, {
|
return new Proxy(actionCallback, {
|
||||||
get(target, objKey) {
|
get(target, objKey) {
|
||||||
if (objKey in target || typeof objKey === 'symbol') {
|
if (objKey in target || typeof objKey === 'symbol') {
|
||||||
return target[objKey];
|
return target[objKey];
|
||||||
}
|
}
|
||||||
const path = aggregatedPath + objKey.toString();
|
// Add the key, encoding dots so they're not interpreted as nested properties.
|
||||||
|
const path =
|
||||||
|
aggregatedPath + encodeURIComponent(objKey.toString()).replaceAll('.', ENCODED_DOT);
|
||||||
function action(param) {
|
function action(param) {
|
||||||
return handleAction(param, path, this);
|
return handleAction(param, path, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,23 @@ describe('Astro Actions', () => {
|
||||||
assert.equal(data.success, true);
|
assert.equal(data.success, true);
|
||||||
assert.equal(data.isFormData, true, 'Should receive plain FormData');
|
assert.equal(data.isFormData, true, 'Should receive plain FormData');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Handles special characters in action names', async () => {
|
||||||
|
for (const name of ['with%2Fslash', 'with%20space', 'with%2Edot']) {
|
||||||
|
const res = await fixture.fetch(`/_actions/${name}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ name: 'ben' }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.equal(res.ok, true);
|
||||||
|
const text = await res.text();
|
||||||
|
assert.equal(res.headers.get('Content-Type'), 'application/json+devalue');
|
||||||
|
const data = devalue.parse(text);
|
||||||
|
assert.equal(data, 'Hello, ben!');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('build', () => {
|
describe('build', () => {
|
||||||
|
@ -428,6 +445,24 @@ describe('Astro Actions', () => {
|
||||||
const dataRest = devalue.parse(await resRest.text());
|
const dataRest = devalue.parse(await resRest.text());
|
||||||
assert.equal('fake', dataRest?.uploadId);
|
assert.equal('fake', dataRest?.uploadId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Handles special characters in action names', async () => {
|
||||||
|
for (const name of ['with%2Fslash', 'with%20space', 'with%2Edot']) {
|
||||||
|
const req = new Request(`http://example.com/_actions/${name}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ name: 'ben' }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const res = await app.render(req);
|
||||||
|
assert.equal(res.ok, true);
|
||||||
|
const text = await res.text();
|
||||||
|
assert.equal(res.headers.get('Content-Type'), 'application/json+devalue');
|
||||||
|
const data = devalue.parse(text);
|
||||||
|
assert.equal(data, 'Hello, ben!');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -161,4 +161,28 @@ export const server = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
"with.dot": defineAction({
|
||||||
|
input: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
handler: async (input) => {
|
||||||
|
return `Hello, ${input.name}!`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"with space": defineAction({
|
||||||
|
input: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
handler: async (input) => {
|
||||||
|
return `Hello, ${input.name}!`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"with/slash": defineAction({
|
||||||
|
input: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
handler: async (input) => {
|
||||||
|
return `Hello, ${input.name}!`
|
||||||
|
}
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue