Detecting Webpack mode in JavaScript files - javascript

I am building a bundle of ReactJS files with Webpack.
Is it possible to detect inside of JSX files which Webpack mode is being used: "none", "development", or "production"?
For example, I would like some console.log() statements and global variables in development mode but not production.

One can build the Webpack bundle for development with:
$ npx webpack --config webpack.config.js --mode=development
and for production with:
$ npx webpack --config webpack.config.js --mode=production
To know whether the current build is in development mode or production mode, webpack.DefinePlugin() should be used in webpack.config.js:
const webpack = require('webpack');
module.exports = (env, argv) => {
return {
// other directives...
plugins: [
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(argv.mode === 'production'),
}),
],
}
};
Then in the bundled JavaScript files one can have this checking:
if (PRODUCTION) {
console.log("This is a production build.");
} else {
console.log("This is a development build.");
}
Note that PRODUCTION here will work not as a global variable, but as a template variable resolved to true or false.

Related

--watch not working after updateing from webpack 4 to 5

I have been migrating a project from Webpack 4 up to 5 and have hit a snag. The --watch option is no longer working with my configuration, and I've tried everything I can find.
const webpack = require('webpack');
const path = require('path');
const PATHS = {
build: path.join(__dirname, "dist")
}
const baseConfig = {
entry: {
main: './js/router.js',
},
output: {
path: PATHS.build,
filename: `[name].[chunkhash].js`,
chunkFilename:`[name].[chunkhash].js`,
publicPath: '/dist'
}
//loaders and stuff
}
module.exports = baseConfig;
webpack --watch --progress --mode development
This is what the config can be boiled down to cause the issue. Webpack builds initially fine and I can view the page locally.
But any further changes to the entry point or any modules it imports does not recompile. The progress option I added to the start command has no effect.
I suspect the issue lies with WSL. When I upgraded to webpack 5, I encountered this issue
For some reason webpack 5 tries to scan system files if it is located in /mnt/c and gets locking errors. I ended up settling on this answer and moving where my project was located from /mnt/c/dev to /usr/local/dev. This solved the locking issue, and here I am now.
I have also tried adding to my webpack config:
watchOptions: {
poll: true
}
and also tried playing around with different polling times. Nothing gave me different results.

Relative references must start with either "/", "./", or "../"

I am newbie to lit-element, and when use import to include the library, I am getting error as:
Uncaught TypeError: Failed to resolve module specifier "lit-element".
Relative references must start with either "/", "./", or "../".
Please provide any alternatives/solutions.
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
render(){
return html`
<div>
<p>A paragraph</p>
</div>
`;
}
}
customElements.define('my-element', MyElement);
This doesn't work because js imports must reference a specific file with either a relative path or an absolute path, assuming your project structure is something like the following:
/node_modules
/lit-element
/other-library
/src
/components
my-element.js
index.html
From my-element.js, your import would be
import {LitElement, html} from '../../node_modules/lit-element/lit-element.js'
However, since this kind of configuration is confusing and you'll probably end up setting a build script sometime too, the recommendation would be to keep your imports as they are right now and use a dev server that remaps your imports to node_modules like open-wc's web server
Update (Feb. 2021): The web server mentioned on this question has gone through a few iterations and improvements. The current package is #web/dev-server
As you probably know, this type of import is known as 'bare import':
import { LitElement, html } from 'lit-element';
And the error happens because web browsers cannot resolve bare imports by themselves.
I don't know what webserver are you using for developing, but a good way of avoid this type of warnings is choose one that could manage this type of imports, for example web-dev-server
There are other approaches, using, for example tools like Webpack, Polymer-cli, Open Web Components, etc, as this article explains, but, IMO, the web-dev-server option is a very good one for start.
I tried webpack for this issue and that works fine for me.
Here is how I set up webpack on my node javascript project.
First, you need to install the following packages on your project
npm install --save-dev webpack
npm install --save-dev html-webpack-plugin
npm install --save-dev css-loader
npm install --save-dev style-loader
Then, you have to create webpack.config.cjs on your project root and paste the following code into it. (please change file paths as your file structure)
const path = require('path');
const HtmlWebpack = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js'
},
plugins:[
new HtmlWebpack({
template:path.resolve(__dirname,'./src/template/index.html')
})
],
module:{
rules:[
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource"
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
]
},
devServer:{
port:8080,
hot: true,
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true,
client: {
progress: true,
},
}
}
then add this line to your package.json file
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open",
},
now open a terminal on your project root and run npm run dev on it.
You can serve bare module imports with polimer-cli.
a. install it: npm install --save-dev polymer-cli
b. Create polymer.json in your project with roughly contens:
{
"entrypoint": "index.html",
"shell": "app.js",
"npm": true
}
c. do polymer serve

