Defining multiple babel preset configurations in webpack config - javascript

I have created a webpack.config.js file that is exporting two different WebPack configuration objects. I need to set up different babel options for presets within these . After a bit of research I have tried creating two different loader configs, each passing a different targets option to the presets like so:
// default JS loader config for browsers that support <script type='module'
{
loader:'babel-loader',
options:{
presets: ['#babel/preset-env', {
targets: {
esmodules: true
}
}]
}
}
...
// fallback for browsers that load the <script nomodule
{
loader:'babel-loader',
options:{
presets: ['#babel/preset-env', {
targets: "> 0.5% in UK, last 2 versions, not dead, ie 11"
}]
}
}
However I am clearly going about this wrong because I get this error on WebPack build
ERROR in ./some-path/WorkflowStage.class.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
ReferenceError: [BABEL] e:\some-path\WorkflowStage.class.js: Unknown option: .targets. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.
I think the crux of the question is: how should I be passing the target option to #babel/preset-env from within my webpack.config.js file when I have multiple presets?

Basically your loader options must look like a JS-encoded .babelrc. Each preset with options must be in it's own array.
So, replace
{
loader: 'babel-loader',
options: {
presets: [
// defines the #babel/preset-env as the first preset
'#babel/preset-env',
// defines an invalid object as a preset (throws error)
{ targets: { esmodules: true } }
]
}
}
with
{
loader: 'babel-loader',
options: {
presets: [
// defines a preset with options
[
'#babel/preset-env', {
targets: {
esmodules: true
}
}
]
]
}
}

Related

Babel won't transpile Vue dependencies

I have a Vue.js Single File Component which depends on primevue/AutoComplete.vue.
When transpiling the code for IE11 (I know, I know) the files produced from AutoComplete.vue are ignored and therefor not transpiled resulting in errors in Internet Explorer.
I have adjusted my babel-loader config as described here and the relevant part of my webpack config now looks like this:
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
exclude: file => (/node_modules/.test(file) && !/\.vue\.js/.test(file))
},
presets: [
["#babel/preset-env",
{
"useBuiltIns": "usage",
"debug": true,
"modules": false,
"corejs": { version: "3.6", proposals: true }
}
]
]
}
}
]
},
Looking at the suggested exclude function, it relies on files having the suffix e.g. AutoComplete.vue.js but when I log the "file" parameter, I don't see any file with this suffix.
How is it supposed to filter the nested Vue dependencies if the proposed file ending isn't provided by vue-loader ? Am I missing these dependencies because of a config error ?

Webpack. Different rules for different entry points with similar test-mask

We have a project with old-good-smarty-part and react-part.
We bundle react-part files with webpack.
We want to bundle non-react-part files with webpack too, but this files are a bit upper than webpack in project-tree. And here is a problem: we need to bundle them and place result to grand-grand-parent directory... So, how to split rules for this entry-point?
const ExtractJS = new ExtractTextPlugin({
filename: "../../[name].min.js"
});
module.exports = {
entry: {
reactApp: ["./src/entries/reactApp/index.tsx"],
// JS -- we need to place it "upper" than webpack directory in project
"scripts/oldStuff": ["./src/entries/oldStuff/index.js"]
},
module: {
rules: [
{
test: /\.js?$/,
loader: "babel-loader",
exclude: /node_modules/,
options: {
cacheDirectory: true,
presets: [["es2015", { modules: false }], "stage-2", "react"],
plugins: ["transform-node-env-inline"],
env: {
development: {
plugins: ["react-hot-loader/babel"]
},
targets: {
browsers: ["last 2 versions", "ie >= 10"]
}
}
}
},
{
test: /\.js?$/,
include: [/(.*?)scripts(.*?)/, /(.*?)lib(.*?)/],
use: ExtractJS.extract({
use: {
loader: "raw-loader"
}
})
}
}
}
}
was tryed to solve this with include and exclude options, but this looks not good -- we got "cross-entry" files (something like /lib/foo/bar.js for react part and /lib/foo/boar.js for non-react part) and its about 15-20 files...
And here is a main question: is it possible to exclude entry-point for one rule and include it for another one (with exception of other entry-points)?

How to exclude core-js using useBuiltIns: "usage"

