diff --git a/package.json b/package.json index 48b2df0..6c6d0ef 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,12 @@ [ "remark-lint-no-html", false + ], + [ + "remark-lint-no-undefined-references", + { + "allowShortcutLink": true + } ] ] }, @@ -76,6 +82,7 @@ "type": "module", "version": "0.0.0", "workspaces": [ + "packages/alert/", "packages/break/", "packages/color/", "packages/dir/", diff --git a/packages/alert/index.js b/packages/alert/index.js new file mode 100644 index 0000000..dab322b --- /dev/null +++ b/packages/alert/index.js @@ -0,0 +1 @@ +export {default} from './lib/index.js' diff --git a/packages/alert/lib/index.js b/packages/alert/lib/index.js new file mode 100644 index 0000000..6031a20 --- /dev/null +++ b/packages/alert/lib/index.js @@ -0,0 +1,184 @@ +/** + * @import {Root} from 'hast' + */ + +/** + * @typedef Icon + * @property {string} d + * @property {string} name + */ + +import {whitespace} from 'hast-util-whitespace' +import {visit} from 'unist-util-visit' + +/** @type {Map} */ +const icons = new Map() + +icons.set('caution', { + d: 'M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z', + name: 'octicon-stop' +}) +icons.set('important', { + d: 'M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z', + name: 'octicon-report' +}) +icons.set('note', { + d: 'M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z', + name: 'octicon-info' +}) +icons.set('tip', { + d: 'M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z', + name: 'octicon-light-bulb' +}) +icons.set('warning', { + d: 'M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z', + name: 'octicon-alert' +}) + +/** + * Plugin to enhance alerts. + * + * @returns + * Transform. + */ +export default function rehypeGithubAlert() { + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ + return function (tree) { + visit(tree, 'element', function (node, index, parent) { + // Must be a `blockquote` element, + // with a parent. + if ( + node.tagName !== 'blockquote' || + typeof index !== 'number' || + !parent || + parent.type !== 'root' + ) { + return + } + + // Must start with a `

`. + let headIndex = 0 + + while ( + headIndex < node.children.length && + whitespace(node.children[headIndex]) + ) { + headIndex++ + } + + const head = node.children[headIndex] + + // Must start with a `p`. + if (!head || head.type !== 'element' || head.tagName !== 'p') return + + // Must start with a `![`. + const text = head.children[0] + if (!text || text.type !== 'text' || !text.value.startsWith('[!')) return + + // Must have `]`. + const end = text.value.indexOf(']') + if (end === -1) return + + // Must be a known name between + const name = text.value.slice(2, end).toLowerCase() + const icon = icons.get(name) + if (!icon) return + + if (end + 1 === text.value.length) { + // Has to be a `
`. + const next = head.children[1] + + if (next) { + if (next.type !== 'element' || next.tagName !== 'br') return + // No next sibling? Exit too. + if (!head.children[2]) return + // Drop the entire `[!note]` text *and* the `
`. + head.children = head.children.slice(2) + // Drop the `\n` that typically (in markdown) follows `
`. + const node = head.children[0] + if (node && node.type === 'text' && node.value.charAt(0) === '\n') { + node.value = node.value.slice(1) + } + } else { + let skipped = false + + // Skip past whitespace. + while ( + headIndex + 1 < node.children.length && + whitespace(node.children[headIndex + 1]) + ) { + skipped = true + headIndex++ + } + + // Exit if there’s no following element. + if ( + headIndex + 1 === node.children.length || + node.children[headIndex + 1].type !== 'element' + ) { + return + } + + // Without whitespace, + // we still want to skip the paragraph to drop the `[!note]`. + if (!skipped) headIndex++ + } + } else if ( + text.value.charAt(end + 1) === '\n' && + (end + 2 === text.value.length || + !whitespace(text.value.slice(end + 2))) + ) { + // Drop the `[!note]` from the `text`. + text.value = text.value.slice(end + 2) + } else { + return + } + + const displayName = name.charAt(0).toUpperCase() + name.slice(1) + + parent.children[index] = { + type: 'element', + tagName: 'div', + properties: {className: ['markdown-alert', 'markdown-alert-' + name]}, + children: [ + { + type: 'element', + tagName: 'p', + properties: {className: ['markdown-alert-title']}, + children: [ + { + type: 'element', + tagName: 'svg', + properties: { + className: ['octicon', icon.name, 'mr-2'], + viewBox: '0 0 16 16', + version: '1.1', + width: '16', + height: '16', + ariaHidden: 'true' + }, + children: [ + { + type: 'element', + tagName: 'path', + properties: {d: icon.d}, + children: [] + } + ] + }, + {type: 'text', value: displayName} + ] + }, + ...node.children.slice(headIndex) + ] + } + }) + } +} diff --git a/packages/alert/license b/packages/alert/license new file mode 100644 index 0000000..bc8f165 --- /dev/null +++ b/packages/alert/license @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) Titus Wormer + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/alert/package.json b/packages/alert/package.json new file mode 100644 index 0000000..bd3691b --- /dev/null +++ b/packages/alert/package.json @@ -0,0 +1,65 @@ +{ + "author": "Titus Wormer (https://wooorm.com)", + "bugs": "https://github.com/rehypejs/rehype-github/issues", + "contributors": [ + "Titus Wormer (https://wooorm.com)" + ], + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "description": "rehype plugin to enhance alerts", + "exports": "./index.js", + "files": [ + "index.d.ts.map", + "index.d.ts", + "index.js", + "lib/" + ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "keywords": [], + "license": "MIT", + "name": "rehype-github-alert", + "prettier": { + "bracketSpacing": false, + "singleQuote": true, + "semi": false, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false + }, + "private": true, + "repository": "rehypejs/rehype-github", + "scripts": { + "test-api": "node --conditions development test/index.js", + "test-coverage": "c8 --100 --reporter lcov -- npm run test-api" + }, + "sideEffects": false, + "typeCoverage": { + "atLeast": 100, + "strict": true + }, + "type": "module", + "version": "0.0.0", + "xo": { + "overrides": [ + { + "files": [ + "**/test/**/*.js" + ], + "rules": { + "no-await-in-loop": "off" + } + } + ], + "prettier": true, + "rules": { + "complexity": "off", + "unicorn/prefer-string-replace-all": "off" + } + } +} diff --git a/packages/alert/readme.md b/packages/alert/readme.md new file mode 100644 index 0000000..18a8a48 --- /dev/null +++ b/packages/alert/readme.md @@ -0,0 +1,266 @@ +# rehype-github-alert + +[![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] + +**[rehype][]** plugin to support alerts like on GitHub. + +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`rehypeGithubAlert()`](#rehypegithubalert) +* [Bugs](#bugs) +* [Authoring](#authoring) +* [HTML](#html) +* [CSS](#css) +* [Syntax](#syntax) +* [Types](#types) +* [Compatibility](#compatibility) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [Notice](#notice) +* [License](#license) + +## What is this? + +This plugin enhances turns the format that `github.com` uses for alerts +into actual alerts. +It turns blockquotes that start with a specific paragraph (such as `[!note]`) +into `div`s with a particular class: + +> \[!note] +> +> This is a note! + +This plugin is part of a monorepo `rehype-github`. +See its readme for more info. + +## When should I use this? + +You can use this plugin when you want to match how github.com works. +For your own pipelines, +similar to github.com, +I would recommend using directives instead. +See [`remark-directive`][remark-directive] for more info. + +## Install + +This package is [ESM only][esm]. +In Node.js (version 16+), +install with [npm][]: + +```sh +npm install rehype-github-alert +``` + +In Deno with [`esm.sh`][esmsh]: + +```js +import rehypeGithubToDo from 'https://esm.sh/rehype-github-alert@0' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + +``` + +## Use + +Say our module `example.js` looks as follows: + +```js +import rehypeGithubAlert from 'rehype-github-alert' +import rehypeParse from 'rehype-parse' +import rehypeStringify from 'rehype-stringify' +import {unified} from 'unified' + +const file = await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeGithubAlert) + .use(rehypeStringify) + .process('

[!note]

hi

') + +console.log(String(file)) +``` + +…now running `node example.js` yields: + +```html +

Note

hi

+``` + +## API + +This package exports no identifiers. +The default export is +[`rehypeGithubAlert`][api-rehype-github-alert]. + +### `rehypeGithubAlert()` + +Plugin to enhance alerts. + +###### Parameters + +There are no parameters. + +###### Returns + +Transform (`(tree: Root, file: VFile) => Promise`). + +## Bugs + +There are just many oddities with how this all works. +If you can, +I would recommend using [`remark-directive`][remark-directive] instead. + +For a combination of the two, +see +[`remark-github-admonitions-to-directives`][remark-github-admonitions-etc]. + +## Authoring + +You do not need to use screaming uppercase: +`[!note]` is fine! + +## HTML + +The markup for that github.com adds is: + +```html +

Note

hi

+``` + +Each different label (such as `tip`) is turned into a different class +(`tip` turns into `markdown-alert-tip`). +They each also get a different icon (`tip` gets `octicon-light-bulb`). + +For different HTML, +see +[`rehype-github-alerts`][rehype-github-alerts]. + +## CSS + +See [`github-markdown-css`][github-markdown-css] for the CSS that GitHub uses. +Relevant styles are under `.markdown-alert` and `.octicon`. + +## Syntax + +No syntax is applicable. + +## Types + +This package is fully typed with [TypeScript][]. +It exports no additional types. + +## Compatibility + +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, +that is Node.js 16+. +Our projects sometimes work with older versions, +but this is not guaranteed. + +This plugin works with `rehype-parse` version 3+, +`rehype-stringify` version 3+, +`rehype` version 5+, +and `unified` version 6+. + +## Security + +This package is safe. + +## Related + +* [`remark-gfm`](https://github.com/remarkjs/remark-gfm) + — support GFM in remark + +## Contribute + +See [`contributing.md` in `rehypejs/.github`][contributing] 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. + +## Notice + +This project is not affiliated with **GitHub**. + +## License + +[MIT][license] © [Titus Wormer][author] + + + +[build-badge]: https://github.com/rehypejs/rehype-github/workflows/main/badge.svg + +[build]: https://github.com/rehypejs/rehype-github/actions + +[coverage-badge]: https://img.shields.io/codecov/c/github/rehypejs/rehype-github.svg + +[coverage]: https://codecov.io/github/rehypejs/rehype-github + +[downloads-badge]: https://img.shields.io/npm/dm/rehype-github-color.svg + +[downloads]: https://www.npmjs.com/package/rehype-github-color + +[size-badge]: https://img.shields.io/bundlephobia/minzip/rehype-github-color.svg + +[size]: https://bundlephobia.com/result?p=rehype-github-color + +[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/rehypejs/rehype/discussions + +[npm]: https://docs.npmjs.com/cli/install + +[esmsh]: https://esm.sh + +[license]: ../../license + +[author]: https://wooorm.com + +[contributing]: https://github.com/rehypejs/.github/blob/main/contributing.md + +[support]: https://github.com/rehypejs/.github/blob/main/support.md + +[coc]: https://github.com/rehypejs/.github/blob/main/code-of-conduct.md + +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[typescript]: https://www.typescriptlang.org + +[github-markdown-css]: https://github.com/sindresorhus/github-markdown-css + +[rehype-github-alerts]: https://github.com/chrisweb/rehype-github-alerts + +[rehype]: https://github.com/rehypjs/rehype + +[remark-directive]: https://github.com/remarkjs/remark-directive + +[remark-github-admonitions-etc]: https://github.com/incentro-ecx/remark-github-admonitions-to-directives + +[api-rehype-github-alert]: #rehypegithubalert diff --git a/packages/alert/test/fixtures/basic.html b/packages/alert/test/fixtures/basic.html new file mode 100644 index 0000000..3bc588a --- /dev/null +++ b/packages/alert/test/fixtures/basic.html @@ -0,0 +1,41 @@ +

Simple alerts

+

Note

This is a note.

+
+

Tip

This is a tip. (Supported since 14 Nov 2023)

+
+

Important

Crutial information comes here.

+
+

Caution

Negative potential consequences of an action. (Supported since 14 Nov 2023)

+
+

Warning

Critical content comes here.

+
+
+

[!NOTE +No closing square bracket.

+
+
+

!NOTE] +No opening bracket.

+
+
+

[NOTE] +No exclamation mark.

+
+
+

![NOTE] +Switched exclamation mark and square bracket.

+
+

Note

Lower note.

+
+

Note

Capitalized note.

+
+

Note

Chaos-case note.

+
+
+

[!UNKNOWN] +Critical content comes here.

+
+

Note

Two?

+

[!NOTE] +Notes?

+
diff --git a/packages/alert/test/fixtures/basic.md b/packages/alert/test/fixtures/basic.md new file mode 100644 index 0000000..03965d2 --- /dev/null +++ b/packages/alert/test/fixtures/basic.md @@ -0,0 +1,46 @@ +## Simple alerts + +> [!NOTE] +> This is a note. + +> [!TIP] +> This is a tip. (Supported since 14 Nov 2023) + +> [!IMPORTANT] +> Crutial information comes here. + +> [!CAUTION] +> Negative potential consequences of an action. (Supported since 14 Nov 2023) + +> [!WARNING] +> Critical content comes here. + +> [!NOTE +> No closing square bracket. + +> !NOTE] +> No opening bracket. + +> [NOTE] +> No exclamation mark. + +> ![NOTE] +> Switched exclamation mark and square bracket. + +> [!note] +> Lower note. + +> [!Note] +> Capitalized note. + +> [!nOtE] +> Chaos-case note. + +> [!UNKNOWN] +> Critical content comes here. + +> [!NOTE] +> Two? +> +> [!NOTE] +> Notes? diff --git a/packages/alert/test/fixtures/block-quote.html b/packages/alert/test/fixtures/block-quote.html new file mode 100644 index 0000000..fb7b0b1 --- /dev/null +++ b/packages/alert/test/fixtures/block-quote.html @@ -0,0 +1,81 @@ +

Block quote

+

alerts in quotes (broken since 14 Nov 2023)

+
+

Alpha. +[!NOTE] +Bravo.

+
+
+

Charlie.
+[!NOTE] +Delta.

+
+
+

Echo.
+[!NOTE] +Foxtrot.

+
+
+

Golf.

+

[!NOTE] +Hotel.

+
+
+

India. +[!NOTE]
Juliett.

+
+
+

Kilo.

+
+

[!NOTE] +Lima.

+
+
+
+

Mike.

+
+

[!NOTE] +November.

+
+
+
+

Oscar.

+
+

[!NOTE] +Papa.

+
+
+

quotes in alerts

+

Note

+
+

Romeo. +Sierra.

+
+
+

Note

+
+

Tango.

+
+
+

Note

+
+

Uniform.

+
+

Victor.

+
+
+
+

Note

+
+
+

Whiskey.

+
+
+
+

Note

+
+
+

X-ray.

+
+
+
diff --git a/packages/alert/test/fixtures/block-quote.md b/packages/alert/test/fixtures/block-quote.md new file mode 100644 index 0000000..ec5728c --- /dev/null +++ b/packages/alert/test/fixtures/block-quote.md @@ -0,0 +1,56 @@ +## Block quote + +### alerts in quotes (broken since 14 Nov 2023) + +> Alpha. +> [!NOTE] +> Bravo. + +> Charlie.\ +> [!NOTE] +> Delta. + +> Echo.
+> [!NOTE] +> Foxtrot. + +> Golf. +> +> [!NOTE] +> Hotel. + +> India. +> [!NOTE]
Juliett. + +> Kilo. +> > [!NOTE] +> > Lima. + +> Mike. +> +> > [!NOTE] +> > November. + +> Oscar. +> > +> > [!NOTE] +> > Papa. + +### quotes in alerts + +> [!NOTE] +> > Romeo. +> > Sierra. + +> [!NOTE] +> > Tango. + +> [!NOTE] +> > Uniform. +> > > Victor. + +> [!NOTE] +> > > Whiskey. + +> [!NOTE] +> > > X-ray. diff --git a/packages/alert/test/fixtures/break-html.html b/packages/alert/test/fixtures/break-html.html new file mode 100644 index 0000000..ae82ae8 --- /dev/null +++ b/packages/alert/test/fixtures/break-html.html @@ -0,0 +1,24 @@ +

You can write one-liner alerts using <br>.

+

Note

Alpha.

+
+

Broken since 14 Nov 2023: no content after <br>

+
+

[!NOTE]

+
+

Note


+
+

Note

Bravo.

+
+

Note

Charlie. +
Delta

+
+

Note


Echo

+
+

Note

Foxtrot.

Golf.

+
+

Note

Hotel.
India.

+
+

Note


Juliett.

+
+

Note

Kilo.

+
diff --git a/packages/alert/test/fixtures/break-html.md b/packages/alert/test/fixtures/break-html.md new file mode 100644 index 0000000..868164b --- /dev/null +++ b/packages/alert/test/fixtures/break-html.md @@ -0,0 +1,27 @@ +You can write one-liner alerts using `
`. + +> [!NOTE]
Alpha. + +Broken since 14 Nov 2023: no content after `
` + +> [!NOTE]
+ +> [!NOTE]

+ +> [!NOTE]
+> Bravo. + +> [!NOTE] +> Charlie. +>
Delta + +> [!NOTE] +>
Echo + +> [!NOTE]
Foxtrot.

Golf. + +> [!NOTE]
Hotel.
India. + +> [!NOTE]

Juliett. + +> [!NOTE]
Kilo. diff --git a/packages/alert/test/fixtures/break-markdown.html b/packages/alert/test/fixtures/break-markdown.html new file mode 100644 index 0000000..76ffeae --- /dev/null +++ b/packages/alert/test/fixtures/break-markdown.html @@ -0,0 +1,41 @@ +

Note

Alpha.

+
+

Note

Bravo +charlie.

+
+

Note

Delta.
+Echo.

+
+
+

[!NOTE] Foxtrot. +Golf.

+
+
+

[!NOTE] Hotel.
+India.

+
+

Juliett.

+
+

[!NOTE]

+
+

Kilo.

+

Note

Lima.

+
+

Note

Mike.

+
+
+

[!NOTE] November. +Oscar.

+
+
+

[!NOTE] Papa. +Québec.

+
+
+

[!NOTE] Romeo +Sierra.

+
+
+

[!NOTE] Tango. +Uniform.

+
diff --git a/packages/alert/test/fixtures/break-markdown.md b/packages/alert/test/fixtures/break-markdown.md new file mode 100644 index 0000000..977cd49 --- /dev/null +++ b/packages/alert/test/fixtures/break-markdown.md @@ -0,0 +1,41 @@ +> [!NOTE]\ +> Alpha. + +> [!NOTE] +> Bravo +> charlie. + +> [!NOTE] +> Delta.\ +> Echo. + +> [!NOTE] Foxtrot. +> Golf. + +> [!NOTE] Hotel.\ +> India. + +Juliett. + +> [!NOTE] +> + +Kilo. + +> [!NOTE]␠ +> Lima. + +> [!NOTE]␠␠ +> Mike. + +> [!NOTE]␠November. +> Oscar. + +> [!NOTE]␠␠Papa. +> Québec. + +> [!NOTE]␠␠Romeo +> ␠␠Sierra. + +> [!NOTE] Tango. +> Uniform. diff --git a/packages/alert/test/fixtures/code.html b/packages/alert/test/fixtures/code.html new file mode 100644 index 0000000..a9a88ef --- /dev/null +++ b/packages/alert/test/fixtures/code.html @@ -0,0 +1,20 @@ +

code block

+

Alpha.

+

Note

+
fn main () {
+  println!("Hello, World!");
+}
+
+

Bravo.

+

Note

Charlie.

+
fn main () {
+  println!("Hello, World!");
+}
+
+

Delta.

+

Note

Echo.

+
fn main () {
+  println!("Hello, World!");
+}
+
+
diff --git a/packages/alert/test/fixtures/code.md b/packages/alert/test/fixtures/code.md new file mode 100644 index 0000000..7f9a932 --- /dev/null +++ b/packages/alert/test/fixtures/code.md @@ -0,0 +1,33 @@ +## code block + +Alpha. + +> [!NOTE] +> +> ```rs +> fn main () { +> println!("Hello, World!"); +> } +> ``` + +Bravo. + +> [!NOTE] +> Charlie. +> +> ```rs +> fn main () { +> println!("Hello, World!"); +> } +> ``` + +Delta. + +> [!NOTE] +> Echo. +> +> ``` +> fn main () { +> println!("Hello, World!"); +> } +> ``` diff --git a/packages/alert/test/fixtures/heading.html b/packages/alert/test/fixtures/heading.html new file mode 100644 index 0000000..ff5f081 --- /dev/null +++ b/packages/alert/test/fixtures/heading.html @@ -0,0 +1,18 @@ +

ATX headings

+

Note

Markdown headings in the block quote.

+

#

+

##

+

###

+

####

+
#####
+
######
+
+

HTML tags

+

Note

HTML headings in the block quote.

+

h1

+

h2

+

h3

+

h4

+
h5
+
h6
+
diff --git a/packages/alert/test/fixtures/heading.md b/packages/alert/test/fixtures/heading.md new file mode 100644 index 0000000..725c435 --- /dev/null +++ b/packages/alert/test/fixtures/heading.md @@ -0,0 +1,23 @@ +## ATX headings + +> [!NOTE] +> Markdown headings in the block quote. +> +> # `# ` +> ## `##` +> ### `###` +> #### `####` +> ##### `#####` +> ###### `######` + +HTML tags + +> [!NOTE] +> HTML headings in the block quote. +> +>

h1

+>

h2

+>

h3

+>

h4

+>
h5
+>
h6
diff --git a/packages/alert/test/fixtures/html-div.html b/packages/alert/test/fixtures/html-div.html new file mode 100644 index 0000000..7fa17f4 --- /dev/null +++ b/packages/alert/test/fixtures/html-div.html @@ -0,0 +1,23 @@ +

<div>

+

Note

+
Alpha.
+
+

Note

Bravo.

+
Charlie.
+
+

Note

Delta.

+
Echo.
+
+
+
+

[!NOTE] +Foxtrot.

+
+
+
+
+

[!NOTE] +Golf.

+
Hote.
+
+
diff --git a/packages/alert/test/fixtures/html-div.md b/packages/alert/test/fixtures/html-div.md new file mode 100644 index 0000000..e5c771c --- /dev/null +++ b/packages/alert/test/fixtures/html-div.md @@ -0,0 +1,29 @@ +## `
` + +> [!NOTE] +>
Alpha.
+ +> [!NOTE] +> Bravo. +>
Charlie.
+ +> [!NOTE] +> Delta. +> +>
Echo.
+ +
+ +> [!NOTE] +> Foxtrot. + +
+ +
+ +> [!NOTE] +> Golf. +> +>
Hote.
+ +
diff --git a/packages/alert/test/fixtures/html-hr.html b/packages/alert/test/fixtures/html-hr.html new file mode 100644 index 0000000..1835ed0 --- /dev/null +++ b/packages/alert/test/fixtures/html-hr.html @@ -0,0 +1,17 @@ +

<hr>

+

Alpha.

+

Note

+
+
+

Bravo.

+

Note

+
+Charlie +
+

Note

Delta.

+
+
+

Note

Echo.

+
+Foxtrot +
diff --git a/packages/alert/test/fixtures/html-hr.md b/packages/alert/test/fixtures/html-hr.md new file mode 100644 index 0000000..c26a10f --- /dev/null +++ b/packages/alert/test/fixtures/html-hr.md @@ -0,0 +1,21 @@ +## `
` + +Alpha. + +> [!NOTE] +>
+ +Bravo. + +> [!NOTE] +>
+> Charlie + +> [!NOTE] +> Delta. +>
+ +> [!NOTE] +> Echo. +>
+> Foxtrot diff --git a/packages/alert/test/fixtures/list.html b/packages/alert/test/fixtures/list.html new file mode 100644 index 0000000..92e417d --- /dev/null +++ b/packages/alert/test/fixtures/list.html @@ -0,0 +1,59 @@ +

List

+

Alert in list

+
    +
  • +
    +

    [!NOTE] +Alpha.

    +
    +
  • +
+
+
    +
  • +

    Bravo.

    +
    +

    [!NOTE] +Charlie.

    +
    +
  • +
+
+
    +
  • +

    Delta.

    +
    +

    [!NOTE] +Echo.

    +
    +
      +
    • +

      Foxtrot.

      +
      +

      [!NOTE] +Golf.

      +
      +
    • +
    +
  • +
+

Lists in alert

+

Note

+
    +
  • Alpha.
  • +
+
+

Note

Bravo.

+
    +
  • Charlie.
  • +
+
+

Note

+
    +
  • Delta. +
      +
    • Echo.
    • +
    +
  • +
+
diff --git a/packages/alert/test/fixtures/list.md b/packages/alert/test/fixtures/list.md new file mode 100644 index 0000000..97da0b3 --- /dev/null +++ b/packages/alert/test/fixtures/list.md @@ -0,0 +1,39 @@ +## List + +### Alert in list + +* > [!NOTE] + > Alpha. + +*** + +* Bravo. + + > [!NOTE] + > Charlie. + +*** + +* Delta. + + > [!NOTE] + > Echo. + + * Foxtrot. + + > [!NOTE] + > Golf. + +### Lists in alert + +> [!NOTE] +> * Alpha. + +> [!NOTE] +> Bravo. +> +> * Charlie. + +> [!NOTE] +> * Delta. +> * Echo. diff --git a/packages/alert/test/fixtures/nesting.html b/packages/alert/test/fixtures/nesting.html new file mode 100644 index 0000000..8374cbb --- /dev/null +++ b/packages/alert/test/fixtures/nesting.html @@ -0,0 +1,13 @@ +

Nested alerts

+

Note

Alpha.

+
+

[!NOTE] +Bravo.

+
+
+

Note

+
+

[!NOTE] +Charlie.

+
+
diff --git a/packages/alert/test/fixtures/nesting.md b/packages/alert/test/fixtures/nesting.md new file mode 100644 index 0000000..89e8494 --- /dev/null +++ b/packages/alert/test/fixtures/nesting.md @@ -0,0 +1,12 @@ +## Nested alerts + +> [!NOTE] +> Alpha. +> +> > [!NOTE] +> > Bravo. + +> [!NOTE] +> +> > [!NOTE] +> > Charlie. diff --git a/packages/alert/test/fixtures/tags-around-break.html b/packages/alert/test/fixtures/tags-around-break.html new file mode 100644 index 0000000..766884c --- /dev/null +++ b/packages/alert/test/fixtures/tags-around-break.html @@ -0,0 +1,7 @@ +

Note

Alpha

+

[!note]Bravo

+

[!note] +Charlie

+

Note

Delta

+

[!note]
Echo

+

Note

Foxtrot

diff --git a/packages/alert/test/fixtures/tags-around-break.md b/packages/alert/test/fixtures/tags-around-break.md new file mode 100644 index 0000000..ab31032 --- /dev/null +++ b/packages/alert/test/fixtures/tags-around-break.md @@ -0,0 +1,13 @@ +

[!note]
Alpha

+ +

[!note]Bravo

+ +

[!note] +Charlie

+ +

[!note] +Delta

+ +

[!note]
Echo

+ +

[!note]
Foxtrot

diff --git a/packages/alert/test/fixtures/thematic-break-and-heading.html b/packages/alert/test/fixtures/thematic-break-and-heading.html new file mode 100644 index 0000000..fd3f8a7 --- /dev/null +++ b/packages/alert/test/fixtures/thematic-break-and-heading.html @@ -0,0 +1,93 @@ +

Thematic break and heading (setext)

+

Alpha -.

+
+

[!NOTE]

+
+

Alpha =.

+
+

[!NOTE]

+
+

Alpha *.

+

Note

+
+
+

Bravo -.

+

Note

+
+
+

Bravo =.

+

Note

+

===

+
+

Bravo *.

+

Note

+
+
+
+

[!NOTE] +Charlie -.

+
+
+

[!NOTE] +Charlie =.

+
+

Note

Charlie *.

+
+
+
+

[!NOTE]

+

Delta -.

+
+
+

[!NOTE]

+

Delta =.

+
+

Note

+
+

Delta *.

+
+

Note

+

Echo -.

+
+

Note

+

Echo =.

+
+

Note

+

Echo *.

+
+
+

Note

+
+

Foxtrot -.

+
+

Note

+

=== +Foxtrot =.

+
+

Note

+
+

Foxtrot *.

+
+

Note

Hotel -.

+
+
+

Note

Hotel =

+

===

+
+

Note

Hotel *

+
+
+
+

[!NOTE] +India -

+

Juliett -

+
+

Note

India *

+
+

Juliett *

+
+
+

[!NOTE] +India =

+

Juliett =

+
diff --git a/packages/alert/test/fixtures/thematic-break-and-heading.md b/packages/alert/test/fixtures/thematic-break-and-heading.md new file mode 100644 index 0000000..1307a39 --- /dev/null +++ b/packages/alert/test/fixtures/thematic-break-and-heading.md @@ -0,0 +1,118 @@ +## Thematic break and heading (setext) + +Alpha `-`. + +> [!NOTE] +> --- + +Alpha `=`. + +> [!NOTE] +> === + +Alpha `*`. + +> [!NOTE] +> *** + +Bravo `-`. + +> [!NOTE] +> +> --- + +Bravo `=`. + +> [!NOTE] +> +> === + +Bravo `*`. + +> [!NOTE] +> +> *** + +> [!NOTE] +> Charlie `-`. +> --- + +> [!NOTE] +> Charlie `=`. +> === + +> [!NOTE] +> Charlie `*`. +> *** + +> [!NOTE] +> --- +> Delta `-`. + +> [!NOTE] +> === +> Delta `=`. + +> [!NOTE] +> *** +> Delta `*`. + +> [!NOTE] +> +> Echo `-`. +> --- + +> [!NOTE] +> +> Echo `=`. +> === + +> [!NOTE] +> +> Echo `*`. +> *** + +> [!NOTE] +> +> --- +> Foxtrot `-`. + +> [!NOTE] +> +> === +> Foxtrot `=`. + +> [!NOTE] +> +> *** +> Foxtrot `*`. + +> [!NOTE] +> Hotel `-`. +> +> --- + +> [!NOTE] +> Hotel `=` +> +> === + +> [!NOTE] +> Hotel `*` +> +> *** + +> [!NOTE] +> India `-` +> --- +> Juliett `-` + +> [!NOTE] +> India `*` +> *** +> Juliett `*` + +> [!NOTE] +> India `=` +> === +> Juliett `=` diff --git a/packages/alert/test/fixtures/whitespace-and-tags.html b/packages/alert/test/fixtures/whitespace-and-tags.html new file mode 100644 index 0000000..d56f030 --- /dev/null +++ b/packages/alert/test/fixtures/whitespace-and-tags.html @@ -0,0 +1,5 @@ +

Note

Alpha

+

[!note]

Bravo

+

Note

Charlie

+

[!note]

Delta
+
[!note]

Echo

diff --git a/packages/alert/test/fixtures/whitespace-and-tags.md b/packages/alert/test/fixtures/whitespace-and-tags.md new file mode 100644 index 0000000..aad960b --- /dev/null +++ b/packages/alert/test/fixtures/whitespace-and-tags.md @@ -0,0 +1,9 @@ +

[!note]

Alpha

+ +

[!note]

Bravo

+ +

[!note]

Charlie

+ +

[!note]

Delta
+ +
[!note]

Echo

diff --git a/packages/alert/test/index.js b/packages/alert/test/index.js new file mode 100644 index 0000000..85a7575 --- /dev/null +++ b/packages/alert/test/index.js @@ -0,0 +1,81 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import test from 'node:test' +import {controlPictures} from 'control-pictures' +import {createGfmFixtures} from 'create-gfm-fixtures' +import rehypeGithubAlert from 'rehype-github-alert' +import rehypeParse from 'rehype-parse' +import rehypeRaw from 'rehype-raw' +import rehypeStringify from 'rehype-stringify' +import remarkParse from 'remark-parse' +import remarkGfm from 'remark-gfm' +import remarkRehype from 'remark-rehype' +import {unified} from 'unified' + +test('rehypeGithubSanitizeSchema', async function (t) { + await t.test('should expose the public api', async function () { + assert.deepEqual(Object.keys(await import('rehype-github-alert')).sort(), [ + 'default' + ]) + }) + + await t.test('should clean', async function () { + assert.equal( + String( + await unified() + .use(rehypeParse, {fragment: true}) + .use(rehypeGithubAlert) + .use(rehypeStringify) + .process('

[!note]

hi

') + ), + '

Note

hi

' + ) + }) +}) + +test('fixtures', async function (t) { + const base = new URL('fixtures/', import.meta.url) + + await createGfmFixtures(base, {controlPictures: true}) + + const processor = unified() + .use(remarkParse) + .use(remarkGfm) + .use(remarkRehype, {allowDangerousHtml: true}) + .use(rehypeRaw, {tagfilter: true}) + .use(rehypeGithubAlert) + // .use(rehypeSanitize, sanitizeSchema) + .use(rehypeStringify) + + const files = await fs.readdir(base) + const extension = '.md' + + for (const d of files) { + if (!d.endsWith(extension)) continue + + const name = d.slice(0, -extension.length) + + await t.test(name, async function () { + const input = await fs.readFile(new URL(name + '.md', base), 'utf8') + let expected = await fs.readFile(new URL(name + '.html', base), 'utf8') + + let actual = String(await processor.process(controlPictures(input))) + + if (name === 'code') { + expected = expected + .replace(/(.+?)<\/span>/g, '$1') + .replace( + /
/g,
+            '
'
+          )
+          .replace(/<\/pre><\/div>/g, '\n
') + } + + if (actual && !/\n$/.test(actual)) { + actual += '\n' + } + + assert.equal(actual, expected) + }) + } +}) diff --git a/packages/alert/tsconfig.json b/packages/alert/tsconfig.json new file mode 100644 index 0000000..b29a7b4 --- /dev/null +++ b/packages/alert/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": {} +} diff --git a/readme.md b/readme.md index d4a241b..4dd2329 100644 --- a/readme.md +++ b/readme.md @@ -53,6 +53,8 @@ install and use each package manually. — turn normal line endings into hard breaks (**comments**) * [`remark-github-yaml-metadata`](packages/yaml-metadata/) — show frontmatter as a table (**files**) +* [`rehype-github-alert`](packages/alert/) + — enhance alerts (**everywhere**) * [`rehype-github-color`](packages/color/) — enhance code for colors (**comments**) * [`rehype-github-dir`](packages/dir/)