Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract MDX headings #16

Open
wants to merge 4 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
"auto": "^11.1.2",
"concurrently": "^8.2.2",
"estree-jsx": "^0.0.1",
"hast-util-select": "^6.0.2",
"hast-util-to-estree": "^3.1.0",
"hast-util-to-string": "^3.0.0",
"husky": ">=6",
"lint-staged": ">=10",
"meriyah": "^4.5.0",
Expand Down
47 changes: 47 additions & 0 deletions src/analyze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [
"hello",
],
"imports": [],
"isTemplate": false,
"metaTags": undefined,
Expand Down Expand Up @@ -75,6 +78,9 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [
"hello",
],
"imports": [],
"isTemplate": false,
"metaTags": undefined,
Expand Down Expand Up @@ -106,6 +112,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [
"@storybook/blocks",
"./Button.stories",
Expand Down Expand Up @@ -146,6 +153,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [
"@storybook/blocks",
"./Button.stories",
Expand Down Expand Up @@ -175,6 +183,9 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [
"Docs with of",
],
"imports": [
"../src/A.stories",
],
Expand Down Expand Up @@ -206,6 +217,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [],
"isTemplate": true,
"metaTags": undefined,
Expand All @@ -221,6 +233,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [],
"isTemplate": true,
"metaTags": undefined,
Expand All @@ -236,6 +249,7 @@ describe('analyze', () => {
`;
expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [],
"isTemplate": false,
"metaTags": undefined,
Expand Down Expand Up @@ -274,6 +288,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [
"./Button.stories",
],
Expand Down Expand Up @@ -322,6 +337,9 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [
"hello",
],
"imports": [],
"isTemplate": false,
"metaTags": undefined,
Expand All @@ -339,6 +357,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [
"./Button.stories",
],
Expand Down Expand Up @@ -382,6 +401,7 @@ describe('analyze', () => {
`;
await expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [],
"imports": [
"./Button.stories",
],
Expand All @@ -394,4 +414,31 @@ describe('analyze', () => {
`);
});
});

describe('headings', () => {
it('markdown', () => {
const input = dedent`
# hello **world**
## Goodbye
### Hi <b>again</b>

<Meta title="foobar" />
`;
expect(analyze(input)).resolves.toMatchInlineSnapshot(`
{
"headings": [
"hello world",
"Goodbye",
"Hi again",
],
"imports": [],
"isTemplate": false,
"metaTags": undefined,
"name": undefined,
"of": undefined,
"title": "foobar",
}
`);
});
});
});
18 changes: 16 additions & 2 deletions src/analyze.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { toEstree } from 'hast-util-to-estree';
import { selectAll } from 'hast-util-select';
import { toString } from 'hast-util-to-string';
import type { Program, ExpressionStatement } from 'hast-util-to-estree/lib';
import type {
JSXFragment,
Expand Down Expand Up @@ -150,6 +152,17 @@ export const extractImports = (root: Program) => {
return varToImport;
};

export const getHeadings = (root: any) => {
const headings = [] as string[];
['h1', 'h2', 'h3', 'h4'].forEach((tag) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be difficult to have options for that? People probably have different opinions about "what should be searchable" and maybe want something like

docs: {
  includeInSearch: 'h1, h2'
}

or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I'll implement it all end-to-end using this simple method and then see how disruptive it is to make it configurable

selectAll(tag, root).forEach((node: any) => {
const heading = toString(node);
headings.push(heading);
});
});
return headings;
};

export const plugin = (store: any) => (root: any) => {
const estree = toEstree(root);
const varToImport = extractImports(estree);
Expand All @@ -160,6 +173,7 @@ export const plugin = (store: any) => (root: any) => {
store.isTemplate = isTemplate;
store.metaTags = metaTags;
store.imports = Array.from(new Set(Object.values(varToImport)));
store.headings = getHeadings(root);

return root;
};
Expand All @@ -177,6 +191,6 @@ export const analyze = async (code: string) => {
await compile(code, {
rehypePlugins: [[plugin, store]],
});
const { title, of, name, isTemplate, metaTags, imports = [] } = store;
return { title, of, name, isTemplate, metaTags, imports };
const { title, of, name, isTemplate, metaTags, imports = [], headings = [] } = store;
return { title, of, name, isTemplate, metaTags, imports, headings };
};
90 changes: 90 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,9 @@ __metadata:
auto: "npm:^11.1.2"
concurrently: "npm:^8.2.2"
estree-jsx: "npm:^0.0.1"
hast-util-select: "npm:^6.0.2"
hast-util-to-estree: "npm:^3.1.0"
hast-util-to-string: "npm:^3.0.0"
husky: "npm:>=6"
lint-staged: "npm:>=10"
meriyah: "npm:^4.5.0"
Expand Down Expand Up @@ -1416,6 +1418,13 @@ __metadata:
languageName: node
linkType: hard

"bcp-47-match@npm:^2.0.0":
version: 2.0.3
resolution: "bcp-47-match@npm:2.0.3"
checksum: ae5c202854df8a9ad4777dc3b49562578495a69164869f365a88c1a089837a9fbbce4c0c44f6f1a5e44c7841f47e91fe6fea00306ca49ce5ec95a7eb71f839c4
languageName: node
linkType: hard

"before-after-hook@npm:^2.2.0":
version: 2.2.3
resolution: "before-after-hook@npm:2.2.3"
Expand All @@ -1430,6 +1439,13 @@ __metadata:
languageName: node
linkType: hard

"boolbase@npm:^1.0.0":
version: 1.0.0
resolution: "boolbase@npm:1.0.0"
checksum: e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf
languageName: node
linkType: hard

"bottleneck@npm:^2.15.3":
version: 2.19.5
resolution: "bottleneck@npm:2.19.5"
Expand Down Expand Up @@ -1832,6 +1848,13 @@ __metadata:
languageName: node
linkType: hard

"css-selector-parser@npm:^3.0.0":
version: 3.0.5
resolution: "css-selector-parser@npm:3.0.5"
checksum: 250b110ffd6926a9971dad56a69802b00ff52d621b6c20b3cfd714f024eb6ed7042a57226dea3446ff00dad864d03f5cee5c451b8c6d09ee12c723f5a9ca71c2
languageName: node
linkType: hard

"date-fns@npm:^2.30.0":
version: 2.30.0
resolution: "date-fns@npm:2.30.0"
Expand Down Expand Up @@ -1947,6 +1970,15 @@ __metadata:
languageName: node
linkType: hard

"direction@npm:^2.0.0":
version: 2.0.1
resolution: "direction@npm:2.0.1"
bin:
direction: cli.js
checksum: dce809431cad978e0778769a3818ea797ebe0bd542c85032ad9ad98971e2021a146be62feb259d7ffe4b76739e07b23e861b29c3f184ac8d38cc6ba956d5c586
languageName: node
linkType: hard

"dotenv@npm:^8.0.0":
version: 8.6.0
resolution: "dotenv@npm:8.6.0"
Expand Down Expand Up @@ -2628,6 +2660,39 @@ __metadata:
languageName: node
linkType: hard

"hast-util-has-property@npm:^3.0.0":
version: 3.0.0
resolution: "hast-util-has-property@npm:3.0.0"
dependencies:
"@types/hast": "npm:^3.0.0"
checksum: 6e2c0e22ca893c6ebb60f8390e184c4deb041c36d09796756f02cd121c1789c0f5c862ed06caea8f1a80ea8c0ef6a7854dd57946c2eebb76488727bd4a1c952e
languageName: node
linkType: hard

"hast-util-select@npm:^6.0.2":
version: 6.0.2
resolution: "hast-util-select@npm:6.0.2"
dependencies:
"@types/hast": "npm:^3.0.0"
"@types/unist": "npm:^3.0.0"
bcp-47-match: "npm:^2.0.0"
comma-separated-tokens: "npm:^2.0.0"
css-selector-parser: "npm:^3.0.0"
devlop: "npm:^1.0.0"
direction: "npm:^2.0.0"
hast-util-has-property: "npm:^3.0.0"
hast-util-to-string: "npm:^3.0.0"
hast-util-whitespace: "npm:^3.0.0"
not: "npm:^0.1.0"
nth-check: "npm:^2.0.0"
property-information: "npm:^6.0.0"
space-separated-tokens: "npm:^2.0.0"
unist-util-visit: "npm:^5.0.0"
zwitch: "npm:^2.0.0"
checksum: 1e757536119af068f6a3f1e77e3c1d01e16cf0f949cd514791f63cfdfac7a86a0238e567585c1d5868d771c8ca6ecc214f90497e56aa798ed7f5a2115bfa66ac
languageName: node
linkType: hard

"hast-util-to-estree@npm:^3.0.0, hast-util-to-estree@npm:^3.1.0":
version: 3.1.0
resolution: "hast-util-to-estree@npm:3.1.0"
Expand Down Expand Up @@ -2675,6 +2740,15 @@ __metadata:
languageName: node
linkType: hard

"hast-util-to-string@npm:^3.0.0":
version: 3.0.0
resolution: "hast-util-to-string@npm:3.0.0"
dependencies:
"@types/hast": "npm:^3.0.0"
checksum: 649edd993cf244563ad86d861aa0863759a4fbec49c43b3d92240e42aa4b69f0c3332ddff9e80954bbd8756c86b0fddc20e97d281c6da59d00427f45da8dab68
languageName: node
linkType: hard

"hast-util-whitespace@npm:^3.0.0":
version: 3.0.0
resolution: "hast-util-whitespace@npm:3.0.0"
Expand Down Expand Up @@ -4075,6 +4149,13 @@ __metadata:
languageName: node
linkType: hard

"not@npm:^0.1.0":
version: 0.1.0
resolution: "not@npm:0.1.0"
checksum: b75d7b2e41d73e2e1cb3327826d53667b41bc6ff7d7ff1d8014ad3bf410d4ecd46f512683b22a4c043e03cbb2b0a483aa69232d4bf9c0e2ee1a9127fe02f047a
languageName: node
linkType: hard

"npm-run-path@npm:^4.0.1":
version: 4.0.1
resolution: "npm-run-path@npm:4.0.1"
Expand All @@ -4093,6 +4174,15 @@ __metadata:
languageName: node
linkType: hard

"nth-check@npm:^2.0.0":
version: 2.1.1
resolution: "nth-check@npm:2.1.1"
dependencies:
boolbase: "npm:^1.0.0"
checksum: 5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479
languageName: node
linkType: hard

"object-assign@npm:^4.0.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
Expand Down
Loading