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,8 +121,22 @@ 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
- const linkRegex = / ( \s + | ^ ) ( h t t p s ? : \/ \/ (?: w w w \. | (? ! w w w ) ) [ ^ \s . ] + \. [ ^ \s ] { 2 , } | w w w \. [ ^ \s ] + \. [ ^ \s ] { 2 , } ) / g;
138
+ const linkRegex =
139
+ / ( \s | ^ ) ( h t t p s ? : \/ \/ (?: w w w \. | (? ! w w w ) ) [ ^ \s . ] + \. [ ^ \s ] { 2 , } | w w w \. [ ^ \s ] + \. [ ^ \s ] { 2 , } ) / g;
124
140
125
141
let index = 0 ;
126
142
let match : RegExpExecArray | null ;
@@ -157,20 +173,87 @@ function convertBundleIntoReact(
157
173
return React . createElement ( "span" , { style, key, className } , content ) ;
158
174
}
159
175
176
+ function linkWithLinkify (
177
+ bundle : AnserJsonEntry ,
178
+ key : number ,
179
+ style : React . CSSProperties | null ,
180
+ className : string | null
181
+ ) : JSX . Element {
182
+ const linker = linkifyit ( { fuzzyEmail : false } ) . tlds ( [ "io" ] , true ) ;
183
+
184
+ if ( ! linker . pretest ( bundle . content ) ) {
185
+ return React . createElement (
186
+ "span" ,
187
+ { style, key, className } ,
188
+ bundle . content
189
+ ) ;
190
+ }
191
+
192
+ const matches = linker . match ( bundle . content ) ;
193
+
194
+ if ( ! matches ) {
195
+ return React . createElement (
196
+ "span" ,
197
+ { style, key, className } ,
198
+ bundle . content
199
+ ) ;
200
+ }
201
+
202
+ const content : React . ReactNode [ ] = [
203
+ bundle . content . substring ( 0 , matches [ 0 ] ?. index ) ,
204
+ ] ;
205
+
206
+ matches . forEach ( ( match , i ) => {
207
+ content . push (
208
+ React . createElement (
209
+ "a" ,
210
+ {
211
+ href : match . url ,
212
+ target : "_blank" ,
213
+ key : i ,
214
+ } ,
215
+ bundle . content . substring ( match . index , match . lastIndex )
216
+ )
217
+ ) ;
218
+
219
+ if ( matches [ i + 1 ] ) {
220
+ content . push (
221
+ bundle . content . substring ( matches [ i ] . lastIndex , matches [ i + 1 ] ?. index )
222
+ ) ;
223
+ }
224
+ } ) ;
225
+
226
+ if ( matches [ matches . length - 1 ] . lastIndex !== bundle . content . length ) {
227
+ content . push (
228
+ bundle . content . substring (
229
+ matches [ matches . length - 1 ] . lastIndex ,
230
+ bundle . content . length
231
+ )
232
+ ) ;
233
+ }
234
+ return React . createElement ( "span" , { style, key, className } , content ) ;
235
+ }
236
+
160
237
declare interface Props {
161
238
children ?: string ;
162
239
linkify ?: boolean ;
240
+ fuzzyLinks ?: boolean ;
163
241
className ?: string ;
164
242
useClasses ?: boolean ;
165
243
}
166
244
167
245
export default function Ansi ( props : Props ) : JSX . Element {
168
- const { className, useClasses, children, linkify } = props ;
246
+ const { className, useClasses, children, linkify, fuzzyLinks } = props ;
169
247
return React . createElement (
170
248
"code" ,
171
249
{ className } ,
172
250
ansiToJSON ( children ?? "" , useClasses ?? false ) . map (
173
- convertBundleIntoReact . bind ( null , linkify ?? false , useClasses ?? false )
251
+ convertBundleIntoReact . bind (
252
+ null ,
253
+ linkify ?? false ,
254
+ fuzzyLinks ?? false ,
255
+ useClasses ?? false
256
+ )
174
257
)
175
258
) ;
176
259
}
0 commit comments