/
transport.js
133 lines (109 loc) · 3.28 KB
/
transport.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
'use strict'
const { createRequire } = require('module')
const getCaller = require('get-caller-file')
const { join, isAbsolute } = require('path')
let onExit
if (global.WeakRef && global.WeakMap && global.FinalizationRegistry) {
// This require MUST be top level otherwise the transport would
// not work from within Jest as it hijacks require.
onExit = require('on-exit-leak-free')
}
const ThreadStream = require('thread-stream')
function setupOnExit (stream) {
/* istanbul ignore next */
if (onExit) {
// This is leak free, it does not leave event handlers
onExit.register(stream, autoEnd)
stream.on('close', function () {
onExit.unregister(stream)
})
} else {
const fn = autoEnd.bind(null, stream)
process.once('beforeExit', fn)
process.once('exit', fn)
stream.on('close', function () {
process.removeListener('beforeExit', fn)
process.removeListener('exit', fn)
})
}
}
function buildStream (filename, workerData, workerOpts) {
const stream = new ThreadStream({
filename,
workerData,
workerOpts
})
stream.on('ready', onReady)
stream.on('close', function () {
process.removeListener('exit', onExit)
})
process.on('exit', onExit)
function onReady () {
process.removeListener('exit', onExit)
stream.unref()
if (workerOpts.autoEnd !== false) {
setupOnExit(stream)
}
}
function onExit () {
if (stream.closed) {
return
}
stream.flushSync()
}
return stream
}
function autoEnd (stream) {
stream.ref()
stream.flushSync()
stream.end()
stream.once('close', function () {
stream.unref()
})
}
function transport (fullOptions) {
const { pipeline, targets, options = {}, worker = {}, caller = getCaller() } = fullOptions
// This function call MUST stay in the top-level function of this module
const callerRequire = createRequire(caller)
// This will be eventually modified by bundlers
const bundlerOverrides = '__bundlerPathsOverrides' in globalThis ? globalThis.__bundlerPathsOverrides : {}
let target = fullOptions.target
if (target && targets) {
throw new Error('only one of target or targets can be specified')
}
if (targets) {
target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
options.targets = targets.map((dest) => {
return {
...dest,
target: fixTarget(dest.target)
}
})
} else if (fullOptions.pipeline) {
target = bundlerOverrides['pino-pipeline-worker'] || join(__dirname, 'worker-pipeline.js')
options.targets = pipeline.map((dest) => {
return {
...dest,
target: fixTarget(dest.target)
}
})
}
return buildStream(fixTarget(target), options, worker)
function fixTarget (origin) {
origin = bundlerOverrides[origin] || origin
if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
return origin
}
switch (origin) {
// This hack should not be needed, however it would not work otherwise
// when testing it from this module and in examples.
case 'pino/file':
return join(__dirname, '..', 'file.js')
/* istanbul ignore next */
default:
// TODO we cannot test this on Windows.. might not work.
return callerRequire.resolve(origin)
}
}
}
module.exports = transport