diff --git a/index.d.ts b/index.d.ts index aa5af5c..5fedfb3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,11 +1,20 @@ /* eslint-disable @typescript-eslint/ban-types, @typescript-eslint/consistent-type-definitions */ +import type {Options as DeadOrAliveOptions} from 'dead-or-alive' + export {default} from './lib/index.js' /** * Configuration. */ export interface Options { + /** + * Options passed to `dead-or-alive` + * (optional); + * `deadOrAliveOptions.findUrls` is always off as further URLs are not used + * by `remark-lint-no-dead-urls`. + */ + deadOrAliveOptions?: DeadOrAliveOptions | null | undefined /** * Check relative values relative to this URL * (optional, example: `'https://example.com/from'`). @@ -14,7 +23,9 @@ export interface Options { /** * Whether to ignore `localhost` links such as `http://localhost/*`, * `http://127.0.0.1/*` - * (default: `false`). + * (default: `false`); + * shortcut for a skip pattern of + * `/^(https?:\/\/)(localhost|127\.0\.0\.1)(:\d+)?/`. */ skipLocalhost?: boolean | null | undefined /** diff --git a/lib/index.js b/lib/index.js index b577321..03da460 100644 --- a/lib/index.js +++ b/lib/index.js @@ -33,6 +33,19 @@ export default remarkLintNoDeadUrls /** * Check URLs. * + * ###### Notes + * + * To improve performance, + * decrease `deadOrAliveOptions.maxRetries` and/or decrease the value used + * for `deadOrAliveOptions.sleep`. + * The normal behavior is to assume connections might be flakey and to sleep a + * while and retry a couple times. + * + * If you do not care about whether anchors work and HTML redirects you can + * pass `deadOrAliveOptions.checkAnchor: false` and + * `deadOrAliveOptions.followMetaHttpEquiv: false`, + * which enables a fast path without parsing HTML. + * * @param {Root} tree * Tree. * @param {VFile} file @@ -55,6 +68,7 @@ async function rule(tree, file, options) { if (settings.skipLocalhost) { defaultSkipUrlPatterns.push(/^(https?:\/\/)(localhost|127\.0\.0\.1)(:\d+)?/) + // To do: this is broken?! return } @@ -81,6 +95,11 @@ async function rule(tree, file, options) { ? new URL(meta.pathname, meta.origin).href : undefined) + const deadOrAliveOptions = { + ...settings.deadOrAliveOptions, + findUrls: false + } + visit(tree, function (node) { if ('url' in node && typeof node.url === 'string') { const value = node.url @@ -129,19 +148,7 @@ async function rule(tree, file, options) { urls.map(async function (url) { const nodes = nodesByUrl.get(url) assert(nodes) - const result = await deadOrAlive(url, { - findUrls: false - // To do: - // * `anchorAllowlist` - // * `checkAnchor` - // * `followMetaHttpEquiv` - // * `maxRedirects` - // * `maxRetries` - // * `resolveClobberPrefix` - // * `sleep` - // * `timeout` - // * `userAgent` - }) + const result = await deadOrAlive(url, deadOrAliveOptions) for (const node of nodes) { for (const message of result.messages) { diff --git a/package.json b/package.json index 21fb391..1ad4237 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@types/node": "^20.0.0", - "c8": "^9.0.0", + "c8": "^10.0.0", "prettier": "^3.0.0", "remark": "^15.0.0", "remark-cli": "^12.0.0", diff --git a/test.js b/test.js index 10aa181..00943e8 100644 --- a/test.js +++ b/test.js @@ -299,6 +299,34 @@ test('should support anchors', async () => { ) }) +test('should support `deadOrAlive` options', async () => { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + const site = mockAgent.get('https://example.com') + + site.intercept({path: '/'}).reply(200, '