init
This commit is contained in:
commit
5f067b3797
9 changed files with 1418 additions and 0 deletions
9
.eslintrc.js
Normal file
9
.eslintrc.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
es2020: true,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'@sup39/basic',
|
||||||
|
],
|
||||||
|
};
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 sup39
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
76
README.md
Normal file
76
README.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# rehype-mdx-export-headings
|
||||||
|
A rehype plugin to extract headings (e.g. h2, h3) from mdx, assign them ids, and export headings list
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```
|
||||||
|
# If you are using yarn
|
||||||
|
yarn add @sup39/rehype-mdx-export-headings
|
||||||
|
|
||||||
|
# If you are using npm
|
||||||
|
npm install @sup39/rehype-mdx-export-headings
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|property|type|description|
|
||||||
|
|--|--|--|
|
||||||
|
|`tags`|`string[]`|Tag name of headings. Default value is `['h2']`|
|
||||||
|
|`name`|`string`|Export name. Default value is `'headings'`|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### Next.js with MDX support
|
||||||
|
In next.config.js:
|
||||||
|
```javascript
|
||||||
|
const ExportHeadings = require('@sup39/rehype-mdx-export-headings');
|
||||||
|
|
||||||
|
const withMDX = require('@next/mdx')({
|
||||||
|
extension: /\.mdx?$/,
|
||||||
|
options: {
|
||||||
|
rehypePlugins: [ExportHeadings],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
module.exports = withMDX({
|
||||||
|
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### with options
|
||||||
|
```javascript
|
||||||
|
const ExportHeadings = require('@sup39/rehype-mdx-export-headings');
|
||||||
|
|
||||||
|
const withMDX = require('@next/mdx')({
|
||||||
|
extension: /\.mdx?$/,
|
||||||
|
options: {
|
||||||
|
rehypePlugins: [
|
||||||
|
() => ExportHeadings({
|
||||||
|
tags: ['h2', 'h3'],
|
||||||
|
name: 'labels',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
module.exports = withMDX({
|
||||||
|
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mjs
|
||||||
|
If you are using next.config.mjs:
|
||||||
|
```javascript
|
||||||
|
import mdx from '@next/mdx';
|
||||||
|
import ExportHeadings from '@sup39/rehype-mdx-export-headings';
|
||||||
|
|
||||||
|
const withMDX = mdx({
|
||||||
|
extension: /\.mdx?$/,
|
||||||
|
options: {
|
||||||
|
rehypePlugins: [
|
||||||
|
() => ExportHeadings({
|
||||||
|
tags: ['h2', 'h3'],
|
||||||
|
name: 'labels',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default withMDX({
|
||||||
|
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||||
|
});
|
||||||
|
```
|
10
index.d.ts
vendored
Normal file
10
index.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export = ExportHeadings;
|
||||||
|
/** @type {import('unified').Plugin<[Partial<ExportHeadingsOptions>?], import('hast').Root>} */
|
||||||
|
declare const ExportHeadings: import('unified').Plugin<[Partial<ExportHeadingsOptions>?], import('hast').Root>;
|
||||||
|
declare namespace ExportHeadings {
|
||||||
|
export { ExportHeadingsOptions };
|
||||||
|
}
|
||||||
|
type ExportHeadingsOptions = {
|
||||||
|
tags: string[];
|
||||||
|
name: string;
|
||||||
|
};
|
63
index.js
Normal file
63
index.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* tags: string[]
|
||||||
|
* name: string
|
||||||
|
* }} ExportHeadingsOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {(e: import('hast').RootContent) => string[]} */
|
||||||
|
const getInnerText = e =>
|
||||||
|
e.type === 'text' ? [e.value] :
|
||||||
|
e.type === 'element' ? e.children.flatMap(getInnerText) : [];
|
||||||
|
|
||||||
|
/** @type {import('unified').Plugin<[Partial<ExportHeadingsOptions>?], import('hast').Root>} */
|
||||||
|
const ExportHeadings = ({
|
||||||
|
tags = ['h2'],
|
||||||
|
name = 'headings',
|
||||||
|
} = {}) => ({children}) => {
|
||||||
|
const items = [];
|
||||||
|
children.forEach(node => {
|
||||||
|
if (node.type !== 'element' || !tags.includes(node.tagName)) return;
|
||||||
|
const innerText = getInnerText(node).join('').trim();
|
||||||
|
// TODO uniqueness
|
||||||
|
const id = String(node.properties?.id || innerText.replace(/\s+/g, '-'));
|
||||||
|
if (node.properties == null) node.properties = {id};
|
||||||
|
else node.properties.id = id;
|
||||||
|
// push item
|
||||||
|
const item = {tagName: node.tagName, label: innerText, id};
|
||||||
|
items.push({
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: Object.entries(item).map(([name, value]) => ({
|
||||||
|
type: 'Property',
|
||||||
|
method: false,
|
||||||
|
shorthand: false,
|
||||||
|
computed: false,
|
||||||
|
key: {type: 'Identifier', name},
|
||||||
|
value: {type: 'Literal', value},
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// prepend: export const headings = [...]
|
||||||
|
children.unshift(/**@type{import('mdast-util-mdx').MdxjsEsm}*/({
|
||||||
|
type: 'mdxjsEsm',
|
||||||
|
value: '',
|
||||||
|
data: {estree: {type: 'Program', sourceType: 'module', body: [{
|
||||||
|
type: 'ExportNamedDeclaration',
|
||||||
|
specifiers: [],
|
||||||
|
declaration: {
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
kind: 'const',
|
||||||
|
declarations: [{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
id: {type: 'Identifier', name},
|
||||||
|
init: {
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
elements: [],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}]}},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
module.exports = ExportHeadings;
|
26
package.json
Normal file
26
package.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "@sup39/rehype-mdx-export-headings",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": "sup39",
|
||||||
|
"repository": "https://github.com/sup39/rehype-mdx-export-headings",
|
||||||
|
"description": "A rehype plugin to extract headings (e.g. h2, h3) from mdx, assign them ids, and export headings list",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"lint": "eslint index.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sup39/eslint-config-basic": "^0.1.5",
|
||||||
|
"@types/hast": "^2.3.4",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"mdast-util-mdx": "^2.0.0",
|
||||||
|
"typescript": "^4.8.4",
|
||||||
|
"unified": "^10.1.2"
|
||||||
|
}
|
||||||
|
}
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"include": ["index.js"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"lib": ["es2020"],
|
||||||
|
"strict": true,
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue