remark-github
Advanced tools
Comparing version 11.2.0 to 11.2.1
142
index.d.ts
@@ -1,130 +0,12 @@ | ||
/** | ||
* Plugin to enable, disable, and ignore messages. | ||
* | ||
* @type {import('unified').Plugin<[Options?]|void[], Root>} | ||
*/ | ||
export default function remarkGithub( | ||
options?: void | Options | undefined | ||
): | ||
| void | ||
| import('unified').Transformer<import('mdast').Root, import('mdast').Root> | ||
export type Root = import('mdast').Root | ||
export type DefaultBuildUrl = (values: BuildUrlValues) => string | ||
export type BuildUrl = ( | ||
values: BuildUrlValues, | ||
defaultBuildUrl: DefaultBuildUrl | ||
) => string | false | ||
export type BuildUrlValues = | ||
| BuildUrlCommitValues | ||
| BuildUrlCompareValues | ||
| BuildUrlIssueValues | ||
| BuildUrlMentionValues | ||
/** | ||
* Arguments for buildUrl functions for commit hash | ||
*/ | ||
export type BuildUrlCommitValues = { | ||
/** | ||
* The type of special object | ||
*/ | ||
type: 'commit' | ||
/** | ||
* The owner of the repo | ||
*/ | ||
user: string | ||
/** | ||
* The project of the repo | ||
*/ | ||
project: string | ||
/** | ||
* The commit hash value | ||
*/ | ||
hash: string | ||
} | ||
/** | ||
* Arguments for buildUrl functions for commit hash ranges | ||
*/ | ||
export type BuildUrlCompareValues = { | ||
/** | ||
* The type of special object | ||
*/ | ||
type: 'compare' | ||
/** | ||
* The owner of the repo | ||
*/ | ||
user: string | ||
/** | ||
* The project of the repo | ||
*/ | ||
project: string | ||
/** | ||
* The SHA of the range start | ||
*/ | ||
base: string | ||
/** | ||
* The SHA of the range end | ||
*/ | ||
compare: string | ||
} | ||
/** | ||
* Arguments for buildUrl functions for issues | ||
*/ | ||
export type BuildUrlIssueValues = { | ||
/** | ||
* The type of special object | ||
*/ | ||
type: 'issue' | ||
/** | ||
* The owner of the repo | ||
*/ | ||
user: string | ||
/** | ||
* The project of the repo | ||
*/ | ||
project: string | ||
/** | ||
* The parsed issue number | ||
*/ | ||
no: string | ||
} | ||
/** | ||
* Arguments for buildUrl functions for mentions | ||
*/ | ||
export type BuildUrlMentionValues = { | ||
/** | ||
* The type of special object | ||
*/ | ||
type: 'mention' | ||
/** | ||
* The parsed user name | ||
*/ | ||
user: string | ||
} | ||
/** | ||
* The owner and project of the repo | ||
*/ | ||
export type RepositoryInfo = { | ||
/** | ||
* The user/organization name | ||
*/ | ||
user: string | ||
/** | ||
* The project/repo name | ||
*/ | ||
project: string | ||
} | ||
/** | ||
* Configuration. | ||
*/ | ||
export type Options = { | ||
buildUrl?: BuildUrl | undefined | ||
/** | ||
* Wrap mentions in `<strong>`, true by default. | ||
* This makes them render more like how GitHub styles them. | ||
* But GitHub itself uses CSS instead of a strong. | ||
*/ | ||
mentionStrong?: boolean | undefined | ||
/** | ||
* Repository to link against. | ||
*/ | ||
repository?: string | undefined | ||
} | ||
export default remarkGithub | ||
export type Options = import('./lib/index.js').Options | ||
export type BuildUrl = import('./lib/index.js').BuildUrl | ||
export type DefaultBuildUrl = import('./lib/index.js').DefaultBuildUrl | ||
export type BuildUrlValues = import('./lib/index.js').BuildUrlValues | ||
export type BuildUrlCommitValues = import('./lib/index.js').BuildUrlCommitValues | ||
export type BuildUrlCompareValues = | ||
import('./lib/index.js').BuildUrlCompareValues | ||
export type BuildUrlIssueValues = import('./lib/index.js').BuildUrlIssueValues | ||
export type BuildUrlMentionValues = | ||
import('./lib/index.js').BuildUrlMentionValues | ||
import remarkGithub from './lib/index.js' |
497
index.js
/** | ||
* @typedef {import('mdast').Root} Root | ||
* | ||
* @callback DefaultBuildUrl | ||
* @param {BuildUrlValues} values | ||
* @returns {string} | ||
* | ||
* @callback BuildUrl | ||
* @param {BuildUrlValues} values | ||
* @param {DefaultBuildUrl} defaultBuildUrl | ||
* @returns {string|false} | ||
* | ||
* @typedef {BuildUrlCommitValues|BuildUrlCompareValues|BuildUrlIssueValues|BuildUrlMentionValues} BuildUrlValues | ||
* | ||
* @typedef BuildUrlCommitValues | ||
* Arguments for buildUrl functions for commit hash | ||
* @property {'commit'} type The type of special object | ||
* @property {string} user The owner of the repo | ||
* @property {string} project The project of the repo | ||
* @property {string} hash The commit hash value | ||
* | ||
* @typedef BuildUrlCompareValues | ||
* Arguments for buildUrl functions for commit hash ranges | ||
* @property {'compare'} type The type of special object | ||
* @property {string} user The owner of the repo | ||
* @property {string} project The project of the repo | ||
* @property {string} base The SHA of the range start | ||
* @property {string} compare The SHA of the range end | ||
* | ||
* @typedef BuildUrlIssueValues | ||
* Arguments for buildUrl functions for issues | ||
* @property {'issue'} type The type of special object | ||
* @property {string} user The owner of the repo | ||
* @property {string} project The project of the repo | ||
* @property {string} no The parsed issue number | ||
* | ||
* @typedef BuildUrlMentionValues | ||
* Arguments for buildUrl functions for mentions | ||
* @property {'mention'} type The type of special object | ||
* @property {string} user The parsed user name | ||
* | ||
* @typedef RepositoryInfo | ||
* The owner and project of the repo | ||
* @property {string} user The user/organization name | ||
* @property {string} project The project/repo name | ||
* | ||
* @typedef Options | ||
* Configuration. | ||
* @property {BuildUrl} [buildUrl] | ||
* @property {boolean} [mentionStrong=true] | ||
* Wrap mentions in `<strong>`, true by default. | ||
* This makes them render more like how GitHub styles them. | ||
* But GitHub itself uses CSS instead of a strong. | ||
* @property {string} [repository] | ||
* Repository to link against. | ||
* @typedef {import('./lib/index.js').Options} Options | ||
* @typedef {import('./lib/index.js').BuildUrl} BuildUrl | ||
* @typedef {import('./lib/index.js').DefaultBuildUrl} DefaultBuildUrl | ||
* @typedef {import('./lib/index.js').BuildUrlValues} BuildUrlValues | ||
* @typedef {import('./lib/index.js').BuildUrlCommitValues} BuildUrlCommitValues | ||
* @typedef {import('./lib/index.js').BuildUrlCompareValues} BuildUrlCompareValues | ||
* @typedef {import('./lib/index.js').BuildUrlIssueValues} BuildUrlIssueValues | ||
* @typedef {import('./lib/index.js').BuildUrlMentionValues} BuildUrlMentionValues | ||
*/ | ||
import fs from 'fs' | ||
import process from 'process' | ||
import path from 'path' | ||
import {visit} from 'unist-util-visit' | ||
import {toString} from 'mdast-util-to-string' | ||
import {findAndReplace} from 'mdast-util-find-and-replace' | ||
import remarkGithub from './lib/index.js' | ||
// Previously, GitHub linked `@mention` and `@mentions` to their blog post about | ||
// mentions (<https://github.com/blog/821>). | ||
// Since June 2019, and possibly earlier, they stopped linking those references. | ||
const denyMention = new Set(['mention', 'mentions']) | ||
// Denylist of SHAs that are also valid words. | ||
// | ||
// GitHub allows abbreviating SHAs up to 7 characters. | ||
// These cases are ignored in text because they might just be ment as normal | ||
// words. | ||
// If you’d like these to link to their SHAs, use more than 7 characters. | ||
// | ||
// Generated by: | ||
// | ||
// ```sh | ||
// egrep -i "^[a-f0-9]{7,}$" /usr/share/dict/words | ||
// ``` | ||
// | ||
// Added a couple forms of 6 character words in GH-20: | ||
// <https://github.com/remarkjs/remark-github/issues/20>. | ||
const denyHash = new Set([ | ||
'acceded', | ||
'deedeed', | ||
'defaced', | ||
'effaced', | ||
'fabaceae' | ||
]) | ||
// Constants. | ||
const minShaLength = 7 | ||
// Username may only contain alphanumeric characters or single hyphens, and | ||
// cannot begin or end with a hyphen*. | ||
// | ||
// \* That is: until <https://github.com/remarkjs/remark-github/issues/13>. | ||
const userGroup = '[\\da-z][-\\da-z]{0,38}' | ||
const projectGroup = '(?:\\.git[\\w-]|\\.(?!git)|[\\w-])+' | ||
const repoGroup = '(' + userGroup + ')\\/(' + projectGroup + ')' | ||
const linkRegex = new RegExp( | ||
'^https?:\\/\\/github\\.com\\/' + | ||
repoGroup + | ||
'\\/(commit|compare|issues|pull)\\/([a-f\\d]+(?:\\.{3}[a-f\\d]+)?\\/?(?=[#?]|$))', | ||
'i' | ||
) | ||
const repoRegex = new RegExp( | ||
'(?:^|/(?:repos/)?)' + repoGroup + '(?=\\.git|[\\/#@]|$)', | ||
'i' | ||
) | ||
const referenceRegex = new RegExp( | ||
'(' + | ||
userGroup + | ||
')(?:\\/(' + | ||
projectGroup + | ||
'))?(?:#([1-9]\\d*)|@([a-f\\d]{7,40}))', | ||
'gi' | ||
) | ||
const mentionRegex = new RegExp( | ||
'@(' + userGroup + '(?:\\/' + userGroup + ')?)', | ||
'gi' | ||
) | ||
/** | ||
* Plugin to enable, disable, and ignore messages. | ||
* | ||
* @type {import('unified').Plugin<[Options?]|void[], Root>} | ||
*/ | ||
export default function remarkGithub(options = {}) { | ||
/** | ||
* @typedef {import('mdast').StaticPhrasingContent} StaticPhrasingContent | ||
* @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction | ||
* @typedef {import('type-fest').PackageJson} PackageJson | ||
* @typedef {{input: string, index: number}} Match | ||
*/ | ||
let repository = options.repository | ||
/** @type {PackageJson|undefined} */ | ||
let pkg | ||
// Get the repository from `package.json`. | ||
if (!repository) { | ||
try { | ||
pkg = JSON.parse( | ||
String(fs.readFileSync(path.join(process.cwd(), 'package.json'))) | ||
) | ||
} catch {} | ||
repository = | ||
pkg && pkg.repository | ||
? // Object form. | ||
/* c8 ignore next 2 */ | ||
typeof pkg.repository === 'object' | ||
? pkg.repository.url | ||
: pkg.repository | ||
: '' | ||
} | ||
/** | ||
* @param {BuildUrlValues} values | ||
* @returns {string|false} | ||
*/ | ||
function buildUrl(values) { | ||
if (options.buildUrl) return options.buildUrl(values, defaultBuildUrl) | ||
return defaultBuildUrl(values) | ||
} | ||
// Parse the URL: See the tests for all possible kinds. | ||
const repositoryMatch = repoRegex.exec(repository || '') | ||
if (!repositoryMatch) { | ||
throw new Error('Missing `repository` field in `options`') | ||
} | ||
/** @type {RepositoryInfo} */ | ||
const repositoryInfo = {user: repositoryMatch[1], project: repositoryMatch[2]} | ||
return (tree) => { | ||
findAndReplace( | ||
tree, | ||
[ | ||
[referenceRegex, replaceReference], | ||
[mentionRegex, replaceMention], | ||
[/(?:#|\bgh-)([1-9]\d*)/gi, replaceIssue], | ||
[/\b([a-f\d]{7,40})\.{3}([a-f\d]{7,40})\b/gi, replaceHashRange], | ||
[/\b[a-f\d]{7,40}\b/gi, replaceHash] | ||
], | ||
{ignore: ['link', 'linkReference']} | ||
) | ||
visit(tree, 'link', (node) => { | ||
const link = parse(node) | ||
if (!link) { | ||
return | ||
} | ||
const comment = link.comment ? ' (comment)' : '' | ||
/** @type {string} */ | ||
let base | ||
if ( | ||
link.project !== repositoryInfo.project || | ||
// Compare page uses full `user/project` for forks. | ||
(link.page === 'compare' && link.user !== repositoryInfo.user) | ||
) { | ||
base = link.user + '/' + link.project | ||
} else if (link.user === repositoryInfo.user) { | ||
base = '' | ||
} else { | ||
base = link.user | ||
} | ||
/** @type {StaticPhrasingContent[]} */ | ||
const children = [] | ||
if (link.page === 'issues' || link.page === 'pull') { | ||
base += '#' | ||
children.push({ | ||
type: 'text', | ||
value: base + link.reference + comment | ||
}) | ||
} else { | ||
if (base) { | ||
children.push({type: 'text', value: base + '@'}) | ||
} | ||
children.push({type: 'inlineCode', value: link.reference}) | ||
if (link.comment) { | ||
children.push({type: 'text', value: comment}) | ||
} | ||
} | ||
node.children = children | ||
}) | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} value | ||
* @param {string} username | ||
* @param {Match} match | ||
*/ | ||
function replaceMention(value, username, match) { | ||
if ( | ||
/[\w`]/.test(match.input.charAt(match.index - 1)) || | ||
/[/\w`]/.test(match.input.charAt(match.index + value.length)) || | ||
denyMention.has(username) | ||
) { | ||
return false | ||
} | ||
const url = buildUrl({type: 'mention', user: username}) | ||
if (!url) return false | ||
/** @type {StaticPhrasingContent} */ | ||
let node = {type: 'text', value} | ||
if (options.mentionStrong !== false) { | ||
node = {type: 'strong', children: [node]} | ||
} | ||
return {type: 'link', title: null, url, children: [node]} | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} value | ||
* @param {string} no | ||
* @param {Match} match | ||
*/ | ||
function replaceIssue(value, no, match) { | ||
if ( | ||
/\w/.test(match.input.charAt(match.index - 1)) || | ||
/\w/.test(match.input.charAt(match.index + value.length)) | ||
) { | ||
return false | ||
} | ||
const url = buildUrl({type: 'issue', ...repositoryInfo, no}) | ||
return url | ||
? {type: 'link', title: null, url, children: [{type: 'text', value}]} | ||
: false | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} value | ||
* @param {string} a | ||
* @param {string} b | ||
* @param {Match} match | ||
*/ | ||
function replaceHashRange(value, a, b, match) { | ||
if ( | ||
/[^\t\n\r (@[{]/.test(match.input.charAt(match.index - 1)) || | ||
/\w/.test(match.input.charAt(match.index + value.length)) || | ||
denyHash.has(value) | ||
) { | ||
return false | ||
} | ||
const url = buildUrl({ | ||
type: 'compare', | ||
...repositoryInfo, | ||
base: a, | ||
compare: b | ||
}) | ||
return url | ||
? { | ||
type: 'link', | ||
title: null, | ||
url, | ||
children: [{type: 'inlineCode', value: abbr(a) + '...' + abbr(b)}] | ||
} | ||
: false | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} value | ||
* @param {Match} match | ||
*/ | ||
function replaceHash(value, match) { | ||
if ( | ||
/[^\t\n\r (@[{.]/.test(match.input.charAt(match.index - 1)) || | ||
// For some weird reason GH does link two dots, but not one 🤷♂️ | ||
(match.input.charAt(match.index - 1) === '.' && | ||
match.input.charAt(match.index - 2) !== '.') || | ||
/\w/.test(match.input.charAt(match.index + value.length)) || | ||
denyHash.has(value) | ||
) { | ||
return false | ||
} | ||
const url = buildUrl({type: 'commit', ...repositoryInfo, hash: value}) | ||
return url | ||
? { | ||
type: 'link', | ||
title: null, | ||
url, | ||
children: [{type: 'inlineCode', value: abbr(value)}] | ||
} | ||
: false | ||
} | ||
/** | ||
* @type {ReplaceFunction} | ||
* @param {string} $0 | ||
* @param {string} user | ||
* @param {string} specificProject | ||
* @param {string} no | ||
* @param {string} hash | ||
* @param {Match} match | ||
*/ | ||
// eslint-disable-next-line max-params | ||
function replaceReference($0, user, specificProject, no, hash, match) { | ||
if ( | ||
/[^\t\n\r (@[{]/.test(match.input.charAt(match.index - 1)) || | ||
/\w/.test(match.input.charAt(match.index + $0.length)) | ||
) { | ||
return false | ||
} | ||
const project = specificProject || repositoryInfo.project | ||
const url = no | ||
? buildUrl({type: 'issue', user, project, no}) | ||
: buildUrl({type: 'commit', user, project, hash}) | ||
if (!url) return false | ||
/** @type {StaticPhrasingContent[]} */ | ||
const nodes = [] | ||
let value = '' | ||
if (project !== repositoryInfo.project) { | ||
value += user + '/' + project | ||
} else if (user !== repositoryInfo.user) { | ||
value += user | ||
} | ||
if (no) { | ||
value += '#' + no | ||
} else { | ||
value += '@' | ||
nodes.push({type: 'inlineCode', value: abbr(hash)}) | ||
} | ||
nodes.unshift({type: 'text', value}) | ||
return {type: 'link', title: null, url, children: nodes} | ||
} | ||
} | ||
/** | ||
* Abbreviate a SHA. | ||
* | ||
* @param {string} sha | ||
* @returns {string} | ||
*/ | ||
function abbr(sha) { | ||
return sha.slice(0, minShaLength) | ||
} | ||
/** | ||
* Given a set of values based on the values type, returns link URL. | ||
* | ||
* @type {DefaultBuildUrl} | ||
*/ | ||
function defaultBuildUrl(values) { | ||
const base = 'https://github.com' | ||
if (values.type === 'mention') return [base, values.user].join('/') | ||
const {project, user} = values | ||
if (values.type === 'commit') | ||
return [base, user, project, 'commit', values.hash].join('/') | ||
if (values.type === 'issue') | ||
return [base, user, project, 'issues', values.no].join('/') | ||
// `values.type` is `'compare'` | ||
return [ | ||
base, | ||
user, | ||
project, | ||
'compare', | ||
values.base + '...' + values.compare | ||
].join('/') | ||
} | ||
/** | ||
* Parse a link and determine whether it links to GitHub. | ||
* | ||
* @param {import('mdast').Link} node | ||
* @returns {{user: string, project: string, page: string, reference: string, comment: boolean}|undefined} | ||
*/ | ||
function parse(node) { | ||
const url = node.url || '' | ||
const match = linkRegex.exec(url) | ||
if ( | ||
// Not a proper URL. | ||
!match || | ||
// Looks like formatting. | ||
node.children.length !== 1 || | ||
node.children[0].type !== 'text' || | ||
toString(node) !== url || | ||
// SHAs can be min 4, max 40 characters. | ||
(match[3] === 'commit' && (match[4].length < 4 || match[4].length > 40)) || | ||
// SHAs can be min 4, max 40 characters. | ||
(match[3] === 'compare' && | ||
!/^[a-f\d]{4,40}\.{3}[a-f\d]{4,40}$/.test(match[4])) || | ||
// Issues / PRs are decimal only. | ||
((match[3] === 'issues' || match[3] === 'pull') && | ||
/[a-f]/i.test(match[4])) || | ||
// Projects can be at most 99 characters. | ||
match[2].length >= 100 | ||
) { | ||
return | ||
} | ||
let reference = match[4] | ||
if (match[3] === 'compare') { | ||
const [base, compare] = reference.split('...') | ||
reference = abbr(base) + '...' + abbr(compare) | ||
} else { | ||
reference = abbr(reference) | ||
} | ||
return { | ||
user: match[1], | ||
project: match[2], | ||
page: match[3], | ||
reference, | ||
comment: | ||
url.charAt(match[0].length) === '#' && match[0].length + 1 < url.length | ||
} | ||
} | ||
export default remarkGithub |
{ | ||
"name": "remark-github", | ||
"version": "11.2.0", | ||
"version": "11.2.1", | ||
"description": "remark plugin to autolink references like in GitHub issues, PRs, and comments", | ||
@@ -40,3 +40,10 @@ "license": "MIT", | ||
"types": "index.d.ts", | ||
"browser": { | ||
"./lib/get-repo-from-package.js": "./lib/get-repo-from-package.browser.js" | ||
}, | ||
"react-native": { | ||
"./lib/get-repo-from-package.js": "./lib/get-repo-from-package.browser.js" | ||
}, | ||
"files": [ | ||
"lib/", | ||
"index.d.ts", | ||
@@ -43,0 +50,0 @@ "index.js" |
255
readme.md
@@ -11,20 +11,57 @@ # remark-github | ||
[**remark**][remark] plugin to automatically link references to commits, issues, | ||
pull-requests, and users, like in GitHub issues, PRs, and comments (see [Writing | ||
on GitHub][writing-on-github]). | ||
[**remark**][remark] plugin to link references to commits, issues, and users, | ||
in the same way that GitHub does in comments, issues, PRs, and releases (see | ||
[Writing on GitHub][writing-on-github]). | ||
## Note! | ||
## Contents | ||
This plugin is ready for the new parser in remark | ||
([`micromark`](https://github.com/micromark/micromark), | ||
see [`remarkjs/remark#536`](https://github.com/remarkjs/remark/pull/536)). | ||
Version 10 works with old (12) and new (13+) remark! | ||
* [What is this?](#what-is-this) | ||
* [When should I use this?](#when-should-i-use-this) | ||
* [Install](#install) | ||
* [Use](#use) | ||
* [API](#api) | ||
* [`unified().use(remarkGithub[, options])`](#unifieduseremarkgithub-options) | ||
* [Examples](#examples) | ||
* [Example: `buildUrl`](#example-buildurl) | ||
* [Syntax](#syntax) | ||
* [Types](#types) | ||
* [Compatibility](#compatibility) | ||
* [Security](#security) | ||
* [Related](#related) | ||
* [Contribute](#contribute) | ||
* [License](#license) | ||
## What is this? | ||
This package is a [unified][] ([remark][]) plugin to link references to commits, | ||
issues, and users: `@wooorm` -> `[**@wooorm**](https://github.com/wooorm)`. | ||
**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. | ||
This is a remark plugin that transforms mdast. | ||
## When should I use this? | ||
This project is useful if you want to emulate how markdown would work in GitHub | ||
comments, issues, PRs, or releases, but it’s actually displayed somewhere else | ||
(on a website, or in other places on GitHub which don’t link references, such as | ||
markdown in a repo or Gist). | ||
This plugin does not support other platforms such as GitLab or Bitbucket and | ||
their custom features. | ||
A different plugin, [`remark-gfm`][remark-gfm], adds support for GFM (GitHub | ||
Flavored Markdown). | ||
GFM is a set of extensions (autolink literals, footnotes, strikethrough, tables, | ||
and tasklists) to markdown that are supported everywhere on GitHub. | ||
Another plugin, [`remark-breaks`][remark-breaks], turns soft line endings | ||
(enters) into hard breaks (`<br>`s). | ||
## 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](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). | ||
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: | ||
[npm][]: | ||
```sh | ||
@@ -34,2 +71,16 @@ npm install remark-github | ||
In Deno with [Skypack][]: | ||
```js | ||
import remarkGithub from 'https://cdn.skypack.dev/remark-github@11?dts' | ||
``` | ||
In browsers with [Skypack][]: | ||
```html | ||
<script type="module"> | ||
import remarkGithub from 'https://cdn.skypack.dev/remark-github@11?min' | ||
</script> | ||
``` | ||
## Use | ||
@@ -63,14 +114,17 @@ | ||
```js | ||
import {readSync} from 'to-vfile' | ||
import {read} from 'to-vfile' | ||
import {remark} from 'remark' | ||
import remarkGfm from 'remark-gfm' | ||
import remarkGithub from 'remark-github' | ||
const file = readSync('example.md') | ||
main() | ||
remark() | ||
.use(remarkGithub) | ||
.process(file) | ||
.then((file) => { | ||
console.log(String(file)) | ||
}) | ||
async function main() { | ||
const file = await remark() | ||
.use(remarkGfm) | ||
.use(remarkGithub) | ||
.process(await read('example.md')) | ||
console.log(String(file)) | ||
} | ||
``` | ||
@@ -108,8 +162,90 @@ | ||
Automatically link references to commits, issues, pull-requests, and users, like | ||
in GitHub issues, PRs, and comments (see | ||
Link references to users, commits, and issues, in the same way that GitHub does | ||
in comments, issues, PRs, and releases (see | ||
[Writing on GitHub][writing-on-github]). | ||
###### Conversion | ||
##### `options` | ||
Configuration (optional). | ||
###### `options.repository` | ||
Repository to link against (`string`, optional). | ||
Detected in Node.js from the `repository` field in `package.json` if not given. | ||
Should point to a GitHub repository, such as | ||
`'https://github.com/user/project.git'` or `'user/project'`. | ||
###### `options.mentionStrong` | ||
Wrap mentions in `strong` (`boolean`, default: `true`). | ||
This makes them render more like how GitHub styles them. | ||
But GitHub itself uses CSS instead of strong. | ||
###### `options.buildUrl` | ||
Change how (and whether) things are linked (`Function`, optional). | ||
This can be used to point links to GitHub Enterprise or other places. | ||
It’s called with the following parameters: | ||
* `values` (`BuildUrlValues`) | ||
— info on the link to build | ||
* `defaultBuildUrl` (`(values: BuildUrlValues) => string`) | ||
— function that can be called to perform normal behavior | ||
It should return the URL to use (`string`) or `false` to not create a link. | ||
The following schemas are passed as `BuildUrlValues`: | ||
* `{type: 'commit', user, project, hash}` | ||
* `{type: 'compare', user, project, base, compare}` | ||
* `{type: 'issue', user, project, no}` | ||
* `{type: 'mention', user}` | ||
## Examples | ||
## Example: `buildUrl` | ||
A `buildUrl` can be passed to not link mentions. | ||
For example, by changing `example.js` from before like so: | ||
```diff | ||
@@ -8,7 +8,11 @@ main() | ||
async function main() { | ||
const file = await remark() | ||
.use(remarkGfm) | ||
- .use(remarkGithub) | ||
+ .use(remarkGithub, { | ||
+ buildUrl(values, defaultBuildUrl) { | ||
+ return values.type === 'mention' ? false : defaultBuildUrl(values) | ||
+ } | ||
+ }) | ||
.process(await read('example.md')) | ||
console.log(String(file)) | ||
``` | ||
To instead point mentions to a different place, change `example.js` like so: | ||
```diff | ||
@@ -8,7 +8,13 @@ main() | ||
async function main() { | ||
const file = await remark() | ||
.use(remarkGfm) | ||
- .use(remarkGithub) | ||
+ .use(remarkGithub, { | ||
+ buildUrl(values, defaultBuildUrl) { | ||
+ return values.type === 'mention' | ||
+ ? `https://yourwebsite.com/${values.user}/` | ||
+ : defaultBuildUrl(values) | ||
+ } | ||
+ }) | ||
.process(await read('example.md')) | ||
console.log(String(file)) | ||
``` | ||
## Syntax | ||
The following references are supported: | ||
* Commits: | ||
@@ -141,46 +277,23 @@ `1f2a4fb` → [`1f2a4fb`][sha] | ||
###### Repository | ||
Autolinks to these references are also transformed: | ||
`https://github.com/wooorm` -> `[**@wooorm**](https://github.com/wooorm)` | ||
These links are generated relative to a project. | ||
In Node this is detected automatically by loading `package.json` and looking for | ||
a `repository` field. | ||
In the browser, or when overwriting this, you can pass a `repository` in | ||
`options`. | ||
The value of `repository` should be a URL to a GitHub repository, such as | ||
`'https://github.com/user/project.git'`, or only `'user/project'`. | ||
## Types | ||
###### Mentions | ||
This package is fully typed with [TypeScript][]. | ||
It exports an `Options` type, which specifies the interface of the accepted | ||
options. | ||
There are also `BuildUrl`, `BuildUrlValues`, `BuildUrlCommitValues`, | ||
`BuildUrlCompareValues`, `BuildUrlIssueValues`, `BuildUrlMentionValues`, | ||
and `DefaultBuildUrl` types exported. | ||
By default, mentions are wrapped in `strong` nodes (that render to `<strong>` in | ||
HTML), to simulate the look of mentions on GitHub. | ||
However, this creates different HTML markup, as the GitHub site applies these | ||
styles using CSS. | ||
Pass `mentionStrong: false` to turn off this behavior. | ||
## Compatibility | ||
##### Custom URLs | ||
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. | ||
By default we build URLs to public GitHub. | ||
You can overwrite them to point to GitHub Enterprise or other places by passing | ||
a `buildUrl`. | ||
That function is given an object with different values and the default | ||
`buildUrl`. | ||
If `buildUrl` returns `false`, the value is not linked. | ||
This plugin works with `unified` version 6+ and `remark` version 7+. | ||
```js | ||
remark() | ||
.use(remarkGithub, { | ||
// The fields in `values` depends on the kind reference: | ||
// {type: 'commit', user, project, hash} | ||
// {type: 'compare', user, project, base, compare} | ||
// {type: 'issue', user, project, no} | ||
// {type: 'mention', user} | ||
// You can return a URL, which will be used, or `false`, to not link. | ||
buildUrl(values, defaultBuildUrl) { | ||
return values.type === 'mention' | ||
? `https://yourwebsite.com/${values.user}/` | ||
: defaultBuildUrl(values) | ||
} | ||
}) | ||
``` | ||
## Security | ||
@@ -192,2 +305,10 @@ | ||
## Related | ||
* [`remark-gfm`][remark-gfm] | ||
— support GFM (autolink literals, footnotes, strikethrough, tables, | ||
tasklists) | ||
* [`remark-breaks`][remark-breaks] | ||
— support breaks without needing spaces or escapes (enters to `<br>`) | ||
## Contribute | ||
@@ -237,2 +358,4 @@ | ||
[skypack]: https://www.skypack.dev | ||
[health]: https://github.com/remarkjs/.github | ||
@@ -252,4 +375,6 @@ | ||
[writing-on-github]: https://help.github.com/articles/writing-on-github/#references | ||
[unified]: https://github.com/unifiedjs/unified | ||
[writing-on-github]: https://docs.github.com/en/github/writing-on-github#references | ||
[sha]: https://github.com/remarkjs/remark-github/commit/1f2a4fb8f88a0a98ea9d0c0522cd538a9898f921 | ||
@@ -265,4 +390,10 @@ | ||
[typescript]: https://www.typescriptlang.org | ||
[rehype]: https://github.com/rehypejs/rehype | ||
[hast]: https://github.com/syntax-tree/hast | ||
[remark-gfm]: https://github.com/remarkjs/remark-gfm | ||
[remark-breaks]: https://github.com/remarkjs/remark-breaks |
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
34049
10
614
391
1