Using babel 7.5.5, core-js 3.1.4 and webpack 4.38.0, how can I exclude core-js from transpiling?
I do not want to exclude node_modules altogether since I have libs that need transpiling
If I use exclude: /node_modules\/(core-js)/, a core-js module throws
TypeError: $ is not a function
This leaves me with two other options.
Use includes instead, include my src directory and every dependency that needs transpiling one by one
Use useBuiltIns: entry instead of usage, since in this case exclude: /node_modules/\(core-js)/ works, and import core.js at the top of main.js
Both of these options don't really seem like good solutions to me since usage is "no longer experimental" since 7.4.
Is there any way to make it work using usage? Is it a bug in either babel-loader or babel? Or is my configuration at fault?
This is my Webpack config:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
main: ['./src/main'],
},
output: {
path: path.resolve(__dirname, './build/'),
publicPath: '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(core-js)/,
use: {
loader: 'babel-loader'
},
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: 'jQuery'
},
{
loader: 'expose-loader',
options: '$'
}
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
};
This is my babel config:
module.exports = function (api) {
api.cache(true);
return {
presets: [
[
'#babel/preset-env',
{
corejs: {
version: 3,
},
useBuiltIns: 'usage',
}
]
],
};
};
You can reproduce the error with the following repository: https://github.com/tomm1996/usebuiltins-exclude-test
You need to exclude both core-js and webpack/buildin from the Babel transpilation.
You can use the folling exclude Regexes:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
]
Here is also a complete babel-loader configuration with some useful comments:
{
module : {
rules : [{
test : /\.js$/,
// Some module should not be transpiled by Babel
// See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
],
loader : "babel-loader",
options : {
babelrc : false,
// Fixes "TypeError: __webpack_require__(...) is not a function"
// https://github.com/webpack/webpack/issues/9379#issuecomment-509628205
// https://babeljs.io/docs/en/options#sourcetype
sourceType : "unambiguous",
presets : [
["#babel/preset-env", {
// Webpack supports ES Modules out of the box and therefore doesn’t require
// import/export to be transpiled resulting in smaller builds, and better tree
// shaking. See https://webpack.js.org/guides/tree-shaking/#conclusion
modules : false,
// Adds specific imports for polyfills when they are used in each file.
// Take advantage of the fact that a bundler will load the polyfill only once.
useBuiltIns : "usage",
corejs : {
version : "3",
proposals : true
}
}]
]
}
}
}
}
See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
Edit: Also if you try to use #babel/plugin-transform-runtime:
plugins : [
// Require the Babel runtime as a separate module to avoid the duplication
// https://webpack.js.org/loaders/babel-loader/#babel-is-injecting-helpers-into-each-file-and-bloating-my-code
["#babel/plugin-transform-runtime", {
// Requires #babel/runtime-corejs3
// https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
corejs : { version: 3, proposals: true }
}],
}
You may run into a similar error:
Uncaught TypeError: _typeof2 is not a function
at _typeof (typeof.js:8)
at eval (sockjs.js:123)
at Object.eval (sockjs.js:131)
at eval (sockjs.js:6565)
at Object../node_modules/sockjs-client/dist/sockjs.js (main.js:13790)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/clients/SockJSClient.js?:110:14)
at Object../node_modules/webpack-dev-server/client/clients/SockJSClient.js (main.js:13874)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/socket.js?:56:41)
This can be solved by excluding #babel/runtime-corejs3 from the transpilation:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/,
/#babel\/runtime-corejs3/
]

Webpack sass loader does not recognize global variables file

