mdast-util-gfm-autolink-literal
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -1,16 +0,4 @@ | ||
/** @type {FromMarkdownExtension} */ | ||
export const gfmAutolinkLiteralFromMarkdown: FromMarkdownExtension | ||
/** @type {ToMarkdownExtension} */ | ||
export const gfmAutolinkLiteralToMarkdown: ToMarkdownExtension | ||
export type Link = import('mdast').Link | ||
export type FromMarkdownExtension = import('mdast-util-from-markdown').Extension | ||
export type FromMarkdownTransform = import('mdast-util-from-markdown').Transform | ||
export type FromMarkdownHandle = import('mdast-util-from-markdown').Handle | ||
export type ToMarkdownExtension = | ||
import('mdast-util-to-markdown/lib/types.js').Options | ||
export type ReplaceFunction = | ||
import('mdast-util-find-and-replace').ReplaceFunction | ||
export type RegExpMatchObject = | ||
import('mdast-util-find-and-replace').RegExpMatchObject | ||
export type PhrasingContent = | ||
import('mdast-util-find-and-replace').PhrasingContent | ||
export { | ||
gfmAutolinkLiteralFromMarkdown, | ||
gfmAutolinkLiteralToMarkdown | ||
} from './lib/index.js' |
246
index.js
@@ -1,242 +0,4 @@ | ||
/** | ||
* @typedef {import('mdast').Link} Link | ||
* @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension | ||
* @typedef {import('mdast-util-from-markdown').Transform} FromMarkdownTransform | ||
* @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle | ||
* @typedef {import('mdast-util-to-markdown/lib/types.js').Options} ToMarkdownExtension | ||
* @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction | ||
* @typedef {import('mdast-util-find-and-replace').RegExpMatchObject} RegExpMatchObject | ||
* @typedef {import('mdast-util-find-and-replace').PhrasingContent} PhrasingContent | ||
*/ | ||
import {ccount} from 'ccount' | ||
import {findAndReplace} from 'mdast-util-find-and-replace' | ||
import {unicodePunctuation, unicodeWhitespace} from 'micromark-util-character' | ||
const inConstruct = 'phrasing' | ||
const notInConstruct = ['autolink', 'link', 'image', 'label'] | ||
/** @type {FromMarkdownExtension} */ | ||
export const gfmAutolinkLiteralFromMarkdown = { | ||
transforms: [transformGfmAutolinkLiterals], | ||
enter: { | ||
literalAutolink: enterLiteralAutolink, | ||
literalAutolinkEmail: enterLiteralAutolinkValue, | ||
literalAutolinkHttp: enterLiteralAutolinkValue, | ||
literalAutolinkWww: enterLiteralAutolinkValue | ||
}, | ||
exit: { | ||
literalAutolink: exitLiteralAutolink, | ||
literalAutolinkEmail: exitLiteralAutolinkEmail, | ||
literalAutolinkHttp: exitLiteralAutolinkHttp, | ||
literalAutolinkWww: exitLiteralAutolinkWww | ||
} | ||
} | ||
/** @type {ToMarkdownExtension} */ | ||
export const gfmAutolinkLiteralToMarkdown = { | ||
unsafe: [ | ||
{ | ||
character: '@', | ||
before: '[+\\-.\\w]', | ||
after: '[\\-.\\w]', | ||
inConstruct, | ||
notInConstruct | ||
}, | ||
{ | ||
character: '.', | ||
before: '[Ww]', | ||
after: '[\\-.\\w]', | ||
inConstruct, | ||
notInConstruct | ||
}, | ||
{character: ':', before: '[ps]', after: '\\/', inConstruct, notInConstruct} | ||
] | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterLiteralAutolink(token) { | ||
this.enter({type: 'link', title: null, url: '', children: []}, token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterLiteralAutolinkValue(token) { | ||
this.config.enter.autolinkProtocol.call(this, token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitLiteralAutolinkHttp(token) { | ||
this.config.exit.autolinkProtocol.call(this, token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitLiteralAutolinkWww(token) { | ||
this.config.exit.data.call(this, token) | ||
const node = /** @type {Link} */ (this.stack[this.stack.length - 1]) | ||
node.url = 'http://' + this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitLiteralAutolinkEmail(token) { | ||
this.config.exit.autolinkEmail.call(this, token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitLiteralAutolink(token) { | ||
this.exit(token) | ||
} | ||
/** @type {FromMarkdownTransform} */ | ||
function transformGfmAutolinkLiterals(tree) { | ||
findAndReplace( | ||
tree, | ||
[ | ||
[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi, findUrl], | ||
[/([-.\w+]+)@([-\w]+(?:\.[-\w]+)+)/g, findEmail] | ||
], | ||
{ignore: ['link', 'linkReference']} | ||
) | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} _ | ||
* @param {string} protocol | ||
* @param {string} domain | ||
* @param {string} path | ||
* @param {RegExpMatchObject} match | ||
*/ | ||
// eslint-disable-next-line max-params | ||
function findUrl(_, protocol, domain, path, match) { | ||
let prefix = '' | ||
// Not an expected previous character. | ||
if (!previous(match)) { | ||
return false | ||
} | ||
// Treat `www` as part of the domain. | ||
if (/^w/i.test(protocol)) { | ||
domain = protocol + domain | ||
protocol = '' | ||
prefix = 'http://' | ||
} | ||
if (!isCorrectDomain(domain)) { | ||
return false | ||
} | ||
const parts = splitUrl(domain + path) | ||
if (!parts[0]) return false | ||
/** @type {PhrasingContent} */ | ||
const result = { | ||
type: 'link', | ||
title: null, | ||
url: prefix + protocol + parts[0], | ||
children: [{type: 'text', value: protocol + parts[0]}] | ||
} | ||
if (parts[1]) { | ||
return [result, {type: 'text', value: parts[1]}] | ||
} | ||
return result | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} _ | ||
* @param {string} atext | ||
* @param {string} label | ||
* @param {RegExpMatchObject} match | ||
*/ | ||
function findEmail(_, atext, label, match) { | ||
if ( | ||
// Not an expected previous character. | ||
!previous(match, true) || | ||
// Label ends in not allowed character. | ||
/[_-\d]$/.test(label) | ||
) { | ||
return false | ||
} | ||
return { | ||
type: 'link', | ||
title: null, | ||
url: 'mailto:' + atext + '@' + label, | ||
children: [{type: 'text', value: atext + '@' + label}] | ||
} | ||
} | ||
/** | ||
* @param {string} domain | ||
* @returns {boolean} | ||
*/ | ||
function isCorrectDomain(domain) { | ||
const parts = domain.split('.') | ||
if ( | ||
parts.length < 2 || | ||
(parts[parts.length - 1] && | ||
(/_/.test(parts[parts.length - 1]) || | ||
!/[a-zA-Z\d]/.test(parts[parts.length - 1]))) || | ||
(parts[parts.length - 2] && | ||
(/_/.test(parts[parts.length - 2]) || | ||
!/[a-zA-Z\d]/.test(parts[parts.length - 2]))) | ||
) { | ||
return false | ||
} | ||
return true | ||
} | ||
/** | ||
* @param {string} url | ||
* @returns {[string, string|undefined]} | ||
*/ | ||
function splitUrl(url) { | ||
const trailExec = /[!"&'),.:;<>?\]}]+$/.exec(url) | ||
/** @type {number} */ | ||
let closingParenIndex | ||
/** @type {number} */ | ||
let openingParens | ||
/** @type {number} */ | ||
let closingParens | ||
/** @type {string|undefined} */ | ||
let trail | ||
if (trailExec) { | ||
url = url.slice(0, trailExec.index) | ||
trail = trailExec[0] | ||
closingParenIndex = trail.indexOf(')') | ||
openingParens = ccount(url, '(') | ||
closingParens = ccount(url, ')') | ||
while (closingParenIndex !== -1 && openingParens > closingParens) { | ||
url += trail.slice(0, closingParenIndex + 1) | ||
trail = trail.slice(closingParenIndex + 1) | ||
closingParenIndex = trail.indexOf(')') | ||
closingParens++ | ||
} | ||
} | ||
return [url, trail] | ||
} | ||
/** | ||
* @param {RegExpMatchObject} match | ||
* @param {boolean} [email=false] | ||
* @returns {boolean} | ||
*/ | ||
function previous(match, email) { | ||
const code = match.input.charCodeAt(match.index - 1) | ||
return ( | ||
(match.index === 0 || | ||
unicodeWhitespace(code) || | ||
unicodePunctuation(code)) && | ||
(!email || code !== 47) | ||
) | ||
} | ||
export { | ||
gfmAutolinkLiteralFromMarkdown, | ||
gfmAutolinkLiteralToMarkdown | ||
} from './lib/index.js' |
{ | ||
"name": "mdast-util-gfm-autolink-literal", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "mdast extension to parse and serialize GFM autolink literals", | ||
@@ -37,2 +37,3 @@ "license": "MIT", | ||
"files": [ | ||
"lib/", | ||
"index.d.ts", | ||
@@ -48,23 +49,22 @@ "index.js" | ||
"devDependencies": { | ||
"@types/tape": "^4.0.0", | ||
"@types/node": "^18.0.0", | ||
"c8": "^7.0.0", | ||
"hast-util-to-html": "^8.0.0", | ||
"mdast-util-from-markdown": "^1.0.0", | ||
"mdast-util-to-hast": "^11.0.0", | ||
"mdast-util-to-hast": "^12.0.0", | ||
"mdast-util-to-markdown": "^1.0.0", | ||
"micromark-extension-gfm-autolink-literal": "^1.0.0", | ||
"prettier": "^2.0.0", | ||
"remark-cli": "^10.0.0", | ||
"remark-cli": "^11.0.0", | ||
"remark-preset-wooorm": "^9.0.0", | ||
"rimraf": "^3.0.0", | ||
"tape": "^5.0.0", | ||
"type-coverage": "^2.0.0", | ||
"typescript": "^4.0.0", | ||
"xo": "^0.44.0" | ||
"xo": "^0.53.0" | ||
}, | ||
"scripts": { | ||
"build": "rimraf \"*.d.ts\" && tsc && type-coverage", | ||
"prepack": "npm run build && npm run format", | ||
"build": "tsc --build --clean && tsc --build && type-coverage", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
"test-api": "node --conditions development test/index.js", | ||
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --conditions development test/index.js", | ||
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", | ||
"test": "npm run build && npm run format && npm run test-coverage" | ||
@@ -81,7 +81,18 @@ }, | ||
"xo": { | ||
"prettier": true | ||
"prettier": true, | ||
"rules": { | ||
"unicorn/prefer-code-point": "off" | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": "test/**/*.js", | ||
"rules": { | ||
"no-await-in-loop": "off" | ||
} | ||
} | ||
] | ||
}, | ||
"remarkConfig": { | ||
"plugins": [ | ||
"preset-wooorm" | ||
"remark-preset-wooorm" | ||
] | ||
@@ -88,0 +99,0 @@ }, |
191
readme.md
@@ -11,20 +11,72 @@ # mdast-util-gfm-autolink-literal | ||
Extension for [`mdast-util-from-markdown`][from-markdown] and/or | ||
[`mdast-util-to-markdown`][to-markdown] to support GitHub flavored markdown | ||
autolink literals in **[mdast][]**. | ||
When parsing (`from-markdown`), must be combined with | ||
[`micromark-extension-gfm-autolink-literal`][extension]. | ||
[mdast][] extensions to parse and serialize [GFM][] autolink literals. | ||
## Contents | ||
* [What is this?](#what-is-this) | ||
* [When to use this](#when-to-use-this) | ||
* [Install](#install) | ||
* [Use](#use) | ||
* [API](#api) | ||
* [`gfmAutolinkLiteralFromMarkdown`](#gfmautolinkliteralfrommarkdown) | ||
* [`gfmAutolinkLiteralToMarkdown`](#gfmautolinkliteraltomarkdown) | ||
* [HTML](#html) | ||
* [Syntax](#syntax) | ||
* [Syntax tree](#syntax-tree) | ||
* [Types](#types) | ||
* [Compatibility](#compatibility) | ||
* [Related](#related) | ||
* [Contribute](#contribute) | ||
* [License](#license) | ||
## What is this? | ||
This package contains two extensions that add support for GFM autolink literals | ||
syntax in markdown to [mdast][]. | ||
These extensions plug into | ||
[`mdast-util-from-markdown`][mdast-util-from-markdown] (to support parsing | ||
GFM autolinks in markdown into a syntax tree) and | ||
[`mdast-util-to-markdown`][mdast-util-to-markdown] (to support serializing | ||
GFM autolinks in syntax trees to markdown). | ||
GitHub employs different algorithms to autolink: one at parse time and one at | ||
transform time (similar to how `@mentions` are done at transform time). | ||
This difference can be observed because character references and escapes are | ||
handled differently. | ||
But also because issues/PRs/comments omit (perhaps by accident?) the second | ||
algorithm for `www.`, `http://`, and `https://` links (but not for email links). | ||
As the corresponding micromark extension | ||
[`micromark-extension-gfm-autolink-literal`][extension] is a syntax extension, | ||
it can only perform the first algorithm. | ||
The tree extension `gfmAutolinkLiteralFromMarkdown` from this package can | ||
perform the second algorithm, and as they are combined, both are done. | ||
## When to use this | ||
Use [`mdast-util-gfm`][mdast-util-gfm] if you want all of GFM. | ||
Use this otherwise. | ||
You can use these extensions when you are working with | ||
`mdast-util-from-markdown` and `mdast-util-to-markdown` already. | ||
When working with `mdast-util-from-markdown`, you must combine this package | ||
with | ||
[`micromark-extension-gfm-autolink-literal`][extension]. | ||
When you don’t need a syntax tree, you can use [`micromark`][micromark] | ||
directly with `micromark-extension-gfm-autolink-literal`. | ||
When you are working with syntax trees and want all of GFM, use | ||
[`mdast-util-gfm`][mdast-util-gfm] instead. | ||
All these packages are used [`remark-gfm`][remark-gfm], which | ||
focusses on making it easier to transform content by abstracting these | ||
internals away. | ||
This utility does not handle how markdown is turned to HTML. | ||
That’s done by [`mdast-util-to-hast`][mdast-util-to-hast]. | ||
## Install | ||
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): | ||
Node 12+ is needed to use it and it must be `import`ed instead of `require`d. | ||
This package is [ESM only][esm]. | ||
In Node.js (version 14.14+ and 16.0+), install with [npm][]: | ||
[npm][]: | ||
```sh | ||
@@ -34,7 +86,28 @@ npm install mdast-util-gfm-autolink-literal | ||
In Deno with [`esm.sh`][esmsh]: | ||
```js | ||
import {gfmAutolinkLiteralFromMarkdown, gfmAutolinkLiteralToMarkdown} from 'https://esm.sh/mdast-util-gfm-autolink-literal@1' | ||
``` | ||
In browsers with [`esm.sh`][esmsh]: | ||
```html | ||
<script type="module"> | ||
import {gfmAutolinkLiteralFromMarkdown, gfmAutolinkLiteralToMarkdown} from 'https://esm.sh/mdast-util-gfm-autolink-literal@1?bundle' | ||
</script> | ||
``` | ||
## Use | ||
Say our module, `example.js`, looks as follows: | ||
Say our document `example.md` contains: | ||
```markdown | ||
www.example.com, https://example.com, and contact@example.com. | ||
``` | ||
…and our module `example.js` looks as follows: | ||
```js | ||
import fs from 'node:fs/promises' | ||
import {fromMarkdown} from 'mdast-util-from-markdown' | ||
@@ -45,3 +118,3 @@ import {toMarkdown} from 'mdast-util-to-markdown' | ||
const doc = 'www.example.com, https://example.com, and contact@example.com.' | ||
const doc = await fs.readFile('example.md') | ||
@@ -60,3 +133,3 @@ const tree = fromMarkdown(doc, { | ||
Now, running `node example` yields: | ||
…now running `node example.js` yields (positional info removed for brevity): | ||
@@ -103,30 +176,62 @@ ```js | ||
This package exports the identifiers | ||
[`gfmAutolinkLiteralFromMarkdown`][api-gfmautolinkliteralfrommarkdown] and | ||
[`gfmAutolinkLiteralToMarkdown`][api-gfmautolinkliteraltomarkdown]. | ||
There is no default export. | ||
### `gfmAutolinkLiteralFromMarkdown` | ||
Extension for [`mdast-util-from-markdown`][mdast-util-from-markdown] to enable | ||
GFM autolink literals ([`FromMarkdownExtension`][frommarkdownextension]). | ||
### `gfmAutolinkLiteralToMarkdown` | ||
Support literal autolinks. | ||
The exports are extensions, respectively | ||
for [`mdast-util-from-markdown`][from-markdown] and | ||
[`mdast-util-to-markdown`][to-markdown]. | ||
Extension for [`mdast-util-to-markdown`][mdast-util-to-markdown] to enable | ||
GFM autolink literals ([`ToMarkdownExtension`][tomarkdownextension]). | ||
## HTML | ||
This utility does not handle how markdown is turned to HTML. | ||
That’s done by [`mdast-util-to-hast`][mdast-util-to-hast]. | ||
## Syntax | ||
See [Syntax in `micromark-extension-gfm-autolink-literal`][syntax]. | ||
## Syntax tree | ||
There are no interfaces added to **[mdast][]** by this utility, as it reuses | ||
the existing **[Link][dfn-link]** interface. | ||
## Types | ||
This package is fully typed with [TypeScript][]. | ||
It does not export additional types. | ||
The `Link` type of the mdast nodes is exposed from `@types/mdast`. | ||
## Compatibility | ||
Projects maintained by the unified collective are compatible with all maintained | ||
versions of Node.js. | ||
As of now, that is Node.js 14.14+ and 16.0+. | ||
Our projects sometimes work with older versions, but this is not guaranteed. | ||
This plugin works with `mdast-util-from-markdown` version 1+ and | ||
`mdast-util-to-markdown` version 1+. | ||
## Related | ||
* [`remarkjs/remark`][remark] | ||
— markdown processor powered by plugins | ||
* [`remarkjs/remark-gfm`][remark-gfm] | ||
— remark plugin to support GFM | ||
* [`micromark/micromark`][micromark] | ||
— the smallest commonmark-compliant markdown parser that exists | ||
* [`syntax-tree/mdast-util-gfm`][mdast-util-gfm] | ||
— same but all of GFM (autolink literals, footnotes, strikethrough, tables, | ||
tasklists) | ||
* [`micromark/micromark-extension-gfm-autolink-literal`][extension] | ||
— micromark extension to parse GFM autolink literals | ||
* [`syntax-tree/mdast-util-from-markdown`][from-markdown] | ||
— mdast parser using `micromark` to create mdast from markdown | ||
* [`syntax-tree/mdast-util-to-markdown`][to-markdown] | ||
— mdast serializer to create markdown from mdast | ||
## Contribute | ||
See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get | ||
started. | ||
See [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for | ||
ways to get started. | ||
See [`support.md`][support] for ways to get help. | ||
@@ -172,2 +277,8 @@ | ||
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c | ||
[esmsh]: https://esm.sh | ||
[typescript]: https://www.typescriptlang.org | ||
[license]: license | ||
@@ -177,2 +288,4 @@ | ||
[health]: https://github.com/syntax-tree/.github | ||
[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md | ||
@@ -188,9 +301,9 @@ | ||
[remark]: https://github.com/remarkjs/remark | ||
[mdast-util-from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown | ||
[remark-gfm]: https://github.com/remarkjs/remark-gfm | ||
[mdast-util-to-markdown]: https://github.com/syntax-tree/mdast-util-to-markdown | ||
[from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown | ||
[mdast-util-to-hast]: https://github.com/syntax-tree/mdast-util-to-hast | ||
[to-markdown]: https://github.com/syntax-tree/mdast-util-to-markdown | ||
[remark-gfm]: https://github.com/remarkjs/remark-gfm | ||
@@ -200,1 +313,15 @@ [micromark]: https://github.com/micromark/micromark | ||
[extension]: https://github.com/micromark/micromark-extension-gfm-autolink-literal | ||
[syntax]: https://github.com/micromark/micromark-extension-gfm-autolink-literal#syntax | ||
[gfm]: https://github.github.com/gfm/ | ||
[dfn-link]: https://github.com/syntax-tree/mdast#link | ||
[frommarkdownextension]: https://github.com/syntax-tree/mdast-util-from-markdown#extension | ||
[tomarkdownextension]: https://github.com/syntax-tree/mdast-util-to-markdown#options | ||
[api-gfmautolinkliteralfrommarkdown]: #gfmautolinkliteralfrommarkdown | ||
[api-gfmautolinkliteraltomarkdown]: #gfmautolinkliteraltomarkdown |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21490
13
7
272
319
1