Compare commits

..

1 commit

Author SHA1 Message Date
bluwy
9884738094 Fix markdown encoding as utf-8 by default 2023-10-10 23:11:22 +08:00
23 changed files with 166 additions and 179 deletions

View file

@ -1,5 +0,0 @@
---
'astro': patch
---
Fixed an issue where the transitions router did not work within framework components.

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix markdown page encoding to be utf-8 by default (same as Astro 2)

View file

@ -1,5 +0,0 @@
---
'@astrojs/cloudflare': patch
---
fixes `AdvancedRuntime` & `DirectoryRuntime` types to work woth Cloudflare caches

View file

@ -1,5 +0,0 @@
import React from 'react';
import { navigate } from "astro:transitions/client";
export default function ClickToNavigate({ to, id }) {
return <button id={id} onClick={() => navigate(to)}>Navigate to `{to}`</button>;
}

View file

@ -1,12 +0,0 @@
---
import ClickToNavigate from "../components/ClickToNavigate.jsx"
import { ViewTransitions } from "astro:transitions";
---
<html>
<head>
<ViewTransitions />
</head>
<body>
<ClickToNavigate id="react-client-load-navigate-button" to="/two" client:load/>
</body>
</html>

View file

@ -753,21 +753,6 @@ test.describe('View Transitions', () => {
await expect(p, 'should have content').toHaveText('Page 1'); await expect(p, 'should have content').toHaveText('Page 1');
}); });
test('Use the client side router in framework components', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/client-load'));
// the button is set to naviagte() to /two
const button = page.locator('#react-client-load-navigate-button');
await expect(button, 'should have content').toHaveText('Navigate to `/two`');
await button.click();
const p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
});
test('body inline scripts do not re-execute on navigation', async ({ page, astro }) => { test('body inline scripts do not re-execute on navigation', async ({ page, astro }) => {
const errors = []; const errors = [];
page.addListener('pageerror', (err) => { page.addListener('pageerror', (err) => {

View file

@ -30,7 +30,7 @@ type RawContentEvent = { name: ChokidarEvent; entry: string };
type ContentEvent = { name: ChokidarEvent; entry: URL }; type ContentEvent = { name: ChokidarEvent; entry: URL };
type DataEntryMetadata = Record<string, never>; type DataEntryMetadata = Record<string, never>;
type ContentEntryMetadata = { slug: string, path: string }; type ContentEntryMetadata = { slug: string };
type CollectionEntryMap = { type CollectionEntryMap = {
[collection: string]: [collection: string]:
| { | {
@ -276,7 +276,7 @@ export async function createContentTypesGenerator({
if (!(entryKey in collectionEntryMap[collectionKey].entries)) { if (!(entryKey in collectionEntryMap[collectionKey].entries)) {
collectionEntryMap[collectionKey] = { collectionEntryMap[collectionKey] = {
type: 'content', type: 'content',
entries: { ...collectionInfo.entries, [entryKey]: { slug: addedSlug, path: event.entry.toString() } }, entries: { ...collectionInfo.entries, [entryKey]: { slug: addedSlug } },
}; };
} }
return { shouldGenerateTypes: true }; return { shouldGenerateTypes: true };
@ -453,15 +453,7 @@ async function writeContentFiles({
)}] }`; )}] }`;
const slugType = JSON.stringify(entryMetadata.slug); const slugType = JSON.stringify(entryMetadata.slug);
contentTypesStr += [ contentTypesStr += `${entryKey}: {\n id: ${entryKey};\n slug: ${slugType};\n body: string;\n collection: ${collectionKey};\n data: ${dataType}\n} & ${renderType};\n`;
`${entryKey}: {`,
` id: ${entryKey};`,
` slug: ${slugType};`,
` path: ${JSON.stringify(entryMetadata.path)};`,
` body: string;`,
` collection: ${collectionKey};`,
` data: ${dataType}`,
`} & ${renderType};`].join("\n");
} }
contentTypesStr += `};\n`; contentTypesStr += `};\n`;
break; break;

View file

@ -63,6 +63,11 @@ export async function renderPage(
body = encoder.encode(body); body = encoder.encode(body);
headers.set('Content-Length', body.byteLength.toString()); headers.set('Content-Length', body.byteLength.toString());
} }
// TODO: Revisit if user should manually set charset by themselves in Astro 4
// This code preserves the existing behaviour for markdown pages since Astro 2
if (route?.component.endsWith('.md')) {
headers.set('Content-Type', 'text/html; charset=utf-8');
}
const response = new Response(body, { ...init, headers }); const response = new Response(body, { ...init, headers });
return response; return response;
} }

View file

@ -13,14 +13,9 @@ type Events = 'astro:page-load' | 'astro:after-swap';
// only update history entries that are managed by us // only update history entries that are managed by us
// leave other entries alone and do not accidently add state. // leave other entries alone and do not accidently add state.
const persistState = (state: State) => history.state && history.replaceState(state, ''); const persistState = (state: State) => history.state && history.replaceState(state, '');
export const supportsViewTransitions = !!document.startViewTransition;
const inBrowser = import.meta.env.SSR === false;
export const supportsViewTransitions = inBrowser && !!document.startViewTransition;
export const transitionEnabledOnThisPage = () => export const transitionEnabledOnThisPage = () =>
inBrowser && !!document.querySelector('[name="astro-view-transitions-enabled"]'); !!document.querySelector('[name="astro-view-transitions-enabled"]');
const samePage = (otherLocation: URL) => const samePage = (otherLocation: URL) =>
location.pathname === otherLocation.pathname && location.search === otherLocation.search; location.pathname === otherLocation.pathname && location.search === otherLocation.search;
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name)); const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
@ -45,27 +40,21 @@ const announce = () => {
60 60
); );
}; };
const PERSIST_ATTR = 'data-astro-transition-persist'; const PERSIST_ATTR = 'data-astro-transition-persist';
const parser = new DOMParser();
let parser: DOMParser;
// The History API does not tell you if navigation is forward or back, so // The History API does not tell you if navigation is forward or back, so
// you can figure it using an index. On pushState the index is incremented so you // you can figure it using an index. On pushState the index is incremented so you
// can use that to determine popstate if going forward or back. // can use that to determine popstate if going forward or back.
let currentHistoryIndex = 0; let currentHistoryIndex = 0;
if (history.state) {
if (inBrowser) { // we reloaded a page with history state
if (history.state) { // (e.g. history navigation from non-transition page or browser reload)
// we reloaded a page with history state currentHistoryIndex = history.state.index;
// (e.g. history navigation from non-transition page or browser reload) scrollTo({ left: history.state.scrollX, top: history.state.scrollY });
currentHistoryIndex = history.state.index; } else if (transitionEnabledOnThisPage()) {
scrollTo({ left: history.state.scrollX, top: history.state.scrollY }); history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, '');
} else if (transitionEnabledOnThisPage()) {
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, '');
}
} }
const throttle = (cb: (...args: any[]) => any, delay: number) => { const throttle = (cb: (...args: any[]) => any, delay: number) => {
let wait = false; let wait = false;
// During the waiting time additional events are lost. // During the waiting time additional events are lost.
@ -347,8 +336,6 @@ async function transition(
toLocation = new URL(response.redirected); toLocation = new URL(response.redirected);
} }
parser ??= new DOMParser();
const newDocument = parser.parseFromString(response.html, response.mediaType); const newDocument = parser.parseFromString(response.html, response.mediaType);
// The next line might look like a hack, // The next line might look like a hack,
// but it is actually necessary as noscript elements // but it is actually necessary as noscript elements
@ -385,22 +372,7 @@ async function transition(
} }
} }
let navigateOnServerWarned = false;
export function navigate(href: string, options?: Options) { export function navigate(href: string, options?: Options) {
if (inBrowser === false) {
if (!navigateOnServerWarned) {
// instantiate an error for the stacktrace to show to user.
const warning = new Error(
'The view transtions client API was called during a server side render. This may be unintentional as the navigate() function is expected to be called in response to user interactions. Please make sure that your usage is correct.'
);
warning.name = 'Warning';
console.warn(warning);
navigateOnServerWarned = true;
}
return;
}
// not ours // not ours
if (!transitionEnabledOnThisPage()) { if (!transitionEnabledOnThisPage()) {
location.href = href; location.href = href;
@ -418,61 +390,58 @@ export function navigate(href: string, options?: Options) {
} }
} }
function onPopState(ev: PopStateEvent) { if (supportsViewTransitions || getFallback() !== 'none') {
if (!transitionEnabledOnThisPage() && ev.state) { addEventListener('popstate', (ev) => {
// The current page doesn't have View Transitions enabled if (!transitionEnabledOnThisPage() && ev.state) {
// but the page we navigate to does (because it set the state). // The current page doesn't have View Transitions enabled
// Do a full page refresh to reload the client-side router from the new page. // but the page we navigate to does (because it set the state).
// Scroll restauration will then happen during the reload when the router's code is re-executed // Do a full page refresh to reload the client-side router from the new page.
// Scroll restauration will then happen during the reload when the router's code is re-executed
if (history.scrollRestoration) {
history.scrollRestoration = 'manual';
}
location.reload();
return;
}
// History entries without state are created by the browser (e.g. for hash links)
// Our view transition entries always have state.
// Just ignore stateless entries.
// The browser will handle navigation fine without our help
if (ev.state === null) {
if (history.scrollRestoration) {
history.scrollRestoration = 'auto';
}
return;
}
// With the default "auto", the browser will jump to the old scroll position
// before the ViewTransition is complete.
if (history.scrollRestoration) { if (history.scrollRestoration) {
history.scrollRestoration = 'manual'; history.scrollRestoration = 'manual';
} }
location.reload();
return;
}
// History entries without state are created by the browser (e.g. for hash links) const state: State = history.state;
// Our view transition entries always have state. if (state.intraPage) {
// Just ignore stateless entries. // this is non transition intra-page scrolling
// The browser will handle navigation fine without our help scrollTo(state.scrollX, state.scrollY);
if (ev.state === null) { } else {
if (history.scrollRestoration) { const nextIndex = state.index;
history.scrollRestoration = 'auto'; const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
currentHistoryIndex = nextIndex;
transition(direction, new URL(location.href), {}, state);
} }
return; });
}
// With the default "auto", the browser will jump to the old scroll position addEventListener('load', onPageLoad);
// before the ViewTransition is complete. // There's not a good way to record scroll position before a back button.
if (history.scrollRestoration) { // So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position.
history.scrollRestoration = 'manual'; const updateState = () => {
} persistState({ ...history.state, scrollX, scrollY });
};
const state: State = history.state; if ('onscrollend' in window) addEventListener('scrollend', updateState);
if (state.intraPage) { else addEventListener('scroll', throttle(updateState, 300));
// this is non transition intra-page scrolling
scrollTo(state.scrollX, state.scrollY); markScriptsExec();
} else {
const nextIndex = state.index;
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
currentHistoryIndex = nextIndex;
transition(direction, new URL(location.href), {}, state);
}
}
if (inBrowser) {
if (supportsViewTransitions || getFallback() !== 'none') {
addEventListener('popstate', onPopState);
addEventListener('load', onPageLoad);
// There's not a good way to record scroll position before a back button.
// So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position.
const updateState = () => {
persistState({ ...history.state, scrollX, scrollY });
};
if ('onscrollend' in window) addEventListener('scrollend', updateState);
else addEventListener('scroll', throttle(updateState, 300));
markScriptsExec();
}
} }

