From 41b31992ccbbdf49287f591c1e63fbe9307a3095 Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Fri, 25 Feb 2022 10:36:47 -0800 Subject: [PATCH] feat(www): Heading ids are hierarchical (#814) Allows for better, nicer visual presentation of the headings while still maintaining very meaningful and stable anchors. --- package-lock.json | 109 ++++++++++-------- .../www/build/md/rehype-hierarchical-slugs.js | 37 ++++++ packages/www/build/md/remark-import.js | 2 + packages/www/build/vite-md.js | 4 +- packages/www/package.json | 6 +- .../routes/api/_content/extending-after.md | 4 +- .../routes/api/_content/extending-before.md | 4 +- .../src/routes/api/_content/extending-done.md | 4 +- .../api/_content/extending-processing.md | 4 +- .../routes/api/_content/usage-browserify.md | 4 +- .../www/src/routes/api/_content/usage-cli.md | 4 +- .../src/routes/api/_content/usage-glob-api.md | 6 +- .../www/src/routes/api/_content/usage-js.md | 4 +- .../src/routes/api/_content/usage-postcss.md | 6 +- .../src/routes/api/_content/usage-rollup.md | 6 +- .../src/routes/api/_content/usage-svelte.md | 16 +-- .../www/src/routes/api/_content/usage-vite.md | 6 +- .../src/routes/api/_content/usage-webpack.md | 6 +- 18 files changed, 148 insertions(+), 84 deletions(-) create mode 100644 packages/www/build/md/rehype-hierarchical-slugs.js diff --git a/package-lock.json b/package-lock.json index a7f5fbfd9..2753e8b98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8674,8 +8674,9 @@ }, "node_modules/github-slugger": { "version": "1.4.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==", + "dev": true }, "node_modules/glob": { "version": "7.1.7", @@ -8963,8 +8964,23 @@ }, "node_modules/hast-util-has-property": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz", + "integrity": "sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==", "dev": true, - "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-heading": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading/-/hast-util-heading-2.0.0.tgz", + "integrity": "sha512-VhBWhZdBzxMe4ZwR7oe0dbXzRwlGWJFf2QM8WdkKQuqr1Zt/TpYvuyi18tpiXjQ8iHZG8vk7Rsb1igysqget9A==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-is-element": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -8972,8 +8988,9 @@ }, "node_modules/hast-util-heading-rank": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.0.tgz", + "integrity": "sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/hast": "^2.0.0" }, @@ -9076,8 +9093,9 @@ }, "node_modules/hast-util-to-string": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz", + "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==", "dev": true, - "license": "MIT", "dependencies": { "@types/hast": "^2.0.0" }, @@ -15919,24 +15937,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-slug": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "github-slugger": "^1.1.1", - "hast-util-has-property": "^2.0.0", - "hast-util-heading-rank": "^2.0.0", - "hast-util-to-string": "^2.0.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/rehype-stringify": { "version": "9.0.2", "dev": true, @@ -18040,8 +18040,9 @@ }, "node_modules/unist-util-visit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz", + "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", @@ -18054,8 +18055,9 @@ }, "node_modules/unist-util-visit-parents": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz", + "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==", "dev": true, - "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" @@ -19292,7 +19294,7 @@ }, "packages/aliases": { "name": "@modular-css/path-aliases", - "version": "27.1.0", + "version": "27.2.0", "license": "MIT", "dependencies": { "escape-string-regexp": "^4.0.0", @@ -19583,7 +19585,7 @@ }, "packages/svelte": { "name": "@modular-css/svelte", - "version": "27.1.0", + "version": "27.2.0", "license": "MIT", "dependencies": { "@modular-css/processor": "file:../processor", @@ -19719,7 +19721,7 @@ }, "packages/www": { "name": "@modular-css/www", - "version": "27.1.1", + "version": "27.2.0", "license": "MIT", "devDependencies": { "@codemirror/state": "^0.19.6", @@ -19736,12 +19738,16 @@ "@sveltejs/kit": "^1.0.0-next.256", "codemirror": "^5.65.1", "debounce": "^1.2.1", + "github-slugger": "^1.4.0", + "hast-util-has-property": "^2.0.0", + "hast-util-heading": "^2.0.0", + "hast-util-heading-rank": "^2.1.0", + "hast-util-to-string": "^2.0.0", "hastscript": "^7.0.2", "lznext": "^0.1.0", "postcss-nested": "^5.0.6", "rehype-autolink-headings": "^6.1.1", "rehype-format": "^4.0.1", - "rehype-slug": "^5.0.1", "rehype-stringify": "^9.0.2", "rehype-toc": "^3.0.2", "remark-parse": "^10.0.1", @@ -21868,8 +21874,8 @@ "requires": { "@codemirror/state": "^0.19.6", "@codemirror/view": "^0.19.40", - "@esbuild-plugins/node-globals-polyfill": "*", - "@esbuild-plugins/node-modules-polyfill": "*", + "@esbuild-plugins/node-globals-polyfill": "^0.1.1", + "@esbuild-plugins/node-modules-polyfill": "^0.1.4", "@modular-css/path-aliases": "file:../aliases", "@modular-css/processor": "file:../processor", "@modular-css/svelte": "file:../svelte", @@ -21880,12 +21886,16 @@ "@sveltejs/kit": "^1.0.0-next.256", "codemirror": "^5.65.1", "debounce": "^1.2.1", + "github-slugger": "^1.4.0", + "hast-util-has-property": "^2.0.0", + "hast-util-heading": "^2.0.0", + "hast-util-heading-rank": "^2.1.0", + "hast-util-to-string": "^2.0.0", "hastscript": "^7.0.2", "lznext": "^0.1.0", "postcss-nested": "^5.0.6", "rehype-autolink-headings": "^6.1.1", "rehype-format": "^4.0.1", - "rehype-slug": "^5.0.1", "rehype-stringify": "^9.0.2", "rehype-toc": "^3.0.2", "remark-parse": "^10.0.1", @@ -26077,6 +26087,8 @@ }, "github-slugger": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==", "dev": true }, "glob": { @@ -26262,10 +26274,24 @@ }, "hast-util-has-property": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.0.tgz", + "integrity": "sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==", "dev": true }, + "hast-util-heading": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading/-/hast-util-heading-2.0.0.tgz", + "integrity": "sha512-VhBWhZdBzxMe4ZwR7oe0dbXzRwlGWJFf2QM8WdkKQuqr1Zt/TpYvuyi18tpiXjQ8iHZG8vk7Rsb1igysqget9A==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "hast-util-is-element": "^2.0.0" + } + }, "hast-util-heading-rank": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.0.tgz", + "integrity": "sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==", "dev": true, "requires": { "@types/hast": "^2.0.0" @@ -26332,6 +26358,8 @@ }, "hast-util-to-string": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz", + "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==", "dev": true, "requires": { "@types/hast": "^2.0.0" @@ -30847,19 +30875,6 @@ "unist-util-is": "^5.0.0" } }, - "rehype-slug": { - "version": "5.0.1", - "dev": true, - "requires": { - "@types/hast": "^2.0.0", - "github-slugger": "^1.1.1", - "hast-util-has-property": "^2.0.0", - "hast-util-heading-rank": "^2.0.0", - "hast-util-to-string": "^2.0.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0" - } - }, "rehype-stringify": { "version": "9.0.2", "dev": true, @@ -32260,6 +32275,8 @@ }, "unist-util-visit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz", + "integrity": "sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -32269,6 +32286,8 @@ }, "unist-util-visit-parents": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz", + "integrity": "sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==", "dev": true, "requires": { "@types/unist": "^2.0.0", diff --git a/packages/www/build/md/rehype-hierarchical-slugs.js b/packages/www/build/md/rehype-hierarchical-slugs.js new file mode 100644 index 000000000..98b1bcc74 --- /dev/null +++ b/packages/www/build/md/rehype-hierarchical-slugs.js @@ -0,0 +1,37 @@ +import ghSlugger from "github-slugger"; +import { hasProperty } from "hast-util-has-property"; +import { heading } from "hast-util-heading"; +import { headingRank } from "hast-util-heading-rank"; +import { toString } from "hast-util-to-string"; +import { visit } from "unist-util-visit"; + +const { slug : slugger } = ghSlugger; + +export default function rehypeHierarchicalSlug() { + return (tree) => { + const slugs = []; + + visit(tree, "element", (node) => { + if(!heading(node) || !node.properties || hasProperty(node, "id")) { + return; + } + + const rank = headingRank(node); + const slug = slugger(toString(node)); + + let last = slugs[slugs.length - 1]; + + while(last && last.rank >= rank) { + slugs.pop(); + + last = slugs[slugs.length - 1]; + } + + if(rank > 1) { + slugs.push({ rank, slug }); + } + + node.properties.id = slugs.map(({ slug : s }) => s).join("-"); + }); + }; +} diff --git a/packages/www/build/md/remark-import.js b/packages/www/build/md/remark-import.js index 566bfee17..5b63587d4 100644 --- a/packages/www/build/md/remark-import.js +++ b/packages/www/build/md/remark-import.js @@ -48,6 +48,8 @@ const remarkImport = (options = false) => { parent[i] = root[i]; } + parent.type = "parent"; + return CONTINUE; }); diff --git a/packages/www/build/vite-md.js b/packages/www/build/vite-md.js index cfdcfb0d7..0d5400d44 100644 --- a/packages/www/build/vite-md.js +++ b/packages/www/build/vite-md.js @@ -3,12 +3,12 @@ import remarkParse from "remark-parse"; import { default as remarkRehype, defaultHandlers } from "remark-rehype"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import rehypeFormat from "rehype-format"; -import rehypeSlug from "rehype-slug"; import rehypeStringify from "rehype-stringify"; import rehypeTableOfContents from "rehype-toc"; import remarkImport from "./md/remark-import.js"; import rehypeCode from "./md/rehype-code.js"; +import rehypeHierarchicalSlugs from "./md/rehype-hierarchical-slugs.js"; const ESCAPES = new Map([ [ "`", "\\`" ], @@ -33,7 +33,7 @@ export default () => { code : rehypeCode, }, }) - .use(rehypeSlug) + .use(rehypeHierarchicalSlugs) .freeze(); const later = [ diff --git a/packages/www/package.json b/packages/www/package.json index 7cc7edc74..5243bd7a3 100644 --- a/packages/www/package.json +++ b/packages/www/package.json @@ -39,12 +39,16 @@ "@sveltejs/kit": "^1.0.0-next.256", "codemirror": "^5.65.1", "debounce": "^1.2.1", + "github-slugger": "^1.4.0", + "hast-util-has-property": "^2.0.0", + "hast-util-heading": "^2.0.0", + "hast-util-heading-rank": "^2.1.0", + "hast-util-to-string": "^2.0.0", "hastscript": "^7.0.2", "lznext": "^0.1.0", "postcss-nested": "^5.0.6", "rehype-autolink-headings": "^6.1.1", "rehype-format": "^4.0.1", - "rehype-slug": "^5.0.1", "rehype-stringify": "^9.0.2", "rehype-toc": "^3.0.2", "remark-parse": "^10.0.1", diff --git a/packages/www/src/routes/api/_content/extending-after.md b/packages/www/src/routes/api/_content/extending-after.md index 9469cbc8c..a88338ad6 100644 --- a/packages/www/src/routes/api/_content/extending-after.md +++ b/packages/www/src/routes/api/_content/extending-after.md @@ -1,4 +1,4 @@ -### `after` hook +### `after` The `after` hook is run once the output location for the CSS is known, but before all the files are combined. By default it will run [`postcss-url`](https://github.com/postcss/postcss-url) to rebase file references based on the final output location, but this can be disabled using the [`rewrite`](#rewrite) option. @@ -13,7 +13,7 @@ Since all manipulations on the file are complete at this point it is a good plac } ``` -#### Using the `after` hook +#### Usage Specify an array of PostCSS plugins to be run after files are processed, but before they are combined. Plugin will be passed a `to` and `from` option. diff --git a/packages/www/src/routes/api/_content/extending-before.md b/packages/www/src/routes/api/_content/extending-before.md index 57138cb50..6ed1bc927 100644 --- a/packages/www/src/routes/api/_content/extending-before.md +++ b/packages/www/src/routes/api/_content/extending-before.md @@ -1,9 +1,9 @@ -### `before` hook +### `before` The `before` hook is run before a CSS file is ever processed by `modular-css`, so it provides access to rewrite files if they aren't actually CSS or contain non-standard syntax. Plugins like [`postcss-nested`](https://github.com/postcss/postcss-nested) go well here. -#### Using the `before` hook +#### Usage Specify an array of PostCSS plugins to be run against each file before it is processed. Plugin will be passed a `from` option. diff --git a/packages/www/src/routes/api/_content/extending-done.md b/packages/www/src/routes/api/_content/extending-done.md index a724cbfc1..c209988d3 100644 --- a/packages/www/src/routes/api/_content/extending-done.md +++ b/packages/www/src/routes/api/_content/extending-done.md @@ -1,8 +1,8 @@ -### `done` hook +### `done` The `done` hook is run after all of the constituent files are combined into a single stylesheet. This makes it a good place to add tools like [`cssnano`](http://cssnano.co/) that need access to the entire stylesheet to be able to accurately optimize the CSS. -#### Using the `done` hook +#### Usage Specify an array of PostCSS plugins to be run against the complete combined CSS. Plugin will be passed a `to` option. diff --git a/packages/www/src/routes/api/_content/extending-processing.md b/packages/www/src/routes/api/_content/extending-processing.md index 76610f829..56ca68649 100644 --- a/packages/www/src/routes/api/_content/extending-processing.md +++ b/packages/www/src/routes/api/_content/extending-processing.md @@ -1,4 +1,4 @@ -### `processing` hook +### `processing` The `processing` hook is run after `modular-css` has parsed the file, but before any response to [`processor.string`](#proccesorstringname-css) or [`processor.file`](#processorfilepath) is returned. Plugins in this hook have a special power: they can change the exports of the file. @@ -22,7 +22,7 @@ new Processor({ The `plugin` field must begin with "modular-css-export", and the `exports` field should be the object to be mixed into the exports of the CSS file. It will be added last, so it can be used to override the default exports if desired. -#### Using the `processing` hook +#### Usage Specify an array of PostCSS plugins to be run against each file during processing. Plugin will be passed a `from` option. diff --git a/packages/www/src/routes/api/_content/usage-browserify.md b/packages/www/src/routes/api/_content/usage-browserify.md index 4a83be6a7..793ef1687 100644 --- a/packages/www/src/routes/api/_content/usage-browserify.md +++ b/packages/www/src/routes/api/_content/usage-browserify.md @@ -6,13 +6,13 @@ This plugin can be combined with the `factor-bundle` plugin to output a common C `@modular-css/browserify` will use the `basedir` passed to browserify as it's `cwd` parameter. -#### browserify Install +#### Install ```shell $ npm i @modular-css/browserify --save-dev ``` -#### browserify Usage +#### Usage ##### CLI diff --git a/packages/www/src/routes/api/_content/usage-cli.md b/packages/www/src/routes/api/_content/usage-cli.md index b4e5d534b..52ce34f86 100644 --- a/packages/www/src/routes/api/_content/usage-cli.md +++ b/packages/www/src/routes/api/_content/usage-cli.md @@ -2,13 +2,13 @@ CLI interface to [`modular-css`](https://github.com/tivac/modular-css). -#### CLI Install +#### Install ```shell $ npm i @modular-css/cli ``` -#### CLI Usage +#### Usage ``` $ modular-css [options] ... diff --git a/packages/www/src/routes/api/_content/usage-glob-api.md b/packages/www/src/routes/api/_content/usage-glob-api.md index e8ba89b8c..06435526e 100644 --- a/packages/www/src/routes/api/_content/usage-glob-api.md +++ b/packages/www/src/routes/api/_content/usage-glob-api.md @@ -2,11 +2,11 @@ A JS API for using glob patterns with [`modular-css`](https://github.com/tivac/modular-css). -#### glob install +#### Install `$ npm i @modular-css/glob` -#### glob usage +#### Usage ```javascript const glob = require("@modular-css/glob"); @@ -19,7 +19,7 @@ const processor = await glob({ }) ``` -#### glob options +#### Options ###### `search` diff --git a/packages/www/src/routes/api/_content/usage-js.md b/packages/www/src/routes/api/_content/usage-js.md index 27a6d28bd..4b0aa4998 100644 --- a/packages/www/src/routes/api/_content/usage-js.md +++ b/packages/www/src/routes/api/_content/usage-js.md @@ -4,7 +4,7 @@ The heart of `modular-css`, the JS API is a `Processor` that will be fed files, Or, you know, their CSS. One of those for sure. -#### Processor Usage +#### Usage Instantiate a new `Processor` instance, call `.file()` or `.string(, )` methods, and then use the returned Promise to get access to the results/output. @@ -211,7 +211,7 @@ new Processor({ }); ``` -#### Processor Properties +#### Properties ##### `.files` diff --git a/packages/www/src/routes/api/_content/usage-postcss.md b/packages/www/src/routes/api/_content/usage-postcss.md index 38b5734ba..9955c18bb 100644 --- a/packages/www/src/routes/api/_content/usage-postcss.md +++ b/packages/www/src/routes/api/_content/usage-postcss.md @@ -2,13 +2,13 @@ `@modular-css/postcss` provides a postcss plugin that can be used like any other. It will output a message with a `type` of `modular-css-exports` containing all the exported class compositions. -#### postcss Install +#### Install ```shell > npm i @modular-css/postcss ``` -#### postcss Usage +#### Usage ##### API @@ -51,7 +51,7 @@ const result = await processor.process("") > postcss --use @modular-css/postcss input.css ``` -#### postcss Options +#### Options ##### `json` diff --git a/packages/www/src/routes/api/_content/usage-rollup.md b/packages/www/src/routes/api/_content/usage-rollup.md index e910a7385..2c846682d 100644 --- a/packages/www/src/routes/api/_content/usage-rollup.md +++ b/packages/www/src/routes/api/_content/usage-rollup.md @@ -2,13 +2,13 @@ Rollup support for modular-css is provided by the `@modular-css/rollup package`. -#### rollup Install +#### Install ```shell > npm i @modular-css/rollup --save-dev ``` -#### rollup Usage +#### Usage ##### API @@ -38,7 +38,7 @@ export default { }; ``` -#### rollup Options +#### Options ##### `dev` diff --git a/packages/www/src/routes/api/_content/usage-svelte.md b/packages/www/src/routes/api/_content/usage-svelte.md index bcaad6826..cecd93a50 100644 --- a/packages/www/src/routes/api/_content/usage-svelte.md +++ b/packages/www/src/routes/api/_content/usage-svelte.md @@ -2,7 +2,7 @@ Svelte preprocessor for [`modular-css`](https://github.com/tivac/modular-css). Process inline `