Skip to content

Commit

Permalink
Merge pull request #49 from photogabble/patch/v1.1.0/#45
Browse files Browse the repository at this point in the history
FEATURE: Make dead link report configurable
  • Loading branch information
carbontwelve authored May 9, 2024
2 parents 5cdf0de + f234d33 commit d11e2ed
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Make dead link report configurable (#49)
- Remove internal dependency upon slugify (#48)
- Add support for custom rendering functions (#47)
- Add support for referencing files by path (#44)
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type EleventyPluginInterlinkOptions = {
// that returns [UNABLE TO LOCATE EMBED].
unableToLocateEmbedFn?: ErrorRenderFn,

// deadLinkReport is the desired output format of the dead link report, by default its set to 'console'
deadLinkReport?: 'console' | 'json' | 'none',

// resolvingFns contains functions used for resolving a wikilinks output.
// see the Custom Resolving Functions section below
resolvingFns?: Map<string, (link: WikilinkMeta, currentPage: any, interlinker: Interlinker) => Promise<string>>
Expand Down Expand Up @@ -191,6 +194,12 @@ You can then display this information in any way you would like, I use the below
{% endif %}
```

### Dead link Report

The default behaviour of this plugin is to report to the console every broken Wikilink and internal link. This behaviour is configurable via the `deadLinkReport` config option. This option accepts three values: `none`, `console` and `json` with `console` being the default.

Setting the value to `none` will disable the dead link report while setting it to `json` will silence console output instead writing to `.dead-links.json` within the project root folder.

### Page lookup logic

This plugin will attempt to identify the page being linked using the following steps in order:
Expand Down
3 changes: 3 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type EleventyPluginInterlinkOptions = {
// slug that you are using. This defaults to a function that returns [UNABLE TO LOCATE EMBED].
unableToLocateEmbedFn?: ErrorRenderFn,

// deadLinkReport is the desired output format of the dead link report, by default its set to 'console'
deadLinkReport?: 'console' | 'json' | 'none',

// resolvingFns is a list of resolving functions. These are invoked by a wikilink containing a `:` character
// prefixed by the fn name. The page in this case is the linking page.
resolvingFns?: Map<string, (link: WikilinkMeta, currentPage: any, interlinker: Interlinker) => Promise<string>>,
Expand Down
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = function (eleventyConfig, options = {}) {
layoutKey: 'embedLayout',
layoutTemplateLangKey: 'embedLayoutLanguage',
resolvingFns: new Map(),
deadLinkReport: 'console',
}, options);

// TODO: deprecate usage of unableToLocateEmbedFn in preference of using resolving fn
Expand Down Expand Up @@ -47,7 +48,9 @@ module.exports = function (eleventyConfig, options = {}) {
// After 11ty has finished generating the site output a list of wikilinks that do not link to
// anything.
// TODO: 1.1.0 have this clear the interlinker cache so that next time 11ty builds its starting from fresh data! (#24)
eleventyConfig.on('eleventy.after', () => interlinker.deadLinks.report());
eleventyConfig.on('eleventy.after', () => {
if (opts.deadLinkReport !== 'none') interlinker.deadLinks.report(opts.deadLinkReport)
});

// Teach Markdown-It how to display MediaWiki Links.
eleventyConfig.amendLibrary('md', (md) => {
Expand Down
40 changes: 29 additions & 11 deletions src/dead-links.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const path = require('node:path');
const chalk = require("chalk");
module.exports = class DeadLinks {
const fs = require('node:fs');

module.exports = class DeadLinks {
constructor() {
this.gravestones = new Map;
this.fileSrc = 'unknown';
Expand All @@ -25,17 +27,33 @@ module.exports = class DeadLinks {
this.gravestones.set(link, names);
}

report() {
for (const [link, files] of this.gravestones.entries()) {
console.warn(
chalk.blue('[@photogabble/wikilinks]'),
chalk.yellow('WARNING'),
`${(link.includes('href') ? 'Link' : 'Wikilink')} (${link}) found pointing to to non-existent page in:`
);

for (const file of files) {
console.warn(`\t- ${file}`);
/**
* @param {'console'|'json'} format
*/
report(format) {
if (format === 'console') {
for (const [link, files] of this.gravestones.entries()) {
console.warn(
chalk.blue('[@photogabble/wikilinks]'),
chalk.yellow('WARNING'),
`${(link.includes('href') ? 'Link' : 'Wikilink')} (${link}) found pointing to to non-existent page in:`
);

for (const file of files) {
console.warn(`\t- ${file}`);
}
}
return;
}

let obj = {};
for (const [link, files] of this.gravestones.entries()) {
obj[link] = files;
}

fs.writeFileSync(
path.join(process.env.ELEVENTY_ROOT, '.dead-links.json'),
JSON.stringify(obj)
);
}
}
14 changes: 13 additions & 1 deletion tests/eleventy.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
const Eleventy = require("@11ty/eleventy");
const {normalize, consoleMockMessages, findResultByUrl, fixturePath} = require('./helpers');
const test = require("ava");
const fs = require('node:fs');
const sinon = require("sinon");
const test = require("ava");

// NOTE: Tests using sinon to mock console.warn need to be run with
// `test.serial` so that they don't run at the same time as one another to
// avoid the "Attempted to wrap warn which is already wrapped" error.
// @see https://stackoverflow.com/a/37900956/1225977

test.afterEach(() => {
const deadLinksPathname = fixturePath('website-with-custom-resolving-fn/.dead-links.json');
if (fs.existsSync(deadLinksPathname)) {
fs.rmSync(deadLinksPathname);
}
})

test("Sample small Website (wikilinks and regular links)", async t => {
let elev = new Eleventy(fixturePath('sample-small-website'), fixturePath('sample-small-website/_site'), {
configPath: fixturePath('sample-small-website/eleventy.config.js'),
Expand Down Expand Up @@ -217,10 +225,14 @@ test("Custom resolving functions are invoked", async t => {
configPath: fixturePath('website-with-custom-resolving-fn/eleventy.config.js'),
});

t.false(fs.existsSync(fixturePath('website-with-custom-resolving-fn/.dead-links.json')));

let results = await elev.toJSON();

t.is(
normalize(findResultByUrl(results, '/').content),
`<div><p>These wikilinks use custom resolving functions:</p><ul><li>Hello RWC!</li><li><a href="https://github.com/photogabble/eleventy-plugin-interlinker/issues/19">#19</a></li></ul></div><div></div>`
);

t.true(fs.existsSync(fixturePath('website-with-custom-resolving-fn/.dead-links.json')));
});
3 changes: 3 additions & 0 deletions tests/fixtures/sample-with-hash-in-title/eleventy.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(
require('../../../index.js'),
{
deadLinkReport: 'none'
}
);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = function (eleventyConfig) {
['howdy', (link, page) => `Hello ${link.name}!`],
['issue', (link, page) => `<a href="${page.data.github}/issues/${link.name}">#${link.name}</a>`],
]),
deadLinkReport: 'json',
}
);

Expand Down

0 comments on commit d11e2ed

Please sign in to comment.