Why adding babel-loader in webpack conf file? - javascript

I am new to webpack and have seen some examples as following:
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
// ....
Why is this necessary since webpack auto transpiles es6 to es5?
Edit:
Ok, it DOES NOT transpile automatically unless instructed to do so.

Why is this necessary since webpack auto transpiles es6 to es5?
Webpack does not auto-transpile ES6 to ES5. It is simply a build tool. It does nothing but execute the plugins & loaders you tell it to.
But the es6 code I wrote get transpiled to es5 even without this "babel" rule
I don't see it transpiling ES6 to ES5.
The first example I looked for in your code was the conversion of let to var in the bundled code since this is probably the most commonly used ES6 feature.
With babel-loader, let gets converted to var (and some other fancy maneuvering). Without, it remains let.
To explore this, I commented out UglifyJS so the bundle was readable and ctrl+fed the file. You should be able to see this same behavior.
If you're expecting import to be converted to require, this won't happen as webpack just reads the file and loads it into the bundle. So, no require & no import appear in the bundle. This isn't transpilation, though. It's just a function of how webpack's bundling process works (searching for & injecting dependencies into the bundle).
Bonus points:
I would recommend adding your dist directory to .gitignore. Typically, you don't want your bundled code version controlled. You should rely on your build tools to handle this (you can add webpack to your package.json's postinstall if you want to simplify the installation for consumers of your project).
In hindsight, I realize you probably only added the dist directory because I asked to see the bundled code. Sorry! :p But I'll leave this here in case it helps someone else in the future.

Related

Why do some minified js files contain calls to "require" function

I've been modernizing some old gulp config where js files are concatenated and then minified by migrating to webpack.
Some of bundles contained libraries such as moment.js and isotope-docs.min.js,
When bundling with webpack I would get error that specific file or path is not found.
For example looking at moment.js
There is require("./locale/"+t) which causes my webpack to fail since i dont have locale directory.
Why would bundled js file have require function when browsers dont understand that?
Before ES modules became a thing, JavaScript did not have an official module syntax. Also, developers wanted to write a library once for both Node.js and the browser. The closest thing available was Node.js's require(), which does not exist on the browser.
So what tools like Browserify and Rollup would do is polyfill an implementation of require() (e.g. wrap the code in a "UMD"). This way, the module worked on any plaform and require() calls work as if in Node.js (its implementation may vary and can be extended because dealing with the filesystem is very different from dealing with a network).
Found a fix, you can just add to webpack confing under module noParse e.g
webpack.config.js
module: {
rules: [ ... ]
noParse: /moment.min.js|isotope-docs.min.js/
}

Grunt and ES6 modules in node - incompatible without the use of .mjs?

So, I'm dabbling a bit with Typescript and Grunt at the moment to see if it's worth it for me. The thing is: Typescript does not compile to *.mjs files but only regular *.js files. Node does support ES6 Modules but only if you either mark them as '*.jsm' files or by setting "type": "module". Setting this top-level field in package.json however has global scope for any *.js file in the same directory and any following ones.
This breaks the Gruntfile.js file as it seems since it uses CommonJS modules, see my very basic Gruntfile as example:
module.exports = function (grunt) {
grunt.initConfig({
ts: {
default: {tsconfig: "./tsconfig.json"}
}
})
grunt.loadNpmTasks("grunt-ts");
grunt.registerTask("default", ["ts"]);
}
Without expecting much success I naively changed the export syntax from module.exports = to export default which expectedly did no work since it didn't make much sense.
Questions
Is there any option to use Grunt with ES6 modules enabled in node?
Is there a proper way to tell TypeScript to compile to *.mjs files?
If you set "type": "module" in your package.json, you need to rename Gruntfile.js to Gruntfile.cjs, and run it with grunt --gruntfile Gruntfile.cjs.
The suggested approach with Babel running before Grunt makes Grunt a bit redundant. Since TypeScript does not yet support exporting ES6 modules to *.mjs files (and you have to use the *.mjs suffix in your import when node should still be running with its CommonJS system) and will probably never fully (see Design Meeting Notes 11/22/2019) I have to conclude that ES6 modules still have serious implications and issues. Changing the file extension is not enough since the extension-less imports fail with node. You'd need to go through every compiled file and change the import to specifically load *.mjs files.
However, the TypeScript Compiler can be set up in a way that it does understand ES6 module syntax and to compile to CommonJS (see TS handbook).
{
"compilerOptions": {
"module": "CommonJS",
// [...]
},
}
This way the TypeScript code be written with ES6 module syntax and the output can be CommonJS compatible without braking other code. As a bonus you can skip the Babel approach and grunt can run TS compiler.
You can using babel-node to compile first. Which will resolve ES6 export and import problem.
npm install --save-dev #babel/core #babel/node
npx babel-node server.js

