remark-validate-links
Advanced tools
Comparing version 10.0.4 to 12.1.1
@@ -1,3 +0,6 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('./lib/index.js').UrlConfig} UrlConfig | ||
* @typedef {import('./lib/index.js').Options} Options | ||
*/ | ||
module.exports = require('./lib') | ||
export {default} from './lib/index.js' |
@@ -1,7 +0,1 @@ | ||
'use strict' | ||
module.exports = checkIfReferencedFilesExist | ||
function checkIfReferencedFilesExist(ctx, next) { | ||
next() | ||
} | ||
export async function checkFiles() {} |
@@ -1,44 +0,41 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('../types.js').VFile} VFile | ||
* @typedef {import('../types.js').Landmarks} Landmarks | ||
* @typedef {import('../types.js').References} References | ||
*/ | ||
var fs = require('fs') | ||
import {constants, promises as fs} from 'node:fs' | ||
module.exports = checkIfReferencedFilesExist | ||
/** | ||
* @param {{files: VFile[], landmarks: Landmarks, references: References}} ctx | ||
*/ | ||
export async function checkFiles(ctx) { | ||
const landmarks = ctx.landmarks | ||
const references = ctx.references | ||
/** @type {Array.<Promise<void>>} */ | ||
const promises = [] | ||
/** @type {string} */ | ||
let filePath | ||
function checkIfReferencedFilesExist(ctx, next) { | ||
var landmarks = ctx.landmarks | ||
var references = ctx.references | ||
var filePaths = [] | ||
var filePath | ||
var actual = 0 | ||
var expected | ||
for (filePath in references) { | ||
if (landmarks[filePath] === undefined) { | ||
filePaths.push(filePath) | ||
} | ||
} | ||
/** @type {Record<string, boolean>} */ | ||
const map = Object.create(null) | ||
expected = filePaths.length | ||
landmarks[filePath] = map | ||
if (expected === 0) { | ||
next() | ||
} else { | ||
filePaths.forEach(checkIfExists) | ||
promises.push( | ||
fs.access(filePath, constants.F_OK).then( | ||
() => { | ||
map[''] = true | ||
}, | ||
(/** @type {NodeJS.ErrnoException} */ error) => { | ||
map[''] = error.code !== 'ENOENT' && error.code !== 'ENOTDIR' | ||
} | ||
) | ||
) | ||
} | ||
} | ||
function checkIfExists(filePath) { | ||
fs.access(filePath, fs.F_OK, onaccess) | ||
function onaccess(error) { | ||
var noEntry = | ||
error && (error.code === 'ENOENT' || error.code === 'ENOTDIR') | ||
landmarks[filePath] = Object.create(null) | ||
landmarks[filePath][''] = !noEntry | ||
if (++actual === expected) { | ||
next() | ||
} | ||
} | ||
} | ||
await Promise.all(promises) | ||
} |
@@ -1,13 +0,11 @@ | ||
'use strict' | ||
import {trough} from 'trough' | ||
import {mergeLandmarks} from './merge-landmarks.js' | ||
import {mergeReferences} from './merge-references.js' | ||
import {checkFiles} from './check-files.js' | ||
import {validate} from './validate.js' | ||
var trough = require('trough') | ||
var mergeLandmarks = require('./merge-landmarks') | ||
var mergeReferences = require('./merge-references') | ||
var checkIfReferencedFilesExist = require('./check-files') | ||
var validate = require('./validate') | ||
module.exports = trough() | ||
export const check = trough() | ||
.use(mergeLandmarks) | ||
.use(mergeReferences) | ||
.use(checkIfReferencedFilesExist) | ||
.use(checkFiles) | ||
.use(validate) |
@@ -1,26 +0,35 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('../types.js').VFile} VFile | ||
* @typedef {import('../types.js').Landmarks} Landmarks | ||
*/ | ||
var constants = require('../constants') | ||
import {constants} from '../constants.js' | ||
module.exports = mergeLandmarks | ||
const own = {}.hasOwnProperty | ||
function mergeLandmarks(ctx) { | ||
var result = Object.create(null) | ||
var files = ctx.files | ||
var length = files.length | ||
var index = -1 | ||
var file | ||
var landmarks | ||
var landmark | ||
/** | ||
* @param {{files: VFile[], landmarks?: Landmarks}} ctx | ||
*/ | ||
export function mergeLandmarks(ctx) { | ||
/** @type {Landmarks} */ | ||
const result = Object.create(null) | ||
const files = ctx.files | ||
let index = -1 | ||
while (++index < length) { | ||
file = files[index] | ||
landmarks = file.data[constants.landmarkId] | ||
while (++index < files.length) { | ||
const file = files[index] | ||
const landmarks = /** @type {Landmarks|undefined} */ ( | ||
file.data[constants.landmarkId] | ||
) | ||
/** @type {string} */ | ||
let landmark | ||
if (landmarks) { | ||
for (landmark in landmarks) { | ||
result[landmark] = Object.assign( | ||
Object.create(null), | ||
landmarks[landmark] | ||
) | ||
if (own.call(landmarks, landmark)) { | ||
result[landmark] = Object.assign( | ||
Object.create(null), | ||
landmarks[landmark] | ||
) | ||
} | ||
} | ||
@@ -27,0 +36,0 @@ } |
@@ -1,22 +0,30 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('../types.js').VFile} VFile | ||
* @typedef {import('../types.js').Landmarks} Landmarks | ||
* @typedef {import('../types.js').References} References | ||
* @typedef {import('../types.js').ReferenceMap} ReferenceMap | ||
* @typedef {import('../types.js').Resource} Resource | ||
*/ | ||
var constants = require('../constants') | ||
import {constants} from '../constants.js' | ||
module.exports = mergeReferences | ||
const own = {}.hasOwnProperty | ||
function mergeReferences(ctx) { | ||
var result = {} | ||
var files = ctx.files | ||
var length = files.length | ||
var index = -1 | ||
var file | ||
var references | ||
var reference | ||
var all | ||
var internal | ||
var hash | ||
/** | ||
* @param {{files: VFile[], landmarks: Landmarks, references?: References}} ctx | ||
*/ | ||
export function mergeReferences(ctx) { | ||
/** @type {References} */ | ||
const result = {} | ||
const files = ctx.files | ||
let index = -1 | ||
while (++index < length) { | ||
file = files[index] | ||
references = file.data[constants.referenceId] | ||
while (++index < files.length) { | ||
const file = files[index] | ||
const references = | ||
/** @type {Record<string, Record<string, Resource[]>>|undefined} */ ( | ||
file.data[constants.referenceId] | ||
) | ||
/** @type {string} */ | ||
let reference | ||
@@ -28,14 +36,23 @@ if (!references) { | ||
for (reference in references) { | ||
internal = references[reference] | ||
all = | ||
reference in result | ||
? result[reference] | ||
: (result[reference] = Object.create(null)) | ||
if (own.call(references, reference)) { | ||
const internal = references[reference] | ||
/** @type {Record<string, ReferenceMap[]>} */ | ||
const all = | ||
reference in result | ||
? result[reference] | ||
: (result[reference] = Object.create(null)) | ||
/** @type {string} */ | ||
let hash | ||
for (hash in internal) { | ||
;(all[hash] || (all[hash] = [])).push({ | ||
file: file, | ||
reference: {filePath: reference, hash: hash}, | ||
nodes: internal[hash] | ||
}) | ||
for (hash in internal) { | ||
// eslint-disable-next-line max-depth | ||
if (own.call(internal, hash)) { | ||
const list = all[hash] || (all[hash] = []) | ||
list.push({ | ||
file, | ||
reference: {filePath: reference, hash}, | ||
nodes: internal[hash] | ||
}) | ||
} | ||
} | ||
} | ||
@@ -42,0 +59,0 @@ } |
@@ -1,28 +0,41 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('../types.js').VFile} VFile | ||
* @typedef {import('../types.js').Landmarks} Landmarks | ||
* @typedef {import('../types.js').References} References | ||
* @typedef {import('../types.js').ReferenceMap} ReferenceMap | ||
*/ | ||
var path = require('path') | ||
var propose = require('propose') | ||
var constants = require('../constants') | ||
import path from 'node:path' | ||
// @ts-expect-error: untyped. | ||
import propose from 'propose' | ||
import {constants} from '../constants.js' | ||
module.exports = validate | ||
const own = {}.hasOwnProperty | ||
function validate(ctx) { | ||
var landmarks = ctx.landmarks | ||
var references = ctx.references | ||
var missing = [] | ||
var reference | ||
var hash | ||
var refs | ||
var lands | ||
var length | ||
var index | ||
/** | ||
* @param {{files: VFile[], landmarks: Landmarks, references: References}} ctx | ||
*/ | ||
export function validate(ctx) { | ||
const landmarks = ctx.landmarks | ||
const references = ctx.references | ||
/** @type {ReferenceMap[]} */ | ||
const missing = [] | ||
/** @type {string} */ | ||
let key | ||
for (reference in references) { | ||
refs = references[reference] | ||
/* istanbul ignore next - `else` could happen in browser. */ | ||
lands = reference in landmarks ? landmarks[reference] : Object.create(null) | ||
for (key in references) { | ||
if (own.call(references, key)) { | ||
const refs = references[key] | ||
/** @type {Landmarks} */ | ||
const lands = | ||
// `else` could happen in browser. | ||
/* c8 ignore next */ | ||
key in landmarks ? landmarks[key] : Object.create(null) | ||
/** @type {string} */ | ||
let hash | ||
for (hash in refs) { | ||
if (!lands[hash]) { | ||
missing = missing.concat(refs[hash]) | ||
for (hash in refs) { | ||
if (!lands[hash]) { | ||
missing.push(...refs[hash]) | ||
} | ||
} | ||
@@ -32,25 +45,29 @@ } | ||
length = missing.length | ||
index = -1 | ||
let index = -1 | ||
while (++index < length) { | ||
reference = missing[index] | ||
warn(ctx, reference.reference, reference.file, reference.nodes) | ||
while (++index < missing.length) { | ||
warn(ctx, missing[index]) | ||
} | ||
} | ||
function warn(ctx, info, file, nodes) { | ||
var landmarks = ctx.landmarks | ||
var absolute = file.path ? path.resolve(file.cwd, file.path) : '' | ||
var base = absolute ? path.dirname(absolute) : null | ||
var filePath = base ? path.relative(base, info.filePath) : info.filePath | ||
var hash = info.hash | ||
var dictionary = [] | ||
var subhash | ||
var landmark | ||
var relativeLandmark | ||
var reason | ||
var ruleId | ||
var origin | ||
var suggestion | ||
/** | ||
* @param {{files: VFile[], landmarks: Landmarks, references: References}} ctx | ||
* @param {ReferenceMap} reference | ||
*/ | ||
function warn(ctx, reference) { | ||
const landmarks = ctx.landmarks | ||
const absolute = reference.file.path | ||
? path.resolve(reference.file.cwd, reference.file.path) | ||
: '' | ||
const base = absolute ? path.dirname(absolute) : null | ||
const filePath = base | ||
? path.relative(base, reference.reference.filePath) | ||
: reference.reference.filePath | ||
const hash = reference.reference.hash | ||
/** @type {string[]} */ | ||
const dictionary = [] | ||
/** @type {string} */ | ||
let reason | ||
/** @type {string} */ | ||
let ruleId | ||
@@ -72,3 +89,5 @@ if (hash) { | ||
origin = [constants.sourceId, ruleId].join(':') | ||
const origin = [constants.sourceId, ruleId].join(':') | ||
/** @type {string} */ | ||
let landmark | ||
@@ -81,3 +100,3 @@ for (landmark in landmarks) { | ||
relativeLandmark = base ? path.relative(base, landmark) : landmark | ||
const relativeLandmark = base ? path.relative(base, landmark) : landmark | ||
@@ -93,2 +112,5 @@ if (!hash) { | ||
/** @type {string} */ | ||
let subhash | ||
for (subhash in landmarks[landmark]) { | ||
@@ -101,3 +123,6 @@ if (subhash !== '') { | ||
suggestion = propose(hash ? hash : filePath, dictionary, {threshold: 0.7}) | ||
/** @type {string} */ | ||
const suggestion = propose(hash ? hash : filePath, dictionary, { | ||
threshold: 0.7 | ||
}) | ||
@@ -108,7 +133,12 @@ if (suggestion) { | ||
nodes.forEach(one) | ||
let index = -1 | ||
function one(node) { | ||
file.message(reason, node, origin) | ||
while (++index < reference.nodes.length) { | ||
const message = reference.file.message( | ||
reason, | ||
reference.nodes[index], | ||
origin | ||
) | ||
message.url = 'https://github.com/remarkjs/remark-validate-links#readme' | ||
} | ||
} |
@@ -1,8 +0,8 @@ | ||
'use strict' | ||
exports.sourceId = 'remark-validate-links' | ||
exports.headingRuleId = 'missing-heading' | ||
exports.headingInFileRuleId = 'missing-heading-in-file' | ||
exports.fileRuleId = 'missing-file' | ||
exports.landmarkId = 'remarkValidateLinksLandmarks' | ||
exports.referenceId = 'remarkValidateLinksReferences' | ||
export const constants = { | ||
sourceId: 'remark-validate-links', | ||
headingRuleId: 'missing-heading', | ||
headingInFileRuleId: 'missing-heading-in-file', | ||
fileRuleId: 'missing-file', | ||
landmarkId: 'remarkValidateLinksLandmarks', | ||
referenceId: 'remarkValidateLinksReferences' | ||
} |
@@ -1,54 +0,69 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('mdast').Root} Root | ||
* @typedef {import('vfile').VFile} VFile | ||
* @typedef {import('unified-engine').FileSet} FileSet | ||
* @typedef {import('hosted-git-info').Hosts} Hosts | ||
* @typedef {import('../index.js').Options} Options | ||
* @typedef {import('../index.js').UrlConfig} UrlConfig | ||
*/ | ||
var hostedGitInfo = require('hosted-git-info') | ||
import hostedGitInfo from 'hosted-git-info' | ||
module.exports = config | ||
/** @type {Partial<Record<Hosts, string>>} */ | ||
const viewPaths = {github: 'blob', gitlab: 'blob', bitbucket: 'src'} | ||
/** @type {Partial<Record<Hosts, string>>} */ | ||
const headingPrefixes = { | ||
github: '#', | ||
gitlab: '#', | ||
bitbucket: '#markdown-header-' | ||
} | ||
/** @type {Partial<Record<Hosts, string>>} */ | ||
const topAnchors = {github: '#readme', gitlab: '#readme'} | ||
/** @type {Partial<Record<Hosts, boolean>>} */ | ||
const lineLinks = {github: true, gitlab: true} | ||
var viewPaths = {github: 'blob', gitlab: 'blob', bitbucket: 'src'} | ||
var headingPrefixes = {github: '#', gitlab: '#', bitbucket: '#markdown-header-'} | ||
var topAnchors = {github: '#readme', gitlab: '#readme'} | ||
var lineLinks = {github: true, gitlab: true} | ||
/** | ||
* @param {{tree: Root, file: VFile, fileSet?: FileSet, options: Options}} ctx | ||
*/ | ||
export function config(ctx) { | ||
const repo = ctx.options.repository | ||
function config(ctx) { | ||
var repo = ctx.repository | ||
var urlConfig = ctx.urlConfig | ||
var info = {} | ||
if (urlConfig) { | ||
if (ctx.options.urlConfig) { | ||
return | ||
} | ||
urlConfig = { | ||
/** @type {UrlConfig} */ | ||
const urlConfig = { | ||
prefix: '', | ||
headingPrefix: '#', | ||
lines: false, | ||
hostname: null, | ||
topAnchor: null | ||
hostname: undefined, | ||
topAnchor: undefined | ||
} | ||
if (repo) { | ||
info = hostedGitInfo.fromUrl(repo) | ||
} | ||
const info = hostedGitInfo.fromUrl(repo) | ||
if (info) { | ||
if (info.type in viewPaths) { | ||
urlConfig.prefix = '/' + info.path() + '/' + viewPaths[info.type] + '/' | ||
} | ||
if (info && info.type !== 'gist') { | ||
if (info.type in viewPaths) { | ||
urlConfig.prefix = '/' + info.path() + '/' + viewPaths[info.type] + '/' | ||
} | ||
if (info.type in headingPrefixes) { | ||
urlConfig.headingPrefix = headingPrefixes[info.type] | ||
} | ||
if (info.type in headingPrefixes) { | ||
urlConfig.headingPrefix = headingPrefixes[info.type] | ||
} | ||
if (info.type in lineLinks) { | ||
urlConfig.lines = lineLinks[info.type] | ||
} | ||
if (info.type in lineLinks) { | ||
urlConfig.lines = lineLinks[info.type] | ||
} | ||
if (info.type in topAnchors) { | ||
urlConfig.topAnchor = topAnchors[info.type] | ||
if (info.type in topAnchors) { | ||
urlConfig.topAnchor = topAnchors[info.type] | ||
} | ||
urlConfig.hostname = info.domain | ||
} | ||
urlConfig.hostname = info.domain | ||
} | ||
ctx.urlConfig = urlConfig | ||
ctx.options.urlConfig = urlConfig | ||
} |
@@ -1,7 +0,1 @@ | ||
'use strict' | ||
module.exports = findRepo | ||
function findRepo(ctx, next) { | ||
next() | ||
} | ||
export async function findRepo() {} |
@@ -1,14 +0,19 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('vfile').VFile} VFile | ||
* @typedef {import('../index.js').Options} Options | ||
*/ | ||
var path = require('path') | ||
var exec = require('child_process').exec | ||
import path from 'node:path' | ||
import {promisify} from 'node:util' | ||
import {exec as execCb} from 'node:child_process' | ||
module.exports = findRepo | ||
const exec = promisify(execCb) | ||
function findRepo(ctx, next) { | ||
var repo = ctx.repository | ||
var file = ctx.file | ||
var base = file.cwd | ||
var actual = 0 | ||
var expected = 0 | ||
/** | ||
* @param {{file: VFile, options: Options}} ctx | ||
*/ | ||
export async function findRepo(ctx) { | ||
const repo = ctx.options.repository | ||
const file = ctx.file | ||
let base = file.cwd | ||
@@ -20,58 +25,23 @@ if (file.path) { | ||
if (repo === null || repo === undefined) { | ||
expected++ | ||
exec('git remote -v', {cwd: base}, onremote) | ||
const {stdout} = await exec('git remote -v', {cwd: base}) | ||
const remote = stdout.match(/origin\t(.+?) \(fetch\)/) | ||
ctx.options.repository = remote ? remote[1] : undefined | ||
if (!ctx.options.repository) { | ||
throw new Error('Could not find remote origin') | ||
} | ||
} | ||
if (ctx.root === null || ctx.root === undefined) { | ||
if (ctx.options.root === null || ctx.options.root === undefined) { | ||
if (repo === null || repo === undefined) { | ||
expected++ | ||
exec('git rev-parse --show-cdup', {cwd: base}, oncdup) | ||
const {stdout} = await exec('git rev-parse --show-cdup', {cwd: base}) | ||
const out = stdout.trim() | ||
ctx.options.root = out ? path.join(base, out) : base | ||
} else { | ||
ctx.root = ctx.file.cwd | ||
ctx.options.root = ctx.file.cwd | ||
} | ||
} else { | ||
ctx.root = path.resolve(file.cwd, ctx.root) | ||
ctx.options.root = path.resolve(file.cwd, ctx.options.root) | ||
} | ||
if (actual === expected) { | ||
next() | ||
} | ||
function onremote(error, stdout) { | ||
var remote | ||
if (error) { | ||
expected = Infinity | ||
return next(error) | ||
} | ||
remote = stdout.match(/origin\t(.+?) \(fetch\)/) | ||
ctx.repository = remote ? remote[1] : null | ||
if (!ctx.repository) { | ||
expected = Infinity | ||
return next(new Error('Could not find remote origin')) | ||
} | ||
if (++actual === expected) { | ||
expected = Infinity | ||
next() | ||
} | ||
} | ||
function oncdup(error, stdout) { | ||
var out | ||
if (error) { | ||
expected = Infinity | ||
return next(error) | ||
} | ||
out = stdout.trim() | ||
ctx.root = out ? path.join(base, out) : base | ||
if (++actual === expected) { | ||
next() | ||
} | ||
} | ||
} |
@@ -1,8 +0,6 @@ | ||
'use strict' | ||
import {trough} from 'trough' | ||
import {findRepo} from './find-repo.js' | ||
import {config} from './config.js' | ||
import {findReferences} from './find-references.js' | ||
var trough = require('trough') | ||
var findRepo = require('./find-repo') | ||
var config = require('./config') | ||
var find = require('./find') | ||
module.exports = trough().use(findRepo).use(config).use(find) | ||
export const find = trough().use(findRepo).use(config).use(findReferences) |
113
lib/index.js
@@ -1,14 +0,57 @@ | ||
'use strict' | ||
/** | ||
* @typedef {import('mdast').Root} Root | ||
* @typedef {import('vfile').VFile} VFile | ||
* @typedef {import('unified-engine').FileSet} FileSet | ||
* @typedef {import('trough').Callback} Callback | ||
* | ||
* @typedef UrlConfig | ||
* Hosted Git info | ||
* @property {string|undefined} [hostname] | ||
* Domain of URLs (example: `'github.com'`, `'bitbucket.org'`). | ||
* @property {string|undefined} [prefix] | ||
* Path prefix before files (example: | ||
* `'/remarkjs/remark-validate-links/blob/'`, | ||
* `'/remarkjs/remark-validate-links/src/'`). | ||
* @property {string|undefined} [headingPrefix] | ||
* Prefix of headings (example: `'#'`, `'#markdown-header-'`). | ||
* @property {string|undefined} [topAnchor] | ||
* Hash to top of readme (example: `#readme`). | ||
* @property {boolean|undefined} [lines] | ||
* Whether lines in files can be linked. | ||
* | ||
* @typedef Options | ||
* Configuration. | ||
* @property {string|false} [repository] | ||
* URL to hosted Git. | ||
* If `repository` is nullish, the Git origin remote is detected. | ||
* If the repository resolves to something npm understands as a Git host such | ||
* as GitHub, GitLab, or Bitbucket, full URLs to that host (say, | ||
* `https://github.com/remarkjs/remark-validate-links/readme.md#install`) can | ||
* also be checked. | ||
* If you’re not in a Git repository, you must pass `repository: false` | ||
* explicitly. | ||
* @property {string} [root] | ||
* A `root` (`string?`) can also be passed, referencing the local Git root | ||
* directory (the folder that contains `.git`). | ||
* If both `root` and `repository` are nullish, the Git root is detected. | ||
* If `root` is not given but `repository` is, `file.cwd` is used. | ||
* @property {UrlConfig} [urlConfig] | ||
* If your project is hosted on `github.com`, `gitlab.com`, or `bitbucket.org`, | ||
* this plugin can automatically detect the url configuration. | ||
* Otherwise, use `urlConfig` to specify this manually. | ||
*/ | ||
var check = require('./check') | ||
var find = require('./find') | ||
var constants = require('./constants') | ||
import {check} from './check/index.js' | ||
import {find} from './find/index.js' | ||
import {constants} from './constants.js' | ||
module.exports = validateLinks | ||
cliCompleter.pluginId = constants.sourceId | ||
function validateLinks(options, fileSet) { | ||
var settings = options || {} | ||
/** | ||
* Plugin to validate that Markdown links and images reference existing local | ||
* files and headings. | ||
* | ||
* @type {import('unified').Plugin<[Options?, FileSet?]|[Options?]|void[], Root>} | ||
*/ | ||
export default function remarkValidateLinks(options, fileSet) { | ||
// Attach a `completer`. | ||
@@ -19,24 +62,27 @@ if (fileSet) { | ||
return transformer | ||
// Find references and landmarks. | ||
function transformer(tree, file, next) { | ||
return (tree, file, next) => { | ||
find.run( | ||
Object.assign({}, settings, {tree: tree, file: file, fileSet: fileSet}), | ||
done | ||
{tree, file, fileSet, options: {...options}}, | ||
/** @type {Callback} */ | ||
(error) => { | ||
if (error) { | ||
next(error) | ||
} else if (fileSet) { | ||
next() | ||
} else { | ||
checkAll([file], next) | ||
} | ||
} | ||
) | ||
function done(error) { | ||
if (error) { | ||
next(error) | ||
} else if (fileSet) { | ||
next() | ||
} else { | ||
checkAll([file], next) | ||
} | ||
} | ||
} | ||
} | ||
// Completer for the CLI (multiple files, supports parsing more files). | ||
/** | ||
* Completer for the CLI (multiple files, supports parsing more files). | ||
* | ||
* @param {FileSet} set | ||
* @param {Callback} next | ||
* @returns {void} | ||
*/ | ||
function cliCompleter(set, next) { | ||
@@ -46,7 +92,18 @@ checkAll(set.valueOf(), next) | ||
/** | ||
* Completer for the CLI (multiple files, supports parsing more files). | ||
* | ||
* @param {VFile[]} files | ||
* @param {Callback} next | ||
* @returns {void} | ||
*/ | ||
function checkAll(files, next) { | ||
// Check all references and landmarks. | ||
check.run({files: files}, function (error) { | ||
next(error) | ||
}) | ||
check.run( | ||
{files}, | ||
/** @type {Callback} */ | ||
(error) => { | ||
next(error) | ||
} | ||
) | ||
} |
{ | ||
"name": "remark-validate-links", | ||
"version": "10.0.4", | ||
"version": "12.1.1", | ||
"description": "remark plugin to validate links to headings and files", | ||
@@ -35,2 +35,11 @@ "license": "MIT", | ||
], | ||
"sideEffects": false, | ||
"type": "module", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"files": [ | ||
"lib/", | ||
"index.d.ts", | ||
"index.js" | ||
], | ||
"browser": { | ||
@@ -40,39 +49,40 @@ "./lib/check/check-files.js": "./lib/check/check-files.browser.js", | ||
}, | ||
"files": [ | ||
"lib/", | ||
"index.js" | ||
], | ||
"dependencies": { | ||
"github-slugger": "^1.0.0", | ||
"hosted-git-info": "^3.0.0", | ||
"mdast-util-to-string": "^1.0.0", | ||
"@types/mdast": "^3.0.0", | ||
"github-slugger": "^2.0.0", | ||
"hosted-git-info": "^6.0.0", | ||
"mdast-util-to-string": "^3.2.0", | ||
"propose": "0.0.5", | ||
"to-vfile": "^6.0.0", | ||
"trough": "^1.0.0", | ||
"unist-util-visit": "^2.0.0" | ||
"to-vfile": "^7.0.0", | ||
"trough": "^2.0.0", | ||
"unified": "^10.0.0", | ||
"unified-engine": "^10.0.1", | ||
"unist-util-visit": "^4.0.0", | ||
"vfile": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"nyc": "^15.0.0", | ||
"@types/hast": "^2.0.0", | ||
"@types/hosted-git-info": "^3.0.0", | ||
"@types/rimraf": "^3.0.1", | ||
"@types/tape": "^4.0.0", | ||
"c8": "^7.0.0", | ||
"prettier": "^2.0.0", | ||
"remark": "^13.0.0", | ||
"remark-cli": "^9.0.0", | ||
"remark-preset-wooorm": "^8.0.0", | ||
"remark": "^14.0.0", | ||
"remark-cli": "^11.0.0", | ||
"remark-preset-wooorm": "^9.0.0", | ||
"rimraf": "^3.0.0", | ||
"strip-ansi": "^6.0.0", | ||
"strip-ansi": "^7.0.0", | ||
"tape": "^5.0.0", | ||
"vfile-sort": "^2.0.0", | ||
"xo": "^0.38.0" | ||
"type-coverage": "^2.0.0", | ||
"typescript": "^5.0.0", | ||
"vfile-sort": "^3.0.0", | ||
"xo": "^0.54.0" | ||
}, | ||
"scripts": { | ||
"build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", | ||
"format": "remark . -qfo --ignore-pattern test/ && prettier . -w --loglevel warn && xo --fix", | ||
"test-api": "node test", | ||
"test-coverage": "nyc --reporter lcov tape test/index.js", | ||
"test": "npm run format && 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", | ||
"test": "npm run build && npm run format && npm run test-coverage" | ||
}, | ||
"nyc": { | ||
"check-coverage": true, | ||
"lines": 100, | ||
"functions": 100, | ||
"branches": 100 | ||
}, | ||
"prettier": { | ||
@@ -88,11 +98,4 @@ "tabWidth": 2, | ||
"prettier": true, | ||
"esnext": false, | ||
"rules": { | ||
"complexity": "off", | ||
"guard-for-in": "off", | ||
"unicorn/no-array-callback-reference": "off", | ||
"unicorn/no-array-for-each": "off", | ||
"unicorn/prefer-number-properties": "off", | ||
"unicorn/prefer-optional-catch-binding": "off", | ||
"unicorn/prefer-includes": "off" | ||
"unicorn/prefer-logical-operator-over-ternary": "off" | ||
} | ||
@@ -104,3 +107,9 @@ }, | ||
] | ||
}, | ||
"typeCoverage": { | ||
"atLeast": 100, | ||
"detail": true, | ||
"strict": true, | ||
"ignoreCatch": true | ||
} | ||
} |
325
readme.md
@@ -11,33 +11,24 @@ # remark-validate-links | ||
[**remark**][remark] plugin to validate that Markdown links and images reference | ||
existing local files and headings. | ||
[**remark**][remark] plugin to check that markdown links and images point to | ||
existing local files and headings in a Git repo. | ||
For example, this document does not have a heading named `Hello`. | ||
So if we’d link to it (`[welcome](#hello)`), we’d get a warning. | ||
Links to headings in other markdown documents (`examples/foo.md#hello`) and | ||
links to files (`license` or `index.js`) are also checked. | ||
In addition, when there’s a link to a heading in another document | ||
(`examples/foo.md#hello`), if that file exists but the heading does not, or if | ||
that file does not exist, we’d also get a warning. | ||
Linking to other files, such as `license` or `index.js` (when they exist) is | ||
fine. | ||
This plugin does not check external URLs (see | ||
[`remark-lint-no-dead-urls`][no-dead-urls]) or undefined references | ||
(see [`remark-lint-no-undefined-references`][no-undef-refs]). | ||
## Note! | ||
This plugin is ready for the new parser in remark | ||
([`remarkjs/remark#536`](https://github.com/remarkjs/remark/pull/536)). | ||
No change is needed: it works exactly the same now as it did before! | ||
## Contents | ||
* [What is this?](#what-is-this) | ||
* [When should I use this?](#when-should-i-use-this) | ||
* [Install](#install) | ||
* [Use](#use) | ||
* [CLI](#cli) | ||
* [API](#api) | ||
* [Configuration](#configuration) | ||
* [API](#api) | ||
* [`unified().use(remarkValidateLinks[, options])`](#unifieduseremarkvalidatelinks-options) | ||
* [Examples](#examples) | ||
* [Example: CLI](#example-cli) | ||
* [Example: CLI in npm scripts](#example-cli-in-npm-scripts) | ||
* [Integration](#integration) | ||
* [Types](#types) | ||
* [Compatibility](#compatibility) | ||
* [Security](#security) | ||
@@ -48,55 +39,50 @@ * [Related](#related) | ||
## Install | ||
## What is this? | ||
[npm][]: | ||
This package is a [unified][] ([remark][]) plugin to check local links in a Git | ||
repo. | ||
```sh | ||
npm install remark-validate-links | ||
``` | ||
**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 inspects mdast. | ||
## Use | ||
## When should I use this? | ||
### CLI | ||
This project is useful if you have a Git repo, such as this one, with docs in | ||
markdown and links to headings and other files, and want to check whether | ||
they’re correct. | ||
Compared to other links checkers, this project can work offline (making it fast | ||
en prone to fewer false positives), and is specifically made for local links in | ||
Git repos. | ||
This plugin does not check external URLs (see | ||
[`remark-lint-no-dead-urls`][no-dead-urls]) or undefined references | ||
(see [`remark-lint-no-undefined-references`][no-undef-refs]). | ||
Use `remark-validate-links` together with [**remark**][remark]: | ||
## Install | ||
```bash | ||
npm install --global remark-cli remark-validate-links | ||
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). | ||
In Node.js (version 14.14+, or 16.0+), install with [npm][]: | ||
```sh | ||
npm install remark-validate-links | ||
``` | ||
Let’s say `readme.md` is this document, and `example.md` looks as follows: | ||
In Deno with [`esm.sh`][esmsh]: | ||
```markdown | ||
# Hello | ||
Read more [whoops, this does not exist](#world). | ||
This doesn’t exist either [whoops!](readme.md#foo). | ||
But this does exist: [license](license). | ||
So does this: [README](readme.md#installation). | ||
```js | ||
import remarkValidateLinks from 'https://esm.sh/remark-validate-links@11' | ||
``` | ||
Now, running `remark -u validate-links .` yields: | ||
In browsers with [`esm.sh`][esmsh]: | ||
```text | ||
example.md | ||
3:11-3:48 warning Link to unknown heading: `world` missing-heading remark-validate-links | ||
5:27-5:51 warning Link to unknown heading in `readme.md`: `foo` missing-heading-in-file remark-validate-links | ||
readme.md: no issues found | ||
⚠ 2 warnings | ||
```html | ||
<script type="module"> | ||
import remarkValidateLinks from 'https://esm.sh/remark-validate-links@11?bundle' | ||
</script> | ||
``` | ||
> Note: passing a file over stdin(4) may not work as expected, because it is not | ||
> known where the file originates from. | ||
## Use | ||
### API | ||
> Note: The API checks links to headings and files. | ||
> It does not check headings in other files. | ||
> In a browser, only local links to headings are checked. | ||
Say we have the following file, `example.md`: | ||
@@ -114,3 +100,3 @@ | ||
Headings in `readme.md` are [checked](readme.md#nosuchheading). | ||
Headings in `readme.md` are [checked](readme.md#no-such-heading). | ||
And [missing files are reported](missing-example.js). | ||
@@ -126,15 +112,18 @@ | ||
And our script, `example.js`, looks as follows: | ||
And a module, `example.js`: | ||
```js | ||
var vfile = require('to-vfile') | ||
var report = require('vfile-reporter') | ||
var remark = require('remark') | ||
var links = require('remark-validate-links') | ||
import {read} from 'to-vfile' | ||
import {remark} from 'remark' | ||
import remarkValidateLinks from 'remark-validate-links' | ||
remark() | ||
.use(links) | ||
.process(vfile.readSync('example.md'), function (err, file) { | ||
console.error(report(err || file)) | ||
}) | ||
main() | ||
async function main() { | ||
const file = await remark() | ||
.use(remarkValidateLinks) | ||
.process(await read('example.md')) | ||
console.log(reporter(file)) | ||
} | ||
``` | ||
@@ -153,52 +142,51 @@ | ||
(Note that `readme.md#nosuchheading` is not warned about, because the API | ||
does not check headings in other Markdown files). | ||
(Note that `readme.md#no-such-heading` is not warned about, because the API does | ||
not check headings in other Markdown files). | ||
## Configuration | ||
## API | ||
This package exports no identifiers. | ||
The default export is `remarkValidateLinks`. | ||
### `unified().use(remarkValidateLinks[, options])` | ||
Check that markdown links and images point to existing local files and headings | ||
in a Git repo. | ||
> ⚠️ **Important**: The API in Node.js checks links to headings and files but | ||
> does not check whether headings in other files exist. | ||
> The API in browsers only checks links to headings in the same file. | ||
> The CLI can check everything. | ||
##### `options` | ||
Typically, you don’t need to configure `remark-validate-links`, as it detects | ||
local Git repositories. | ||
If one is detected that references a known Git host (GitHub, GitLab, | ||
or Bitbucket), some extra links can be checked. | ||
If one is detected that does not reference a known Git host, local links still | ||
work as expected. | ||
If you’re not in a Git repository, you must pass `repository: false` explicitly. | ||
You can pass a `repository` (`string?`, `false`). | ||
###### `options.repository` | ||
URL to hosted Git (`string?` or `false`). | ||
If `repository` is nullish, the Git origin remote is detected. | ||
If the repository resolves to something [npm understands][package-repository] | ||
as a Git host such as GitHub, GitLab, or Bitbucket, full URLs to that host | ||
(say, `https://github.com/remarkjs/remark-validate-links/readme.md#install`) | ||
can also be checked. | ||
If the repository resolves to something [npm understands][package-repository] as | ||
a Git host such as GitHub, GitLab, or Bitbucket, then full URLs to that host | ||
(say, `https://github.com/remarkjs/remark-validate-links/readme.md#install`) can | ||
also be checked. | ||
If you’re not in a Git repository, you must pass `repository: false` | ||
explicitly. | ||
```sh | ||
remark --use 'validate-links=repository:"foo/bar"' example.md | ||
``` | ||
###### `options.root` | ||
For this to work, a `root` (`string?`) is also used, referencing the local Git | ||
root directory (the place where `.git` is). | ||
A `root` (`string?`) can also be passed, referencing the local Git root | ||
directory (the folder that contains `.git`). | ||
If both `root` and `repository` are nullish, the Git root is detected. | ||
If `root` is not given but `repository` is, [`file.cwd`][cwd] is used. | ||
You can define this repository in [configuration files][cli] too. | ||
An example `.remarkrc` file could look as follows: | ||
###### `options.urlConfig` | ||
```json | ||
{ | ||
"plugins": [ | ||
[ | ||
"validate-links", | ||
{ | ||
"repository": "foo/bar" | ||
} | ||
] | ||
] | ||
} | ||
``` | ||
If your project is hosted on `github.com`, `gitlab.com`, or `bitbucket.org`, | ||
this plugin can automatically detect the url configuration. | ||
Otherwise, use `urlConfig` to specify this manually. | ||
For this repository (`remarkjs/remark-validate-links` on GitHub) `urlConfig` | ||
looks as follows: | ||
If you’re self-hosting a Git server, you can provide URL information directly, | ||
as `urlConfig` (`Object`). | ||
For this repository, `urlConfig` looks as follows: | ||
```js | ||
@@ -212,2 +200,4 @@ { | ||
headingPrefix: '#', | ||
// Hash to top of markdown documents: | ||
topAnchor: '#readme', | ||
// Whether lines in files can be linked: | ||
@@ -229,2 +219,82 @@ lines: true | ||
## Examples | ||
### Example: CLI | ||
It’s recommended to use `remark-validate-links` on the CLI with | ||
[`remark-cli`][cli]. | ||
Install both with [npm][]: | ||
```sh | ||
npm install remark-cli remark-validate-links --save-dev | ||
``` | ||
Let’s say we have a `readme.md` (this current document) and an `example.md` | ||
with the following text: | ||
```markdown | ||
# Hello | ||
Read more [whoops, this does not exist](#world). | ||
This doesn’t exist either [whoops!](readme.md#foo). | ||
But this does exist: [license](license). | ||
So does this: [readme](readme.md#install). | ||
``` | ||
Now, running `./node_modules/.bin/remark --use remark-validate-links .` yields: | ||
```txt | ||
example.md | ||
3:11-3:48 warning Link to unknown heading: `world` missing-heading remark-validate-links | ||
5:27-5:51 warning Link to unknown heading in `readme.md`: `foo` missing-heading-in-file remark-validate-links | ||
readme.md: no issues found | ||
⚠ 2 warnings | ||
``` | ||
### Example: CLI in npm scripts | ||
You can use `remark-validate-links` and [`remark-cli`][cli] in an npm script to | ||
check and format markdown in your project. | ||
Install both with [npm][]: | ||
```sh | ||
npm install remark-cli remark-validate-links --save-dev | ||
``` | ||
Then, add a format script and configuration to `package.json`: | ||
```js | ||
{ | ||
// … | ||
"scripts": { | ||
// … | ||
"format": "remark . --quiet --frail --output", | ||
// … | ||
}, | ||
"remarkConfig": { | ||
"plugins": [ | ||
"remark-validate-links" | ||
] | ||
}, | ||
// … | ||
} | ||
``` | ||
> 💡 **Tip**: Add other tools such as prettier or ESLint to check and format | ||
> other files. | ||
> | ||
> 💡 **Tip**: Run `./node_modules/.bin/remark --help` for help with | ||
> `remark-cli`. | ||
Now you check and format markdown in your project with: | ||
```sh | ||
npm run format | ||
``` | ||
## Integration | ||
@@ -235,9 +305,28 @@ | ||
* `node.data.hProperties.name` — Used by [`remark-html`][remark-html] | ||
* `node.data.hProperties.name` — Used by | ||
[`mdast-util-to-hast`][mdast-util-to-hast] | ||
to create a `name` attribute, which anchors can link to | ||
* `node.data.hProperties.id` — Used by [`remark-html`][remark-html] | ||
* `node.data.hProperties.id` — Used by | ||
[`mdast-util-to-hast`][mdast-util-to-hast] | ||
to create an `id` attribute, which anchors can link to | ||
* `node.data.id` — Used, in the future, by other tools to signal | ||
* `node.data.id` — Used potentially in the future by other tools to signal | ||
unique identifiers on nodes | ||
## Types | ||
This package is fully typed with [TypeScript][]. | ||
It exports an `Options` type, which specifies the interface of the accepted | ||
options, and an `UrlConfig` type, which specifies the interface of its | ||
corresponding option. | ||
## 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 `unified` version 6+, `remark` version 7+, and | ||
`remark-cli` version 8+. | ||
## Security | ||
@@ -254,4 +343,6 @@ | ||
* [`remark-lint`][remark-lint] — Markdown code style linter | ||
* [`remark-lint-no-dead-urls`][no-dead-urls] — Ensure external links are alive | ||
* [`remark-lint`][remark-lint] | ||
— markdown code style linter | ||
* [`remark-lint-no-dead-urls`][no-dead-urls] | ||
— check that external links are alive | ||
@@ -302,2 +393,4 @@ ## Contribute | ||
[esmsh]: https://esm.sh | ||
[health]: https://github.com/remarkjs/.github | ||
@@ -315,4 +408,8 @@ | ||
[unified]: https://github.com/unifiedjs/unified | ||
[remark]: https://github.com/remarkjs/remark | ||
[typescript]: https://www.typescriptlang.org | ||
[cli]: https://github.com/remarkjs/remark/tree/HEAD/packages/remark-cli#readme | ||
@@ -322,3 +419,3 @@ | ||
[remark-html]: https://github.com/remarkjs/remark-html | ||
[mdast-util-to-hast]: https://github.com/syntax-tree/mdast-util-to-hast#notes | ||
@@ -325,0 +422,0 @@ [no-dead-urls]: https://github.com/davidtheclark/remark-lint-no-dead-urls |
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
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
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
46712
33
966
419
1
0
Yes
11
16
+ Added@types/mdast@^3.0.0
+ Addedunified@^10.0.0
+ Addedunified-engine@^10.0.1
+ Addedvfile@^5.0.0
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@isaacs/cliui@8.0.2(transitive)
+ Added@npmcli/config@6.4.1(transitive)
+ Added@npmcli/map-workspaces@3.0.6(transitive)
+ Added@npmcli/name-from-folder@2.0.0(transitive)
+ Added@pkgjs/parseargs@0.11.0(transitive)
+ Added@types/concat-stream@2.0.3(transitive)
+ Added@types/debug@4.1.12(transitive)
+ Added@types/is-empty@1.2.3(transitive)
+ Added@types/mdast@3.0.15(transitive)
+ Added@types/ms@0.7.34(transitive)
+ Added@types/node@18.19.64(transitive)
+ Added@types/supports-color@8.1.3(transitive)
+ Addedabbrev@2.0.0(transitive)
+ Addedansi-regex@5.0.16.1.0(transitive)
+ Addedansi-styles@4.3.06.2.1(transitive)
+ Addedbail@2.0.2(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedci-info@4.0.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedconcat-stream@2.0.0(transitive)
+ Addedcross-spawn@7.0.3(transitive)
+ Addeddebug@4.3.7(transitive)
+ Addedeastasianwidth@0.2.0(transitive)
+ Addedemoji-regex@8.0.09.2.2(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedextend@3.0.2(transitive)
+ Addedfault@2.0.1(transitive)
+ Addedforeground-child@3.3.0(transitive)
+ Addedformat@0.2.2(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedgithub-slugger@2.0.0(transitive)
+ Addedglob@10.4.58.1.0(transitive)
+ Addedhosted-git-info@6.1.1(transitive)
+ Addedignore@5.3.2(transitive)
+ Addedimport-meta-resolve@2.2.2(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedini@4.1.3(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-empty@1.2.0(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedis-plain-obj@4.1.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedjackspeak@3.4.3(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedjson-parse-even-better-errors@2.3.13.0.2(transitive)
+ Addedlines-and-columns@2.0.4(transitive)
+ Addedload-plugin@5.1.0(transitive)
+ Addedlru-cache@10.4.37.18.3(transitive)
+ Addedmdast-util-to-string@3.2.0(transitive)
+ Addedminimatch@5.1.69.0.5(transitive)
+ Addedminipass@7.1.2(transitive)
+ Addedms@2.1.3(transitive)
+ Addednopt@7.2.1(transitive)
+ Addednpm-normalize-package-bin@3.0.1(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpackage-json-from-dist@1.0.1(transitive)
+ Addedparse-json@6.0.2(transitive)
+ Addedpath-key@3.1.1(transitive)
+ Addedpath-scurry@1.11.1(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedproc-log@3.0.0(transitive)
+ Addedread-package-json-fast@3.0.2(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsemver@7.6.3(transitive)
+ Addedshebang-command@2.0.0(transitive)
+ Addedshebang-regex@3.0.0(transitive)
+ Addedsignal-exit@4.1.0(transitive)
+ Addedstring-width@4.2.35.1.2(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedstrip-ansi@6.0.17.1.0(transitive)
+ Addedsupports-color@9.4.0(transitive)
+ Addedto-vfile@7.2.4(transitive)
+ Addedtrough@2.2.0(transitive)
+ Addedtypedarray@0.0.6(transitive)
+ Addedundici-types@5.26.5(transitive)
+ Addedunified@10.1.2(transitive)
+ Addedunified-engine@10.1.0(transitive)
+ Addedunist-util-inspect@7.0.2(transitive)
+ Addedunist-util-is@5.2.1(transitive)
+ Addedunist-util-stringify-position@3.0.3(transitive)
+ Addedunist-util-visit@4.1.2(transitive)
+ Addedunist-util-visit-parents@5.1.3(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedvfile@5.3.7(transitive)
+ Addedvfile-message@3.1.4(transitive)
+ Addedvfile-reporter@7.0.5(transitive)
+ Addedvfile-sort@3.0.1(transitive)
+ Addedvfile-statistics@2.0.1(transitive)
+ Addedwalk-up-path@3.0.1(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedwrap-ansi@7.0.08.1.0(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedyaml@2.6.0(transitive)
- Removedgithub-slugger@1.5.0(transitive)
- Removedhosted-git-info@3.0.8(transitive)
- Removedlru-cache@6.0.0(transitive)
- Removedmdast-util-to-string@1.1.0(transitive)
- Removedto-vfile@6.1.0(transitive)
- Removedtrough@1.0.5(transitive)
- Removedunist-util-is@4.1.0(transitive)
- Removedunist-util-stringify-position@2.0.3(transitive)
- Removedunist-util-visit@2.0.3(transitive)
- Removedunist-util-visit-parents@3.1.1(transitive)
- Removedvfile@4.2.1(transitive)
- Removedvfile-message@2.0.4(transitive)
- Removedyallist@4.0.0(transitive)
Updatedgithub-slugger@^2.0.0
Updatedhosted-git-info@^6.0.0
Updatedmdast-util-to-string@^3.2.0
Updatedto-vfile@^7.0.0
Updatedtrough@^2.0.0
Updatedunist-util-visit@^4.0.0