-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
264cee4
commit 374f11f
Showing
14 changed files
with
443 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,90 @@ | ||
/* | ||
import { lintRule } from 'unified-lint-rule' | ||
import findTOC from './lib/findTOC.js' | ||
import createListOfSections from './lib/createListOfSections.js' | ||
import getTOCSections from './lib/getTOCSections.js' | ||
|
||
1. Find TOC: | ||
- Look for list items (listItem) that contain links (link) with URLs pointing to headings within the document (usually identified by URLs starting with #). | ||
function checkTOC(tree, file) { | ||
const toc = findTOC(tree) | ||
|
||
2. Collect all headings from document. | ||
// If TOC not detected - stop plugin | ||
if (!toc) { | ||
file.data.customInfo = { | ||
message: 'No TOC detected' | ||
} | ||
|
||
3. Find TOC elemets and compare them with the existing titles. | ||
return false | ||
} | ||
|
||
*/ | ||
// Create a list of sections from documents that represent TOC | ||
const docSections = createListOfSections(tree) | ||
const docSectionsLength = docSections.length | ||
|
||
import { lintRule } from 'unified-lint-rule' | ||
import { visit } from 'unist-util-visit' | ||
// Collect TOC headings in object for quick checking | ||
const tocSections = getTOCSections(toc) | ||
|
||
function checkToc(tree, file) { | ||
// Find out if the document contains a TOC | ||
let tocIsExist = false | ||
visit(tree, 'listItem', node => { | ||
if (node.children[0].type === 'paragraph') { | ||
// Try to extract URL | ||
const { url } = node.children[0].children[0] | ||
|
||
// If so - check is it TOC URL or not | ||
if (url && url.startsWith('#')) { | ||
tocIsExist = true | ||
return | ||
} | ||
} | ||
}) | ||
// Check if forgot to update TOC when sections was deleted | ||
if (tocSections.list.length > docSectionsLength) { | ||
file.message( | ||
`TOС contains more sections than document`, | ||
toc // TOC position | ||
) | ||
} | ||
|
||
// If the TOC is not detected, no further calculations are performed | ||
if (!tocIsExist) { | ||
file.data.customInfo = { | ||
message: 'No TOC detected. Stopping the plugin.' | ||
} | ||
// Check correspondence of document sections to the existing TOC | ||
for (let i = 0; i < docSectionsLength; i++) { | ||
const { title, url, depth } = docSections[i] | ||
|
||
return | ||
} | ||
// Check that the document section is included in the table of contents. | ||
if (!tocSections[title]) { | ||
file.message( | ||
`Section: '${title}' was not found in TOС`, | ||
toc // TOC position | ||
) | ||
|
||
// Collect all headings and add right URLs | ||
const headings = {} | ||
continue // If not - check next section | ||
} | ||
|
||
visit(tree, 'heading', node => { | ||
const heading = node.children[0].value | ||
headings[heading] = { | ||
url: | ||
'#' + | ||
heading | ||
.toLowerCase() | ||
.replace(/\s+/g, '-') | ||
.replace(/[^\w-]+/g, '') | ||
// Check that URl is correct | ||
if (url !== tocSections[title].url) { | ||
file.message( | ||
`Incorrect URL in TOС: '${tocSections[title].url}'`, | ||
tocSections[title].position // position node with url in TOC | ||
) | ||
} | ||
}) | ||
|
||
// Find TOC elemets and compare them with the existing titles | ||
visit(tree, 'link', node => { | ||
if (node.url.startsWith('#')) { | ||
const tocTitle = node.children[0].value | ||
// Check the order of sections in TOС | ||
if (title !== tocSections.list[i]) { | ||
// Find index of the section in existing TOC | ||
const sectionIndex = tocSections.list.indexOf(title) | ||
|
||
if (!headings[tocTitle] || node.url !== headings[tocTitle].url) { | ||
if (i === 0) { | ||
file.message( | ||
`Incorrect title or URL in TOC. Title: ${tocTitle}, URL: ${node.url}`, | ||
node | ||
`Incorrect order in TOC: '${title}' must be first element`, | ||
tocSections[title].position | ||
) | ||
} // Check that the sections are in the same order as in the document | ||
else if ( | ||
docSections[i - 1].title !== tocSections.list[sectionIndex - 1] | ||
) { | ||
file.message( | ||
`Incorrect order in TOC: '${title}' must be after '${ | ||
docSections[i - 1].title | ||
}'`, | ||
tocSections[title].position | ||
) | ||
} | ||
} | ||
}) | ||
|
||
// Check the section hierarchy according to the heading levels in the document | ||
if (depth !== tocSections[title].depth) { | ||
file.message( | ||
`Incorrect section hierarchy in TOC: '${title}' `, | ||
tocSections[title].position // position node with url in TOC | ||
) | ||
} | ||
} | ||
} | ||
|
||
const remarkLintCheckToc = lintRule('remark-lint:check-toc', checkToc) | ||
const remarkLintCheckTOC = lintRule('remark-lint:check-toc', checkTOC) | ||
|
||
export default remarkLintCheckToc | ||
export default remarkLintCheckTOC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { visit } from "unist-util-visit"; | ||
import { slug } from "github-slugger"; | ||
|
||
export default function createListOfSections(tree) { | ||
const docSections = []; // Use array for represent order of sections in doc | ||
|
||
visit(tree, "heading", (node) => { | ||
if (node.depth > 1) { | ||
const title = node.children[0].value; | ||
docSections.push({ | ||
depth: node.depth - 1, // Heading hierarchy level in TOC (## = 1, or ### = 2 etc.) | ||
title: title, | ||
url: "#" + slug(title), | ||
position: node.position, | ||
}); | ||
} | ||
}); | ||
|
||
return docSections; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { find } from "unist-util-find"; | ||
|
||
export default function findTOC(tree) { | ||
return find(tree, (node) => { | ||
return ( | ||
node.type === "list" && | ||
node.children.some((listItem) => { | ||
return ( | ||
listItem.type === "listItem" && | ||
listItem.children.some((paragraph) => { | ||
return ( | ||
paragraph.type === "paragraph" && | ||
paragraph.children.some((linkNode) => { | ||
return ( | ||
linkNode.type === "link" && | ||
linkNode.url && | ||
linkNode.url.startsWith("#") | ||
); | ||
}) | ||
); | ||
}) | ||
); | ||
}) | ||
); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { visitParents } from "unist-util-visit-parents"; | ||
|
||
export default function getTOCSections(tocList) { | ||
const tocSections = { list: [] }; // list array that represent order of headings in TOC | ||
visitParents(tocList, "link", (node, ancestors) => { | ||
const title = node.children[0].value; | ||
tocSections[title] = { | ||
depth: ancestors.filter((ancestor) => ancestor.type === "listItem") | ||
.length, // Get the heading hierarchy level | ||
url: node.url, | ||
position: node.position, | ||
}; | ||
|
||
tocSections.list.push(title); | ||
}); | ||
|
||
return tocSections; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# My Doc with TOC | ||
|
||
*** | ||
|
||
* [Heading One](#heading-one) | ||
* [Heading Two](#heading-two) | ||
* [Sub Heading One](#sub-heading-one) | ||
* [Sub Heading Three](#sub-heading-three) | ||
* [Heading Three](#heading-three) | ||
* [Heading Four](#heading-four) | ||
|
||
## Heading One | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Two | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
### Sub Heading One | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
### Sub Heading Three | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Three | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Four | ||
|
||
* Item 1 | ||
* Item 2 | ||
* Sub Item 1 | ||
* [Sub Item Link](https://www.google.com/) | ||
* `code block` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# My Doc with TOC | ||
|
||
*** | ||
|
||
* [Heading One](#heading-one) | ||
* [Heading Two](#eading-two) | ||
* [Sub Heading One](#sub-heading-one) | ||
* [Sub Heading Three](#sub-heading-three) | ||
* [Heading Three](#headingthree) | ||
* [Heading Four](#heaing-four) | ||
|
||
## Heading One | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Two | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
### Sub Heading One | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
### Sub Heading Three | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Three | ||
|
||
Lorem ipsum dolor sit amet. | ||
|
||
## Heading Four | ||
|
||
* Item 1 | ||
* Item 2 | ||
* Sub Item 1 | ||
* [Sub Item Link](https://www.google.com/) | ||
* `code block` |
File renamed without changes.
Oops, something went wrong.