Webpack with module and main transpilation

I've discovered that for some reason, webpack favors module instead of main on package.json.
We have a lot of libraries that export module in ES6 and main transpiled but we end up having stuff not being transpiled then. Taking a couple of famous React modules such as React Bootstrap or React Toolbox
I can see that I'm not going against convention here yet I'm surprised not many people run into this. React needs to run both on the browser and node if SSR is in place so I'm not sure how to proceed here.
Example library here:
https://github.com/firstandthird/domodule/blob/master/package.json#L6
Both including node_modules on babel loader and doing the switch indicated on the above solution seem to go against every other thing which, again, surprises me.
Only partial solution I've found is to not exclude node_modules on babel-loader but that seems like it might bite me back.
Here's the relevant portion of Webpack's config.
module.exports = config => ({
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
babelrc: false,
configFile: false,
presets: [
[
'#babel/preset-env',
{
targets: config.browserlist,
useBuiltIns: false,
modules: false,
exclude: ['transform-typeof-symbol']
}
]
],
cacheDirectory: true,
cacheCompression: config.minify,
compact: config.minify
}
}
});
Question is, what's the proper way to either configure Webpack or my libraries. I don't mind changing all the libraries as long as it's a known standard or something that we could be missing here.
Well, the standard right now is to provide pkg.main as the webpack's output fully transpiled and bundled and have pkg.module pointing in the transpiled but not bundled output.
Keep in mind that pkg.module should only have import/export from ES6 capabilities and not other stuff (like arrow functions). The unfortunate thing here is that webpack doesn't provide any way to output such a thing. It always puts it's __webpack_require__ stuff.
As you figured out, you can of course adjust your application's config to run babel on the library by including node_modules/yourlibrary if this is a private library that you only use. I don't think that a public library should enforce clients to transpile it every time, especially if there are specific rules or plugins that should apply.
The other solution that libraries use is to just take the source code and run babel only on top of it, without any webpack. This of course will work, but advanced configurations with webpack specifics (like alias,etc) will just fail.
Another solution is to use rollup as a bundler for libraries that seems to have these capabilities out of the box.
There is an open issue in webpack with more information if you are intrested.
Here is also a page from rollup that describes this concept.

Which tools do I need in order to use ES6 style imports for my project using Gulp?

I have a legacy project which uses Gulp to compile SASS and minify JS etc. (based on Drupal).
I'd like to use ES6 style imports and also be able to import modules that are located in node_modules without referencing the full path (for example import the lodash.debounce module as
import throttle from 'lodash.throttle'
What do I need to achieve that? (besides Gulp and NPM). Babel? Browserify? Lost with all the tooling.
I'd like to avoid webpack as it would increase the complexity of my project (and I don't know if I can use it in parallel with the way Drupal 7 is doing things)
Yep, you want babel. The documentation to set it up in your gulpfile is here: http://babeljs.io/docs/setup/#installation
You'll want to use the es2015 preset to enable the import syntax.
var gulp = require("gulp");
var babel = require("gulp-babel");
gulp.task("default", function () {
return gulp.src("src/app.js")
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest("dist"));
});
To use import you need a transpiler, for example babel.
Assuming you are building code for the browser then to use import (transpiled to require) you need webpack or browserify which will also allow you to import from node_modules (there are other module builders too, jspm and systemjs, however I know next-to-nothing about them).
For either of these you will also need something to enable babel to be used in the build process. I use webpack with babel-loader.
I found a useful reference here specifically on using ES6 modules with webpack.

Using webpack to bundle a custom yeoman generator

I'm relatively new to Node in general, including webpack and yeoman, so the solution might be simple.
I built a custom yeoman generator that generates html and wish to use the generator in a browser (so html can be generated client side in the browser). I figure webpack could help me solve this solution but I'm hitting some module dependency snags when trying to bundle the generator.
Specifically, I'm getting several errors in regards to missing fs modules and issues bundling json files. From my understanding, setting target: 'node' in the webpack.config.js file resolves the fs issue, but unfortunately since I''ll be using this bundle in the browser I have target' set to 'web'. As per the json errors, I'm trying to include json-loader like so:
module: {
loaders: [
{ test: /\.json$/, loader: "json-loader" }
]
},
but forwhatever reason, json-loader cannot be resolved when webpack is run.
Any ideas for fixing either of the dependency issues?

Categories

Resources