1
1
import Anser , { AnserJsonEntry } from "anser" ;
2
2
import { escapeCarriageReturn } from "escape-carriage" ;
3
+ import linkifyit from "linkify-it" ;
3
4
import * as React from "react" ;
4
5
5
6
/**
@@ -104,6 +105,7 @@ function createStyle(bundle: AnserJsonEntry): React.CSSProperties {
104
105
105
106
function convertBundleIntoReact (
106
107
linkify : boolean ,
108
+ fuzzyLinks : boolean ,
107
109
useClasses : boolean ,
108
110
bundle : AnserJsonEntry ,
109
111
key : number
@@ -119,6 +121,19 @@ function convertBundleIntoReact(
119
121
) ;
120
122
}
121
123
124
+ if ( fuzzyLinks ) {
125
+ return linkWithLinkify ( bundle , key , style , className ) ;
126
+ }
127
+
128
+ return linkWithClassicMode ( bundle , key , style , className ) ;
129
+ }
130
+
131
+ function linkWithClassicMode (
132
+ bundle : AnserJsonEntry ,
133
+ key : number ,
134
+ style : React . CSSProperties | null ,
135
+ className : string | null
136
+ ) {
122
137
const content : React . ReactNode [ ] = [ ] ;
123
138
const linkRegex = / ( \s | ^ ) ( h t t p s ? : \/ \/ (?: w w w \. | (? ! w w w ) ) [ ^ \s . ] + \. [ ^ \s ] { 2 , } | w w w \. [ ^ \s ] + \. [ ^ \s ] { 2 , } ) / g;
124
139
@@ -157,20 +172,87 @@ function convertBundleIntoReact(
157
172
return React . createElement ( "span" , { style, key, className } , content ) ;
158
173
}
159
174
175
+ function linkWithLinkify (
176
+ bundle : AnserJsonEntry ,
177
+ key : number ,
178
+ style : React . CSSProperties | null ,
179
+ className : string | null
180
+ ) : JSX . Element {
181
+ const linker = linkifyit ( { fuzzyEmail : false } ) . tlds ( [ "io" ] , true ) ;
182
+
183
+ if ( ! linker . pretest ( bundle . content ) ) {
184
+ return React . createElement (
185
+ "span" ,
186
+ { style, key, className } ,
187
+ bundle . content
188
+ ) ;
189
+ }
190
+
191
+ const matches = linker . match ( bundle . content ) ;
192
+
193
+ if ( ! matches ) {
194
+ return React . createElement (
195
+ "span" ,
196
+ { style, key, className } ,
197
+ bundle . content
198
+ ) ;
199
+ }
200
+
201
+ const content : React . ReactNode [ ] = [
202
+ bundle . content . substring ( 0 , matches [ 0 ] ?. index ) ,
203
+ ] ;
204
+
205
+ matches . forEach ( ( match , i ) => {
206
+ content . push (
207
+ React . createElement (
208
+ "a" ,
209
+ {
210
+ href : match . url ,
211
+ target : "_blank" ,
212
+ key : i ,
213
+ } ,
214
+ bundle . content . substring ( match . index , match . lastIndex )
215
+ )
216
+ ) ;
217
+
218
+ if ( matches [ i + 1 ] ) {
219
+ content . push (
220
+ bundle . content . substring ( matches [ i ] . lastIndex , matches [ i + 1 ] ?. index )
221
+ ) ;
222
+ }
223
+ } ) ;
224
+
225
+ if ( matches [ matches . length - 1 ] . lastIndex !== bundle . content . length ) {
226
+ content . push (
227
+ bundle . content . substring (
228
+ matches [ matches . length - 1 ] . lastIndex ,
229
+ bundle . content . length
230
+ )
231
+ ) ;
232
+ }
233
+ return React . createElement ( "span" , { style, key, className } , content ) ;
234
+ }
235
+
160
236
declare interface Props {
161
237
children ?: string ;
162
238
linkify ?: boolean ;
239
+ fuzzyLinks ?: boolean ;
163
240
className ?: string ;
164
241
useClasses ?: boolean ;
165
242
}
166
243
167
244
export default function Ansi ( props : Props ) : JSX . Element {
168
- const { className, useClasses, children, linkify } = props ;
245
+ const { className, useClasses, children, linkify, fuzzyLinks } = props ;
169
246
return React . createElement (
170
247
"code" ,
171
248
{ className } ,
172
249
ansiToJSON ( children ?? "" , useClasses ?? false ) . map (
173
- convertBundleIntoReact . bind ( null , linkify ?? false , useClasses ?? false )
250
+ convertBundleIntoReact . bind (
251
+ null ,
252
+ linkify ?? false ,
253
+ fuzzyLinks ?? false ,
254
+ useClasses ?? false
255
+ )
174
256
)
175
257
) ;
176
258
}
0 commit comments