|
| 1 | +// @ts-check |
| 2 | + |
| 3 | +/** @type {import('markdownlint').Options} */ |
| 4 | +const options = { |
| 5 | + config: { |
| 6 | + // TODO: enable some of these rules |
| 7 | + 'line-length': false, |
| 8 | + 'no-multiple-blanks': false, |
| 9 | + 'no-hard-tabs': false, |
| 10 | + 'no-space-in-links': false, |
| 11 | + 'no-space-in-emphasis': false, |
| 12 | + 'blanks-around-fences': false, |
| 13 | + 'list-marker-space': false, |
| 14 | + 'ol-prefix': false, |
| 15 | + 'ul-style': false, |
| 16 | + 'ul-indent': false, |
| 17 | + 'no-duplicate-header': false, |
| 18 | + 'no-emphasis-as-heading': false, |
| 19 | + 'no-trailing-punctuation': false, |
| 20 | + 'heading-increment': false, |
| 21 | + 'first-line-heading': false, |
| 22 | + 'fenced-code-language': false, |
| 23 | + 'commands-show-output': false, |
| 24 | + 'no-inline-html': false, |
| 25 | + 'no-alt-text': false, |
| 26 | + }, |
| 27 | + customRules: [{ |
| 28 | + // Check relative links have a file extension, and file exists |
| 29 | + names: ['relative-links-have-ext'], |
| 30 | + description: 'for Docusaurus routing', |
| 31 | + tags: ['links'], |
| 32 | + information: new URL('https://github.com/oasisprotocol/docs/issues/4'), |
| 33 | + function: ({ name, tokens }, onError) => { |
| 34 | + const fs = require('fs'); |
| 35 | + const path = require('path'); |
| 36 | + const dir = path.dirname(name); |
| 37 | + for (const token of tokens) { |
| 38 | + if (token.type === 'inline') { |
| 39 | + for (const child of token.children) { |
| 40 | + if (child.type === 'link_open') { |
| 41 | + const [_key, href] = child.attrs.find(([key, value]) => key === 'href'); |
| 42 | + const isAbsoluteUrl = new URL(href, 'relative://a.b').protocol !== 'relative:'; |
| 43 | + const isAbsolutePathOrNetworkPath = href.startsWith('/'); |
| 44 | + const isFragment = href.startsWith('#'); |
| 45 | + |
| 46 | + const isRelativePath = !isAbsoluteUrl && !isAbsolutePathOrNetworkPath && !isFragment; |
| 47 | + if (isRelativePath) { |
| 48 | + const relativePath = href.split('#')[0]; |
| 49 | + const missingExtension = !path.extname(relativePath); |
| 50 | + const missingFile = !fs.existsSync(path.join(dir, relativePath)); |
| 51 | + if (missingExtension) { |
| 52 | + const postfixSuggestions = ['.md', '.mdx', 'README.md', 'README.mdx', '/README.md', '/README.mdx', 'index.md', '/index.md']; |
| 53 | + const goodPostfix = postfixSuggestions.find( |
| 54 | + postfix => fs.existsSync(path.join(dir, relativePath + postfix)) |
| 55 | + ); |
| 56 | + const postfixColumn = 1 + child.line.indexOf(href) + relativePath.length; |
| 57 | + const canFix = goodPostfix && child.line.indexOf(href) >= 0; |
| 58 | + onError({ |
| 59 | + lineNumber: child.lineNumber, |
| 60 | + context: href, |
| 61 | + detail: 'Filename extension missing', |
| 62 | + fixInfo: !canFix ? undefined : { |
| 63 | + insertText: goodPostfix, |
| 64 | + editColumn: postfixColumn, |
| 65 | + }, |
| 66 | + }); |
| 67 | + } else if (missingFile) { |
| 68 | + onError({ |
| 69 | + lineNumber: child.lineNumber, |
| 70 | + context: href, |
| 71 | + detail: 'File missing', |
| 72 | + }); |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + } |
| 79 | + }, |
| 80 | + }], |
| 81 | +}; |
| 82 | + |
| 83 | +module.exports = options; |
0 commit comments