View file

@ -3,23 +3,31 @@ import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js'; import { loadFixture } from './test-utils.js';
describe('Astro basics', () => { describe('Astro basics', () => {
/** @type {import('./test-utils').Fixture} */
let fixture; let fixture;
let previewServer;
before(async () => { before(async () => {
fixture = await loadFixture({ fixture = await loadFixture({
root: './fixtures/astro-basic/', root: './fixtures/astro-basic/',
}); });
await fixture.build();
previewServer = await fixture.preview();
});
// important: close preview server (free up port and connection)
after(async () => {
await previewServer.stop();
}); });
describe('build', () => { describe('build', () => {
let previewServer;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-basic/',
});
await fixture.build();
previewServer = await fixture.preview();
});
// important: close preview server (free up port and connection)
after(async () => {
await previewServer.stop();
});
it('Can load page', async () => { it('Can load page', async () => {
const html = await fixture.readFile(`/index.html`); const html = await fixture.readFile(`/index.html`);
const $ = cheerio.load(html); const $ = cheerio.load(html);
@ -108,6 +116,18 @@ describe('Astro basics', () => {
const $ = cheerio.load(html); const $ = cheerio.load(html);
expect($('#rendered-order').text()).to.eq('Rendered order: A, B'); expect($('#rendered-order').text()).to.eq('Rendered order: A, B');
}); });
it('renders markdown in utf-8 by default', async () => {
const html = await fixture.readFile('/chinese-encoding-md/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
});
it('renders MDX in utf-8 by default', async () => {
const html = await fixture.readFile('/chinese-encoding-mdx/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
});
}); });
it('Supports void elements whose name is a string (#2062)', async () => { it('Supports void elements whose name is a string (#2062)', async () => {
@ -143,4 +163,40 @@ describe('Astro basics', () => {
expect(result.status).to.equal(404); expect(result.status).to.equal(404);
}); });
}); });
describe('development', () => {
/** @type {import('./test-utils').DevServer} */
let devServer;
before(async () => {
devServer = await fixture.startDevServer();
});
after(async () => {
await devServer.stop();
});
it('Renders markdown in utf-8 by default', async () => {
const res = await fixture.fetch('/chinese-encoding-md');
expect(res.status).to.equal(200);
const html = await res.text();
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
const isUtf8 =
res.headers.get('content-type').includes('charset=utf-8') ||
html.includes('<meta charset="utf-8">');
expect(isUtf8).to.be.true;
});
it('Renders MDX in utf-8 by default', async () => {
const res = await fixture.fetch('/chinese-encoding-mdx');
expect(res.status).to.equal(200);
const html = await res.text();
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
const isUtf8 =
res.headers.get('content-type').includes('charset=utf-8') ||
html.includes('<meta charset="utf-8">');
expect(isUtf8).to.be.true;
});
});
}); });

