Skip to content

Commit 4107bac

Browse files
committed
feat(markdown): add section extraction options
1 parent 62b616e commit 4107bac

7 files changed

Lines changed: 525 additions & 14 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ Get local file contents.
392392

393393
**Options:**
394394
- `src`: The relative path to the file to pull in
395+
- `sections`: Comma-separated list or array of markdown section headings to include
396+
- `section`: Single markdown section heading to include
397+
- `headings`: Array of markdown heading levels to include, such as `headings={[2,3]}`
398+
- `removeLeadingH1`: Remove the first H1 from imported markdown
399+
- `shiftHeaders`: Shift imported markdown headings up or down by a number
395400

396401
**Example:**
397402
```md
@@ -415,6 +420,12 @@ Get any remote Data and put in markdown
415420

416421
**Options:**
417422
- `url`: The URL of the remote content to pull in
423+
- `src`: Alias for `url`
424+
- `sections`: Comma-separated list or array of markdown section headings to include
425+
- `section`: Single markdown section heading to include
426+
- `headings`: Array of markdown heading levels to include, such as `headings={[2,3]}`
427+
- `removeLeadingH1`: Remove the first H1 from imported markdown
428+
- `shiftHeaders`: Shift imported markdown headings up or down by a number
418429

419430
**Example:**
420431
```md

docs/4-advanced-usage.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,34 @@ This guide covers advanced patterns and techniques for using markdown-magic effe
99

1010
## Complex Transform Combinations
1111

12+
### Sync Selected README Sections
13+
14+
Use `sections` when you want to include only specific README sections:
15+
16+
```md
17+
<!-- docs REMOTE
18+
src="https://raw.githubusercontent.com/DavidWells/example/master/README.md"
19+
sections="Installation,Usage,API"
20+
removeLeadingH1
21+
shiftHeaders=1
22+
-->
23+
<!-- /docs -->
24+
```
25+
26+
Use `headings` when you want to include every section at specific heading levels:
27+
28+
```md
29+
<!-- docs FILE
30+
src="./README.md"
31+
headings={[2,3]}
32+
removeLeadingH1
33+
shiftHeaders=1
34+
-->
35+
<!-- /docs -->
36+
```
37+
38+
`sections` matches normalized heading text. `headings={[2,3]}` includes all H2 and H3 sections without duplicating nested sections that are already part of a selected parent.
39+
1240
### Chaining Transforms
1341

