fix: log4js-node/log4js-node#968#1023
Conversation
Improve dynamic require for webpack.
You may also need this in the `package.json` of the project using
log4js:
```
"browser": {
"fs": false,
"cluster": false
},
```
Minor comment to clarify, and also kick off another build.
Download
left a comment
There was a problem hiding this comment.
I took the liberty to review your code, even though I'm not in any way affiliated with this project :)
Hope it's helpful.
| typeof process !== 'undefined' && | ||
| process.versions != null && | ||
| process.versions.node != null; | ||
|
|
There was a problem hiding this comment.
Just a nitpick, but why check against null?
I would expect:
const isNode = typeof process !== 'undefined' && process.versions && process.versions.nodeThere was a problem hiding this comment.
Good call. I should have seen this.
| coreAppenders.set('fileSync', require('./fileSync')); | ||
| coreAppenders.set('file', isNode ? require('./file') : {}); | ||
| coreAppenders.set('dateFile', isNode ? require('./dateFile') : {}); | ||
| coreAppenders.set('fileSync', isNode ? require('./fileSync'): {}); |
There was a problem hiding this comment.
I wonder whether there is a better way to do this...
There is a field in package.json called browser that might be better suited for this use case.
The way it works is this: You specify which file(s) to use instead of the default one(s), when running on the browser.
For example, we could make a new file called core-appenders.js and place the lines creating the coreAppenders map and populating it with the core appenders in that file. We could then make a second file called core-appenders-browser.js and place the equivalent code for the browser in that file. Then, in package.json we would write:
{
"browser": {
"./lib/appenders/core-appenders.js": "./lib/appenders/core-appenders-browser.js"
}
}Then, Browserify, Webpack and Rollup et all will know to replace the code in core-appenders.js with the code in core-appenders-browser.js when building the browser version.
An example where this is used is the popular logging package debug:
https://github.com/visionmedia/debug/blob/4.2.0/package.json#L55
There are two forms for the browser field:
- Object with node file names as keys and browser filenames as values
- File name of entry point to use instead of the one listed in the
mainfield.
There was a problem hiding this comment.
I hadn't realised this - nicely spotted, and clearly explained! Only thing that comes to mind is a possible ordering issue, or that there would be code duplication and one of those files (node or browser) could get left behind. I'm not sure i that's a higher price or the runtime cost; given the runtime cost is (as far as I can see) once off, I think it might be better to leave it as-is. More thought required!
There was a problem hiding this comment.
An important thing to understand about this line of code:
coreAppenders.set('file', isNode ? require('./file') : {});Even though the isNode check will prevent the file appender from being added to the coreAppenders map on the browser, it actually still gets bundled into the browser bundle. The reason is that, even though Webpack is pretty clever, it's not actually smart enough to figure out that isNode will always be false on browsers. Basically what Webpack does is find all required files in a static analysis phase and make sure each and every single file that might be required at runtime gets packaged into the bundle.
Using the browser field to inform Webpack and other bundlers about the fact that the file appender is not used on browsers is the accepted/standard way to do this.
there would be code duplication
That depends entirely on how you structure things. Make sure you place only the code that differs between Node and browsers in separate files and then use the browser field to map the browser versions of those files. No code needs to be duplicated.
So happy to get your code review! Thanks for your time! |
|
Any update on this? :-| |
|
Yeah sorry, I've had a series of release deadlines that have stopped me from tackling this. I had a look recently and noted that the core logic that routes log messages - a kind of root logger, if you will - is built around nodejs cluster API (https://nodejs.org/api/cluster.html) which, of course, isn't a thing in the browser. That had me thinking that the approach of substituting files when building for the browse - which @Download rightly points out - may be adequate but not a very elegant solution. It'd be preferable to change the root logger idea into one which is browser or node, and that root logger requires all the bits it needs. But naturally this is bigger than it looks and that takes me back to just doing the simplest solution to let webpack work. Anyone have views and thoughts on best approach? |
|
Don't solve this in this version ? |
This wasn't merged. You might want to see #1050 for possible solutions (which sadly no one ever got back on). |
|
Superseded by #1374 |

This addresses #968 by allowing certain
require's to run only in node, not in the browser.All tests pass as before. However, I didn't add new tests specifically for this, which I
acknowledge isn't great, but I'm not sure how to write tests for "this works when
building with webpack" beyond adding a whole webpack project to the tests, and
grepping output. There might be a great way, but I'm not sure what it is.
I also found it useful to add this in the
package.jsonof the project using log4js,which I'm adding as a note here only in case it is useful for others facing this problem.
The initial PR build failed on Windows for unknown reasons; hopefully another build attempt will resolve that.