@jsreport/jsreport-core 3.11.3 → 3.12.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.
@@ -0,0 +1,462 @@
1
+ const Module = require('module')
2
+ const path = require('path')
3
+ const fs = require('fs')
4
+
5
+ const REQUIRE_RESOLVE_CACHE = new Map()
6
+ const REQUIRE_SCRIPT_CACHE = new Map()
7
+ const PACKAGE_JSON_CACHE = new Map()
8
+
9
+ let ALL_BUILTIN_MODULES
10
+
11
+ // The isolated require is a function that replicates the node.js require but that does not
12
+ // cache the modules with the standard node.js cache, instead its uses its own cache in order
13
+ // to bring isolated modules across renders and without memory leaks.
14
+ // most of the code is copied from node.js source code and adapted a bit
15
+ // (you will see in some parts specific links to node.js source code counterpart for reference)
16
+ function isolatedRequire (_moduleId, requireFromRootDirectory, isolatedModulesMeta) {
17
+ const parentModule = typeof _moduleId !== 'string' ? _moduleId.parent : null
18
+ const moduleId = parentModule ? _moduleId.moduleId : _moduleId
19
+
20
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#LL1134C3-L1134C27
21
+ validateString(moduleId, 'id')
22
+
23
+ if (moduleId === '') {
24
+ throw createInvalidArgValueError('id', moduleId, 'must be a non-empty string')
25
+ }
26
+
27
+ if (isBuiltinModule(moduleId)) {
28
+ // built-in modules can not be require from other part than the node.js require
29
+ // perhaps in the future it can be possible:
30
+ // https://github.com/nodejs/node/issues/31852
31
+ // https://github.com/nodejs/node/issues/28823
32
+ return require(moduleId)
33
+ }
34
+
35
+ const { modulesCache, requireExtensions } = isolatedModulesMeta
36
+ const fullModulePath = resolveFilename(requireFromRootDirectory, moduleId, { parentModulePath: parentModule?.path })
37
+
38
+ if (modulesCache[fullModulePath]) {
39
+ return modulesCache[fullModulePath].exports
40
+ }
41
+
42
+ const mod = new IsolatedModule(fullModulePath, parentModule)
43
+
44
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1133
45
+ // we can not add this to the IsolatedModule.prototype because we need access to other variables
46
+ mod.require = function (id) {
47
+ return isolatedRequire({ parent: this, moduleId: id }, requireFromRootDirectory, isolatedModulesMeta)
48
+ }
49
+
50
+ modulesCache[fullModulePath] = mod
51
+
52
+ mod.filename = fullModulePath
53
+ mod.paths = Module._nodeModulePaths(path.dirname(fullModulePath))
54
+
55
+ const extension = findLongestRegisteredExtension(fullModulePath, requireExtensions)
56
+
57
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1113
58
+ // allow .mjs to be overridden
59
+ if (fullModulePath.endsWith('.mjs') && !requireExtensions['.mjs']) {
60
+ throw createRequireESMError(fullModulePath)
61
+ }
62
+
63
+ const moduleResolver = requireExtensions[extension]
64
+
65
+ moduleResolver(mod, fullModulePath)
66
+
67
+ mod.loaded = true
68
+
69
+ return mod.exports
70
+ }
71
+
72
+ function setDefaultRequireExtensions (currentExtensions, requireFromRootDirectory, compileScript) {
73
+ const extensions = Object.create(null)
74
+
75
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1263
76
+ extensions['.js'] = function (_module, filename) {
77
+ const dirname = path.dirname(filename)
78
+
79
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1273
80
+ if (filename.endsWith('.js')) {
81
+ const pkg = readPackageScope(filename)
82
+
83
+ // Function require shouldn't be used in ES modules.
84
+ if (pkg?.data?.type === 'module') {
85
+ const parent = _module.parent
86
+ const parentPath = parent?.filename
87
+ throw createRequireESMError(filename, parentPath)
88
+ }
89
+ }
90
+
91
+ let compiledScript
92
+
93
+ if (REQUIRE_SCRIPT_CACHE.has(filename)) {
94
+ compiledScript = REQUIRE_SCRIPT_CACHE.get(filename)
95
+ } else {
96
+ let moduleContent = fs.readFileSync(filename, 'utf8')
97
+
98
+ moduleContent = removeShebangFromModuleContent(moduleContent)
99
+
100
+ const moduleWrappedContent = Module.wrap(moduleContent)
101
+
102
+ compiledScript = compileScript(moduleWrappedContent, filename, false)
103
+
104
+ REQUIRE_SCRIPT_CACHE.set(filename, compiledScript)
105
+ }
106
+
107
+ // we run module in same context than main context because we want to reproduce the same behavior
108
+ // than older versions of jsreport, in which the module code is executed in the same context than main
109
+ // (because older version was using normal require, and that is how the normal node.js
110
+ // require works when evaluating modules code).
111
+ // Choosing different context means things like constructors (Number, String) and instanceof
112
+ // checks have different results and we wanted to keep the same behavior than older versions,
113
+ // however one benefit of running the modules code in same context is that we don't have to care
114
+ // about re-expose the node.js globals (like Buffer, process, etc) to the module code, because
115
+ // the main context already have those
116
+ const runScript = () => {
117
+ return compiledScript.runInThisContext({
118
+ displayErrors: true
119
+ })
120
+ }
121
+
122
+ const moduleWrappedFn = runScript()
123
+
124
+ const requireInModule = makeRequireFunction(_module, requireFromRootDirectory, currentExtensions)
125
+
126
+ const args = [_module.exports, requireInModule, _module, filename, dirname]
127
+
128
+ moduleWrappedFn.apply(_module.exports, args)
129
+ }
130
+
131
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1313
132
+ extensions['.json'] = function (_module, filename) {
133
+ const content = fs.readFileSync(filename, 'utf8')
134
+
135
+ try {
136
+ setOwnProperty(_module, 'exports', JSON.parse(stripBOM(content)))
137
+ } catch (err) {
138
+ err.message = filename + ': ' + err.message
139
+ throw err
140
+ }
141
+ }
142
+
143
+ // https://github.com/nodejs/node/blob/v16.11.0/lib/internal/modules/cjs/loader.js#L1176
144
+ extensions['.node'] = function (_module, filename) {
145
+ // Be aware this doesn't use `content`
146
+ return process.dlopen(_module, path.toNamespacedPath(filename))
147
+ }
148
+
149
+ Object.assign(currentExtensions, extensions)
150
+ }
151
+
152
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L225
153
+ function IsolatedModule (id = '', parent) {
154
+ this.id = id
155
+ this.path = path.dirname(id)
156
+ setOwnProperty(this, 'exports', {})
157
+ this.filename = null
158
+ this.loaded = false
159
+ // NOTE: since we don't set parent we don't keep track of children either
160
+ // (because to correctly track it we would need to store the module instance
161
+ // and not just the module.exports like we do now, if the need appears we can do it)
162
+ this.children = []
163
+ // NOTE: this property is already deprecated (according to node.js docs),
164
+ // it seems it does not make sense to replicate
165
+ // something here, however if the need appears we can check what we can do about it
166
+ // we should be aware of the expected values it carries according to the node.js docs
167
+ // https://nodejs.org/api/modules.html#moduleparent
168
+ this.parent = undefined
169
+
170
+ // this is always false for our case, because our modules we never run during the
171
+ // Node.js preload phase
172
+ Object.defineProperty(this, 'isPreloading', {
173
+ get () { return false }
174
+ })
175
+ }
176
+
177
+ // NOTE: we can not use Module.isBuiltin because it is not available on node 16
178
+ // we can upgrade our implementation to just use Module.isBuiltin when we drop support for node 16
179
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
180
+ function isBuiltinModule (moduleId) {
181
+ // use the standard function when available
182
+ if (Module.isBuiltin) {
183
+ return Module.isBuiltin(moduleId)
184
+ }
185
+
186
+ // the only version in which this code would run is node 16 and early versions of node 18
187
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
188
+ if (!ALL_BUILTIN_MODULES) {
189
+ ALL_BUILTIN_MODULES = new Set(Module.builtinModules.flatMap((bm) => [bm, `node:${bm}`]))
190
+ }
191
+
192
+ return ALL_BUILTIN_MODULES.has(moduleId)
193
+ }
194
+
195
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/helpers.js#L65
196
+ function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions) {
197
+ const requireFn = function require (path) {
198
+ return mod.require(path)
199
+ }
200
+
201
+ function resolve (request, options) {
202
+ const extra = {
203
+ parentModulePath: mod.path,
204
+ options
205
+ }
206
+
207
+ return resolveFilename(requireFromRootDirectory, request, extra)
208
+ }
209
+
210
+ requireFn.resolve = resolve
211
+
212
+ function paths (request) {
213
+ validateString(request, 'request')
214
+ return Module._resolveLookupPaths(request, mod)
215
+ }
216
+
217
+ resolve.paths = paths
218
+
219
+ setOwnProperty(requireFn, 'main', process.mainModule)
220
+
221
+ // Enable support to add extra extension types
222
+ requireFn.extensions = currentExtensions
223
+
224
+ // NOTE: we pass just use empty object here, it is not going to be set/used by us anywhere
225
+ // we just provide it for back-compatibility in case some module expect it to exists
226
+ requireFn.cache = Object.create(null)
227
+
228
+ return requireFn
229
+ }
230
+
231
+ function resolveFilename (requireFromRootDirectory, moduleId, extra) {
232
+ const { parentModulePath, options } = extra
233
+ const useCache = options == null
234
+ const resolveCacheKey = parentModulePath ? `${parentModulePath}::${moduleId}` : moduleId
235
+ let fullModulePath
236
+
237
+ if (useCache && REQUIRE_RESOLVE_CACHE.has(resolveCacheKey)) {
238
+ fullModulePath = REQUIRE_RESOLVE_CACHE.get(resolveCacheKey)
239
+ } else {
240
+ if (parentModulePath) {
241
+ const optionsToUse = { ...options }
242
+
243
+ // search from the parent module path by default if not explicit .paths has been passed
244
+ if (optionsToUse.paths == null) {
245
+ optionsToUse.paths = [parentModulePath]
246
+ }
247
+
248
+ fullModulePath = requireFromRootDirectory.resolve(moduleId, optionsToUse)
249
+ } else {
250
+ fullModulePath = requireFromRootDirectory.resolve(moduleId)
251
+ }
252
+
253
+ if (useCache) {
254
+ REQUIRE_RESOLVE_CACHE.set(resolveCacheKey, fullModulePath)
255
+ }
256
+ }
257
+
258
+ return fullModulePath
259
+ }
260
+
261
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L496
262
+ // Find the longest (possibly multi-dot) extension registered in extensions
263
+ function findLongestRegisteredExtension (fullPath, extensions) {
264
+ const name = path.basename(fullPath)
265
+ let currentExtension
266
+ let index
267
+ let startIndex = 0
268
+
269
+ while ((index = name.indexOf('.', startIndex)) !== -1) {
270
+ startIndex = index + 1
271
+
272
+ if (index === 0) {
273
+ // Skip dotfiles like .gitignore
274
+ continue
275
+ }
276
+
277
+ currentExtension = name.slice(index)
278
+
279
+ if (extensions[currentExtension]) {
280
+ return currentExtension
281
+ }
282
+ }
283
+
284
+ return '.js'
285
+ }
286
+
287
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L404
288
+ function readPackageScope (checkPath) {
289
+ const sep = path.sep
290
+ const rootSeparatorIndex = checkPath.indexOf(sep)
291
+ let separatorIndex
292
+
293
+ do {
294
+ separatorIndex = checkPath.lastIndexOf(sep)
295
+ checkPath = checkPath.slice(0, separatorIndex)
296
+
297
+ if (checkPath.endsWith(sep + 'node_modules')) {
298
+ return false
299
+ }
300
+
301
+ const pjson = readPackage(checkPath + sep)
302
+
303
+ if (pjson) {
304
+ return {
305
+ data: pjson,
306
+ path: checkPath
307
+ }
308
+ }
309
+ } while (separatorIndex > rootSeparatorIndex)
310
+
311
+ return false
312
+ }
313
+
314
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L362
315
+ function readPackage (requestPath) {
316
+ const jsonPath = path.resolve(requestPath, 'package.json')
317
+
318
+ const existing = PACKAGE_JSON_CACHE.get(jsonPath)
319
+
320
+ if (existing !== undefined) {
321
+ return existing
322
+ }
323
+
324
+ let json
325
+
326
+ try {
327
+ json = fs.readFileSync(jsonPath, 'utf8')
328
+ } catch (error) {}
329
+
330
+ if (json === undefined) {
331
+ PACKAGE_JSON_CACHE.set(jsonPath, false)
332
+ return false
333
+ }
334
+
335
+ try {
336
+ const filtered = filterOwnProperties(JSON.parse(json), [
337
+ 'name',
338
+ 'main',
339
+ 'exports',
340
+ 'imports',
341
+ 'type'
342
+ ])
343
+
344
+ PACKAGE_JSON_CACHE.set(jsonPath, filtered)
345
+ return filtered
346
+ } catch (e) {
347
+ e.path = jsonPath
348
+ e.message = 'Error parsing ' + jsonPath + ': ' + e.message
349
+ throw e
350
+ }
351
+ }
352
+
353
+ function removeShebangFromModuleContent (content) {
354
+ let moduleContent = content
355
+ // https://github.com/nodejs/node/blob/v7.5.0/lib/module.js#L511
356
+ // remove shebang
357
+ const contLen = moduleContent.length
358
+
359
+ if (contLen >= 2) {
360
+ if (
361
+ /* # */
362
+ moduleContent.charCodeAt(0) === 35 &&
363
+ /* ! */
364
+ moduleContent.charCodeAt(1) === 33
365
+ ) {
366
+ if (contLen === 2) {
367
+ // Exact match
368
+ moduleContent = ''
369
+ } else {
370
+ // Find end of shebang line and slice it off
371
+ let i = 2
372
+
373
+ for (; i < contLen; ++i) {
374
+ const code = moduleContent.charCodeAt(i)
375
+ /* \n || \r */
376
+ if (code === 10 || code === 13) {
377
+ break
378
+ }
379
+ }
380
+
381
+ if (i === contLen) {
382
+ moduleContent = ''
383
+ } else {
384
+ // Note that this actually includes the newline character(s) in the
385
+ // new output. This duplicates the behavior of the regular
386
+ // expression that was previously used to replace the shebang line
387
+ moduleContent = moduleContent.slice(i)
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ return moduleContent
394
+ }
395
+
396
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/helpers.js#L143
397
+ function stripBOM (content) {
398
+ if (content.charCodeAt() === 0xFEFF) {
399
+ content = content.slice(1)
400
+ }
401
+ return content
402
+ }
403
+
404
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/util.js#L548
405
+ function setOwnProperty (obj, key, value) {
406
+ return Object.defineProperty(obj, key, {
407
+ __proto__: null,
408
+ configurable: true,
409
+ enumerable: true,
410
+ value,
411
+ writable: true
412
+ })
413
+ }
414
+
415
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/util.js#L529
416
+ function filterOwnProperties (source, keys) {
417
+ const filtered = Object.create(null)
418
+
419
+ for (let i = 0; i < keys.length; i++) {
420
+ const key = keys[i]
421
+
422
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
423
+ filtered[key] = source[key]
424
+ }
425
+ }
426
+
427
+ return filtered
428
+ }
429
+
430
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/errors.js#L1518
431
+ // simplified version of the require ESM error
432
+ function createRequireESMError (filename, parentPath) {
433
+ const parentPathExtraMsg = parentPath ? ` from ${parentPath}` : ''
434
+ const error = new Error(`require() of ES Module ${filename}${parentPathExtraMsg} not supported.`)
435
+
436
+ error.name = 'Error [ERR_REQUIRE_ESM]'
437
+
438
+ return error
439
+ }
440
+
441
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/errors.js#L1279
442
+ function createInvalidArgValueError (name, value, reason) {
443
+ const error = new TypeError(`The argument '${name}' ${reason}. Received ${value}`)
444
+
445
+ error.name = 'TypeError [ERR_INVALID_ARG_VALUE]'
446
+
447
+ return error
448
+ }
449
+
450
+ // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/validators.js#L161
451
+ function validateString (value, name) {
452
+ if (typeof value !== 'string') {
453
+ const error = new TypeError(`The "${name}" argument must be of type string. Received type ${typeof value} (${value})`)
454
+
455
+ error.name = 'TypeError [ERR_INVALID_ARG_TYPE]'
456
+
457
+ throw error
458
+ }
459
+ }
460
+
461
+ module.exports = isolatedRequire
462
+ module.exports.setDefaultRequireExtensions = setDefaultRequireExtensions