I found that the source code is full of comparisons if ("production" !== "development").
For example (from https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/react.js, line 6240):
if ("production" !== "development") {
this._checkPropTypes(
contextTypes,
maskedContext,
ReactPropTypeLocations.context
);
}
But why? The result of such checks is always the same.
They use browserify with envify.
envify will replace your environment variable checks with ordinary
strings - only the variables you use will be included, so you don't
have to worry about, say, AWS_SECRET_KEY leaking through either. [...]
By running this through a good minifier (e.g. UglifyJS2), the above
code would be stripped out completely.
This is done to enable development-only checks and logging.
If you look through the React source, you'll see if (__DEV__) checks, which get replaced with if ("production" !== process.env.NODE_ENV) by React's build process.
envify is then used to replace references to process.env.NODE_ENV in the source with its current value at the time the build process is run.
For the uncompressed development build of React (and when using the npm version, by default) process.env.NODE_ENV is set to "development", so you get the benefit of these extra checks, such as validating props passed to component against its propTypes, warning if controlled components are potentially read-only due to misconfiguration, warnings about lists of items without a key prop and all the other development-mode logging React provides for common problems such as misspelt lifecycle method and HTML attribute prop names.
For the compressed production build, process.env.NODE_ENV is set to "production", so when the build is compressed with UglifyJS, these blocks of code are detected by Ugilify's dead code elimination process as code which will never get run and are completely removed from the source.
A nice side-effect of this is that you can take advantage of if ("production" !== process.env.NODE_ENV) checks to add development-only checks and logging to your own React components and libraries, since people bundling React from npm currently (with v0.12.2 the current version at the time of writing) have to deal with this as part of their build process.
Related
So what I want to ask is: Is there a way to convert this simple file into a library which can work with Browsers (script tag), Node JS and Single Page Applications using single codebase?
Until now, all I have been doing was using libraries but it never came to my mind that it isn't actually that simple to make one. I am working on a React application where I created a simple helper file with exports. I thought "why not make it an independent library in NPM?". Just as I started testing it independently in NodeJS environment, I came to realize that there is actually a lot of difference in the way both environments make imports.
I have slight knowledge of Webpack, but I don't know how to approach it. Would I need different codes for different environments?
Another thing that confused me was when I thought about "How do we actually import things/functions from libraries?" Like when we install any library from NPM INSTALL and we do "import { abc } from 'library'", does it look for an index.js file in the library folder or what? Or in case of Node, "let lib = require('library')", where it does it look since its a different environment than SPA?
In conclusion, I have a simple single file I want to launch in NPM as a library which could work in any environment.
This is a very broad question, so a very broad answer: Look at tools like webpack, rollup, and browserify which are often used to make Node.js things available in the browser.
Another possibility is to look at how a module like slug is coded such that it works in the browser and in Node.js, but requires some hacks (and a decent test setup) to do it. For example, it checks typeof window !== 'undefined' to switch between code that needs to run in the browser vs. code that needs to run in Node.js. And this bit at the end detects the module system being used and acts accordingly:
if (typeof define !== 'undefined' && define.amd) { // AMD
define([], function () { return slug })
} else if (typeof module !== 'undefined' && module.exports) { // CommonJS
module.exports = slug
} else { // Script tag
root.slug = slug
}
This is a good helper for development mode, but does this code get removed from bundles or is a particular plugin needed to remove it? Seems like it would have to be removed since process is not available in the browser. What setting causes this to happen?
The webpack docs aren't really all that clear about what these options do, just giving vague references:
https://webpack.js.org/concepts/targets/
and
https://webpack.js.org/concepts/output/
if (process.env.NODE_ENV !== 'production') {
if (typeof nextValue === 'undefined') {
console.info(next);
throw new Error('React Table: A reducer hook ☝️ just returned undefined! This is not allowed.');
}
}
process.env.NODE_ENV is actually available in the browser because Webpack creates the process variable as a global in code that is output. It is controlled via the Webpack configuration mode.
You can set the mode in your config or the command line. If you use enivornment based Webpack configs (ie. webpack.dev.js, webpack.prod.js) they will automatically set the mode.
webpack --mode=production
I have a fairly simple webpack set up with a bit of a twist. I have a few different ways I can think of to create my intended behavior, but I'm wondering if there are better approaches (I'm still fairly new to webpack).
I have a basic TypeScript/Scss application and all of the src files exist in a src directory. I also have a component library bundle that's dynamically generated through a separate build process (triggered via Node). This bundle also ends up in the src directory (it contains some sass variables and a few other assets that belong in src). This is src/custom-library-bundle. My current webpack set up moves the appropriate bundle files to a public (dist) directory via the CopyWebpackPlugin. My webpack dev server watches for changes and reloads as expected. This all works beautifully.
Now for the twist. I've set up a custom watcher that exists elsewhere to watch for the custom component library bundle changes. When a change occurs there, it triggers that custom build process mentioned above. This (as expected) modifies the src/custom-library-bundle directory and sets off several watches/reloads as the bundle is populated. Technically it works? And the behavior is expected, but ideally, I could tell it to wait until the custom installation work is "done" and only then have it trigger the compilation/reload.
Phew. This isn't an easy one to explain. Here's a webpack config that will (hopefully) help clarify:
devServer: {
contentBase: path.join(__dirname, 'public'),
port: 9000,
host: '127.0.0.1',
after: (app, server) => {
new MyCustomWatcherForOtherThings().watch(() => {
// invoked after the src/custom-library-bundle is done doing its thing (each time)
// now that I know it's done, I want to trigger the normal compilation/reload
})
},
watchOptions: {
ignored: [
/node_modules/
// I've been experimenting with the ignored feature a bit
// path.resolve(__dirname, 'src/custom-library-bundle/')
]
}
}
Ideal Approach: In my most ideal scenario, I want to just manually trigger webpack dev server to do its thing in my custom watch callback; have it ignore the src/custom-library-bundle until I tell it to pay attention. I can't seem to find a way to do this, however. Is this possible?
Alternate Approach #1: I could ignore the src/custom-library-bundle directory, move the updated files to public (not utilizing the webpack approach), and then trigger just a reload when I know that's complete. I don't love this because I want to utilize the same process whether I'm watching or just doing a one-off build (I want everything to end up in the public directory because webpack did that work, not becuase I wrote a script to put it there under specific scenarios). But, let's say I get over that, how can I trigger a browser reload for the dev server? Is this possible?
Alternate Approach #2 This is the one I'm leaning towards but it feels like extra, unnecessary work. I could have my custom build process output to a different directory (one that my webpack set up doesn't care about at all). Once the build process is complete, I could move all the files to src/custom-library-bundle where the watch would pick up 1 change and do a single complication/reload. This gets me so close, but feels like I'm adding a step I don't want to.
Alternate Approach #3? Can you think of a better way to solve this?
Update (including versions):
webpack 4.26.1
webpack-dev-server 3.1.14
Add the following server.sockWrite call to your after method:
devServer: {
after: (app, server) => {
new MyCustomWatcherForOtherThings().watch(() => {
// invoked after the src/custom-library-bundle is done doing its thing (each time)
// now that I know it's done, I want to trigger the normal compilation/reload
// calling this on `server` triggers a full page refresh
server.sockWrite(server.sockets, "content-changed");
});
};
}
I've never found this in the documentation, but one of webpack's core devs mentioned it in a comment on GitHub, so it's sort of sanctioned.
Useful things that webpack provides that come to mind are multi-compiler builds, creating a child compiler from a Compiler or Compilation instance, the DllPlugin and programmatically managing the compiler by calling webpack.watch() or webpack.compile().
the multi-compiler setup is useful when you want to build several compilations in a single run whose outputs are independent of each other
the child compilers allows you to set up dependencies between
compilers and using hooks they allow you to block the parent until,
say, the child compilation finished emitting the latest bundle into
the assets
the DllPlugin allows you to create a compilation and
it's manifest that could produce chunks that can contain modules that
can be used as dependents for yet unbuilt compilations (the manifest
needs to be produced and passed to them beforehand)
programmatically
managing you compiler lets you write a straightforward Node.js script
that could accomplish most of this manually.
If I understood correctly, your webpack compiler doesn't really have any dependencies on your bundle aside from expecting it to have something added to the output assets, so you might consider doing a multi-compiler build. If that doesn't work for you, you can write a simple plugin that creates a child compiler that watches all the component bundle dependencies and emits the built bundle into the main compilation assets on changes. Ultimately, as you mentioned yourself, you could write a simple script and programmatically weave everything together. All of these approaches offload tracking build dependencies to webpack, so if you put the compilers into watch mode webpack will keep track when something needs updating.
If you're interested in taking a closer look at how child compilers are created and used, I would heartily recommend reading through the sources of the html-webpack-plugin plugin. It doesn't seem like the plugin is handling the same kind of build setup as you have, but worth noting is that the HTML plugin works with files that aren't part of the build dependencies (nothing in the sources references or depends on the files/templates that are used for creating the HTML files that are added to the output).
Please see the code here
if (process.env.NODE_ENV !== 'production') {
if ('function' === typeof Object.freeze) {
Object.keys(self._routes).forEach(function freezeRoute(name) {
var route = self._routes[name];
Object.freeze(route.config);
Object.freeze(route.keys);
Object.freeze(route);
});
Object.freeze(self._routes);
}
}
why do freeze in non production mode. Is it to verify it is not being modified during development, but avoid any kind of runtime cost during production?
Yes, this is precisely the reason mentioned in the commit where this functionality was added:
We use Object.freeze to freeze the router and route objects for non-production environments to ensure the immutability of these objects.
For production environments, it is recommended to use tools like envify along with uglify as part of your build process to strip out the [non-]production specific code for performance benefits.
We use if (process.env.NODE_ENV !== 'production') to wrap around Object.freeze(), so that you can use various tools to build the code for different environments:
The reason they did this was because Object.freeze was slow at the time - at this point the performance hit of Object.freeze has been greatly mitigated (at least in V8).
I have a node.js (v0.6.12) application that starts by evaluating a Javascript file, startup.js. It takes a long time to evaluate startup.js, and I'd like to 'bake it in' to a custom build of Node if possible.
The v8 source directory distributed with Node, node/deps/v8/src, contains a SconScript that can almost be used to do this. On line 302, we have
LIBRARY_FILES = '''
runtime.js
v8natives.js
array.js
string.js
uri.js
math.js
messages.js
apinatives.js
date.js
regexp.js
json.js
liveedit-debugger.js
mirror-debugger.js
debug-debugger.js
'''.split()
Those javascript files are present in the same directory. Something in the build process apparently evaluates them, takes a snapshot of state, and saves it as a byte string in node/out/Release/obj/release/snapshot.cc (on Mac OS). This file seems to be baked into Node.
Some customization of the startup snapshot is possible by altering the SconScript. For example, I can change the definition of the builtin Date.toString by altering date.js. I can even add new global variables by adding startup.js to the list of library files, with contents global.test = 1.
However, I can't put just any javascript code in startup.js. If it contains Date.toString = 1;, an error results even though the code is valid at the node repl:
Build failed: -> task failed (err #2):
{task: libv8.a SConstruct -> libv8.a}
make: *** [program] Error 1
And it obviously can't make use of code that depends on libraries Node adds to v8. global.underscore = require('underscore'); causes the same error.
I'd ideally like a tool, customSnapshot, where customSnapshot startup.js evaluates startup.js with Node and then dumps a snapshot to a file, snapshot.cc, which I can put into the Node source directory. I can then build node and tell it not to rebuild the snapshot.
I just added an option to the mksnapshot command (which runs while you are building V8). The new --extra-file=filename.js flag lets you specify a file that is to be loaded and run in the process and then put in the snapshot. It's on the trunk version of V8, not the 3.11 branch that is being used for node 0.8 so you will have to run node 0.8 with V8 version 3.11. As far as I know at the moment that works, but you will be somewhat on your own.
Please file bugs if you try this and it doesn't work for you.