mirror of
				https://github.com/Xahau/xahau-web.git
				synced 2025-11-04 04:15:47 +00:00 
			
		
		
		
	Merge pull request #51 from tequdev/global-ref
Enable external references in Markdown
This commit is contained in:
		@@ -6,6 +6,7 @@ import starlight from '@astrojs/starlight'
 | 
			
		||||
import tailwindcss from '@tailwindcss/vite'
 | 
			
		||||
import { defineConfig } from 'astro/config'
 | 
			
		||||
import starlightOpenAPI, { openAPISidebarGroups } from 'starlight-openapi'
 | 
			
		||||
import { remarkGlobalReferences } from './src/plugins/remarkGlobalReferences'
 | 
			
		||||
 | 
			
		||||
// https://astro.build/config
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
@@ -311,6 +312,9 @@ export default defineConfig({
 | 
			
		||||
    }),
 | 
			
		||||
    mdx(),
 | 
			
		||||
  ],
 | 
			
		||||
  markdown: {
 | 
			
		||||
    remarkPlugins: [remarkGlobalReferences],
 | 
			
		||||
  },
 | 
			
		||||
  vite: {
 | 
			
		||||
    plugins: [tailwindcss()],
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -18,6 +18,7 @@
 | 
			
		||||
        "astro": "^5.10.1",
 | 
			
		||||
        "react": "^19.1.0",
 | 
			
		||||
        "react-dom": "^19.1.0",
 | 
			
		||||
        "remark-reference-links": "^7.0.0",
 | 
			
		||||
        "starlight-openapi": "^0.19.1",
 | 
			
		||||
        "tailwindcss": "^4.1.11",
 | 
			
		||||
        "vanilla-cookieconsent": "^3.1.0"
 | 
			
		||||
@@ -6421,6 +6422,20 @@
 | 
			
		||||
        "url": "https://opencollective.com/unified"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/remark-reference-links": {
 | 
			
		||||
      "version": "7.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-7.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-OMACEps7CkpBio5nutUToCcXFJr9QkkoHdku41iIholMdFZ0jdRxgFmPm2B7R+DSvW83ZShdA3ubWTH+C3M6Eg==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/mdast": "^4.0.0",
 | 
			
		||||
        "unist-util-visit": "^5.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "type": "opencollective",
 | 
			
		||||
        "url": "https://opencollective.com/unified"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/remark-rehype": {
 | 
			
		||||
      "version": "11.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
    "astro": "^5.10.1",
 | 
			
		||||
    "react": "^19.1.0",
 | 
			
		||||
    "react-dom": "^19.1.0",
 | 
			
		||||
    "remark-reference-links": "^7.0.0",
 | 
			
		||||
    "starlight-openapi": "^0.19.1",
 | 
			
		||||
    "tailwindcss": "^4.1.11",
 | 
			
		||||
    "vanilla-cookieconsent": "^3.1.0"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/content/references/global.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/content/references/global.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<!-- Currently, remarkPlugins does not support HMR, so you will need to restart the dev server if you change this file. -->
 | 
			
		||||
[Internal Type]: /docs/protocol-reference/binary-format
 | 
			
		||||
[Sequence Number]: /docs/protocol-reference/data-types/#account-sequence
 | 
			
		||||
[Index Number]: /docs/protocol-reference/data-types/#index-number
 | 
			
		||||
[SHA-512Half]: /docs/protocol-reference/data-types/#hashes
 | 
			
		||||
[Specifying Time]: /docs/protocol-reference/data-types/#specifying-time
 | 
			
		||||
[seconds since the Ripple Epoch]: /docs/protocol-reference/data-types/#specifying-time
 | 
			
		||||
[Ledger Index]: /docs/protocol-reference/data-types/#ledger-index
 | 
			
		||||
[ledger index]: /docs/protocol-reference/data-types/#ledger-index
 | 
			
		||||
[Hash]: /docs/protocol-reference/data-types/#hashes
 | 
			
		||||
[Address]: /docs/protocol-reference/data-types/#addresses
 | 
			
		||||
[Currency Amount]: /docs/protocol-reference/data-types/#specifying-currency-amounts
 | 
			
		||||
[drops of XAH]: /docs/protocol-reference/data-types/#specifying-currency-amounts
 | 
			
		||||
							
								
								
									
										5
									
								
								src/content/references/pseudo-transactions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/content/references/pseudo-transactions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
EnableAmendment
 | 
			
		||||
EmitFailure
 | 
			
		||||
SetFee
 | 
			
		||||
UNLModify
 | 
			
		||||
UNLReport
 | 
			
		||||
							
								
								
									
										38
									
								
								src/content/references/transactions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/content/references/transactions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
AccountDelete
 | 
			
		||||
AccountSet
 | 
			
		||||
CheckCancel
 | 
			
		||||
CheckCash
 | 
			
		||||
CheckCreate
 | 
			
		||||
ClaimReward
 | 
			
		||||
Clawback
 | 
			
		||||
DepositPreauth
 | 
			
		||||
EscrowCancel
 | 
			
		||||
EscrowCreate
 | 
			
		||||
EscrowFinish
 | 
			
		||||
GenesisMint
 | 
			
		||||
Import
 | 
			
		||||
Invoke
 | 
			
		||||
NFTokenAcceptOffer
 | 
			
		||||
NFTokenBurn
 | 
			
		||||
NFTokenCancelOffer
 | 
			
		||||
NFTokenCreateOffer
 | 
			
		||||
NFTokenMint
 | 
			
		||||
OfferCancel
 | 
			
		||||
OfferCreate
 | 
			
		||||
Payment
 | 
			
		||||
PaymentChannelClaim
 | 
			
		||||
PaymentChannelCreate
 | 
			
		||||
PaymentChannelFund
 | 
			
		||||
Remit
 | 
			
		||||
SetHook
 | 
			
		||||
SetRegularKey
 | 
			
		||||
SetRemarks
 | 
			
		||||
SignerListSet
 | 
			
		||||
SpinalTap
 | 
			
		||||
TicketCreate
 | 
			
		||||
TrustSet
 | 
			
		||||
URITokenBurn
 | 
			
		||||
URITokenBuy
 | 
			
		||||
URITokenCancelSellOffer
 | 
			
		||||
URITokenCreateSellOffer
 | 
			
		||||
URITokenMint
 | 
			
		||||
							
								
								
									
										168
									
								
								src/plugins/remarkGlobalReferences.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/plugins/remarkGlobalReferences.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
import { readFileSync } from 'node:fs'
 | 
			
		||||
import { visit } from 'unist-util-visit'
 | 
			
		||||
 | 
			
		||||
const parseRules: {
 | 
			
		||||
  path: string
 | 
			
		||||
  parse: (content: string) => { label: string; url: string }[]
 | 
			
		||||
}[] = [
 | 
			
		||||
  {
 | 
			
		||||
    path: 'src/content/references/global.md',
 | 
			
		||||
    parse: (content: string) => {
 | 
			
		||||
      const lines = content.split('\n')
 | 
			
		||||
      const result: { label: string; url: string }[] = []
 | 
			
		||||
      for (const line of lines) {
 | 
			
		||||
        const match = line.match(/^\[([^\]]+)\]:\s*(.+)$/)
 | 
			
		||||
        if (match) {
 | 
			
		||||
          const [, label, url] = match
 | 
			
		||||
          result.push({ label, url: url.trim() })
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return result
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: 'src/content/references/transactions.md',
 | 
			
		||||
    parse: (content: string) => {
 | 
			
		||||
      const lines = content.split('\n')
 | 
			
		||||
      const result: { label: string; url: string }[] = []
 | 
			
		||||
      for (const line of lines) {
 | 
			
		||||
        if (line.length > 0) {
 | 
			
		||||
          const url = `/docs/protocol-reference/transactions/transaction-types/${line.toLowerCase()}`
 | 
			
		||||
          result.push({ label: `${line}`, url })
 | 
			
		||||
          result.push({ label: `${line} transaction`, url })
 | 
			
		||||
          result.push({ label: `${line} transactions`, url })
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return result
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: 'src/content/references/pseudo-transactions.md',
 | 
			
		||||
    parse: (content: string) => {
 | 
			
		||||
      const lines = content.split('\n')
 | 
			
		||||
      const result: { label: string; url: string }[] = []
 | 
			
		||||
      for (const line of lines) {
 | 
			
		||||
        if (line.length > 0) {
 | 
			
		||||
          const url = `/docs/protocol-reference/transactions/pseudo-transaction-types/${line.toLowerCase()}`
 | 
			
		||||
          result.push({ label: `${line}`, url })
 | 
			
		||||
          result.push({ label: `${line} transaction`, url })
 | 
			
		||||
          result.push({ label: `${line} transactions`, url })
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return result
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remark plugin to resolve reference-style links from global.md
 | 
			
		||||
 */
 | 
			
		||||
export function remarkGlobalReferences() {
 | 
			
		||||
  let globalRefs: Record<string, string> | null = null
 | 
			
		||||
 | 
			
		||||
  function loadGlobalReferences() {
 | 
			
		||||
    if (globalRefs !== null) return globalRefs
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      globalRefs = {}
 | 
			
		||||
      for (const rule of parseRules) {
 | 
			
		||||
        const content = readFileSync(rule.path, 'utf-8')
 | 
			
		||||
        const refs = rule.parse(content)
 | 
			
		||||
        for (const ref of refs) {
 | 
			
		||||
          globalRefs[ref.label] = ref.url
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return globalRefs
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
      console.warn('Could not load global.md references:', error.message)
 | 
			
		||||
      globalRefs = {}
 | 
			
		||||
      return globalRefs
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return function transformer(tree: any) {
 | 
			
		||||
    const refs = loadGlobalReferences()
 | 
			
		||||
 | 
			
		||||
    // Find all reference-style links in the format [text][]
 | 
			
		||||
    visit(tree, 'linkReference', (node) => {
 | 
			
		||||
      if (
 | 
			
		||||
        node.referenceType === 'shortcut' ||
 | 
			
		||||
        node.referenceType === 'collapsed'
 | 
			
		||||
      ) {
 | 
			
		||||
        const label = node.label || node.children?.[0]?.value
 | 
			
		||||
 | 
			
		||||
        if (label && refs[label]) {
 | 
			
		||||
          // Convert linkReference to a regular link
 | 
			
		||||
          node.type = 'link'
 | 
			
		||||
          node.url = refs[label]
 | 
			
		||||
          delete node.referenceType
 | 
			
		||||
          delete node.identifier
 | 
			
		||||
          delete node.label
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    // Also look for escaped reference-style links in text nodes: \[label]\[]
 | 
			
		||||
    visit(tree, 'text', (node, index, parent) => {
 | 
			
		||||
      if (!node.value) return
 | 
			
		||||
 | 
			
		||||
      // Find escaped reference-style links: \[label]\[]
 | 
			
		||||
      const pattern = /\\?\[([^\]]+)\\?\]\\?\[\]/g
 | 
			
		||||
      const replacements = []
 | 
			
		||||
      let match: RegExpExecArray | null = null
 | 
			
		||||
 | 
			
		||||
      match = pattern.exec(node.value)
 | 
			
		||||
      while (match !== null) {
 | 
			
		||||
        const [fullMatch, label] = match
 | 
			
		||||
        if (refs[label]) {
 | 
			
		||||
          replacements.push({
 | 
			
		||||
            start: match.index,
 | 
			
		||||
            end: match.index + fullMatch.length,
 | 
			
		||||
            label,
 | 
			
		||||
            url: refs[label],
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
        match = pattern.exec(node.value)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Apply replacements from right to left to maintain indices
 | 
			
		||||
      if (replacements.length > 0) {
 | 
			
		||||
        const newNodes = []
 | 
			
		||||
        let lastIndex = 0
 | 
			
		||||
 | 
			
		||||
        for (const replacement of replacements) {
 | 
			
		||||
          // Add text before the replacement
 | 
			
		||||
          if (replacement.start > lastIndex) {
 | 
			
		||||
            newNodes.push({
 | 
			
		||||
              type: 'text',
 | 
			
		||||
              value: node.value.slice(lastIndex, replacement.start),
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Add the link
 | 
			
		||||
          newNodes.push({
 | 
			
		||||
            type: 'link',
 | 
			
		||||
            url: replacement.url,
 | 
			
		||||
            children: [{ type: 'text', value: replacement.label }],
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
          lastIndex = replacement.end
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add remaining text
 | 
			
		||||
        if (lastIndex < node.value.length) {
 | 
			
		||||
          newNodes.push({
 | 
			
		||||
            type: 'text',
 | 
			
		||||
            value: node.value.slice(lastIndex),
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Replace the current node with the new nodes
 | 
			
		||||
        if (parent && typeof index === 'number') {
 | 
			
		||||
          parent.children.splice(index, 1, ...newNodes)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user