markdown-magic 3.2.0 → 3.3.1

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.
@@ -3,12 +3,12 @@ const path = require('path')
3
3
  const { remoteRequest } = require('../../utils/remoteRequest')
4
4
  const { isLocalPath } = require('../../utils/fs')
5
5
  const { deepLog } = require('../../utils/logs')
6
- const { getLineCount, getTextBetweenLines, stripOutMultiLineDoubleDashComments } = require('../../utils/text')
6
+ const { getLineCount, getTextBetweenLines, stripMultiLineDoubleDashComments, stripHTMLComments } = require('../../utils/text')
7
7
  const { resolveGithubContents, isGithubLink } = require('./resolve-github-file')
8
8
 
9
9
  const GITHUB_LINK = /https:\/\/github\.com\/([^/\s]*)\/([^/\s]*)\/blob\//
10
10
  const GIST_LINK = /https:\/\/gist\.github\.com\/([^/\s]*)\/([^/\s]*)(\/)?/
11
- const RAW_URL_LIKE = /^([A-Za-z0-9_]*)\.([A-Za-z0-9_]*)\/(.*)/
11
+ const RAW_URL_LIKE = /^([A-Za-z0-9_]+)\.([A-Za-z0-9_]*)\/(.*)/
12
12
 
13
13
  /**
14
14
  * Options for specifying source code to include in documentation.
@@ -17,6 +17,7 @@ const RAW_URL_LIKE = /^([A-Za-z0-9_]*)\.([A-Za-z0-9_]*)\/(.*)/
17
17
  * @property {string} [syntax] - The syntax of the code. If not specified, it will be inferred by fileType.
18
18
  * @property {string} [header] - The header comment to add to the code snippet. Useful for pointing to relative source directory or adding live doc links.
19
19
  * @property {string} [lines] - A range of lines of code to include from the file. The line range should be defined like "lines=22-44".
20
+ * @property {string} [section] - A section of code to include from the file. Defined like "section=SECTION_NAME". The code should have "// SECTION_NAME" surrounding the block of text.
20
21
  * @property {boolean} [trimDeadCode] - Remove multi-line comments that start with `//` from the code.
21
22
  * @example
22
23
  ```md
@@ -65,7 +66,7 @@ module.exports = async function CODE(api) {
65
66
  const fileDir = (srcPath) ? path.dirname(srcPath) : process.cwd()
66
67
  codeFilePath = path.resolve(fileDir, src)
67
68
  /* If the path looks like a URL, attempt to resolve it */
68
- const isUrlLike = src.match(RAW_URL_LIKE)
69
+ const isUrlLike = !src.startsWith('./') && !src.startsWith('../') && src.match(RAW_URL_LIKE)
69
70
  if (isUrlLike) {
70
71
  remoteContent = await resolveRemoteContent(options, settings, srcPath)
71
72
  code = remoteContent
@@ -81,8 +82,12 @@ module.exports = async function CODE(api) {
81
82
  if (isUrlLike) {
82
83
  throw new Error(`Unable to resolve ${src}`)
83
84
  }
84
- console.log(`FILE NOT FOUND ${codeFilePath}`)
85
- throw e
85
+ if (src === './relative/path/to/code.js') {
86
+ return api.content
87
+ } else {
88
+ console.log(`FILE NOT FOUND ${codeFilePath}`)
89
+ throw e
90
+ }
86
91
  }
87
92
  }
88
93
  } else {
@@ -133,6 +138,17 @@ module.exports = async function CODE(api) {
133
138
  }
134
139
  }
135
140
 
141
+ if (options.section) {
142
+ code = findInnerBlock(code, options.section)
143
+ }
144
+
145
+ if (options.symbol && syntax === 'js') {
146
+ // Code to get function block or class block or variable block
147
+ }
148
+
149
+ /* Ensure lowercase on syntax */
150
+ syntax = (syntax || '').toLowerCase()
151
+
136
152
  /* Check for Id */