Conditional requires with browserify

with browserify, I'm trying to require a module only when in development. For some reason it is always being included in production too.
NPM Scripts:
"start": "export NODE_ENV=development&& grunt watch & grunt serve",
"prod": "export NODE_ENV=production&& grunt prod"
Component requires:
const isProduction = process.env.NODE_ENV === 'production';
require('animation.gsap');
if (!isProduction) {
require('debug.addIndicators');
}
browserify bundles at compile time. So it is unaware of your variables. Check this package https://www.npmjs.com/package/conditionalify. It can solve your problem (though I have not use it) . You can have different context for different environment while bundling.

"ReferenceError: Can't find variable: require" (using babel.js output) [duplicate]

When this code (generated from babel) runs I get an error exports is undefined
Object.defineProperty(exports, '__esModule', {
any ideas?
You are most likely not executing the code in an environment that supports CommonJS modules. You could use a bundler, such as Browserify or webpack
to bundle your modules into something that can be run in different environments.
Or you could choose a different module transformer.
With webpack
Run npm install -g webpack; npm install -D babel-loader. Then with this webpack configuration:
// webpack.config.js
module.exports = {
entry: "./path/to/entry/module.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"}
]
}
};
running the webpack command will convert all *.js files reachable via the entry file with babel and bundle them together into bundle.js.
I read an article about how ES6 import and export are only supposed to work in browser with "statically analyzable files" and Babel removed import and export support in the browser because of this. Something to do with async or possibly security?
If you want to skip the server-side bundling for dev purposes, you can put
window.MyModule = MyModule at the bottom, then import
var MyModule = window.MyModule at the top of your next file

Do I need require js when I use babel?