View file

@ -1,9 +1,10 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact'; import preact from '@astrojs/preact';
import mdx from '@astrojs/mdx';
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
integrations: [preact()], integrations: [preact(), mdx()],
// make sure CLI flags have precedence // make sure CLI flags have precedence
server: () => ({ port: 4321 }) server: () => ({ port: 4321 })
}); });

View file

@ -3,6 +3,7 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@astrojs/mdx": "workspace:*",
"@astrojs/preact": "workspace:*", "@astrojs/preact": "workspace:*",
"astro": "workspace:*", "astro": "workspace:*",
"preact": "^10.17.1" "preact": "^10.17.1"

View file

@ -0,0 +1,3 @@
# 我的第一篇博客文章
发表于2022-07-01

View file

@ -0,0 +1,3 @@
# 我的第一篇博客文章
发表于2022-07-01

View file

@ -90,7 +90,7 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page); const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }], links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
mod: PageModule, mod: PageModule,
@ -171,7 +171,7 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page); const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }], links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env, env,
@ -218,7 +218,7 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page); const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }], links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env, env,

View file

@ -45,7 +45,7 @@ describe('core/render', () => {
const mod = createAstroModule(Page); const mod = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
env, env,
mod, mod,
@ -91,7 +91,7 @@ describe('core/render', () => {
const mod = createAstroModule(Page); const mod = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
env, env,
mod, mod,
@ -117,7 +117,7 @@ describe('core/render', () => {
const mod = createAstroModule(Page); const mod = createAstroModule(Page);
const ctx = await createRenderContext({ const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' }, route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'), request: new Request('http://example.com/'),
env, env,
mod, mod,

View file

@ -169,7 +169,7 @@ default: `false`
Whether or not to import `.wasm` files [directly as ES modules](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) using the `.wasm?module` import syntax. Whether or not to import `.wasm` files [directly as ES modules](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) using the `.wasm?module` import syntax.
Add `wasmModuleImports: true` to `astro.config.mjs` to enable this functionality in both the Cloudflare build and the Astro dev server. Read more about [using Wasm modules](#use-wasm-modules). Add `wasmModuleImports: true` to `astro.config.mjs` to enable this functionality in both the Cloudflare build and the Astro dev server. Read more about [using Wasm modules](#use-wasm-modules)
```diff lang="js" ```diff lang="js"
// astro.config.mjs // astro.config.mjs
@ -221,7 +221,7 @@ Currently supported bindings:
- [Cloudflare Workers KV](https://developers.cloudflare.com/kv/) - [Cloudflare Workers KV](https://developers.cloudflare.com/kv/)
- [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/) - [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/)
You can access the runtime from Astro components through `Astro.locals` inside any `.astro` file. You can access the runtime from Astro components through `Astro.locals` inside any .astro` file.
```astro ```astro
--- ---

View file

@ -1,8 +1,4 @@
import type { import type { Request as CFRequest, ExecutionContext } from '@cloudflare/workers-types';
Request as CFRequest,
CacheStorage,
ExecutionContext,
} from '@cloudflare/workers-types';
import type { SSRManifest } from 'astro'; import type { SSRManifest } from 'astro';
import { App } from 'astro/app'; import { App } from 'astro/app';
import { getProcessEnvProxy, isNode } from '../util.js'; import { getProcessEnvProxy, isNode } from '../util.js';
@ -20,7 +16,7 @@ export interface AdvancedRuntime<T extends object = object> {
waitUntil: (promise: Promise<any>) => void; waitUntil: (promise: Promise<any>) => void;
env: Env & T; env: Env & T;
cf: CFRequest['cf']; cf: CFRequest['cf'];
caches: CacheStorage; caches: typeof caches;
}; };
} }
@ -54,7 +50,7 @@ export function createExports(manifest: SSRManifest) {
}, },
env: env, env: env,
cf: request.cf, cf: request.cf,
caches: caches as unknown as CacheStorage, caches: caches,
}, },
}; };

View file

@ -1,4 +1,4 @@
import type { Request as CFRequest, CacheStorage, EventContext } from '@cloudflare/workers-types'; import type { Request as CFRequest, EventContext } from '@cloudflare/workers-types';
import type { SSRManifest } from 'astro'; import type { SSRManifest } from 'astro';
import { App } from 'astro/app'; import { App } from 'astro/app';
import { getProcessEnvProxy, isNode } from '../util.js'; import { getProcessEnvProxy, isNode } from '../util.js';
@ -6,12 +6,13 @@ import { getProcessEnvProxy, isNode } from '../util.js';
if (!isNode) { if (!isNode) {
process.env = getProcessEnvProxy(); process.env = getProcessEnvProxy();
} }
export interface DirectoryRuntime<T extends object = object> { export interface DirectoryRuntime<T extends object = object> {
runtime: { runtime: {
waitUntil: (promise: Promise<any>) => void; waitUntil: (promise: Promise<any>) => void;
env: EventContext<unknown, string, unknown>['env'] & T; env: EventContext<unknown, string, unknown>['env'] & T;
cf: CFRequest['cf']; cf: CFRequest['cf'];
caches: CacheStorage; caches: typeof caches;
}; };
} }
@ -47,7 +48,7 @@ export function createExports(manifest: SSRManifest) {
}, },
env: context.env, env: context.env,
cf: request.cf, cf: request.cf,
caches: caches as unknown as CacheStorage, caches: caches,
}, },
}; };

View file

@ -116,14 +116,14 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
if (!id.endsWith('.mdx')) return; if (!id.endsWith('.mdx')) return;
// Read code from file manually to prevent Vite from parsing `import.meta.env` expressions // Read code from file manually to prevent Vite from parsing `import.meta.env` expressions
const { fileId, fileUrl } = getFileInfo(id, config); const { fileId } = getFileInfo(id, config);
const code = await fs.readFile(fileId, 'utf-8'); const code = await fs.readFile(fileId, 'utf-8');
const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
const vfile = new VFile({ value: pageContent, path: id }); const vfile = new VFile({ value: pageContent, path: id });
// Ensure `data.astro` is available to all remark plugins // Ensure `data.astro` is available to all remark plugins
setVfileFrontmatter(vfile, frontmatter, { fileURL: new URL(fileUrl) }); setVfileFrontmatter(vfile, frontmatter);
try { try {
const compiled = await processor.process(vfile); const compiled = await processor.process(vfile);

View file

@ -1,5 +1,5 @@
import type { VFileData as Data, VFile } from 'vfile'; import type { VFileData as Data, VFile } from 'vfile';
import type { MarkdownAstroData, MarkdownProcessorRenderOptions } from './types.js'; import type { MarkdownAstroData } from './types.js';
function isValidAstroData(obj: unknown): obj is MarkdownAstroData { function isValidAstroData(obj: unknown): obj is MarkdownAstroData {
if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) { if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) {
@ -27,15 +27,10 @@ export function safelyGetAstroData(vfileData: Data): MarkdownAstroData | Invalid
return astro; return astro;
} }
export function setVfileFrontmatter( export function setVfileFrontmatter(vfile: VFile, frontmatter: Record<string, any>) {
vfile: VFile,
frontmatter: Record<string, any>,
renderOpts: MarkdownProcessorRenderOptions | undefined
) {
vfile.data ??= {}; vfile.data ??= {};
vfile.data.astro ??= {}; vfile.data.astro ??= {};
(vfile.data.astro as any).frontmatter = frontmatter; (vfile.data.astro as any).frontmatter = frontmatter;
(vfile.data.astro as any).fileURL = renderOpts?.fileURL;
} }
/** /**

View file

@ -124,9 +124,8 @@ export async function createMarkdownProcessor(
return { return {
async render(content, renderOpts) { async render(content, renderOpts) {
console.log('url', renderOpts?.fileURL);
const vfile = new VFile({ value: content, path: renderOpts?.fileURL }); const vfile = new VFile({ value: content, path: renderOpts?.fileURL });
setVfileFrontmatter(vfile, renderOpts?.frontmatter ?? {}, renderOpts); setVfileFrontmatter(vfile, renderOpts?.frontmatter ?? {});
const result: MarkdownVFile = await parser.process(vfile).catch((err) => { const result: MarkdownVFile = await parser.process(vfile).catch((err) => {
// Ensure that the error message contains the input filename // Ensure that the error message contains the input filename

3
pnpm-lock.yaml generated
View file

@ -1747,6 +1747,9 @@ importers:
packages/astro/test/fixtures/astro-basic: packages/astro/test/fixtures/astro-basic:
dependencies: dependencies:
'@astrojs/mdx':
specifier: workspace:*
version: link:../../../../integrations/mdx
'@astrojs/preact': '@astrojs/preact':
specifier: workspace:* specifier: workspace:*
version: link:../../../../integrations/preact version: link:../../../../integrations/preact