@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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/CHANGELOG.md +63 -0
  3. package/dist/index.js +11 -25
  4. package/dist/rehype-collect-headings.js +4 -14
  5. package/dist/remark-content-rel-image-error.js +2 -0
  6. package/dist/remark-shiki.js +2 -2
  7. package/dist/types.d.ts +1 -3
  8. package/package.json +7 -15
  9. package/src/index.ts +11 -30
  10. package/src/rehype-collect-headings.ts +3 -19
  11. package/src/remark-content-rel-image-error.ts +2 -0
  12. package/src/remark-shiki.ts +2 -2
  13. package/src/types.ts +1 -3
  14. package/test/autolinking.test.js +10 -75
  15. package/test/entities.test.js +5 -14
  16. package/test/plugins.test.js +2 -0
  17. package/test/test-utils.js +3 -0
  18. package/dist/mdast-util-mdxish.d.ts +0 -2
  19. package/dist/mdast-util-mdxish.js +0 -14
  20. package/dist/mdxjs.d.ts +0 -3
  21. package/dist/mdxjs.js +0 -20
  22. package/dist/rehype-escape.d.ts +0 -2
  23. package/dist/rehype-escape.js +0 -21
  24. package/dist/rehype-expressions.d.ts +0 -1
  25. package/dist/rehype-expressions.js +0 -20
  26. package/dist/rehype-islands.d.ts +0 -1
  27. package/dist/rehype-islands.js +0 -27
  28. package/dist/rehype-jsx.d.ts +0 -2
  29. package/dist/rehype-jsx.js +0 -51
  30. package/dist/remark-escape.d.ts +0 -1
  31. package/dist/remark-escape.js +0 -13
  32. package/dist/remark-mark-and-unravel.d.ts +0 -17
  33. package/dist/remark-mark-and-unravel.js +0 -45
  34. package/dist/remark-mdxish.d.ts +0 -1
  35. package/dist/remark-mdxish.js +0 -53
  36. package/dist/remark-unwrap.d.ts +0 -1
  37. package/dist/remark-unwrap.js +0 -31
  38. package/src/mdast-util-mdxish.ts +0 -12
  39. package/src/mdxjs.ts +0 -27
  40. package/src/rehype-escape.ts +0 -22
  41. package/src/rehype-expressions.ts +0 -18
  42. package/src/rehype-islands.ts +0 -43
  43. package/src/rehype-jsx.ts +0 -65
  44. package/src/remark-escape.ts +0 -15
  45. package/src/remark-mark-and-unravel.ts +0 -72
  46. package/src/remark-mdxish.ts +0 -61
  47. package/src/remark-unwrap.ts +0 -44
  48. package/test/components.test.js +0 -78
  49. package/test/expressions.test.js +0 -125
  50. package/test/strictness.test.js +0 -98
