rn-shiki 0.0.37-7 → 0.0.37-9

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rn-shiki",
3
3
  "type": "module",
4
- "version": "0.0.37-7",
4
+ "version": "0.0.37-9",
5
5
  "description": "Shiki syntax highlighter for React Native.",
6
6
  "author": "Ryan Skinner <hello@ryanskinner.com>",
7
7
  "license": "MIT",
@@ -34,6 +34,7 @@
34
34
  "shiki": "^1.1.7"
35
35
  },
36
36
  "dependencies": {
37
+ "css-to-react-native": "^3.2.0",
37
38
  "shiki": "^1.1.7"
38
39
  },
39
40
  "devDependencies": {
@@ -1,9 +1,9 @@
1
- import type { ThemeRegistrationAny } from 'shiki'
2
1
  import type { SyntaxHighlighterProps } from 'src/types/shiki'
3
- import React from 'react'
2
+ import type { ReactStyle } from '../../utils/style-transformer'
3
+ import React, { useMemo } from 'react'
4
4
  import { Platform, ScrollView, StyleSheet, Text, type TextStyle, View } from 'react-native'
5
5
  import { useSyntaxHighlighter } from '../../hooks/useSyntaxHighlighter'
6
- import { convertShikiTheme } from '../../utils/style-transformer'
6
+ import { getRNStylesFromShikiStyle } from '../../utils/style-transformer'
7
7
 
8
8
  const monospaceFont = Platform.select({
9
9
  ios: 'Menlo',
@@ -16,9 +16,6 @@ const styles = StyleSheet.create({
16
16
  flex: 1,
17
17
  minHeight: 20,
18
18
  },
19
- contentContainer: {
20
- flexGrow: 1,
21
- },
22
19
  lineContainer: {
23
20
  flexDirection: 'row',
24
21
  flexWrap: 'wrap',
@@ -26,7 +23,7 @@ const styles = StyleSheet.create({
26
23
  })
27
24
 
28
25
  const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, languageId, theme }) => {
29
- const themeStyles = React.useMemo(() => convertShikiTheme(theme as ThemeRegistrationAny), [theme])
26
+ const stylesheet = useMemo(() => getRNStylesFromShikiStyle(theme as ReactStyle), [theme])
30
27
  const { tokens } = useSyntaxHighlighter({
31
28
  text,
32
29
  language,
@@ -35,16 +32,18 @@ const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, l
35
32
  })
36
33
 
37
34
  const renderToken = (token: any, index: number, lineIndex: number) => {
38
- const tokenStyle: TextStyle = {
39
- ...themeStyles.token,
40
- fontFamily: monospaceFont,
41
- ...(token.color && { color: token.color }),
42
- ...(token.fontStyle === 'italic' && themeStyles.italic),
43
- ...(token.fontWeight === 'bold' && themeStyles.bold),
44
- }
35
+ const tokenStyles: TextStyle = StyleSheet.flatten([
36
+ stylesheet.token,
37
+ {
38
+ fontFamily: monospaceFont,
39
+ ...(token.color && { color: token.color }),
40
+ ...(token.fontStyle === 'italic' && stylesheet.italic),
41
+ ...(token.fontWeight === 'bold' && stylesheet.bold),
42
+ },
43
+ ])
45
44
 
46
45
  return (
47
- <Text key={`${lineIndex}-${index}`} style={tokenStyle}>
46
+ <Text key={`${lineIndex}-${index}`} style={tokenStyles}>
48
47
  {token.content.replace(/ /g, '\u00A0')}
49
48
  </Text>
50
49
  )
@@ -1,108 +1,28 @@
1
- import type { RawThemeSetting, ThemeRegistrationAny } from 'shiki'
2
- import { StyleSheet, type TextStyle } from 'react-native'
1
+ import type { CSSProperties } from 'react'
2
+ import type { TextStyle } from 'react-native'
3
+ import transform, { type StyleTuple } from 'css-to-react-native'
3
4
 
4
5
  export interface HighlighterStyleSheet {
5
- container: TextStyle
6
- token: TextStyle
7
- selection: TextStyle
8
- styles: Record<string, TextStyle>
9
- [key: string]: TextStyle | Record<string, TextStyle>
6
+ [key: string]: TextStyle
10
7
  }
11
-
12
- interface TokenStyle {
13
- color?: string
14
- fontStyle?: 'normal' | 'italic'
15
- fontWeight?: 'normal' | 'bold'
16
- }
17
-
18
- function processSettings(settings: RawThemeSetting[]): Record<string, TokenStyle> {
19
- const tokenStyles: Record<string, TokenStyle> = {}
20
-
21
- settings.forEach((setting) => {
22
- if (!setting.scope || !setting.settings)
23
- return
24
-
25
- const style: TokenStyle = {}
26
- const { foreground, fontStyle } = setting.settings
27
-
28
- if (foreground) {
29
- style.color = foreground
30
- }
31
-
32
- if (fontStyle) {
33
- if (fontStyle.includes('italic')) {
34
- style.fontStyle = 'italic'
35
- }
36
- if (fontStyle.includes('bold')) {
37
- style.fontWeight = 'bold'
38
- }
39
- }
40
-
41
- // Handle single scope
42
- if (typeof setting.scope === 'string') {
43
- tokenStyles[setting.scope] = style
44
- }
45
- // Handle multiple scopes
46
- else if (Array.isArray(setting.scope)) {
47
- setting.scope.forEach((scope) => {
48
- tokenStyles[scope] = style
49
- })
50
- }
51
- })
52
-
53
- return tokenStyles
8
+ export type ReactStyle = Record<string, CSSProperties>
9
+
10
+ const ALLOWED_STYLE_PROPERTIES: Record<string, boolean> = {
11
+ color: true,
12
+ background: true,
13
+ backgroundColor: true,
14
+ fontWeight: true,
15
+ fontStyle: true,
54
16
  }
55
17
 
56
- export function convertShikiTheme(theme: ThemeRegistrationAny): ReturnType<typeof StyleSheet.create> {
57
- // Get all styles from both settings and tokenColors
58
- const settings = [...(theme.settings || []), ...(theme.tokenColors || [])]
59
-
60
- // Process basic colors from the theme
61
- const backgroundColor = theme.colors?.['editor.background'] || theme.bg || '#1e1e1e'
62
- const defaultColor = theme.colors?.['editor.foreground'] || theme.fg || '#d4d4d4'
63
- const selectionColor = theme.colors?.['editor.selectionBackground'] || 'rgba(255, 255, 255, 0.1)'
64
-
65
- // Process token styles
66
- const tokenStyles = processSettings(settings)
67
-
68
- // Convert token styles to RN styles
69
- const tokenStylesMap = Object.entries(tokenStyles).reduce<Record<string, TextStyle>>((acc, [scope, style]) => {
70
- acc[`token-${scope.replace(/\./g, '-')}`] = {
71
- color: style.color,
72
- ...(style.fontStyle && { fontStyle: style.fontStyle }),
73
- ...(style.fontWeight && { fontWeight: style.fontWeight }),
74
- }
75
- return acc
76
- }, {})
77
-
78
- // Create the complete styles object
79
- const completeStyles: HighlighterStyleSheet = {
80
- container: {
81
- backgroundColor,
82
- flex: 1,
83
- padding: 16,
84
- },
85
- token: {
86
- color: defaultColor,
87
- fontSize: 14,
88
- lineHeight: 21,
89
- },
90
- selection: {
91
- backgroundColor: selectionColor,
92
- },
93
- styles: tokenStylesMap,
94
- }
95
-
96
- return StyleSheet.create(completeStyles)
18
+ export function getRNStylesFromShikiStyle(shikiStyle: ReactStyle): HighlighterStyleSheet {
19
+ return Object.fromEntries(Object.entries(shikiStyle).map(([className, style]) => [className, cleanStyle(style)]))
97
20
  }
98
21
 
99
- export function getTokenStyle(theme: HighlighterStyleSheet, tokenTypes: string[], defaultColor: string): TextStyle {
100
- const matchingStyles = tokenTypes.map(type => theme.styles[`token-${type.replace(/\./g, '-')}`]).filter(Boolean)
22
+ export function cleanStyle(style: CSSProperties) {
23
+ const styles = Object.entries(style)
24
+ .filter(([key]) => ALLOWED_STYLE_PROPERTIES[key])
25
+ .map<StyleTuple>(([key, value]) => [key, value])
101
26
 
102
- return {
103
- ...theme.token,
104
- color: matchingStyles[0]?.color || defaultColor,
105
- ...(matchingStyles.some(s => s?.fontStyle === 'italic') && { fontStyle: 'italic' }),
106
- ...(matchingStyles.some(s => s?.fontWeight === 'bold') && { fontWeight: 'bold' }),
107
- }
27
+ return transform(styles)
108
28
  }