137
153
  if (id) {
138
154
  const lines = code.split("\n")
@@ -157,15 +173,19 @@ module.exports = async function CODE(api) {
157
173
  }
158
174
 
159
175
  // trim leading and trailing spaces/line breaks in code and keeps the indentation of the first non-empty line
160
- code = code.replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
176
+ code = (code || '').replace(/^(?:[\t ]*(?:\r?\n|\r))+|\s+$/g, '')
161
177
 
162
178
  let header = ''
163
179
  if (options.header) {
164
180
  header = `\n${options.header}`
165
181
  }
166
-
182
+
167
183
  if (options.trimDeadCode) {
168
- code = stripOutMultiLineDoubleDashComments(code)
184
+ if (syntax === 'js' || syntax === 'ts' || syntax === 'javascript' || syntax === 'typescript') {
185
+ code = stripMultiLineDoubleDashComments(code)
186
+ } else if (syntax === 'html') {
187
+ code = stripHTMLComments(code, { multilineOnly: true })
188
+ }
169
189
  }
170
190
 
171
191
  return `\`\`\`${syntax}${header}
@@ -192,4 +212,34 @@ async function resolveRemoteContent(options, settings, srcPath) {
192
212
  }
193
213
 
194
214
  return remoteContent
215
+ }
216
+
217
+
218
+ function findInnerBlock(str, text) {
219
+ const matches = matchInnerContent(str, text, text)
220
+ if (!matches.length) {
221
+ throw new Error(`No blocks match ${text} in code`)
222
+ }
223
+ if (matches.length > 1) {
224
+ throw new Error(`Multiple blocks match ${text} in code`)
225
+ }
226
+
227
+ return matches[0]
228
+ }
229
+
230
+ function matchInnerContent(str, open, close) {
231
+ const pattern = new RegExp(`\\/\\/ ${open}\\n([\\s\\S]*?)\\n\\/\\/ ${close}`, 'g')
232
+
233
+ // console.log('closeTagRegex', closeTagRegex)
234
+ var matches
235
+ var blocks = []
236
+ while ((matches = pattern.exec(str)) !== null) {
237
+ if (matches.index === pattern.lastIndex) {
238
+ pattern.lastIndex++ // avoid infinite loops with zero-width matches
239
+ }
240
+ const [ _match, innerContent ] = matches
241
+ console.log('matches', matches)
242
+ blocks.push(innerContent)
243
+ }
244
+ return blocks
195
245
  }
@@ -5,7 +5,6 @@ const isLocalPath = require('is-local-path')
5
5
  module.exports = function FILE(api) {
6
6
  /*
7
7
  console.log('FILE API', api)
8
-
9
8
  /** */
10
9
  const { options, srcPath } = api
11
10
  if (!options.src) {
@@ -19,6 +18,10 @@ module.exports = function FILE(api) {
19
18
  // console.log('READFILE', resolvedFilePath)
20
19
  fileContents = fs.readFileSync(resolvedFilePath, 'utf8')
21
20
  } catch (e) {
21
+ // if demo path. Todo probably remove
22
+ if (options.src === './path/to/file') {
23
+ return api.content
24
+ }
22
25
  console.log(`FILE NOT FOUND ${resolvedFilePath}`)
23
26
  throw e
24
27
  }
@@ -4,6 +4,7 @@ module.exports = async function REMOTE(api) {
4
4
  // console.log('REMOTE api', api)
5
5
  const { options, content, settings } = api
6
6
  const { regex } = settings
7
+
7
8
  // console.log('MAKE REMOTE REQUEST')
8
9
  const remoteContent = await remoteRequest(options.url, settings, api.srcPath)
9
10
  if (!remoteContent) {
@@ -26,6 +26,10 @@ function remoteRequestSync(url, settings = {}, srcPath) {
26
26
  async function remoteRequest(url, settings = {}, srcPath) {
27
27
  let body
28
28
  const finalUrl = formatUrl(url)
29
+ // ignore demo url todo remove one day
30
+ if (finalUrl === 'http://url-to-raw-md-file.md') {
31
+ return
32
+ }
29
33
  try {
30
34
  const res = await fetch(finalUrl)
31
35
  body = await res.text()
package/lib/utils/text.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { match } = require('uvu/assert')
1
2
  const { syntaxMap } = require('./syntax')
2
3
 
3
4
  function getLines(str = '') {
@@ -77,7 +78,7 @@ function getTextBetweenLines(content, startLine, endLine) {
77
78
  if (startDefined && !endDefined) {
78
79
  return lines.slice(startLine - 1, startLine).join('')
79
80
  }
80
- if ((startLine) && (endLine) && parseInt(startLine, 10) <= parseInt(endLine, 10)) {
81
+ if (startLine && endLine && parseInt(startLine, 10) <= parseInt(endLine, 10)) {
81
82
  return lines.slice(startLine - 1, endLine).join('\n')
82
83
  }
83
84
  }
@@ -119,7 +120,11 @@ function indentString(string, count = 1, options = {}) {
119
120
  includeEmptyLines = false
120
121
  } = options;
121
122
  if (count === 0) return string
122
- const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
123
+ // const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm
124
+ const indentPattern = indent.repeat(count)
125
+ const regex = includeEmptyLines
126
+ ? new RegExp(`^(?!${indentPattern})`, 'gm')
127
+ : new RegExp(`^(?!${indentPattern})(?!\\s*$)`, 'gm')
123
128
  return string.replace(regex, indent.repeat(count))
124
129
  }
125
130
 
@@ -133,14 +138,14 @@ function dedentString(str) {
133
138
  str = str.replace(/^[ \t]*\r?\n/, ''); // remove leading blank line
134
139
  var indent = /^[ \t]+/m.exec(str); // detected indent
135
140
  if (indent) str = str.replace(new RegExp('^' + indent[0], 'gm'), ''); // remove indent
136
- return str.replace(/(\r?\n)[ \t]+$/, '$1'); // remove trailling blank line
141
+ return str.replace(/(\r?\n)[ \t]+$/, '$1'); // remove trailing blank line
137
142
  }
138
143
 
139
144
  /**
140
145
  * Strip out comment blocks
141
146
  * @param {string} str
142
147
  * @param {'md' | 'js'} syntax
143
- * @returns {string} clean commentless string
148
+ * @returns {string} clean comment-less string
144
149
  */
145
150
  function stripCommentBlockOld(str, syntax = 'md') {
146
151
  const [ openPattern, closePattern ] = syntaxMap[syntax].pattern
@@ -177,7 +182,7 @@ function stripCommentBlockOld(str, syntax = 'md') {
177
182
  * Strip out comment blocks
178
183
  * @param {string} str
179
184
  * @param {typeof import('../types')['syntaxType']} syntax
180
- * @returns {string} clean commentless string
185
+ * @returns {string} clean comment-less string
181
186
  */
182
187
  function stripComments(str, syntax = 'md') {
183
188
  const syntaxData = syntaxMap[syntax]
@@ -193,7 +198,7 @@ function stripComments(str, syntax = 'md') {
193
198
 
194
199
 
195
200
  // https://regex101.com/r/nCnt2J/1
196
- function stripOutMultiLineDoubleDashComments(str = '') {
201
+ function stripMultiLineDoubleDashComments(str = '') {
197
202
  return str.replace(/(?:\n\s*\/\/.*){2,}/g, '')
198
203
  }
199
204
 
@@ -204,8 +209,10 @@ function stripOutMultilineJsComments(str = '') {
204
209
  })
205
210
  }
206
211
 
212
+
207
213
  // @TODO export as util to import into CODE
208
- function stripAllHTMLComments(block) {
214
+ function stripHTMLComments(block, opts) {
215
+ const options = opts || {}
209
216
  // ([^\s]*)?([ \t]*)?(\/\*{1,}[\n\*]*(\s?[\s\S]*?)?\*\/)([^\s<]*)?(\n{1,2})?
210
217
  // https://regex101.com/r/WSioZ7/1
211
218
  const pattern = new RegExp(`([^\\s]*)?([ \\t]*)?(<!-{2,}(\\s?[\\s\\S]*?)?-{2,}>)([^\\s<]*)?(\n{1,2})?`, 'gi')
@@ -230,8 +237,12 @@ function stripAllHTMLComments(block) {
230
237
  console.log('trailingNewLine', trailingNewLine)
231
238
  /** */
232
239
  const newLineCount = (trailingNewLine || '').length
233
- const trailing = (!trailingText && newLineCount > 1) ? `${trailingNewLine || ''}` : ''
234
- const leading = (leadingSpace) ? leadingSpace.slice(1) : ''
240
+ const trailing = (!trailingText && !leadingText && newLineCount >= 1) ? `${trailingNewLine || ''}` : ''
241
+ let leading = (leadingSpace) ? leadingSpace : ''
242
+
243
+ if (options.multilineOnly && comment.indexOf('\n') === -1) {
244
+ continue
245
+ }
235
246
  remove.push(`${leading}${comment}${trailing}`)
236
247
  }
237
248
  return remove.reduce((acc, curr) => {
@@ -341,7 +352,8 @@ module.exports = {
341
352
  dedentString,
342
353
  stripComments,
343
354
  convertCommentSyntax,
344
- stripOutMultiLineDoubleDashComments,
355
+ stripMultiLineDoubleDashComments,
356
+ stripHTMLComments,
345
357
  // stripCommentBlockJS,
346
358
  trimString,
347
359
  // future https://github.com/junfengliang/autowrap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown-magic",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Automatically update markdown files with content from external sources",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -21,7 +21,6 @@
21
21
  "test": "npm run test:lib && npm run test:test && echo 'tests done'",
22
22
  "test:lib": "uvu lib '.test.([mc]js|[jt]sx?)$'",
23
23
  "test:test": "uvu test '.test.([mc]js|[jt]sx?)$'",
24
- "test:options": "uvu lib 'options-parser.test.([mc]js|[jt]sx?)$'",
25
24
  "test:block": "uvu lib 'block-parser.test.([mc]js|[jt]sx?)$'",
26
25
  "test:cli": "uvu lib 'cli.test.([mc]js|[jt]sx?)$'",
27
26
  "test:md": "uvu lib 'md.test.([mc]js|[jt]sx?)$'",
@@ -54,7 +53,7 @@
54
53
  "micro-mdx-parser": "^1.1.0",
55
54
  "mri": "^1.2.0",
56
55
  "node-fetch": "^2.7.0",
57
- "oparser": "^3.0.13",
56
+ "oparser": "^3.0.22",
58
57
  "smart-glob": "^1.0.2",
59
58
  "sync-request": "^6.1.0"
60
59
  },