Skip to content

Commit

Permalink
feat: Generate README table of contents (#137)
Browse files Browse the repository at this point in the history
* Test references

* Test

* Test indentation

* Test indentation

* Add angle brackets

* Update target

* Trailing underscore

* Try lowercase

* Try nested contents

* Try unordered list

* Test without new line

* Add more contents

* Add new lines

* Add script for generating TOC

* Generate TOC

* Add generate TOC to postgenerate script

* Fix the hyperlinks produced by the generate TOC script

* Improve script's readability

* More readability improvements

* Update package.json

Co-authored-by: Evan Sosenko <[email protected]>

---------

Co-authored-by: Evan Sosenko <[email protected]>
  • Loading branch information
andrii-balitskyi and razor-x authored Aug 2, 2024
1 parent a561bbf commit 1ae8408
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
55 changes: 55 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,61 @@ accurate and fully typed.

.. _seam_home: https://www.seam.co

Contents
--------

* `Installation <Installation_>`_

* `Usage <Usage_>`_

* `Examples <Examples_>`_

* `List devices <List devices_>`_

* `Unlock a door <Unlock a door_>`_

* `Authentication Method <Authentication Method_>`_

* `API Key <API Key_>`_

* `Personal Access Token <Personal Access Token_>`_

* `Action Attempts <Action Attempts_>`_

* `Interacting with Multiple Workspaces <Interacting with Multiple Workspaces_>`_

* `Webhooks <Webhooks_>`_

* `Advanced Usage <Advanced Usage_>`_

* `Setting the endpoint <Setting the endpoint_>`_

* `Development and Testing <Development and Testing_>`_

* `Quickstart <Quickstart_>`_

* `Source Code <Source Code_>`_

* `Requirements <Requirements_>`_

* `Tests <Tests_>`_

* `Publishing <Publishing_>`_

* `Automatic <Automatic_>`_

* `Manual <Manual_>`_

* `GitHub Actions <GitHub Actions_>`_

* `Secrets for Optional GitHub Actions <Secrets for Optional GitHub Actions_>`_

* `Contributing <Contributing_>`_

* `License <License_>`_

* `Warranty <Warranty_>`_

Installation
------------

Expand Down
92 changes: 92 additions & 0 deletions generate-readme-toc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import fs from 'fs/promises'

const README_FILE = 'README.rst'
const CONTENTS_HEADING = 'Contents'
const CONTENTS_UNDERLINE = '--------'
const TOC_INDENT_LEVELS = {
'-': 1,
'~': 2,
'^': 3,
}
const HEADING_UNDERLINE_REGEX = /^[-~^]+$/

async function generateTableOfContents(content) {
const lines = content.split('\n')
const headings = []
let contentsHeadingIndex = -1
let parsingHeadings = false

for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
const nextLine = lines[i + 1]?.trim() || ''

if (line === CONTENTS_HEADING && nextLine.startsWith(CONTENTS_UNDERLINE)) {
contentsHeadingIndex = i
parsingHeadings = true // Start parsing headings after the "Contents" section
i++ // Skip the underline
continue
}

if (
parsingHeadings &&
nextLine &&
nextLine.match(HEADING_UNDERLINE_REGEX)
) {
const level = TOC_INDENT_LEVELS[nextLine[0]] || 0

if (level > 0 && line) {
headings.push({ text: line, level })
}

i++
}
}

// Generate table of contents
const toc = ['']
headings.forEach((heading) => {
const indent = ' '.repeat(heading.level - 1)

toc.push(`${indent}* \`${heading.text} <${heading.text}_>\`_`)
toc.push('')
})

return { toc, contentsHeadingIndex }
}

function findTocEndIndex(lines, startIndex) {
return lines.findIndex(
(line, index) =>
index > startIndex + 2 &&
!line.trim().startsWith('*') &&
line.trim() !== '',
)
}

async function updateReadme() {
try {
const content = await fs.readFile(README_FILE, 'utf8')
const { toc, contentsHeadingIndex } = await generateTableOfContents(content)

if (contentsHeadingIndex === -1) {
throw new Error('Contents heading not found in the README.')
}

const lines = content.split('\n')
const tocEndIndex = findTocEndIndex(lines, contentsHeadingIndex)

const newContent = [
...lines.slice(0, contentsHeadingIndex + 2), // Include content before "Contents" heading, the heading itself and its underline
...toc,
...lines.slice(tocEndIndex),
].join('\n')

await fs.writeFile(README_FILE, newContent)
console.log(`Table of Contents has been updated in ${README_FILE}`)
} catch (error) {
console.error('Error updating README:', error.message)
process.exit(1)
}
}

updateReadme()
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"type": "module",
"scripts": {
"generate": "node generate-routes.js",
"pregenerate": "node generate-readme-toc.js",
"postgenerate": "make format",
"format": "prettier --write --ignore-path .gitignore .",
"start": "fake-seam-connect --seed"
Expand Down

0 comments on commit 1ae8408

Please sign in to comment.