Babel used with Grunt working differently than when used with Webpack - javascript

I have a project using Babel for Webpack, transpiling ES6 code into plain Js. This works perfectly well, including the imported classes etc, see below.
Result:
var _Person = __webpack_require__(2);
var vic = new _Person.person("Fred McIntire", "Web Developer");
vic.EchoProperties();
Webpack.config.js
module: {
loaders: [{
test: /\.js$/,
exclude: "/node_modules/",
loader: ['babel'],
query: {
presets: ['es2015']
}
}]
},
However while using Babel with Grunt in another project, hence "grunt-babel" plugin, I noticed it transpiles the imports into CommonJs by default and does not include the imported classes.
Result
var _Person=require("./modules/Person"),
vic=new _Person.person("Fred McIntire","Web Developer");
vic.EchoProperties();
gruntfile.js
babel: {
options: {
sourceMap: true,
presets: ['es2015'],
plugins: ['transform-es2015-modules-amd']
},
dist: {
files: {
'assets/js/transpiled/app.es6.js': 'assets/js/custom/app.js'
}
}
},
(I included "plugins: ['transform-es2015-modules-amd']" just for testing purposes).
I want the grunt-babel to transpile down to: (and include the imports)
var _Person = __webpack_require__(2);
not
var _Person=require("./modules/Person")
Therefore how do i update the gruntfile.js settings for grunt-babel in order to act in the same manner as it does used with Webpack?
Thanks in advance...

Related

Babel does not transpile imported modules from 'node_modules'

