From 021b72ffc2650499682904715da80932ffb977fc Mon Sep 17 00:00:00 2001
From: akabeko
Date: Wed, 28 Apr 2021 18:03:03 +0900
Subject: [PATCH 1/3] feat: Added section leveling
---
docs/vfm.md | 22 +++++++--
src/plugins/section.ts | 57 ++++++++++++++--------
tests/attr.test.ts | 6 +--
tests/index.test.ts | 40 ---------------
tests/metadata.test.ts | 6 +--
tests/section.test.ts | 108 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 171 insertions(+), 68 deletions(-)
create mode 100644 tests/section.test.ts
diff --git a/docs/vfm.md b/docs/vfm.md
index 6645a08..4e0d3f7 100644
--- a/docs/vfm.md
+++ b/docs/vfm.md
@@ -203,22 +203,33 @@ ruby rt {
# Introduction {#intro}
# Welcome {.title}
+
+# Level 1
+
+## Level 2
```
**HTML**
```html
-
+
-
+
-
+
+
+
```
**CSS**
@@ -234,6 +245,11 @@ section.title {
section.title > h1:first-child {
}
+
+.level1 {
+}
+.level2 {
+}
```
### Plain section
diff --git a/src/plugins/section.ts b/src/plugins/section.ts
index 7cadbc2..0bf9c26 100644
--- a/src/plugins/section.ts
+++ b/src/plugins/section.ts
@@ -1,29 +1,24 @@
-// derived from remark-sectionize
-// https://github.com/jake-low/remark-sectionize
-// MIT License
-// original: 2019 Jake Low
-// modified: 2020 Yasuaki Uechi
+/**
+ * derived from `remark-sectionize`.
+ * original: 2019 Jake Low
+ * modified: 2020 Yasuaki Uechi, 2021 and later is Akabeko
+ * @license MIT
+ * @see https://github.com/jake-low/remark-sectionize
+ */
import { Parent } from 'mdast';
import findAfter from 'unist-util-find-after';
import visit from 'unist-util-visit-parents';
-// TODO: handle @subtitle properly
-
+/** Maximum depth of hierarchy to process headings. */
const MAX_HEADING_DEPTH = 6;
-export const mdast = () => (tree: any) => {
- for (let depth = MAX_HEADING_DEPTH; depth > 0; depth--) {
- visit(
- tree,
- (node: any) => {
- return node.type === 'heading' && node.depth === depth;
- },
- sectionize as any,
- );
- }
-};
-
+/**
+ * Wrap the header in sections.
+ * @param node Node of Markdown AST.
+ * @param ancestors Parents.
+ * @todo handle `@subtitle` properly.
+ */
function sectionize(node: any, ancestors: Parent[]) {
const start = node;
const depth = start.depth;
@@ -66,6 +61,14 @@ function sectionize(node: any, ancestors: Parent[]) {
return;
}
+ // output section levels like Pandoc
+ if (Array.isArray(hProperties.class)) {
+ // `remark-attr` may add classes, so make sure they come before them (always top)
+ hProperties.class.unshift(`level${depth}`);
+ } else {
+ hProperties.class = [`level${depth}`];
+ }
+
const section = {
type,
data: {
@@ -78,3 +81,19 @@ function sectionize(node: any, ancestors: Parent[]) {
parent.children.splice(startIndex, section.children.length, section);
}
+
+/**
+ * Process Markdown AST.
+ * @returns Transformer.
+ */
+export const mdast = () => (tree: any) => {
+ for (let depth = MAX_HEADING_DEPTH; depth > 0; depth--) {
+ visit(
+ tree,
+ (node: any) => {
+ return node.type === 'heading' && node.depth === depth;
+ },
+ sectionize as any,
+ );
+ }
+};
diff --git a/tests/attr.test.ts b/tests/attr.test.ts
index 9fa042a..c3ec3b8 100644
--- a/tests/attr.test.ts
+++ b/tests/attr.test.ts
@@ -12,7 +12,7 @@ it(
│ data: {"hProperties":{"id":"foo"}}
└─0 text "Heading"
`,
- ``,
+ ``,
),
);
@@ -27,7 +27,7 @@ it(
│ data: {"hProperties":{"id":"foo"}}
└─0 text "Heading"
`,
- ``,
+ ``,
),
);
@@ -44,7 +44,7 @@ it(
└─1 emphasis[1]
└─0 text "test"
`,
- ``,
+ ``,
),
);
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 9867d20..2b4a2b8 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -1,46 +1,6 @@
import * as lib from '../src';
import { ReplaceRule } from '../src/plugins/replace';
-/**
- * Run VFM stringify in partial mode.
- * @param body Markdown string that becomes `` part.
- * @param hardLineBreaks Add `
` at the position of hard line breaks, without needing spaces.
- * @returns HTML string.
- */
-function partial(body: string, hardLineBreaks = false) {
- return lib.stringify(body, {
- partial: true,
- hardLineBreaks,
- disableFormatHtml: true,
- });
-}
-
-// Snippet
-//
-// it('do something', ()=>{
-// expect(partial(``)).toBe(``)
-// })
-
-it.skip('plain section', () => {
- expect(partial(`# {.ok}`)).toBe(``);
-});
-
-it('stringify markdown string into html document', () => {
- expect(lib.stringify('# こんにちは', { disableFormatHtml: true }))
- .toBe(`
-
-
-
-こんにちは
-
-
-
-
-
-
-`);
-});
-
it('replace', () => {
const rules = [
{
diff --git a/tests/metadata.test.ts b/tests/metadata.test.ts
index afd4003..8b7e23f 100644
--- a/tests/metadata.test.ts
+++ b/tests/metadata.test.ts
@@ -20,7 +20,7 @@ class: 'my-class'
-
+
@@ -39,7 +39,7 @@ it('title from heading, missing "title" property of Frontmatter', () => {
-
+
@@ -142,7 +142,7 @@ class: 'my-class'
-
+
diff --git a/tests/section.test.ts b/tests/section.test.ts
new file mode 100644
index 0000000..944f367
--- /dev/null
+++ b/tests/section.test.ts
@@ -0,0 +1,108 @@
+import { stringify } from '../src/index';
+
+// This test always fails, `remark-attr` does not handle empty headings.
+/*
+it('plain section', () => {
+ const md = '# {.ok}';
+ const received = stringify(md, { partial: true, disableFormatHtml: true });
+ const expected = '';
+ expect(received).toBe(expected);
+});
+*/
+
+it('', () => {
+ const md = '# こんにちは {.test}';
+ const received = stringify(md, { partial: true, disableFormatHtml: true });
+ const expected =
+ '';
+ expect(received).toBe(expected);
+});
+
+it(' is not heading', () => {
+ const md = '####### こんにちは {.test}';
+ const received = stringify(md, { partial: true, disableFormatHtml: true });
+ const expected = '####### こんにちは {.test}
';
+ expect(received).toBe(expected);
+});
+
+it(', ... ', () => {
+ const md = `# Header 1
+## Header 2
+### Header 3
+#### Header 4
+##### Header 5
+###### Header 6`;
+ const received = stringify(md, { partial: true });
+ const expected = `
+
+`;
+ expect(received).toBe(expected);
+});
+
+// It seems that when the class is processed by `remark-attr`, it is output before id.
+it(', ... with attribute', () => {
+ const md = `# Header 1 {.depth1}
+## Header 2 {.depth2}
+### Header 3 {.depth3}
+#### Header 4 {.depth4}
+##### Header 5 {.depth5}
+###### Header 6 {.depth6}`;
+ const received = stringify(md, { partial: true });
+ const expected = `
+
+`;
+ expect(received).toBe(expected);
+});
+
+it('Complex structure', () => {
+ const md = `# Header 1
+## Header 2 {.foo}
+# Header 1`;
+ const received = stringify(md, { partial: true });
+ const expected = `
+
+
+`;
+ expect(received).toBe(expected);
+});
From 6bd8b3056b850d3f073df3ed3c685762dac54537 Mon Sep 17 00:00:00 2001
From: akabeko
Date: Wed, 28 Apr 2021 19:49:01 +0900
Subject: [PATCH 2/3] docs: Changed heading description from "Header" to
"Heading"
---
tests/attr.test.ts | 8 ++--
tests/math.test.ts | 2 +-
tests/section.test.ts | 90 +++++++++++++++++++++----------------------
3 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/tests/attr.test.ts b/tests/attr.test.ts
index c3ec3b8..9d11dac 100644
--- a/tests/attr.test.ts
+++ b/tests/attr.test.ts
@@ -2,7 +2,7 @@ import { stripIndent } from 'common-tags';
import { buildProcessorTestingCode } from './utils';
it(
- 'Header with attributes',
+ 'Heading with attributes',
buildProcessorTestingCode(
`# Heading {#foo}`,
stripIndent`
@@ -17,7 +17,7 @@ it(
);
it(
- 'Header with attributes, specification by line break',
+ 'Heading with attributes, specification by line break',
buildProcessorTestingCode(
`# Heading\n{#foo}`,
stripIndent`
@@ -32,7 +32,7 @@ it(
);
it(
- 'Header with attributes and inline elements, specification by line break',
+ 'Heading with attributes and inline elements, specification by line break',
buildProcessorTestingCode(
`# Heading *test*\n{#foo}`,
stripIndent`
@@ -52,7 +52,7 @@ it(
// https://github.com/arobase-che/remark-attr/issues/24
/*
it(
- 'Header with attributes and inline elements',
+ 'Heading with attributes and inline elements',
buildProcessorTestingCode(
`# Heading *test* {#foo}`,
stripIndent`
diff --git a/tests/math.test.ts b/tests/math.test.ts
index 312f58e..067da12 100644
--- a/tests/math.test.ts
+++ b/tests/math.test.ts
@@ -149,7 +149,7 @@ $$
`;
expect(received).toBe(expected);
});
-it('HTML header and body', () => {
+it('HTML heading and body', () => {
const received = stringify('$x=y$', { math: true, disableFormatHtml: true });
const expected = `
diff --git a/tests/section.test.ts b/tests/section.test.ts
index 944f367..fe9524f 100644
--- a/tests/section.test.ts
+++ b/tests/section.test.ts
@@ -26,26 +26,26 @@ it(' is not heading', () => {
});
it(', ... ', () => {
- const md = `# Header 1
-## Header 2
-### Header 3
-#### Header 4
-##### Header 5
-###### Header 6`;
+ const md = `# Heading 1
+## Heading 2
+### Heading 3
+#### Heading 4
+##### Heading 5
+###### Heading 6`;
const received = stringify(md, { partial: true });
const expected = `
-