Im experimenting with ES6, and Im using gulp to build and babel to transpile to ES5. The output is not being run in node, just linked to from a .htm file with a tag. Im thinking I need to add
<script src='require.js'></script>
or something like that.
Im trying to import / export.
////////////////scripts.js
import {Circle} from 'shapes';
c = new Circle(4);
console.log(c.area());
/////////////////shapes.js
export class Circle {
circle(radius) {
this.radius = radius;
}
area() {
return this.radius * this.radius * Math.PI;
}
}
Error is
Uncaught ReferenceError: require is not defined
Refers to this (after .pipe(babel()) in gulp)
var _shapes = require('shapes');
Do I need require js when I use babel?
You might need some module loader, but it is not necessary RequireJS. You have several options. The following will help you to get started.
rollup.js with rollup-plugin-babel
Rollup is a next-generation JavaScript module bundler. It understands ES2015 modules natively, and will produce a bundle that doesn't need any module loader to operate. Unused exports will be trimmed from the output, it's called tree-shaking.
Now I personally recommend using rollupjs, as it produces the clearest output, and is easy to setup, however, it gives a different aspect to the answer. All the other approaches do the following:
Compile the ES6 code with babel, use the module format of your choice
Concatenate the compiled modules alongside with a module loader OR use a bundler that will traverse the dependencies for you.
With rollupjs things doesn't really work this way. Here, rollup is the first step, instead of babel. It only understands ES6 modules by default. You must give an entry module of which the dependencies will be traversed and concatenated. As ES6 allows multiple named exports in a module, rollupjs is smart enough to strip unused exports, thus shrinking bundle size. Unfortunately rollupjs-s parser doesn't understand >ES6 syntax, so ES7 modules have to be compiled before rollup parses them, but the compilation should not affect the ES6 imports. It is done by using the rollup-plugin-babel plugin with the babel-preset-es2015-rollup preset (this preset is the same as the es2015 one, except the module transformer and the external-helpers plugin). So rollup will do the following with your modules if correctly set up:
Reads your ES6-7 module from the filesystem
The babel plugin compiles it to ES6 in memory
rollup parses the ES6 code for imports and exports (using acorn parser, compiled into rollup)
it traverses the whole graph, and creates a single bundle (which still might have external dependencies, and the entry's exports might be exported, in a format of your choice)
Example nodejs build:
// setup by `npm i rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// build.js:
require("rollup").rollup({
entry: "./src/main.js",
plugins: [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
]
}).then(bundle => {
var result = bundle.generate({
// output format - 'amd', 'cjs', 'es6', 'iife', 'umd'
format: 'iife'
});
require("fs").writeFileSync("./dist/bundle.js", result.code);
// sourceMaps are supported too!
}).then(null, err => console.error(err));
Example grunt build with grunt-rollup
// setup by `npm i grunt grunt-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-rollup");
grunt.initConfig({
"rollup": {
"options": {
"format": "iife",
"plugins": [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
]
},
"dist": {
"files": {
"./dist/bundle.js": ["./src/main.js"]
}
}
}
});
}
Example gulp build with gulp-rollup
// setup by `npm i gulp gulp-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// gulpfile.js
var gulp = require('gulp'),
rollup = require('gulp-rollup');
gulp.task('bundle', function() {
gulp.src('./src/**/*.js')
// transform the files here.
.pipe(rollup({
// any option supported by Rollup can be set here.
"format": "iife",
"plugins": [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
],
entry: './src/main.js'
}))
.pipe(gulp.dest('./dist'));
});
Babelify + Browserify
Babel has a neat package called babelify. It's usage is simple and straightforward:
$ npm install --save-dev babelify babel-preset-es2015 babel-preset-react
$ npm install -g browserify
$ browserify src/script.js -o bundle.js \
-t [ babelify --presets [ es2015 react ] ]
or you can use it from node.js:
$ npm install --save-dev browserify babelify babel-preset-es2015 babel-preset-react
...
var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
.transform("babelify", {presets: ["es2015", "react"]})
.bundle()
.pipe(fs.createWriteStream("bundle.js"));
This will transpile and concatenate your code at once. Browserify's .bundle will include a nice little CommonJS loader, and will organize your transpiled modules into functions. You can even have relative imports.
Example:
// project structure
.
+-- src/
| +-- library/
| | \-- ModuleA.js
| +-- config.js
| \-- script.js
+-- dist/
\-- build.js
...
// build.js
var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
.transform("babelify", {presets: ["es2015", "react"]})
.bundle()
.pipe(fs.createWriteStream("dist/bundle.js"));
// config.js
export default "Some config";
// ModuleA.js
import config from '../config';
export default "Some nice export: " + config;
// script.js
import ModuleA from './library/ModuleA';
console.log(ModuleA);
To compile just run node build.js in your project root.
Babel + WebPack
Compile all your code using babel. I recommend you to use the amd module transformer (called babel-plugin-transform-es2015-modules-amd in babel 6). After that bundle your compiled sources with WebPack.
WebPack 2 is out! It understands native ES6 modules, and will perform (or rather simulate) tree shaking using babili-s builtin dead code elimination. For now (September 2016) I would still suggest to use rollup with babel, although my opinion might change with the first release of WebPack 2. Feel free to discuss your opinions in the comments.
Custom compilation pipeline
Sometimes you want to have more control over the compilation process. You can implement your own pipeline like this:
First, you have to configure babel to use amd modules. By default babel transpiles to CommonJS modules, which is a little complicated to handle in the browser, although browserify manages to handle them in a nice way.
Babel 5: use { modules: 'amdStrict', ... } option
Babel 6: use the es2015-modules-amd plugin
Don't forget to turn on the moduleIds: true option.
Check the transpiled code for generated modul names, there are often mismatches between defined and required modules. See sourceRoot and moduleRoot.
Finally, you have to have some kind of module loader, but it isn't necessairy requirejs. There is almondjs, a tiny require shim that works well. You can even implement your own:
var __modules = new Map();
function define(name, deps, factory) {
__modules.set(name, { n: name, d: deps, e: null, f: factory });
}
function require(name) {
const module = __modules.get(name);
if (!module.e) {
module.e = {};
module.f.apply(null, module.d.map(req));
}
return module.e;
function req(name) {
return name === 'exports' ? module.e : require(name);
}
}
At the end, you can just concatenate the loader shim and the compiled modules together, and running an uglify on that.
Babel's boilerplate code is duplicated in every module
By default, most of the above methods compile each module with babel individually, and then concatenate them together. That's what babelify does too. But if you look at the compiled code, you see that babel inserts lots of boilerplate at the beginning of each file, most of them are duplicated across all files.
To prevent this you can use the babel-plugin-transform-runtime plugin.
barebones webpack 2
1) If this is your root directory:
index.html
<html>
...
<script src="./bundle.js"></script>
...
</html>
scripts.js
import { Circle } from './shapes.js';
...
shapes.js
export class Circle {
...
}
2) have node installed node
3) run the following command in your terminal:
$ npm install -g webpack
5) in your root directory run the following:
$ webpack scripts.js bundle.js
You should now have a file called bundle.js in your root directory which will be the file your index.html will consume. This is a minimalistic bundling feature from webpack. You can learn more here
require does not exist in the browser, so this error is expected. You need to use something like require.js or Browserify.

Categories

Resources