1
+ // Copyright 2024 Fastly Inc.
2
+ // License: the Apache License v2.0 with LLVM Exceptions.
3
+ // See https://llvm.org/LICENSE.txt for license information.
4
+ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5
+
6
+ import regexpuc from 'regexpu-core' ;
7
+ import { parse } from 'acorn' ;
8
+ import MagicString from 'magic-string' ;
9
+ import { simple as simpleWalk } from 'acorn-walk' ;
10
+
11
+ const PREAMBLE = `;{
12
+ // Precompiled regular expressions
13
+ const precompile = (r) => { r.exec('a'); r.exec('\\u1000'); };` ;
14
+ const POSTAMBLE = '}' ;
15
+
16
+ /// Emit a block of javascript that will pre-compile the regular expressions given. As spidermonkey
17
+ /// will intern regular expressions, duplicating them at the top level and testing them with both
18
+ /// an ascii and utf8 string should ensure that they won't be re-compiled when run in the fetch
19
+ /// handler.
20
+ export function precompile ( source , filename = '<input>' , moduleMode = false ) {
21
+ const magicString = new MagicString ( source , {
22
+ filename,
23
+ } ) ;
24
+
25
+ const ast = parse ( source , {
26
+ ecmaVersion : 'latest' ,
27
+ sourceType : moduleMode ? 'module' : 'script' ,
28
+ } ) ;
29
+
30
+ const precompileCalls = [ ] ;
31
+ simpleWalk ( ast , {
32
+ Literal ( node ) {
33
+ if ( ! node . regex ) return ;
34
+ let transpiledPattern ;
35
+ try {
36
+ transpiledPattern = regexpuc ( node . regex . pattern , node . regex . flags , {
37
+ unicodePropertyEscapes : 'transform' ,
38
+ } ) ;
39
+ } catch {
40
+ // swallow regex parse errors here to instead throw them at the engine level
41
+ // this then also avoids regex parser bugs being thrown unnecessarily
42
+ transpiledPattern = node . regex . pattern ;
43
+ }
44
+ const transpiledRegex = `/${ transpiledPattern } /${ node . regex . flags } ` ;
45
+ precompileCalls . push ( `precompile(${ transpiledRegex } );` ) ;
46
+ magicString . overwrite ( node . start , node . end , transpiledRegex ) ;
47
+ } ,
48
+ } ) ;
49
+
50
+ if ( ! precompileCalls . length ) return source ;
51
+
52
+ magicString . prepend ( `${ PREAMBLE } ${ precompileCalls . join ( '\n' ) } ${ POSTAMBLE } ` ) ;
53
+
54
+ // When we're ready to pipe in source maps:
55
+ // const map = magicString.generateMap({
56
+ // source: 'source.js',
57
+ // file: 'converted.js.map',
58
+ // includeContent: true
59
+ // });
60
+
61
+ return magicString . toString ( ) ;
62
+ }
0 commit comments