Feat: new legacy.astroFlavoredMarkdown
flag (#4016)
* refactor: add legacy.jsxInMarkdown flag to config * refactor: jsxInMarkdown -> astroFlavoredMarkdown * refactor: remove `markdown.mode` * feat: wire up legacy.astroFlavoredMarkdown * test: add legacy to astro-markdown fixture * test: remark autolinking * test: remark components * test: remark expressions * test: remark strictness * chore: remove "mode" from md component * chore: remove "mode: md" from tests * Fixing legacy MD tests, adding named slots tests for MDX pages * chore: update lock file * WIP: debugging named slots in MDX * fix: handle named slots in MDX properly * chore: re-enabling slots tests for MDX pages * fixing test validation for svelte & vue * removing unused Tailwind test * legacy flag for Markdown component tests * adding is:raw to Markdown component test * adding is:raw to all Markdown component test fixtures * can't use is:raw when nesting markdown components * another nested test can't use is:raw * one more <Markdown> test fix * fixing another JSX markdown component test * chore: add changeset * e2e tests were missing the legacy flag * removing the broken tailwind E2E markdown page Co-authored-by: Tony Sullivan <tony.f.sullivan@outlook.com> Co-authored-by: Nate Moore <nate@astro.build>
This commit is contained in:
parent
c17efc1ad9
commit
00fab4ce13
64 changed files with 481 additions and 223 deletions
20
.changeset/two-hounds-sort.md
Normal file
20
.changeset/two-hounds-sort.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
'astro': minor
|
||||
'@astrojs/markdown-component': minor
|
||||
'@astrojs/markdown-remark': minor
|
||||
---
|
||||
|
||||
The use of components and JSX expressions in Markdown are no longer supported by default.
|
||||
|
||||
For long term support, migrate to the `@astrojs/mdx` integration for MDX support (including `.mdx` pages!).
|
||||
|
||||
Not ready to migrate to MDX? Add the legacy flag to your Astro config to re-enable the previous Markdown support.
|
||||
|
||||
```js
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
}
|
||||
});
|
||||
```
|
|
@ -3,5 +3,8 @@ import preact from '@astrojs/preact';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [preact({ compat: true })],
|
||||
});
|
||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [preact(), mdx()],
|
||||
});
|
||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [react(), mdx()],
|
||||
});
|
||||
|
|
|
@ -4,5 +4,8 @@ import solid from '@astrojs/solid-js';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [solid(), mdx()],
|
||||
});
|
||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [svelte(), mdx()],
|
||||
});
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
title: "Markdown + Tailwind"
|
||||
setup: |
|
||||
import Button from '../components/Button.astro';
|
||||
import Complex from '../components/Complex.astro';
|
||||
---
|
||||
|
||||
<div class="grid place-items-center h-screen content-center">
|
||||
<Button>Tailwind Button in Markdown!</Button>
|
||||
<Complex />
|
||||
</div>
|
|
@ -4,6 +4,9 @@ import mdx from '@astrojs/mdx';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [
|
||||
mdx(),
|
||||
vue({
|
||||
|
|
|
@ -527,27 +527,6 @@ export interface AstroUserConfig {
|
|||
*/
|
||||
drafts?: boolean;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name markdown.mode
|
||||
* @type {'md' | 'mdx'}
|
||||
* @default `mdx`
|
||||
* @description
|
||||
* Control whether Markdown processing is done using MDX or not.
|
||||
*
|
||||
* MDX processing enables you to use JSX inside your Markdown files. However, there may be instances where you don't want this behavior, and would rather use a "vanilla" Markdown processor. This field allows you to control that behavior.
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* markdown: {
|
||||
* // Example: Use non-MDX processor for Markdown files
|
||||
* mode: 'md',
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
mode?: 'md' | 'mdx';
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name markdown.shikiConfig
|
||||
|
@ -716,6 +695,16 @@ export interface AstroUserConfig {
|
|||
buildOptions?: never;
|
||||
/** @deprecated `devOptions` has been renamed to `server` */
|
||||
devOptions?: never;
|
||||
|
||||
legacy?: {
|
||||
/**
|
||||
* Enable components and JSX expressions in markdown
|
||||
* Consider our MDX integration before applying this flag!
|
||||
* @see https://docs.astro.build/en/guides/integrations-guide/mdx/
|
||||
* Default: false
|
||||
*/
|
||||
astroFlavoredMarkdown?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
|
||||
|
|
|
@ -213,7 +213,10 @@ async function generatePath(
|
|||
adapterName: undefined,
|
||||
links,
|
||||
logging,
|
||||
markdown: astroConfig.markdown,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||
},
|
||||
mod,
|
||||
mode: opts.mode,
|
||||
origin,
|
||||
|
|
|
@ -153,7 +153,10 @@ function buildManifest(
|
|||
routes,
|
||||
site: astroConfig.site,
|
||||
base: astroConfig.base,
|
||||
markdown: astroConfig.markdown,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||
},
|
||||
pageMap: null as any,
|
||||
renderers: [],
|
||||
entryModules,
|
||||
|
|
|
@ -50,6 +50,9 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
|||
rehypePlugins: [],
|
||||
},
|
||||
vite: {},
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: false,
|
||||
}
|
||||
};
|
||||
|
||||
async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
|
||||
|
@ -172,9 +175,6 @@ export const AstroConfigSchema = z.object({
|
|||
.default({}),
|
||||
markdown: z
|
||||
.object({
|
||||
// NOTE: "mdx" allows us to parse/compile Astro components in markdown.
|
||||
// TODO: This should probably be updated to something more like "md" | "astro"
|
||||
mode: z.enum(['md', 'mdx']).default('mdx'),
|
||||
drafts: z.boolean().default(false),
|
||||
syntaxHighlight: z
|
||||
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
|
||||
|
@ -212,6 +212,12 @@ export const AstroConfigSchema = z.object({
|
|||
vite: z
|
||||
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
|
||||
.default(ASTRO_CONFIG_DEFAULTS.vite),
|
||||
legacy: z
|
||||
.object({
|
||||
astroFlavoredMarkdown: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.legacy.astroFlavoredMarkdown),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
});
|
||||
|
||||
/** Turn raw config values into normalized values */
|
||||
|
|
|
@ -173,7 +173,10 @@ export async function render(
|
|||
links,
|
||||
styles,
|
||||
logging,
|
||||
markdown: astroConfig.markdown,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown
|
||||
},
|
||||
mod,
|
||||
mode,
|
||||
origin,
|
||||
|
|
|
@ -29,23 +29,24 @@ export function transformSlots(vnode: AstroVNode) {
|
|||
delete child.props.slot;
|
||||
delete vnode.props.children;
|
||||
}
|
||||
if (!Array.isArray(vnode.props.children)) return;
|
||||
// Handle many children with slot attributes
|
||||
vnode.props.children = vnode.props.children
|
||||
.map((child) => {
|
||||
if (!isVNode(child)) return child;
|
||||
if (!('slot' in child.props)) return child;
|
||||
const name = toSlotName(child.props.slot);
|
||||
if (Array.isArray(slots[name])) {
|
||||
slots[name].push(child);
|
||||
} else {
|
||||
slots[name] = [child];
|
||||
slots[name]['$$slot'] = true;
|
||||
}
|
||||
delete child.props.slot;
|
||||
return Empty;
|
||||
})
|
||||
.filter((v) => v !== Empty);
|
||||
if (Array.isArray(vnode.props.children)) {
|
||||
// Handle many children with slot attributes
|
||||
vnode.props.children = vnode.props.children
|
||||
.map((child) => {
|
||||
if (!isVNode(child)) return child;
|
||||
if (!('slot' in child.props)) return child;
|
||||
const name = toSlotName(child.props.slot);
|
||||
if (Array.isArray(slots[name])) {
|
||||
slots[name].push(child);
|
||||
} else {
|
||||
slots[name] = [child];
|
||||
slots[name]['$$slot'] = true;
|
||||
}
|
||||
delete child.props.slot;
|
||||
return Empty;
|
||||
})
|
||||
.filter((v) => v !== Empty);
|
||||
}
|
||||
Object.assign(vnode.props, slots);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ let consoleFilterRefs = 0;
|
|||
export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
||||
switch (true) {
|
||||
case vnode instanceof HTMLString:
|
||||
if (vnode.toString().trim() === '') {
|
||||
return '';
|
||||
}
|
||||
return vnode;
|
||||
case typeof vnode === 'string':
|
||||
return markHTMLString(escapeHTML(vnode));
|
||||
|
@ -55,6 +58,9 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
|||
}
|
||||
|
||||
if (vnode.type) {
|
||||
if (typeof vnode.type === 'function' && (vnode.type as any)['astro:renderer']) {
|
||||
skipAstroJSXCheck.add(vnode.type)
|
||||
}
|
||||
if (typeof vnode.type === 'function' && vnode.props['server:root']) {
|
||||
const output = await vnode.type(vnode.props ?? {});
|
||||
return await renderJSX(result, output);
|
||||
|
@ -76,7 +82,7 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
|||
}
|
||||
|
||||
const { children = null, ...props } = vnode.props ?? {};
|
||||
const slots: Record<string, any> = {
|
||||
const _slots: Record<string, any> = {
|
||||
default: [],
|
||||
};
|
||||
function extractSlots(child: any): any {
|
||||
|
@ -84,19 +90,32 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
|||
return child.map((c) => extractSlots(c));
|
||||
}
|
||||
if (!isVNode(child)) {
|
||||
return slots.default.push(child);
|
||||
_slots.default.push(child);
|
||||
return
|
||||
}
|
||||
if ('slot' in child.props) {
|
||||
slots[child.props.slot] = [...(slots[child.props.slot] ?? []), child];
|
||||
_slots[child.props.slot] = [...(_slots[child.props.slot] ?? []), child];
|
||||
delete child.props.slot;
|
||||
return;
|
||||
}
|
||||
slots.default.push(child);
|
||||
_slots.default.push(child);
|
||||
}
|
||||
extractSlots(children);
|
||||
for (const [key, value] of Object.entries(slots)) {
|
||||
slots[key] = () => renderJSX(result, value);
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (value['$$slot']) {
|
||||
_slots[key] = value;
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
const slotPromises = [];
|
||||
const slots: Record<string, any> = {};
|
||||
for (const [key, value] of Object.entries(_slots)) {
|
||||
slotPromises.push(renderJSX(result, value).then(output => {
|
||||
if (output.toString().trim().length === 0) return;
|
||||
slots[key] = () => output;
|
||||
}))
|
||||
}
|
||||
await Promise.all(slotPromises);
|
||||
|
||||
let output: string | AsyncIterable<string>;
|
||||
if (vnode.type === ClientOnlyPlaceholder && vnode.props['client:only']) {
|
||||
|
|
|
@ -137,7 +137,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
|||
const filename = normalizeFilename(id);
|
||||
const source = await fs.promises.readFile(filename, 'utf8');
|
||||
const renderOpts = config.markdown;
|
||||
const isMDX = renderOpts.mode === 'mdx';
|
||||
const isAstroFlavoredMd = config.legacy.astroFlavoredMarkdown;
|
||||
|
||||
const fileUrl = new URL(`file://${filename}`);
|
||||
const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname);
|
||||
|
@ -149,7 +149,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
|||
// Turn HTML comments into JS comments while preventing nested `*/` sequences
|
||||
// from ending the JS comment by injecting a zero-width space
|
||||
// Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin.
|
||||
if (isMDX) {
|
||||
if (isAstroFlavoredMd) {
|
||||
markdownContent = markdownContent.replace(
|
||||
/<\s*!--([^-->]*)(.*?)-->/gs,
|
||||
(whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}`
|
||||
|
@ -159,6 +159,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
|||
let renderResult = await renderMarkdown(markdownContent, {
|
||||
...renderOpts,
|
||||
fileURL: fileUrl,
|
||||
isAstroFlavoredMd,
|
||||
} as any);
|
||||
let { code: astroResult, metadata } = renderResult;
|
||||
const { layout = '', components = '', setup = '', ...content } = frontmatter;
|
||||
|
@ -168,9 +169,9 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
|||
const prelude = `---
|
||||
import Slugger from 'github-slugger';
|
||||
${layout ? `import Layout from '${layout}';` : ''}
|
||||
${isMDX && components ? `import * from '${components}';` : ''}
|
||||
${isAstroFlavoredMd && components ? `import * from '${components}';` : ''}
|
||||
${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''}
|
||||
${isMDX ? setup : ''}
|
||||
${isAstroFlavoredMd ? setup : ''}
|
||||
|
||||
const slugger = new Slugger();
|
||||
function $$slug(value) {
|
||||
|
@ -178,7 +179,7 @@ function $$slug(value) {
|
|||
}
|
||||
|
||||
const $$content = ${JSON.stringify(
|
||||
isMDX
|
||||
isAstroFlavoredMd
|
||||
? content
|
||||
: // Avoid stripping "setup" and "components"
|
||||
// in plain MD mode
|
||||
|
@ -186,11 +187,11 @@ const $$content = ${JSON.stringify(
|
|||
)}
|
||||
---`;
|
||||
const imports = `${layout ? `import Layout from '${layout}';` : ''}
|
||||
${isMDX ? setup : ''}`.trim();
|
||||
${isAstroFlavoredMd ? setup : ''}`.trim();
|
||||
|
||||
// Wrap with set:html fragment to skip
|
||||
// JSX expressions and components in "plain" md mode
|
||||
if (!isMDX) {
|
||||
if (!isAstroFlavoredMd) {
|
||||
astroResult = `<Fragment set:html={${JSON.stringify(astroResult)}} />`;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ describe('Astro Markdown - plain MD mode', () => {
|
|||
root: './fixtures/astro-markdown-md-mode/',
|
||||
markdown: {
|
||||
syntaxHighlight: 'prism',
|
||||
mode: 'md',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
|
|
|
@ -2,5 +2,8 @@ import { defineConfig } from 'astro/config';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: []
|
||||
});
|
||||
|
|
|
@ -3,9 +3,6 @@ import svelte from "@astrojs/svelte";
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
markdown: {
|
||||
mode: 'md',
|
||||
},
|
||||
integrations: [svelte()],
|
||||
site: 'https://astro.build/',
|
||||
});
|
||||
|
|
|
@ -6,4 +6,7 @@ import svelte from "@astrojs/svelte";
|
|||
export default defineConfig({
|
||||
integrations: [preact(), svelte()],
|
||||
site: 'https://astro.build/',
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
}
|
||||
});
|
||||
|
|
9
packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs
vendored
Normal file
9
packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: []
|
||||
});
|
|
@ -1,7 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import preact from '@astrojs/preact';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [preact()],
|
||||
});
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [preact(), mdx()],
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/preact": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
|
|
7
packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx
vendored
Normal file
7
packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Counter from '../components/Counter.jsx'
|
||||
|
||||
# Slots: Preact
|
||||
|
||||
<Counter case="content" client:visible><h1 id="slotted">Hello world!</h1></Counter>
|
||||
<Counter case="named" client:visible><h1 slot="named"> / Named</h1></Counter>
|
||||
<Counter case="dash-case" client:visible><h1 slot="dash-case"> / Dash Case</h1></Counter>
|
|
@ -1,7 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import react from '@astrojs/react';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [react()],
|
||||
});
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [react(), mdx()],
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/react": "workspace:*",
|
||||
"astro": "workspace:*",
|
||||
"react": "^18.1.0",
|
||||
|
|
7
packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx
vendored
Normal file
7
packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Counter from '../components/Counter.jsx'
|
||||
|
||||
# Slots: React
|
||||
|
||||
<Counter case="content" client:visible><h1 id="slotted">Hello world!</h1></Counter>
|
||||
<Counter case="named" client:visible><h1 slot="named"> / Named</h1></Counter>
|
||||
<Counter case="dash-case" client:visible><h1 slot="dash-case"> / Dash Case</h1></Counter>
|
|
@ -1,7 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import solid from '@astrojs/solid-js';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [solid()],
|
||||
});
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [solid(), mdx()],
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/solid-js": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
|
|
7
packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx
vendored
Normal file
7
packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Counter from '../components/Counter.jsx'
|
||||
|
||||
# Slots: Solid
|
||||
|
||||
<Counter case="content" client:visible><h1 id="slotted">Hello world!</h1></Counter>
|
||||
<Counter case="named" client:visible><h1 slot="named"> / Named</h1></Counter>
|
||||
<Counter case="dash-case" client:visible><h1 slot="dash-case"> / Dash Case</h1></Counter>
|
|
@ -1,7 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import svelte from '@astrojs/svelte';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [svelte()],
|
||||
});
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [svelte(), mdx()],
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/svelte": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
|
|
7
packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx
vendored
Normal file
7
packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Counter from '../components/Counter.svelte'
|
||||
|
||||
# Slots: Svelte
|
||||
|
||||
<Counter id="content" client:visible><h1 id="slotted">Hello world!</h1></Counter>
|
||||
<Counter id="named" client:visible><h1 slot="named"> / Named</h1></Counter>
|
||||
<Counter id="dash-case" client:visible><h1 slot="dash-case"> / Dash Case</h1></Counter>
|
|
@ -1,7 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import vue from '@astrojs/vue';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [vue()],
|
||||
});
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [vue(), mdx()],
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"@astrojs/vue": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
|
|
7
packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx
vendored
Normal file
7
packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Counter from '../components/Counter.vue'
|
||||
|
||||
# Slots: Vue
|
||||
|
||||
<Counter case="content" client:visible><h1 id="slotted">Hello world!</h1></Counter>
|
||||
<Counter case="named" client:visible><h1 slot="named"> / Named</h1></Counter>
|
||||
<Counter case="dash-case" client:visible><h1 slot="dash-case"> / Dash Case</h1></Counter>
|
|
@ -3,10 +3,13 @@ import tailwind from '@astrojs/tailwind';
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [tailwind()],
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: Preact', () => {
|
|||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
|
||||
describe('For MDX Pages', () => {
|
||||
it('Renders default slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#content').text().trim()).to.equal('Hello world!');
|
||||
});
|
||||
|
||||
it('Renders named slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#named').text().trim()).to.equal('Fallback / Named');
|
||||
});
|
||||
|
||||
it('Converts dash-case slot to camelCase', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: React', () => {
|
|||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
|
||||
describe('For MDX Pages', () => {
|
||||
it('Renders default slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#content').text().trim()).to.equal('Hello world!');
|
||||
});
|
||||
|
||||
it('Renders named slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#named').text().trim()).to.equal('Fallback / Named');
|
||||
});
|
||||
|
||||
it('Converts dash-case slot to camelCase', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: Solid', () => {
|
|||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
|
||||
describe('For MDX Pages', () => {
|
||||
it('Renders default slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#content').text().trim()).to.equal('Hello world!');
|
||||
});
|
||||
|
||||
it('Renders named slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#named').text().trim()).to.equal('Fallback / Named');
|
||||
});
|
||||
|
||||
it('Converts dash-case slot to camelCase', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: Svelte', () => {
|
|||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
|
||||
describe('For MDX Pages', () => {
|
||||
it('Renders default slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#content').text().trim()).to.equal('Hello world!');
|
||||
});
|
||||
|
||||
it('Renders named slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#named').text().trim()).to.equal('Fallback / Named');
|
||||
});
|
||||
|
||||
it('Preserves dash-case slot', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: Vue', () => {
|
|||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
|
||||
describe('For MDX Pages', () => {
|
||||
it('Renders default slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#content').text().trim()).to.equal('Hello world!');
|
||||
});
|
||||
|
||||
it('Renders named slot', async () => {
|
||||
const html = await fixture.readFile('/mdx/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#named').text().trim()).to.equal('Fallback / Named');
|
||||
});
|
||||
|
||||
it('Converts dash-case slot to camelCase', async () => {
|
||||
const html = await fixture.readFile('/markdown/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,7 +41,6 @@ if (!content) {
|
|||
|
||||
if (content) {
|
||||
htmlContent = await (Astro as any).__renderMarkdown(content, {
|
||||
mode: 'md',
|
||||
$: {
|
||||
scopedClassName: className,
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
</Markdown>
|
||||
</Layout>
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```rinfo
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```js
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```js
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```
|
||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
# Hello world
|
||||
|
||||
```
|
||||
|
|
|
@ -4,6 +4,9 @@ import svelte from "@astrojs/svelte";
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: true,
|
||||
},
|
||||
integrations: [preact(), svelte()],
|
||||
site: 'https://astro.build/',
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ const title = 'My Blog Post';
|
|||
const description = 'This is a post about some stuff.';
|
||||
---
|
||||
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
## Interesting Topic
|
||||
|
||||
```js
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import Markdown from '@astrojs/markdown-component';
|
||||
import Layout from '../layouts/content.astro';
|
||||
import Hello from '../components/Hello.jsx';
|
||||
import Counter from '../components/Counter.jsx';
|
||||
|
||||
const title = 'My Blog Post';
|
||||
const description = 'This is a post about some stuff.';
|
||||
|
@ -13,6 +12,5 @@ const description = 'This is a post about some stuff.';
|
|||
## Interesting Topic
|
||||
|
||||
<Hello name={`world`} />
|
||||
<Counter client:load />
|
||||
</Markdown>
|
||||
</Layout>
|
||||
|
|
|
@ -10,19 +10,19 @@ const description = 'This is a post about some stuff.';
|
|||
|
||||
<div id="deep">
|
||||
<section class="a">
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
## A
|
||||
</Markdown>
|
||||
</section>
|
||||
|
||||
<section class="b">
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
## B
|
||||
</Markdown>
|
||||
</section>
|
||||
|
||||
<section class="c">
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
## C
|
||||
</Markdown>
|
||||
</section>
|
||||
|
|
|
@ -20,7 +20,7 @@ const content = `
|
|||
<body>
|
||||
<h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
|
||||
<div id="target">
|
||||
<Markdown>
|
||||
<Markdown is:raw>
|
||||
- list
|
||||
- nested list
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
import Markdown from '@astrojs/markdown-component';
|
||||
---
|
||||
|
||||
<Markdown></Markdown>
|
||||
<Markdown is:raw></Markdown>
|
||||
|
|
|
@ -29,24 +29,23 @@ export const DEFAULT_REHYPE_PLUGINS = [];
|
|||
/** Shared utility for rendering markdown */
|
||||
export async function renderMarkdown(
|
||||
content: string,
|
||||
opts: MarkdownRenderingOptions = {}
|
||||
opts: MarkdownRenderingOptions,
|
||||
): Promise<MarkdownRenderingResult> {
|
||||
let {
|
||||
fileURL,
|
||||
mode = 'mdx',
|
||||
syntaxHighlight = 'shiki',
|
||||
shikiConfig = {},
|
||||
remarkPlugins = [],
|
||||
rehypePlugins = [],
|
||||
isAstroFlavoredMd = false,
|
||||
} = opts;
|
||||
const input = new VFile({ value: content, path: fileURL });
|
||||
const scopedClassName = opts.$?.scopedClassName;
|
||||
const isMDX = mode === 'mdx';
|
||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
||||
|
||||
let parser = unified()
|
||||
.use(markdown)
|
||||
.use(isMDX ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []);
|
||||
.use(isAstroFlavoredMd ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []);
|
||||
|
||||
if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
|
||||
remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
|
||||
|
@ -75,7 +74,7 @@ export async function renderMarkdown(
|
|||
markdownToHtml as any,
|
||||
{
|
||||
allowDangerousHtml: true,
|
||||
passThrough: isMDX
|
||||
passThrough: isAstroFlavoredMd
|
||||
? [
|
||||
'raw',
|
||||
'mdxFlowExpression',
|
||||
|
@ -94,7 +93,7 @@ export async function renderMarkdown(
|
|||
|
||||
parser
|
||||
.use(
|
||||
isMDX
|
||||
isAstroFlavoredMd
|
||||
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeaders]
|
||||
: [rehypeCollectHeaders, rehypeRaw]
|
||||
)
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|||
$?: {
|
||||
scopedClassName: string | null;
|
||||
};
|
||||
isAstroFlavoredMd?: boolean;
|
||||
}
|
||||
|
||||
export interface MarkdownHeader {
|
||||
|
|
|
@ -2,91 +2,107 @@ import { renderMarkdown } from '../dist/index.js';
|
|||
import chai from 'chai';
|
||||
|
||||
describe('autolinking', () => {
|
||||
it('autolinks URLs starting with a protocol in plain text', async () => {
|
||||
const { code } = await renderMarkdown(`See https://example.com for more.`, {});
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(`<p>See <a href="https://example.com">https://example.com</a> for more.</p>`);
|
||||
});
|
||||
|
||||
it('autolinks URLs starting with "www." in plain text', async () => {
|
||||
const { code } = await renderMarkdown(`See www.example.com for more.`, {});
|
||||
|
||||
chai
|
||||
.expect(code.trim())
|
||||
.to.equal(`<p>See <a href="http://www.example.com">www.example.com</a> for more.</p>`);
|
||||
});
|
||||
|
||||
it('does not autolink URLs in code blocks', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
'See `https://example.com` or `www.example.com` for more.',
|
||||
{}
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.trim())
|
||||
.to.equal(
|
||||
`<p>See <code is:raw>https://example.com</code> or ` +
|
||||
`<code is:raw>www.example.com</code> for more.</p>`
|
||||
describe('plain md', () => {
|
||||
it('autolinks URLs starting with a protocol in plain text', async () => {
|
||||
const { code } = await renderMarkdown(`See https://example.com for more.`, {});
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(`<p>See <a href="https://example.com">https://example.com</a> for more.</p>`);
|
||||
});
|
||||
|
||||
it('autolinks URLs starting with "www." in plain text', async () => {
|
||||
const { code } = await renderMarkdown(`See www.example.com for more.`, {});
|
||||
|
||||
chai
|
||||
.expect(code.trim())
|
||||
.to.equal(`<p>See <a href="http://www.example.com">www.example.com</a> for more.</p>`);
|
||||
});
|
||||
|
||||
it('does not autolink URLs in code blocks', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
'See `https://example.com` or `www.example.com` for more.',
|
||||
{}
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.trim())
|
||||
.to.equal(
|
||||
`<p>See <code>https://example.com</code> or ` +
|
||||
`<code>www.example.com</code> for more.</p>`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not autolink URLs in fenced code blocks', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
'Example:\n```\nGo to https://example.com or www.example.com now.\n```',
|
||||
{}
|
||||
);
|
||||
describe('astro-flavored md', () => {
|
||||
const renderAstroMd = text => renderMarkdown(text, { isAstroFlavoredMd: true });
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
.to.contain(`<pre is:raw`)
|
||||
.to.contain(`Go to https://example.com or www.example.com now.`);
|
||||
});
|
||||
|
||||
it('does not autolink URLs starting with a protocol when nested inside links', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
`See [http://example.com](http://example.com) or ` +
|
||||
`<a test href="https://example.com">https://example.com</a>`,
|
||||
{}
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<p>See <a href="http://example.com">http://example.com</a> or ` +
|
||||
`<a test href="https://example.com">https://example.com</a></p>`
|
||||
it('does not autolink URLs in code blocks', async () => {
|
||||
const { code } = await renderAstroMd(
|
||||
'See `https://example.com` or `www.example.com` for more.',
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('does not autolink URLs starting with "www." when nested inside links', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
`See [www.example.com](https://www.example.com) or ` +
|
||||
`<a test href="https://www.example.com">www.example.com</a>`,
|
||||
{}
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<p>See <a href="https://www.example.com">www.example.com</a> or ` +
|
||||
`<a test href="https://www.example.com">www.example.com</a></p>`
|
||||
|
||||
chai
|
||||
.expect(code.trim())
|
||||
.to.equal(
|
||||
`<p>See <code is:raw>https://example.com</code> or ` +
|
||||
`<code is:raw>www.example.com</code> for more.</p>`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not autolink URLs in fenced code blocks', async () => {
|
||||
const { code } = await renderAstroMd(
|
||||
'Example:\n```\nGo to https://example.com or www.example.com now.\n```'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not autolink URLs when nested several layers deep inside links', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
`<a href="https://www.example.com">**Visit _our www.example.com or ` +
|
||||
`http://localhost pages_ for more!**</a>`,
|
||||
{}
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<a href="https://www.example.com"><strong>` +
|
||||
`Visit <em>our www.example.com or http://localhost pages</em> for more!` +
|
||||
`</strong></a>`
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
.to.contain(`<pre is:raw`)
|
||||
.to.contain(`Go to https://example.com or www.example.com now.`);
|
||||
});
|
||||
|
||||
it('does not autolink URLs starting with a protocol when nested inside links', async () => {
|
||||
const { code } = await renderAstroMd(
|
||||
`See [http://example.com](http://example.com) or ` +
|
||||
`<a test href="https://example.com">https://example.com</a>`
|
||||
);
|
||||
});
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<p>See <a href="http://example.com">http://example.com</a> or ` +
|
||||
`<a test href="https://example.com">https://example.com</a></p>`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not autolink URLs starting with "www." when nested inside links', async () => {
|
||||
const { code } = await renderAstroMd(
|
||||
`See [www.example.com](https://www.example.com) or ` +
|
||||
`<a test href="https://www.example.com">www.example.com</a>`
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<p>See <a href="https://www.example.com">www.example.com</a> or ` +
|
||||
`<a test href="https://www.example.com">www.example.com</a></p>`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not autolink URLs when nested several layers deep inside links', async () => {
|
||||
const { code } = await renderAstroMd(
|
||||
`<a href="https://www.example.com">**Visit _our www.example.com or ` +
|
||||
`http://localhost pages_ for more!**</a>`
|
||||
);
|
||||
|
||||
chai
|
||||
.expect(code.replace(/\n/g, ''))
|
||||
.to.equal(
|
||||
`<a href="https://www.example.com"><strong>` +
|
||||
`Visit <em>our www.example.com or http://localhost pages</em> for more!` +
|
||||
`</strong></a>`
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
|
@ -2,32 +2,34 @@ import { renderMarkdown } from '../dist/index.js';
|
|||
import chai from 'chai';
|
||||
|
||||
describe('components', () => {
|
||||
const renderAstroMd = (text) => renderMarkdown(text, { isAstroFlavoredMd: true });
|
||||
|
||||
it('should be able to serialize string', async () => {
|
||||
const { code } = await renderMarkdown(`<Component str="cool!" />`, {});
|
||||
const { code } = await renderAstroMd(`<Component str="cool!" />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component str="cool!" />`);
|
||||
});
|
||||
|
||||
it('should be able to serialize boolean attribute', async () => {
|
||||
const { code } = await renderMarkdown(`<Component bool={true} />`, {});
|
||||
const { code } = await renderAstroMd(`<Component bool={true} />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component bool={true} />`);
|
||||
});
|
||||
|
||||
it('should be able to serialize array', async () => {
|
||||
const { code } = await renderMarkdown(`<Component prop={["a", "b", "c"]} />`, {});
|
||||
const { code } = await renderAstroMd(`<Component prop={["a", "b", "c"]} />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component prop={["a", "b", "c"]} />`);
|
||||
});
|
||||
|
||||
it('should be able to serialize object', async () => {
|
||||
const { code } = await renderMarkdown(`<Component prop={{ a: 0, b: 1, c: 2 }} />`, {});
|
||||
const { code } = await renderAstroMd(`<Component prop={{ a: 0, b: 1, c: 2 }} />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component prop={{ a: 0, b: 1, c: 2 }} />`);
|
||||
});
|
||||
|
||||
it('should be able to serialize empty attribute', async () => {
|
||||
const { code } = await renderMarkdown(`<Component empty />`, {});
|
||||
const { code } = await renderAstroMd(`<Component empty />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component empty />`);
|
||||
});
|
||||
|
@ -35,25 +37,25 @@ describe('components', () => {
|
|||
// Notable omission: shorthand attribute
|
||||
|
||||
it('should be able to serialize spread attribute', async () => {
|
||||
const { code } = await renderMarkdown(`<Component {...spread} />`, {});
|
||||
const { code } = await renderAstroMd(`<Component {...spread} />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component {...spread} />`);
|
||||
});
|
||||
|
||||
it('should allow client:* directives', async () => {
|
||||
const { code } = await renderMarkdown(`<Component client:load />`, {});
|
||||
const { code } = await renderAstroMd(`<Component client:load />`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component client:load />`);
|
||||
});
|
||||
|
||||
it('should normalize children', async () => {
|
||||
const { code } = await renderMarkdown(`<Component bool={true}>Hello world!</Component>`, {});
|
||||
const { code } = await renderAstroMd(`<Component bool={true}>Hello world!</Component>`);
|
||||
|
||||
chai.expect(code).to.equal(`<Component bool={true}>Hello world!</Component>`);
|
||||
});
|
||||
|
||||
it('should be able to nest components', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<Component bool={true}><Component>Hello world!</Component></Component>`,
|
||||
{}
|
||||
);
|
||||
|
@ -64,7 +66,7 @@ describe('components', () => {
|
|||
});
|
||||
|
||||
it('should allow markdown without many spaces', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<Component>
|
||||
# Hello world!
|
||||
</Component>`,
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
import { renderMarkdown } from '../dist/index.js';
|
||||
import chai, { expect } from 'chai';
|
||||
import chai from 'chai';
|
||||
|
||||
describe('expressions', () => {
|
||||
const renderAstroMd = (text, opts) => renderMarkdown(text, { isAstroFlavoredMd: true, ...opts });
|
||||
|
||||
it('should be able to serialize bare expression', async () => {
|
||||
const { code } = await renderMarkdown(`{a}`, {});
|
||||
const { code } = await renderAstroMd(`{a}`, {});
|
||||
|
||||
chai.expect(code).to.equal(`{a}`);
|
||||
});
|
||||
|
||||
it('should be able to serialize expression inside component', async () => {
|
||||
const { code } = await renderMarkdown(`<Component>{a}</Component>`, {});
|
||||
const { code } = await renderAstroMd(`<Component>{a}</Component>`, {});
|
||||
|
||||
chai.expect(code).to.equal(`<Component>{a}</Component>`);
|
||||
});
|
||||
|
||||
it('should be able to serialize expression inside markdown', async () => {
|
||||
const { code } = await renderMarkdown(`# {frontmatter.title}`, {});
|
||||
const { code } = await renderAstroMd(`# {frontmatter.title}`, {});
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
|
@ -23,7 +25,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to serialize complex expression inside markdown', async () => {
|
||||
const { code } = await renderMarkdown(`# Hello {frontmatter.name}`, {});
|
||||
const { code } = await renderAstroMd(`# Hello {frontmatter.name}`, {});
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
|
@ -31,7 +33,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to serialize complex expression with markup inside markdown', async () => {
|
||||
const { code } = await renderMarkdown(`# Hello <span>{frontmatter.name}</span>`, {});
|
||||
const { code } = await renderAstroMd(`# Hello <span>{frontmatter.name}</span>`, {});
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
|
@ -41,7 +43,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to avoid evaluating JSX-like expressions in an inline code & generate a slug for id', async () => {
|
||||
const { code } = await renderMarkdown(`# \`{frontmatter.title}\``, {});
|
||||
const { code } = await renderAstroMd(`# \`{frontmatter.title}\``, {});
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
|
@ -49,7 +51,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to avoid evaluating JSX-like expressions in inline codes', async () => {
|
||||
const { code } = await renderMarkdown(`# \`{ foo }\` is a shorthand for \`{ foo: foo }\``, {});
|
||||
const { code } = await renderAstroMd(`# \`{ foo }\` is a shorthand for \`{ foo: foo }\``, {});
|
||||
|
||||
chai
|
||||
.expect(code)
|
||||
|
@ -59,7 +61,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to avoid evaluating JSX-like expressions & escape HTML tag characters in inline codes', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`###### \`{}\` is equivalent to \`Record<never, never>\` <small>(at TypeScript v{frontmatter.version})</small>`,
|
||||
{}
|
||||
);
|
||||
|
@ -72,7 +74,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to encode ampersand characters in code blocks', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
'The ampersand in ` ` must be encoded in code blocks.',
|
||||
{}
|
||||
);
|
||||
|
@ -85,7 +87,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to encode ampersand characters in fenced code blocks', async () => {
|
||||
const { code } = await renderMarkdown(`
|
||||
const { code } = await renderAstroMd(`
|
||||
\`\`\`md
|
||||
The ampersand in \` \` must be encoded in code blocks.
|
||||
\`\`\`
|
||||
|
@ -95,7 +97,7 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should be able to serialize function expression', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`{frontmatter.list.map(item => <p id={item}>{item}</p>)}`,
|
||||
{}
|
||||
);
|
||||
|
@ -104,13 +106,13 @@ describe('expressions', () => {
|
|||
});
|
||||
|
||||
it('should unwrap HTML comments in inline code blocks', async () => {
|
||||
const { code } = await renderMarkdown(`\`{/*<!-- HTML comment -->*/}\``);
|
||||
const { code } = await renderAstroMd(`\`{/*<!-- HTML comment -->*/}\``);
|
||||
|
||||
chai.expect(code).to.equal('<p><code is:raw><!-- HTML comment --></code></p>');
|
||||
});
|
||||
|
||||
it('should unwrap HTML comments in code fences', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`
|
||||
\`\`\`
|
||||
<!-- HTML comment -->
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { renderMarkdown } from '../dist/index.js';
|
||||
import chai from 'chai';
|
||||
|
||||
describe('strictness', () => {
|
||||
describe('strictness in Astro-flavored markdown', () => {
|
||||
const renderAstroMd = (text, opts) => renderMarkdown(text, { isAstroFlavoredMd: true, ...opts });
|
||||
|
||||
it('should allow self-closing HTML tags (void elements)', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`Use self-closing void elements<br>like word<wbr>break and images: <img src="hi.jpg">`,
|
||||
{}
|
||||
);
|
||||
|
@ -17,25 +19,25 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names starting with ":" after element names', async () => {
|
||||
const { code } = await renderMarkdown(`<div :class="open ? '' : 'hidden'">Test</div>`, {});
|
||||
const { code } = await renderAstroMd(`<div :class="open ? '' : 'hidden'">Test</div>`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<div :class="open ? '' : 'hidden'">Test</div>`);
|
||||
});
|
||||
|
||||
it('should allow attribute names starting with ":" after local element names', async () => {
|
||||
const { code } = await renderMarkdown(`<div.abc :class="open ? '' : 'hidden'">x</div.abc>`, {});
|
||||
const { code } = await renderAstroMd(`<div.abc :class="open ? '' : 'hidden'">x</div.abc>`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<div.abc :class="open ? '' : 'hidden'">x</div.abc>`);
|
||||
});
|
||||
|
||||
it('should allow attribute names starting with ":" after attribute names', async () => {
|
||||
const { code } = await renderMarkdown(`<input type="text" disabled :placeholder="hi">`, {});
|
||||
const { code } = await renderAstroMd(`<input type="text" disabled :placeholder="hi">`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<input type="text" disabled :placeholder="hi" />`);
|
||||
});
|
||||
|
||||
it('should allow attribute names starting with ":" after local attribute names', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<input type="text" x-test:disabled :placeholder="hi">`,
|
||||
{}
|
||||
);
|
||||
|
@ -44,19 +46,19 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names starting with ":" after attribute values', async () => {
|
||||
const { code } = await renderMarkdown(`<input type="text" :placeholder="placeholder">`, {});
|
||||
const { code } = await renderAstroMd(`<input type="text" :placeholder="placeholder">`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<input type="text" :placeholder="placeholder" />`);
|
||||
});
|
||||
|
||||
it('should allow attribute names starting with "@" after element names', async () => {
|
||||
const { code } = await renderMarkdown(`<button @click="handleClick">Test</button>`, {});
|
||||
const { code } = await renderAstroMd(`<button @click="handleClick">Test</button>`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<button @click="handleClick">Test</button>`);
|
||||
});
|
||||
|
||||
it('should allow attribute names starting with "@" after local element names', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<button.local @click="handleClick">Test</button.local>`,
|
||||
{}
|
||||
);
|
||||
|
@ -65,7 +67,7 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names starting with "@" after attribute names', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<button disabled @click="handleClick">Test</button>`,
|
||||
{}
|
||||
);
|
||||
|
@ -74,7 +76,7 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names starting with "@" after local attribute names', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<button x-test:disabled @click="handleClick">Test</button>`,
|
||||
{}
|
||||
);
|
||||
|
@ -83,7 +85,7 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names starting with "@" after attribute values', async () => {
|
||||
const { code } = await renderMarkdown(
|
||||
const { code } = await renderAstroMd(
|
||||
`<button type="submit" @click="handleClick">Test</button>`,
|
||||
{}
|
||||
);
|
||||
|
@ -92,7 +94,7 @@ describe('strictness', () => {
|
|||
});
|
||||
|
||||
it('should allow attribute names containing dots', async () => {
|
||||
const { code } = await renderMarkdown(`<input x-on:input.debounce.500ms="fetchResults">`, {});
|
||||
const { code } = await renderAstroMd(`<input x-on:input.debounce.500ms="fetchResults">`, {});
|
||||
|
||||
chai.expect(code.trim()).to.equal(`<input x-on:input.debounce.500ms="fetchResults" />`);
|
||||
});
|
||||
|
|
|
@ -1714,19 +1714,23 @@ importers:
|
|||
|
||||
packages/astro/test/fixtures/slots-preact:
|
||||
specifiers:
|
||||
'@astrojs/mdx': workspace:*
|
||||
'@astrojs/preact': workspace:*
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||
'@astrojs/preact': link:../../../../integrations/preact
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/slots-react:
|
||||
specifiers:
|
||||
'@astrojs/mdx': workspace:*
|
||||
'@astrojs/react': workspace:*
|
||||
astro: workspace:*
|
||||
react: ^18.1.0
|
||||
react-dom: ^18.1.0
|
||||
dependencies:
|
||||
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||
'@astrojs/react': link:../../../../integrations/react
|
||||
astro: link:../../..
|
||||
react: 18.2.0
|
||||
|
@ -1734,25 +1738,31 @@ importers:
|
|||
|
||||
packages/astro/test/fixtures/slots-solid:
|
||||
specifiers:
|
||||
'@astrojs/mdx': workspace:*
|
||||
'@astrojs/solid-js': workspace:*
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||
'@astrojs/solid-js': link:../../../../integrations/solid
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/slots-svelte:
|
||||
specifiers:
|
||||
'@astrojs/mdx': workspace:*
|
||||
'@astrojs/svelte': workspace:*
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||
'@astrojs/svelte': link:../../../../integrations/svelte
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/slots-vue:
|
||||
specifiers:
|
||||
'@astrojs/mdx': workspace:*
|
||||
'@astrojs/vue': workspace:*
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||
'@astrojs/vue': link:../../../../integrations/vue
|
||||
astro: link:../../..
|
||||
|
||||
|
|
Loading…
Reference in a new issue