Move the Markdown component to its own package (#3986)

* Move the Markdown component to its own package

* Update the examples

* Updated lockfile

* Use is:raw

* Add a main field

* Update the formatting of the readme

* Rename to @astrojs/markdown-component
This commit is contained in:
Matthew Phillips 2022-07-20 16:45:05 -04:00 committed by GitHub
parent d13afad272
commit bccd88f0eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 1392 additions and 719 deletions

View file

@ -0,0 +1,22 @@
---
'astro': minor
'@astrojs/markdown-component': minor
---
Move the Markdown component to its own package
This change moves the Markdown component into its own package where it will be maintained separately. All that needs to change from a user's perspective is the import statement:
```astro
---
import { Markdown } from 'astro/components';
---
```
Becomes:
```astro
---
import Markdown from '@astrojs/markdown-component';
---
```

View file

@ -27,7 +27,6 @@ const { content } = Astro.props;
<header>
<nav class="nav">
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<slot />

View file

@ -1,20 +0,0 @@
---
import { Markdown } from "astro/components";
import MainLayout from "../layouts/main.astro";
---
<MainLayout content={{ title: "About" }}>
<Markdown>
# About
Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
## My story
Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut.
</Markdown>
</MainLayout>

View file

@ -8,5 +8,4 @@ npm init astro -- --template with-markdown-shiki
This example showcases Astro's [built-in Markdown support](https://docs.astro.build/en/guides/markdown-content/).
- `src/pages/index.astro` uses Astro's `<Markdown>` component.
- `src/pages/other.md` is a treated as a page entrypoint and uses a `layout`.
- `src/pages/index.md` is a treated as a page entrypoint and uses a `layout`.

View file

@ -1,19 +0,0 @@
# build output
dist/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

View file

@ -1,2 +0,0 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true

View file

@ -1,6 +0,0 @@
{
"startCommand": "npm start",
"env": {
"ENABLE_CJS_IMPORTS": true
}
}

View file

@ -1,12 +0,0 @@
# Astro Example: Markdown
```
npm init astro -- --template with-markdown
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-markdown)
This example showcases Astro's [built-in Markdown support](https://docs.astro.build/en/guides/markdown-content/).
- `src/pages/index.astro` uses Astro's `<Markdown>` component.
- `src/pages/other.md` is a treated as a page entrypoint and uses a `layout`.

View file

@ -1,11 +0,0 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
integrations: [preact(), react(), svelte(), vue()],
});

View file

@ -1,26 +0,0 @@
{
"name": "@example/with-markdown",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/markdown-remark": "^0.12.0",
"@astrojs/preact": "^0.5.2",
"@astrojs/react": "^0.4.2",
"@astrojs/svelte": "^0.4.1",
"@astrojs/vue": "^0.4.1",
"astro": "^1.0.0-beta.73"
},
"dependencies": {
"preact": "^10.7.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"svelte": "^3.48.0",
"vue": "^3.2.37"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,11 +0,0 @@
{
"infiniteLoopProtection": true,
"hardReloadOnChange": false,
"view": "browser",
"template": "node",
"container": {
"port": 3000,
"startScript": "start",
"node": "14"
}
}

View file

@ -1,20 +0,0 @@
import { h, Fragment } from 'preact';
import { useState } from 'preact/hooks';
/** a counter written in Preact */
export default function PreactCounter({ children }) {
const [count, setCount] = useState(0);
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
return (
<>
<div className="counter">
<button onClick={subtract}>-</button>
<pre>{count}</pre>
<button onClick={add}>+</button>
</div>
<div className="children">{children}</div>
</>
);
}

View file

@ -1,19 +0,0 @@
import React, { useState } from 'react';
/** a counter written in React */
export default function ReactCounter({ children }) {
const [count, setCount] = useState(0);
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
return (
<>
<div className="counter">
<button onClick={subtract}>-</button>
<pre>{count}</pre>
<button onClick={add}>+</button>
</div>
<div className="children">{children}</div>
</>
);
}

View file

@ -1,22 +0,0 @@
<script>
let children;
let count = 0;
function add() {
count += 1;
}
function subtract() {
count -= 1;
}
</script>
<div class="counter">
<button on:click={subtract}>-</button>
<pre>{ count }</pre>
<button on:click={add}>+</button>
</div>
<div class="children">
<slot />
</div>

View file

@ -1,27 +0,0 @@
<template>
<div class="counter">
<button @click="subtract()">-</button>
<pre>{{ count }}</pre>
<button @click="add()">+</button>
</div>
<div class="children">
<slot />
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const add = () => (count.value = count.value + 1);
const subtract = () => (count.value = count.value - 1);
return {
count,
add,
subtract,
};
},
};
</script>

View file

@ -1,18 +0,0 @@
---
import "../styles/global.css";
const { content } = Astro.props;
---
<html lang={content.lang || "en"}>
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<title>{content.title}</title>
</head>
<body>
<slot />
</body>
</html>

View file

@ -1,19 +0,0 @@
---
import { Markdown } from "astro/components";
import Layout from "../layouts/main.astro";
const title = `External Markdown`;
const content = `Markdown *content* to render`;
---
<Layout content={{ title }}>
<main>
<div>
<Markdown {content}>
</Markdown>
<p>Some other stuff</p>
</div>
<p>Lastly...</p>
</main>
</Layout>

View file

@ -1,65 +0,0 @@
---
// Component Imports
import { Markdown } from "astro/components";
import Layout from "../layouts/main.astro";
import ReactCounter from "../components/ReactCounter.jsx";
import PreactCounter from "../components/PreactCounter.tsx";
import VueCounter from "../components/VueCounter.vue";
import SvelteCounter from "../components/SvelteCounter.svelte";
// Component Script:
// You can write any JavaScript/TypeScript that you'd like here.
// It will run during the build, but never in the browser.
// All variables are available to use in the HTML template below.
const title = "Astro Markdown";
const variable = "content";
const items = ["A", "B", "C"];
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---
<Layout content={{ title }}>
<Markdown>
# Introducing {title}
**Astro Markdown** brings native Markdown support to HTML!
> It's inspired by [`MDX`](https://mdxjs.com/) and powered by [`remark`](https://github.com/remarkjs/remark).
The best part? It comes with all the Astro features you expect.
[Other example](./other)
## Embed framework components
<ReactCounter client:visible />
<PreactCounter client:visible />
<VueCounter client:visible />
<SvelteCounter client:visible />
## Use Expressions
You can use any {variable} in scope and use JavaScript for templating ({items.join(', ')})
## Oh yeah...
<ReactCounter client:visible>
🤯 It's also _recursive_!
### Markdown can be embedded in any child component
</ReactCounter>
## Code
Should work!
```js
import Something from "./another";
const thing = new Something();
```
</Markdown>
</Layout>

View file

@ -1,18 +0,0 @@
---
title: Some Markdown Page
layout: ../layouts/main.astro
---
# Code
```js
var foo = 'bar';
function doSomething() {
return foo;
}
```
# Paragraph
text here.

View file

@ -1,70 +0,0 @@
pre,
code {
color: #d4d4d4;
font-size: 14px;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 1.5;
direction: ltr;
white-space: pre;
text-align: left;
text-shadow: none;
word-break: normal;
word-spacing: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre::selection,
code::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
pre,
code {
text-shadow: none;
}
}
pre {
margin: 0.5rem 0 16px;
padding: 0.8rem 1rem 0.9rem;
overflow: auto;
background: #282a36;
border-radius: 4px;
}
code {
padding: 0.1em 0.3em;
color: #db4c69;
background: #f9f2f4;
border-radius: 0.3em;
white-space: pre-wrap;
}
pre.astro-code > code {
all: unset;
}
/*********************************************************
* Line highlighting
*/
pre[data-line] {
position: relative;
}
pre > code {
position: relative;
z-index: 1;
}
body {
max-width: 900px;
margin: auto;
}

View file

@ -1,15 +0,0 @@
{
"compilerOptions": {
// Enable top-level await, and other modern ESM features.
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
"moduleResolution": "node",
// Enable JSON imports.
"resolveJsonModule": true,
// Enable stricter transpilation for better output.
"isolatedModules": true,
// Add type definitions for our Astro runtime.
"types": ["astro/client"]
}
}

View file

@ -1,3 +1,2 @@
export { default as Code } from './Code.astro';
export { default as Debug } from './Debug.astro';
export { default as Markdown } from './Markdown.astro';

View file

@ -82,7 +82,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
"@astrojs/compiler": "^0.19.0",
"@astrojs/compiler": "^0.20.0",
"@astrojs/language-server": "^0.20.0",
"@astrojs/markdown-remark": "^0.12.0",
"@astrojs/prism": "0.6.1",

View file

@ -34,15 +34,4 @@ describe('Astro Markdown plugins', () => {
// teste 2: Added .title to h1
expect($('#hello-world').hasClass('title')).to.equal(true);
});
it('Can render Astro <Markdown> with plugins', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
// test 1: Added a TOC
expect($('.toc')).to.have.lengthOf(1);
// teste 2: Added .title to h1
expect($('#hello-world').hasClass('title')).to.equal(true);
});
});

View file

@ -23,20 +23,6 @@ describe('Astro Markdown Shiki', () => {
expect($('pre').attr().style).to.equal('background-color: #0d1117; overflow-x: auto;');
});
it('Can render Astro <Markdown> with shiki', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
// There should be no HTML from Prism
expect($('.token')).to.have.lengthOf(0);
expect($('pre')).to.have.lengthOf(2);
expect($('span.line')).to.have.lengthOf(2);
expect($('span.line').get(0).children).to.have.lengthOf(1);
expect($('span.line').get(1).children).to.have.lengthOf(5);
});
it('Can render diff syntax with "user-select: none"', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
@ -63,15 +49,6 @@ describe('Astro Markdown Shiki', () => {
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #ffffff; overflow-x: auto;');
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre')).to.have.lengthOf(1);
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #ffffff; overflow-x: auto;');
});
});
describe('Custom theme', async () => {
@ -90,15 +67,6 @@ describe('Astro Markdown Shiki', () => {
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #FDFDFE; overflow-x: auto;');
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre')).to.have.lengthOf(1);
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #FDFDFE; overflow-x: auto;');
});
});
});
@ -125,21 +93,6 @@ describe('Astro Markdown Shiki', () => {
'<span style="color: #c9d1d9">This language does not exist</span>'
);
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
const segments = $('.line').get(6).children;
expect(segments).to.have.lengthOf(3);
expect(segments[0].attribs.style).to.be.equal('color: #C9D1D9');
expect(segments[1].attribs.style).to.be.equal('color: #79C0FF');
const unknownLang = $('.line').last().html();
expect(unknownLang).to.be.equal(
'<span style="color: #c9d1d9">This language does not exist</span>'
);
});
});
describe('Wrap', () => {
@ -160,14 +113,6 @@ describe('Astro Markdown Shiki', () => {
expect($('pre')).to.have.lengthOf(1);
expect($('pre').attr('style')).to.equal(style);
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
});
@ -187,14 +132,6 @@ describe('Astro Markdown Shiki', () => {
expect($('pre')).to.have.lengthOf(1);
expect($('pre').attr('style')).to.equal(style);
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
describe('wrap = null', () => {
@ -213,13 +150,5 @@ describe('Astro Markdown Shiki', () => {
expect($('pre')).to.have.lengthOf(1);
expect($('pre').attr('style')).to.equal(style);
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
});

View file

@ -12,17 +12,6 @@ describe('Astro Markdown', () => {
await fixture.build();
});
it('Can load markdown pages with Astro', async () => {
const html = await fixture.readFile('/post/index.html');
const $ = cheerio.load(html);
// test 1: There is a div added in markdown
expect($('#first').length).to.be.ok;
// test 2: There is a div added via a component from markdown
expect($('#test').length).to.be.ok;
});
it('Can parse JSX expressions in markdown pages', async () => {
const html = await fixture.readFile('/jsx-expressions/index.html');
const $ = cheerio.load(html);
@ -99,13 +88,6 @@ describe('Astro Markdown', () => {
expect(html).not.to.match(new RegExp('/src/scripts/test.js'));
});
it('Can load more complex jsxy stuff', async () => {
const html = await fixture.readFile('/complex/index.html');
const $ = cheerio.load(html);
expect($('#test').text()).to.equal('Hello world');
});
it('Empty code blocks do not fail', async () => {
const html = await fixture.readFile('/empty-code/index.html');
const $ = cheerio.load(html);
@ -117,143 +99,11 @@ describe('Astro Markdown', () => {
expect($('pre')[1].children).to.have.lengthOf(0);
});
it('Runs code blocks through syntax highlighter', async () => {
const html = await fixture.readFile('/code/index.html');
const $ = cheerio.load(html);
// test 1: There are child spans in code blocks
expect($('code span').length).greaterThan(0);
});
it('Scoped styles should not break syntax highlight', async () => {
const html = await fixture.readFile('/scopedStyles-code/index.html');
const $ = cheerio.load(html);
// test 1: <pre> tag has correct shiki class
expect($('pre').hasClass('astro-code')).to.equal(true);
// test 2: inline styles are still applied
expect($('pre').is('[style]')).to.equal(true);
// test 3: There are styled child spans in code blocks
expect($('pre code span').length).to.be.greaterThan(0);
expect($('pre code span').is('[style]')).to.equal(true);
});
function isAstroScopedClass(cls) {
return /^astro-.*/.test(cls);
}
it('Scoped styles should be applied to syntax highlighted lines', async () => {
const html = await fixture.readFile('/scopedStyles-code/index.html');
const $ = cheerio.load(html);
// test 1: the "pre" tag receives scoped style
const preClassList = $('pre').attr('class').split(/\s+/);
expect(preClassList.length).to.equal(2);
const preAstroClass = preClassList.find(isAstroScopedClass);
expect(Boolean(preAstroClass)).to.equal(true);
// test 2: each "span" line receives scoped style
const spanClassList = $('pre code span').attr('class').split(/\s+/);
expect(spanClassList.length).to.equal(2);
const spanAstroClass = spanClassList.find(isAstroScopedClass);
expect(Boolean(spanAstroClass)).to.equal(true);
});
it('Renders correctly when deeply nested on a page', async () => {
const html = await fixture.readFile('/deep/index.html');
const $ = cheerio.load(html);
// test 1: Rendered all children
expect($('#deep').children()).to.have.lengthOf(3);
// tests 24: Only rendered title in each section
expect($('.a').children()).to.have.lengthOf(1);
expect($('.b').children()).to.have.lengthOf(1);
expect($('.c').children()).to.have.lengthOf(1);
// test 57: Rendered title in correct section
expect($('.a > h2').text()).to.equal('A');
expect($('.b > h2').text()).to.equal('B');
expect($('.c > h2').text()).to.equal('C');
});
it('Renders dynamic content though the content attribute', async () => {
const html = await fixture.readFile('/external/index.html');
const $ = cheerio.load(html);
// test 1: Rendered markdown content
expect($('#outer')).to.have.lengthOf(1);
// test 2: Nested markdown content
expect($('#inner')).to.have.lengthOf(1);
// test 3: Scoped class passed down
expect($('#inner').is('[class]')).to.equal(true);
});
it('Renders curly braces correctly', async () => {
const html = await fixture.readFile('/braces/index.html');
const $ = cheerio.load(html);
// test 1: Rendered curly braces markdown content
expect($('code')).to.have.lengthOf(3);
// test 2: Rendered curly braces markdown content
expect($('code:first-child').text()).to.equal('({})');
// test 3: Rendered curly braces markdown content
expect($('code:nth-child(2)').text()).to.equal('{...props}');
// test 4: Rendered curly braces markdown content
expect($('code:last-child').text()).to.equal('{/* JavaScript */}');
});
it('Does not close parent early when using content attribute (#494)', async () => {
const html = await fixture.readFile('/close/index.html');
const $ = cheerio.load(html);
// test <Markdown content /> closed div#target early
expect($('#target').children()).to.have.lengthOf(2);
});
it('Can render markdown with --- for horizontal rule', async () => {
const html = await fixture.readFile('/dash/index.html');
expect(!!html).to.equal(true);
});
it('Can render markdown content prop (#1259)', async () => {
const html = await fixture.readFile('/content/index.html');
const $ = cheerio.load(html);
// test Markdown rendered correctly via content prop
expect($('h1').text()).to.equal('Foo');
});
it("doesn't occurs TypeError when no elements", async () => {
const html = await fixture.readFile('/no-elements/index.html');
// render html without error
expect(html).to.be.ok;
});
it('can render nested list correctly', async () => {
const html = await fixture.readFile('/nested-list/index.html');
const $ = cheerio.load(html);
/**
* - list
* - list
*/
expect($('#target > ul > li').children()).to.have.lengthOf(1);
expect($('#target > ul > li > ul > li').text()).to.equal('nested list');
/**
* 1. Hello
* 1. nested hello
*/
expect($('#target > ol > li').children()).to.have.lengthOf(1);
expect($('#target > ol > li > ol > li').text()).to.equal('nested hello');
});
it('Exposes raw markdown content', async () => {
const { raw } = JSON.parse(await fixture.readFile('/raw-content.json'));

View file

@ -1,6 +0,0 @@
---
import { Markdown } from 'astro/components';
const content = '# Foo';
---
<Markdown content={content} />

View file

@ -1,5 +0,0 @@
---
import { Markdown } from 'astro/components';
---
<Markdown></Markdown>

View file

@ -1,14 +0,0 @@
---
import { Markdown } from 'astro/components';
---
<html>
<head><title>Testing</title></head>
<body>
<Markdown>
# Something
else here
</Markdown>
</body>
</html>

View file

@ -31,10 +31,4 @@ describe('Markdown pages in SSR', () => {
const $ = cheerioLoad(html);
expect($('#subheading').text()).to.equal('Subheading');
});
it('Renders the Markdown component correctly', async () => {
const html = await fetchHTML('/page');
const $ = cheerioLoad(html);
expect($('#something')).to.have.lengthOf(1);
});
});

View file

@ -0,0 +1,31 @@
{
"name": "@astrojs/markdown-component",
"version": "0.1.0",
"type": "module",
"author": "withastro",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/withastro/astro.git",
"directory": "packages/markdown/component"
},
"bugs": "https://github.com/withastro/astro/issues",
"homepage": "https://astro.build",
"main": "./Markdown.astro",
"exports": {
".": {
"astro": "./Markdown.astro"
}
},
"scripts": {
"test": "mocha --exit --timeout 20000"
},
"devDependencies": {
"astro": "workspace:*",
"chai": "^4.3.6",
"cheerio": "^1.0.0-rc.11",
"mocha": "^9.2.2",
"@types/mocha": "^9.1.1"
},
"keywords": ["astro", "astro-component"]
}

View file

@ -0,0 +1,17 @@
# @astrojs/markdown
This package brings legacy support for the `<Markdown />` component to all Astro projects.
> The `<Markdown />` component does not work in SSR. Consider [importing Markdown content](https://docs.astro.build/en/guides/markdown-content/#importing-markdown) instead.
:::
```astro
---
import Markdown from '@astrojs/markdown-component';
---
<Markdown>
# Markdown syntax is now supported! **Yay!**
</Markdown>
```
See our [Markdown Guide](https://docs.astro.build/en/guides/markdown-content/) for more info.

View file

@ -0,0 +1,37 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
import addClasses from './fixtures/astro-markdown-plugins/add-classes.mjs';
describe('Astro Markdown plugins', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown-plugins/',
markdown: {
remarkPlugins: [
'remark-code-titles',
['rehype-autolink-headings', { behavior: 'prepend' }],
],
rehypePlugins: [
'rehype-slug',
['rehype-toc', { headings: ['h2', 'h3'] }],
[addClasses, { 'h1,h2,h3': 'title' }],
],
},
});
await fixture.build();
});
it('Can render Astro <Markdown> with plugins', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
// test 1: Added a TOC
expect($('.toc')).to.have.lengthOf(1);
// teste 2: Added .title to h1
expect($('#hello-world').hasClass('title')).to.equal(true);
});
});

View file

@ -0,0 +1,147 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro Markdown Shiki', () => {
describe('Render shiki', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/normal/' });
await fixture.build();
});
it('Can render Astro <Markdown> with shiki', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
// There should be no HTML from Prism
expect($('.token')).to.have.lengthOf(0);
expect($('pre')).to.have.lengthOf(2);
expect($('span.line')).to.have.lengthOf(2);
expect($('span.line').get(0).children).to.have.lengthOf(1);
expect($('span.line').get(1).children).to.have.lengthOf(5);
});
});
describe('Themes', () => {
describe('Integrated theme', async () => {
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/themes-integrated/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre')).to.have.lengthOf(1);
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #ffffff; overflow-x: auto;');
});
});
describe('Custom theme', async () => {
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/themes-custom/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre')).to.have.lengthOf(1);
expect($('pre').hasClass('astro-code')).to.equal(true);
expect($('pre').attr().style).to.equal('background-color: #FDFDFE; overflow-x: auto;');
});
});
});
describe('Custom langs', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/langs/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
const segments = $('.line').get(6).children;
expect(segments).to.have.lengthOf(3);
expect(segments[0].attribs.style).to.be.equal('color: #C9D1D9');
expect(segments[1].attribs.style).to.be.equal('color: #79C0FF');
const unknownLang = $('.line').last().html();
expect(unknownLang).to.be.equal(
'<span style="color: #c9d1d9">This language does not exist</span>'
);
});
});
describe('Wrap', () => {
describe('wrap = true', () => {
const style =
'background-color: #0d1117; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-true/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
});
describe('wrap = false', () => {
const style = 'background-color: #0d1117; overflow-x: auto;';
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-false/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
describe('wrap = null', () => {
const style = 'background-color: #0d1117';
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-null/' });
await fixture.build();
});
it('<Markdown /> component', async () => {
const html = await fixture.readFile('/astro/index.html');
const $ = cheerio.load(html);
expect($('pre').get(0).attribs.style).to.equal(style);
expect($('pre').get(1).attribs.style).to.equal(style);
});
});
});

View file

@ -0,0 +1,350 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture, fixLineEndings } from './test-utils.js';
describe('Astro Markdown', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown/',
});
await fixture.build();
});
it('Can load markdown pages with Astro', async () => {
const html = await fixture.readFile('/post/index.html');
const $ = cheerio.load(html);
// test 1: There is a div added in markdown
expect($('#first').length).to.be.ok;
// test 2: There is a div added via a component from markdown
expect($('#test').length).to.be.ok;
});
it('Can parse JSX expressions in markdown pages', async () => {
const html = await fixture.readFile('/jsx-expressions/index.html');
const $ = cheerio.load(html);
expect($('h2').html()).to.equal('Blog Post with JSX expressions');
expect(html).to.contain('JSX at the start of the line!');
for (let listItem of ['test-1', 'test-2', 'test-3']) {
expect($(`#${listItem}`).html()).to.equal(`${listItem}`);
}
});
it('Can handle slugs with JSX expressions in markdown pages', async () => {
const html = await fixture.readFile('/slug/index.html');
const $ = cheerio.load(html);
expect($('h1').attr('id')).to.equal('my-blog-post');
});
it('Can handle code elements without extra spacing', async () => {
const html = await fixture.readFile('/code-element/index.html');
const $ = cheerio.load(html);
$('code').each((_, el) => {
expect($(el).html()).to.equal($(el).html().trim());
});
});
it('Can handle namespaced components in markdown', async () => {
const html = await fixture.readFile('/namespace/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('Hello Namespace!');
expect($('button').length).to.equal(1);
});
it('Correctly handles component children in markdown pages (#3319)', async () => {
const html = await fixture.readFile('/children/index.html');
expect(html).not.to.contain('<p></p>');
});
it('Can handle HTML comments in markdown pages', async () => {
const html = await fixture.readFile('/comment/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('It works!');
});
it('Prevents `*/` sequences from breaking HTML comments (#3476)', async () => {
const html = await fixture.readFile('/comment-with-js/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('It still works!');
});
it('Can handle HTML comments in inline code', async () => {
const html = await fixture.readFile('/comment-with-js/index.html');
const $ = cheerio.load(html);
expect($('p code').text()).to.equal('<!-- HTML comments in code -->');
});
it('Can handle HTML comments in code fences', async () => {
const html = await fixture.readFile('/comment-with-js/index.html');
const $ = cheerio.load(html);
expect($('pre > code').text()).to.equal('<!-- HTML comments in code fence -->');
});
// https://github.com/withastro/astro/issues/3254
it('Can handle scripts in markdown pages', async () => {
const html = await fixture.readFile('/script/index.html');
expect(html).not.to.match(new RegExp('/src/scripts/test.js'));
});
it('Can load more complex jsxy stuff', async () => {
const html = await fixture.readFile('/complex/index.html');
const $ = cheerio.load(html);
expect($('#test').text()).to.equal('Hello world');
});
it('Empty code blocks do not fail', async () => {
const html = await fixture.readFile('/empty-code/index.html');
const $ = cheerio.load(html);
// test 1: There is not a `<code>` in the codeblock
expect($('pre')[0].children).to.have.lengthOf(1);
// test 2: The empty `<pre>` failed to render
expect($('pre')[1].children).to.have.lengthOf(0);
});
it('Runs code blocks through syntax highlighter', async () => {
const html = await fixture.readFile('/code/index.html');
const $ = cheerio.load(html);
// test 1: There are child spans in code blocks
expect($('code span').length).greaterThan(0);
});
it('Scoped styles should not break syntax highlight', async () => {
const html = await fixture.readFile('/scopedStyles-code/index.html');
const $ = cheerio.load(html);
// test 1: <pre> tag has correct shiki class
expect($('pre').hasClass('astro-code')).to.equal(true);
// test 2: inline styles are still applied
expect($('pre').is('[style]')).to.equal(true);
// test 3: There are styled child spans in code blocks
expect($('pre code span').length).to.be.greaterThan(0);
expect($('pre code span').is('[style]')).to.equal(true);
});
function isAstroScopedClass(cls) {
return /^astro-.*/.test(cls);
}
it('Scoped styles should be applied to syntax highlighted lines', async () => {
const html = await fixture.readFile('/scopedStyles-code/index.html');
const $ = cheerio.load(html);
// test 1: the "pre" tag receives scoped style
const preClassList = $('pre').attr('class').split(/\s+/);
expect(preClassList.length).to.equal(2);
const preAstroClass = preClassList.find(isAstroScopedClass);
expect(Boolean(preAstroClass)).to.equal(true);
// test 2: each "span" line receives scoped style
const spanClassList = $('pre code span').attr('class').split(/\s+/);
expect(spanClassList.length).to.equal(2);
const spanAstroClass = spanClassList.find(isAstroScopedClass);
expect(Boolean(spanAstroClass)).to.equal(true);
});
it('Renders correctly when deeply nested on a page', async () => {
const html = await fixture.readFile('/deep/index.html');
const $ = cheerio.load(html);
// test 1: Rendered all children
expect($('#deep').children()).to.have.lengthOf(3);
// tests 24: Only rendered title in each section
expect($('.a').children()).to.have.lengthOf(1);
expect($('.b').children()).to.have.lengthOf(1);
expect($('.c').children()).to.have.lengthOf(1);
// test 57: Rendered title in correct section
expect($('.a > h2').text()).to.equal('A');
expect($('.b > h2').text()).to.equal('B');
expect($('.c > h2').text()).to.equal('C');
});
it('Renders dynamic content though the content attribute', async () => {
const html = await fixture.readFile('/external/index.html');
const $ = cheerio.load(html);
// test 1: Rendered markdown content
expect($('#outer')).to.have.lengthOf(1);
// test 2: Nested markdown content
expect($('#inner')).to.have.lengthOf(1);
// test 3: Scoped class passed down
expect($('#inner').is('[class]')).to.equal(true);
});
it('Renders curly braces correctly', async () => {
const html = await fixture.readFile('/braces/index.html');
const $ = cheerio.load(html);
// test 1: Rendered curly braces markdown content
expect($('code')).to.have.lengthOf(3);
// test 2: Rendered curly braces markdown content
expect($('code:first-child').text()).to.equal('({})');
// test 3: Rendered curly braces markdown content
expect($('code:nth-child(2)').text()).to.equal('{...props}');
// test 4: Rendered curly braces markdown content
expect($('code:last-child').text()).to.equal('{/* JavaScript */}');
});
it('Does not close parent early when using content attribute (#494)', async () => {
const html = await fixture.readFile('/close/index.html');
const $ = cheerio.load(html);
// test <Markdown content /> closed div#target early
expect($('#target').children()).to.have.lengthOf(2);
});
it('Can render markdown with --- for horizontal rule', async () => {
const html = await fixture.readFile('/dash/index.html');
expect(!!html).to.equal(true);
});
it('Can render markdown content prop (#1259)', async () => {
const html = await fixture.readFile('/content/index.html');
const $ = cheerio.load(html);
// test Markdown rendered correctly via content prop
expect($('h1').text()).to.equal('Foo');
});
it("doesn't occurs TypeError when no elements", async () => {
const html = await fixture.readFile('/no-elements/index.html');
// render html without error
expect(html).to.be.ok;
});
it('can render nested list correctly', async () => {
const html = await fixture.readFile('/nested-list/index.html');
const $ = cheerio.load(html);
/**
* - list
* - list
*/
expect($('#target > ul > li').children()).to.have.lengthOf(1);
expect($('#target > ul > li > ul > li').text()).to.equal('nested list');
/**
* 1. Hello
* 1. nested hello
*/
expect($('#target > ol > li').children()).to.have.lengthOf(1);
expect($('#target > ol > li > ol > li').text()).to.equal('nested hello');
});
it('Exposes raw markdown content', async () => {
const { raw } = JSON.parse(await fixture.readFile('/raw-content.json'));
expect(fixLineEndings(raw)).to.equal(
`\n## With components\n\n### Non-hydrated\n\n<Hello name="Astro Naut" />\n\n### Hydrated\n\n<Counter client:load />\n<SvelteButton client:load />\n`
);
});
it('Exposes HTML parser for raw markdown content', async () => {
const { compiled } = JSON.parse(await fixture.readFile('/raw-content.json'));
expect(fixLineEndings(compiled)).to.equal(
`<h2 id="with-components">With components</h2>\n<h3 id="non-hydrated">Non-hydrated</h3>\n<Hello name="Astro Naut" />\n<h3 id="hydrated">Hydrated</h3>\n<Counter client:load />\n<SvelteButton client:load />`
);
});
it('Allows referencing Vite env var names in markdown (#3412)', async () => {
const html = await fixture.readFile('/vite-env-vars/index.html');
const $ = cheerio.load(html);
// test 1: referencing an existing var name
expect($('code').eq(0).text()).to.equal('import.meta.env.SITE');
expect($('li').eq(0).text()).to.equal('import.meta.env.SITE');
expect($('code').eq(3).text()).to.contain('site: import.meta.env.SITE');
expect($('blockquote').text()).to.contain('import.meta.env.SITE');
// test 2: referencing a non-existing var name
expect($('code').eq(1).text()).to.equal('import.meta.env.TITLE');
expect($('li').eq(1).text()).to.equal('import.meta.env.TITLE');
expect($('code').eq(3).text()).to.contain('title: import.meta.env.TITLE');
expect($('blockquote').text()).to.contain('import.meta.env.TITLE');
// test 3: referencing `import.meta.env` itself (without any var name)
expect($('code').eq(2).text()).to.equal('import.meta.env');
expect($('li').eq(2).text()).to.equal('import.meta.env');
expect($('code').eq(3).text()).to.contain('// Use Vite env vars with import.meta.env');
expect($('blockquote').text()).to.match(/import\.meta\.env\s*$/);
});
it('Escapes HTML tags in code blocks', async () => {
const html = await fixture.readFile('/code-in-md/index.html');
const $ = cheerio.load(html);
expect($('code').eq(0).html()).to.equal('&lt;script&gt;');
expect($('blockquote').length).to.equal(1);
expect($('code').eq(1).html()).to.equal('&lt;/script&gt;');
expect($('pre').html()).to.contain('&gt;This should also work without any problems.&lt;');
});
it('Allows defining slot contents in component children', async () => {
const html = await fixture.readFile('/slots/index.html');
const $ = cheerio.load(html);
const slots = $('article').eq(0);
expect(slots.find('> .fragmentSlot > div').text()).to.contain('1:');
expect(slots.find('> .fragmentSlot > div + p').text()).to.contain('2:');
expect(slots.find('> .pSlot > p[title="hello"]').text()).to.contain('3:');
expect(slots.find('> .defaultSlot').html()).to.match(
new RegExp(
`<div>4: Div in default slot</div>` +
// Optional extra paragraph due to the line breaks between components
`(<p></p>)?` +
`<p>5: Paragraph in fragment in default slot</p>` +
// Optional whitespace due to the line breaks between components
`[\s\n]*` +
`6: Regular text in default slot`
)
);
const nestedSlots = $('article').eq(1);
expect(nestedSlots.find('> .fragmentSlot').html()).to.contain('1:');
expect(nestedSlots.find('> .pSlot > p').text()).to.contain('2:');
expect(nestedSlots.find('> .defaultSlot > article').text().replace(/\s+/g, ' ')).to.equal(
`
3: nested fragmentSlot
4: nested pSlot
5: nested text in default slot
`.replace(/\s+/g, ' ')
);
expect($('article').eq(3).text().replace(/[^❌]/g, '')).to.equal('❌❌❌');
expect($('article').eq(4).text().replace(/[^❌]/g, '')).to.equal('❌❌❌');
});
it('Generate the right props for the layout', async () => {
const html = await fixture.readFile('/layout-props/index.html');
const $ = cheerio.load(html);
expect($('#title').text()).to.equal('Hello world!');
expect($('#url').text()).to.equal('/layout-props');
expect($('#file').text()).to.match(/.*\/layout-props.md$/);
});
});

View file

@ -0,0 +1,18 @@
import { selectAll } from 'hast-util-select';
export default (additions) => {
const adders = Object.entries(additions).map(adder);
return (node) => adders.forEach((a) => a(node));
};
const adder = ([selector, className]) => {
const writer = write(className);
return (node) => selectAll(selector, node).forEach(writer);
};
const write =
(className) =>
({ properties }) => {
if (!properties.className) properties.className = className;
else properties.className += ` ${className}`;
};

View file

@ -0,0 +1,7 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
// https://astro.build/config
export default defineConfig({
integrations: [preact()],
});

View file

@ -0,0 +1,12 @@
{
"name": "@test/astro-markdown-component-plugins",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/preact": "workspace:*",
"@astrojs/markdown-component": "workspace:*",
"astro": "workspace:*",
"hast-util-select": "^5.0.2",
"rehype-slug": "^5.0.1"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,19 @@
const riGrammar = JSON.parse(
String.raw`{"name":"rinfo","patterns":[{"include":"#lf-rinfo"}],"repository":{"lf-rinfo":{"patterns":[{"include":"#control"},{"include":"#operator"},{"include":"#strings"},{"include":"#number"},{"include":"#comment"},{"include":"#literal"}]},"control":{"patterns":[{"name":"keyword.control.ri","match":"\\b(si|mientras|repetir)\\b"},{"name":"keyword.other.ri","match":"\\b(programa|robots|areas|variables|comenzar|fin)\\b"},{"name":"support.function.other.ri","match":"\\b(tomarFlor|HayFlorEnLaBolsa|HayFlorEnLaEsquina|depositarFlor|HayPapelEnLaBolsa|HayPapelEnLaEsquina|tomarPapel|depositarPapel)\\b"}]},"operator":{"comment":"Captures operators and also puts them in different sub-groups that further describe them","patterns":[{"match":"\\+|-|\\*|/","name":"keyword.operator.arithmetic.ri"},{"match":"<|>|<=|>=|=|<>|!=","name":"keyword.operator.comparison.ri"},{"match":"\\b(Pos|Informar|Leer|Iniciar|AsignarArea|AreaC)\\b","name":"support.function.arithmetic.ri"},{"match":":=","name":"keyword.operator.assign.ri"},{"match":"(&|~)","name":"support.function.logical.ri"}]},"strings":{"name":"string.quoted.double.ri","beginCaptures":{"0":{"name":"string.quoted.double.begin.ri"}},"endCaptures":{"0":{"name":"string.quoted.double.end.ri"}},"begin":"\"","end":"\"","patterns":[{"name":"constant.character.escape.ri","match":"\\\\."}]},"comment":{"patterns":[{"name":"comment.block.ri","begin":"{","end":"}","patterns":[{"include":"#comment"}]}]},"literal":{"patterns":[{"name":"constant.language.ri","match":"\\b(verdadero|falso|boolean|numero)\\b"}]},"number":{"patterns":[{"comment":"Captures decimal numbers, with the negative sign being considered an operator","match":"(-)?(?:((?:\\b\\d+(?:\\.\\d*)?|\\.\\d+)(?:\\b|e-?\\d+\\b)%?)|(\\$[0-9]+\\b))","captures":{"1":{"name":"keyword.operator.arithmetic.ri"},"2":{"name":"constant.numeric.decimal.ri"},"3":{"name":"constant.numeric.hex.ri"}}}]}},"scopeName":"source.rinfo"}`
);
export default {
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: {
langs: [
{
id: 'rinfo',
scopeName: 'source.rinfo',
grammar: riGrammar,
aliases: ['ri'],
},
],
},
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-langs",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,5 @@
export default {
markdown: {
syntaxHighlight: 'shiki',
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-normal",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-component-shiki",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-themes-custom",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,6 @@
export default {
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: { theme: 'github-light' },
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-themes-integrated",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,24 @@
---
layout: ../layouts/content.astro
---
# Hello world
```yaml
apiVersion: v3
kind: Pod
metadata:
name: rss-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
- containerPort: 88
```

View file

@ -0,0 +1,6 @@
export default {
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: { wrap: false },
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-wrap-false",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,6 @@
export default {
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: { wrap: null },
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-wrap-null",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,6 @@
export default {
markdown: {
syntaxHighlight: 'shiki',
shikiConfig: { wrap: true },
},
}

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-skiki-wrap-true",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/markdown-component": "workspace:*"
}
}

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
---

View file

@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
integrations: [preact(), svelte()],
site: 'https://astro.build/',
});

View file

@ -0,0 +1,11 @@
{
"name": "@test/astro-markdown-component",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/preact": "workspace:*",
"@astrojs/svelte": "workspace:*",
"@astrojs/markdown-component": "workspace:*",
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,7 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
export default function () {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

View file

@ -0,0 +1,5 @@
import { h } from 'preact';
export default function () {
return <div id="test">Testing</div>;
}

View file

@ -0,0 +1,5 @@
import { h } from 'preact';
export default function ({ name }) {
return <div id="test">Hello {name}</div>;
}

View file

@ -0,0 +1,13 @@
<article>
<section class="fragmentSlot">
<slot name="fragmentSlot">❌ Missing content for slot "fragmentSlot"</slot>
</section>
<section class="pSlot">
<slot name="pSlot">❌ Missing content for slot "pSlot"</slot>
</section>
<section class="defaultSlot">
<slot>❌ Missing content for default slot</slot>
</section>
</article>

View file

@ -0,0 +1,11 @@
<script>
let cool = false
</script>
<button on:click={() => cool = true}>This is cool right? {cool}</button>
<style>
button {
background: green;
}
</style>

View file

@ -0,0 +1,20 @@
import { h } from 'preact';
const TextBlock = ({
title,
children,
noPadding = false,
}) => {
return (
<div
className={`${
noPadding ? "" : "md:px-2 lg:px-4"
} flex-1 prose prose-headings:font-grotesk`}
>
<h3>{title}</h3>
<p>{children}</p>
</div>
);
};
export default TextBlock;

View file

@ -0,0 +1,5 @@
import Counter from './Counter';
export default {
Counter
}

View file

@ -0,0 +1,3 @@
This should have `nospace` around it.
This should have <code class="custom-class">nospace</code> around it.

View file

@ -0,0 +1,6 @@
---
---
## Plain jane
I am plain markdown!

View file

@ -0,0 +1,17 @@
---
setup: |
import Counter from '../components/Counter.jsx'
import Hello from '../components/Hello.jsx'
import SvelteButton from '../components/SvelteButton.svelte'
---
## With components
### Non-hydrated
<Hello name="Astro Naut" />
### Hydrated
<Counter client:load />
<SvelteButton client:load />

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -0,0 +1,17 @@
---
interface Props {
url: string;
file: string;
title: string;
}
const { title, url, file } = Astro.props.content as Props;
---
<html>
<body>
<div id="title">{title}</div>
<div id="url">{url}</div>
<div id="file">{file}</div>
</body>
</html>

View file

@ -1,10 +1,10 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
const title = 'My Blog Post';
const description = 'This is a post about some stuff.';
---
<Markdown>
<Markdown is:raw>
## Interesting Topic
`({})`

View file

@ -0,0 +1,12 @@
---
setup: import TextBlock from '../components/TextBlock'
---
{/* https://github.com/withastro/astro/issues/3319 */}
<TextBlock title="Hello world!" noPadding>
<ul class="not-prose">
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</TextBlock>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
const content = `Markdown *content* to render`;
---

View file

@ -0,0 +1,7 @@
---
const content = await Astro.glob('../content/*.md');
---
<div>
{content.map(({ Content }) => <Content />)}
</div>

View file

@ -0,0 +1,16 @@
# Inline code blocks
`<script>` tags in **Astro** components are now built,
bundled and optimized by default.
> Markdown formatting still works between tags in inline code blocks.
We can also use closing `</script>` tags without any problems.
# Fenced code blocks
```html
<body>
<div>This should also work without any problems.</div>
</body>
```

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
const title = 'My Blog Post';
const description = 'This is a post about some stuff.';
---

View file

@ -0,0 +1,23 @@
<!--
HTML comments with */ inside!
-->
<!--
```js
/**
* It even works inside nested fenced code blocks!
*/
function test() {
/* Yay */
return 'Nice!';
}
```
-->
```
<!-- HTML comments in code fence -->
```
`<!-- HTML comments in code -->`
# It still works!

View file

@ -0,0 +1,2 @@
<!-- HTML comments! -->
# It works!

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
import Hello from '../components/Hello.jsx';
import Counter from '../components/Counter.jsx';

View file

@ -0,0 +1,6 @@
---
import Markdown from '@astrojs/markdown-component';
const content = '# Foo';
---
<Markdown content={content} />

View file

@ -0,0 +1,14 @@
---
title: My Blog Post
layout: ../layouts/content.astro
---
## Title
Hello world
With this in the body ---
## Another
more content

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
import Layout from '../layouts/content.astro';
import Hello from '../components/Hello.jsx';
import Counter from '../components/Counter.jsx';

View file

@ -0,0 +1,20 @@
---
title: My Blog Post
layout: ../layouts/content.astro
---
## Title
Hello world
With this in the body ---
## Another
more content
```
```
<pre></pre>

View file

@ -1,5 +1,5 @@
---
import { Markdown } from 'astro/components';
import Markdown from '@astrojs/markdown-component';
const outer = `# Outer`;
const inner = `## Inner`;
@ -16,4 +16,4 @@ const inner = `## Inner`;
<Markdown content={inner} />
</Markdown>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show more