Skip to content

Commit

Permalink
feat: Support bundlers. (#1209)
Browse files Browse the repository at this point in the history
* feat: Support bundlers.

* test: Added bundlers support tests.

* docs: Added bundling docs.
  • Loading branch information
ShogunPanda committed Nov 10, 2021
1 parent 38ff645 commit 4e08b37
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 6 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -92,6 +92,12 @@ In many cases, Pino is over 5x faster than alternatives.

See the [Benchmarks](docs/benchmarks.md) document for comparisons.

### Bundling support

Pino supports to being bundled using tools like webpack or esbuild.

See [Bundling](docs/bundling.md) document for more informations.

<a name="team"></a>
## The Team

Expand Down
30 changes: 30 additions & 0 deletions docs/bundling.md
@@ -0,0 +1,30 @@
# Bundling

Due to its internal architecture based on Worker Threads, it is not possible to bundle Pino *without* generating additional files.

In particular, a bundler must ensure that the following files are also bundle separately:

* `lib/worker.js` from the `thread-stream` dependency
* `file.js`
* `lib/worker.js`
* `lib/worker-pipeline.js`
* Any transport used by the user (like `pino-pretty`)

Once the files above have been generated, the bundler must also add information about the files above by injecting a code which sets `__bundlerPathsOverrides` in the `globalThis` object.

The variable is a object whose keys are identifier for the files and the the values are the paths of files relative to the currently bundle files.

Example:

```javascript
// Inject this using your bundle plugin
globalThis.__bundlerPathsOverrides = {
'thread-stream-worker': pinoWebpackAbsolutePath('./thread-stream-worker.js')
'pino/file': pinoWebpackAbsolutePath('./pino-file.js'),
'pino-worker': pinoWebpackAbsolutePath('./pino-worker.js'),
'pino-pipeline-worker': pinoWebpackAbsolutePath('./pino-pipeline-worker.js'),
'pino-pretty': pinoWebpackAbsolutePath('./pino-pretty.js'),
};
```

Note that `pino/file`, `pino-worker`, `pino-pipeline-worker` and `thread-stream-worker` are required identifiers. Other identifiers are possible based on the user configuration.
8 changes: 6 additions & 2 deletions lib/transport.js
Expand Up @@ -81,6 +81,8 @@ 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

Expand All @@ -89,15 +91,15 @@ function transport (fullOptions) {
}

if (targets) {
target = join(__dirname, 'worker.js')
target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
options.targets = targets.map((dest) => {
return {
...dest,
target: fixTarget(dest.target)
}
})
} else if (fullOptions.pipeline) {
target = join(__dirname, 'worker-pipeline.js')
target = bundlerOverrides['pino-pipeline-worker'] || join(__dirname, 'worker-pipeline.js')
options.targets = pipeline.map((dest) => {
return {
...dest,
Expand All @@ -109,6 +111,8 @@ function transport (fullOptions) {
return buildStream(fixTarget(target), options, worker)

function fixTarget (origin) {
origin = bundlerOverrides[origin] || origin

if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
return origin
}
Expand Down
5 changes: 3 additions & 2 deletions lib/worker-pipeline.js
@@ -1,6 +1,7 @@
'use strict'

const EE = require('events')
const { realImport, realRequire } = require('real-require')
const { pipeline, PassThrough } = require('stream')

// This file is not checked by the code coverage tool,
Expand All @@ -13,11 +14,11 @@ module.exports = async function ({ targets }) {
let fn
try {
const toLoad = 'file://' + t.target
fn = (await import(toLoad)).default
fn = (await realImport(toLoad)).default
} catch (error) {
// See this PR for details: https://github.com/pinojs/thread-stream/pull/34
if ((error.code === 'ENOTDIR' || error.code === 'ERR_MODULE_NOT_FOUND')) {
fn = require(t.target)
fn = realRequire(t.target)
} else {
throw error
}
Expand Down
5 changes: 3 additions & 2 deletions lib/worker.js
Expand Up @@ -2,6 +2,7 @@

const pino = require('../pino.js')
const build = require('pino-abstract-transport')
const { realImport, realRequire } = require('real-require')

// This file is not checked by the code coverage tool,
// as it is not reliable.
Expand All @@ -13,11 +14,11 @@ module.exports = async function ({ targets }) {
let fn
try {
const toLoad = 'file://' + t.target
fn = (await import(toLoad)).default
fn = (await realImport(toLoad)).default
} catch (error) {
// See this PR for details: https://github.com/pinojs/thread-stream/pull/34
if ((error.code === 'ENOTDIR' || error.code === 'ERR_MODULE_NOT_FOUND')) {
fn = require(t.target)
fn = realRequire(t.target)
} else {
throw error
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -109,6 +109,7 @@
"pino-abstract-transport": "v0.5.0",
"pino-std-serializers": "^4.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.1.0",
"safe-stable-stringify": "^2.1.0",
"sonic-boom": "^2.2.1",
"thread-stream": "^0.12.0"
Expand Down
106 changes: 106 additions & 0 deletions test/transport/bundlers-support.test.js
@@ -0,0 +1,106 @@
'use strict'

const os = require('os')
const { join } = require('path')
const { readFile } = require('fs').promises
const { watchFileCreated } = require('../helper')
const { test } = require('tap')
const pino = require('../../pino')

const { pid } = process
const hostname = os.hostname()

test('pino.transport with destination overriden by bundler', async ({ same, teardown }) => {
globalThis.__bundlerPathsOverrides = {
foobar: join(__dirname, '..', 'fixtures', 'to-file-transport.js')
}

const destination = join(
os.tmpdir(),
'_' + Math.random().toString(36).substr(2, 9)
)
const transport = pino.transport({
target: 'foobar',
options: { destination }
})
teardown(transport.end.bind(transport))
const instance = pino(transport)
instance.info('hello')
await watchFileCreated(destination)
const result = JSON.parse(await readFile(destination))
delete result.time
same(result, {
pid,
hostname,
level: 30,
msg: 'hello'
})

globalThis.__bundlerPathsOverrides = undefined
})

test('pino.transport with worker destination overriden by bundler', async ({ same, teardown }) => {
globalThis.__bundlerPathsOverrides = {
'pino-worker': join(__dirname, '..', '..', 'lib/worker.js')
}

const destination = join(
os.tmpdir(),
'_' + Math.random().toString(36).substr(2, 9)
)
const transport = pino.transport({
targets: [
{
target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'),
options: { destination }
}
]
})
teardown(transport.end.bind(transport))
const instance = pino(transport)
instance.info('hello')
await watchFileCreated(destination)
const result = JSON.parse(await readFile(destination))
delete result.time
same(result, {
pid,
hostname,
level: 30,
msg: 'hello'
})

globalThis.__bundlerPathsOverrides = undefined
})

test('pino.transport with worker-pipeline destination overriden by bundler', async ({ same, teardown }) => {
globalThis.__bundlerPathsOverrides = {
'pino-pipeline-worker': join(__dirname, '..', '..', 'lib/worker-pipeline.js')
}

const destination = join(
os.tmpdir(),
'_' + Math.random().toString(36).substr(2, 9)
)
const transport = pino.transport({
pipeline: [
{
target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'),
options: { destination }
}
]
})
teardown(transport.end.bind(transport))
const instance = pino(transport)
instance.info('hello')
await watchFileCreated(destination)
const result = JSON.parse(await readFile(destination))
delete result.time
same(result, {
pid,
hostname,
level: 30,
msg: 'hello'
})

globalThis.__bundlerPathsOverrides = undefined
})

0 comments on commit 4e08b37

Please sign in to comment.