Is there a way to visit a tree one after the other like await? #114
-
i am trying to get this is my code: .use(rehypeRewrite, {
rewrite: (node, index, parent) => {
if (
node.type == 'element' &&
(node.tagName == 'h1' ||
node.tagName == 'h2' ||
node.tagName == 'h3' ||
node.tagName == 'h4' ||
node.tagName == 'h5' ||
node.tagName == 'h6') &&
!node.properties.id
) {
if (
node.children[0].value === title ||
node.children[0].value === tocTitle
)
return
const noOfHastags = '#'.repeat(Number(node.tagName.replace('h', '')))
lis.push(`${noOfHastags} ${node.children[0].value}`)
console.log({ lis })
node.children = [
{
...node.children[0],
properties: { id: slugify(node.children[0].value) },
children: [{ type: 'text', value: node.children[0].value }],
},
]
}
},
})
.use(rehypeRewrite, {
rewrite: (node, index, parent) => {
if (node.type === 'element' && node.tagName === 'body') {
node.children = [
{
type: 'element',
tagName: 'h1',
properties: {},
children: [{ type: 'text', value: title }],
},
{
type: 'element',
tagName: 'nav',
properties: {
id: 'toc',
role: 'doc-toc',
},
children: [
{
type: 'element',
tagName: 'h2',
properties: {},
children: [
{ type: 'text', value: tocTitle || 'Table of Contents' },
],
},
{
type: 'element',
tagName: 'ol',
properties: {},
children: lis.map((li) => {
const text = li.replaceAll('#', '').trim()
const depth = li.match(/#/g || []).length
let properties = {
href: '',
class: '',
}
if (depth > 1) {
properties = {
href: `#${slugify(text)}`,
class: `ml-${(depth - 1) * 3}`,
}
}
return {
type: 'element',
tagName: 'li',
properties: {},
children: [
{
type: 'element',
tagName: 'a',
properties,
children: [
{
type: 'text',
value: text,
},
],
},
],
}
}),
},
],
},
...node.children,
]
}
},
}) the first part of the second part inserts it so the <body>
<h1>my ebook</h1>
<nav id="toc" role="doc-toc">
<h2>table of contents</h2>
<ol>
<li><a href="chapter1/index.html">chapter 1</a></li>
<li><a href="chapter2/index.html">chapter 2</a></li>
<li><a href="chapter3/index.html">chapter 3</a></li>
<li><a href="chapter4/index.html">chapter 4</a></li>
<li><a href="chapter5/index.html">chapter 5</a></li>
</ol>
</nav>
</body> it needs to be in this format only because i'm using vivliostyle but it does toc for however i don't see any initially, i put it inside 1 block of i also tried using another unified processor but that doesn't work either? i also tried simple ways to get toc like using remark-toc but couldn't get it to work so went with this complicated way. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
Yes, transformers plugins, which modify the AST, run in the order they are added in
Your example isn't runnable, so I'm not entirely following what you're saying/asking. From what I can gather |
Beta Was this translation helpful? Give feedback.
-
Yes. Create your own plugins and use To get a ToC from a markdown tree, use |
Beta Was this translation helpful? Give feedback.
-
thank you @ChristianMurphy @wooorm 🎉 i used full code inside scripts/generate-toc.mjs/**
* node imports
*/
import path from 'path'
/**
* unified imports
*/
import { unified } from 'unified'
import { visit } from 'unist-util-visit'
import { read, write } from 'to-vfile'
/**
* remark imports
*/
import remarkRehype from 'remark-rehype'
import remarkParse from 'remark-parse'
/**
* rehype imports
*/
import rehypeDocument from 'rehype-document'
import rehypeStringify from 'rehype-stringify'
import rehypeRewrite from 'rehype-rewrite'
import rehypeFormat from 'rehype-format'
/**
* npm imports
*/
import GithubSlugger from 'github-slugger'
/**
* local imports
*/
import vivliostyleConfig from '../vivliostyle.config.js'
const readMarkdownFiles = async (mdFilePaths, entryContext) => {
return await Promise.all(
mdFilePaths.map(async (mdPath) => {
const content = await read(path.join(entryContext, mdPath), {
encoding: 'utf8',
})
return { path: mdPath, content: content.value }
})
)
}
const main = async () => {
// read entry from vivliostyle.config.js in order & then change toc.html to contain only toc
const { title, entry, entryContext, toc, tocTitle } = vivliostyleConfig
const slugger = new GithubSlugger()
let tocCss = ''
const mdFilePaths = entry.filter((e) => {
if (typeof e === 'string') {
return true
}
if (e.rel === 'contents') {
tocCss = e.theme
}
return false
})
const files = await readMarkdownFiles(mdFilePaths, entryContext)
const link = [
{
rel: 'publication',
href: 'publication.json',
type: 'application/ld+json',
},
]
if (tocCss.length !== 0) {
link.push({
href: `./${tocCss}`,
rel: 'stylesheet',
})
}
let lis = []
let n = 1
files.forEach((file) => {
const tree = unified().use(remarkParse).parse(file.content)
visit(tree, (node) => {
if (node.type === 'heading') {
const noOfHastags = '#'.repeat(node.depth)
const htmlFilePath = file.path.replace('.md', '.html')
const href =
node.depth === 1
? htmlFilePath
: `${htmlFilePath}#${slugger.slug(node.children[0].value)}`
const value = `${noOfHastags} ${node.children[0].value}`
lis.push({ href, value })
}
})
})
const markup = files.map((file) => file.content).join('\n\n')
const processor = await unified()
.use(remarkParse, { fragment: true })
.use(remarkRehype)
.use(rehypeDocument, {
title,
link,
})
.use(rehypeRewrite, {
rewrite: (node, index, parent) => {
if (
node.type == 'element' &&
(node.tagName == 'h1' ||
node.tagName == 'h2' ||
node.tagName == 'h3' ||
node.tagName == 'h4' ||
node.tagName == 'h5' ||
node.tagName == 'h6') &&
!node.properties.id
) {
if (
node.children[0].value === title ||
node.children[0].value === tocTitle
)
return
node.properties.id = slugify(node.children[0].value)
}
if (node.type === 'element' && node.tagName === 'body') {
node.children = [
{
type: 'element',
tagName: 'h1',
properties: {},
children: [{ type: 'text', value: title }],
},
{
type: 'element',
tagName: 'nav',
properties: {
id: 'toc',
role: 'doc-toc',
},
children: [
{
type: 'element',
tagName: 'h2',
properties: {},
children: [
{ type: 'text', value: tocTitle || 'table of contents' },
],
},
{
type: 'element',
tagName: 'ol',
properties: {},
children: lis.map((li) => {
const text = li.value.replaceAll('#', '').trim()
const depth = li.value.match(/#/g || []).length
let properties = {
href: li.href,
class: '',
}
if (depth > 1) {
properties = {
href: li.href,
class: `ml-${(depth - 1) * 3}`,
}
}
return {
type: 'element',
tagName: 'li',
properties: {},
children: [
{
type: 'element',
tagName: 'a',
properties,
children: [
{
type: 'text',
value: text,
},
],
},
],
}
}),
},
],
},
]
}
},
})
.use(rehypeFormat)
.use(rehypeStringify)
try {
const file = await processor.process(markup)
file.path = path.join(entryContext, 'toc')
file.extname = '.html'
await write(file)
} catch (error) {
throw error
}
}
main() |
Beta Was this translation helpful? Give feedback.
thank you @ChristianMurphy @wooorm 🎉
i used
unist-util-visit
for toc &rehype-rewrite
to put the toc inside html.full code inside
scripts/generate-toc.mjs
→ https://github.com/deadcoder0904/vivliostylescripts/generate-toc.mjs