Просмотр исходного кода

add @growi/remark-growi-plugin package

Yuki Takei 3 лет назад
Родитель
Сommit
a7a1a8ff50

+ 3 - 0
packages/app/src/services/renderer/renderer.ts

@@ -1,3 +1,4 @@
+import growiPlugin from '@growi/remark-growi-plugin';
 import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
 import katex from 'rehype-katex';
 import raw from 'rehype-raw';
@@ -9,6 +10,7 @@ import emoji from 'remark-emoji';
 import gfm from 'remark-gfm';
 import math from 'remark-math';
 
+
 import { CodeBlock } from '~/components/ReactMarkdownComponents/CodeBlock';
 import { Header } from '~/components/ReactMarkdownComponents/Header';
 import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
@@ -222,6 +224,7 @@ const generateCommonOptions = (pagePath: string|undefined, config: RendererConfi
     remarkPlugins: [
       gfm,
       pukiwikiLikeLinker,
+      growiPlugin,
     ],
     rehypePlugins: [
       slug,

+ 2 - 0
packages/remark-growi-plugin/.eslintignore

@@ -0,0 +1,2 @@
+/dist/**
+/test/**

+ 2 - 0
packages/remark-growi-plugin/.gitignore

@@ -0,0 +1,2 @@
+/dist
+/coverage

+ 56 - 0
packages/remark-growi-plugin/package.json

@@ -0,0 +1,56 @@
+{
+  "name": "@growi/remark-growi-plugin",
+  "version": "5.1.3-RC.0",
+  "description": "remark plugin to support GROWI plugin",
+  "license": "MIT",
+  "keywords": [
+    "unified",
+    "remark",
+    "remark-plugin",
+    "plugin",
+    "mdast",
+    "markdown",
+    "generic"
+  ],
+  "type": "module",
+  "main": "dist/index.js",
+  "typings": "dist/index.d.ts",
+  "scripts": {
+    "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
+    "clean": "npx -y shx rm -rf dist",
+    "tsc": "tsc -p tsconfig.build.json",
+    "tsc:w": "yarn tsc -w",
+    "test": "cross-env NODE_ENV=test npm run test-coverage",
+    "test-api": "node --conditions development test/index.js",
+    "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
+    "lint": "eslint src --ext .ts",
+    "lint:fix": "eslint src --ext .ts --fix"
+  },
+  "dependencies": {
+    "@types/mdast": "^3.0.0",
+    "mdast-util-directive": "^2.0.0",
+    "micromark-extension-directive": "^2.0.0",
+    "unified": "^10.0.0"
+  },
+  "devDependencies": {
+    "@types/tape": "^4.0.0",
+    "c8": "^7.0.0",
+    "is-hidden": "^2.0.0",
+    "prettier": "^2.0.0",
+    "remark": "^14.0.0",
+    "remark-cli": "^10.0.0",
+    "remark-preset-wooorm": "^9.0.0",
+    "rimraf": "^3.0.0",
+    "tape": "^5.0.0",
+    "to-vfile": "^7.0.0",
+    "type-coverage": "^2.0.0",
+    "typescript": "^4.0.0",
+    "xo": "^0.46.0"
+  },
+  "typeCoverage": {
+    "atLeast": 100,
+    "detail": true,
+    "strict": true,
+    "ignoreCatch": true
+  }
+}

+ 423 - 0
packages/remark-growi-plugin/readme.md

@@ -0,0 +1,423 @@
+# remark-directive
+
+[![Build][build-badge]][build]
+[![Coverage][coverage-badge]][coverage]
+[![Downloads][downloads-badge]][downloads]
+[![Size][size-badge]][size]
+[![Sponsors][sponsors-badge]][collective]
+[![Backers][backers-badge]][collective]
+[![Chat][chat-badge]][chat]
+
+[**remark**][remark] plugin to support the [generic directives proposal][prop]
+(`:cite[smith04]`, `::youtube[Video of a cat in a box]{v=01ab2cd3efg}`, and
+such).
+
+## Contents
+
+*   [What is this?](#what-is-this)
+*   [When should I use this?](#when-should-i-use-this)
+*   [Install](#install)
+*   [Use](#use)
+*   [API](#api)
+    *   [`unified().use(remarkDirective)`](#unifieduseremarkdirective)
+*   [Examples](#examples)
+    *   [Example: YouTube](#example-youtube)
+    *   [Example: Styled blocks](#example-styled-blocks)
+*   [Syntax](#syntax)
+*   [Syntax tree](#syntax-tree)
+*   [Types](#types)
+*   [Compatibility](#compatibility)
+*   [Security](#security)
+*   [Related](#related)
+*   [Contribute](#contribute)
+*   [License](#license)
+
+## What is this?
+
+This package is a [unified][] ([remark][]) plugin to add support for directives:
+one syntax for arbitrary extensions in markdown.
+You can use this with some more code to match your specific needs, to allow for
+anything from callouts, citations, styled blocks, forms, embeds, spoilers, etc.
+
+**unified** is a project that transforms content with abstract syntax trees
+(ASTs).
+**remark** adds support for markdown to unified.
+**mdast** is the markdown AST that remark uses.
+**micromark** is the markdown parser we use.
+This is a remark plugin that adds support for the directives syntax and AST to
+remark.
+
+## When should I use this?
+
+Directives are one of the four ways to extend markdown: an arbitrary extension
+syntax (see [Extending markdown](https://github.com/micromark/micromark#extending-markdown)
+in micromark’s docs for the alternatives and more info).
+This mechanism works well when you control the content: who authors it, what
+tools handle it, and where it’s displayed.
+When authors can read a guide on how to embed a tweet but are not expected to
+know the ins and outs of HTML or JavaScript.
+Directives don’t work well if you don’t know who authors content, what tools
+handle it, and where it ends up.
+Example use cases are a docs website for a project or product, or blogging tools
+and static site generators.
+
+## Install
+
+This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
+In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:
+
+```sh
+npm install remark-directive
+```
+
+In Deno with [`esm.sh`][esmsh]:
+
+```js
+import remarkDirective from 'https://esm.sh/remark-directive@2'
+```
+
+In browsers with [`esm.sh`][esmsh]:
+
+```html
+<script type="module">
+  import remarkDirective from 'https://esm.sh/remark-directive@2?bundle'
+</script>
+```
+
+## Use
+
+Say we have the following file, `example.md`:
+
+```markdown
+:::main{#readme}
+
+Lorem:br
+ipsum.
+
+::hr{.red}
+
+A :i[lovely] language know as :abbr[HTML]{title="HyperText Markup Language"}.
+
+:::
+```
+
+And our module, `example.js`, looks as follows:
+
+```js
+import {read} from 'to-vfile'
+import {unified} from 'unified'
+import remarkParse from 'remark-parse'
+import remarkDirective from 'remark-directive'
+import remarkRehype from 'remark-rehype'
+import rehypeFormat from 'rehype-format'
+import rehypeStringify from 'rehype-stringify'
+import {visit} from 'unist-util-visit'
+import {h} from 'hastscript'
+
+main()
+
+async function main() {
+  const file = await unified()
+    .use(remarkParse)
+    .use(remarkDirective)
+    .use(myRemarkPlugin)
+    .use(remarkRehype)
+    .use(rehypeFormat)
+    .use(rehypeStringify)
+    .process(await read('example.md'))
+
+  console.log(String(file))
+}
+
+// This plugin is an example to let users write HTML with directives.
+// It’s informative but rather useless.
+// See below for others examples.
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+function myRemarkPlugin() {
+  return (tree) => {
+    visit(tree, (node) => {
+      if (
+        node.type === 'textDirective' ||
+        node.type === 'leafDirective' ||
+        node.type === 'containerDirective'
+      ) {
+        const data = node.data || (node.data = {})
+        const hast = h(node.name, node.attributes)
+
+        data.hName = hast.tagName
+        data.hProperties = hast.properties
+      }
+    })
+  }
+}
+```
+
+Now, running `node example` yields:
+
+```html
+<main id="readme">
+  <p>Lorem<br>ipsum.</p>
+  <hr class="red">
+  <p>A <i>lovely</i> language know as <abbr title="HyperText Markup Language">HTML</abbr>.</p>
+</main>
+```
+
+## API
+
+This package exports no identifiers.
+The default export is `remarkDirective`.
+
+### `unified().use(remarkDirective)`
+
+Configures remark so that it can parse and serialize directives.
+Doesn’t handle the directives: [create your own plugin][create-plugin] to do
+that.
+
+## Examples
+
+### Example: YouTube
+
+This example shows how directives can be used for YouTube embeds.
+It’s based on the example in Use above.
+If `myRemarkPlugin` was replaced with this function:
+
+```js
+// This plugin is an example to turn `::youtube` into iframes.
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+function myRemarkPlugin() {
+  return (tree, file) => {
+    visit(tree, (node) => {
+      if (
+        node.type === 'textDirective' ||
+        node.type === 'leafDirective' ||
+        node.type === 'containerDirective'
+      ) {
+        if (node.name !== 'youtube') return
+
+        const data = node.data || (node.data = {})
+        const attributes = node.attributes || {}
+        const id = attributes.id
+
+        if (node.type === 'textDirective') file.fail('Text directives for `youtube` not supported', node)
+        if (!id) file.fail('Missing video id', node)
+
+        data.hName = 'iframe'
+        data.hProperties = {
+          src: 'https://www.youtube.com/embed/' + id,
+          width: 200,
+          height: 200,
+          frameBorder: 0,
+          allow: 'picture-in-picture',
+          allowFullScreen: true
+        }
+      }
+    })
+  }
+}
+```
+
+…and `example.md` contains:
+
+```markdown
+# Cat videos
+
+::youtube[Video of a cat in a box]{#01ab2cd3efg}
+```
+
+…then running `node example` yields:
+
+```html
+<h1>Cat videos</h1>
+<iframe src="https://www.youtube.com/embed/01ab2cd3efg" width="200" height="200" frameborder="0" allow="picture-in-picture" allowfullscreen>Video of a cat in a box</iframe>
+```
+
+### Example: Styled blocks
+
+Note: This is sometimes called admonitions, callouts, etc.
+
+This example shows how directives can be used to style blocks.
+It’s based on the example in Use above.
+If `myRemarkPlugin` was replaced with this function:
+
+```js
+// This plugin is an example to turn `::note` into divs, passing arbitrary
+// attributes.
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+function myRemarkPlugin() {
+  return (tree) => {
+    visit(tree, (node) => {
+      if (
+        node.type === 'textDirective' ||
+        node.type === 'leafDirective' ||
+        node.type === 'containerDirective'
+      ) {
+        if (node.name !== 'note') return
+
+        const data = node.data || (node.data = {})
+        const tagName = node.type === 'textDirective' ? 'span' : 'div'
+
+        data.hName = tagName
+        data.hProperties = h(tagName, node.attributes).properties
+      }
+    })
+  }
+}
+```
+
+…and `example.md` contains:
+
+```markdown
+# How to use xxx
+
+You can use xxx.
+
+:::note{.warning}
+if you chose xxx, you should also use yyy somewhere…
+:::
+```
+
+…then running `node example` yields:
+
+```html
+<h1>How to use xxx</h1>
+<p>You can use xxx.</p>
+<div class="warning">
+  <p>if you chose xxx, you should also use yyy somewhere…</p>
+</div>
+```
+
+## Syntax
+
+This plugin applies a micromark extensions to parse the syntax.
+See its readme for parse details:
+
+*   [`micromark-extension-directive`](https://github.com/micromark/micromark-extension-directive#syntax)
+
+## Syntax tree
+
+This plugin applies one mdast utility to build and serialize the AST.
+See its readme for the node types supported in the tree:
+
+*   [`mdast-util-directive`](https://github.com/syntax-tree/mdast-util-directive#syntax-tree)
+
+## Types
+
+This package is fully typed with [TypeScript][].
+If you’re working with the syntax tree, make sure to import this plugin
+somewhere in your types, as that registers the new node types in the tree.
+
+```js
+/** @typedef {import('remark-directive')} */
+
+import {visit} from 'unist-util-visit'
+
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+export default function myRemarkPlugin() {
+  return (tree) => {
+    visit(tree, (node) => {
+      // `node` can now be one of the nodes for directives.
+    })
+  }
+}
+```
+
+## Compatibility
+
+Projects maintained by the unified collective are compatible with all maintained
+versions of Node.js.
+As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
+Our projects sometimes work with older versions, but this is not guaranteed.
+
+This plugin works with unified version 9+ and remark version 14+.
+
+## Security
+
+Use of `remark-directive` does not involve [**rehype**][rehype]
+([**hast**][hast]) or user content so there are no openings for [cross-site
+scripting (XSS)][xss] attacks.
+
+## Related
+
+*   [`remark-gfm`](https://github.com/remarkjs/remark-gfm)
+    — support GFM (autolink literals, footnotes, strikethrough, tables,
+    tasklists)
+*   [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter)
+    — support frontmatter (YAML, TOML, and more)
+*   [`remark-math`](https://github.com/remarkjs/remark-math)
+    — support math
+*   [`remark-mdx`](https://github.com/mdx-js/mdx/tree/main/packages/remark-mdx)
+    — support MDX (JSX, expressions, ESM)
+
+## Contribute
+
+See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways
+to get started.
+See [`support.md`][support] for ways to get help.
+
+This project has a [code of conduct][coc].
+By interacting with this repository, organization, or community you agree to
+abide by its terms.
+
+## License
+
+[MIT][license] © [Titus Wormer][author]
+
+<!-- Definitions -->
+
+[build-badge]: https://github.com/remarkjs/remark-directive/workflows/main/badge.svg
+
+[build]: https://github.com/remarkjs/remark-directive/actions
+
+[coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-directive.svg
+
+[coverage]: https://codecov.io/github/remarkjs/remark-directive
+
+[downloads-badge]: https://img.shields.io/npm/dm/remark-directive.svg
+
+[downloads]: https://www.npmjs.com/package/remark-directive
+
+[size-badge]: https://img.shields.io/bundlephobia/minzip/remark-directive.svg
+
+[size]: https://bundlephobia.com/result?p=remark-directive
+
+[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
+
+[backers-badge]: https://opencollective.com/unified/backers/badge.svg
+
+[collective]: https://opencollective.com/unified
+
+[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
+
+[chat]: https://github.com/remarkjs/remark/discussions
+
+[npm]: https://docs.npmjs.com/cli/install
+
+[esmsh]: https://esm.sh
+
+[health]: https://github.com/remarkjs/.github
+
+[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md
+
+[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md
+
+[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md
+
+[license]: license
+
+[author]: https://wooorm.com
+
+[unified]: https://github.com/unifiedjs/unified
+
+[remark]: https://github.com/remarkjs/remark
+
+[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
+
+[typescript]: https://www.typescriptlang.org
+
+[rehype]: https://github.com/rehypejs/rehype
+
+[hast]: https://github.com/syntax-tree/hast
+
+[prop]: https://talk.commonmark.org/t/generic-directives-plugins-syntax/444
+
+[create-plugin]: https://unifiedjs.com/learn/guide/create-a-plugin/

+ 36 - 0
packages/remark-growi-plugin/src/index.js

@@ -0,0 +1,36 @@
+/**
+ * @typedef {import('mdast').Root} Root
+ *
+ * @typedef {import('mdast-util-directive')} DoNotTouchAsThisImportIncludesDirectivesInTree
+ */
+
+import { directiveFromMarkdown, directiveToMarkdown } from 'mdast-util-directive';
+import { directive } from 'micromark-extension-directive';
+
+/**
+ * Plugin to support the generic directives proposal (`:cite[smith04]`,
+ * `::youtube[Video of a cat in a box]{v=01ab2cd3efg}`, and such).
+ *
+ * @type {import('unified').Plugin<void[], Root>}
+ */
+export default function remarkDirective() {
+  const data = this.data();
+
+  add('micromarkExtensions', directive());
+  add('fromMarkdownExtensions', directiveFromMarkdown);
+  add('toMarkdownExtensions', directiveToMarkdown);
+
+  /**
+   * @param {string} field
+   * @param {unknown} value
+   */
+  function add(field, value) {
+    const list = /** @type {unknown[]} */ (
+      // Other extensions
+      /* c8 ignore next 2 */
+      data[field] ? data[field] : (data[field] = [])
+    );
+
+    list.push(value);
+  }
+}

+ 32 - 0
packages/remark-growi-plugin/test/fixtures/container/input.md

@@ -0,0 +1,32 @@
+:::a
+:::
+
+:::a[b]
+:::
+
+:::a{b}
+:::
+
+:::a[b]{c}
+:::
+
+:::a[b *c* d **e**]
+:::
+
+:::a{#b.c.d id=e class="f g" h="i &amp; j k"}
+:::
+
+:::::a
+::::b
+:::c
+:::::
+
+:::::a
+b
+::::c
+d
+:::e
+- - f
+:::
+> g h
+:::::

+ 38 - 0
packages/remark-growi-plugin/test/fixtures/container/output.md

@@ -0,0 +1,38 @@
+:::a
+:::
+
+:::a[b]
+:::
+
+:::a{b}
+:::
+
+:::a[b]{c}
+:::
+
+:::a[b *c* d **e**]
+:::
+
+:::a{#e .c.d.f.g h="i & j k"}
+:::
+
+:::::a
+::::b
+:::c
+:::
+::::
+:::::
+
+:::::a
+b
+
+::::c
+d
+
+:::e
+*   *   f
+:::
+
+> g h
+::::
+:::::

+ 666 - 0
packages/remark-growi-plugin/test/fixtures/container/tree.json

@@ -0,0 +1,666 @@
+{
+  "type": "root",
+  "children": [
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [],
+      "position": {
+        "start": {
+          "line": 1,
+          "column": 1,
+          "offset": 0
+        },
+        "end": {
+          "line": 2,
+          "column": 4,
+          "offset": 8
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "paragraph",
+          "data": {
+            "directiveLabel": true
+          },
+          "children": [
+            {
+              "type": "text",
+              "value": "b",
+              "position": {
+                "start": {
+                  "line": 4,
+                  "column": 6,
+                  "offset": 15
+                },
+                "end": {
+                  "line": 4,
+                  "column": 7,
+                  "offset": 16
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 4,
+              "column": 5,
+              "offset": 14
+            },
+            "end": {
+              "line": 4,
+              "column": 8,
+              "offset": 17
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 4,
+          "column": 1,
+          "offset": 10
+        },
+        "end": {
+          "line": 5,
+          "column": 4,
+          "offset": 21
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {
+        "b": ""
+      },
+      "children": [],
+      "position": {
+        "start": {
+          "line": 7,
+          "column": 1,
+          "offset": 23
+        },
+        "end": {
+          "line": 8,
+          "column": 4,
+          "offset": 34
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {
+        "c": ""
+      },
+      "children": [
+        {
+          "type": "paragraph",
+          "data": {
+            "directiveLabel": true
+          },
+          "children": [
+            {
+              "type": "text",
+              "value": "b",
+              "position": {
+                "start": {
+                  "line": 10,
+                  "column": 6,
+                  "offset": 41
+                },
+                "end": {
+                  "line": 10,
+                  "column": 7,
+                  "offset": 42
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 10,
+              "column": 5,
+              "offset": 40
+            },
+            "end": {
+              "line": 10,
+              "column": 8,
+              "offset": 43
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 10,
+          "column": 1,
+          "offset": 36
+        },
+        "end": {
+          "line": 11,
+          "column": 4,
+          "offset": 50
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "paragraph",
+          "data": {
+            "directiveLabel": true
+          },
+          "children": [
+            {
+              "type": "text",
+              "value": "b ",
+              "position": {
+                "start": {
+                  "line": 13,
+                  "column": 6,
+                  "offset": 57
+                },
+                "end": {
+                  "line": 13,
+                  "column": 8,
+                  "offset": 59
+                }
+              }
+            },
+            {
+              "type": "emphasis",
+              "children": [
+                {
+                  "type": "text",
+                  "value": "c",
+                  "position": {
+                    "start": {
+                      "line": 13,
+                      "column": 9,
+                      "offset": 60
+                    },
+                    "end": {
+                      "line": 13,
+                      "column": 10,
+                      "offset": 61
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 13,
+                  "column": 8,
+                  "offset": 59
+                },
+                "end": {
+                  "line": 13,
+                  "column": 11,
+                  "offset": 62
+                }
+              }
+            },
+            {
+              "type": "text",
+              "value": " d ",
+              "position": {
+                "start": {
+                  "line": 13,
+                  "column": 11,
+                  "offset": 62
+                },
+                "end": {
+                  "line": 13,
+                  "column": 14,
+                  "offset": 65
+                }
+              }
+            },
+            {
+              "type": "strong",
+              "children": [
+                {
+                  "type": "text",
+                  "value": "e",
+                  "position": {
+                    "start": {
+                      "line": 13,
+                      "column": 16,
+                      "offset": 67
+                    },
+                    "end": {
+                      "line": 13,
+                      "column": 17,
+                      "offset": 68
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 13,
+                  "column": 14,
+                  "offset": 65
+                },
+                "end": {
+                  "line": 13,
+                  "column": 19,
+                  "offset": 70
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 13,
+              "column": 5,
+              "offset": 56
+            },
+            "end": {
+              "line": 13,
+              "column": 20,
+              "offset": 71
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 13,
+          "column": 1,
+          "offset": 52
+        },
+        "end": {
+          "line": 14,
+          "column": 4,
+          "offset": 75
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {
+        "id": "e",
+        "class": "c d f g",
+        "h": "i & j k"
+      },
+      "children": [],
+      "position": {
+        "start": {
+          "line": 16,
+          "column": 1,
+          "offset": 77
+        },
+        "end": {
+          "line": 17,
+          "column": 4,
+          "offset": 126
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "containerDirective",
+          "name": "b",
+          "attributes": {},
+          "children": [
+            {
+              "type": "containerDirective",
+              "name": "c",
+              "attributes": {},
+              "children": [],
+              "position": {
+                "start": {
+                  "line": 21,
+                  "column": 1,
+                  "offset": 141
+                },
+                "end": {
+                  "line": 22,
+                  "column": 1,
+                  "offset": 146
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 20,
+              "column": 1,
+              "offset": 135
+            },
+            "end": {
+              "line": 22,
+              "column": 1,
+              "offset": 146
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 19,
+          "column": 1,
+          "offset": 128
+        },
+        "end": {
+          "line": 22,
+          "column": 6,
+          "offset": 151
+        }
+      }
+    },
+    {
+      "type": "containerDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "paragraph",
+          "children": [
+            {
+              "type": "text",
+              "value": "b",
+              "position": {
+                "start": {
+                  "line": 25,
+                  "column": 1,
+                  "offset": 160
+                },
+                "end": {
+                  "line": 25,
+                  "column": 2,
+                  "offset": 161
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 25,
+              "column": 1,
+              "offset": 160
+            },
+            "end": {
+              "line": 25,
+              "column": 2,
+              "offset": 161
+            }
+          }
+        },
+        {
+          "type": "containerDirective",
+          "name": "c",
+          "attributes": {},
+          "children": [
+            {
+              "type": "paragraph",
+              "children": [
+                {
+                  "type": "text",
+                  "value": "d",
+                  "position": {
+                    "start": {
+                      "line": 27,
+                      "column": 1,
+                      "offset": 168
+                    },
+                    "end": {
+                      "line": 27,
+                      "column": 2,
+                      "offset": 169
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 27,
+                  "column": 1,
+                  "offset": 168
+                },
+                "end": {
+                  "line": 27,
+                  "column": 2,
+                  "offset": 169
+                }
+              }
+            },
+            {
+              "type": "containerDirective",
+              "name": "e",
+              "attributes": {},
+              "children": [
+                {
+                  "type": "list",
+                  "ordered": false,
+                  "start": null,
+                  "spread": false,
+                  "children": [
+                    {
+                      "type": "listItem",
+                      "spread": false,
+                      "checked": null,
+                      "children": [
+                        {
+                          "type": "list",
+                          "ordered": false,
+                          "start": null,
+                          "spread": false,
+                          "children": [
+                            {
+                              "type": "listItem",
+                              "spread": false,
+                              "checked": null,
+                              "children": [
+                                {
+                                  "type": "paragraph",
+                                  "children": [
+                                    {
+                                      "type": "text",
+                                      "value": "f",
+                                      "position": {
+                                        "start": {
+                                          "line": 29,
+                                          "column": 5,
+                                          "offset": 179
+                                        },
+                                        "end": {
+                                          "line": 29,
+                                          "column": 6,
+                                          "offset": 180
+                                        }
+                                      }
+                                    }
+                                  ],
+                                  "position": {
+                                    "start": {
+                                      "line": 29,
+                                      "column": 5,
+                                      "offset": 179
+                                    },
+                                    "end": {
+                                      "line": 29,
+                                      "column": 6,
+                                      "offset": 180
+                                    }
+                                  }
+                                }
+                              ],
+                              "position": {
+                                "start": {
+                                  "line": 29,
+                                  "column": 3,
+                                  "offset": 177
+                                },
+                                "end": {
+                                  "line": 29,
+                                  "column": 6,
+                                  "offset": 180
+                                }
+                              }
+                            }
+                          ],
+                          "position": {
+                            "start": {
+                              "line": 29,
+                              "column": 3,
+                              "offset": 177
+                            },
+                            "end": {
+                              "line": 29,
+                              "column": 6,
+                              "offset": 180
+                            }
+                          }
+                        }
+                      ],
+                      "position": {
+                        "start": {
+                          "line": 29,
+                          "column": 1,
+                          "offset": 175
+                        },
+                        "end": {
+                          "line": 29,
+                          "column": 6,
+                          "offset": 180
+                        }
+                      }
+                    }
+                  ],
+                  "position": {
+                    "start": {
+                      "line": 29,
+                      "column": 1,
+                      "offset": 175
+                    },
+                    "end": {
+                      "line": 29,
+                      "column": 6,
+                      "offset": 180
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 28,
+                  "column": 1,
+                  "offset": 170
+                },
+                "end": {
+                  "line": 30,
+                  "column": 4,
+                  "offset": 184
+                }
+              }
+            },
+            {
+              "type": "blockquote",
+              "children": [
+                {
+                  "type": "paragraph",
+                  "children": [
+                    {
+                      "type": "text",
+                      "value": "g h",
+                      "position": {
+                        "start": {
+                          "line": 31,
+                          "column": 3,
+                          "offset": 187
+                        },
+                        "end": {
+                          "line": 31,
+                          "column": 6,
+                          "offset": 190
+                        }
+                      }
+                    }
+                  ],
+                  "position": {
+                    "start": {
+                      "line": 31,
+                      "column": 3,
+                      "offset": 187
+                    },
+                    "end": {
+                      "line": 31,
+                      "column": 6,
+                      "offset": 190
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 31,
+                  "column": 1,
+                  "offset": 185
+                },
+                "end": {
+                  "line": 31,
+                  "column": 6,
+                  "offset": 190
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 26,
+              "column": 1,
+              "offset": 162
+            },
+            "end": {
+              "line": 32,
+              "column": 1,
+              "offset": 191
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 24,
+          "column": 1,
+          "offset": 153
+        },
+        "end": {
+          "line": 32,
+          "column": 6,
+          "offset": 196
+        }
+      }
+    }
+  ],
+  "position": {
+    "start": {
+      "line": 1,
+      "column": 1,
+      "offset": 0
+    },
+    "end": {
+      "line": 33,
+      "column": 1,
+      "offset": 197
+    }
+  }
+}

+ 11 - 0
packages/remark-growi-plugin/test/fixtures/leaf/input.md

@@ -0,0 +1,11 @@
+::a
+
+::a[b]
+
+::a{b}
+
+::a[b]{c}
+
+::a[b *c* d **e**]
+
+::a{#b.c.d id=e class="f g" h="i &amp; j k"}

+ 11 - 0
packages/remark-growi-plugin/test/fixtures/leaf/output.md

@@ -0,0 +1,11 @@
+::a
+
+::a[b]
+
+::a{b}
+
+::a[b]{c}
+
+::a[b *c* d **e**]
+
+::a{#e .c.d.f.g h="i & j k"}

+ 266 - 0
packages/remark-growi-plugin/test/fixtures/leaf/tree.json

@@ -0,0 +1,266 @@
+{
+  "type": "root",
+  "children": [
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [],
+      "position": {
+        "start": {
+          "line": 1,
+          "column": 1,
+          "offset": 0
+        },
+        "end": {
+          "line": 1,
+          "column": 4,
+          "offset": 3
+        }
+      }
+    },
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "text",
+          "value": "b",
+          "position": {
+            "start": {
+              "line": 3,
+              "column": 5,
+              "offset": 9
+            },
+            "end": {
+              "line": 3,
+              "column": 6,
+              "offset": 10
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 3,
+          "column": 1,
+          "offset": 5
+        },
+        "end": {
+          "line": 3,
+          "column": 7,
+          "offset": 11
+        }
+      }
+    },
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {
+        "b": ""
+      },
+      "children": [],
+      "position": {
+        "start": {
+          "line": 5,
+          "column": 1,
+          "offset": 13
+        },
+        "end": {
+          "line": 5,
+          "column": 7,
+          "offset": 19
+        }
+      }
+    },
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {
+        "c": ""
+      },
+      "children": [
+        {
+          "type": "text",
+          "value": "b",
+          "position": {
+            "start": {
+              "line": 7,
+              "column": 5,
+              "offset": 25
+            },
+            "end": {
+              "line": 7,
+              "column": 6,
+              "offset": 26
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 7,
+          "column": 1,
+          "offset": 21
+        },
+        "end": {
+          "line": 7,
+          "column": 10,
+          "offset": 30
+        }
+      }
+    },
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {},
+      "children": [
+        {
+          "type": "text",
+          "value": "b ",
+          "position": {
+            "start": {
+              "line": 9,
+              "column": 5,
+              "offset": 36
+            },
+            "end": {
+              "line": 9,
+              "column": 7,
+              "offset": 38
+            }
+          }
+        },
+        {
+          "type": "emphasis",
+          "children": [
+            {
+              "type": "text",
+              "value": "c",
+              "position": {
+                "start": {
+                  "line": 9,
+                  "column": 8,
+                  "offset": 39
+                },
+                "end": {
+                  "line": 9,
+                  "column": 9,
+                  "offset": 40
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 9,
+              "column": 7,
+              "offset": 38
+            },
+            "end": {
+              "line": 9,
+              "column": 10,
+              "offset": 41
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": " d ",
+          "position": {
+            "start": {
+              "line": 9,
+              "column": 10,
+              "offset": 41
+            },
+            "end": {
+              "line": 9,
+              "column": 13,
+              "offset": 44
+            }
+          }
+        },
+        {
+          "type": "strong",
+          "children": [
+            {
+              "type": "text",
+              "value": "e",
+              "position": {
+                "start": {
+                  "line": 9,
+                  "column": 15,
+                  "offset": 46
+                },
+                "end": {
+                  "line": 9,
+                  "column": 16,
+                  "offset": 47
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 9,
+              "column": 13,
+              "offset": 44
+            },
+            "end": {
+              "line": 9,
+              "column": 18,
+              "offset": 49
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 9,
+          "column": 1,
+          "offset": 32
+        },
+        "end": {
+          "line": 9,
+          "column": 19,
+          "offset": 50
+        }
+      }
+    },
+    {
+      "type": "leafDirective",
+      "name": "a",
+      "attributes": {
+        "id": "e",
+        "class": "c d f g",
+        "h": "i & j k"
+      },
+      "children": [],
+      "position": {
+        "start": {
+          "line": 11,
+          "column": 1,
+          "offset": 52
+        },
+        "end": {
+          "line": 11,
+          "column": 45,
+          "offset": 96
+        }
+      }
+    }
+  ],
+  "position": {
+    "start": {
+      "line": 1,
+      "column": 1,
+      "offset": 0
+    },
+    "end": {
+      "line": 12,
+      "column": 1,
+      "offset": 97
+    }
+  }
+}

+ 7 - 0
packages/remark-growi-plugin/test/fixtures/text/input.md

@@ -0,0 +1,7 @@
+One :a, two :a[b], three :a{b}, four :a[b]{c}.
+
+:a[b *c*
+d **e**].
+
+:a{#b.c.d id=e class="f g" h="i &amp; j
+k"}.

+ 7 - 0
packages/remark-growi-plugin/test/fixtures/text/output.md

@@ -0,0 +1,7 @@
+One :a, two :a[b], three :a{b}, four :a[b]{c}.
+
+:a[b *c*
+d **e**].
+
+:a{#e .c.d.f.g h="i & j
+k"}.

+ 429 - 0
packages/remark-growi-plugin/test/fixtures/text/tree.json

@@ -0,0 +1,429 @@
+{
+  "type": "root",
+  "children": [
+    {
+      "type": "paragraph",
+      "children": [
+        {
+          "type": "text",
+          "value": "One ",
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 1,
+              "offset": 0
+            },
+            "end": {
+              "line": 1,
+              "column": 5,
+              "offset": 4
+            }
+          }
+        },
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {},
+          "children": [],
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 5,
+              "offset": 4
+            },
+            "end": {
+              "line": 1,
+              "column": 7,
+              "offset": 6
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ", two ",
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 7,
+              "offset": 6
+            },
+            "end": {
+              "line": 1,
+              "column": 13,
+              "offset": 12
+            }
+          }
+        },
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {},
+          "children": [
+            {
+              "type": "text",
+              "value": "b",
+              "position": {
+                "start": {
+                  "line": 1,
+                  "column": 16,
+                  "offset": 15
+                },
+                "end": {
+                  "line": 1,
+                  "column": 17,
+                  "offset": 16
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 13,
+              "offset": 12
+            },
+            "end": {
+              "line": 1,
+              "column": 18,
+              "offset": 17
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ", three ",
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 18,
+              "offset": 17
+            },
+            "end": {
+              "line": 1,
+              "column": 26,
+              "offset": 25
+            }
+          }
+        },
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {
+            "b": ""
+          },
+          "children": [],
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 26,
+              "offset": 25
+            },
+            "end": {
+              "line": 1,
+              "column": 31,
+              "offset": 30
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ", four ",
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 31,
+              "offset": 30
+            },
+            "end": {
+              "line": 1,
+              "column": 38,
+              "offset": 37
+            }
+          }
+        },
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {
+            "c": ""
+          },
+          "children": [
+            {
+              "type": "text",
+              "value": "b",
+              "position": {
+                "start": {
+                  "line": 1,
+                  "column": 41,
+                  "offset": 40
+                },
+                "end": {
+                  "line": 1,
+                  "column": 42,
+                  "offset": 41
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 38,
+              "offset": 37
+            },
+            "end": {
+              "line": 1,
+              "column": 46,
+              "offset": 45
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ".",
+          "position": {
+            "start": {
+              "line": 1,
+              "column": 46,
+              "offset": 45
+            },
+            "end": {
+              "line": 1,
+              "column": 47,
+              "offset": 46
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 1,
+          "column": 1,
+          "offset": 0
+        },
+        "end": {
+          "line": 1,
+          "column": 47,
+          "offset": 46
+        }
+      }
+    },
+    {
+      "type": "paragraph",
+      "children": [
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {},
+          "children": [
+            {
+              "type": "text",
+              "value": "b ",
+              "position": {
+                "start": {
+                  "line": 3,
+                  "column": 4,
+                  "offset": 51
+                },
+                "end": {
+                  "line": 3,
+                  "column": 6,
+                  "offset": 53
+                }
+              }
+            },
+            {
+              "type": "emphasis",
+              "children": [
+                {
+                  "type": "text",
+                  "value": "c",
+                  "position": {
+                    "start": {
+                      "line": 3,
+                      "column": 7,
+                      "offset": 54
+                    },
+                    "end": {
+                      "line": 3,
+                      "column": 8,
+                      "offset": 55
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 3,
+                  "column": 6,
+                  "offset": 53
+                },
+                "end": {
+                  "line": 3,
+                  "column": 9,
+                  "offset": 56
+                }
+              }
+            },
+            {
+              "type": "text",
+              "value": "\nd ",
+              "position": {
+                "start": {
+                  "line": 3,
+                  "column": 9,
+                  "offset": 56
+                },
+                "end": {
+                  "line": 4,
+                  "column": 3,
+                  "offset": 59
+                }
+              }
+            },
+            {
+              "type": "strong",
+              "children": [
+                {
+                  "type": "text",
+                  "value": "e",
+                  "position": {
+                    "start": {
+                      "line": 4,
+                      "column": 5,
+                      "offset": 61
+                    },
+                    "end": {
+                      "line": 4,
+                      "column": 6,
+                      "offset": 62
+                    }
+                  }
+                }
+              ],
+              "position": {
+                "start": {
+                  "line": 4,
+                  "column": 3,
+                  "offset": 59
+                },
+                "end": {
+                  "line": 4,
+                  "column": 8,
+                  "offset": 64
+                }
+              }
+            }
+          ],
+          "position": {
+            "start": {
+              "line": 3,
+              "column": 1,
+              "offset": 48
+            },
+            "end": {
+              "line": 4,
+              "column": 9,
+              "offset": 65
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ".",
+          "position": {
+            "start": {
+              "line": 4,
+              "column": 9,
+              "offset": 65
+            },
+            "end": {
+              "line": 4,
+              "column": 10,
+              "offset": 66
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 3,
+          "column": 1,
+          "offset": 48
+        },
+        "end": {
+          "line": 4,
+          "column": 10,
+          "offset": 66
+        }
+      }
+    },
+    {
+      "type": "paragraph",
+      "children": [
+        {
+          "type": "textDirective",
+          "name": "a",
+          "attributes": {
+            "id": "e",
+            "class": "c d f g",
+            "h": "i & j\nk"
+          },
+          "children": [],
+          "position": {
+            "start": {
+              "line": 6,
+              "column": 1,
+              "offset": 68
+            },
+            "end": {
+              "line": 7,
+              "column": 4,
+              "offset": 111
+            }
+          }
+        },
+        {
+          "type": "text",
+          "value": ".",
+          "position": {
+            "start": {
+              "line": 7,
+              "column": 4,
+              "offset": 111
+            },
+            "end": {
+              "line": 7,
+              "column": 5,
+              "offset": 112
+            }
+          }
+        }
+      ],
+      "position": {
+        "start": {
+          "line": 6,
+          "column": 1,
+          "offset": 68
+        },
+        "end": {
+          "line": 7,
+          "column": 5,
+          "offset": 112
+        }
+      }
+    }
+  ],
+  "position": {
+    "start": {
+      "line": 1,
+      "column": 1,
+      "offset": 0
+    },
+    "end": {
+      "line": 8,
+      "column": 1,
+      "offset": 113
+    }
+  }
+}

+ 67 - 0
packages/remark-growi-plugin/test/index.js

@@ -0,0 +1,67 @@
+/**
+ * @typedef {import('mdast').Root} Root
+ */
+
+import fs from 'node:fs'
+import path from 'node:path'
+import test from 'tape'
+import {readSync} from 'to-vfile'
+import {unified} from 'unified'
+import {remark} from 'remark'
+import {isHidden} from 'is-hidden'
+import directive from '../src/index.js'
+
+test('directive()', (t) => {
+  t.doesNotThrow(() => {
+    remark().use(directive).freeze()
+  }, 'should not throw if not passed options')
+
+  t.doesNotThrow(() => {
+    unified().use(directive).freeze()
+  }, 'should not throw if without parser or compiler')
+
+  t.end()
+})
+
+test('fixtures', (t) => {
+  const base = path.join('test', 'fixtures')
+  const entries = fs.readdirSync(base).filter((d) => !isHidden(d))
+
+  t.plan(entries.length)
+
+  let index = -1
+  while (++index < entries.length) {
+    const fixture = entries[index]
+    t.test(fixture, (st) => {
+      const file = readSync(path.join(base, fixture, 'input.md'))
+      const input = String(file)
+      const outputPath = path.join(base, fixture, 'output.md')
+      const treePath = path.join(base, fixture, 'tree.json')
+      const proc = remark().use(directive).freeze()
+      const actual = proc.parse(file)
+      /** @type {string} */
+      let output
+      /** @type {Root} */
+      let expected
+
+      try {
+        expected = JSON.parse(String(fs.readFileSync(treePath)))
+      } catch {
+        // New fixture.
+        fs.writeFileSync(treePath, JSON.stringify(actual, null, 2) + '\n')
+        expected = actual
+      }
+
+      try {
+        output = fs.readFileSync(outputPath, 'utf8')
+      } catch {
+        output = input
+      }
+
+      st.deepEqual(actual, expected, 'tree')
+      st.equal(String(proc.processSync(file)), output, 'process')
+
+      st.end()
+    })
+  }
+})

+ 11 - 0
packages/remark-growi-plugin/tsconfig.base.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+  },
+  "include": [
+    "src"
+  ],
+  "exclude": [
+    "test"
+  ]
+}

+ 16 - 0
packages/remark-growi-plugin/tsconfig.build.json

@@ -0,0 +1,16 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "outDir": "dist",
+    "declaration": true,
+    "noResolve": false,
+    "preserveConstEnums": true,
+    "sourceMap": false,
+    "noEmit": false,
+
+    "baseUrl": ".",
+    "paths": {
+    }
+  }
+}

+ 10 - 0
packages/remark-growi-plugin/tsconfig.json

@@ -0,0 +1,10 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "~/*": ["./src/*"],
+      "@growi/*": ["../*/src"]
+    }
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 542 - 13
yarn.lock


Некоторые файлы не были показаны из-за большого количества измененных файлов