@jsreport/jsreport-core 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,18 @@
1
1
  const Module = require('module')
2
2
  const path = require('path')
3
3
  const fs = require('fs')
4
+ const resolveFilename = require('./resolveFilename')
4
5
 
5
- const REQUIRE_RESOLVE_CACHE = new Map()
6
- const REQUIRE_SCRIPT_CACHE = new Map()
7
- const PACKAGE_JSON_CACHE = new Map()
6
+ const ISOLATED_REQUIRE_RESOLVE_CACHE = new Map()
7
+ const ISOLATED_REQUIRE_SCRIPT_CACHE = new Map()
8
+ const ISOLATED_PACKAGE_JSON_CACHE = new Map()
8
9
 
9
10
  // The isolated require is a function that replicates the node.js require but that does not
10
11
  // cache the modules with the standard node.js cache, instead its uses its own cache in order
11
12
  // to bring isolated modules across renders and without memory leaks.
12
13
  // most of the code is copied from node.js source code and adapted a bit
13
14
  // (you will see in some parts specific links to node.js source code counterpart for reference)
14
- function isolatedRequire (_moduleId, requireFromRootDirectory, isolatedModulesMeta) {
15
+ function isolatedRequire (_moduleId, modulesMeta, requireFromRootDirectory) {
15
16
  const parentModule = typeof _moduleId !== 'string' ? _moduleId.parent : null
16
17
  const moduleId = parentModule ? _moduleId.moduleId : _moduleId
17
18
 
@@ -30,19 +31,26 @@ function isolatedRequire (_moduleId, requireFromRootDirectory, isolatedModulesMe
30
31
  return require(moduleId)
31
32
  }
32
33
 
33
- const { modulesCache, requireExtensions } = isolatedModulesMeta
34
- const fullModulePath = resolveFilename(requireFromRootDirectory, moduleId, { parentModulePath: parentModule?.path })
34
+ const { rootModule, modulesCache, requireExtensions } = modulesMeta
35
+
36
+ const fullModulePath = resolveFilename(ISOLATED_REQUIRE_RESOLVE_CACHE, requireFromRootDirectory.resolve, moduleId, { parentModulePath: parentModule?.path })
35
37
 
36
38
  if (modulesCache[fullModulePath]) {
37
39
  return modulesCache[fullModulePath].exports
38
40
  }
39
41
 
40
- const mod = new IsolatedModule(fullModulePath, parentModule)
42
+ let targetParentModule = parentModule
43
+
44
+ if (targetParentModule == null) {
45
+ targetParentModule = rootModule
46
+ }
47
+
48
+ const mod = new IsolatedModule(fullModulePath, targetParentModule)
41
49
 
42
50
  // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1133
43
51
  // we can not add this to the IsolatedModule.prototype because we need access to other variables
44
52
  mod.require = function (id) {
45
- return isolatedRequire({ parent: this, moduleId: id }, requireFromRootDirectory, isolatedModulesMeta)
53
+ return isolatedRequire({ parent: this, moduleId: id }, modulesMeta, requireFromRootDirectory)
46
54
  }
47
55
 
48
56
  modulesCache[fullModulePath] = mod
@@ -88,8 +96,8 @@ function setDefaultRequireExtensions (currentExtensions, requireFromRootDirector
88
96
 
89
97
  let compiledScript
90
98
 
91
- if (REQUIRE_SCRIPT_CACHE.has(filename)) {
92
- compiledScript = REQUIRE_SCRIPT_CACHE.get(filename)
99
+ if (ISOLATED_REQUIRE_SCRIPT_CACHE.has(filename)) {
100
+ compiledScript = ISOLATED_REQUIRE_SCRIPT_CACHE.get(filename)
93
101
  } else {
94
102
  let moduleContent = fs.readFileSync(filename, 'utf8')
95
103
 
@@ -99,7 +107,7 @@ function setDefaultRequireExtensions (currentExtensions, requireFromRootDirector
99
107
 
100
108
  compiledScript = compileScript(moduleWrappedContent, filename, false)
101
109
 
102
- REQUIRE_SCRIPT_CACHE.set(filename, compiledScript)
110
+ ISOLATED_REQUIRE_SCRIPT_CACHE.set(filename, compiledScript)
103
111
  }
104
112
 
105
113
  // we run module in same context than main context because we want to reproduce the same behavior
@@ -163,7 +171,7 @@ function IsolatedModule (id = '', parent) {
163
171
  // something here, however if the need appears we can check what we can do about it
164
172
  // we should be aware of the expected values it carries according to the node.js docs
165
173
  // https://nodejs.org/api/modules.html#moduleparent
166
- this.parent = undefined
174
+ this.parent = parent
167
175
 
168
176
  // this is always false for our case, because our modules we never run during the
169
177
  // Node.js preload phase
@@ -184,7 +192,7 @@ function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions)
184
192
  options
185
193
  }
186
194
 
187
- return resolveFilename(requireFromRootDirectory, request, extra)
195
+ return resolveFilename(ISOLATED_REQUIRE_RESOLVE_CACHE, requireFromRootDirectory.resolve, request, extra)
188
196
  }
189
197
 
190
198
  requireFn.resolve = resolve
@@ -208,36 +216,6 @@ function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions)
208
216
  return requireFn
209
217
  }
210
218
 
211
- function resolveFilename (requireFromRootDirectory, moduleId, extra) {
212
- const { parentModulePath, options } = extra
213
- const useCache = options == null
214
- const resolveCacheKey = parentModulePath ? `${parentModulePath}::${moduleId}` : moduleId
215
- let fullModulePath
216
-
217
- if (useCache && REQUIRE_RESOLVE_CACHE.has(resolveCacheKey)) {
218
- fullModulePath = REQUIRE_RESOLVE_CACHE.get(resolveCacheKey)
219
- } else {
220
- if (parentModulePath) {
221
- const optionsToUse = { ...options }
222
-
223
- // search from the parent module path by default if not explicit .paths has been passed
224
- if (optionsToUse.paths == null) {
225
- optionsToUse.paths = [parentModulePath]
226
- }
227
-
228
- fullModulePath = requireFromRootDirectory.resolve(moduleId, optionsToUse)
229
- } else {
230
- fullModulePath = requireFromRootDirectory.resolve(moduleId)
231
- }
232
-
233
- if (useCache) {
234
- REQUIRE_RESOLVE_CACHE.set(resolveCacheKey, fullModulePath)
235
- }
236
- }
237
-
238
- return fullModulePath
239
- }
240
-
241
219
  // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L496
242
220
  // Find the longest (possibly multi-dot) extension registered in extensions
243
221
  function findLongestRegisteredExtension (fullPath, extensions) {
@@ -295,7 +273,7 @@ function readPackageScope (checkPath) {
295
273
  function readPackage (requestPath) {
296
274
  const jsonPath = path.resolve(requestPath, 'package.json')
297
275
 
298
- const existing = PACKAGE_JSON_CACHE.get(jsonPath)
276
+ const existing = ISOLATED_PACKAGE_JSON_CACHE.get(jsonPath)
299
277
 
300
278
  if (existing !== undefined) {
301
279
  return existing
@@ -308,7 +286,7 @@ function readPackage (requestPath) {
308
286
  } catch (error) {}
309
287
 
310
288
  if (json === undefined) {
311
- PACKAGE_JSON_CACHE.set(jsonPath, false)
289
+ ISOLATED_PACKAGE_JSON_CACHE.set(jsonPath, false)
312
290
  return false
313
291
  }
314
292
 
@@ -321,7 +299,7 @@ function readPackage (requestPath) {
321
299
  'type'
322
300
  ])
323
301
 
324
- PACKAGE_JSON_CACHE.set(jsonPath, filtered)
302
+ ISOLATED_PACKAGE_JSON_CACHE.set(jsonPath, filtered)
325
303
  return filtered
326
304
  } catch (e) {
327
305
  e.path = jsonPath
@@ -439,4 +417,5 @@ function validateString (value, name) {
439
417
  }
440
418
 
441
419
  module.exports = isolatedRequire
420
+ module.exports.IsolatedModule = IsolatedModule
442
421
  module.exports.setDefaultRequireExtensions = setDefaultRequireExtensions
@@ -1,8 +1,11 @@
1
1
  const Module = require('module')
2
2
  const os = require('os')
3
3
  const path = require('path')
4
+ const resolveFilename = require('./resolveFilename')
4
5
  const isolatedRequire = require('./isolatedRequire')
5
6
 
7
+ const REQUIRE_RESOLVE_CACHE = new Map()
8
+
6
9
  module.exports = function createSandboxRequire (safeExecution, isolateModules, modulesCache, {
7
10
  rootDirectory,
8
11
  requirePaths,
@@ -15,20 +18,28 @@ module.exports = function createSandboxRequire (safeExecution, isolateModules, m
15
18
  throw new Error(`rootDirectory must be an absolute path, path: ${rootDirectory}`)
16
19
  }
17
20
 
18
- // we pass directory with trailing slash to ensure node recognize the path as directory
19
- const requireFromRootDirectory = Module.createRequire(ensureTrailingSlash(rootDirectory))
21
+ const rootProxyPath = path.join(rootDirectory, '___sandbox___')
20
22
 
21
- let isolatedModulesMeta
23
+ let modulesMeta
22
24
 
23
25
  if (isolateModules) {
24
- const requireExtensions = Object.create(null)
26
+ const rootModule = new isolatedRequire.IsolatedModule(rootProxyPath, null)
27
+ rootModule.filename = rootProxyPath
28
+ rootModule.paths = Module._nodeModulePaths(rootProxyPath)
29
+ rootModule.loaded = true
30
+
31
+ modulesMeta = {
32
+ rootModule,
33
+ modulesCache
34
+ }
35
+ }
25
36
 
26
- isolatedRequire.setDefaultRequireExtensions(requireExtensions, modulesCache, compileScript)
37
+ const requireFromRootDirectory = Module.createRequire(rootProxyPath)
27
38
 
28
- isolatedModulesMeta = {
29
- modulesCache: modulesCache,
30
- requireExtensions
31
- }
39
+ if (isolateModules) {
40
+ const requireExtensions = Object.create(null)
41
+ isolatedRequire.setDefaultRequireExtensions(requireExtensions, modulesCache, compileScript)
42
+ modulesMeta.requireExtensions = requireExtensions
32
43
  }
33
44
 
34
45
  return function sandboxRequire (moduleId, { context, useMap = true, allowAllModules = false } = {}) {
@@ -41,13 +52,13 @@ module.exports = function createSandboxRequire (safeExecution, isolateModules, m
41
52
  }
42
53
 
43
54
  if (!safeExecution || allowAllModules || allowedModules === '*') {
44
- return doRequire(moduleId, requireFromRootDirectory, requirePaths, isolatedModulesMeta)
55
+ return doRequire(moduleId, requireFromRootDirectory, requirePaths, modulesMeta)
45
56
  }
46
57
 
47
58
  const m = allowedModules.find(mod => (mod.id || mod) === moduleId)
48
59
 
49
60
  if (m) {
50
- return doRequire(m.path || moduleId, requireFromRootDirectory, requirePaths, isolatedModulesMeta)
61
+ return doRequire(m.path || moduleId, requireFromRootDirectory, requirePaths, modulesMeta)
51
62
  }
52
63
 
53
64
  const error = new Error(
@@ -62,25 +73,27 @@ module.exports = function createSandboxRequire (safeExecution, isolateModules, m
62
73
  }
63
74
  }
64
75
 
65
- function doRequire (moduleId, requireFromRootDirectory, _requirePaths, isolatedModulesMeta) {
66
- const isolateModules = isolatedModulesMeta != null
76
+ function doRequire (moduleId, requireFromRootDirectory, _requirePaths, modulesMeta) {
77
+ const isolateModules = modulesMeta != null
67
78
  const searchedPaths = []
68
79
  const requirePaths = _requirePaths || []
69
80
  const _require = isolateModules ? isolatedRequire : requireFromRootDirectory
70
81
  const extraRequireParams = []
71
82
 
72
83
  if (isolateModules) {
73
- extraRequireParams.push(requireFromRootDirectory, isolatedModulesMeta)
84
+ extraRequireParams.push(modulesMeta, requireFromRootDirectory)
74
85
  }
75
86
 
76
- let result = executeRequire(_require, moduleId, searchedPaths, ...extraRequireParams)
87
+ const resolveModule = requireFromRootDirectory.resolve
88
+
89
+ let result = executeRequire(_require, resolveModule, moduleId, searchedPaths, ...extraRequireParams)
77
90
 
78
91
  if (!result) {
79
92
  let pathsSearched = 0
80
93
 
81
94
  while (!result && pathsSearched < requirePaths.length) {
82
95
  const newModuleId = path.join(requirePaths[pathsSearched], moduleId)
83
- result = executeRequire(_require, newModuleId, searchedPaths, ...extraRequireParams)
96
+ result = executeRequire(_require, resolveModule, newModuleId, searchedPaths, ...extraRequireParams)
84
97
  pathsSearched++
85
98
  }
86
99
  }
@@ -92,10 +105,46 @@ function doRequire (moduleId, requireFromRootDirectory, _requirePaths, isolatedM
92
105
  return result
93
106
  }
94
107
 
95
- function executeRequire (_require, moduleId, searchedPaths, ...restOfParams) {
108
+ function executeRequire (_require, resolveModule, moduleId, searchedPaths, ...restOfParams) {
109
+ const isolateModules = restOfParams.length > 0
110
+ const shouldHandleModuleResolveFilenameOptimization = !isolateModules
111
+
112
+ const originalModuleResolveFilename = Module._resolveFilename
113
+
96
114
  try {
97
- return _require(moduleId, ...restOfParams)
115
+ if (shouldHandleModuleResolveFilenameOptimization) {
116
+ // when isolate modules is disabled we add an extra cache here to optimize require resolution,
117
+ // basically we want to avoid the overhead that node require resolution
118
+ // adds when trying to resolve the filename/path of a module, because even if the module
119
+ // is cached in require.cache module filename/path resolution still happens and have a cost
120
+ const customResolveFilename = (...args) => {
121
+ const customResolveModule = (...resolveArgs) => {
122
+ Module._resolveFilename = originalModuleResolveFilename
123
+ try {
124
+ return resolveModule(...resolveArgs)
125
+ } finally {
126
+ Module._resolveFilename = customResolveFilename
127
+ }
128
+ }
129
+
130
+ return optimizedResolveFilename(customResolveModule, ...args)
131
+ }
132
+
133
+ Module._resolveFilename = customResolveFilename
134
+ }
135
+
136
+ const result = _require(moduleId, ...restOfParams)
137
+
138
+ if (shouldHandleModuleResolveFilenameOptimization) {
139
+ Module._resolveFilename = originalModuleResolveFilename
140
+ }
141
+
142
+ return result
98
143
  } catch (e) {
144
+ if (shouldHandleModuleResolveFilenameOptimization) {
145
+ Module._resolveFilename = originalModuleResolveFilename
146
+ }
147
+
99
148
  if (e.code && e.code === 'MODULE_NOT_FOUND') {
100
149
  if (!searchedPaths.includes(moduleId)) {
101
150
  searchedPaths.push(moduleId)
@@ -108,10 +157,6 @@ function executeRequire (_require, moduleId, searchedPaths, ...restOfParams) {
108
157
  }
109
158
  }
110
159
 
111
- function ensureTrailingSlash (fullPath) {
112
- if (fullPath.endsWith(path.sep)) {
113
- return fullPath
114
- }
115
-
116
- return fullPath + path.sep
160
+ function optimizedResolveFilename (resolveModule, request, parent, isMain, options) {
161
+ return resolveFilename(REQUIRE_RESOLVE_CACHE, resolveModule, request, { parentModulePath: parent?.path, options })
117
162
  }
@@ -0,0 +1,30 @@
1
+
2
+ module.exports = function resolveFilename (cache, resolveModulePath, moduleId, extra) {
3
+ const { parentModulePath, options } = extra
4
+ const useCache = options == null
5
+ const resolveCacheKey = parentModulePath ? `${parentModulePath}::${moduleId}` : moduleId
6
+ let fullModulePath
7
+
8
+ if (useCache && cache.has(resolveCacheKey)) {
9
+ fullModulePath = cache.get(resolveCacheKey)
10
+ } else {
11
+ if (parentModulePath) {
12
+ const optionsToUse = { ...options }
13
+
14
+ // search from the parent module path by default if not explicit .paths has been passed
15
+ if (optionsToUse.paths == null) {
16
+ optionsToUse.paths = [parentModulePath]
17
+ }
18
+
19
+ fullModulePath = resolveModulePath(moduleId, optionsToUse)
20
+ } else {
21
+ fullModulePath = resolveModulePath(moduleId)
22
+ }
23
+
24
+ if (useCache) {
25
+ cache.set(resolveCacheKey, fullModulePath)
26
+ }
27
+ }
28
+
29
+ return fullModulePath
30
+ }
@@ -66,6 +66,22 @@ module.exports = function createRunInSandbox (reporter) {
66
66
  const m = reporter.options.sandbox.modules.find((m) => m.alias === moduleName || m.path === moduleName)
67
67
 
68
68
  if (m) {
69
+ const cachedModuleFromOutside = require.cache[m.path]
70
+
71
+ // this is an optimization, the requireMap always go the built-in node require,
72
+ // which has an overhead, even if the module you are trying to resolve is already cached
73
+ // node will try to resolve filename when the require happens from different module than
74
+ // it was requested first time.
75
+ // this becomes an issue when you do a lazy require inside helper execution, if this helper
76
+ // is a hot path (executed multiple times during rendering)
77
+ // then you are adding extra time per the amount of times you call the helper
78
+ // (which is big if you call this in a loop for 15K items),
79
+ // the solution is to try first from the require cache directly, and fallback to built-in require
80
+ // when not found, we can do this because we are sure the m.path is the path to the resolved filename of module
81
+ if (cachedModuleFromOutside != null) {
82
+ return cachedModuleFromOutside.exports
83
+ }
84
+
69
85
  return require(m.path)
70
86
  }
71
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsreport/jsreport-core",
3
- "version": "4.0.1",
3
+ "version": "4.2.0",
4
4
  "description": "javascript based business reporting",
5
5
  "keywords": [
6
6
  "report",
@@ -36,15 +36,15 @@
36
36
  "test:watch": "mocha --watch --recursive test"
37
37
  },
38
38
  "dependencies": {
39
- "@babel/code-frame": "7.12.13",
40
- "@babel/parser": "7.14.4",
41
- "@babel/traverse": "7.12.9",
39
+ "@babel/code-frame": "7.23.5",
40
+ "@babel/parser": "7.23.5",
41
+ "@babel/traverse": "7.23.5",
42
42
  "@colors/colors": "1.5.0",
43
- "@jsreport/advanced-workers": "2.0.0",
43
+ "@jsreport/advanced-workers": "2.0.2",
44
44
  "@jsreport/mingo": "2.4.1",
45
45
  "@jsreport/reap": "0.1.0",
46
46
  "@jsreport/serializator": "1.0.0",
47
- "@jsreport/ses": "1.0.0",
47
+ "@jsreport/ses": "1.0.1",
48
48
  "ajv": "6.12.6",
49
49
  "app-root-path": "3.0.0",
50
50
  "bytes": "3.1.2",
@@ -78,7 +78,7 @@
78
78
  },
79
79
  "devDependencies": {
80
80
  "@node-rs/jsonwebtoken": "0.2.0",
81
- "mocha": "9.2.2",
81
+ "mocha": "10.1.0",
82
82
  "should": "13.2.3",
83
83
  "standard": "16.0.4",
84
84
  "std-mocks": "1.0.1",
@@ -8,23 +8,31 @@ module.exports = (reporter, definition) => {
8
8
 
9
9
  reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
10
10
  data.req = reporter.Request(data.req)
11
- await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
12
- return { req: data.req, res: data.res }
11
+ const localRes = reporter.Response(data.req.context.id)
12
+ await localRes.parse(data.res)
13
+ await reporter.tests.beforeRenderListeners.fire(data.req, localRes)
14
+ return { req: data.req, res: localRes.serialize() }
13
15
  })
14
16
  reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
15
17
  data.req = reporter.Request(data.req)
16
- await reporter.tests.afterRenderListeners.fire(data.req, data.res)
17
- return { req: data.req, res: data.res }
18
+ const localRes = reporter.Response(data.req.context.id)
19
+ await localRes.parse(data.res)
20
+ await reporter.tests.afterRenderListeners.fire(data.req, localRes)
21
+ return { req: data.req, res: localRes.serialize() }
18
22
  })
19
23
  reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
20
24
  data.req = reporter.Request(data.req)
21
- await reporter.tests.validateRenderListeners.fire(data.req, data.res)
22
- return { req: data.req, res: data.res }
25
+ const localRes = reporter.Response(data.req.context.id)
26
+ await localRes.parse(data.res)
27
+ await reporter.tests.validateRenderListeners.fire(data.req, localRes)
28
+ return { req: data.req, res: localRes.serialize() }
23
29
  })
24
30
  reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
25
31
  data.req = reporter.Request(data.req)
26
- await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
27
- return { req: data.req, res: data.res }
32
+ const localRes = reporter.Response(data.req.context.id)
33
+ await localRes.parse(data.res)
34
+ await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, localRes)
35
+ return { req: data.req, res: localRes.serialize() }
28
36
  })
29
37
 
30
38
  let beforeRenderEval
@@ -1,81 +1,81 @@
1
- const extend = require('node.extend.without.arrays')
2
- const vm = require('vm')
3
- const Module = require('module')
4
- const path = require('path')
5
- const process = require('process')
6
-
7
- module.exports = (reporter, definition) => {
8
- reporter.initializeListeners.add('test-listeners', () => {
9
- reporter.beforeRenderListeners.add('listeners', async (req, res) => {
10
- const result = await reporter.executeMainAction('test-beforeRender-listeners', { req, res }, req)
11
- extend(true, req, result.req)
12
- extend(true, res, result.res)
13
- })
14
-
15
- reporter.afterRenderListeners.add('listeners', async (req, res) => {
16
- const result = await reporter.executeMainAction('test-afterRender-listeners', { req, res }, req)
17
- extend(true, req, result.req)
18
- extend(true, res, result.res)
19
- })
20
-
21
- reporter.validateRenderListeners.add('listeners', async (req, res) => {
22
- const result = await reporter.executeMainAction('test-validateRender-listeners', { req, res }, req)
23
- extend(true, req, result.req)
24
- extend(true, res, result.res)
25
- })
26
-
27
- reporter.afterTemplatingEnginesExecutedListeners.add('listeners', async (req, res) => {
28
- const result = await reporter.executeMainAction('test-afterTemplatingEnginesExecuted-listeners', { req, res }, req)
29
- extend(true, req, result.req)
30
- extend(true, res, result.res)
31
- })
32
-
33
- const evalInWorker = (code, req, res) => {
34
- const script = new vm.Script(`
35
- ;(function () {
36
- return ${code}
37
- })()
38
- `)
39
-
40
- return script.runInThisContext({
41
- displayErrors: true
42
- })(req, res, {
43
- mainModuleFilename: require.main.filename,
44
- require: (m) => {
45
- if (Module.builtinModules.includes(m)) {
46
- return require(m)
47
- }
48
-
49
- try {
50
- return require(path.join(process.cwd(), 'node_modules', m))
51
- } catch (e) {
52
- // hack, make it working in monorepo as well as normal extension
53
- return require(path.join(process.cwd(), '../../node_modules', m))
54
- }
55
- },
56
- reporter
57
- })
58
- }
59
-
60
- reporter.afterRenderListeners.add('eval-listeners', async (req, res) => {
61
- const code = await reporter.executeMainAction('test-afterRenderEval', {}, req)
62
- if (code) {
63
- return evalInWorker(code, req, res)
64
- }
65
- })
66
-
67
- reporter.beforeRenderListeners.insert(0, 'eval-listeners', async (req, res) => {
68
- const code = await reporter.executeMainAction('test-beforeRenderEval', {}, req)
69
- if (code) {
70
- return evalInWorker(code, req, res)
71
- }
72
- })
73
-
74
- reporter.afterTemplatingEnginesExecutedListeners.add('eval-listeners', async (req, res) => {
75
- const code = await reporter.executeMainAction('test-afterTemplatingEnginesExecutedEval', {}, req)
76
- if (code) {
77
- return evalInWorker(code, req, res)
78
- }
79
- })
80
- })
81
- }
1
+ const extend = require('node.extend.without.arrays')
2
+ const vm = require('vm')
3
+ const Module = require('module')
4
+ const path = require('path')
5
+ const process = require('process')
6
+
7
+ module.exports = (reporter, definition) => {
8
+ reporter.initializeListeners.add('test-listeners', () => {
9
+ reporter.beforeRenderListeners.add('listeners', async (req, res) => {
10
+ const result = await reporter.executeMainAction('test-beforeRender-listeners', { req, res: await res.serialize() }, req)
11
+ extend(true, req, result.req)
12
+ await res.parse(result.res)
13
+ })
14
+
15
+ reporter.afterRenderListeners.add('listeners', async (req, res) => {
16
+ const result = await reporter.executeMainAction('test-afterRender-listeners', { req, res: await res.serialize() }, req)
17
+ extend(true, req, result.req)
18
+ await res.parse(result.res)
19
+ })
20
+
21
+ reporter.validateRenderListeners.add('listeners', async (req, res) => {
22
+ const result = await reporter.executeMainAction('test-validateRender-listeners', { req, res: await res.serialize() }, req)
23
+ extend(true, req, result.req)
24
+ await res.parse(result.res)
25
+ })
26
+
27
+ reporter.afterTemplatingEnginesExecutedListeners.add('listeners', async (req, res) => {
28
+ const result = await reporter.executeMainAction('test-afterTemplatingEnginesExecuted-listeners', { req, res: await res.serialize() }, req)
29
+ extend(true, req, result.req)
30
+ await res.parse(result.res)
31
+ })
32
+
33
+ const evalInWorker = (code, req, res) => {
34
+ const script = new vm.Script(`
35
+ ;(function () {
36
+ return ${code}
37
+ })()
38
+ `)
39
+
40
+ return script.runInThisContext({
41
+ displayErrors: true
42
+ })(req, res, {
43
+ mainModuleFilename: require.main.filename,
44
+ require: (m) => {
45
+ if (Module.builtinModules.includes(m)) {
46
+ return require(m)
47
+ }
48
+
49
+ try {
50
+ return require(path.join(process.cwd(), 'node_modules', m))
51
+ } catch (e) {
52
+ // hack, make it working in monorepo as well as normal extension
53
+ return require(path.join(process.cwd(), '../../node_modules', m))
54
+ }
55
+ },
56
+ reporter
57
+ })
58
+ }
59
+
60
+ reporter.afterRenderListeners.add('eval-listeners', async (req, res) => {
61
+ const code = await reporter.executeMainAction('test-afterRenderEval', {}, req)
62
+ if (code) {
63
+ return evalInWorker(code, req, res)
64
+ }
65
+ })
66
+
67
+ reporter.beforeRenderListeners.insert(0, 'eval-listeners', async (req, res) => {
68
+ const code = await reporter.executeMainAction('test-beforeRenderEval', {}, req)
69
+ if (code) {
70
+ return evalInWorker(code, req, res)
71
+ }
72
+ })
73
+
74
+ reporter.afterTemplatingEnginesExecutedListeners.add('eval-listeners', async (req, res) => {
75
+ const code = await reporter.executeMainAction('test-afterTemplatingEnginesExecutedEval', {}, req)
76
+ if (code) {
77
+ return evalInWorker(code, req, res)
78
+ }
79
+ })
80
+ })
81
+ }