summaryrefslogtreecommitdiff
path: root/packages/integrations/mdx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/mdx')
-rw-r--r--packages/integrations/mdx/CHANGELOG.md23
-rw-r--r--packages/integrations/mdx/package.json8
-rw-r--r--packages/integrations/mdx/src/index.ts33
-rw-r--r--packages/integrations/mdx/src/plugins.ts2
-rw-r--r--packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts84
-rw-r--r--packages/integrations/mdx/src/rehype-collect-headings.ts6
-rw-r--r--packages/integrations/mdx/src/rehype-images-to-component.ts10
-rw-r--r--packages/integrations/mdx/src/server.ts73
-rw-r--r--packages/integrations/mdx/src/utils.ts6
-rw-r--r--packages/integrations/mdx/src/vite-plugin-mdx.ts31
-rw-r--r--packages/integrations/mdx/test/css-head-mdx.test.js7
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-images/src/content/config.ts5
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-images/tsconfig.json4
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-page/src/layouts/EncodingLayout.astro1
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-frontmatter.mdx7
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-manual.mdx12
-rw-r--r--packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding.mdx3
-rw-r--r--packages/integrations/mdx/test/mdx-page.test.js44
-rw-r--r--packages/integrations/mdx/test/mdx-vite-env-vars.test.js4
19 files changed, 308 insertions, 55 deletions
diff --git a/packages/integrations/mdx/CHANGELOG.md b/packages/integrations/mdx/CHANGELOG.md
index e4742f9ea..1b3baaeec 100644
--- a/packages/integrations/mdx/CHANGELOG.md
+++ b/packages/integrations/mdx/CHANGELOG.md
@@ -1,5 +1,28 @@
# @astrojs/mdx
+## 4.0.0-alpha.2
+
+### Patch Changes
+
+- [#11861](https://github.com/withastro/astro/pull/11861) [`3ab3b4e`](https://github.com/withastro/astro/commit/3ab3b4efbcdd2aabea5f949deedf51a5acefae59) Thanks [@bluwy](https://github.com/bluwy)! - Updates `@astrojs/markdown-remark` and handle its breaking changes
+
+- Updated dependencies [[`3ab3b4e`](https://github.com/withastro/astro/commit/3ab3b4efbcdd2aabea5f949deedf51a5acefae59), [`560ef15`](https://github.com/withastro/astro/commit/560ef15ad23bd137b56ef1048eb2df548b99fdce), [`3ab3b4e`](https://github.com/withastro/astro/commit/3ab3b4efbcdd2aabea5f949deedf51a5acefae59)]:
+ - @astrojs/markdown-remark@6.0.0-alpha.1
+
+## 4.0.0-alpha.1
+
+### Minor Changes
+
+- [#11741](https://github.com/withastro/astro/pull/11741) [`6617491`](https://github.com/withastro/astro/commit/6617491c3bc2bde87f7867d7dec2580781852cfc) Thanks [@bluwy](https://github.com/bluwy)! - Updates adapter server entrypoint to use `@astrojs/mdx/server.js`
+
+ This is an internal change. Handling JSX in your `.mdx` files has been moved from Astro internals and is now the responsibility of this integration. You should not notice a change in your project, and no update to your code is required.
+
+## 4.0.0-alpha.0
+
+- Updated dependencies [[`b6fbdaa`](https://github.com/withastro/astro/commit/b6fbdaa94a9ecec706a99e1938fbf5cd028c72e0), [`89bab1e`](https://github.com/withastro/astro/commit/89bab1e70786123fbe933a9d7a1b80c9334dcc5f), [`d74617c`](https://github.com/withastro/astro/commit/d74617cbd3278feba05909ec83db2d73d57a153e), [`83a2a64`](https://github.com/withastro/astro/commit/83a2a648418ad30f4eb781d1c1b5f2d8a8ac846e), [`e90f559`](https://github.com/withastro/astro/commit/e90f5593d23043579611452a84b9e18ad2407ef9), [`2df49a6`](https://github.com/withastro/astro/commit/2df49a6fb4f6d92fe45f7429430abe63defeacd6), [`8a53517`](https://github.com/withastro/astro/commit/8a5351737d6a14fc55f1dafad8f3b04079e81af6)]:
+ - astro@5.0.0-alpha.0
+ - @astrojs/markdown-remark@6.0.0-alpha.0
+
## 3.1.9
### Patch Changes
diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json
index cac6e8f4c..1783f3e89 100644
--- a/packages/integrations/mdx/package.json
+++ b/packages/integrations/mdx/package.json
@@ -1,7 +1,7 @@
{
"name": "@astrojs/mdx",
"description": "Add support for MDX pages in your Astro site",
- "version": "3.1.9",
+ "version": "4.0.0-beta.2",
"type": "module",
"types": "./dist/index.d.ts",
"author": "withastro",
@@ -20,6 +20,7 @@
"homepage": "https://docs.astro.build/en/guides/integrations-guide/mdx/",
"exports": {
".": "./dist/index.js",
+ "./server.js": "./dist/server.js",
"./package.json": "./package.json"
},
"files": [
@@ -38,7 +39,6 @@
"acorn": "^8.14.0",
"es-module-lexer": "^1.5.4",
"estree-util-visit": "^2.0.0",
- "gray-matter": "^4.0.3",
"hast-util-to-html": "^9.0.3",
"kleur": "^4.1.5",
"rehype-raw": "^7.0.0",
@@ -49,7 +49,7 @@
"vfile": "^6.0.3"
},
"peerDependencies": {
- "astro": "^4.8.0"
+ "astro": "^5.0.0-alpha.0"
},
"devDependencies": {
"@types/estree": "^1.0.6",
@@ -70,7 +70,7 @@
"remark-toc": "^9.0.0",
"shiki": "^1.22.2",
"unified": "^11.0.5",
- "vite": "^5.4.10"
+ "vite": "6.0.0-beta.2"
},
"engines": {
"node": "^18.17.1 || ^20.3.0 || >=21.0.0"
diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts
index de29003ff..fb6766e5f 100644
--- a/packages/integrations/mdx/src/index.ts
+++ b/packages/integrations/mdx/src/index.ts
@@ -8,13 +8,12 @@ import type {
ContentEntryType,
HookParameters,
} from 'astro';
-import astroJSXRenderer from 'astro/jsx/renderer.js';
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
import type { PluggableList } from 'unified';
import type { OptimizeOptions } from './rehype-optimize-static.js';
-import { ignoreStringPlugins, parseFrontmatter } from './utils.js';
+import { ignoreStringPlugins, safeParseFrontmatter } from './utils.js';
import { vitePluginMdxPostprocess } from './vite-plugin-mdx-postprocess.js';
-import { vitePluginMdx } from './vite-plugin-mdx.js';
+import { type VitePluginMdxOptions, vitePluginMdx } from './vite-plugin-mdx.js';
export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
extendMarkdownConfig: boolean;
@@ -37,14 +36,14 @@ type SetupHookParams = HookParameters<'astro:config:setup'> & {
export function getContainerRenderer(): ContainerRenderer {
return {
name: 'astro:jsx',
- serverEntrypoint: 'astro/jsx/server.js',
+ serverEntrypoint: '@astrojs/mdx/server.js',
};
}
export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration {
// @ts-expect-error Temporarily assign an empty object here, which will be re-assigned by the
// `astro:config:done` hook later. This is so that `vitePluginMdx` can get hold of a reference earlier.
- let mdxOptions: MdxOptions = {};
+ let vitePluginMdxOptions: VitePluginMdxOptions = {};
return {
name: '@astrojs/mdx',
@@ -53,17 +52,20 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
const { updateConfig, config, addPageExtension, addContentEntryType, addRenderer } =
params as SetupHookParams;
- addRenderer(astroJSXRenderer);
+ addRenderer({
+ name: 'astro:jsx',
+ serverEntrypoint: '@astrojs/mdx/server.js',
+ });
addPageExtension('.mdx');
addContentEntryType({
extensions: ['.mdx'],
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
- const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
+ const parsed = safeParseFrontmatter(contents, fileURLToPath(fileUrl));
return {
- data: parsed.data,
- body: parsed.content,
- slug: parsed.data.slug,
- rawData: parsed.matter,
+ data: parsed.frontmatter,
+ body: parsed.content.trim(),
+ slug: parsed.frontmatter.slug,
+ rawData: parsed.rawFrontmatter,
};
},
contentModuleTypes: await fs.readFile(
@@ -77,7 +79,7 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
updateConfig({
vite: {
- plugins: [vitePluginMdx(mdxOptions), vitePluginMdxPostprocess(config)],
+ plugins: [vitePluginMdx(vitePluginMdxOptions), vitePluginMdxPostprocess(config)],
},
});
},
@@ -96,10 +98,13 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
});
// Mutate `mdxOptions` so that `vitePluginMdx` can reference the actual options
- Object.assign(mdxOptions, resolvedMdxOptions);
+ Object.assign(vitePluginMdxOptions, {
+ mdxOptions: resolvedMdxOptions,
+ srcDir: config.srcDir,
+ });
// @ts-expect-error After we assign, we don't need to reference `mdxOptions` in this context anymore.
// Re-assign it so that the garbage can be collected later.
- mdxOptions = {};
+ vitePluginMdxOptions = {};
},
},
};
diff --git a/packages/integrations/mdx/src/plugins.ts b/packages/integrations/mdx/src/plugins.ts
index 082e8f6fd..77c76243c 100644
--- a/packages/integrations/mdx/src/plugins.ts
+++ b/packages/integrations/mdx/src/plugins.ts
@@ -83,7 +83,7 @@ function getRehypePlugins(mdxOptions: MdxOptions): PluggableList {
}
rehypePlugins.push(
- // Render info from `vfile.data.astro.data.frontmatter` as JS
+ // Render info from `vfile.data.astro.frontmatter` as JS
rehypeApplyFrontmatterExport,
// Analyze MDX nodes and attach to `vfile.data.__astroMetadata`
rehypeAnalyzeAstroMetadata,
diff --git a/packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts b/packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts
index 1b981a68e..5880c30b3 100644
--- a/packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts
+++ b/packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts
@@ -1,23 +1,35 @@
-import { InvalidAstroDataError } from '@astrojs/markdown-remark';
-import { safelyGetAstroData } from '@astrojs/markdown-remark/dist/internal.js';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { isFrontmatterValid } from '@astrojs/markdown-remark';
+import type { Root, RootContent } from 'hast';
import type { VFile } from 'vfile';
import { jsToTreeNode } from './utils.js';
+// Passed metadata to help determine adding charset utf8 by default
+declare module 'vfile' {
+ interface DataMap {
+ applyFrontmatterExport?: {
+ srcDir?: URL;
+ };
+ }
+}
+
+const exportConstPartialTrueRe = /export\s+const\s+partial\s*=\s*true/;
+
export function rehypeApplyFrontmatterExport() {
- return function (tree: any, vfile: VFile) {
- const astroData = safelyGetAstroData(vfile.data);
- if (astroData instanceof InvalidAstroDataError)
+ return function (tree: Root, vfile: VFile) {
+ const frontmatter = vfile.data.astro?.frontmatter;
+ if (!frontmatter || !isFrontmatterValid(frontmatter))
throw new Error(
// Copied from Astro core `errors-data`
// TODO: find way to import error data from core
'[MDX] A remark or rehype plugin attempted to inject invalid frontmatter. Ensure "astro.frontmatter" is set to a valid JSON object that is not `null` or `undefined`.',
);
- const { frontmatter } = astroData;
- const exportNodes = [
+ const extraChildren: RootContent[] = [
jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`),
];
if (frontmatter.layout) {
- exportNodes.unshift(
+ extraChildren.unshift(
jsToTreeNode(
// NOTE: Use `__astro_*` import names to prevent conflicts with user code
/** @see 'vite-plugin-markdown' for layout props reference */
@@ -41,7 +53,61 @@ export default function ({ children }) {
};`,
),
);
+ } else if (shouldAddCharset(tree, vfile)) {
+ extraChildren.unshift({
+ type: 'mdxJsxFlowElement',
+ name: 'meta',
+ attributes: [
+ {
+ type: 'mdxJsxAttribute',
+ name: 'charset',
+ value: 'utf-8',
+ },
+ ],
+ children: [],
+ });
}
- tree.children = exportNodes.concat(tree.children);
+ tree.children = extraChildren.concat(tree.children);
};
}
+
+/**
+ * If this is a page (e.g. in src/pages), has no layout frontmatter (handled before calling this function),
+ * has no leading component that looks like a wrapping layout, and `partial` isn't set to true, we default to
+ * adding charset=utf-8 like markdown so that users don't have to worry about it for MDX pages without layouts.
+ */
+function shouldAddCharset(tree: Root, vfile: VFile) {
+ const srcDirUrl = vfile.data.applyFrontmatterExport?.srcDir;
+ if (!srcDirUrl) return false;
+
+ const hasConstPartialTrue = tree.children.some(
+ (node) => node.type === 'mdxjsEsm' && exportConstPartialTrueRe.test(node.value),
+ );
+ if (hasConstPartialTrue) return false;
+
+ // NOTE: the pages directory is a non-configurable Astro behaviour
+ const pagesDir = path.join(fileURLToPath(srcDirUrl), 'pages').replace(/\\/g, '/');
+ // `vfile.path` comes from Vite, which is a normalized path (no backslashes)
+ const filePath = vfile.path;
+ if (!filePath.startsWith(pagesDir)) return false;
+
+ const hasLeadingUnderscoreInPath = filePath
+ .slice(pagesDir.length)
+ .replace(/\\/g, '/')
+ .split('/')
+ .some((part) => part.startsWith('_'));
+ if (hasLeadingUnderscoreInPath) return false;
+
+ // Bail if the first content found is a wrapping layout component
+ for (const child of tree.children) {
+ if (child.type === 'element') break;
+ if (child.type === 'mdxJsxFlowElement') {
+ // If is fragment or lowercase tag name (html tags), skip and assume there's no layout
+ if (child.name == null) break;
+ if (child.name[0] === child.name[0].toLowerCase()) break;
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/packages/integrations/mdx/src/rehype-collect-headings.ts b/packages/integrations/mdx/src/rehype-collect-headings.ts
index fafc59721..a51e8e9f0 100644
--- a/packages/integrations/mdx/src/rehype-collect-headings.ts
+++ b/packages/integrations/mdx/src/rehype-collect-headings.ts
@@ -1,9 +1,9 @@
-import type { MarkdownHeading, MarkdownVFile } from '@astrojs/markdown-remark';
+import type { VFile } from 'vfile';
import { jsToTreeNode } from './utils.js';
export function rehypeInjectHeadingsExport() {
- return function (tree: any, file: MarkdownVFile) {
- const headings: MarkdownHeading[] = file.data.__astroHeadings || [];
+ return function (tree: any, file: VFile) {
+ const headings = file.data.astro?.headings ?? [];
tree.children.unshift(
jsToTreeNode(`export function getHeadings() { return ${JSON.stringify(headings)} }`),
);
diff --git a/packages/integrations/mdx/src/rehype-images-to-component.ts b/packages/integrations/mdx/src/rehype-images-to-component.ts
index 95b500784..da2f25ee5 100644
--- a/packages/integrations/mdx/src/rehype-images-to-component.ts
+++ b/packages/integrations/mdx/src/rehype-images-to-component.ts
@@ -1,8 +1,8 @@
-import type { MarkdownVFile } from '@astrojs/markdown-remark';
import type { Properties, Root } from 'hast';
import type { MdxJsxAttribute, MdxjsEsm } from 'mdast-util-mdx';
import type { MdxJsxFlowElementHast } from 'mdast-util-mdx-jsx';
import { visit } from 'unist-util-visit';
+import type { VFile } from 'vfile';
import { jsToTreeNode } from './utils.js';
export const ASTRO_IMAGE_ELEMENT = 'astro-image';
@@ -72,18 +72,18 @@ function getImageComponentAttributes(props: Properties): MdxJsxAttribute[] {
}
export function rehypeImageToComponent() {
- return function (tree: Root, file: MarkdownVFile) {
- if (!file.data.imagePaths) return;
+ return function (tree: Root, file: VFile) {
+ if (!file.data.astro?.imagePaths) return;
const importsStatements: MdxjsEsm[] = [];
const importedImages = new Map<string, string>();
visit(tree, 'element', (node, index, parent) => {
- if (!file.data.imagePaths || node.tagName !== 'img' || !node.properties.src) return;
+ if (!file.data.astro?.imagePaths || node.tagName !== 'img' || !node.properties.src) return;
const src = decodeURI(String(node.properties.src));
- if (!file.data.imagePaths.has(src)) return;
+ if (!file.data.astro.imagePaths?.includes(src)) return;
let importName = importedImages.get(src);
diff --git a/packages/integrations/mdx/src/server.ts b/packages/integrations/mdx/src/server.ts
new file mode 100644
index 000000000..79934eb32
--- /dev/null
+++ b/packages/integrations/mdx/src/server.ts
@@ -0,0 +1,73 @@
+import type { NamedSSRLoadedRendererValue } from 'astro';
+import { AstroError } from 'astro/errors';
+import { AstroJSX, jsx } from 'astro/jsx-runtime';
+import { renderJSX } from 'astro/runtime/server/index.js';
+
+const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
+
+// NOTE: In practice, MDX components are always tagged with `__astro_tag_component__`, so the right renderer
+// is used directly, and this check is not often used to return true.
+export async function check(
+ Component: any,
+ props: any,
+ { default: children = null, ...slotted } = {},
+) {
+ if (typeof Component !== 'function') return false;
+ const slots: Record<string, any> = {};
+ for (const [key, value] of Object.entries(slotted)) {
+ const name = slotName(key);
+ slots[name] = value;
+ }
+ try {
+ const result = await Component({ ...props, ...slots, children });
+ return result[AstroJSX];
+ } catch (e) {
+ throwEnhancedErrorIfMdxComponent(e as Error, Component);
+ }
+ return false;
+}
+
+export async function renderToStaticMarkup(
+ this: any,
+ Component: any,
+ props = {},
+ { default: children = null, ...slotted } = {},
+) {
+ const slots: Record<string, any> = {};
+ for (const [key, value] of Object.entries(slotted)) {
+ const name = slotName(key);
+ slots[name] = value;
+ }
+
+ const { result } = this;
+ try {
+ const html = await renderJSX(result, jsx(Component, { ...props, ...slots, children }));
+ return { html };
+ } catch (e) {
+ throwEnhancedErrorIfMdxComponent(e as Error, Component);
+ throw e;
+ }
+}
+
+function throwEnhancedErrorIfMdxComponent(error: Error, Component: any) {
+ // if the exception is from an mdx component
+ // throw an error
+ if (Component[Symbol.for('mdx-component')]) {
+ // if it's an existing AstroError, we don't need to re-throw, keep the original hint
+ if (AstroError.is(error)) return;
+ // Mimic the fields of the internal `AstroError` class (not from `astro/errors`) to
+ // provide better title and hint for the error overlay
+ (error as any).title = error.name;
+ (error as any).hint =
+ `This issue often occurs when your MDX component encounters runtime errors.`;
+ throw error;
+ }
+}
+
+const renderer: NamedSSRLoadedRendererValue = {
+ name: 'astro:jsx',
+ check,
+ renderToStaticMarkup,
+};
+
+export default renderer;
diff --git a/packages/integrations/mdx/src/utils.ts b/packages/integrations/mdx/src/utils.ts
index ad98abb9e..7dcd4a14c 100644
--- a/packages/integrations/mdx/src/utils.ts
+++ b/packages/integrations/mdx/src/utils.ts
@@ -1,7 +1,7 @@
+import { parseFrontmatter } from '@astrojs/markdown-remark';
import type { Options as AcornOpts } from 'acorn';
import { parse } from 'acorn';
import type { AstroConfig, AstroIntegrationLogger, SSRError } from 'astro';
-import matter from 'gray-matter';
import { bold } from 'kleur/colors';
import type { MdxjsEsm } from 'mdast-util-mdx';
import type { PluggableList } from 'unified';
@@ -48,9 +48,9 @@ export function getFileInfo(id: string, config: AstroConfig): FileInfo {
* Match YAML exception handling from Astro core errors
* @see 'astro/src/core/errors.ts'
*/
-export function parseFrontmatter(code: string, id: string) {
+export function safeParseFrontmatter(code: string, id: string) {
try {
- return matter(code);
+ return parseFrontmatter(code, { frontmatter: 'empty-with-spaces' });
} catch (e: any) {
if (e.name === 'YAMLException') {
const err: SSRError = e;
diff --git a/packages/integrations/mdx/src/vite-plugin-mdx.ts b/packages/integrations/mdx/src/vite-plugin-mdx.ts
index 5a409d40d..869c65d26 100644
--- a/packages/integrations/mdx/src/vite-plugin-mdx.ts
+++ b/packages/integrations/mdx/src/vite-plugin-mdx.ts
@@ -1,13 +1,18 @@
-import { setVfileFrontmatter } from '@astrojs/markdown-remark';
import type { SSRError } from 'astro';
import { getAstroMetadata } from 'astro/jsx/rehype.js';
import { VFile } from 'vfile';
import type { Plugin } from 'vite';
import type { MdxOptions } from './index.js';
import { createMdxProcessor } from './plugins.js';
-import { parseFrontmatter } from './utils.js';
+import { safeParseFrontmatter } from './utils.js';
-export function vitePluginMdx(mdxOptions: MdxOptions): Plugin {
+export interface VitePluginMdxOptions {
+ mdxOptions: MdxOptions;
+ srcDir: URL;
+}
+
+// NOTE: Do not destructure `opts` as we're assigning a reference that will be mutated later
+export function vitePluginMdx(opts: VitePluginMdxOptions): Plugin {
let processor: ReturnType<typeof createMdxProcessor> | undefined;
let sourcemapEnabled: boolean;
@@ -39,16 +44,24 @@ export function vitePluginMdx(mdxOptions: MdxOptions): Plugin {
async transform(code, id) {
if (!id.endsWith('.mdx')) return;
- const { data: frontmatter, content: pageContent, matter } = parseFrontmatter(code, id);
- const frontmatterLines = matter ? matter.match(/\n/g)?.join('') + '\n\n' : '';
+ const { frontmatter, content } = safeParseFrontmatter(code, id);
- const vfile = new VFile({ value: frontmatterLines + pageContent, path: id });
- // Ensure `data.astro` is available to all remark plugins
- setVfileFrontmatter(vfile, frontmatter);
+ const vfile = new VFile({
+ value: content,
+ path: id,
+ data: {
+ astro: {
+ frontmatter,
+ },
+ applyFrontmatterExport: {
+ srcDir: opts.srcDir,
+ },
+ },
+ });
// Lazily initialize the MDX processor
if (!processor) {
- processor = createMdxProcessor(mdxOptions, { sourcemap: sourcemapEnabled });
+ processor = createMdxProcessor(opts.mdxOptions, { sourcemap: sourcemapEnabled });
}
try {
diff --git a/packages/integrations/mdx/test/css-head-mdx.test.js b/packages/integrations/mdx/test/css-head-mdx.test.js
index 4d4df5cec..96ee7c900 100644
--- a/packages/integrations/mdx/test/css-head-mdx.test.js
+++ b/packages/integrations/mdx/test/css-head-mdx.test.js
@@ -15,7 +15,6 @@ describe('Head injection w/ MDX', () => {
integrations: [mdx()],
// test suite was authored when inlineStylesheets defaulted to never
build: { inlineStylesheets: 'never' },
- experimental: { contentLayer: true },
});
});
@@ -24,14 +23,14 @@ describe('Head injection w/ MDX', () => {
await fixture.build();
});
- it('only injects contents into head', async () => {
+ it('injects content styles into head', async () => {
const html = await fixture.readFile('/indexThree/index.html');
const { document } = parseHTML(html);
const links = document.querySelectorAll('head link[rel=stylesheet]');
assert.equal(links.length, 1);
- const scripts = document.querySelectorAll('head script[type=module]');
+ const scripts = document.querySelectorAll('script[type=module]');
assert.equal(scripts.length, 1);
});
@@ -50,7 +49,7 @@ describe('Head injection w/ MDX', () => {
const links = document.querySelectorAll('head link[rel=stylesheet]');
assert.equal(links.length, 1);
- const scripts = document.querySelectorAll('head script[type=module]');
+ const scripts = document.querySelectorAll('script[type=module]');
assert.equal(scripts.length, 1);
});
diff --git a/packages/integrations/mdx/test/fixtures/mdx-images/src/content/config.ts b/packages/integrations/mdx/test/fixtures/mdx-images/src/content/config.ts
new file mode 100644
index 000000000..14443e78d
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/mdx-images/src/content/config.ts
@@ -0,0 +1,5 @@
+import { defineCollection, z } from 'astro:content';
+
+const blog = defineCollection({});
+
+export const collections = { blog };
diff --git a/packages/integrations/mdx/test/fixtures/mdx-images/tsconfig.json b/packages/integrations/mdx/test/fixtures/mdx-images/tsconfig.json
index b5bf6a715..c193287fc 100644
--- a/packages/integrations/mdx/test/fixtures/mdx-images/tsconfig.json
+++ b/packages/integrations/mdx/test/fixtures/mdx-images/tsconfig.json
@@ -5,5 +5,7 @@
"paths": {
"~/assets/*": ["src/assets/*"]
},
- }
+ },
+ "include": [".astro/types.d.ts", "**/*"],
+ "exclude": ["dist"]
}
diff --git a/packages/integrations/mdx/test/fixtures/mdx-page/src/layouts/EncodingLayout.astro b/packages/integrations/mdx/test/fixtures/mdx-page/src/layouts/EncodingLayout.astro
new file mode 100644
index 000000000..13e0e91ed
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/mdx-page/src/layouts/EncodingLayout.astro
@@ -0,0 +1 @@
+<slot></slot>
diff --git a/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-frontmatter.mdx b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-frontmatter.mdx
new file mode 100644
index 000000000..471827de0
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-frontmatter.mdx
@@ -0,0 +1,7 @@
+---
+layout: ../layouts/EncodingLayout.astro
+---
+
+# 我的第一篇博客文章
+
+发表于:2022-07-01
diff --git a/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-manual.mdx b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-manual.mdx
new file mode 100644
index 000000000..1c8c78630
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding-layout-manual.mdx
@@ -0,0 +1,12 @@
+import EncodingLayout from '../layouts/EncodingLayout.astro'
+
+{/* Ensure random stuff preceding the wrapper layout is ignored when detecting a wrapper layout */}
+export const foo = {}
+
+<EncodingLayout>
+
+# 我的第一篇博客文章
+
+发表于:2022-07-01
+
+</EncodingLayout>
diff --git a/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding.mdx b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding.mdx
new file mode 100644
index 000000000..572b3c370
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/mdx-page/src/pages/chinese-encoding.mdx
@@ -0,0 +1,3 @@
+# 我的第一篇博客文章
+
+发表于:2022-07-01
diff --git a/packages/integrations/mdx/test/mdx-page.test.js b/packages/integrations/mdx/test/mdx-page.test.js
index 7948de653..b58781efc 100644
--- a/packages/integrations/mdx/test/mdx-page.test.js
+++ b/packages/integrations/mdx/test/mdx-page.test.js
@@ -1,5 +1,6 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
+import * as cheerio from 'cheerio';
import { parseHTML } from 'linkedom';
import { loadFixture } from '../../../astro/test/test-utils.js';
@@ -36,6 +37,23 @@ describe('MDX Page', () => {
assert.notEqual(stylesheet, null);
});
+
+ it('Renders MDX in utf-8 by default', async () => {
+ const html = await fixture.readFile('/chinese-encoding/index.html');
+ const $ = cheerio.load(html);
+ assert.equal($('h1').text(), '我的第一篇博客文章');
+ assert.match(html, /<meta charset="utf-8"/);
+ });
+
+ it('Renders MDX with layout frontmatter without utf-8 by default', async () => {
+ const html = await fixture.readFile('/chinese-encoding-layout-frontmatter/index.html');
+ assert.doesNotMatch(html, /<meta charset="utf-8"/);
+ });
+
+ it('Renders MDX with layout manual import without utf-8 by default', async () => {
+ const html = await fixture.readFile('/chinese-encoding-layout-manual/index.html');
+ assert.doesNotMatch(html, /<meta charset="utf-8"/);
+ });
});
describe('dev', () => {
@@ -61,5 +79,31 @@ describe('MDX Page', () => {
assert.equal(h1.textContent, 'Hello page!');
});
+
+ it('Renders MDX in utf-8 by default', async () => {
+ const res = await fixture.fetch('/chinese-encoding/');
+ assert.equal(res.status, 200);
+ const html = await res.text();
+ const $ = cheerio.load(html);
+ assert.equal($('h1').text(), '我的第一篇博客文章');
+ assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.match(html, /<meta charset="utf-8"/);
+ });
+
+ it('Renders MDX with layout frontmatter without utf-8 by default', async () => {
+ const res = await fixture.fetch('/chinese-encoding-layout-frontmatter/');
+ assert.equal(res.status, 200);
+ const html = await res.text();
+ assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.doesNotMatch(html, /<meta charset="utf-8"/);
+ });
+
+ it('Renders MDX with layout manual import without utf-8 by default', async () => {
+ const res = await fixture.fetch('/chinese-encoding-layout-manual/');
+ assert.equal(res.status, 200);
+ const html = await res.text();
+ assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.doesNotMatch(html, /<meta charset="utf-8"/);
+ });
});
});
diff --git a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js b/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
index 80a9b1cec..213386ceb 100644
--- a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
+++ b/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
@@ -57,8 +57,8 @@ describe('MDX - Vite env vars', () => {
const dataAttrDump = document.querySelector('[data-env-dump]');
assert.notEqual(dataAttrDump, null);
- assert.notEqual(dataAttrDump.getAttribute('data-env-prod'), null);
- assert.equal(dataAttrDump.getAttribute('data-env-dev'), null);
+ assert.equal(dataAttrDump.getAttribute('data-env-prod'), 'true');
+ assert.equal(dataAttrDump.getAttribute('data-env-dev'), 'false');
assert.equal(dataAttrDump.getAttribute('data-env-base-url'), '/');
assert.equal(dataAttrDump.getAttribute('data-env-mode'), 'production');
});