@astrojs/markdown-remark 2.0.0-beta.0 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +63 -0
- package/dist/index.js +11 -25
- package/dist/rehype-collect-headings.js +4 -14
- package/dist/remark-content-rel-image-error.js +2 -0
- package/dist/remark-shiki.js +2 -2
- package/dist/types.d.ts +1 -3
- package/package.json +7 -15
- package/src/index.ts +11 -30
- package/src/rehype-collect-headings.ts +3 -19
- package/src/remark-content-rel-image-error.ts +2 -0
- package/src/remark-shiki.ts +2 -2
- package/src/types.ts +1 -3
- package/test/autolinking.test.js +10 -75
- package/test/entities.test.js +5 -14
- package/test/plugins.test.js +2 -0
- package/test/test-utils.js +3 -0
- package/dist/mdast-util-mdxish.d.ts +0 -2
- package/dist/mdast-util-mdxish.js +0 -14
- package/dist/mdxjs.d.ts +0 -3
- package/dist/mdxjs.js +0 -20
- package/dist/rehype-escape.d.ts +0 -2
- package/dist/rehype-escape.js +0 -21
- package/dist/rehype-expressions.d.ts +0 -1
- package/dist/rehype-expressions.js +0 -20
- package/dist/rehype-islands.d.ts +0 -1
- package/dist/rehype-islands.js +0 -27
- package/dist/rehype-jsx.d.ts +0 -2
- package/dist/rehype-jsx.js +0 -51
- package/dist/remark-escape.d.ts +0 -1
- package/dist/remark-escape.js +0 -13
- package/dist/remark-mark-and-unravel.d.ts +0 -17
- package/dist/remark-mark-and-unravel.js +0 -45
- package/dist/remark-mdxish.d.ts +0 -1
- package/dist/remark-mdxish.js +0 -53
- package/dist/remark-unwrap.d.ts +0 -1
- package/dist/remark-unwrap.js +0 -31
- package/src/mdast-util-mdxish.ts +0 -12
- package/src/mdxjs.ts +0 -27
- package/src/rehype-escape.ts +0 -22
- package/src/rehype-expressions.ts +0 -18
- package/src/rehype-islands.ts +0 -43
- package/src/rehype-jsx.ts +0 -65
- package/src/remark-escape.ts +0 -15
- package/src/remark-mark-and-unravel.ts +0 -72
- package/src/remark-mdxish.ts +0 -61
- package/src/remark-unwrap.ts +0 -44
- package/test/components.test.js +0 -78
- package/test/expressions.test.js +0 -125
- package/test/strictness.test.js +0 -98
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
[35m@astrojs/markdown-remark:build: [0mcache hit, replaying output [
|
|
1
|
+
[35m@astrojs/markdown-remark:build: [0mcache hit, replaying output [2m1c75f68e5ab1d40c[0m
|
|
2
2
|
[35m@astrojs/markdown-remark:build: [0m
|
|
3
|
-
[35m@astrojs/markdown-remark:build: [0m> @astrojs/markdown-remark@2.0.0-beta.
|
|
3
|
+
[35m@astrojs/markdown-remark:build: [0m> @astrojs/markdown-remark@2.0.0-beta.2 build /home/runner/work/astro/astro/packages/markdown/remark
|
|
4
4
|
[35m@astrojs/markdown-remark:build: [0m> astro-scripts build "src/**/*.ts" && tsc -p tsconfig.json
|
|
5
5
|
[35m@astrojs/markdown-remark:build: [0m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @astrojs/markdown-remark
|
|
2
2
|
|
|
3
|
+
## 2.0.0-beta.2
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#5785](https://github.com/withastro/astro/pull/5785) [`16107b6a1`](https://github.com/withastro/astro/commit/16107b6a10514ef1b563e585ec9add4b14f42b94) Thanks [@delucis](https://github.com/delucis)! - Drop support for legacy Astro-flavored Markdown
|
|
8
|
+
|
|
9
|
+
- [#5825](https://github.com/withastro/astro/pull/5825) [`52209ca2a`](https://github.com/withastro/astro/commit/52209ca2ad72a30854947dcb3a90ab4db0ac0a6f) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Baseline the experimental `contentCollections` flag. You're free to remove this from your astro config!
|
|
10
|
+
|
|
11
|
+
```diff
|
|
12
|
+
import { defineConfig } from 'astro/config';
|
|
13
|
+
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
- experimental: { contentCollections: true }
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- [#5806](https://github.com/withastro/astro/pull/5806) [`7572f7402`](https://github.com/withastro/astro/commit/7572f7402238da37de748be58d678fedaf863b53) Thanks [@matthewp](https://github.com/matthewp)! - Make astro a peerDependency of integrations
|
|
21
|
+
|
|
22
|
+
This marks `astro` as a peerDependency of several packages that are already getting `major` version bumps. This is so we can more properly track the dependency between them and what version of Astro they are being used with.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- [#5837](https://github.com/withastro/astro/pull/5837) [`12f65a4d5`](https://github.com/withastro/astro/commit/12f65a4d55e3fd2993c2f67b18794dd536280c69) Thanks [@giuseppelt](https://github.com/giuseppelt)! - fix shiki css class replace logic
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`01f3f463b`](https://github.com/withastro/astro/commit/01f3f463bf2918b310d130a9fabbf3ee21d14029), [`1f92d64ea`](https://github.com/withastro/astro/commit/1f92d64ea35c03fec43aff64eaf704dc5a9eb30a), [`c2180746b`](https://github.com/withastro/astro/commit/c2180746b4f6d9ef1b6f86924f21f52cc6ab4e63), [`ae8a012a7`](https://github.com/withastro/astro/commit/ae8a012a7b6884a03c50494332ee37b4505c2c3b), [`cf2de5422`](https://github.com/withastro/astro/commit/cf2de5422c26bfdea4c75f76e57b57299ded3e3a), [`ec09bb664`](https://github.com/withastro/astro/commit/ec09bb6642064dbd7d2f3369afb090363ae18de2), [`665a2c222`](https://github.com/withastro/astro/commit/665a2c2225e42881f5a9550599e8f3fc1deea0b4), [`f7aa1ec25`](https://github.com/withastro/astro/commit/f7aa1ec25d1584f7abd421903fbef66b1c050e2a), [`302e0ef8f`](https://github.com/withastro/astro/commit/302e0ef8f5d5232e3348afe680e599f3e537b5c5), [`840412128`](https://github.com/withastro/astro/commit/840412128b00a04515156e92c314a929d6b94f6d), [`1f49cddf9`](https://github.com/withastro/astro/commit/1f49cddf9e9ffc651efc171b2cbde9fbe9e8709d), [`4a1cabfe6`](https://github.com/withastro/astro/commit/4a1cabfe6b9ef8a6fbbcc0727a0dc6fa300cedaa), [`c4b0cb8bf`](https://github.com/withastro/astro/commit/c4b0cb8bf2b41887d9106440bb2e70d421a5f481), [`23dc9ea96`](https://github.com/withastro/astro/commit/23dc9ea96a10343852d965efd41fe6665294f1fb), [`63a6ceb38`](https://github.com/withastro/astro/commit/63a6ceb38d88331451dca64d0034c7c58e3d26f1), [`52209ca2a`](https://github.com/withastro/astro/commit/52209ca2ad72a30854947dcb3a90ab4db0ac0a6f), [`2303f9514`](https://github.com/withastro/astro/commit/2303f95142aa740c99213a098f82b99dd37d74a0)]:
|
|
29
|
+
- astro@2.0.0-beta.2
|
|
30
|
+
- @astrojs/prism@2.0.0-beta.0
|
|
31
|
+
|
|
32
|
+
## 2.0.0-beta.1
|
|
33
|
+
|
|
34
|
+
### Minor Changes
|
|
35
|
+
|
|
36
|
+
- [#5769](https://github.com/withastro/astro/pull/5769) [`93e633922`](https://github.com/withastro/astro/commit/93e633922c2e449df3bb2357b3683af1d3c0e07b) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Introduce a `smartypants` flag to opt-out of Astro's default SmartyPants plugin.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
{
|
|
40
|
+
markdown: {
|
|
41
|
+
smartypants: false,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Migration
|
|
47
|
+
|
|
48
|
+
You may have disabled Astro's built-in plugins (GitHub-Flavored Markdown and Smartypants) with the `extendDefaultPlugins` option. This has now been split into 2 flags to disable each plugin individually:
|
|
49
|
+
|
|
50
|
+
- `markdown.gfm` to disable GitHub-Flavored Markdown
|
|
51
|
+
- `markdown.smartypants` to disable SmartyPants
|
|
52
|
+
|
|
53
|
+
```diff
|
|
54
|
+
// astro.config.mjs
|
|
55
|
+
import { defineConfig } from 'astro/config';
|
|
56
|
+
|
|
57
|
+
export default defineConfig({
|
|
58
|
+
markdown: {
|
|
59
|
+
- extendDefaultPlugins: false,
|
|
60
|
+
+ smartypants: false,
|
|
61
|
+
+ gfm: false,
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
3
66
|
## 2.0.0-beta.0
|
|
4
67
|
|
|
5
68
|
### Major Changes
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { toRemarkInitializeAstroData } from "./frontmatter-injection.js";
|
|
2
2
|
import { loadPlugins } from "./load-plugins.js";
|
|
3
3
|
import { rehypeHeadingIds } from "./rehype-collect-headings.js";
|
|
4
|
-
import rehypeEscape from "./rehype-escape.js";
|
|
5
|
-
import rehypeExpressions from "./rehype-expressions.js";
|
|
6
|
-
import rehypeIslands from "./rehype-islands.js";
|
|
7
|
-
import rehypeJsx from "./rehype-jsx.js";
|
|
8
4
|
import toRemarkContentRelImageError from "./remark-content-rel-image-error.js";
|
|
9
|
-
import remarkEscape from "./remark-escape.js";
|
|
10
|
-
import remarkMarkAndUnravel from "./remark-mark-and-unravel.js";
|
|
11
|
-
import remarkMdxish from "./remark-mdxish.js";
|
|
12
5
|
import remarkPrism from "./remark-prism.js";
|
|
13
6
|
import scopedStyles from "./remark-scoped-styles.js";
|
|
14
7
|
import remarkShiki from "./remark-shiki.js";
|
|
15
|
-
import remarkUnwrap from "./remark-unwrap.js";
|
|
16
8
|
import rehypeRaw from "rehype-raw";
|
|
17
9
|
import rehypeStringify from "rehype-stringify";
|
|
18
10
|
import remarkGfm from "remark-gfm";
|
|
19
11
|
import markdown from "remark-parse";
|
|
20
12
|
import markdownToHtml from "remark-rehype";
|
|
13
|
+
import remarkSmartypants from "remark-smartypants";
|
|
21
14
|
import { unified } from "unified";
|
|
22
15
|
import { VFile } from "vfile";
|
|
23
16
|
import { rehypeHeadingIds as rehypeHeadingIds2 } from "./rehype-collect-headings.js";
|
|
@@ -32,7 +25,8 @@ const markdownConfigDefaults = {
|
|
|
32
25
|
remarkPlugins: [],
|
|
33
26
|
rehypePlugins: [],
|
|
34
27
|
remarkRehype: {},
|
|
35
|
-
gfm: true
|
|
28
|
+
gfm: true,
|
|
29
|
+
smartypants: true
|
|
36
30
|
};
|
|
37
31
|
async function renderMarkdown(content, opts) {
|
|
38
32
|
var _a;
|
|
@@ -44,17 +38,19 @@ async function renderMarkdown(content, opts) {
|
|
|
44
38
|
rehypePlugins = markdownConfigDefaults.rehypePlugins,
|
|
45
39
|
remarkRehype = markdownConfigDefaults.remarkRehype,
|
|
46
40
|
gfm = markdownConfigDefaults.gfm,
|
|
47
|
-
|
|
48
|
-
isExperimentalContentCollections = false,
|
|
41
|
+
smartypants = markdownConfigDefaults.smartypants,
|
|
49
42
|
contentDir,
|
|
50
43
|
frontmatter: userFrontmatter = {}
|
|
51
44
|
} = opts;
|
|
52
45
|
const input = new VFile({ value: content, path: fileURL });
|
|
53
46
|
const scopedClassName = (_a = opts.$) == null ? void 0 : _a.scopedClassName;
|
|
54
|
-
let parser = unified().use(markdown).use(toRemarkInitializeAstroData({ userFrontmatter })).use(
|
|
47
|
+
let parser = unified().use(markdown).use(toRemarkInitializeAstroData({ userFrontmatter })).use([]);
|
|
55
48
|
if (gfm) {
|
|
56
49
|
parser.use(remarkGfm);
|
|
57
50
|
}
|
|
51
|
+
if (smartypants) {
|
|
52
|
+
parser.use(remarkSmartypants);
|
|
53
|
+
}
|
|
58
54
|
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
|
59
55
|
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
|
|
60
56
|
loadedRemarkPlugins.forEach(([plugin, pluginOpts]) => {
|
|
@@ -68,21 +64,13 @@ async function renderMarkdown(content, opts) {
|
|
|
68
64
|
} else if (syntaxHighlight === "prism") {
|
|
69
65
|
parser.use([remarkPrism(scopedClassName)]);
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
parser.use([toRemarkContentRelImageError({ contentDir })]);
|
|
73
|
-
}
|
|
67
|
+
parser.use([toRemarkContentRelImageError({ contentDir })]);
|
|
74
68
|
parser.use([
|
|
75
69
|
[
|
|
76
70
|
markdownToHtml,
|
|
77
71
|
{
|
|
78
72
|
allowDangerousHtml: true,
|
|
79
|
-
passThrough:
|
|
80
|
-
"raw",
|
|
81
|
-
"mdxFlowExpression",
|
|
82
|
-
"mdxJsxFlowElement",
|
|
83
|
-
"mdxJsxTextElement",
|
|
84
|
-
"mdxTextExpression"
|
|
85
|
-
] : [],
|
|
73
|
+
passThrough: [],
|
|
86
74
|
...remarkRehype
|
|
87
75
|
}
|
|
88
76
|
]
|
|
@@ -90,9 +78,7 @@ async function renderMarkdown(content, opts) {
|
|
|
90
78
|
loadedRehypePlugins.forEach(([plugin, pluginOpts]) => {
|
|
91
79
|
parser.use([[plugin, pluginOpts]]);
|
|
92
80
|
});
|
|
93
|
-
parser.use(
|
|
94
|
-
isAstroFlavoredMd ? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeHeadingIds] : [rehypeHeadingIds, rehypeRaw]
|
|
95
|
-
).use(rehypeStringify, { allowDangerousHtml: true });
|
|
81
|
+
parser.use([rehypeHeadingIds, rehypeRaw]).use(rehypeStringify, { allowDangerousHtml: true });
|
|
96
82
|
let vfile;
|
|
97
83
|
try {
|
|
98
84
|
vfile = await parser.process(input);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Slugger from "github-slugger";
|
|
2
|
-
import { toHtml } from "hast-util-to-html";
|
|
3
2
|
import { visit } from "unist-util-visit";
|
|
4
3
|
const rawNodeTypes = /* @__PURE__ */ new Set(["text", "raw", "mdxTextExpression"]);
|
|
5
4
|
const codeTagNames = /* @__PURE__ */ new Set(["code", "pre"]);
|
|
@@ -19,7 +18,6 @@ function rehypeHeadingIds() {
|
|
|
19
18
|
return;
|
|
20
19
|
const depth = Number.parseInt(level);
|
|
21
20
|
let text = "";
|
|
22
|
-
let isJSX = false;
|
|
23
21
|
visit(node, (child, __, parent) => {
|
|
24
22
|
if (child.type === "element" || parent == null) {
|
|
25
23
|
return;
|
|
@@ -34,23 +32,15 @@ function rehypeHeadingIds() {
|
|
|
34
32
|
text += child.value;
|
|
35
33
|
} else {
|
|
36
34
|
text += child.value.replace(/\{/g, "${");
|
|
37
|
-
isJSX = isJSX || child.value.includes("{");
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
37
|
});
|
|
41
38
|
node.properties = node.properties || {};
|
|
42
39
|
if (typeof node.properties.id !== "string") {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
node.value = `<${node.tagName} id={${node.properties.id}}>${raw}</${node.tagName}>`;
|
|
48
|
-
} else {
|
|
49
|
-
let slug = slugger.slug(text);
|
|
50
|
-
if (slug.endsWith("-"))
|
|
51
|
-
slug = slug.slice(0, -1);
|
|
52
|
-
node.properties.id = slug;
|
|
53
|
-
}
|
|
40
|
+
let slug = slugger.slug(text);
|
|
41
|
+
if (slug.endsWith("-"))
|
|
42
|
+
slug = slug.slice(0, -1);
|
|
43
|
+
node.properties.id = slug;
|
|
54
44
|
}
|
|
55
45
|
headings.push({ depth, slug: node.properties.id, text });
|
|
56
46
|
});
|
|
@@ -3,6 +3,8 @@ import { pathToFileURL } from "url";
|
|
|
3
3
|
function toRemarkContentRelImageError({ contentDir }) {
|
|
4
4
|
return function remarkContentRelImageError() {
|
|
5
5
|
return (tree, vfile) => {
|
|
6
|
+
if (typeof (vfile == null ? void 0 : vfile.path) !== "string")
|
|
7
|
+
return;
|
|
6
8
|
const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href);
|
|
7
9
|
if (!isContentFile)
|
|
8
10
|
return;
|
package/dist/remark-shiki.js
CHANGED
|
@@ -43,8 +43,8 @@ const remarkShiki = async ({ langs = [], theme = "github-dark", wrap = false },
|
|
|
43
43
|
}
|
|
44
44
|
let html = highlighter.codeToHtml(node.value, { lang });
|
|
45
45
|
html = html.replace(
|
|
46
|
-
|
|
47
|
-
`<pre is:raw class="
|
|
46
|
+
/<pre class="(.*?)shiki(.*?)"/,
|
|
47
|
+
`<pre is:raw class="$1astro-code$2${scopedClassName ? " " + scopedClassName : ""}"`
|
|
48
48
|
);
|
|
49
49
|
if (node.lang === "diff") {
|
|
50
50
|
html = html.replace(
|
package/dist/types.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export interface AstroMarkdownOptions {
|
|
|
29
29
|
rehypePlugins?: RehypePlugins;
|
|
30
30
|
remarkRehype?: RemarkRehype;
|
|
31
31
|
gfm?: boolean;
|
|
32
|
+
smartypants?: boolean;
|
|
32
33
|
}
|
|
33
34
|
export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
34
35
|
/** @internal */
|
|
@@ -37,9 +38,6 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
|
37
38
|
$?: {
|
|
38
39
|
scopedClassName: string | null;
|
|
39
40
|
};
|
|
40
|
-
isAstroFlavoredMd?: boolean;
|
|
41
|
-
/** Used to prevent relative image imports from `src/content/` */
|
|
42
|
-
isExperimentalContentCollections?: boolean;
|
|
43
41
|
/** Used to prevent relative image imports from `src/content/` */
|
|
44
42
|
contentDir: URL;
|
|
45
43
|
/** Used for frontmatter injection plugins */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/markdown-remark",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "withastro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,28 +16,21 @@
|
|
|
16
16
|
".": "./dist/index.js",
|
|
17
17
|
"./dist/internal.js": "./dist/internal.js"
|
|
18
18
|
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"astro": "^2.0.0-beta.2"
|
|
21
|
+
},
|
|
19
22
|
"dependencies": {
|
|
20
|
-
"@astrojs/
|
|
21
|
-
"@astrojs/prism": "^1.0.0",
|
|
22
|
-
"acorn": "^8.7.1",
|
|
23
|
-
"acorn-jsx": "^5.3.2",
|
|
23
|
+
"@astrojs/prism": "^2.0.0-beta.0",
|
|
24
24
|
"github-slugger": "^1.4.0",
|
|
25
|
-
"hast-util-to-html": "^8.0.3",
|
|
26
25
|
"import-meta-resolve": "^2.1.0",
|
|
27
|
-
"mdast-util-from-markdown": "^1.2.0",
|
|
28
|
-
"mdast-util-mdx-expression": "^1.2.1",
|
|
29
|
-
"mdast-util-mdx-jsx": "^1.2.0",
|
|
30
|
-
"micromark-extension-mdx-expression": "^1.0.3",
|
|
31
|
-
"micromark-extension-mdx-md": "^1.0.0",
|
|
32
|
-
"micromark-util-combine-extensions": "^1.0.0",
|
|
33
26
|
"rehype-raw": "^6.1.1",
|
|
34
27
|
"rehype-stringify": "^9.0.3",
|
|
35
28
|
"remark-gfm": "^3.0.1",
|
|
36
29
|
"remark-parse": "^10.0.1",
|
|
37
30
|
"remark-rehype": "^10.1.0",
|
|
31
|
+
"remark-smartypants": "^2.0.0",
|
|
38
32
|
"shiki": "^0.11.1",
|
|
39
33
|
"unified": "^10.1.2",
|
|
40
|
-
"unist-util-map": "^3.1.1",
|
|
41
34
|
"unist-util-visit": "^4.1.0",
|
|
42
35
|
"vfile": "^5.3.2"
|
|
43
36
|
},
|
|
@@ -48,9 +41,8 @@
|
|
|
48
41
|
"@types/mdast": "^3.0.10",
|
|
49
42
|
"@types/mocha": "^9.1.1",
|
|
50
43
|
"@types/unist": "^2.0.6",
|
|
51
|
-
"astro-scripts": "0.0.
|
|
44
|
+
"astro-scripts": "0.0.10-beta.0",
|
|
52
45
|
"chai": "^4.3.6",
|
|
53
|
-
"micromark-util-types": "^1.0.2",
|
|
54
46
|
"mocha": "^9.2.2"
|
|
55
47
|
},
|
|
56
48
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -8,24 +8,17 @@ import type {
|
|
|
8
8
|
import { toRemarkInitializeAstroData } from './frontmatter-injection.js';
|
|
9
9
|
import { loadPlugins } from './load-plugins.js';
|
|
10
10
|
import { rehypeHeadingIds } from './rehype-collect-headings.js';
|
|
11
|
-
import rehypeEscape from './rehype-escape.js';
|
|
12
|
-
import rehypeExpressions from './rehype-expressions.js';
|
|
13
|
-
import rehypeIslands from './rehype-islands.js';
|
|
14
|
-
import rehypeJsx from './rehype-jsx.js';
|
|
15
11
|
import toRemarkContentRelImageError from './remark-content-rel-image-error.js';
|
|
16
|
-
import remarkEscape from './remark-escape.js';
|
|
17
|
-
import remarkMarkAndUnravel from './remark-mark-and-unravel.js';
|
|
18
|
-
import remarkMdxish from './remark-mdxish.js';
|
|
19
12
|
import remarkPrism from './remark-prism.js';
|
|
20
13
|
import scopedStyles from './remark-scoped-styles.js';
|
|
21
14
|
import remarkShiki from './remark-shiki.js';
|
|
22
|
-
import remarkUnwrap from './remark-unwrap.js';
|
|
23
15
|
|
|
24
16
|
import rehypeRaw from 'rehype-raw';
|
|
25
17
|
import rehypeStringify from 'rehype-stringify';
|
|
26
18
|
import remarkGfm from 'remark-gfm';
|
|
27
19
|
import markdown from 'remark-parse';
|
|
28
20
|
import markdownToHtml from 'remark-rehype';
|
|
21
|
+
import remarkSmartypants from 'remark-smartypants';
|
|
29
22
|
import { unified } from 'unified';
|
|
30
23
|
import { VFile } from 'vfile';
|
|
31
24
|
|
|
@@ -43,6 +36,7 @@ export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'draft
|
|
|
43
36
|
rehypePlugins: [],
|
|
44
37
|
remarkRehype: {},
|
|
45
38
|
gfm: true,
|
|
39
|
+
smartypants: true,
|
|
46
40
|
};
|
|
47
41
|
|
|
48
42
|
/** Shared utility for rendering markdown */
|
|
@@ -58,8 +52,7 @@ export async function renderMarkdown(
|
|
|
58
52
|
rehypePlugins = markdownConfigDefaults.rehypePlugins,
|
|
59
53
|
remarkRehype = markdownConfigDefaults.remarkRehype,
|
|
60
54
|
gfm = markdownConfigDefaults.gfm,
|
|
61
|
-
|
|
62
|
-
isExperimentalContentCollections = false,
|
|
55
|
+
smartypants = markdownConfigDefaults.smartypants,
|
|
63
56
|
contentDir,
|
|
64
57
|
frontmatter: userFrontmatter = {},
|
|
65
58
|
} = opts;
|
|
@@ -69,12 +62,16 @@ export async function renderMarkdown(
|
|
|
69
62
|
let parser = unified()
|
|
70
63
|
.use(markdown)
|
|
71
64
|
.use(toRemarkInitializeAstroData({ userFrontmatter }))
|
|
72
|
-
.use(
|
|
65
|
+
.use([]);
|
|
73
66
|
|
|
74
67
|
if (gfm) {
|
|
75
68
|
parser.use(remarkGfm);
|
|
76
69
|
}
|
|
77
70
|
|
|
71
|
+
if (smartypants) {
|
|
72
|
+
parser.use(remarkSmartypants);
|
|
73
|
+
}
|
|
74
|
+
|
|
78
75
|
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
|
79
76
|
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
|
|
80
77
|
|
|
@@ -93,24 +90,14 @@ export async function renderMarkdown(
|
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
// Apply later in case user plugins resolve relative image paths
|
|
96
|
-
|
|
97
|
-
parser.use([toRemarkContentRelImageError({ contentDir })]);
|
|
98
|
-
}
|
|
93
|
+
parser.use([toRemarkContentRelImageError({ contentDir })]);
|
|
99
94
|
|
|
100
95
|
parser.use([
|
|
101
96
|
[
|
|
102
97
|
markdownToHtml as any,
|
|
103
98
|
{
|
|
104
99
|
allowDangerousHtml: true,
|
|
105
|
-
passThrough:
|
|
106
|
-
? [
|
|
107
|
-
'raw',
|
|
108
|
-
'mdxFlowExpression',
|
|
109
|
-
'mdxJsxFlowElement',
|
|
110
|
-
'mdxJsxTextElement',
|
|
111
|
-
'mdxTextExpression',
|
|
112
|
-
]
|
|
113
|
-
: [],
|
|
100
|
+
passThrough: [],
|
|
114
101
|
...remarkRehype,
|
|
115
102
|
},
|
|
116
103
|
],
|
|
@@ -120,13 +107,7 @@ export async function renderMarkdown(
|
|
|
120
107
|
parser.use([[plugin, pluginOpts]]);
|
|
121
108
|
});
|
|
122
109
|
|
|
123
|
-
parser
|
|
124
|
-
.use(
|
|
125
|
-
isAstroFlavoredMd
|
|
126
|
-
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeHeadingIds]
|
|
127
|
-
: [rehypeHeadingIds, rehypeRaw]
|
|
128
|
-
)
|
|
129
|
-
.use(rehypeStringify, { allowDangerousHtml: true });
|
|
110
|
+
parser.use([rehypeHeadingIds, rehypeRaw]).use(rehypeStringify, { allowDangerousHtml: true });
|
|
130
111
|
|
|
131
112
|
let vfile: MarkdownVFile;
|
|
132
113
|
try {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Slugger from 'github-slugger';
|
|
2
|
-
import { toHtml } from 'hast-util-to-html';
|
|
3
2
|
import { visit } from 'unist-util-visit';
|
|
4
3
|
|
|
5
4
|
import type { MarkdownHeading, MarkdownVFile, RehypePlugin } from './types.js';
|
|
@@ -21,7 +20,6 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
|
|
|
21
20
|
const depth = Number.parseInt(level);
|
|
22
21
|
|
|
23
22
|
let text = '';
|
|
24
|
-
let isJSX = false;
|
|
25
23
|
visit(node, (child, __, parent) => {
|
|
26
24
|
if (child.type === 'element' || parent == null) {
|
|
27
25
|
return;
|
|
@@ -36,31 +34,17 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
|
|
|
36
34
|
text += child.value;
|
|
37
35
|
} else {
|
|
38
36
|
text += child.value.replace(/\{/g, '${');
|
|
39
|
-
isJSX = isJSX || child.value.includes('{');
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
});
|
|
43
40
|
|
|
44
41
|
node.properties = node.properties || {};
|
|
45
42
|
if (typeof node.properties.id !== 'string') {
|
|
46
|
-
|
|
47
|
-
// HACK: serialized JSX from internal plugins, ignore these for slug
|
|
48
|
-
const raw = toHtml(node.children, { allowDangerousHtml: true })
|
|
49
|
-
.replace(/\n(<)/g, '<')
|
|
50
|
-
.replace(/(>)\n/g, '>');
|
|
51
|
-
// HACK: for ids that have JSX content, use $$slug helper to generate slug at runtime
|
|
52
|
-
node.properties.id = `$$slug(\`${text}\`)`;
|
|
53
|
-
(node as any).type = 'raw';
|
|
54
|
-
(
|
|
55
|
-
node as any
|
|
56
|
-
).value = `<${node.tagName} id={${node.properties.id}}>${raw}</${node.tagName}>`;
|
|
57
|
-
} else {
|
|
58
|
-
let slug = slugger.slug(text);
|
|
43
|
+
let slug = slugger.slug(text);
|
|
59
44
|
|
|
60
|
-
|
|
45
|
+
if (slug.endsWith('-')) slug = slug.slice(0, -1);
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
}
|
|
47
|
+
node.properties.id = slug;
|
|
64
48
|
}
|
|
65
49
|
|
|
66
50
|
headings.push({ depth, slug: node.properties.id, text });
|
|
@@ -10,6 +10,8 @@ import type { VFile } from 'vfile';
|
|
|
10
10
|
export default function toRemarkContentRelImageError({ contentDir }: { contentDir: URL }) {
|
|
11
11
|
return function remarkContentRelImageError() {
|
|
12
12
|
return (tree: any, vfile: VFile) => {
|
|
13
|
+
if (typeof vfile?.path !== 'string') return;
|
|
14
|
+
|
|
13
15
|
const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href);
|
|
14
16
|
if (!isContentFile) return;
|
|
15
17
|
|
package/src/remark-shiki.ts
CHANGED
|
@@ -70,8 +70,8 @@ const remarkShiki = async (
|
|
|
70
70
|
|
|
71
71
|
// Replace "shiki" class naming with "astro" and add "is:raw".
|
|
72
72
|
html = html.replace(
|
|
73
|
-
|
|
74
|
-
`<pre is:raw class="
|
|
73
|
+
/<pre class="(.*?)shiki(.*?)"/,
|
|
74
|
+
`<pre is:raw class="$1astro-code$2${scopedClassName ? ' ' + scopedClassName : ''}"`
|
|
75
75
|
);
|
|
76
76
|
// Add "user-select: none;" for "+"/"-" diff symbols
|
|
77
77
|
if (node.lang === 'diff') {
|
package/src/types.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface AstroMarkdownOptions {
|
|
|
48
48
|
rehypePlugins?: RehypePlugins;
|
|
49
49
|
remarkRehype?: RemarkRehype;
|
|
50
50
|
gfm?: boolean;
|
|
51
|
+
smartypants?: boolean;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
@@ -57,9 +58,6 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
|
57
58
|
$?: {
|
|
58
59
|
scopedClassName: string | null;
|
|
59
60
|
};
|
|
60
|
-
isAstroFlavoredMd?: boolean;
|
|
61
|
-
/** Used to prevent relative image imports from `src/content/` */
|
|
62
|
-
isExperimentalContentCollections?: boolean;
|
|
63
61
|
/** Used to prevent relative image imports from `src/content/` */
|
|
64
62
|
contentDir: URL;
|
|
65
63
|
/** Used for frontmatter injection plugins */
|
package/test/autolinking.test.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { renderMarkdown } from '../dist/index.js';
|
|
2
2
|
import chai from 'chai';
|
|
3
|
+
import { mockRenderMarkdownParams } from './test-utils.js';
|
|
3
4
|
|
|
4
5
|
describe('autolinking', () => {
|
|
5
6
|
describe('plain md', () => {
|
|
6
7
|
it('autolinks URLs starting with a protocol in plain text', async () => {
|
|
7
|
-
const { code } = await renderMarkdown(
|
|
8
|
+
const { code } = await renderMarkdown(
|
|
9
|
+
`See https://example.com for more.`,
|
|
10
|
+
mockRenderMarkdownParams
|
|
11
|
+
);
|
|
8
12
|
|
|
9
13
|
chai
|
|
10
14
|
.expect(code.replace(/\n/g, ''))
|
|
@@ -12,7 +16,10 @@ describe('autolinking', () => {
|
|
|
12
16
|
});
|
|
13
17
|
|
|
14
18
|
it('autolinks URLs starting with "www." in plain text', async () => {
|
|
15
|
-
const { code } = await renderMarkdown(
|
|
19
|
+
const { code } = await renderMarkdown(
|
|
20
|
+
`See www.example.com for more.`,
|
|
21
|
+
mockRenderMarkdownParams
|
|
22
|
+
);
|
|
16
23
|
|
|
17
24
|
chai
|
|
18
25
|
.expect(code.trim())
|
|
@@ -22,7 +29,7 @@ describe('autolinking', () => {
|
|
|
22
29
|
it('does not autolink URLs in code blocks', async () => {
|
|
23
30
|
const { code } = await renderMarkdown(
|
|
24
31
|
'See `https://example.com` or `www.example.com` for more.',
|
|
25
|
-
|
|
32
|
+
mockRenderMarkdownParams
|
|
26
33
|
);
|
|
27
34
|
|
|
28
35
|
chai
|
|
@@ -33,76 +40,4 @@ describe('autolinking', () => {
|
|
|
33
40
|
);
|
|
34
41
|
});
|
|
35
42
|
});
|
|
36
|
-
|
|
37
|
-
describe('astro-flavored md', () => {
|
|
38
|
-
const renderAstroMd = (text) => renderMarkdown(text, { isAstroFlavoredMd: true });
|
|
39
|
-
|
|
40
|
-
it('does not autolink URLs in code blocks', async () => {
|
|
41
|
-
const { code } = await renderAstroMd(
|
|
42
|
-
'See `https://example.com` or `www.example.com` for more.',
|
|
43
|
-
{}
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
chai
|
|
47
|
-
.expect(code.trim())
|
|
48
|
-
.to.equal(
|
|
49
|
-
`<p>See <code is:raw>https://example.com</code> or ` +
|
|
50
|
-
`<code is:raw>www.example.com</code> for more.</p>`
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('does not autolink URLs in fenced code blocks', async () => {
|
|
55
|
-
const { code } = await renderAstroMd(
|
|
56
|
-
'Example:\n```\nGo to https://example.com or www.example.com now.\n```'
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
chai
|
|
60
|
-
.expect(code)
|
|
61
|
-
.to.contain(`<pre is:raw`)
|
|
62
|
-
.to.contain(`Go to https://example.com or www.example.com now.`);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('does not autolink URLs starting with a protocol when nested inside links', async () => {
|
|
66
|
-
const { code } = await renderAstroMd(
|
|
67
|
-
`See [http://example.com](http://example.com) or ` +
|
|
68
|
-
`<a test href="https://example.com">https://example.com</a>`
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
chai
|
|
72
|
-
.expect(code.replace(/\n/g, ''))
|
|
73
|
-
.to.equal(
|
|
74
|
-
`<p>See <a href="http://example.com">http://example.com</a> or ` +
|
|
75
|
-
`<a test href="https://example.com">https://example.com</a></p>`
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('does not autolink URLs starting with "www." when nested inside links', async () => {
|
|
80
|
-
const { code } = await renderAstroMd(
|
|
81
|
-
`See [www.example.com](https://www.example.com) or ` +
|
|
82
|
-
`<a test href="https://www.example.com">www.example.com</a>`
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
chai
|
|
86
|
-
.expect(code.replace(/\n/g, ''))
|
|
87
|
-
.to.equal(
|
|
88
|
-
`<p>See <a href="https://www.example.com">www.example.com</a> or ` +
|
|
89
|
-
`<a test href="https://www.example.com">www.example.com</a></p>`
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('does not autolink URLs when nested several layers deep inside links', async () => {
|
|
94
|
-
const { code } = await renderAstroMd(
|
|
95
|
-
`<a href="https://www.example.com">**Visit _our www.example.com or ` +
|
|
96
|
-
`http://localhost pages_ for more!**</a>`
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
chai
|
|
100
|
-
.expect(code.replace(/\n/g, ''))
|
|
101
|
-
.to.equal(
|
|
102
|
-
`<a href="https://www.example.com"><strong>` +
|
|
103
|
-
`Visit <em>our www.example.com or http://localhost pages</em> for more!` +
|
|
104
|
-
`</strong></a>`
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
43
|
});
|
package/test/entities.test.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { renderMarkdown } from '../dist/index.js';
|
|
2
2
|
import { expect } from 'chai';
|
|
3
|
+
import { mockRenderMarkdownParams } from './test-utils.js';
|
|
3
4
|
|
|
4
5
|
describe('entities', () => {
|
|
5
6
|
it('should not unescape entities in regular Markdown', async () => {
|
|
6
|
-
const { code } = await renderMarkdown(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const { code } = await renderMarkdown(
|
|
8
|
+
`<i>This should NOT be italic</i>`,
|
|
9
|
+
mockRenderMarkdownParams
|
|
10
|
+
);
|
|
9
11
|
|
|
10
12
|
expect(code).to.equal(`<p><i>This should NOT be italic</i></p>`);
|
|
11
13
|
});
|
|
12
|
-
|
|
13
|
-
it('should not escape entities in code blocks twice in Astro-flavored markdown', async () => {
|
|
14
|
-
const { code } = await renderMarkdown(`\`\`\`astro\n<h1>{x && x.name || ''}!</h1>\n\`\`\``, {
|
|
15
|
-
isAstroFlavoredMd: true,
|
|
16
|
-
syntaxHighlight: false,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
expect(code).to.equal(
|
|
20
|
-
`<pre is:raw><code class="language-astro"><h1>{x && x.name || ''}!</h1>\n</code></pre>`
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
14
|
});
|
package/test/plugins.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { renderMarkdown } from '../dist/index.js';
|
|
2
|
+
import { mockRenderMarkdownParams } from './test-utils.js';
|
|
2
3
|
import chai from 'chai';
|
|
3
4
|
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -8,6 +9,7 @@ describe('plugins', () => {
|
|
|
8
9
|
it('should be able to get file path when passing fileURL', async () => {
|
|
9
10
|
let context;
|
|
10
11
|
await renderMarkdown(`test`, {
|
|
12
|
+
...mockRenderMarkdownParams,
|
|
11
13
|
fileURL: new URL('virtual.md', import.meta.url),
|
|
12
14
|
remarkPlugins: [
|
|
13
15
|
function () {
|