1442
You can use multiple transform blocks in sequence to build complex documentation:
@@ -321,4 +349,4 @@ module.exports = {
321349
3. **Testing**: Write tests for custom transforms
322350
4. **Documentation**: Document custom transforms and options
323351
5. **Validation**: Validate inputs before processing
324-
6. **Modularity**: Keep transforms focused and reusable
352+
6. **Modularity**: Keep transforms focused and reusable

packages/core/_tests/transform-file.test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ function getNewFile(result) {
1616

1717
const SILENT = true
1818

19+
function ensureDir(dir) {
20+
fs.mkdirSync(dir, { recursive: true })
21+
}
22+
1923
test('FILE - includes local file content', async () => {
2024
const fileName = 'transform-file.md'
2125
const filePath = path.join(MARKDOWN_FIXTURE_DIR, fileName)
@@ -50,4 +54,56 @@ test('FILE - includes file with line ranges', async () => {
5054
assert.is(matches.length, 4, 'correct number of insertions')
5155
})
5256

57+
test('FILE - extracts markdown sections and heading levels', async () => {
58+
const tempDir = path.join(MARKDOWN_FIXTURE_DIR, 'temp-file')
59+
ensureDir(tempDir)
60+
61+
const sourceFile = path.join(tempDir, 'README.md')
62+
const markdownFile = path.join(tempDir, 'sections.md')
63+
64+
fs.writeFileSync(sourceFile, `# Package
65+
66+
## Install
67+
68+
Install docs.
69+
70+
### Browser
71+
72+
Browser docs.
73+
74+
## Usage
75+
76+
Usage docs.
77+
78+
## License
79+
80+
MIT.
81+
`)
82+
83+
fs.writeFileSync(markdownFile, `<!-- docs FILE
84+
src="./README.md"
85+
sections="Install"
86+
headings={[3]}
87+
removeLeadingH1
88+
shiftHeaders=1
89+
-->
90+
original
91+
<!-- /docs -->`)
92+
93+
const result = await markdownMagic(markdownFile, {
94+
open: 'docs',
95+
close: '/docs',
96+
outputDir: OUTPUT_DIR,
97+
applyTransformsToSource: false,
98+
silent: SILENT
99+
})
100+
101+
const newContent = fs.readFileSync(getNewFile(result), 'utf8')
102+
assert.ok(newContent.includes('### Install'), 'selected section was included and shifted')
103+
assert.ok(newContent.includes('#### Browser'), 'nested heading was included and shifted')
104+
assert.is(newContent.match(/Browser docs/g).length, 1, 'nested heading content was not duplicated')
105+
assert.not.ok(newContent.includes('## Usage'), 'unselected sibling was excluded')
106+
assert.not.ok(newContent.includes('## License'), 'unselected license was excluded')
107+
})
108+
53109
test.run()

packages/core/_tests/transform-remote.test.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Tests for remote transform
22

33
const fs = require('fs')
4+
const http = require('http')
45
const path = require('path')
56
const { test } = require('uvu')
67
const assert = require('uvu/assert')
@@ -23,6 +24,23 @@ function ensureDir(dir) {
2324

2425
const SILENT = true
2526

27+
function listen(server) {
28+
return new Promise((resolve) => {
29+
server.listen(0, () => {
30+
resolve(server.address().port)
31+
})
32+
})
33+
}
34+
35+
function close(server) {
36+
return new Promise((resolve, reject) => {
37+
server.close((err) => {
38+
if (err) return reject(err)
39+
resolve()
40+
})
41+
})
42+
}
43+
2644
test('remote - fetches content from URL', async () => {
2745
const fileName = 'transform-remote.md'
2846
const filePath = path.join(MARKDOWN_FIXTURE_DIR, fileName)
@@ -141,4 +159,63 @@ original
141159
)
142160
})
143161

162+
test('remote - extracts markdown sections and heading levels', async () => {
163+
const server = http.createServer((req, res) => {
164+
res.setHeader('content-type', 'text/markdown')
165+
res.end(`# Package
166+
167+
## Install
168+
169+
Install docs.
170+
171+
### Browser
172+
173+
Browser docs.
174+
175+
## Usage
176+
177+
Usage docs.
178+
179+
## License
180+
181+
MIT.
182+
`)
183+
})
184+
185+
const port = await listen(server)
186+
187+
try {
188+
const content = `<!-- docs remote
189+
url='http://127.0.0.1:${port}/README.md'
190+
sections="Install"
191+
headings={[3]}
192+
removeLeadingH1
193+
shiftHeaders=1
194+
-->
195+
original
196+
<!-- /docs -->`
197+
198+
ensureDir(TEMP_FIXTURE_DIR)
199+
const tempFile = path.join(TEMP_FIXTURE_DIR, 'remote-sections.md')
200+
fs.writeFileSync(tempFile, content)
201+
202+
const result = await markdownMagic(tempFile, {
203+
open: 'docs',
204+
close: '/docs',
205+
outputDir: OUTPUT_DIR,
206+
applyTransformsToSource: false,
207+
silent: SILENT
208+
})
209+
210+
const newContent = fs.readFileSync(getNewFile(result), 'utf8')
211+
assert.ok(newContent.includes('### Install'), 'selected section was included and shifted')
212+
assert.ok(newContent.includes('#### Browser'), 'nested heading was included and shifted')
213+
assert.is(newContent.match(/Browser docs/g).length, 1, 'nested heading content was not duplicated')
214+
assert.not.ok(newContent.includes('## Usage'), 'unselected sibling was excluded')
215+
assert.not.ok(newContent.includes('## License'), 'unselected license was excluded')
216+
} finally {
217+
await close(server)
218+
}
219+
})
220+
144221
test.run()

packages/core/src/transforms/index.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ const transforms = {
7474
*
7575
* **Options:**
7676
* - `src`: The relative path to the file to pull in
77+
* - `sections`: Comma-separated list or array of markdown section headings to include
78+
* - `section`: Single markdown section heading to include
79+
* - `headings`: Array of markdown heading levels to include, such as `headings={[2,3]}`
80+
* - `removeLeadingH1`: Remove the first H1 from imported markdown
81+
* - `shiftHeaders`: Shift imported markdown headings up or down by a number
7782
*
7883
* **Example:**
7984
* ```md
@@ -97,6 +102,12 @@ const transforms = {
97102
*
98103
* **Options:**
99104
* - `url`: The URL of the remote content to pull in
105+
* - `src`: Alias for `url`
106+
* - `sections`: Comma-separated list or array of markdown section headings to include
107+
* - `section`: Single markdown section heading to include
108+
* - `headings`: Array of markdown heading levels to include, such as `headings={[2,3]}`
109+
* - `removeLeadingH1`: Remove the first H1 from imported markdown
110+
* - `shiftHeaders`: Shift imported markdown headings up or down by a number
100111
*
101112
* **Example:**
102113
* ```md
@@ -208,4 +219,4 @@ const transforms = {
208219
install: install,
209220
}
210221

211-
module.exports = transforms
222+
module.exports = transforms

0 commit comments

Comments
 (0)