Laravel Mix: Configure Babel for IE11 compatibility (transformations and polyfills) - laravel

In a Laravel 6 application with Laravel-Mix 4, and using the Vue preset, I need to compile my JavaScript code to be compatible for IE11. This means adding any polyfills for missing functions, compiling out arrow functions, and so on. Out of the box, this is not done.
My test code in resources/js/app.js:
//require('./bootstrap');
let test = [1, 2, [3, 4]];
console.log(
test.flat().map((x) => 2*x)
);
With default config, laravel mix does not appear to compile JavaScript code, but only do some formatting. Comments are preserved in the compiled output.
The result of npm run dev is:
Asset Size Chunks Chunk Names
/css/app.css 0 bytes /js/app [emitted] /js/app
/js/app.js 4.95 KiB /js/app [emitted] /js/app
How do I get Laravel-Mix to use Babel to create IE11-compatible source code?

Enable Babel compilation with Laravel Mix, and use polyfills for internet explorer
Step 1: Install Corejs to get polyfills
Following the Babeljs docs for babel-preset-env 2, we first need to install core-js (which contains the polyfills):
$ npm install core-js#3 --save
Step 2: Configure .babelrc
create a .babelrc file in the project root:
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": {
"version": 3,
"proposals": false
},
"targets": {
"ie": "11"
}
}
]
]
}
Now run npm run dev and you will find polyfills inserted, arrow functions compiled out etc. - your code may just run on IE11!
Laravel-Mix, Babel, IE: Some gotchas
node_modules are not processed through babel
With the default configuration, only source code in the project itself - not its dependencies - runs through the babel compilation step. This means that any let or similar in the dependencies will trip up legacy browsers 3.
using `mix.babel' risks compiling a file twice
The laravel mix docs suggest using the mix.babel function in the Vanilla JS section 1. What this appears to do:
if no .babelrc is present, the specified file is run through babel.
if a .babelrc is present, the normal mix compilation step already uses babel. Using mix.babel causes the compilation step to be run twice.
Interestingly, the twice-compiled code does not run on IE. One problem is that it will contain require() calls for polyfills that cannot be handled:
SCRIPT5009: 'require' is undefined

This is how I managed to get our webpage to work on IE11.
I'm listing all of the packages related to Babel, though some of them are only needed to make Jest work.
package.json
"devDependencies": {
"#babel/core": "^7.10.5",
"#babel/plugin-transform-runtime": "^7.10.5",
"#babel/preset-env": "^7.10.4",
"#babel/runtime-corejs3": "^7.10.5",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.9.0",
},
.babelrc
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": "entry",
"bugfixes": true,
"targets": ">0.25%",
"corejs": {
"version": 3,
"proposals": false
}
}
]
],
"plugins": [
["#babel/plugin-transform-runtime", { "corejs": 3 }]
]
}
And finally
app.js
import './bootstrap';
import "core-js";
import Vue from 'vue';
// ...
I must say that I'm confused about the useBuiltIns property because different articles point toward different directions. It looks like if you use "useBuiltIns": "usage" you don't need to import core-js in app.js, anyway I have tried different combinations and this one is working fine.
According to the readme of core-js you need to import it, but I'm not 100% sure. Other resources that pointed me to the right directions were those two articles: https://www.valentinog.com/blog/preset-env/ and https://web.dev/serve-modern-code-to-modern-browsers/.
After this setup we only needed to update some CSS and the app was running fine. The only downside is that the vendor.js file is very heavy. I'd like to create a different bundle for browsers that support modules, but that's a different story...

Seems some use mix.babel(), but I believe that is better compatible with react. I had similar issue and I use babel-loader, #babel/preset-env and #babel/polyfill. Had to resort to polyfill cos I couldn't get core-js 3 to work following their docs. So if anyone is able to figure out how to make it work with core-js 3. I'd be glad to learn. And only install only what I seem to need for my project
Install:
npm install babel-loader #babel/preset-env #babel/polyfill --save
Webpack.mix.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
Finally, import at the begining of main/js or app/js
import '#babel/polyfill';
This has been tested on Laravel 7.x | vue 2.6
Dependencies:
"#babel/polyfill": "^7.10.4",
"#babel/preset-env": "^7.10.4",
"babel-loader": "^8.1.0",
Note: I decided to remove .babelrc from the root app completely, may seem like no effect but incase I need it, I prefer adding it to config.js

Related

How to migrate from laravel mix to pure Webpack?

Given the webpack.mix.js of a fresh Laravel project :
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');
What is the equivalent using just webpack and a webpack.config.js? (Im looking to remove laravel mix as a dependency on an existing project.)
I did find this default file in the source but it did not help me. Is there a way I can see the "compiled/resulting" webpack configuration or a template/starting point that corresponds to laravel mix default settings?
You can, but the result is not very satisfactory.
Create a JS script with this:
console.log (JSON.stringify(
require('./node_modules/laravel-mix/setup/webpack.config.js'), null, 4)
);
and save it in the root folder of your laravel project. Run it with Node and the output will be the configuration object Laravel Mix receives and inputs to webpack.
However, this file is very long and covers a vast amount of settings, which you wouldn't need if you made your file from scratch. Yes, you could try and remove every extra setting you think you can remove without breaking your output, but in the end it's better to learn how Webpack works so you can write better, mode adjusted configs. At least you can use it to understand how it does certain things.
Just put into webpack.mix.js
Mix.listen('configReady', function (config) {
RegExp.prototype.toJSON = RegExp.prototype.toString;
console.log(JSON.stringify(config));
});
So you will get webpack config from your laravel.mix.
With recent laravel-mix you just need to invoke mix.dump() (in the webpack.mix.js script).
The file you referenced seems to point exactly to the default configuration. Why did this not help?
In order to migrate you could
Learn the basics
Extract the dependencies from Laravel mix aÇıd add them to your package.json
Hint: The dependencies there are your devDependencies
Start by installing npm install --save-dev everything "webpack", "babel" and prefixed with "-loader".
If you need Sass and extracted css - npm install --save-dev node-sass sass-loader mini-css-extract-plugin.
Minimal example of a webpack config for your mix example from above would be
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './resources/js/app.js',
output: {
filename: 'js/[name].js',
path: path.join(__dirname, 'public')
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
],
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
'sass-loader'
]
}
]
}
};
Learn the more advanced basics for your use case

how to compile js to es5 with laravel mix?

I have laravel Vue app and it works perfectly with chrome and firefox. but it doesn't work on Edge or IE11 and the console shows error on arrow function!?
How to compile or transpile to es5 with laravel mix and webpack?
could you show the correct configuration for webpack.mix.js?
tnx alot
UPDATE February 2020
If anyone still need help with this, mix already provide a babel compilation to es5:
A slight variation of mix.scripts() is mix.babel(). Its method
signature is identical to scripts; however, the concatenated file will
receive Babel compilation, which translates any ES2015 code to vanilla
JavaScript that all browsers will understand.
You can use it like this:
mix.babel(['public/js/es6file.js'], 'public/js/app.es5.js')
DOCS
In order to compile your es6 code to es5 follow the following steps:
1) install the babel-env preset
npm install #babel/preset-env --save
And then declare it in your .babelrc in the root directory:
{
"presets": ["#babel/preset-env"]
}
2) compile your code using
npm run dev //for dev environment
or
npm run prod // for production environment
after a lot of search, I've found out that this Vuecomponent causes the error "https://github.com/godbasin/vue-select2" how can I compile it to es5.
the edge console error:
Expected identifier, string or number
and the corresponding line that it shows is this:
setOption(val = []) {
this.select2.empty();
this.select2.select2({
-----> ...this.settings,
data: val
});
this.setValue(this.value);
},
sorry for taking your time

Setup Laravel Mix with SASS and PostCSS critical CSS splitting

I want to setup Laravel Mix with mix.sass AND PostCss Critical CSS splitting. In the end I want two files: app.css and app-critical.css.
Unfortunately I can get this to work. One of the setups (webpack.mix.js) I did try:
mix
.js('templates/src/js/app.js', 'web/assets/dist/')
.js('templates/src/js/home.js', 'web/assets/dist/')
.extract(['vue','axios','lazysizes','svgxuse', 'fontfaceobserver'], 'web/assets/dist/vendor.js')
.sass('templates/src/scss/app.scss', 'web/assets/dist/')
.sourceMaps()
.options({
postCss: [
require('postcss-critical-css')({
preserve: false,
minify: false
})
]
})
.browserSync({
proxy: '127.0.0.1:8080',
files: [
'templates/**/*.twig',
'templates/src/js/**/*',
'templates/src/scss/**/*'
]
});
if (mix.inProduction()) {
console.log("In production");
mix.version();
}
When I run the script via 'npm run watch' I get an error:
10% building modules 0/1 modules 1 active ...ign-tools/templates/src/scss/app.scssWithout `from` option PostCSS could generate wrong source map and will not find Browserslist config. Set it to CSS file path or to `undefined` to prevent this warning.
Also, my file is just copying over all the critical styling. The file grows bigger and bigger, expanding the duplicate code everytime the input SCSS/CSS-file changes.
I did try to set up Laravel Mix + mix.sass + one of the following plugins:
https://github.com/zgreen/postcss-critical-css
https://www.npmjs.com/package/postcss-critical-split
Without success :(
Anybody with a working setup or link to an example repository?
Thanks,
Teun

How to #import external SCSS properly with webpack and Vue.js?

As in Material Component Web's example, I want to be able to import SCSS from my node_modules like this:
#import '#material/elevation/mdc-elevation';
However, I'm getting this error message when trying to run the webpack build:
File to import not found or unreadable: #material/elevation/mdc-elevation.
#import './~/#material/elevation/mdc-elevation.scss'; doesn't work either.
I'm pretty sure the issue is somewhere in my webpack config, but I can't figure out where.
What did they do in Material Components Web's Vue.js example in order to make it work?
Here's my npm-debug.log in case you need it.
And here's the corresponding Git repository: sk22/spg-tinf-sem03/proj01
Thanks in advance!
Edit: I want to be able to import the scss files, not the compiled css.
Got it.
here's a part of my webpack 2 config's module.rules:
{
test: /\.(sass|scss)$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'node_modules')],
},
},
],
},
So what did I do wrong?
My options object was placed in the rule directly, not the loader.
The old webpack config rule looked like this:
{
test: /\.(sass|scss)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
options: { includePaths: [path.resolve(__dirname, './node_modules')] },
},
See the difference? Instead of the 'sass-loader' string, I extended it to an object, containing the loader name and the options object, because the options only apply to the sass-loader.
(You could also drop the path.resolve and only write 'node_modules', but it might be safer to leave it.)
Check out this documentation page for further information. https://webpack.js.org/configuration/module/#rule-use
Without that loader, you must prefix each import with a ~, which webpack converts to the node_modules folder, at least with my previous configuration.
But this will break 3rd party SCSS frameworks like Material Components Web, because they use #import statements without a leading ~ themselves, for example here.
Inside .vue files
This will not work in .vue files, as vue-loader just uses sass-loader without any options by default.
So if you want that to work, you probably need to make use of vue-loader's own options, as described in its documentation.
(I'm unable to get it to work for some reason I don't know...)
EDIT: Webpack has a section on sass-loader now: https://webpack.js.org/loaders/sass-loader/ also mentioning includepaths.
I had the same issue with #material and Vue. I managed to resolve the problem without adjusting the use property directly.
Solution
Step 1: First create a default Vue 2.1 project using the CLI.
Your file structure will have a ./build directory.
Step 2: Open the file 'utils' you will see a cssLoaders() function which returns an object/map for the languages vue-loader supports.
You will see both sass and scss in that map.
Step 3: Change the values of sass and scss to:
sass: generateLoaders('sass', {
indentedSyntax: true,
includePaths: [path.resolve(__dirname, '../node_modules')]
}),
scss: generateLoaders('sass', {
includePaths: [path.resolve(__dirname, '../node_modules')]
}),
Step 4: Go to the .vue file you're using and change the lang attribute in your <style> element to either sass or scss.
Step 5: After you've done that go to the terminal/console and install sass-loader with:
npm install sass-loader node-sass webpack --save-dev
Step 6: Then run npm run dev and it should work.
Why does this work?
Libraries
I dug around a bit and it turns out sass-loader uses node-sass which has some options such asincludePaths one mentioned by #22samuelk. IncludePaths tells node-sass or rather the underlying library LibSass to include sass files from that directory/path.
Vue
Sass-loader options
By default Vue expects your assets to be in your projects src/assets folder (correct me if I'm wrong). You can however use ~ to indicat you want to start at your projects root which would look like `~/node_modules/#material/smth/mdc-smth.scss.
Now if you want your sass-loader to use something other than those options you need to explicitly tell them.
Hence path.resolve(__dirname, '../node_modules' since the utils file is in ./build and you need to use an absolute path for sass-loader to understand where to look.
Vue-loader config
This is not really specific to the question but the vue-loader config defined in vue-loader.conf.js works as follows:
It uses the map returned by cssLoaders() to build the loaders expected by webpack.
The returned map ({key:value}) is then used by providing key as a file extension used in test: for a loader object. The value is used as the loader object.
Which would like like this:
{
test: /\.(key)$/,
use: [
{
loader: '//ld//-loader',
options: {
/*Options passed to generateLoaders('//ld//', options)*/
},
},
],
}
Where key is the file extention. In this case that would be either sass or scss. And //ld//is the loader you which to use. Which is shown in Step 3 as 'sass'.
Hopefully this clears up some stuff. Took me a while because I just started using Vue.

Vue PulseLoader given unexpected token with Laravel

I installed the vue-spinner with npm install vue-spinner --save-dev,
and I am trying to use the spinner on my file, but when I use the import
import PulseLoader from 'vue-spinner/src/PulseLoader.vue'
I get this error:
node_modules/vue-spinner/src/PulseLoader.vue:1
<template>
^
My gulpfile is requiring vueify, here is it:
var elixir = require('laravel-elixir');
require('laravel-elixir-vueify');
elixir(function(mix) {
mix.sass('app.scss','public/assets/css/')
.browserify('Main.js');
});
You actually don't need to require laravel-elixir-vueify in your guilpfile, it's easier just to install the original vueify (version 9 for Vue 2.0):
npm install vueify --save-dev
Then add the following to your package.json:
"browserify": {
"transform": [
"vueify"
]
}
Now elixir will use the vueify transform automatically when browserify is called.
EDIT
I just took a look at the vue-spinner page and it says that when using browserify you need to do the following:
import { PulseLoader } from 'vue-spinner/dist/vue-spinner.min.js'
So that may fix your problem without the vueify step, however, I still recommend avoiding the elixir add ons for the time being because their versioning is all over the place at the moment with Elixir 6 about to be released and Vue 2 being newly implemented in laravel.

Resources