ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

102 lines
2.5 KiB

  1. import type { Content, Parent, Root, RootContent } from 'mdast'
  2. const escapeForRegExp = (s: string) => s.replace (/[.*+?^${}()|[\]\\]/g, '\\$&')
  3. export default (pageNames: string[], basePath = '/wiki'): ((tree: Root) => void) => {
  4. const names = [...pageNames].sort ((a, b) => b.length - a.length)
  5. if (names.length === 0)
  6. {
  7. return () => {
  8. ;
  9. }
  10. }
  11. const re = new RegExp (`(${ names.map (escapeForRegExp).join ('|') })`, 'g')
  12. return (tree: Root) => {
  13. const edits: { parent: Parent; index: number; parts: RootContent[] }[] = []
  14. const walk = (node: Content | Root, ancestors: Parent[]) => {
  15. if (!(node) || (typeof node !== 'object'))
  16. return
  17. if (!(ancestors.some (ancestor => ['link',
  18. 'linkReference',
  19. 'image',
  20. 'imageReference',
  21. 'code',
  22. 'inlineCode'].includes (ancestor?.type)))
  23. && (node.type === 'text'))
  24. {
  25. const value = node.value ?? ''
  26. if (value)
  27. {
  28. re.lastIndex = 0
  29. let m: RegExpExecArray | null
  30. let last = 0
  31. const parts: RootContent[] = []
  32. while (m = re.exec (value))
  33. {
  34. const start = m.index
  35. const end = start + m[0].length
  36. if (start > last)
  37. parts.push ({ type: 'text', value: value.slice (last, start) })
  38. const name = m[1]
  39. parts.push ({ type: 'link',
  40. url: `${ basePath }/${ encodeURIComponent (name) }`,
  41. title: null,
  42. children: [{ type: 'text', value: name }],
  43. data: { hProperties: { 'data-wiki': '1' } } })
  44. last = end
  45. }
  46. if (parts.length)
  47. {
  48. if (last < value.length)
  49. parts.push ({ type: 'text', value: value.slice (last) })
  50. const parent = ancestors[ancestors.length - 1]
  51. if (parent && Array.isArray (parent.children))
  52. {
  53. const index = parent.children.indexOf (node)
  54. if (index >= 0)
  55. edits.push ({ parent, index, parts })
  56. }
  57. }
  58. }
  59. }
  60. const maybeChidren = (node as any).children
  61. if (Array.isArray (maybeChidren))
  62. {
  63. const parent = node as Parent
  64. for (let i = 0; i < maybeChidren.length; ++i)
  65. {
  66. const child: Content | undefined = maybeChidren[i]
  67. if (!(child))
  68. continue
  69. walk (child, ancestors.concat (parent))
  70. }
  71. }
  72. }
  73. walk (tree, [])
  74. for (let i = edits.length - 1; i >= 0; --i)
  75. {
  76. const { parent, index, parts } = edits[i]
  77. if (!(parent) || !(Array.isArray (parent.children)))
  78. continue
  79. if (0 <= index && index < parent.children.length)
  80. parent.children.splice (index, 1, ...parts)
  81. }
  82. }
  83. }