react-native-shiki-engine
Version:
Shiki syntax highlighting for React Native. Zero bridge overhead with native Oniguruma regex engine.
75 lines (63 loc) • 2.39 kB
text/typescript
import type { PatternScanner, RegexEngine } from '@shikijs/types'
import type { IOnigMatch, OnigString } from '@shikijs/vscode-textmate'
import { TurboModuleRegistry } from 'react-native'
import ShikiEngine from '../NativeShikiEngine'
import { convertToOnigMatch } from './utils'
export function createNativeEngine(options: { maxCacheSize?: number } = {}): RegexEngine {
const { maxCacheSize = 1000 } = options
if (!isNativeEngineAvailable()) {
throw new Error('Native engine not available')
}
return {
createScanner(patterns: (string | RegExp)[]): PatternScanner {
if (!Array.isArray(patterns) || patterns.some(p => typeof p !== 'string' && !(p instanceof RegExp))) {
throw new TypeError('Patterns must be an array of strings or RegExp objects')
}
const stringPatterns = patterns.map(p => typeof p === 'string' ? p : p.source)
const scannerId = ShikiEngine.createScanner(stringPatterns, maxCacheSize)
if (typeof scannerId !== 'number') {
throw new TypeError('Failed to create native scanner')
}
return {
findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch | null {
if (startPosition < 0)
throw new RangeError('Start position must be >= 0')
const stringContent = typeof string === 'string' ? string : string.content
if (typeof stringContent !== 'string')
throw new TypeError('Invalid input string')
try {
const result = ShikiEngine.findNextMatchSync(scannerId, stringContent, startPosition)
return convertToOnigMatch(result)
}
catch (err) {
if (__DEV__)
console.error('Error in findNextMatchSync:', err)
throw err
}
},
dispose(): void {
try {
ShikiEngine.destroyScanner(scannerId)
}
catch (err) {
if (__DEV__)
console.error('Error disposing scanner:', err)
}
},
}
},
createString(s: string): OnigString {
if (typeof s !== 'string')
throw new TypeError('Input must be a string')
return { content: s }
},
}
}
export function isNativeEngineAvailable(): boolean {
try {
return TurboModuleRegistry.getEnforcing('ShikiEngine') != null
}
catch {
return false
}
}