I got a problem with transpiling imported modules from node_modules. Babel for some reason doesn't transpile imported module from node_modules, but transpile modules imported from src.
Here is an example repo: https://github.com/NikitaKA/babeltest
main.js
// result code contains const and let, but it shouldn't. :(
index.js
import qs from 'query-string; // not transpiled
import lib from './lib' // transpiled
const query = qs.parse(window.location.search);
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
}
]
}
};
.babelrc
{
"presets": [
["#babel/preset-env", {
"modules": false,
"targets": {
"chrome": 39
}
}],
["#babel/preset-stage-1", {
"modules": false,
"decoratorsLegacy": true,
"pipelineProposal": "minimal"
}]
],
"plugins": [
"transform-es2015-constants",
"#babel/plugin-transform-block-scoping",
"#babel/plugin-transform-runtime"
]
}
The solution in this case is to transpile the module again, this can be done by modifying the exclude property in your webpack config:
{
test: /\.js$/,
exclude: /node_modules\/(?!(es6-module|another-es6-module)\/).*/,
},
Modules es6-module and another-es6-module will no longer be ignored by webpack and will be transpiled with the rest of your source code.
See the GitHub issue on the webpack project.
Tested with webpack#3.12.0, babel-core#6.26.3 and babel-core#6.26.3
To expand upon my comments:
You really don't want to transpile all of node_modules – it'll take a long time, and most of the code there should already be ES5 (unless they're actually ES6 modules, in which case the ES6 entry point is announced as "module" in the package.json manifest).
query-string#6.x isn't, and it says so in its README:
This module targets Node.js 6 or later and the latest version of Chrome, Firefox, and Safari. If you want support for older browsers, use version 5: npm install query-string#5.
If you're using Vue, there is a simple solution. Just list your module in transpileDependencies:
vue.config.js
module.exports = {
publicPath: '',
outputDir: 'www',
transpileDependencies: [
'query-string'
],
}

Webpack bundled JS not being executed

This is a very strange problem because actually, some of the bundled code is being executed. I use style loader for my CSS and that of course gets put into bundle.js and loads and works fine. However, I also have a file with some code to set up the jQuery localScroll plugin, and that code isn't working.
To test it, I included in the same file a call to console.log(), just telling it to write the number 4. If I open up bundle.js, I can see the console.log() call as well as the call to $.localScroll(), they just simply aren't running. Calling $.localScroll() manually from the console works as intended.
Here is the JS file in question:
console.log(4);
$(() => {
$.localScroll({duration: 800});
});
Here is my Webpack config:
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './webpack-entry.js',
plugins: [
new CleanWebpackPlugin(['dist']),
new webpack.optimize.UglifyJsPlugin()
],
output: {
filename: './javascripts/bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg)$/,
use: {
loader: 'url-loader',
options: {
limit: 8192,
fallback: 'file-loader',
name: './images/[hash].[ext]'
}
}
},
{
test: /\.pug$/,
use: [
{
loader: 'file-loader',
options: {
name(file) {
if(new RegExp(/partials/).test(file)) {
return './views/partials/[name].[ext]'
}
return './views/[name].[ext]'
}
}
},
'pug-asset-loader?root=./src'
],
},
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['env'],
}
},
]
}
],
}
};
Finally, here is bundle.js (my custom code seems to be at the very bottom, in some sort of array of functions). The non-uglified version is too long for SO, so here it is on Hastebin: https://hastebin.com/vululimupi.js
The problem is that you haven't defined this dependency as a module. Rewriting it following supported module format specs should help.
After some testing, it seems that the essential problem is just as #uKolka was saying - my files were not getting required as modules. While I'm still not entirely certain why their code was still appearing in my bundle file but not running, I have found a way to still reap the recursive benefits of require.context(). It seems that require.context() returns a function which is itself capable of resolving the files it logs in whatever folder you have pointed it at. It also has a member function keys() which quite conveniently returns each dependent file name and is easily used with forEach().
Given all that, this is how my webpack-entry.js looks now:
import './src/stylesheets/scss/master.scss';
require.context('./src/views', true, /\.pug$/);
const js = require.context('./src/javascripts/', false, /\.js$/);
js.keys().forEach(key => js(key));
This works just fine.
In my case it was due to Babel and React. If you use React, then try to call ./src/index.js directly and use "#babel/preset-env", "#babel/preset-react" in your .babelrc. You can take a look at my gist. and in your webpack.config.js:
{
test: /\.js?$/,
use: ["babel-loader"],
exclude: /node_modules/
},
take a look at the gist:
https://gist.github.com/Nagibaba/14e898d99a4be89b00a60d28abc19bc0
For ruby's rails/webpacker users only.
This question title and content made me find it before this issue which may be the correct answers for some users here. To avoid link rot, I'll explain it shortly below:
Make sure your #rails/webpacker npm library and webpacker gem both have the same exact version.
I ended up doing:
IO.foreach("package.json").find { |line| line[%r("#rails/webpacker": "(.*?)")]}
gem "webpacker", Regexp.last_match(1).tr("-", ".")

How do Webpack '!' imports work?

