safe-mdx 0.0.1 → 0.0.3

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/src/safe-mdx.tsx CHANGED
@@ -151,7 +151,9 @@ export class MdastToJsx {
151
151
  return null
152
152
  }
153
153
 
154
- let attrsList = this.getJsxAttrs(node)
154
+ let attrsList = getJsxAttrs(node, (err) => {
155
+ this.errors.push(err)
156
+ })
155
157
 
156
158
  let attrs = Object.fromEntries(attrsList)
157
159
  return (
@@ -246,9 +248,7 @@ export class MdastToJsx {
246
248
  const code = node.value
247
249
  return (
248
250
  <this.c.pre>
249
- <this.c.code className={`language-${language}`}>
250
- {code}
251
- </this.c.code>
251
+ <this.c.code>{code}</this.c.code>
252
252
  </this.c.pre>
253
253
  )
254
254
  }
@@ -319,9 +319,14 @@ export class MdastToJsx {
319
319
  return <Fragment>{this.mapMdastChildren(node)}</Fragment>
320
320
  }
321
321
  case 'table': {
322
- const align = node.align
322
+ const [head, ...body] = React.Children.toArray(
323
+ this.mapMdastChildren(node),
324
+ )
323
325
  return (
324
- <this.c.table>{this.mapMdastChildren(node)}</this.c.table>
326
+ <this.c.table>
327
+ {head && <this.c.thead>{head}</this.c.thead>}
328
+ {!!body?.length && <this.c.tbody>{body}</this.c.tbody>}
329
+ </this.c.table>
325
330
  )
326
331
  }
327
332
  case 'tableRow': {
@@ -403,72 +408,78 @@ export class MdastToJsx {
403
408
  }
404
409
  }
405
410
  }
406
- getJsxAttrs(node: MdxJsxFlowElement | MdxJsxTextElement) {
407
- let attrsList = node.attributes
408
- .map((attr) => {
409
- if (attr.type === 'mdxJsxExpressionAttribute') {
410
- this.errors.push({
411
- message: `Expressions in jsx props are not supported (${attr.value.replace(
412
- /\n+/g,
413
- ' ',
414
- )})`,
415
- })
416
- return
411
+ }
412
+
413
+ export function getJsxAttrs(
414
+ node: MdxJsxFlowElement | MdxJsxTextElement,
415
+ onError: (err: { message: string }) => void = console.error,
416
+ ) {
417
+ let attrsList = node.attributes
418
+ .map((attr) => {
419
+ if (attr.type === 'mdxJsxExpressionAttribute') {
420
+ onError({
421
+ message: `Expressions in jsx props are not supported (${attr.value.replace(
422
+ /\n+/g,
423
+ ' ',
424
+ )})`,
425
+ })
426
+ return
427
+ }
428
+ if (attr.type !== 'mdxJsxAttribute') {
429
+ throw new Error(`non mdxJsxAttribute is not supported: ${attr}`)
430
+ }
431
+
432
+ const v = attr.value
433
+ if (typeof v === 'string' || typeof v === 'number') {
434
+ return [attr.name, v]
435
+ }
436
+ if (v === null) {
437
+ return [attr.name, true]
438
+ }
439
+ if (v?.type === 'mdxJsxAttributeValueExpression') {
440
+ if (v.value === 'true') {
441
+ return [attr.name, true]
417
442
  }
418
- if (attr.type !== 'mdxJsxAttribute') {
419
- throw new Error(
420
- `non mdxJsxAttribute is not supported: ${attr}`,
421
- )
443
+ if (v.value === 'false') {
444
+ return [attr.name, false]
422
445
  }
423
-
424
- const v = attr.value
425
- if (typeof v === 'string' || typeof v === 'number') {
426
- return [attr.name, v]
446
+ if (v.value === 'null') {
447
+ return [attr.name, null]
427
448
  }
428
- if (v === null) {
429
- return [attr.name, true]
449
+ if (v.value === 'undefined') {
450
+ return [attr.name, undefined]
430
451
  }
431
- if (v?.type === 'mdxJsxAttributeValueExpression') {
432
- if (v.value === 'true') {
433
- return [attr.name, true]
434
- }
435
- if (v.value === 'false') {
436
- return [attr.name, false]
437
- }
438
- if (v.value === 'null') {
439
- return [attr.name, null]
440
- }
441
- if (v.value === 'undefined') {
442
- return [attr.name, undefined]
443
- }
444
- let quote = ['"', "'", '`'].find(
445
- (q) => v.value.startsWith(q) && v.value.endsWith(q),
446
- )
447
- if (quote) {
448
- let value = v.value
449
- if (quote !== '"') {
450
- value = v.value.replace(new RegExp(quote, 'g'), '"')
451
- }
452
- return [attr.name, JSON.parse(value)]
453
- }
454
-
455
- const number = Number(v.value)
456
- if (!isNaN(number)) {
457
- return [attr.name, number]
452
+ let quote = ['"', "'", '`'].find(
453
+ (q) => v.value.startsWith(q) && v.value.endsWith(q),
454
+ )
455
+ if (quote) {
456
+ let value = v.value
457
+ if (quote !== '"') {
458
+ value = v.value.replace(new RegExp(quote, 'g'), '"')
458
459
  }
460
+ return [attr.name, JSON.parse(value)]
461
+ }
459
462
 
460
- this.errors.push({
461
- message: `Expressions in jsx props are not supported (${attr.name}={${v.value}})`,
462
- })
463
- } else {
464
- console.log('unhandled attr', { attr }, attr.type)
463
+ const number = Number(v.value)
464
+ if (!isNaN(number)) {
465
+ return [attr.name, number]
466
+ }
467
+ const parsedJson = safeJsonParse(v.value)
468
+ if (parsedJson) {
469
+ return [attr.name, parsedJson]
465
470
  }
466
471
 
467
- return
468
- })
469
- .filter(isTruthy) as [string, any][]
470
- return attrsList
471
- }
472
+ onError({
473
+ message: `Expressions in jsx props are not supported (${attr.name}={${v.value}})`,
474
+ })
475
+ } else {
476
+ console.log('unhandled attr', { attr }, attr.type)
477
+ }
478
+
479
+ return
480
+ })
481
+ .filter(isTruthy) as [string, any][]
482
+ return attrsList
472
483
  }
473
484
 
474
485
  function isTruthy<T>(val: T | undefined | null | false): val is T {
@@ -503,3 +514,11 @@ export function mdastBfs(
503
514
  }
504
515
  return result
505
516
  }
517
+
518
+ function safeJsonParse(str: string) {
519
+ try {
520
+ return JSON.parse(str)
521
+ } catch (err) {
522
+ return null
523
+ }
524
+ }