diff --git a/src/partials/page.hbs b/src/partials/page.hbs index 01b3a54..6e99461 100644 --- a/src/partials/page.hbs +++ b/src/partials/page.hbs @@ -16,13 +16,26 @@ {{/if}} @@ -31,7 +44,7 @@
{{/if}} diff --git a/src/views/develop/guides/_sidebar.md b/src/views/develop/guides/_sidebar.md index 35efe4a..ae9b145 100644 --- a/src/views/develop/guides/_sidebar.md +++ b/src/views/develop/guides/_sidebar.md @@ -1,3 +1,5 @@ +# All Guides + * :bi-book:Getting Started * [Introduction](/develop/guides) * [Environment Setup](/develop/guides/env-setup) diff --git a/webpack.config.mjs b/webpack.config.mjs index 57d771f..8fd9659 100644 --- a/webpack.config.mjs +++ b/webpack.config.mjs @@ -5,11 +5,7 @@ import FaviconsBundlerPlugin from "html-bundler-webpack-plugin/plugins/favicons- import ImageMinimizerPlugin from "image-minimizer-webpack-plugin"; import {PurgeCSSPlugin} from "purgecss-webpack-plugin"; import CssMinimizerPlugin from "css-minimizer-webpack-plugin"; -import {remark} from "remark"; -import remarkRehype from "remark-rehype"; -import rehypeStringify from "rehype-stringify"; -import remarkBootstrapIcon from "./webpack/remark/bootstrap-icon.js"; -import {rehypeLinkActive} from "./webpack/rehype/link-active.js"; +import markdownToPage from "./webpack/markdown-to-page.js"; const babelLoader = { loader: 'babel-loader', @@ -19,39 +15,13 @@ const babelLoader = { } }; -/** - * @param relative {string} - * @return {string} - */ -function getPagePath(relative) { - let segs = relative.split(path.sep); - if (segs[0] === 'src' && segs[1] === 'views') { - segs = segs.slice(2); - } - if (segs[0] === 'index') { - return '/'; - } - const parsed = path.parse(segs.join(path.posix.sep)); - return `/${path.posix.format(parsed.name === 'index' ? {root: parsed.dir} : {dir: parsed.dir, name: parsed.name})}`; -} - /** @type {HtmlBundlerPlugin.LoaderOptions} */ const HtmlBundlerMarkdownOptions = { - beforePreprocessor({content, meta}, {resourcePath, data, context, rootContext, fs}) { - if (!resourcePath.endsWith('.md')) { + beforePreprocessor({content, meta}, loaderContext) { + if (!loaderContext.resourcePath.endsWith('.md')) { return undefined; } - const sidebarEnt = fs.readdirSync(path.dirname(resourcePath)) - .find(ent => ent.startsWith('_sidebar.')); - const processor = remark() - .use(remarkBootstrapIcon) - .use(remarkRehype, {allowDangerousHtml: true}) - .use(rehypeLinkActive, {active: getPagePath(path.relative(rootContext, resourcePath))}) - .use(rehypeStringify, {allowDangerousCharacters: true, allowDangerousHtml: true}); - const sidebar = sidebarEnt && processor - .processSync(fs.readFileSync(path.join(context, sidebarEnt))); - Object.assign(data, meta, sidebar && {sidebar}); - return `{{#> page }}${content}{{/page}}`; + return markdownToPage(content, meta, loaderContext); } }; diff --git a/webpack/markdown-to-page.js b/webpack/markdown-to-page.js new file mode 100644 index 0000000..46e32ce --- /dev/null +++ b/webpack/markdown-to-page.js @@ -0,0 +1,55 @@ +import path from "path"; + +import {remark} from "remark"; + +import remarkRehype from "remark-rehype"; +import remarkBootstrapIcon from "./remark/bootstrap-icon.js"; + +import {rehypeLinkActive} from "./rehype/link-active.js"; +import rehypeStringify from "rehype-stringify"; + +import {visit} from "unist-util-visit"; +import {toString} from "hast-util-to-string"; + +/** + * @param relative {string} + * @return {string} + */ +function getPagePath(relative) { + let segs = relative.split(path.sep); + if (segs[0] === 'src' && segs[1] === 'views') { + segs = segs.slice(2); + } + if (segs[0] === 'index') { + return '/'; + } + const parsed = path.parse(segs.join(path.posix.sep)); + return `/${path.posix.format(parsed.name === 'index' ? {root: parsed.dir} : {dir: parsed.dir, name: parsed.name})}`; +} + +/** + * @param content {string} + * @param meta {Object} + * @param loaderContext {import('webpack').LoaderContext & { data: { [key: string]: any } | string }} + */ +export default function (content, meta, loaderContext) { + const {resourcePath, data, context, rootContext, fs} = loaderContext; + const sidebarEnt = fs.readdirSync(path.dirname(resourcePath)) + .find(ent => ent.startsWith('_sidebar.')); + const processor = remark() + .use(remarkBootstrapIcon) + .use(remarkRehype, {allowDangerousHtml: true}) + .use(rehypeLinkActive, {active: getPagePath(path.relative(rootContext, resourcePath))}) + .use(() => (tree, vfile) => { + visit(tree, e => e.tagName === 'h1', (h1, index, parent) => { + vfile.data.title = toString(h1); + parent.children[index] = {type: 'comment', value: vfile.data.title}; + }); + }) + .use(rehypeStringify, {allowDangerousCharacters: true, allowDangerousHtml: true}); + /** @type {VFile|undefined} */ + const sidebar = sidebarEnt ? processor + .processSync(fs.readFileSync(path.join(context, sidebarEnt))) : undefined; + Object.assign(data, meta, sidebar && {sidebar: sidebar.data.title || 'Pages'}); + return `{{#partial 'sidebar'}}${sidebar}{{/partial}}{{#> page }}${content}{{/page}}`; +} \ No newline at end of file