I have a React component that I'm installing and importing as a node_module in my project. This component requires an SVG and is transpiled to ES5 via babel before I require it.
The relevant transpiled code looks like this.
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _hudInline = require('./icons/hud.inline.svg');
var _hudInline2 = _interopRequireDefault(_hudInline);
_react2.default.createElement(_hudInline2.default, {
className: 'FooterNav-hud-icon',
style: {
width: '16px',
fill: 'white'
}
})
My main project that imports this code has a webpack config which has a babel loader rule for js files and a svg loader rule for svg files.
If I import the component using this syntax it works:
import MyComponent from '!babel-loader!my_node_modules_folder/MyComponent';
I do not understand why this works. I was under the assumption that using "!" bypasses the webpack config and uses the loaders I define. However, babel-loader shouldn't know how to process the SVG.
If I remove my svg loader in the webpack config, the above !babel-loader import does NOT work either. Does "!" only use the listed loader for the initial file, but as it traverses the dependency tree, subsequent requires use the webpack config?
If I don't use the above import style, but change my webpack config to NOT exclude /node_modules/ the code does not work. Webpack complains that it doesn't know how to deal with the SVG (unexpected character), making me believe that it is not hitting the correct svg-loader.
Am I misunderstanding how the webpack loaders / requires work? Could it be because the originally transpiled ES6 -> ES5 messes up the require?
Here are my js and svg loader options
const babelLoader = {
test: /\.jsx?$/,
use: [{
loader: require.resolve('babel-loader'),
query: {
babelrc: false,
presets: [['es2015', { modules: false }], 'react', 'stage-0'],
cacheDirectory: true
}
}]
};
const inlineSvgLoader = {
test: /\.inline.svg$/,
use: [{
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}, {
loader: 'react-svg-loader',
query: {
jsx: true,
...
};
}]
EDIT:
My problem was that despite babel-loader including the .js files from my package in node_modules, the plugin webpack-node-externals needed to also whitelist the module on the server side webpack build so that it got included.
Does "!" only use the listed loader for the initial file, but as it traverses the dependency tree, subsequent requires use the webpack config?
Yes. Inline loaders only apply to the file that contains the module you are importing. Otherwise it wouldn't be possible to have that module require anything that needs a different loader.

Multiple package.jsons with ES6 Babel

I have a large project that I would like to divide up into multiple package.json's so that the dependencies for each part can be clearly stated and so those packages can be exported as individual parts.
However, I want my app to include each of these packages and compile them using webpack and babel. There are shared dependencies for the packages, so I don't want to just output each one to a /dist folder.
My ideal directory structure looks like this:
\main
\app
\node_modules
package.json
\package1
package.json
node_modules
index.js
\package2
package.json
node_modules
index.js
I tried multiple approaches:
Using webpack's resolve modules with something like path.resolve('app'). This just doesn't work, even though it should in theory.
Using main's package.json to reference others using "package1" : "file:../package1". This doesn't treat package1 as es6 javascript and throws errors. Using resolveLoaders in the webpack configuration does not help.
The webpack config I have is as follows.
module: {
loaders: [
{
test: /\.js?/,
loader: 'babel-loader',
include: [
path.resolve('app'),
path.resolve('../prose'),
],
query: {
plugins: [
['react-transform', {
transforms: [{
transform: 'react-transform-hmr',
// If you use React Native, pass 'react-native' instead:
imports: ['react'],
// This is important for Webpack HMR:
locals: ['module']
}]
}],
['transform-object-assign']
]
}
},
{ test: /\.css$/, loader: 'style-loader!css-loader!sass-loader' },
{ test: /\.svg$/, loader: 'file-loader' },
{ test: /\.png$/, loader: 'file-loader' },
{ test: /\.jpg$/, loader: 'file-loader' },
{ test: /\.json$/, loader: 'json-loader' }
]
},
resolve: {
modules: [
path.resolve('app'),
'node_modules',
],
extensions: ['.json', '.js', '.jsx'],
}
Any thoughts or examples of other projects that do this would be appreciated!
You should check out lerna. It enables you to use multiple package.jsons and even packages in one repo. It might help you with you requirements.

Using Webpack and Babel to convert ES6 to AMD

I'm using webpack in my app, and have babel converting my js/jsx files from es6 to es5.
I'd like to have babel convert the module loading in these files to AMD. I see how to do this with grunt-babel:
Using Babel to convert ES6 modules to ES5 AMD modules, not working as expected
How would I do this if I want webpack to handle the babel conversion?
For example, in webpack.config.js I have:
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
}
}
Can I set an option in there for Babel to use AMD?
You can set an options for babel with query key:
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
modules: 'amd'
}
}
}
For all available options take a look here: http://babeljs.io/docs/usage/options/
If you want to generate the whole bundle as AMD module, you can set it in the "output.libraryTarget" config:
{
output: {
libraryTarget: "amd"
}
}
See here, in "output.libraryTarget":
https://webpack.github.io/docs/configuration.html

Categories