1
1
"use strict" ;
2
2
3
3
const fs = require ( "fs" ) ;
4
- const vm = require ( "vm" ) ;
5
4
const { getAllFiles } = require ( "./test-discovery" ) ;
6
5
const components = require ( "../../components.json" ) ;
7
6
const getLoader = require ( "../../dependencies" ) ;
8
7
const languagesCatalog = components . languages ;
8
+ const coreChecks = require ( './checks' ) ;
9
9
10
10
11
11
/**
@@ -14,6 +14,13 @@ const languagesCatalog = components.languages;
14
14
* @property {Set<string> } loaded A set of loaded components.
15
15
*/
16
16
17
+ /** @type {Map<string, string> } */
18
+ const fileSourceCache = new Map ( ) ;
19
+ /** @type {() => any } */
20
+ let coreSupplierFunction = null ;
21
+ /** @type {Map<string, (Prism: any) => void> } */
22
+ const languageCache = new Map ( ) ;
23
+
17
24
module . exports = {
18
25
19
26
/**
@@ -51,9 +58,15 @@ module.exports = {
51
58
throw new Error ( `Language '${ id } ' not found.` ) ;
52
59
}
53
60
54
- // load the language itself
55
- const languageSource = this . loadComponentSource ( id ) ;
56
- context . Prism = this . runFileWithContext ( languageSource , { Prism : context . Prism } ) . Prism ;
61
+ // get the function which adds the language from cache
62
+ let languageFunction = languageCache . get ( id ) ;
63
+ if ( languageFunction === undefined ) {
64
+ // make a function from the code which take "Prism" as an argument, so the language grammar
65
+ // references the function argument
66
+ const func = new Function ( 'Prism' , this . loadComponentSource ( id ) ) ;
67
+ languageCache . set ( id , languageFunction = ( Prism ) => func ( Prism ) ) ;
68
+ }
69
+ languageFunction ( context . Prism ) ;
57
70
58
71
context . loaded . add ( id ) ;
59
72
} ) ;
@@ -69,45 +82,26 @@ module.exports = {
69
82
* @returns {Prism }
70
83
*/
71
84
createEmptyPrism ( ) {
72
- const coreSource = this . loadComponentSource ( "core" ) ;
73
- const context = this . runFileWithContext ( coreSource ) ;
74
-
75
- for ( const testSource of this . getChecks ( ) . map ( src => this . loadFileSource ( src ) ) ) {
76
- context . Prism = this . runFileWithContext ( testSource , {
77
- Prism : context . Prism ,
78
- /**
79
- * A pseudo require function for the checks.
80
- *
81
- * This function will behave like the regular `require` in real modules when called form a check file.
82
- *
83
- * @param {string } id The id of relative path to require.
84
- */
85
- require ( id ) {
86
- if ( id . startsWith ( './' ) ) {
87
- // We have to rewrite relative paths starting with './'
88
- return require ( './../checks/' + id . substr ( 2 ) ) ;
89
- } else {
90
- // This might be an id like 'mocha' or 'fs' or a relative path starting with '../'.
91
- // In both cases we don't have to change anything.
92
- return require ( id ) ;
93
- }
94
- }
95
- } ) . Prism ;
85
+ if ( ! coreSupplierFunction ) {
86
+ const source = this . loadComponentSource ( "core" ) ;
87
+ // Core exports itself in 2 ways:
88
+ // 1) it uses `module.exports = Prism` which what we'll use
89
+ // 2) it uses `global.Prism = Prism` which we want to sabotage to prevent leaking
90
+ const func = new Function ( 'module' , 'global' , source ) ;
91
+ coreSupplierFunction = ( ) => {
92
+ const module = {
93
+ // that's all we care about
94
+ exports : { }
95
+ } ;
96
+ func ( module , { } ) ;
97
+ return module . exports ;
98
+ } ;
96
99
}
97
-
98
- return context . Prism ;
100
+ const Prism = coreSupplierFunction ( ) ;
101
+ coreChecks ( Prism ) ;
102
+ return Prism ;
99
103
} ,
100
104
101
-
102
- /**
103
- * Cached file sources, to prevent massive HDD work
104
- *
105
- * @private
106
- * @type {Object.<string, string> }
107
- */
108
- fileSourceCache : { } ,
109
-
110
-
111
105
/**
112
106
* Loads the given component's file source as string
113
107
*
@@ -127,36 +121,10 @@ module.exports = {
127
121
* @returns {string }
128
122
*/
129
123
loadFileSource ( src ) {
130
- return this . fileSourceCache [ src ] = this . fileSourceCache [ src ] || fs . readFileSync ( src , "utf8" ) ;
131
- } ,
132
-
133
-
134
- checkCache : null ,
135
-
136
- /**
137
- * Returns a list of files which add additional checks to Prism functions.
138
- *
139
- * @returns {ReadonlyArray<string> }
140
- */
141
- getChecks ( ) {
142
- return this . checkCache = this . checkCache || getAllFiles ( __dirname + "/../checks" ) ;
143
- } ,
144
-
145
-
146
- /**
147
- * Runs a VM for a given file source with the given context
148
- *
149
- * @private
150
- * @param {string } fileSource
151
- * @param {* } [context={}]
152
- *
153
- * @returns {* }
154
- */
155
- runFileWithContext ( fileSource , context = { } ) {
156
- // we don't have to pass our console but it's the only way these scripts can talk
157
- // not supplying console here means that all references to `console` inside them will refer to a no-op console
158
- context . console = console ;
159
- vm . runInNewContext ( fileSource , context ) ;
160
- return context ;
124
+ let content = fileSourceCache . get ( src ) ;
125
+ if ( content === undefined ) {
126
+ fileSourceCache . set ( src , content = fs . readFileSync ( src , "utf8" ) ) ;
127
+ }
128
+ return content ;
161
129
}
162
130
} ;
0 commit comments