Reputation: 1205
I'm trying to understand how webpack uses DefinePlugin. I have:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
and a function:
export const foo = () => {
console.log(process)
console.log(process.env.NODE_ENV)
}
window.foo = foo
when I print foo, I see the following in my browser console:
ƒ foo() {
console.log(process);
console.log("development");
}
It seems like the variable "development" was injected while webpack was compiling the input file. At the same time webpack also injected the process object into the JavaScript code, and the browser did print out the process object when foo was called:
{title: "browser", browser: true, env: {…}, argv: Array(0), nextTick: ƒ, …}
My question is, how can the process object, which is a Node concept, be made available to the browser?
In fact, if I do:
window.process = process
I can use process.nextTick right inside the browser console! I thought the nextTick function was a Node-specific implementation! Could anybody explain this?
Thank you!
Upvotes: 20
Views: 23980
Reputation: 1
see https://webpack.js.org/configuration/node/#node
These options configure whether to polyfill or mock certain Node.js globals and modules. This allows code originally written for the Node.js environment to run in other environments like the browser.
This feature is provided by webpack's internal NodeStuffPlugin plugin. If the target is "web" (default) or "webworker", the NodeSourcePlugin plugin is also activated.
Upvotes: 0
Reputation: 14318
This doesn't directly answer this question, but it was the solution I needed. I was trying to access process
in code that webpack compiled, intending the compiled code to be run in a NodeJS environment rather than in the browser. The process
variable doesn't exist on window
, but on global
.
The solution was to set the target
in the webpack config.
webpack.config.js
const config = {
// ...
target: 'node',
// ...
};
module.exports = config;
This removes the window
mock.
Upvotes: 2
Reputation: 10429
How webpack deals with Node globals and webpack.DefinePlugin
are actually two different concerns.
Default node globals are globally injected, while constants defined in webpack.DefinePlugin
are physically replaced one by one trough all the codebase.
eg:
// config
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.MY_VAR': {foo: JSON.stringify('bar')},
}),
// source
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
console.log('process.env.MY_VAR', process.env.MY_VAR);
console.log('process.env', process.env);
console.log('process', process);
// compiled
console.log('process.env.NODE_ENV', "development");
console.log('process.env.MY_VAR', __webpack_require__.i({"foo":"bar"}));
console.log('process.env', process.env);
console.log('process', process);
Note that process.env.NODE_ENV
and process.env.MY_VAR
physically are replaced, while process.env
and process
keep their reference to the injected process
mock.
But webpack.DefinePlugin
is also able to override the mocked process
object (or just part of it): a lot of power which implies the risk of getting unexpected behaviours.
From Webpack docs:
When defining values for process prefer 'process.env.NODE_ENV': JSON.stringify('production') over process: { env: { NODE_ENV: JSON.stringify('production') } }. Using the latter will overwrite the process object which can break compatibility with some modules that expect other values on the process object to be defined.
eg:
// config
new webpack.DefinePlugin({
'process': JSON.stringify('override'),
'process.env.NODE_ENV': JSON.stringify('development'),
}),
// source
console.log('process', process);
console.log('process.env', process.env);
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
// compiled
console.log('process', "override");
console.log('process.env', "override".env); // [boum!]
console.log('process.env.NODE_ENV', "development"); // [replaced by DefinePlugin!]
Upvotes: 6
Reputation: 2586
I am going to assume you are experimenting with this on your local webpack dev server and not in your production build. Webpack dev server utilizes websockets for real time communication between the underlying node processes and the front-end bundle. If you wanted to utilize this in a production environment you would need to set up a socket between your front end JS and your node instance to send commands. From a security perspective it sounds like a nightmare, but may have some valid use cases.
Upvotes: -1
Reputation: 12926
As mentioned here https://webpack.js.org/configuration/node/#node-process, the webpack can make polyfills for different node functions, but it appears as the node.process
is a "mock"
;
"mock": Provide a mock that implements the expected interface but has little or no functionality.
Have you tested it to see if it actually works? It might just be an empty shell.
If it works I assume that the plugin actually uses something like node-process as shown in this blog-post: http://timnew.me/blog/2014/06/23/process-nexttick-implementation-in-browser/
Copied from that blogpost:
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
It is a bit hard to know for sure from the info you provided. If it truly works I think it is very likely you have Browserify enabled in your node app. Perhaps you find some of what you need here: https://webpack.js.org/loaders/transform-loader/#src/components/Sidebar/Sidebar.jsx
Hopefully you find this answer somewhat helpful.
The bottom line is that I believe it is a polyfill from somewhere.
Upvotes: 8