@san-siva/blogkit-md 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +15 -22
- package/package.json +2 -2
- package/src/utils/groupSections.test.ts +18 -14
- package/src/utils/groupSections.ts +7 -9
- package/src/utils/renderMarkdown.test.tsx +36 -26
- package/src/utils/renderMarkdown.tsx +17 -16
package/dist/index.js
CHANGED
|
@@ -93,7 +93,7 @@ var consumeNode = (nodes, index, sections, section) => {
|
|
|
93
93
|
if (isIncrementingHeading) {
|
|
94
94
|
if (section.nodes.length > 0) {
|
|
95
95
|
const subSection = {
|
|
96
|
-
|
|
96
|
+
titleNodes: [],
|
|
97
97
|
headingLevel: section.headingLevel,
|
|
98
98
|
nodes: section.nodes,
|
|
99
99
|
subsections: [],
|
|
@@ -103,7 +103,7 @@ var consumeNode = (nodes, index, sections, section) => {
|
|
|
103
103
|
section.nodes = [];
|
|
104
104
|
}
|
|
105
105
|
const subsection = {
|
|
106
|
-
|
|
106
|
+
titleNodes: node.children,
|
|
107
107
|
headingLevel,
|
|
108
108
|
nodes: [],
|
|
109
109
|
subsections: [],
|
|
@@ -114,7 +114,7 @@ var consumeNode = (nodes, index, sections, section) => {
|
|
|
114
114
|
}
|
|
115
115
|
if (!section.previousSection) {
|
|
116
116
|
const subSection = {
|
|
117
|
-
|
|
117
|
+
titleNodes: node.children,
|
|
118
118
|
headingLevel,
|
|
119
119
|
nodes: [],
|
|
120
120
|
subsections: [],
|
|
@@ -127,7 +127,7 @@ var consumeNode = (nodes, index, sections, section) => {
|
|
|
127
127
|
};
|
|
128
128
|
var groupSections = (nodes) => {
|
|
129
129
|
const initialSection = {
|
|
130
|
-
|
|
130
|
+
titleNodes: [],
|
|
131
131
|
headingLevel: Infinity,
|
|
132
132
|
nodes: [],
|
|
133
133
|
subsections: [],
|
|
@@ -135,7 +135,7 @@ var groupSections = (nodes) => {
|
|
|
135
135
|
};
|
|
136
136
|
const sections = [initialSection];
|
|
137
137
|
consumeNode(nodes, 0, sections, initialSection);
|
|
138
|
-
return sections.filter((s) => s.
|
|
138
|
+
return sections.filter((s) => s.titleNodes.length > 0 || s.nodes.length > 0);
|
|
139
139
|
};
|
|
140
140
|
|
|
141
141
|
// src/utils/renderPhrasingContent.tsx
|
|
@@ -326,18 +326,11 @@ function renderNode({
|
|
|
326
326
|
(item) => item.checked !== null && item.checked !== void 0
|
|
327
327
|
);
|
|
328
328
|
if (isTaskList) {
|
|
329
|
-
const items = node.children.map((item, index) =>
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
node: child,
|
|
335
|
-
key: childIndex,
|
|
336
|
-
inList: true,
|
|
337
|
-
counters
|
|
338
|
-
})
|
|
339
|
-
)
|
|
340
|
-
}));
|
|
329
|
+
const items = node.children.map((item, index) => {
|
|
330
|
+
const para = item.children.find((c) => c.type === "paragraph");
|
|
331
|
+
const children = (para == null ? void 0 : para.type) === "paragraph" ? /* @__PURE__ */ jsx2("p", { children: renderPhrasingContent(para.children) }) : /* @__PURE__ */ jsx2("p", {});
|
|
332
|
+
return { id: String(index), isChecked: item.checked === true, children };
|
|
333
|
+
});
|
|
341
334
|
return /* @__PURE__ */ jsx2(CheckList, { items, hasMarginUp: true, hasMarginDown: true }, key);
|
|
342
335
|
}
|
|
343
336
|
const Tag = node.ordered ? "ol" : "ul";
|
|
@@ -361,8 +354,8 @@ function renderNodes(nodes, counters) {
|
|
|
361
354
|
);
|
|
362
355
|
}
|
|
363
356
|
function renderSection(section, counters, key = -1) {
|
|
364
|
-
|
|
365
|
-
return /* @__PURE__ */ jsxs(BlogSection, { title
|
|
357
|
+
const title = section.titleNodes.length > 0 ? /* @__PURE__ */ jsx2("p", { children: renderPhrasingContent(section.titleNodes) }) : "";
|
|
358
|
+
return /* @__PURE__ */ jsxs(BlogSection, { title, children: [
|
|
366
359
|
renderNodes(section.nodes, counters),
|
|
367
360
|
section.subsections.map(
|
|
368
361
|
(subsection, index) => renderSection(subsection, counters, index)
|
|
@@ -371,9 +364,9 @@ function renderSection(section, counters, key = -1) {
|
|
|
371
364
|
}
|
|
372
365
|
function stripRedundantSectionTitle(grouped, isTitleEmpty) {
|
|
373
366
|
if (!isTitleEmpty && grouped.length === 1) {
|
|
374
|
-
const [{
|
|
375
|
-
if (
|
|
376
|
-
grouped[0].
|
|
367
|
+
const [{ titleNodes, nodes, subsections }] = grouped;
|
|
368
|
+
if (titleNodes.length > 0 && (nodes.length > 0 || subsections.length > 0)) {
|
|
369
|
+
grouped[0].titleNodes = [];
|
|
377
370
|
}
|
|
378
371
|
}
|
|
379
372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@san-siva/blogkit-md",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Converts markdown files into JSX blog posts for Blogkit",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"fix": "eslint . --config eslint.config.ts --fix"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@san-siva/blogkit": "^1.1.
|
|
24
|
+
"@san-siva/blogkit": "^1.1.32",
|
|
25
25
|
"@san-siva/stylekit": "^1.0.12",
|
|
26
26
|
"next": "^16.0.10",
|
|
27
27
|
"react": "^19.0.0",
|
|
@@ -4,6 +4,7 @@ import remarkParse from 'remark-parse';
|
|
|
4
4
|
import { unified } from 'unified';
|
|
5
5
|
import type { Root } from 'mdast';
|
|
6
6
|
|
|
7
|
+
import { extractText } from './extractText';
|
|
7
8
|
import { groupSections } from './groupSections';
|
|
8
9
|
|
|
9
10
|
const sections = (md: string) => {
|
|
@@ -11,6 +12,9 @@ const sections = (md: string) => {
|
|
|
11
12
|
return groupSections(ast.children);
|
|
12
13
|
};
|
|
13
14
|
|
|
15
|
+
const title = (s: ReturnType<typeof sections>[number]) =>
|
|
16
|
+
extractText(s.titleNodes);
|
|
17
|
+
|
|
14
18
|
describe('groupSections', () => {
|
|
15
19
|
describe('empty input', () => {
|
|
16
20
|
it('returns an empty array', () => {
|
|
@@ -22,7 +26,7 @@ describe('groupSections', () => {
|
|
|
22
26
|
it('returns a single untitled section containing all nodes', () => {
|
|
23
27
|
const result = sections('some text\n\nmore text');
|
|
24
28
|
expect(result).toHaveLength(1);
|
|
25
|
-
expect(result[0].
|
|
29
|
+
expect(result[0].titleNodes).toHaveLength(0);
|
|
26
30
|
expect(result[0].nodes).toHaveLength(2);
|
|
27
31
|
});
|
|
28
32
|
});
|
|
@@ -31,21 +35,21 @@ describe('groupSections', () => {
|
|
|
31
35
|
it('creates a section for H1', () => {
|
|
32
36
|
const result = sections('# Title');
|
|
33
37
|
expect(result).toHaveLength(1);
|
|
34
|
-
expect(result[0]
|
|
38
|
+
expect(title(result[0])).toBe('Title');
|
|
35
39
|
expect(result[0].headingLevel).toBe(1);
|
|
36
40
|
});
|
|
37
41
|
|
|
38
42
|
it('creates a section for H2', () => {
|
|
39
43
|
const result = sections('## Section');
|
|
40
44
|
expect(result).toHaveLength(1);
|
|
41
|
-
expect(result[0]
|
|
45
|
+
expect(title(result[0])).toBe('Section');
|
|
42
46
|
expect(result[0].headingLevel).toBe(2);
|
|
43
47
|
});
|
|
44
48
|
|
|
45
49
|
it('creates multiple sections for sequential H2s', () => {
|
|
46
50
|
const result = sections('## One\n\n## Two\n\n## Three');
|
|
47
51
|
expect(result).toHaveLength(3);
|
|
48
|
-
expect(result.map(s => s
|
|
52
|
+
expect(result.map(s => title(s))).toEqual(['One', 'Two', 'Three']);
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
it('groups content nodes under their section', () => {
|
|
@@ -59,9 +63,9 @@ describe('groupSections', () => {
|
|
|
59
63
|
it('returns an untitled intro section followed by headed sections', () => {
|
|
60
64
|
const result = sections('intro text\n\n## Section');
|
|
61
65
|
expect(result).toHaveLength(2);
|
|
62
|
-
expect(result[0].
|
|
66
|
+
expect(result[0].titleNodes).toHaveLength(0);
|
|
63
67
|
expect(result[0].nodes).toHaveLength(1);
|
|
64
|
-
expect(result[1]
|
|
68
|
+
expect(title(result[1])).toBe('Section');
|
|
65
69
|
});
|
|
66
70
|
});
|
|
67
71
|
|
|
@@ -69,20 +73,20 @@ describe('groupSections', () => {
|
|
|
69
73
|
it('moves content preceding a subsection into its own untitled subsection', () => {
|
|
70
74
|
const result = sections('## Parent\n\nsome text\n\n### Child');
|
|
71
75
|
expect(result).toHaveLength(1);
|
|
72
|
-
expect(result[0]
|
|
76
|
+
expect(title(result[0])).toBe('Parent');
|
|
73
77
|
expect(result[0].nodes).toHaveLength(0);
|
|
74
78
|
expect(result[0].subsections).toHaveLength(2);
|
|
75
|
-
expect(result[0].subsections[0].
|
|
79
|
+
expect(result[0].subsections[0].titleNodes).toHaveLength(0);
|
|
76
80
|
expect(result[0].subsections[0].nodes).toHaveLength(1);
|
|
77
|
-
expect(result[0].subsections[1]
|
|
81
|
+
expect(title(result[0].subsections[1])).toBe('Child');
|
|
78
82
|
});
|
|
79
83
|
|
|
80
84
|
it('skips the untitled subsection when no content precedes the deeper heading', () => {
|
|
81
85
|
const result = sections('## Parent\n\n### Child');
|
|
82
86
|
expect(result).toHaveLength(1);
|
|
83
|
-
expect(result[0]
|
|
87
|
+
expect(title(result[0])).toBe('Parent');
|
|
84
88
|
expect(result[0].subsections).toHaveLength(1);
|
|
85
|
-
expect(result[0].subsections[0]
|
|
89
|
+
expect(title(result[0].subsections[0])).toBe('Child');
|
|
86
90
|
});
|
|
87
91
|
});
|
|
88
92
|
|
|
@@ -91,19 +95,19 @@ describe('groupSections', () => {
|
|
|
91
95
|
const result = sections('## Parent\n\n### Child');
|
|
92
96
|
expect(result).toHaveLength(1);
|
|
93
97
|
expect(result[0].subsections).toHaveLength(1);
|
|
94
|
-
expect(result[0].subsections[0]
|
|
98
|
+
expect(title(result[0].subsections[0])).toBe('Child');
|
|
95
99
|
});
|
|
96
100
|
|
|
97
101
|
it('promotes H3 to top-level when no parent section exists', () => {
|
|
98
102
|
const result = sections('### Orphan');
|
|
99
103
|
expect(result).toHaveLength(1);
|
|
100
|
-
expect(result[0]
|
|
104
|
+
expect(title(result[0])).toBe('Orphan');
|
|
101
105
|
});
|
|
102
106
|
|
|
103
107
|
it('creates multiple subsections under one parent', () => {
|
|
104
108
|
const result = sections('## Parent\n\n### A\n\n### B');
|
|
105
109
|
expect(result).toHaveLength(1);
|
|
106
|
-
expect(result[0].subsections.map(s => s
|
|
110
|
+
expect(result[0].subsections.map(s => title(s))).toEqual(['A', 'B']);
|
|
107
111
|
});
|
|
108
112
|
});
|
|
109
113
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import type { RootContent } from 'mdast';
|
|
2
|
-
|
|
3
|
-
import { extractText } from './extractText';
|
|
1
|
+
import type { PhrasingContent, RootContent } from 'mdast';
|
|
4
2
|
|
|
5
3
|
export type Section = {
|
|
6
|
-
|
|
4
|
+
titleNodes: PhrasingContent[];
|
|
7
5
|
headingLevel: number;
|
|
8
6
|
nodes: RootContent[];
|
|
9
7
|
subsections: Section[];
|
|
@@ -37,7 +35,7 @@ const consumeNode = (
|
|
|
37
35
|
// instead of: Parent { nodes: [...], subsections: [Child] }
|
|
38
36
|
if (section.nodes.length > 0) {
|
|
39
37
|
const subSection: Section = {
|
|
40
|
-
|
|
38
|
+
titleNodes: [],
|
|
41
39
|
headingLevel: section.headingLevel,
|
|
42
40
|
nodes: section.nodes,
|
|
43
41
|
subsections: [],
|
|
@@ -47,7 +45,7 @@ const consumeNode = (
|
|
|
47
45
|
section.nodes = [];
|
|
48
46
|
}
|
|
49
47
|
const subsection: Section = {
|
|
50
|
-
|
|
48
|
+
titleNodes: node.children,
|
|
51
49
|
headingLevel,
|
|
52
50
|
nodes: [],
|
|
53
51
|
subsections: [],
|
|
@@ -59,7 +57,7 @@ const consumeNode = (
|
|
|
59
57
|
|
|
60
58
|
if (!section.previousSection) {
|
|
61
59
|
const subSection: Section = {
|
|
62
|
-
|
|
60
|
+
titleNodes: node.children,
|
|
63
61
|
headingLevel,
|
|
64
62
|
nodes: [],
|
|
65
63
|
subsections: [],
|
|
@@ -74,7 +72,7 @@ const consumeNode = (
|
|
|
74
72
|
|
|
75
73
|
export const groupSections = (nodes: RootContent[]): Section[] => {
|
|
76
74
|
const initialSection: Section = {
|
|
77
|
-
|
|
75
|
+
titleNodes: [],
|
|
78
76
|
headingLevel: Infinity,
|
|
79
77
|
nodes: [],
|
|
80
78
|
subsections: [],
|
|
@@ -82,5 +80,5 @@ export const groupSections = (nodes: RootContent[]): Section[] => {
|
|
|
82
80
|
};
|
|
83
81
|
const sections: Section[] = [initialSection];
|
|
84
82
|
consumeNode(nodes, 0, sections, initialSection);
|
|
85
|
-
return sections.filter(s => s.
|
|
83
|
+
return sections.filter(s => s.titleNodes.length > 0 || s.nodes.length > 0);
|
|
86
84
|
};
|
|
@@ -17,13 +17,16 @@ vi.mock('@san-siva/blogkit', () => ({
|
|
|
17
17
|
|
|
18
18
|
import { renderMarkdownAst, stripRedundantSectionTitle } from './renderMarkdown';
|
|
19
19
|
import type { Section } from './groupSections';
|
|
20
|
+
import type { PhrasingContent } from 'mdast';
|
|
21
|
+
|
|
22
|
+
const textNode = (value: string): PhrasingContent => ({ type: 'text', value });
|
|
20
23
|
|
|
21
24
|
const makeSection = (
|
|
22
|
-
|
|
25
|
+
titleNodes: PhrasingContent[] = [],
|
|
23
26
|
nodes: Section['nodes'] = [],
|
|
24
27
|
subsections: Section[] = []
|
|
25
28
|
): Section => ({
|
|
26
|
-
|
|
29
|
+
titleNodes,
|
|
27
30
|
headingLevel: 2,
|
|
28
31
|
nodes,
|
|
29
32
|
subsections,
|
|
@@ -33,45 +36,45 @@ const parse = (md: string): Root =>
|
|
|
33
36
|
unified().use(remarkParse).use(remarkGfm).parse(md) as Root;
|
|
34
37
|
|
|
35
38
|
describe('stripRedundantSectionTitle', () => {
|
|
36
|
-
it('clears
|
|
37
|
-
const grouped = [makeSection('My Title', [{ type: 'paragraph', children: [] }])];
|
|
39
|
+
it('clears titleNodes when isTitleEmpty is false and the section has nodes', () => {
|
|
40
|
+
const grouped = [makeSection([textNode('My Title')], [{ type: 'paragraph', children: [] }])];
|
|
38
41
|
stripRedundantSectionTitle(grouped, false);
|
|
39
|
-
expect(grouped[0].
|
|
42
|
+
expect(grouped[0].titleNodes).toHaveLength(0);
|
|
40
43
|
});
|
|
41
44
|
|
|
42
|
-
it('clears
|
|
43
|
-
const sub = makeSection('Sub', [{ type: 'paragraph', children: [] }]);
|
|
44
|
-
const grouped = [makeSection('My Title', [], [sub])];
|
|
45
|
+
it('clears titleNodes when isTitleEmpty is false and the section has subsections', () => {
|
|
46
|
+
const sub = makeSection([textNode('Sub')], [{ type: 'paragraph', children: [] }]);
|
|
47
|
+
const grouped = [makeSection([textNode('My Title')], [], [sub])];
|
|
45
48
|
stripRedundantSectionTitle(grouped, false);
|
|
46
|
-
expect(grouped[0].
|
|
49
|
+
expect(grouped[0].titleNodes).toHaveLength(0);
|
|
47
50
|
});
|
|
48
51
|
|
|
49
|
-
it('does not clear
|
|
50
|
-
const grouped = [makeSection('My Title', [{ type: 'paragraph', children: [] }])];
|
|
52
|
+
it('does not clear titleNodes when isTitleEmpty is true', () => {
|
|
53
|
+
const grouped = [makeSection([textNode('My Title')], [{ type: 'paragraph', children: [] }])];
|
|
51
54
|
stripRedundantSectionTitle(grouped, true);
|
|
52
|
-
expect(grouped[0].
|
|
55
|
+
expect(grouped[0].titleNodes).toEqual([textNode('My Title')]);
|
|
53
56
|
});
|
|
54
57
|
|
|
55
|
-
it('does not clear
|
|
58
|
+
it('does not clear titleNodes when there are multiple sections', () => {
|
|
56
59
|
const grouped = [
|
|
57
|
-
makeSection('Title A', [{ type: 'paragraph', children: [] }]),
|
|
58
|
-
makeSection('Title B', [{ type: 'paragraph', children: [] }]),
|
|
60
|
+
makeSection([textNode('Title A')], [{ type: 'paragraph', children: [] }]),
|
|
61
|
+
makeSection([textNode('Title B')], [{ type: 'paragraph', children: [] }]),
|
|
59
62
|
];
|
|
60
63
|
stripRedundantSectionTitle(grouped, false);
|
|
61
|
-
expect(grouped[0].
|
|
62
|
-
expect(grouped[1].
|
|
64
|
+
expect(grouped[0].titleNodes).toEqual([textNode('Title A')]);
|
|
65
|
+
expect(grouped[1].titleNodes).toEqual([textNode('Title B')]);
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
it('does not clear
|
|
66
|
-
const grouped = [makeSection('My Title')];
|
|
68
|
+
it('does not clear titleNodes when the section has no nodes or subsections', () => {
|
|
69
|
+
const grouped = [makeSection([textNode('My Title')])];
|
|
67
70
|
stripRedundantSectionTitle(grouped, false);
|
|
68
|
-
expect(grouped[0].
|
|
71
|
+
expect(grouped[0].titleNodes).toEqual([textNode('My Title')]);
|
|
69
72
|
});
|
|
70
73
|
|
|
71
|
-
it('does not clear
|
|
72
|
-
const grouped = [makeSection(
|
|
74
|
+
it('does not clear titleNodes when they are already empty', () => {
|
|
75
|
+
const grouped = [makeSection([], [{ type: 'paragraph', children: [] }])];
|
|
73
76
|
stripRedundantSectionTitle(grouped, false);
|
|
74
|
-
expect(grouped[0].
|
|
77
|
+
expect(grouped[0].titleNodes).toHaveLength(0);
|
|
75
78
|
});
|
|
76
79
|
});
|
|
77
80
|
|
|
@@ -100,12 +103,19 @@ describe('renderMarkdownAst', () => {
|
|
|
100
103
|
it('preserves the section title when isTitleEmpty is true', () => {
|
|
101
104
|
const result = renderMarkdownAst(parse('## Section\n\nSome content'), true);
|
|
102
105
|
const section = result.sections[0] as React.ReactElement;
|
|
103
|
-
|
|
106
|
+
const titleEl = section.props.title as React.ReactElement;
|
|
107
|
+
expect(titleEl.type).toBe('p');
|
|
108
|
+
expect(titleEl.props.children).toEqual(['Section']);
|
|
104
109
|
});
|
|
105
110
|
|
|
106
111
|
it('preserves titles of all sections when there are multiple sections', () => {
|
|
107
112
|
const result = renderMarkdownAst(parse('## One\n\n## Two'), false);
|
|
108
|
-
const
|
|
109
|
-
|
|
113
|
+
const titleEls = (result.sections as React.ReactElement[]).map(
|
|
114
|
+
s => s.props.title as React.ReactElement
|
|
115
|
+
);
|
|
116
|
+
expect(titleEls[0].type).toBe('p');
|
|
117
|
+
expect(titleEls[0].props.children).toEqual(['One']);
|
|
118
|
+
expect(titleEls[1].type).toBe('p');
|
|
119
|
+
expect(titleEls[1].props.children).toEqual(['Two']);
|
|
110
120
|
});
|
|
111
121
|
});
|
|
@@ -155,18 +155,16 @@ function renderNode({
|
|
|
155
155
|
item => item.checked !== null && item.checked !== undefined
|
|
156
156
|
);
|
|
157
157
|
if (isTaskList) {
|
|
158
|
-
const items: CheckListItem[] = node.children.map((item, index) =>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
),
|
|
169
|
-
}));
|
|
158
|
+
const items: CheckListItem[] = node.children.map((item, index) => {
|
|
159
|
+
const para = item.children.find(c => c.type === 'paragraph');
|
|
160
|
+
const children =
|
|
161
|
+
para?.type === 'paragraph' ? (
|
|
162
|
+
<p>{renderPhrasingContent(para.children)}</p>
|
|
163
|
+
) : (
|
|
164
|
+
<p />
|
|
165
|
+
);
|
|
166
|
+
return { id: String(index), isChecked: item.checked === true, children };
|
|
167
|
+
});
|
|
170
168
|
return <CheckList key={key} items={items} hasMarginUp hasMarginDown />;
|
|
171
169
|
}
|
|
172
170
|
const Tag = node.ordered ? 'ol' : 'ul';
|
|
@@ -207,8 +205,11 @@ function renderSection(
|
|
|
207
205
|
counters: Counters,
|
|
208
206
|
key = -1
|
|
209
207
|
): React.ReactNode {
|
|
208
|
+
const title = section.titleNodes.length > 0
|
|
209
|
+
? <p>{renderPhrasingContent(section.titleNodes)}</p>
|
|
210
|
+
: '';
|
|
210
211
|
return (
|
|
211
|
-
<BlogSection key={key} title={
|
|
212
|
+
<BlogSection key={key} title={title}>
|
|
212
213
|
{renderNodes(section.nodes, counters)}
|
|
213
214
|
{section.subsections.map((subsection, index) =>
|
|
214
215
|
renderSection(subsection, counters, index)
|
|
@@ -226,9 +227,9 @@ export function stripRedundantSectionTitle(
|
|
|
226
227
|
isTitleEmpty: boolean
|
|
227
228
|
): void {
|
|
228
229
|
if (!isTitleEmpty && grouped.length === 1) {
|
|
229
|
-
const [{
|
|
230
|
-
if (
|
|
231
|
-
grouped[0].
|
|
230
|
+
const [{ titleNodes, nodes, subsections }] = grouped;
|
|
231
|
+
if (titleNodes.length > 0 && (nodes.length > 0 || subsections.length > 0)) {
|
|
232
|
+
grouped[0].titleNodes = [];
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
}
|