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