Skip to content

Commit

Permalink
feat!: move to ESM, generated typescript types from JSDoc (#44)
Browse files Browse the repository at this point in the history
* feat!: move to ESM, generated typescript types from JSDoc

* types: leverage types from check-links

* test: initial tests with esm implementation

leverage node:test and node:assert

* ci: add github actions

* test: pass test suite and lint checks

* docs: update badges in readme

* docs: add coverage badge

* build: widen version range

* docs: add notes on new features to changelog

* ci: upgrade covecov github action
  • Loading branch information
ChristianMurphy authored Mar 10, 2023
1 parent f86eefe commit e6489d4
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 6,865 deletions.
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

16 changes: 0 additions & 16 deletions .eslintrc

This file was deleted.

21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: main
on:
- pull_request
- push
jobs:
main:
name: ${{matrix.node}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
- uses: codecov/codecov-action@v3
strategy:
matrix:
node:
- lts/hydrogen
- node
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
node_modules
*.log
coverage/
node_modules/
.DS_Store
coverage
*.d.ts
*.log
yarn.lock
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package-lock=false
ignore-scripts=true
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
coverage/
*.md
4 changes: 0 additions & 4 deletions .travis.yml

This file was deleted.

35 changes: 21 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
# Changelog

## 2.0.0

* Migrate package to [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c)
* Add [TypeScript](https://www.typescriptlang.org/) typings
* Support `remark` version 14

## 1.1.0

- Add `skipUrlPatterns` option.
* Add `skipUrlPatterns` option.

## 1.0.2

- Walk the AST fewer times.
* Walk the AST fewer times.

## 1.0.1

- Bump patch versions of dependencies.
* Bump patch versions of dependencies.

## 1.0.0

- Add `skipLocalhost` option.
* Add `skipLocalhost` option.

## 0.5.0

- Drop Node 6 support.
- Update dependencies to remove deprecation notice about `OutgoingMessage.prototype._headers`.
* Drop Node 6 support.
* Update dependencies to remove deprecation notice about `OutgoingMessage.prototype._headers`.

## 0.4.1

- Bump check-links dependency.
* Bump check-links dependency.

## 0.4.0

- Use [check-links](https://github.com/transitive-bullshit/check-links).
- Replace `baseUrl` option with `gotOptions.baseUrl`.
- Remove `cache` option. check-links does not expose similar cache configuration.
- Drop Node 4 support.
* Use [check-links](https://github.com/transitive-bullshit/check-links).
* Replace `baseUrl` option with `gotOptions.baseUrl`.
* Remove `cache` option.
check-links does not expose similar cache configuration.
* Drop Node 4 support.

## 0.3.0

- Skip URLs with protocols other than `http:` and `https:`.
- Handle offline smoothly and add `skipOffline` option.
* Skip URLs with protocols other than `http:` and `https:`.
* Handle offline smoothly and add `skipOffline` option.

## 0.2.0

- Start this log.
* Start this log.
73 changes: 49 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# remark-lint-no-dead-urls

> [remark-lint] plugin to ensure that external URLs in your Markdown are alive.
> [remark-lint][] plugin to ensure that external URLs in your Markdown are
> alive.
[![NPM](https://img.shields.io/npm/v/remark-lint-no-dead-urls.svg)](https://www.npmjs.com/package/remark-lint-no-dead-urls) [![Build Status](https://travis-ci.org/davidtheclark/remark-lint-no-dead-urls.svg?branch=master)](https://travis-ci.org/davidtheclark/remark-lint-no-dead-urls)
[![Build Status](https://github.com/davidtheclark/remark-lint-no-dead-urls/workflows/main/badge.svg)](https://github.com/davidtheclark/remark-lint-no-dead-urls/actions)
[![Coverage](https://img.shields.io/codecov/c/github/davidtheclark/remark-lint-no-dead-urls.svg)](https://codecov.io/github/davidtheclark/remark-lint-no-dead-urls)
[![NPM](https://img.shields.io/npm/v/remark-lint-no-dead-urls.svg)](https://www.npmjs.com/package/remark-lint-no-dead-urls)
[![Size](https://img.shields.io/bundlephobia/minzip/remark-lint-no-dead-urls.svg)](https://bundlephobia.com/result?p=remark-lint-no-dead-urls)

Checks all of the following:

Expand All @@ -16,35 +20,54 @@ Checks definitions: see the [walrus].
[walrus]: /path/to/walrus.jpg
```

Uses [check-links] to check URLs for liveness.
Uses [check-links][] to check URLs for liveness.

A few details to keep in mind:

- By default, relative URLs are skipped. To check relative URLs, set `gotOptions.baseUrl` (see below).
- Ignores absolute URLs with protocols other than `http:` and `https:`.
- [check-links] memoizes results, so on any given run each URL will only be pinged once; subsequent checks will be returned from the cache.
* By default, relative URLs are skipped.
To check relative URLs, set `gotOptions.baseUrl` (see below).
* Ignores absolute URLs with protocols other than `http:` and `https:`.
* [check-links][] memoizes results, so on any given run each URL will only be
pinged once; subsequent checks will be returned from the cache.

## Usage

Use like any other [remark-lint] plugin. Check out the [remark-lint] documentation for details.
Use like any other [remark-lint][] plugin.
Check out the [remark-lint][] documentation for details.

## Options

All options are optional. The options object may contain any of the following properties:

- **skipOffline** `{boolean}` - Default: `false`.
By default, if you are offline when you run the check you will receive a warning.
If you want to let offline runs quietly pass, set this option to `true`.
- **skipLocalhost** `{boolean}` - Default: `false`.
By default, `localhost` links are treated the same as other links, so if your project is not running locally you'll receive a warning.
If you want to ignore `localhost` links (e.g. `http://localhost/*`, `http://127.0.0.1/*`), set this option to `true`.
- **skipUrlPatterns** `{Array}` - Array of `String` | `RegExp`.
A list of patterns for URLs that should be skipped. Each URL will be tested against each pattern, and will be ignored if `new RegExp(pattern).test(url) === true`. For example, with `skipUrls: [/^http:\/\/(.*)url-to-ignore\.com/, 'https://never-check.com']`, links with the URLs `http://www.url-to-ignore.com/foo` and `https://never-check.com/foo/bar` will not be checked.
- **gotOptions** `{Object}` - Passed through [check-links] to [Got]. See documentation for [Got options](https://github.com/sindresorhus/got#options). With these options, you can customize retry logic, specify custom headers, and more. Here are some specific Got options that you might want to use:
- **gotOptions.baseUrl** `{string}` - Used as the base URL against which relative URLs are checked.
By default, relative URLs are ignored: you must provide this option to check them.
For example, with `baseUrl: 'https://www.github.com'`, the relative URL `/davidtheclark` is checked as `https://www.github.com/davidtheclark`.
- **gotOptions.concurrency** `{number}` - Maximum number of URLs to check concurrently (default `8`).
All options are optional.
The options object may contain any of the following properties:

* **skipOffline** `{boolean}` - Default: `false`.
By default, if you are offline when you run the check you will receive a
warning.
If you want to let offline runs quietly pass, set this option to `true`.
* **skipLocalhost** `{boolean}` - Default: `false`.
By default, `localhost` links are treated the same as other links, so if
your project is not running locally you’ll receive a warning.
If you want to ignore `localhost` links (e.g. `http://localhost/*`,
`http://127.0.0.1/*`), set this option to `true`.
* **skipUrlPatterns** `{Array}` - Array of `String` | `RegExp`.
A list of patterns for URLs that should be skipped.
Each URL will be tested against each pattern, and will be ignored if `new RegExp(pattern).test(url) === true`.
For example, with `skipUrls: [/^http:\/\/(.*)url-to-ignore\.com/, 'https://never-check.com']`,
links with the URLs `http://www.url-to-ignore.com/foo` and `https://never-check.com/foo/bar`
will not be checked.
* **gotOptions** `{Object}` - Passed through [check-links][] to [Got][].
See documentation for [Got options](https://github.com/sindresorhus/got#options).
With these options, you can customize retry logic, specify custom headers,
and more.
Here are some specific Got options that you might want to use:
* **gotOptions.prefixUrl** `{string}` - Used as the base URL against
which relative URLs are checked.
By default, relative URLs are ignored: you must provide this option to
check them.
For example, with `prefixUrl: 'https://www.github.com'`, the relative
URL `/davidtheclark` is checked as `https://www.github.com/davidtheclark`.
* **gotOptions.concurrency** `{number}` - Maximum number of URLs to check
concurrently (default `8`).

## Example

Expand All @@ -60,7 +83,7 @@ When this rule is turned on, the following `invalid.md` is **not** ok:
Here is a [bad link](https://www.github.com/wooom/remark-dead-link)
```

```
```text
1:11-1:68: Link to https://www.github.com/wooom/remark-dead-link is dead
```

Expand Down Expand Up @@ -88,5 +111,7 @@ Here is a [bad relative link](wooorm/remark-dead-link)
```

[check-links]: https://github.com/transitive-bullshit/check-links
[Got]: https://github.com/sindresorhus/got

[got]: https://github.com/sindresorhus/got

[remark-lint]: https://github.com/remarkjs/remark-lint
76 changes: 45 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,82 @@
'use strict';
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import checkLinks from 'check-links'
import isOnline from 'is-online'

const rule = require('unified-lint-rule');
const visit = require('unist-util-visit');
const checkLinks = require('check-links');
const isOnline = require('is-online');
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Link} Link
* @typedef {import('mdast').Image} Image
* @typedef {import('mdast').Definition} Definition
*
* @typedef {Object} Options
* @property {import('got').OptionsOfTextResponseBody} [gotOptions]
* @property {boolean} [skipLocalhost]
* @property {boolean} [skipOffline]
* @property {Array<string | RegExp>} [skipUrlPatterns]
*/

/** @type {import('unified-lint-rule').Rule<Root, Options>} */
function noDeadUrls(ast, file, options) {
const urlToNodes = {};
/** @type {{[url: string]: Array<Link | Image | Definition>}} */
const urlToNodes = {}

const aggregate = (node) => {
const url = node.url;
if (!url) return;
visit(ast, ['link', 'image', 'definition'], (node) => {
const url = /** @type {Link | Image | Definition} */ (node).url
if (
options.skipLocalhost &&
/^(https?:\/\/)(localhost|127\.0\.0\.1)(:\d+)?/.test(url)
) {
return;
return
}

if (
options.skipUrlPatterns &&
options.skipUrlPatterns.some((skipPattern) =>
new RegExp(skipPattern).test(url)
)
) {
return;
return
}

if (!urlToNodes[url]) {
urlToNodes[url] = [];
urlToNodes[url] = []
}

urlToNodes[url].push(node);
};

visit(ast, ['link', 'image', 'definition'], aggregate);
urlToNodes[url].push(/** @type {Link | Image | Definition} */ (node))
})

return checkLinks(Object.keys(urlToNodes), options.gotOptions).then(
(results) => {
Object.keys(results).forEach((url) => {
const result = results[url];
if (result.status !== 'dead') return;
for (const url of Object.keys(results)) {
const result = results[url]
if (result.status !== 'dead') continue

const nodes = urlToNodes[url];
if (!nodes) return;
const nodes = urlToNodes[url]

for (const node of nodes) {
file.message(`Link to ${url} is dead`, node);
file.message(`Link to ${url} is dead`, node)
}
});
}
}
);
)
}

function wrapper(ast, file, options) {
options = options || {};
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
function wrapper(ast, file, options = {}) {
return isOnline().then((online) => {
if (!online) {
if (!options.skipOffline) {
file.message('You are not online and have not set skipOffline: true.');
file.message('You are not online and have not set skipOffline: true.')
}
return;

return
}
return noDeadUrls(ast, file, options);
});

return noDeadUrls(ast, file, options)
})
}

module.exports = rule('remark-lint:no-dead-urls', wrapper);
const remarkLintNoDeadLinks = lintRule('remark-lint:no-dead-urls', wrapper)

export default remarkLintNoDeadLinks
Loading

0 comments on commit e6489d4

Please sign in to comment.