@@ -1,14 +0,0 @@
1
- import { mdxExpressionFromMarkdown, mdxExpressionToMarkdown } from "mdast-util-mdx-expression";
2
- import { mdxJsxFromMarkdown, mdxJsxToMarkdown } from "mdast-util-mdx-jsx";
3
- function mdxFromMarkdown() {
4
- return [mdxExpressionFromMarkdown, mdxJsxFromMarkdown];
5
- }
6
- function mdxToMarkdown() {
7
- return {
8
- extensions: [mdxExpressionToMarkdown, mdxJsxToMarkdown]
9
- };
10
- }
11
- export {
12
- mdxFromMarkdown,
13
- mdxToMarkdown
14
- };
package/dist/mdxjs.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import type { Options } from 'micromark-extension-mdx-expression';
2
- import type { Extension } from 'micromark-util-types';
3
- export declare function mdxjs(options: Options): Extension;
package/dist/mdxjs.js DELETED
@@ -1,20 +0,0 @@
1
- import { mdxJsx } from "@astrojs/micromark-extension-mdx-jsx";
2
- import { Parser } from "acorn";
3
- import acornJsx from "acorn-jsx";
4
- import { mdxExpression } from "micromark-extension-mdx-expression";
5
- import { mdxMd } from "micromark-extension-mdx-md";
6
- import { combineExtensions } from "micromark-util-combine-extensions";
7
- function mdxjs(options) {
8
- const settings = Object.assign(
9
- {
10
- acorn: Parser.extend(acornJsx()),
11
- acornOptions: { ecmaVersion: 2020, sourceType: "module" },
12
- addResult: true
13
- },
14
- options
15
- );
16
- return combineExtensions([mdxExpression(settings), mdxJsx(settings), mdxMd]);
17
- }
18
- export {
19
- mdxjs
20
- };
@@ -1,2 +0,0 @@
1
- export declare function escapeEntities(value: string): string;
2
- export default function rehypeEscape(): any;
@@ -1,21 +0,0 @@
1
- import { SKIP, visit } from "unist-util-visit";
2
- function escapeEntities(value) {
3
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4
- }
5
- function rehypeEscape() {
6
- return function(node) {
7
- return visit(node, "element", (el) => {
8
- if (el.tagName === "code" || el.tagName === "pre") {
9
- el.properties["is:raw"] = true;
10
- visit(el, "raw", (raw) => {
11
- raw.value = escapeEntities(raw.value);
12
- });
13
- return SKIP;
14
- }
15
- });
16
- };
17
- }
18
- export {
19
- rehypeEscape as default,
20
- escapeEntities
21
- };
@@ -1 +0,0 @@
1
- export default function rehypeExpressions(): any;
@@ -1,20 +0,0 @@
1
- import { map } from "unist-util-map";
2
- function rehypeExpressions() {
3
- return function(node) {
4
- return map(node, (child) => {
5
- if (child.type === "text") {
6
- return { ...child, type: "raw" };
7
- }
8
- if (child.type === "mdxTextExpression") {
9
- return { type: "raw", value: `{${child.value}}` };
10
- }
11
- if (child.type === "mdxFlowExpression") {
12
- return { type: "raw", value: `{${child.value}}` };
13
- }
14
- return child;
15
- });
16
- };
17
- }
18
- export {
19
- rehypeExpressions as default
20
- };
@@ -1 +0,0 @@
1
- export default function rehypeIslands(): any;
@@ -1,27 +0,0 @@
1
- import { SKIP, visit as _visit } from "unist-util-visit";
2
- const visit = _visit;
3
- function rehypeIslands() {
4
- return function(node) {
5
- return visit(node, "element", (el) => {
6
- if (el.tagName == "astro-island") {
7
- visit(el, "text", (child, index, parent) => {
8
- if (child.type === "text") {
9
- if (parent && child.value.indexOf("<!--") > -1 && index != null) {
10
- parent.children.splice(index, 1, {
11
- ...child,
12
- type: "comment",
13
- value: child.value.replace("<!--", "").replace("-->", "").trim()
14
- });
15
- return [SKIP, index];
16
- }
17
- child.value = child.value.replace(/\n+/g, "");
18
- return child;
19
- }
20
- });
21
- }
22
- });
23
- };
24
- }
25
- export {
26
- rehypeIslands as default
27
- };
@@ -1,2 +0,0 @@
1
- import type { RehypePlugin } from './types.js';
2
- export default function rehypeJsx(): ReturnType<RehypePlugin>;
@@ -1,51 +0,0 @@
1
- import { visit } from "unist-util-visit";
2
- const MDX_ELEMENTS = ["mdxJsxFlowElement", "mdxJsxTextElement"];
3
- function rehypeJsx() {
4
- return function(tree) {
5
- visit(tree, MDX_ELEMENTS, (node, index, parent) => {
6
- if (index === null || !Boolean(parent))
7
- return;
8
- const attrs = node.attributes.reduce((acc, entry) => {
9
- let attr = entry.value;
10
- if (attr && typeof attr === "object") {
11
- attr = `{${attr.value}}`;
12
- } else if (attr && entry.type === "mdxJsxExpressionAttribute") {
13
- attr = `{${attr}}`;
14
- } else if (attr === null) {
15
- attr = "";
16
- } else if (typeof attr === "string") {
17
- attr = `"${attr}"`;
18
- }
19
- if (!entry.name) {
20
- return acc + ` ${attr}`;
21
- }
22
- return acc + ` ${entry.name}${attr ? "=" : ""}${attr}`;
23
- }, "");
24
- if (node.children.length === 0) {
25
- node.type = "raw";
26
- node.value = `<${node.name}${attrs} />`;
27
- return;
28
- }
29
- if (node.name === "a") {
30
- visit(node, "element", (el, elIndex, elParent) => {
31
- const isAutolink = el.tagName === "a" && el.children.length === 1 && el.children[0].type === "text" && el.children[0].value.match(/^(https?:\/\/|www\.)/i);
32
- if (isAutolink) {
33
- elParent.children.splice(elIndex, 1, el.children[0]);
34
- }
35
- });
36
- }
37
- const openingTag = {
38
- type: "raw",
39
- value: `<${node.name}${attrs}>`
40
- };
41
- const closingTag = {
42
- type: "raw",
43
- value: `</${node.name}>`
44
- };
45
- parent.children.splice(index, 1, openingTag, ...node.children, closingTag);
46
- });
47
- };
48
- }
49
- export {
50
- rehypeJsx as default
51
- };
@@ -1 +0,0 @@
1
- export default function remarkEscape(): (tree: any) => void;
@@ -1,13 +0,0 @@
1
- import { visit } from "unist-util-visit";
2
- function remarkEscape() {
3
- return (tree) => {
4
- visit(tree, "code", removeCommentWrapper);
5
- visit(tree, "inlineCode", removeCommentWrapper);
6
- };
7
- function removeCommentWrapper(node) {
8
- node.value = node.value.replace(/{\/\*<!--/gs, "<!--").replace(/-->\*\/}/gs, "-->");
9
- }
10
- }
11
- export {
12
- remarkEscape as default
13
- };
@@ -1,17 +0,0 @@
1
- /**
2
- * @typedef {import('mdast').Root} Root
3
- * @typedef {import('mdast').Content} Content
4
- * @typedef {Root|Content} Node
5
- * @typedef {Extract<Node, import('unist').Parent>} Parent
6
- *
7
- * @typedef {import('remark-mdx')} DoNotTouchAsThisImportItIncludesMdxInTree
8
- */
9
- /**
10
- * A tiny plugin that unravels `<p><h1>x</h1></p>` but also
11
- * `<p><Component /></p>` (so it has no knowledge of “HTML”).
12
- * It also marks JSX as being explicitly JSX, so when a user passes a `h1`
13
- * component, it is used for `# heading` but not for `<h1>heading</h1>`.
14
- *
15
- * @type {import('unified').Plugin<Array<void>, Root>}
16
- */
17
- export default function remarkMarkAndUnravel(): (tree: any) => void;
@@ -1,45 +0,0 @@
1
- import { visit } from "unist-util-visit";
2
- function remarkMarkAndUnravel() {
3
- return (tree) => {
4
- visit(tree, (node, index, parent_) => {
5
- const parent = parent_;
6
- let offset = -1;
7
- let all = true;
8
- let oneOrMore;
9
- if (parent && typeof index === "number" && node.type === "paragraph") {
10
- const children = node.children;
11
- while (++offset < children.length) {
12
- const child = children[offset];
13
- if (child.type === "mdxJsxTextElement" || child.type === "mdxTextExpression") {
14
- oneOrMore = true;
15
- } else if (child.type === "text" && /^[\t\r\n ]+$/.test(String(child.value))) {
16
- } else {
17
- all = false;
18
- break;
19
- }
20
- }
21
- if (all && oneOrMore) {
22
- offset = -1;
23
- while (++offset < children.length) {
24
- const child = children[offset];
25
- if (child.type === "mdxJsxTextElement") {
26
- child.type = "mdxJsxFlowElement";
27
- }
28
- if (child.type === "mdxTextExpression") {
29
- child.type = "mdxFlowExpression";
30
- }
31
- }
32
- parent.children.splice(index, 1, ...children);
33
- return index;
34
- }
35
- }
36
- if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
37
- const data = node.data || (node.data = {});
38
- data._mdxExplicitJsx = true;
39
- }
40
- });
41
- };
42
- }
43
- export {
44
- remarkMarkAndUnravel as default
45
- };
@@ -1 +0,0 @@
1
- export default function remarkMdxish(this: any): void;
@@ -1,53 +0,0 @@
1
- import { mdxFromMarkdown, mdxToMarkdown } from "./mdast-util-mdxish.js";
2
- import { mdxjs } from "./mdxjs.js";
3
- const extMdxJs = mdxjs({});
4
- const extMdxFromMarkdown = makeFromMarkdownLessStrict(mdxFromMarkdown());
5
- const extMdxToMarkdown = mdxToMarkdown();
6
- function remarkMdxish() {
7
- const data = this.data();
8
- add("micromarkExtensions", extMdxJs);
9
- add("fromMarkdownExtensions", extMdxFromMarkdown);
10
- add("toMarkdownExtensions", extMdxToMarkdown);
11
- function add(field, value) {
12
- const list = data[field] ? data[field] : data[field] = [];
13
- list.push(value);
14
- }
15
- }
16
- function makeFromMarkdownLessStrict(extensions) {
17
- extensions.forEach((extension) => {
18
- ["mdxJsxFlowTag", "mdxJsxTextTag"].forEach((exitHandler) => {
19
- if (!extension.exit || !extension.exit[exitHandler])
20
- return;
21
- extension.exit[exitHandler] = chainHandlers(fixSelfClosing, extension.exit[exitHandler]);
22
- });
23
- });
24
- return extensions;
25
- }
26
- const selfClosingTags = /* @__PURE__ */ new Set([
27
- "area",
28
- "base",
29
- "br",
30
- "col",
31
- "embed",
32
- "hr",
33
- "img",
34
- "input",
35
- "link",
36
- "meta",
37
- "source",
38
- "track",
39
- "wbr"
40
- ]);
41
- function fixSelfClosing() {
42
- const tag = this.getData("mdxJsxTag");
43
- if (tag.name && selfClosingTags.has(tag.name))
44
- tag.selfClosing = true;
45
- }
46
- function chainHandlers(...handlers) {
47
- return function handlerChain(token) {
48
- handlers.forEach((handler) => handler.call(this, token));
49
- };
50
- }
51
- export {
52
- remarkMdxish as default
53
- };
@@ -1 +0,0 @@
1
- export default function remarkUnwrap(): (tree: any) => void;
@@ -1,31 +0,0 @@
1
- import { SKIP, visit as _visit } from "unist-util-visit";
2
- const visit = _visit;
3
- function remarkUnwrap() {
4
- const astroRootNodes = /* @__PURE__ */ new Set();
5
- let insideAstroRoot = false;
6
- return (tree) => {
7
- insideAstroRoot = false;
8
- astroRootNodes.clear();
9
- visit(tree, "html", (node) => {
10
- if (node.value.indexOf("<astro-island") > -1 && !insideAstroRoot) {
11
- insideAstroRoot = true;
12
- }
13
- if (node.value.indexOf("</astro-island") > -1 && insideAstroRoot) {
14
- insideAstroRoot = false;
15
- }
16
- astroRootNodes.add(node);
17
- });
18
- visit(tree, "paragraph", (node, index, parent) => {
19
- if (parent && typeof index === "number" && containsAstroRootNode(node)) {
20
- parent.children.splice(index, 1, ...node.children);
21
- return [SKIP, index];
22
- }
23
- });
24
- };
25
- function containsAstroRootNode(node) {
26
- return node.children.map((child) => astroRootNodes.has(child)).reduce((all, v) => all ? all : v, false);
27
- }
28
- }
29
- export {
30
- remarkUnwrap as default
31
- };
@@ -1,12 +0,0 @@
1
- import { mdxExpressionFromMarkdown, mdxExpressionToMarkdown } from 'mdast-util-mdx-expression';
2
- import { mdxJsxFromMarkdown, mdxJsxToMarkdown } from 'mdast-util-mdx-jsx';
3
-
4
- export function mdxFromMarkdown(): any {
5
- return [mdxExpressionFromMarkdown, mdxJsxFromMarkdown];
6
- }
7
-
8
- export function mdxToMarkdown(): any {
9
- return {
10
- extensions: [mdxExpressionToMarkdown, mdxJsxToMarkdown],
11
- };
12
- }
package/src/mdxjs.ts DELETED
@@ -1,27 +0,0 @@
1
- // Note: The code in this file is based on `micromark-extension-mdxjs`
2
- // and was adapted to use our fork `@astrojs/micromark-extension-mdx-jsx`
3
- // instead of `micromark-extension-mdx-jsx` to allow some extended syntax.
4
- // See `@astrojs/micromark-extension-mdx-jsx` on NPM for more details.
5
- // Also, support for ESM imports & exports in Markdown content was removed.
6
-
7
- import { mdxJsx } from '@astrojs/micromark-extension-mdx-jsx';
8
- import { Parser } from 'acorn';
9
- import acornJsx from 'acorn-jsx';
10
- import type { Options } from 'micromark-extension-mdx-expression';
11
- import { mdxExpression } from 'micromark-extension-mdx-expression';
12
- import { mdxMd } from 'micromark-extension-mdx-md';
13
- import { combineExtensions } from 'micromark-util-combine-extensions';
14
- import type { Extension } from 'micromark-util-types';
15
-
16
- export function mdxjs(options: Options): Extension {
17
- const settings: any = Object.assign(
18
- {
19
- acorn: Parser.extend(acornJsx()),
20
- acornOptions: { ecmaVersion: 2020, sourceType: 'module' },
21
- addResult: true,
22
- },
23
- options
24
- );
25
-
26
- return combineExtensions([mdxExpression(settings), mdxJsx(settings), mdxMd]);
27
- }
@@ -1,22 +0,0 @@
1
- import { SKIP, visit } from 'unist-util-visit';
2
-
3
- export function escapeEntities(value: string): string {
4
- return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
5
- }
6
-
7
- export default function rehypeEscape(): any {
8
- return function (node: any): any {
9
- return visit(node, 'element', (el) => {
10
- if (el.tagName === 'code' || el.tagName === 'pre') {
11
- el.properties['is:raw'] = true;
12
- // Visit all raw children and escape HTML tags to prevent Markdown code
13
- // like "This is a `<script>` tag" from actually opening a script tag
14
- visit(el, 'raw', (raw) => {
15
- raw.value = escapeEntities(raw.value);
16
- });
17
- // Do not visit children to prevent double escaping
18
- return SKIP;
19
- }
20
- });
21
- };
22
- }
@@ -1,18 +0,0 @@
1
- import { map } from 'unist-util-map';
2
-
3
- export default function rehypeExpressions(): any {
4
- return function (node: any): any {
5
- return map(node, (child) => {
6
- if (child.type === 'text') {
7
- return { ...child, type: 'raw' };
8
- }
9
- if (child.type === 'mdxTextExpression') {
10
- return { type: 'raw', value: `{${(child as any).value}}` };
11
- }
12
- if (child.type === 'mdxFlowExpression') {
13
- return { type: 'raw', value: `{${(child as any).value}}` };
14
- }
15
- return child;
16
- });
17
- };
18
- }
@@ -1,43 +0,0 @@
1
- import { SKIP, visit as _visit } from 'unist-util-visit';
2
-
3
- // This is a workaround.
4
- // It fixes a compatibility issue between different, incompatible ASTs given by plugins to Unist
5
- const visit = _visit as (
6
- node: any,
7
- type: string,
8
- callback?: (node: any, index: number, parent: any) => any
9
- ) => any;
10
-
11
- // This fixes some confusing bugs coming from somewhere inside of our Markdown pipeline.
12
- // `unist`/`remark`/`rehype` (not sure) often generate malformed HTML inside of <astro-island>
13
- // For hydration to work properly, frameworks need the DOM to be the exact same on server/client.
14
- // This reverts some "helpful corrections" that are applied to our perfectly valid HTML!
15
- export default function rehypeIslands(): any {
16
- return function (node: any): any {
17
- return visit(node, 'element', (el) => {
18
- // Bugs only happen inside of <astro-island> islands
19
- if (el.tagName == 'astro-island') {
20
- visit(el, 'text', (child, index, parent) => {
21
- if (child.type === 'text') {
22
- // Sometimes comments can be trapped as text, which causes them to be escaped
23
- // This casts them back to real HTML comments
24
- if (parent && child.value.indexOf('<!--') > -1 && index != null) {
25
- parent.children.splice(index, 1, {
26
- ...child,
27
- type: 'comment',
28
- value: child.value.replace('<!--', '').replace('-->', '').trim(),
29
- });
30
- return [SKIP, index];
31
- }
32
- // For some reason `rehype` likes to inject extra linebreaks,
33
- // but React and Vue throw hydration errors when they see these!
34
- // This removes any extra linebreaks, which is fine because
35
- // framework compilers don't preserve them anyway
36
- child.value = child.value.replace(/\n+/g, '');
37
- return child;
38
- }
39
- });
40
- }
41
- });
42
- };
43
- }
package/src/rehype-jsx.ts DELETED
@@ -1,65 +0,0 @@
1
- import { visit } from 'unist-util-visit';
2
- import type { RehypePlugin } from './types.js';
3
-
4
- const MDX_ELEMENTS = ['mdxJsxFlowElement', 'mdxJsxTextElement'];
5
-
6
- export default function rehypeJsx(): ReturnType<RehypePlugin> {
7
- return function (tree) {
8
- visit(tree, MDX_ELEMENTS, (node: any, index: number | null, parent: any) => {
9
- if (index === null || !Boolean(parent)) return;
10
-
11
- const attrs = node.attributes.reduce((acc: any[], entry: any) => {
12
- let attr = entry.value;
13
- if (attr && typeof attr === 'object') {
14
- attr = `{${attr.value}}`;
15
- } else if (attr && entry.type === 'mdxJsxExpressionAttribute') {
16
- attr = `{${attr}}`;
17
- } else if (attr === null) {
18
- attr = '';
19
- } else if (typeof attr === 'string') {
20
- attr = `"${attr}"`;
21
- }
22
- if (!entry.name) {
23
- return acc + ` ${attr}`;
24
- }
25
- return acc + ` ${entry.name}${attr ? '=' : ''}${attr}`;
26
- }, '');
27
-
28
- if (node.children.length === 0) {
29
- node.type = 'raw';
30
- node.value = `<${node.name}${attrs} />`;
31
- return;
32
- }
33
-
34
- // If the current node is a JSX <a> element, remove autolinks from its children
35
- // to prevent Markdown code like `<a href="/">**Go to www.example.com now!**</a>`
36
- // from creating a nested link to `www.example.com`
37
- if (node.name === 'a') {
38
- visit(node, 'element', (el, elIndex, elParent) => {
39
- const isAutolink =
40
- el.tagName === 'a' &&
41
- el.children.length === 1 &&
42
- el.children[0].type === 'text' &&
43
- el.children[0].value.match(/^(https?:\/\/|www\.)/i);
44
-
45
- // If we found an autolink, remove it by replacing it with its text-only child
46
- if (isAutolink) {
47
- elParent.children.splice(elIndex, 1, el.children[0]);
48
- }
49
- });
50
- }
51
-
52
- // Replace the current node with its children
53
- // wrapped by raw opening and closing tags
54
- const openingTag = {
55
- type: 'raw',
56
- value: `<${node.name}${attrs}>`,
57
- };
58
- const closingTag = {
59
- type: 'raw',
60
- value: `</${node.name}>`,
61
- };
62
- parent.children.splice(index, 1, openingTag, ...node.children, closingTag);
63
- });
64
- };
65
- }
@@ -1,15 +0,0 @@
1
- import type { Literal } from 'unist';
2
- import { visit } from 'unist-util-visit';
3
-
4
- // In code blocks, this removes the JS comment wrapper added to
5
- // HTML comments by vite-plugin-markdown-legacy.
6
- export default function remarkEscape() {
7
- return (tree: any) => {
8
- visit(tree, 'code', removeCommentWrapper);
9
- visit(tree, 'inlineCode', removeCommentWrapper);
10
- };
11
-
12
- function removeCommentWrapper(node: Literal<string>) {
13
- node.value = node.value.replace(/{\/\*<!--/gs, '<!--').replace(/-->\*\/}/gs, '-->');
14
- }
15
- }
@@ -1,72 +0,0 @@
1
- // https://github.com/mdx-js/mdx/blob/main/packages/mdx/lib/plugin/remark-mark-and-unravel.js
2
- /**
3
- * @typedef {import('mdast').Root} Root
4
- * @typedef {import('mdast').Content} Content
5
- * @typedef {Root|Content} Node
6
- * @typedef {Extract<Node, import('unist').Parent>} Parent
7
- *
8
- * @typedef {import('remark-mdx')} DoNotTouchAsThisImportItIncludesMdxInTree
9
- */
10
-
11
- import { visit } from 'unist-util-visit';
12
-
13
- /**
14
- * A tiny plugin that unravels `<p><h1>x</h1></p>` but also
15
- * `<p><Component /></p>` (so it has no knowledge of “HTML”).
16
- * It also marks JSX as being explicitly JSX, so when a user passes a `h1`
17
- * component, it is used for `# heading` but not for `<h1>heading</h1>`.
18
- *
19
- * @type {import('unified').Plugin<Array<void>, Root>}
20
- */
21
- export default function remarkMarkAndUnravel() {
22
- return (tree: any) => {
23
- visit(tree, (node, index, parent_) => {
24
- const parent = /** @type {Parent} */ parent_;
25
- let offset = -1;
26
- let all = true;
27
- /** @type {boolean|undefined} */
28
- let oneOrMore;
29
-
30
- if (parent && typeof index === 'number' && node.type === 'paragraph') {
31
- const children = node.children;
32
-
33
- while (++offset < children.length) {
34
- const child = children[offset];
35
-
36
- if (child.type === 'mdxJsxTextElement' || child.type === 'mdxTextExpression') {
37
- oneOrMore = true;
38
- } else if (child.type === 'text' && /^[\t\r\n ]+$/.test(String(child.value))) {
39
- // Empty.
40
- } else {
41
- all = false;
42
- break;
43
- }
44
- }
45
-
46
- if (all && oneOrMore) {
47
- offset = -1;
48
-
49
- while (++offset < children.length) {
50
- const child = children[offset];
51
-
52
- if (child.type === 'mdxJsxTextElement') {
53
- child.type = 'mdxJsxFlowElement';
54
- }
55
-
56
- if (child.type === 'mdxTextExpression') {
57
- child.type = 'mdxFlowExpression';
58
- }
59
- }
60
-
61
- parent.children.splice(index, 1, ...children);
62
- return index;
63
- }
64
- }
65
-
66
- if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
67
- const data = node.data || (node.data = {});
68
- data._mdxExplicitJsx = true;
69
- }
70
- });
71
- };
72
- }