md-processor 1.0.0 → 1.2.0

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/README.md CHANGED
@@ -20,9 +20,12 @@ nxp md-processor input.md > output.md
20
20
  Sections of other markdown files can be imported like this:
21
21
 
22
22
  ```markdown
23
- @[import{section}](filename)
23
+ @[import{section,deep,depth=x}](filename)
24
24
  ```
25
25
 
26
- - `section`: The full header of the section to import
26
+ - `section`: The full header of the section to import.
27
+ - `deep`: If given, the nested blocks will be imported.
28
+ - `depth`: Change the level of the headers by adding `x`.
29
+ `x` can be positive or negative.
27
30
  - `filename`: The path to the markdown file to import.
28
31
  The path is relative to the input file.
package/lib/Block.js CHANGED
@@ -1,16 +1,30 @@
1
1
  class Block {
2
- constructor ({ header = null, level = 0 } = {}) {
2
+ constructor ({ header = null, level = 0, lines = [] } = {}) {
3
3
  this.header = header
4
4
  this.level = level
5
- this.lines = []
5
+ this.lines = lines
6
6
  }
7
7
 
8
8
  append (line) {
9
9
  this.lines.push(line)
10
10
  }
11
11
 
12
+ changeLevel (depth) {
13
+ return new Block({
14
+ header: this.header,
15
+ level: this.level + depth,
16
+ lines: this.lines
17
+ })
18
+ }
19
+
12
20
  toString () {
13
- return this.lines.join('\n')
21
+ let headerLine = ''
22
+
23
+ if (this.level > 0) {
24
+ headerLine = `${'#'.repeat(this.level)} ${this.header}\n`
25
+ }
26
+
27
+ return `${headerLine}${this.lines.join('\n')}`
14
28
  }
15
29
  }
16
30
 
package/lib/Markdown.js CHANGED
@@ -26,6 +26,33 @@ class Markdown {
26
26
  return this.blocks.find(block => block.header === header)
27
27
  }
28
28
 
29
+ getAll (header) {
30
+ const root = this.get(header)
31
+
32
+ if (!root) {
33
+ return []
34
+ }
35
+
36
+ const matches = []
37
+ let index = this.blocks.indexOf(root)
38
+
39
+ for (;;) {
40
+ matches.push(this.blocks[index])
41
+
42
+ const next = this.blocks[++index]
43
+
44
+ if (!next) {
45
+ break
46
+ }
47
+
48
+ if (next.level <= root.level) {
49
+ break
50
+ }
51
+ }
52
+
53
+ return matches
54
+ }
55
+
29
56
  parse (content) {
30
57
  const lines = content.split('\n')
31
58
 
@@ -35,11 +62,11 @@ class Markdown {
35
62
  if (parsed) {
36
63
  this.append({
37
64
  header: parsed[2],
38
- level: parsed[1]
65
+ level: parsed[1].length
39
66
  })
67
+ } else {
68
+ this.last.append(line)
40
69
  }
41
-
42
- this.last.append(line)
43
70
  }
44
71
 
45
72
  return this
package/lib/Processor.js CHANGED
@@ -22,15 +22,31 @@ class Processor {
22
22
  async processImports ({ content, path }) {
23
23
  const imports = new Map()
24
24
 
25
- for (const [text, header, importPath] of [...content.matchAll(parseImportRegex)]) {
26
- imports.set(text, { header, importPath })
25
+ for (const [text, args, importPath] of [...content.matchAll(parseImportRegex)]) {
26
+ const [header, ...others] = args.split(',')
27
+ const deep = others.includes('deep')
28
+ const depth = others
29
+ .filter(other => other.startsWith('depth='))
30
+ .map(raw => parseInt(raw.split('=')[1]))[0]
31
+
32
+ imports.set(text, { deep, depth, header, importPath })
27
33
  }
28
34
 
29
- for (const [text, { header, importPath }] of imports) {
35
+ for (const [text, { deep, depth = 0, header, importPath }] of imports) {
30
36
  const resolved = resolve(dirname(path), importPath)
31
37
  const markdown = await Markdown.load(resolved)
32
38
 
33
- content = content.replaceAll(text, markdown.get(header))
39
+ let imported
40
+
41
+ if (deep) {
42
+ imported = markdown.getAll(header)
43
+ .map(block => block.changeLevel(depth).toString())
44
+ .join('\n')
45
+ } else {
46
+ imported = markdown.get(header)
47
+ }
48
+
49
+ content = content.replaceAll(text, imported)
34
50
  }
35
51
 
36
52
  return content
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md-processor",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A preprocessor for markdown files",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -91,6 +91,42 @@ describe('Markdown', () => {
91
91
  })
92
92
  })
93
93
 
94
+ describe('.getAll', () => {
95
+ it('should be a method', () => {
96
+ const markdown = new Markdown()
97
+
98
+ strictEqual(typeof markdown.getAll, 'function')
99
+ })
100
+
101
+ it('should return all blocks linked to the matching header', () => {
102
+ const markdown = new Markdown()
103
+ .append({ header: 'abc' })
104
+ .append({ header: 'def', level: 1 })
105
+ .append({ header: 'def 1', level: 2 })
106
+ .append({ header: 'def 1.1', level: 3 })
107
+ .append({ header: 'def 2', level: 2 })
108
+ .append({ header: 'ghi' })
109
+
110
+ const result = markdown.getAll('def', { deep: true })
111
+
112
+ strictEqual(Array.isArray(result), true)
113
+ strictEqual(result.length, 4)
114
+ strictEqual(result[0].header, 'def')
115
+ strictEqual(result[1].header, 'def 1')
116
+ strictEqual(result[2].header, 'def 1.1')
117
+ strictEqual(result[3].header, 'def 2')
118
+ })
119
+
120
+ it('should return an empty Array no matching block was found', () => {
121
+ const markdown = new Markdown()
122
+
123
+ const result = markdown.getAll('def')
124
+
125
+ strictEqual(Array.isArray(result), true)
126
+ strictEqual(result.length, 0)
127
+ })
128
+ })
129
+
94
130
  describe('.parse', () => {
95
131
  it('should be a method', () => {
96
132
  const markdown = new Markdown()
@@ -119,7 +155,7 @@ describe('Markdown', () => {
119
155
  })
120
156
 
121
157
  it('should parse the given file', async () => {
122
- const markdown = await Markdown.load('./test/support/multiBlock.md')
158
+ const markdown = await Markdown.load('./test/support/multi-block.md')
123
159
 
124
160
  strictEqual(markdown.blocks[0].header, examples.multiBlock.blocks[0].header)
125
161
  strictEqual(markdown.blocks[0].level, examples.multiBlock.blocks[0].level)
@@ -31,9 +31,33 @@ describe('Processor', () => {
31
31
  it('should process all imports', async () => {
32
32
  const processor = new Processor()
33
33
 
34
- const result = await processor.process('./test/support/multiImports.md')
34
+ const result = await processor.process('./test/support/multi-imports.md')
35
35
 
36
36
  strictEqual(result, examples.multiImports.content)
37
37
  })
38
+
39
+ it('should process deep imports', async () => {
40
+ const processor = new Processor()
41
+
42
+ const result = await processor.process('./test/support/import-deep.md')
43
+
44
+ strictEqual(result, examples.importDeep.content)
45
+ })
46
+
47
+ it('should process deep with decreasing depth change', async () => {
48
+ const processor = new Processor()
49
+
50
+ const result = await processor.process('./test/support/import-deep-change-minus.md')
51
+
52
+ strictEqual(result, examples.importDeepChangeMinus.content)
53
+ })
54
+
55
+ it('should process deep with increasing depth change', async () => {
56
+ const processor = new Processor()
57
+
58
+ const result = await processor.process('./test/support/import-deep-change-plus.md')
59
+
60
+ strictEqual(result, examples.importDeepChangePlus.content)
61
+ })
38
62
  })
39
63
  })
@@ -1,17 +1,27 @@
1
+ function blocksToContent (blocks) {
2
+ return blocks.map(block => {
3
+ let headerLine = ''
4
+
5
+ if (block.level > 0) {
6
+ headerLine = `${'#'.repeat(block.level)} ${block.header}\n`
7
+ }
8
+
9
+ return `${headerLine}${block.lines.join('\n')}`
10
+ }).join('\n')
11
+ }
12
+
1
13
  const multiBlock = (() => {
2
14
  const blocks = [{
3
15
  header: 'Header 1',
4
- level: '#',
16
+ level: 1,
5
17
  lines: [
6
- '# Header 1',
7
18
  'First line of the body of header 1',
8
19
  'Second line of the body of header 1'
9
20
  ]
10
21
  }, {
11
22
  header: 'Header 1.1',
12
- level: '##',
23
+ level: 2,
13
24
  lines: [
14
- '## Header 1.1',
15
25
  'First line of the body of header 1.1',
16
26
  'Second line of the body of header 1.1'
17
27
  ]
@@ -19,10 +29,7 @@ const multiBlock = (() => {
19
29
 
20
30
  return {
21
31
  blocks,
22
- content: [
23
- blocks[0].lines.join('\n'),
24
- blocks[1].lines.join('\n')
25
- ].join('\n')
32
+ content: blocksToContent(blocks)
26
33
  }
27
34
  })()
28
35
 
@@ -31,17 +38,15 @@ const multiImports = (() => {
31
38
  ...multiBlock.blocks,
32
39
  {
33
40
  header: 'Header 2',
34
- level: '#',
41
+ level: 1,
35
42
  lines: [
36
- '# Header 2',
37
43
  'First line of the body of header 2',
38
44
  'Second line of the body of header 2'
39
45
  ]
40
46
  }, {
41
47
  header: 'Header 2.1',
42
- level: '##',
48
+ level: 2,
43
49
  lines: [
44
- '## Header 2.1',
45
50
  'First line of the body of header 2.1',
46
51
  'Second line of the body of header 2.1'
47
52
  ]
@@ -49,16 +54,69 @@ const multiImports = (() => {
49
54
 
50
55
  return {
51
56
  blocks,
52
- content: [
53
- blocks[0].lines.join('\n'),
54
- blocks[1].lines.join('\n'),
55
- blocks[2].lines.join('\n'),
56
- blocks[3].lines.join('\n')
57
- ].join('\n')
57
+ content: blocksToContent(blocks)
58
+ }
59
+ })()
60
+
61
+ const importDeep = multiImports
62
+
63
+ const importDeepChangeMinus = (() => {
64
+ const blocks = [
65
+ {
66
+ level: 0,
67
+ lines: [
68
+ 'First line of the body of header 1',
69
+ 'Second line of the body of header 1'
70
+ ]
71
+ }, {
72
+ header: 'Header 1.1',
73
+ level: 1,
74
+ lines: [
75
+ 'First line of the body of header 1.1',
76
+ 'Second line of the body of header 1.1'
77
+ ]
78
+ },
79
+ importDeep.blocks[2],
80
+ importDeep.blocks[3]
81
+ ]
82
+
83
+ return {
84
+ blocks,
85
+ content: blocksToContent(blocks)
86
+ }
87
+ })()
88
+
89
+ const importDeepChangePlus = (() => {
90
+ const blocks = [
91
+ {
92
+ header: 'Header 1',
93
+ level: 3,
94
+ lines: [
95
+ 'First line of the body of header 1',
96
+ 'Second line of the body of header 1'
97
+ ]
98
+ }, {
99
+ header: 'Header 1.1',
100
+ level: 4,
101
+ lines: [
102
+ 'First line of the body of header 1.1',
103
+ 'Second line of the body of header 1.1'
104
+ ]
105
+ },
106
+ importDeep.blocks[2],
107
+ importDeep.blocks[3]
108
+ ]
109
+
110
+ return {
111
+ blocks,
112
+ content: blocksToContent(blocks)
58
113
  }
59
114
  })()
60
115
 
61
116
  export {
117
+ importDeep,
118
+ importDeepChangeMinus,
119
+ importDeepChangePlus,
62
120
  multiBlock,
63
121
  multiImports
64
122
  }
@@ -1,5 +1,4 @@
1
- @[import{Header 1}](multiBlock.md)
2
- @[import{Header 1.1}](multiBlock.md)
1
+ @[import{Header 1,deep,depth=-1}](multi-block.md)
3
2
  # Header 2
4
3
  First line of the body of header 2
5
4
  Second line of the body of header 2
@@ -0,0 +1,7 @@
1
+ @[import{Header 1,deep,depth=+2}](multi-block.md)
2
+ # Header 2
3
+ First line of the body of header 2
4
+ Second line of the body of header 2
5
+ ## Header 2.1
6
+ First line of the body of header 2.1
7
+ Second line of the body of header 2.1
@@ -0,0 +1,7 @@
1
+ @[import{Header 1,deep}](multi-block.md)
2
+ # Header 2
3
+ First line of the body of header 2
4
+ Second line of the body of header 2
5
+ ## Header 2.1
6
+ First line of the body of header 2.1
7
+ Second line of the body of header 2.1
@@ -0,0 +1,8 @@
1
+ @[import{Header 1}](multi-block.md)
2
+ @[import{Header 1.1}](multi-block.md)
3
+ # Header 2
4
+ First line of the body of header 2
5
+ Second line of the body of header 2
6
+ ## Header 2.1
7
+ First line of the body of header 2.1
8
+ Second line of the body of header 2.1
File without changes