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
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [preact({ compat: true })],
|
integrations: [preact({ compat: true })],
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [preact(), mdx()],
|
integrations: [preact(), mdx()],
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [react(), mdx()],
|
integrations: [react(), mdx()],
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,5 +4,8 @@ import solid from '@astrojs/solid-js';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [solid(), mdx()],
|
integrations: [solid(), mdx()],
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [svelte(), mdx()],
|
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
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
mdx(),
|
mdx(),
|
||||||
vue({
|
vue({
|
||||||
|
|
|
@ -527,27 +527,6 @@ export interface AstroUserConfig {
|
||||||
*/
|
*/
|
||||||
drafts?: boolean;
|
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
|
* @docs
|
||||||
* @name markdown.shikiConfig
|
* @name markdown.shikiConfig
|
||||||
|
@ -716,6 +695,16 @@ export interface AstroUserConfig {
|
||||||
buildOptions?: never;
|
buildOptions?: never;
|
||||||
/** @deprecated `devOptions` has been renamed to `server` */
|
/** @deprecated `devOptions` has been renamed to `server` */
|
||||||
devOptions?: never;
|
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
|
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
|
||||||
|
|
|
@ -213,7 +213,10 @@ async function generatePath(
|
||||||
adapterName: undefined,
|
adapterName: undefined,
|
||||||
links,
|
links,
|
||||||
logging,
|
logging,
|
||||||
markdown: astroConfig.markdown,
|
markdown: {
|
||||||
|
...astroConfig.markdown,
|
||||||
|
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||||
|
},
|
||||||
mod,
|
mod,
|
||||||
mode: opts.mode,
|
mode: opts.mode,
|
||||||
origin,
|
origin,
|
||||||
|
|
|
@ -153,7 +153,10 @@ function buildManifest(
|
||||||
routes,
|
routes,
|
||||||
site: astroConfig.site,
|
site: astroConfig.site,
|
||||||
base: astroConfig.base,
|
base: astroConfig.base,
|
||||||
markdown: astroConfig.markdown,
|
markdown: {
|
||||||
|
...astroConfig.markdown,
|
||||||
|
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||||
|
},
|
||||||
pageMap: null as any,
|
pageMap: null as any,
|
||||||
renderers: [],
|
renderers: [],
|
||||||
entryModules,
|
entryModules,
|
||||||
|
|
|
@ -50,6 +50,9 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
||||||
rehypePlugins: [],
|
rehypePlugins: [],
|
||||||
},
|
},
|
||||||
vite: {},
|
vite: {},
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: false,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
|
async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
|
||||||
|
@ -172,9 +175,6 @@ export const AstroConfigSchema = z.object({
|
||||||
.default({}),
|
.default({}),
|
||||||
markdown: z
|
markdown: z
|
||||||
.object({
|
.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),
|
drafts: z.boolean().default(false),
|
||||||
syntaxHighlight: z
|
syntaxHighlight: z
|
||||||
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
|
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
|
||||||
|
@ -212,6 +212,12 @@ export const AstroConfigSchema = z.object({
|
||||||
vite: z
|
vite: z
|
||||||
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
|
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
|
||||||
.default(ASTRO_CONFIG_DEFAULTS.vite),
|
.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 */
|
/** Turn raw config values into normalized values */
|
||||||
|
|
|
@ -173,7 +173,10 @@ export async function render(
|
||||||
links,
|
links,
|
||||||
styles,
|
styles,
|
||||||
logging,
|
logging,
|
||||||
markdown: astroConfig.markdown,
|
markdown: {
|
||||||
|
...astroConfig.markdown,
|
||||||
|
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown
|
||||||
|
},
|
||||||
mod,
|
mod,
|
||||||
mode,
|
mode,
|
||||||
origin,
|
origin,
|
||||||
|
|
|
@ -29,23 +29,24 @@ export function transformSlots(vnode: AstroVNode) {
|
||||||
delete child.props.slot;
|
delete child.props.slot;
|
||||||
delete vnode.props.children;
|
delete vnode.props.children;
|
||||||
}
|
}
|
||||||
if (!Array.isArray(vnode.props.children)) return;
|
if (Array.isArray(vnode.props.children)) {
|
||||||
// Handle many children with slot attributes
|
// Handle many children with slot attributes
|
||||||
vnode.props.children = vnode.props.children
|
vnode.props.children = vnode.props.children
|
||||||
.map((child) => {
|
.map((child) => {
|
||||||
if (!isVNode(child)) return child;
|
if (!isVNode(child)) return child;
|
||||||
if (!('slot' in child.props)) return child;
|
if (!('slot' in child.props)) return child;
|
||||||
const name = toSlotName(child.props.slot);
|
const name = toSlotName(child.props.slot);
|
||||||
if (Array.isArray(slots[name])) {
|
if (Array.isArray(slots[name])) {
|
||||||
slots[name].push(child);
|
slots[name].push(child);
|
||||||
} else {
|
} else {
|
||||||
slots[name] = [child];
|
slots[name] = [child];
|
||||||
slots[name]['$$slot'] = true;
|
slots[name]['$$slot'] = true;
|
||||||
}
|
}
|
||||||
delete child.props.slot;
|
delete child.props.slot;
|
||||||
return Empty;
|
return Empty;
|
||||||
})
|
})
|
||||||
.filter((v) => v !== Empty);
|
.filter((v) => v !== Empty);
|
||||||
|
}
|
||||||
Object.assign(vnode.props, slots);
|
Object.assign(vnode.props, slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ let consoleFilterRefs = 0;
|
||||||
export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case vnode instanceof HTMLString:
|
case vnode instanceof HTMLString:
|
||||||
|
if (vnode.toString().trim() === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return vnode;
|
return vnode;
|
||||||
case typeof vnode === 'string':
|
case typeof vnode === 'string':
|
||||||
return markHTMLString(escapeHTML(vnode));
|
return markHTMLString(escapeHTML(vnode));
|
||||||
|
@ -55,6 +58,9 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vnode.type) {
|
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']) {
|
if (typeof vnode.type === 'function' && vnode.props['server:root']) {
|
||||||
const output = await vnode.type(vnode.props ?? {});
|
const output = await vnode.type(vnode.props ?? {});
|
||||||
return await renderJSX(result, output);
|
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 { children = null, ...props } = vnode.props ?? {};
|
||||||
const slots: Record<string, any> = {
|
const _slots: Record<string, any> = {
|
||||||
default: [],
|
default: [],
|
||||||
};
|
};
|
||||||
function extractSlots(child: any): any {
|
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));
|
return child.map((c) => extractSlots(c));
|
||||||
}
|
}
|
||||||
if (!isVNode(child)) {
|
if (!isVNode(child)) {
|
||||||
return slots.default.push(child);
|
_slots.default.push(child);
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if ('slot' in child.props) {
|
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;
|
delete child.props.slot;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
slots.default.push(child);
|
_slots.default.push(child);
|
||||||
}
|
}
|
||||||
extractSlots(children);
|
extractSlots(children);
|
||||||
for (const [key, value] of Object.entries(slots)) {
|
for (const [key, value] of Object.entries(props)) {
|
||||||
slots[key] = () => renderJSX(result, value);
|
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>;
|
let output: string | AsyncIterable<string>;
|
||||||
if (vnode.type === ClientOnlyPlaceholder && vnode.props['client:only']) {
|
if (vnode.type === ClientOnlyPlaceholder && vnode.props['client:only']) {
|
||||||
|
|
|
@ -137,7 +137,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
const filename = normalizeFilename(id);
|
const filename = normalizeFilename(id);
|
||||||
const source = await fs.promises.readFile(filename, 'utf8');
|
const source = await fs.promises.readFile(filename, 'utf8');
|
||||||
const renderOpts = config.markdown;
|
const renderOpts = config.markdown;
|
||||||
const isMDX = renderOpts.mode === 'mdx';
|
const isAstroFlavoredMd = config.legacy.astroFlavoredMarkdown;
|
||||||
|
|
||||||
const fileUrl = new URL(`file://${filename}`);
|
const fileUrl = new URL(`file://${filename}`);
|
||||||
const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname);
|
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
|
// Turn HTML comments into JS comments while preventing nested `*/` sequences
|
||||||
// from ending the JS comment by injecting a zero-width space
|
// from ending the JS comment by injecting a zero-width space
|
||||||
// Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin.
|
// Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin.
|
||||||
if (isMDX) {
|
if (isAstroFlavoredMd) {
|
||||||
markdownContent = markdownContent.replace(
|
markdownContent = markdownContent.replace(
|
||||||
/<\s*!--([^-->]*)(.*?)-->/gs,
|
/<\s*!--([^-->]*)(.*?)-->/gs,
|
||||||
(whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}`
|
(whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}`
|
||||||
|
@ -159,6 +159,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
let renderResult = await renderMarkdown(markdownContent, {
|
let renderResult = await renderMarkdown(markdownContent, {
|
||||||
...renderOpts,
|
...renderOpts,
|
||||||
fileURL: fileUrl,
|
fileURL: fileUrl,
|
||||||
|
isAstroFlavoredMd,
|
||||||
} as any);
|
} as any);
|
||||||
let { code: astroResult, metadata } = renderResult;
|
let { code: astroResult, metadata } = renderResult;
|
||||||
const { layout = '', components = '', setup = '', ...content } = frontmatter;
|
const { layout = '', components = '', setup = '', ...content } = frontmatter;
|
||||||
|
@ -168,9 +169,9 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
const prelude = `---
|
const prelude = `---
|
||||||
import Slugger from 'github-slugger';
|
import Slugger from 'github-slugger';
|
||||||
${layout ? `import Layout from '${layout}';` : ''}
|
${layout ? `import Layout from '${layout}';` : ''}
|
||||||
${isMDX && components ? `import * from '${components}';` : ''}
|
${isAstroFlavoredMd && components ? `import * from '${components}';` : ''}
|
||||||
${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''}
|
${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''}
|
||||||
${isMDX ? setup : ''}
|
${isAstroFlavoredMd ? setup : ''}
|
||||||
|
|
||||||
const slugger = new Slugger();
|
const slugger = new Slugger();
|
||||||
function $$slug(value) {
|
function $$slug(value) {
|
||||||
|
@ -178,7 +179,7 @@ function $$slug(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const $$content = ${JSON.stringify(
|
const $$content = ${JSON.stringify(
|
||||||
isMDX
|
isAstroFlavoredMd
|
||||||
? content
|
? content
|
||||||
: // Avoid stripping "setup" and "components"
|
: // Avoid stripping "setup" and "components"
|
||||||
// in plain MD mode
|
// in plain MD mode
|
||||||
|
@ -186,11 +187,11 @@ const $$content = ${JSON.stringify(
|
||||||
)}
|
)}
|
||||||
---`;
|
---`;
|
||||||
const imports = `${layout ? `import Layout from '${layout}';` : ''}
|
const imports = `${layout ? `import Layout from '${layout}';` : ''}
|
||||||
${isMDX ? setup : ''}`.trim();
|
${isAstroFlavoredMd ? setup : ''}`.trim();
|
||||||
|
|
||||||
// Wrap with set:html fragment to skip
|
// Wrap with set:html fragment to skip
|
||||||
// JSX expressions and components in "plain" md mode
|
// JSX expressions and components in "plain" md mode
|
||||||
if (!isMDX) {
|
if (!isAstroFlavoredMd) {
|
||||||
astroResult = `<Fragment set:html={${JSON.stringify(astroResult)}} />`;
|
astroResult = `<Fragment set:html={${JSON.stringify(astroResult)}} />`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ describe('Astro Markdown - plain MD mode', () => {
|
||||||
root: './fixtures/astro-markdown-md-mode/',
|
root: './fixtures/astro-markdown-md-mode/',
|
||||||
markdown: {
|
markdown: {
|
||||||
syntaxHighlight: 'prism',
|
syntaxHighlight: 'prism',
|
||||||
mode: 'md',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
|
|
|
@ -2,5 +2,8 @@ import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: []
|
integrations: []
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,9 +3,6 @@ import svelte from "@astrojs/svelte";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
markdown: {
|
|
||||||
mode: 'md',
|
|
||||||
},
|
|
||||||
integrations: [svelte()],
|
integrations: [svelte()],
|
||||||
site: 'https://astro.build/',
|
site: 'https://astro.build/',
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,4 +6,7 @@ import svelte from "@astrojs/svelte";
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [preact(), svelte()],
|
integrations: [preact(), svelte()],
|
||||||
site: 'https://astro.build/',
|
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 { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
import preact from '@astrojs/preact';
|
import preact from '@astrojs/preact';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [preact()],
|
legacy: {
|
||||||
});
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
|
integrations: [preact(), mdx()],
|
||||||
|
});
|
||||||
|
|
|
@ -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:*"
|
||||||
}
|
}
|
||||||
|
|
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 { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [react()],
|
legacy: {
|
||||||
});
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
|
integrations: [react(), mdx()],
|
||||||
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
"@astrojs/react": "workspace:*",
|
"@astrojs/react": "workspace:*",
|
||||||
"astro": "workspace:*",
|
"astro": "workspace:*",
|
||||||
"react": "^18.1.0",
|
"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 { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
import solid from '@astrojs/solid-js';
|
import solid from '@astrojs/solid-js';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [solid()],
|
legacy: {
|
||||||
});
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
|
integrations: [solid(), mdx()],
|
||||||
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
"@astrojs/solid-js": "workspace:*",
|
"@astrojs/solid-js": "workspace:*",
|
||||||
"astro": "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 { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
import svelte from '@astrojs/svelte';
|
import svelte from '@astrojs/svelte';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [svelte()],
|
legacy: {
|
||||||
});
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
|
integrations: [svelte(), mdx()],
|
||||||
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
"@astrojs/svelte": "workspace:*",
|
"@astrojs/svelte": "workspace:*",
|
||||||
"astro": "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 { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
import vue from '@astrojs/vue';
|
import vue from '@astrojs/vue';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [vue()],
|
legacy: {
|
||||||
});
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
|
integrations: [vue(), mdx()],
|
||||||
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
"@astrojs/vue": "workspace:*",
|
"@astrojs/vue": "workspace:*",
|
||||||
"astro": "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
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [tailwind()],
|
integrations: [tailwind()],
|
||||||
vite: {
|
vite: {
|
||||||
build: {
|
build: {
|
||||||
assetsInlineLimit: 0,
|
assetsInlineLimit: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,4 +53,24 @@ describe('Slots: Preact', () => {
|
||||||
expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case');
|
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');
|
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');
|
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');
|
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');
|
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) {
|
if (content) {
|
||||||
htmlContent = await (Astro as any).__renderMarkdown(content, {
|
htmlContent = await (Astro as any).__renderMarkdown(content, {
|
||||||
mode: 'md',
|
|
||||||
$: {
|
$: {
|
||||||
scopedClassName: className,
|
scopedClassName: className,
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```rinfo
|
```rinfo
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -4,6 +4,9 @@ import svelte from "@astrojs/svelte";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
legacy: {
|
||||||
|
astroFlavoredMarkdown: true,
|
||||||
|
},
|
||||||
integrations: [preact(), svelte()],
|
integrations: [preact(), svelte()],
|
||||||
site: 'https://astro.build/',
|
site: 'https://astro.build/',
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ const title = 'My Blog Post';
|
||||||
const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
## Interesting Topic
|
## Interesting Topic
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import Markdown from '@astrojs/markdown-component';
|
import Markdown from '@astrojs/markdown-component';
|
||||||
import Layout from '../layouts/content.astro';
|
import Layout from '../layouts/content.astro';
|
||||||
import Hello from '../components/Hello.jsx';
|
import Hello from '../components/Hello.jsx';
|
||||||
import Counter from '../components/Counter.jsx';
|
|
||||||
|
|
||||||
const title = 'My Blog Post';
|
const title = 'My Blog Post';
|
||||||
const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
|
@ -13,6 +12,5 @@ const description = 'This is a post about some stuff.';
|
||||||
## Interesting Topic
|
## Interesting Topic
|
||||||
|
|
||||||
<Hello name={`world`} />
|
<Hello name={`world`} />
|
||||||
<Counter client:load />
|
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -10,19 +10,19 @@ const description = 'This is a post about some stuff.';
|
||||||
|
|
||||||
<div id="deep">
|
<div id="deep">
|
||||||
<section class="a">
|
<section class="a">
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
## A
|
## A
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="b">
|
<section class="b">
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
## B
|
## B
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="c">
|
<section class="c">
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
## C
|
## C
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -20,7 +20,7 @@ const content = `
|
||||||
<body>
|
<body>
|
||||||
<h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
|
<h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
|
||||||
<div id="target">
|
<div id="target">
|
||||||
<Markdown>
|
<Markdown is:raw>
|
||||||
- list
|
- list
|
||||||
- nested list
|
- nested list
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
import Markdown from '@astrojs/markdown-component';
|
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 */
|
/** Shared utility for rendering markdown */
|
||||||
export async function renderMarkdown(
|
export async function renderMarkdown(
|
||||||
content: string,
|
content: string,
|
||||||
opts: MarkdownRenderingOptions = {}
|
opts: MarkdownRenderingOptions,
|
||||||
): Promise<MarkdownRenderingResult> {
|
): Promise<MarkdownRenderingResult> {
|
||||||
let {
|
let {
|
||||||
fileURL,
|
fileURL,
|
||||||
mode = 'mdx',
|
|
||||||
syntaxHighlight = 'shiki',
|
syntaxHighlight = 'shiki',
|
||||||
shikiConfig = {},
|
shikiConfig = {},
|
||||||
remarkPlugins = [],
|
remarkPlugins = [],
|
||||||
rehypePlugins = [],
|
rehypePlugins = [],
|
||||||
|
isAstroFlavoredMd = false,
|
||||||
} = opts;
|
} = opts;
|
||||||
const input = new VFile({ value: content, path: fileURL });
|
const input = new VFile({ value: content, path: fileURL });
|
||||||
const scopedClassName = opts.$?.scopedClassName;
|
const scopedClassName = opts.$?.scopedClassName;
|
||||||
const isMDX = mode === 'mdx';
|
|
||||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
||||||
|
|
||||||
let parser = unified()
|
let parser = unified()
|
||||||
.use(markdown)
|
.use(markdown)
|
||||||
.use(isMDX ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []);
|
.use(isAstroFlavoredMd ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []);
|
||||||
|
|
||||||
if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
|
if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
|
||||||
remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
|
remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
|
||||||
|
@ -75,7 +74,7 @@ export async function renderMarkdown(
|
||||||
markdownToHtml as any,
|
markdownToHtml as any,
|
||||||
{
|
{
|
||||||
allowDangerousHtml: true,
|
allowDangerousHtml: true,
|
||||||
passThrough: isMDX
|
passThrough: isAstroFlavoredMd
|
||||||
? [
|
? [
|
||||||
'raw',
|
'raw',
|
||||||
'mdxFlowExpression',
|
'mdxFlowExpression',
|
||||||
|
@ -94,7 +93,7 @@ export async function renderMarkdown(
|
||||||
|
|
||||||
parser
|
parser
|
||||||
.use(
|
.use(
|
||||||
isMDX
|
isAstroFlavoredMd
|
||||||
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeaders]
|
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeaders]
|
||||||
: [rehypeCollectHeaders, rehypeRaw]
|
: [rehypeCollectHeaders, rehypeRaw]
|
||||||
)
|
)
|
||||||
|
|
|
@ -41,6 +41,7 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
||||||
$?: {
|
$?: {
|
||||||
scopedClassName: string | null;
|
scopedClassName: string | null;
|
||||||
};
|
};
|
||||||
|
isAstroFlavoredMd?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownHeader {
|
export interface MarkdownHeader {
|
||||||
|
|
|
@ -2,91 +2,107 @@ import { renderMarkdown } from '../dist/index.js';
|
||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
|
|
||||||
describe('autolinking', () => {
|
describe('autolinking', () => {
|
||||||
it('autolinks URLs starting with a protocol in plain text', async () => {
|
describe('plain md', () => {
|
||||||
const { code } = await renderMarkdown(`See https://example.com for more.`, {});
|
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, ''))
|
chai
|
||||||
.to.equal(`<p>See <a href="https://example.com">https://example.com</a> for more.</p>`);
|
.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.`, {});
|
it('autolinks URLs starting with "www." in plain text', async () => {
|
||||||
|
const { code } = await renderMarkdown(`See www.example.com for more.`, {});
|
||||||
chai
|
|
||||||
.expect(code.trim())
|
chai
|
||||||
.to.equal(`<p>See <a href="http://www.example.com">www.example.com</a> for more.</p>`);
|
.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(
|
it('does not autolink URLs in code blocks', async () => {
|
||||||
'See `https://example.com` or `www.example.com` for more.',
|
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>`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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 () => {
|
describe('astro-flavored md', () => {
|
||||||
const { code } = await renderMarkdown(
|
const renderAstroMd = text => renderMarkdown(text, { isAstroFlavoredMd: true });
|
||||||
'Example:\n```\nGo to https://example.com or www.example.com now.\n```',
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
chai
|
it('does not autolink URLs in code blocks', async () => {
|
||||||
.expect(code)
|
const { code } = await renderAstroMd(
|
||||||
.to.contain(`<pre is:raw`)
|
'See `https://example.com` or `www.example.com` for more.',
|
||||||
.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>`
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
chai
|
||||||
it('does not autolink URLs starting with "www." when nested inside links', async () => {
|
.expect(code.trim())
|
||||||
const { code } = await renderMarkdown(
|
.to.equal(
|
||||||
`See [www.example.com](https://www.example.com) or ` +
|
`<p>See <code is:raw>https://example.com</code> or ` +
|
||||||
`<a test href="https://www.example.com">www.example.com</a>`,
|
`<code is:raw>www.example.com</code> for more.</p>`
|
||||||
{}
|
);
|
||||||
);
|
});
|
||||||
|
|
||||||
chai
|
it('does not autolink URLs in fenced code blocks', async () => {
|
||||||
.expect(code.replace(/\n/g, ''))
|
const { code } = await renderAstroMd(
|
||||||
.to.equal(
|
'Example:\n```\nGo to https://example.com or www.example.com now.\n```'
|
||||||
`<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
|
||||||
it('does not autolink URLs when nested several layers deep inside links', async () => {
|
.expect(code)
|
||||||
const { code } = await renderMarkdown(
|
.to.contain(`<pre is:raw`)
|
||||||
`<a href="https://www.example.com">**Visit _our www.example.com or ` +
|
.to.contain(`Go to https://example.com or www.example.com now.`);
|
||||||
`http://localhost pages_ for more!**</a>`,
|
});
|
||||||
{}
|
|
||||||
);
|
it('does not autolink URLs starting with a protocol when nested inside links', async () => {
|
||||||
|
const { code } = await renderAstroMd(
|
||||||
chai
|
`See [http://example.com](http://example.com) or ` +
|
||||||
.expect(code.replace(/\n/g, ''))
|
`<a test href="https://example.com">https://example.com</a>`
|
||||||
.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.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';
|
import chai from 'chai';
|
||||||
|
|
||||||
describe('components', () => {
|
describe('components', () => {
|
||||||
|
const renderAstroMd = (text) => renderMarkdown(text, { isAstroFlavoredMd: true });
|
||||||
|
|
||||||
it('should be able to serialize string', async () => {
|
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!" />`);
|
chai.expect(code).to.equal(`<Component str="cool!" />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize boolean attribute', async () => {
|
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} />`);
|
chai.expect(code).to.equal(`<Component bool={true} />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize array', async () => {
|
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"]} />`);
|
chai.expect(code).to.equal(`<Component prop={["a", "b", "c"]} />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize object', async () => {
|
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 }} />`);
|
chai.expect(code).to.equal(`<Component prop={{ a: 0, b: 1, c: 2 }} />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize empty attribute', async () => {
|
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 />`);
|
chai.expect(code).to.equal(`<Component empty />`);
|
||||||
});
|
});
|
||||||
|
@ -35,25 +37,25 @@ describe('components', () => {
|
||||||
// Notable omission: shorthand attribute
|
// Notable omission: shorthand attribute
|
||||||
|
|
||||||
it('should be able to serialize spread attribute', async () => {
|
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} />`);
|
chai.expect(code).to.equal(`<Component {...spread} />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow client:* directives', async () => {
|
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 />`);
|
chai.expect(code).to.equal(`<Component client:load />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize children', async () => {
|
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>`);
|
chai.expect(code).to.equal(`<Component bool={true}>Hello world!</Component>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to nest components', async () => {
|
it('should be able to nest components', async () => {
|
||||||
const { code } = await renderMarkdown(
|
const { code } = await renderAstroMd(
|
||||||
`<Component bool={true}><Component>Hello world!</Component></Component>`,
|
`<Component bool={true}><Component>Hello world!</Component></Component>`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
@ -64,7 +66,7 @@ describe('components', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow markdown without many spaces', async () => {
|
it('should allow markdown without many spaces', async () => {
|
||||||
const { code } = await renderMarkdown(
|
const { code } = await renderAstroMd(
|
||||||
`<Component>
|
`<Component>
|
||||||
# Hello world!
|
# Hello world!
|
||||||
</Component>`,
|
</Component>`,
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import { renderMarkdown } from '../dist/index.js';
|
import { renderMarkdown } from '../dist/index.js';
|
||||||
import chai, { expect } from 'chai';
|
import chai from 'chai';
|
||||||
|
|
||||||
describe('expressions', () => {
|
describe('expressions', () => {
|
||||||
|
const renderAstroMd = (text, opts) => renderMarkdown(text, { isAstroFlavoredMd: true, ...opts });
|
||||||
|
|
||||||
it('should be able to serialize bare expression', async () => {
|
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}`);
|
chai.expect(code).to.equal(`{a}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize expression inside component', async () => {
|
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>`);
|
chai.expect(code).to.equal(`<Component>{a}</Component>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize expression inside markdown', async () => {
|
it('should be able to serialize expression inside markdown', async () => {
|
||||||
const { code } = await renderMarkdown(`# {frontmatter.title}`, {});
|
const { code } = await renderAstroMd(`# {frontmatter.title}`, {});
|
||||||
|
|
||||||
chai
|
chai
|
||||||
.expect(code)
|
.expect(code)
|
||||||
|
@ -23,7 +25,7 @@ describe('expressions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize complex expression inside markdown', async () => {
|
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
|
chai
|
||||||
.expect(code)
|
.expect(code)
|
||||||
|
@ -31,7 +33,7 @@ describe('expressions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize complex expression with markup inside markdown', async () => {
|
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
|
chai
|
||||||
.expect(code)
|
.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 () => {
|
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
|
chai
|
||||||
.expect(code)
|
.expect(code)
|
||||||
|
@ -49,7 +51,7 @@ describe('expressions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to avoid evaluating JSX-like expressions in inline codes', async () => {
|
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
|
chai
|
||||||
.expect(code)
|
.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 () => {
|
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>`,
|
`###### \`{}\` 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 () => {
|
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.',
|
'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 () => {
|
it('should be able to encode ampersand characters in fenced code blocks', async () => {
|
||||||
const { code } = await renderMarkdown(`
|
const { code } = await renderAstroMd(`
|
||||||
\`\`\`md
|
\`\`\`md
|
||||||
The ampersand in \` \` must be encoded in code blocks.
|
The ampersand in \` \` must be encoded in code blocks.
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
@ -95,7 +97,7 @@ describe('expressions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to serialize function expression', async () => {
|
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>)}`,
|
`{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 () => {
|
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>');
|
chai.expect(code).to.equal('<p><code is:raw><!-- HTML comment --></code></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unwrap HTML comments in code fences', async () => {
|
it('should unwrap HTML comments in code fences', async () => {
|
||||||
const { code } = await renderMarkdown(
|
const { code } = await renderAstroMd(
|
||||||
`
|
`
|
||||||
\`\`\`
|
\`\`\`
|
||||||
<!-- HTML comment -->
|
<!-- HTML comment -->
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { renderMarkdown } from '../dist/index.js';
|
import { renderMarkdown } from '../dist/index.js';
|
||||||
import chai from 'chai';
|
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 () => {
|
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">`,
|
`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 () => {
|
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>`);
|
chai.expect(code.trim()).to.equal(`<div :class="open ? '' : 'hidden'">Test</div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with ":" after local element names', async () => {
|
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>`);
|
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 () => {
|
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" />`);
|
chai.expect(code.trim()).to.equal(`<input type="text" disabled :placeholder="hi" />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with ":" after local attribute names', async () => {
|
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">`,
|
`<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 () => {
|
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" />`);
|
chai.expect(code.trim()).to.equal(`<input type="text" :placeholder="placeholder" />`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with "@" after element names', async () => {
|
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>`);
|
chai.expect(code.trim()).to.equal(`<button @click="handleClick">Test</button>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with "@" after local element names', async () => {
|
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>`,
|
`<button.local @click="handleClick">Test</button.local>`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
@ -65,7 +67,7 @@ describe('strictness', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with "@" after attribute names', async () => {
|
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>`,
|
`<button disabled @click="handleClick">Test</button>`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
@ -74,7 +76,7 @@ describe('strictness', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names starting with "@" after local attribute names', async () => {
|
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>`,
|
`<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 () => {
|
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>`,
|
`<button type="submit" @click="handleClick">Test</button>`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
@ -92,7 +94,7 @@ describe('strictness', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow attribute names containing dots', async () => {
|
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" />`);
|
chai.expect(code.trim()).to.equal(`<input x-on:input.debounce.500ms="fetchResults" />`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1714,19 +1714,23 @@ importers:
|
||||||
|
|
||||||
packages/astro/test/fixtures/slots-preact:
|
packages/astro/test/fixtures/slots-preact:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
'@astrojs/preact': workspace:*
|
'@astrojs/preact': workspace:*
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||||
'@astrojs/preact': link:../../../../integrations/preact
|
'@astrojs/preact': link:../../../../integrations/preact
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/slots-react:
|
packages/astro/test/fixtures/slots-react:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
'@astrojs/react': workspace:*
|
'@astrojs/react': workspace:*
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
react: ^18.1.0
|
react: ^18.1.0
|
||||||
react-dom: ^18.1.0
|
react-dom: ^18.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||||
'@astrojs/react': link:../../../../integrations/react
|
'@astrojs/react': link:../../../../integrations/react
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
@ -1734,25 +1738,31 @@ importers:
|
||||||
|
|
||||||
packages/astro/test/fixtures/slots-solid:
|
packages/astro/test/fixtures/slots-solid:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
'@astrojs/solid-js': workspace:*
|
'@astrojs/solid-js': workspace:*
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||||
'@astrojs/solid-js': link:../../../../integrations/solid
|
'@astrojs/solid-js': link:../../../../integrations/solid
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/slots-svelte:
|
packages/astro/test/fixtures/slots-svelte:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
'@astrojs/svelte': workspace:*
|
'@astrojs/svelte': workspace:*
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||||
'@astrojs/svelte': link:../../../../integrations/svelte
|
'@astrojs/svelte': link:../../../../integrations/svelte
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/slots-vue:
|
packages/astro/test/fixtures/slots-vue:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
'@astrojs/vue': workspace:*
|
'@astrojs/vue': workspace:*
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../../../integrations/mdx
|
||||||
'@astrojs/vue': link:../../../../integrations/vue
|
'@astrojs/vue': link:../../../../integrations/vue
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue