I am new to Vue but I thought I'd give it a go in a recent project, I can see why its liked. Anyway, everything was going great until I switched over to IE, where nothing worked at all.
With errors such as Object doesn't support property or method 'assign' I gave it a Google and apparently IE doesn't support ES6 very well, according to this question: Getting Error: Object doesn't support property or method 'assign'
So, I'd heard of Babel and it looked like the sort of thing that would do the job as it is capable of converting from ES6. Following this I attempted to integrate Babel into my Laravel project.
I updated my webpack.min.js as follows:
let 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/assets/js/app.js', 'public/js')
.webpackConfig({
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}]
}
})
.sass('resources/assets/sass/app.scss', 'public/css');
if (!mix.inProduction()) {
mix.webpackConfig({
devtool: 'source-map'
})
.sourceMaps();
} else if (mix.inProduction()) {
mix.version();
}
mix.browserSync({
proxy: '127.0.0.1:8000'
});
The env package refers to: https://babeljs.io/docs/en/babel-preset-env
However, this did not seem to solve my problem.
Should I just use mix.babel instead?
You need a polyfill: https://babeljs.io/docs/en/babel-polyfill
Import that as the first line in your entry (app.js).
However, I'd recommend using vue cli instead of laravel mix entirely.
Related
I am learning about tree-shaking with a webpack 4/React application that uses Lodash.
At first, my Lodash usage looked like this:
import * as _ from "lodash";
_.random(...
I soon learned, via the BundleAnalyzerPlugin, that the entirety of Lodash was being included in both dev and prod builds (527MB).
After googling around I realized that I needed to use a specific syntax:
import random from "lodash/random";
random(...
Now, only random and it's dependencies are correctly included in the bundle, but I'm still a little confused.
If I need to explicitly specify functions in my import statement, then what role is the tree-shaking actually playing?
The BundleAnalyzerPlugin isn't showing a difference in payload size when comparing between dev and production mode builds (it's the correct small size in both, but I thought that tree-shaking only took place with production builds?).
I was under the impression that TreeShaking would perform some sort of static code analysis to determine which parts of the code were actually being used (perhaps based on function?) and clip off the unused bits.
Why can't we always just use * in our import and rely on TreeShaking to figure out what to actually include in the bundle?
In case it helps, here is my webpack.config.js:
const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
entry: {
app: ["babel-polyfill", "./src/index.js"]
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "static",
openAnalyzer: false
})
],
devtool: "source-map",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].bundle.js",
publicPath: ""
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
include: /src/,
options: {
babelrc: false,
presets: [
[
"env",
{
targets: {
browsers: ["last 2 Chrome versions"]
}
}
],
"#babel/preset-env",
"#babel/preset-react"
],
plugins: ["syntax-dynamic-import"]
}
},
{
test: /\.(ts|tsx)$/,
use: [
{
loader: require.resolve("ts-loader"),
options: {
compiler: require.resolve("typescript")
}
}
]
}
]
},
resolve: {
symlinks: false,
extensions: [".js", ".ts", ".tsx"],
alias: {
react: path.resolve("./node_modules/react")
}
}
};
I'm invoking webpack with webpack --mode=development and webpack --mode=production.
All two existing answers are wrong, webpack do treeshake import *, however that only happens when you're using a esmodule, while lodash is not. The correct solution is to use lodash-es
Edit: this answer only applies to webpack4, while webpack 5 supported a limited subset of tree shaking for commonjs, but I haven't tested it myself
Actually, it is not related to the Webpack ability to tree-shake. base on Webpack docs about tree-shaking
The new webpack 4 release expands on this capability with a way to provide hints to the compiler via the "sideEffects" package.json property to denote which files in your project are "pure" and therefore safe to prune if unused
When you set the "sideEffects: false on your package.json based on the linked docs:
All the code noted above does not contain side effects, so we can simply mark the property as false to inform webpack that it can safely prune unused exports.
If you have some files or packages which you know they are pure add them to sideEffects to prune it if unused. There are some other solutions to do tree-shaking that I proffer to read the whole article on Webpack docs.
One of the manual ways are using direct importing like below:
import get from 'lodash/get';
That Webpack understands add just get from the whole lodash package. another way is destructing importing that needs some Webpack optimization for to tree-shaking, so you should import like below:
import { get } from 'lodash';
Also, another tricky way is just to install a specific package, I mean:
yarn add lodash.get
OR
npm install --save lodash.get
Then for import just write:
import get from 'lodash.get';
Definitely, it is not tree-shaking, it is a tight mindset development, but it causes you just add what you want.
BUT
YOU DON'T DO ANYTHING OF ABOVE SOLUTIONS, you just add the whole package by writing import * as _ from "lodash"; and then use _.random or any function and expect Webpack understand you wanna the tree-shaking be happening?
Surely, the Webpack works well. you should use some configs and coding style to see the tree-shaking happens.
If you're already using Babel, the easiest method to properly tree shake lodash is to use the official babel-plugin-lodash by the lodash team.
This uses Babel to rewrite your lodash imports into a more tree-shakeable form. Doing this dropped my team's bundle size by ~32kB (compressed) with less than 5 minutes of effort.
I have an entire legacy AngularJS 1.x application that used to run through gulp and babel. We are transitioning to the newer Angular 2+ but I'm running into an issue trying to get Webpack to actually find and compile the legacy files. I've tried following instructions which are all almost identical like this video:
https://www.youtube.com/watch?v=H_QACBSqRBE
But the webpack config simply doesn't do anything to the existing files. Is there a way to grab a WHOLE FOLDER of older components, that DO NOT have any imports or exports? I feel like entry is supposed to follow the import dependency path but that just doesn't exist for older AngularJS 1.x projects.
Errors are NOT being thrown during the build it just...doesn't transpile or polyfill.
example of what that section of the config looks like:
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
We did this recently. We created "dummy" entry point files to avoid having to change all of our AngularJS files to have require/import statements.
./entry-points/feature1.ts
export const importAll = (r: any): void => {
r.keys().forEach(r);
};
importAll(require.context('./app/feature1', true, /module\.js$/));
importAll(require.context('./app/feature1', true, /(^(?!.*(spec|module)\.js).*\.js)$/));
webpack.config.js
entry: {
'feature1': './entry-points/feature1.ts'
}
More detail here
I am building something like a static website generator that uses webpack to build the project and create a bundle with it.
In this project, a user is able to specify custom css files. I want those css files to be bundled with the final result. The issue is, that I do not have the paths to those css files available during development, so I can't do import 'some-asset-file-provided-by-the-user.css' in the javascript code that is going to be bundled. But I have them available when calling webpack.compile(config).
I am looking for a way to inject those css files into the bundle. So far I tried various ways, such as:
const stylesheet = 'some-asset-file-provided-by-the-user.css'
require(stylesheet)
Which did not work, probably because webpack is not able to deal with this "dynamic" require. Then I used the webpack define plugin for this
/* webpack.config.js */
new webpack.DefinePlugin({
stylesheet: 'some-asset-file-provided-by-the-user.css'
}),
/* app.js */
require(stylesheet) // should be replaced by the webpack define plugin with 'some-asset-file-provided-by-the-user.css'
which also did not work. I also tried to find a way to do something like this:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(Object.assign({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
useFiles: ['file-a.css', 'file-b.css']
}
}
]
}, extractTextPluginOptions))
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
which also failed because apparently neither style-loader nor css-loader support this type of interaction.
How can I solve this? I am open to writing a plugin for this, but I'd rather use something existing.
The simplest way to include the CSS is by adding it to your entry point. To make this easier, you should use an array as entry point even if it's just a single file, so you can simply push the CSS.
For example:
entry: {
app: ['./src/index.js'],
// Other entries
},
In your compile script you add it to entry.app before passing it to webpack.
config.entry.app.push('./user.css');
const compiler = webpack(config);
I have a TypeScript project which I am bundling with Webpack. It is a demo/docs app for an open source lib I am writing, so I want to show some of the source code as part of the docs.
In my webpack config I have:
loaders: [
{ test: /\.ts$/, loader: 'ts'},
{ test: /\.css$/, loader: 'style!raw' },
{ test: /\.html/, loader: 'html' }
]
which works fine for transpiling and bundling my TypeScript files. In one of my app components I do this:
basicCodeT: string = require('./basic-example-cmp.html');
basicCodeC: string = require('!raw!./basic-example-cmp.ts');
to load the source code into a string which I then want to display in the docs.
As you can see, there is a leading ! in the second line which I discovered seems to "bypass" the default loaders from the config and loads the raw TypeScript as a string.
In my dev build this works, but when I do a "production" build with the UglifyJsPlugin and OccurrenceOrderPlugin, I get the following output:
ERROR in ./demo/src/basic-example-cmp.html
Module build failed:
# ./demo/src/demo-app.ts 24:26-61
which corresponds to the line in the source where I try to require the raw TypeScript.
So, I want to pass basic-example-cmp.ts through the TS compiler as part of the app build, but also want to require it as raw text in the app.
My question then is: Is there a proper way to tell webpack to "ignore" loaders in specific require cases?
Is my way of prepending a ! correct? Is it a hack?
Update
Turns out my problem is simply due to the way Webpack handles HTML templates - it does not like the Angular 2 template syntax, see: https://github.com/webpack/webpack/issues/992
You can add two exclamation to ignore loaders in the webpack config file
!!raw!file.ts
one exclamation will only disable preloaders!
https://webpack.js.org/concepts/loaders/#inline
As far as I know that is the only way you are going to be able to load a file in two different ways. I expect the issue is that your paths are different in your production build.
I would suggest running webpack with the --display-error-details flag to get more info on why it fails.
Is there a proper way to tell webpack to "ignore" loaders in specific require cases?
Yes. Update your test in { test: /\.ts$/, loader: 'ts'}, as desired.
TL;DR
What is the proper way of extending jQuery object with plugins, exposing it globally and using external AMD libs with ES6 modules in webpack?
Is webpack the right tool for the task, or would SystemJs suit the situation of refactoring a legacy app to ES6 modules better?
I am trying to wrap my head around working with webpack and ES6 modules. I have a legacy mostly jquery app that I am currently converting. I am facing the following challenges:
finding best practices in the webpack/babel-loader workflow
figuring out which loader/plugin to use for which purpose
getting AMD resources like jquery and jquery plugins to play nice with the rest of the modules.
exposing jquery globals, extended with all the plugins and jquery-ui
I have relied on the following resources:
This great answer explains a lot, though it does not mention the exports loader, which I am mostly relying on: https://stackoverflow.com/a/28989476/2613786
http://webpack.github.io/docs/shimming-modules.html - the documentation lists many possibilities, but i lack the experience to decide which one is the right one. It seems to be preferred to use the ProvidePlugin instead of the expose-loader. Sadly I didn't get it to work with an extended jQuery object. Neither did it work for the use of module functions invoced in <script> tags.
I still struggle to find programatic solutions and decide which webpack plugin is the right one for the job. Some advice or examples from an experienced webpack user are greatly appreciated.
In my webpack.config.js i have the following loaders to expose jquery and transpile with babel:
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {modules: 'common'}
},
{
test: /jquery\.js$/,
exclude: /node_modules/,
loader: 'expose?jQuery',
},
{
test: /jquery\.js$/,
exclude: /node_modules/,
loader: 'expose?$',
},
{
test: /[\/\\]vendor[\/\\]jquery.sparkline\.js$/,
loader: "imports?define=>false"
}
]
},
amd: { jQuery: true },
// plugins: [
// new webpack.ProvidePlugin({
// $: 'jquery',
// jQuery: 'jquery',
// 'window.jQuery': 'jquery',
// 'root.jQuery': 'jquery'
// })
// ], ...
In my entry.js file I include jquery in the following way:
import 'expose?jQuery!expose?$!./vendor/jquery';
import './jquery/jquery-ui';
import './vendor/jquery.sparkline';
I had to comment out the ProvidePlugin, when i use it, the jQuery is not extended with custom plugins anymore, any idea why that is the case?
Does it have to do with the plugins using ES6 module syntax?
I had to add loader: "imports?define=>false" for jquery.sparkline.js to get it to be recogniced. Is this really necessary, or is there a better way to do it?
Concerning jquery-ui i had to find an old version that did not use AMD define to get it to add to the jquery object. What would be the right way to do it?
Any help and advice is greatly appreciated, a reason to switch to SystemJs and Jspm might also be a solution.
I have had issues with this and it seems that it was because some plugins assume $ and others assume jQuery, but the following eventually worked for me, even if it is rather ugly:
Edit, note that I'm testing for plugins which are named jquery.xyz.js, you'd have to adjust the regex appropriately.
Also, I'm not sure if the two different expose loaders for jQuery are causing issues, but so far this works.
// webpack.config.js
...
"module": {
"loaders": [
{
test: require.resolve("jquery"),
loader: "expose?$!expose?jQuery"
},
{
test: /jquery\..*\.js/,
loader: "imports?$=jquery,jQuery=jquery,this=>window"
}
...