i have this sass directory:
- _vars.scss
- main.scss
//vars.scss
$base-container: 1400px;
//main.scss
#import './vars';
In other js file i have:
require('./some-module-sass-file');
//some-module-sass-file.scss
.container {
width: $base-container;
}
The problem is i have global variables in the vars file and the some-module-sass-file not recognize them and throw an error:
undefined variable $base-container
Without using sass-resources-loader:
Thanks to #Arseniy-II for helping me get to this answer, in conjunction with this thread:
https://github.com/webpack-contrib/sass-loader/issues/218
Using loader options in your webpack module rules, you can assign a data property to sass-loader, you should then be able to use all sass functions as expected:
module: {
rules: [
// Apply loader
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
data: '#import "path/to/global.scss";',
includePaths:[__dirname, 'src']
},
},
],
},
],
}
You have to import the vars file into every Sass partial that uses those variables, because every partial is compiled on its own; none of the files will 'know about' the others unless you specifically import them.
If you don't want to have to type the imports in every Sass file, you can look at baggage-loader, which will automatically add them for you.
Note that you need to use " and ; at the end
{
loader: 'sass-loader',
options: {
additionalData:'#import "path/to/general.sass";',
},
},
If you have Webpack 5 you have to use additionalData, the other options are not valid now:
{
loader: 'sass-loader',
options: {
additionalData: '#import path/to/general.sass',
}
},
If you do like this your general sass or scss file will get prepended
Webpack 4 solution:
{
loader: 'sass-loader', options: {
sourceMap: true,
prependData: '#import "pathto/vars";'
},
}
In order #import "pathto/vars"; to work you need Webpack to configure to resolve such imports. So it simply prepends the line.
This solution is good because you have no issues with sourcemaps like with using sass-resources-loader
You can use text-transform-loader package.
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
{
loader: 'text-transform-loader',
options: {
prependText: `#import "${path.resolve(__dirname, './base.scss')}";`,
}
}
]
}

Make webpack's library output compatible with babel6

Babel's 6th version changes the functioning of export default and in particular its relation with commonjs require.
To summarise, while until babel5, require('module') where giving the default export of the module, it now always returns the module object containing all of the exports of the module.
If one only wants the default, he/she must use require('module').default.
As explained here, there is very good reasons behind this and the aim of this question is not to break or hack this behaviour.
However, if one is building a library, he/she usually does not want to distribute a module but the export value of his library (e.g. a function, whatever module system is used internally).
This is well dealt with by webpack and the output.library configuration when using commonjs or AMD. Because prior babel's versions allowed the default export to be required with commonjs, babel was also compatible with this mechanism. However it is not the case anymore: the library now always provides an es6 module object.
Here is an example.
src/main.js
export default "my lib content";
webpack.config.js
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.join(__dirname, "dist"),
filename: "mylib-build.js",
library: 'myLib'
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.join(__dirname, "src"),
query: { presets: ['es2015'] }
}
]
}
};
test.html
<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>
This is a very simple example but I obviously want the export of mylib to be the string "my lib content" instead of { default: "my lib content" }.
One solution could be to create an export source file in commonjs to perform the transformation:
module.exports = require('./main').default;
However I find this solution quite poor. One should be able to solve it at the compilation level, without changing the source code.
Any idea?
Was just going at this my self. Whether one like to call it a workaround or solution, there seem to be a Babel plugin that "solve it".
Using the plugin babel-plugin-add-module-exports as referenced in https://stackoverflow.com/a/34778391/1592572
Example config
var webpackOptions = {
entry: {
Lib1: './src/Lib1.js',
Lib2: './src/Lib2.js'
},
output: {
filename: "Master.[name].js",
library: ["Master","[name]"],
libraryTarget: "var"
},
module: {
loaders: [
{
loader: 'babel',
query: {
presets: ['es2015'],
plugins: ["add-module-exports"]
}
}
]
}
};
This yields Master.Lib1 to be lib1 instead of Master.Lib1.default.
Webpack 2 now supports es6 modules which partially solves this issue. Migrating from webpack 1 to webpack 2 is relatively painless. One just needs to remember to disable babel's es6 module to commonjs conversion to make this work:
.babelrc
{
"presets": [
["es2015", {"modules": false}]
]
}
However, unfortunately, it does not work properly with export default (but an issue is opened, hopefully a solution will be released eventually).
EDIT
Good news! Webpack 3 supports the output.libraryExport option that can be used to directly expose the default export:
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "mylib-build.js",
library: "myLib",
// Expose the default export.
libraryExport: "default"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.resolve(__dirname, "src")
}
]
}
};
You can use this solution (this is more like workaround, but it allow you to keep your sources from change):
There is a loader called callback-loader. It allow you to change your sources in a build time by calling a callback and put a result instead of it. In other words you can turn all you require('module') into a require('module').default automatically in a build time.
Here is your config for it:
var webpackConfig = {
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
...
]
},
...
callbackLoader: {
require: function() {
return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
}
}
};

Categories

Resources