I am trying to remove script tags for javascript libraries from my html, and so have removed underscore.js from a template page.
To replace this, within my index.js (webpack entry point), I have the following
import 'underscore';
The size of the webpack outputted bundle.js file increases by 50k when I do this, so I know that the library is in bundle.js. However, underscore is not available when I try to use it in the console on a page which has the bundle.js included.
Any thoughts would be appreciated.
const webpack = require('webpack');
const path = require('path');
const precss = require('precss');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const postcssImport = require('postcss-import');
module.exports = {
context: __dirname + '/frontend',
devtool: 'source-map',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, './static'),
},
module: {
loaders: [
{ test: /\.js$/, loader: 'babel', exclude: /node_modules/, query: { presets: ['es2015'] } },
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap&importLoaders=1!postcss') },
],
},
vendor: [
'underscore',
],
plugins: [
new ExtractTextPlugin('si-styles.css'),
new webpack.ProvidePlugin({
underscore: 'underscore',
}),
],
postcss: function(webpack) {
return [
postcssImport({ addDependencyTo: webpack }), // Must be first item in list
precss,
autoprefixer({ browsers: ['last 2 versions'] }),
];
},
};
In order to achieve that you can use this webpack plugin:
new webpack.ProvidePlugin({
underscore: "underscore"
})
by the way it is not necessary that you import the library in the index file of your directory. You will have access to the library also specifying a new entry point in your webpack config file.. You could then put all your vendor code in a vendor.js boundle like so:
entry: {
main: [
'./app/js/main.js'
],
vendor: [
'underscore',
'lodash',
'my-awesome-library!'
]
}
UPDATE: There is a very good tutorial in how to use webpack in production on egghead.io.. Try to check it out!
Related
I have issue with Webpack building production bundle with test files + test libs included.
In this case it is Enzyme and Jest which we use.
Webpack version 3.10.0
Webpack.build.js
const path = require('path');
const webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = function() {
return {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '../build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: [
/node_modules/,
/__snapshots__/,
/test-util/,
],
loader: 'babel-loader'
},
{
test: /\.scss$/,
use: [
{
loader: 'isomorphic-style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
]
},
plugins: [
new UglifyJsPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}
}),
]
}
};
Here is the file structure
I have tried to workaround the issue by placing Enzyme as external resource in webpack.
externals: {
'enzyme': 'enzyme',
'enzyme-adapter-react-16': 'enzyme-adapter-react-16',
}
That does exclude it from build but (it is workaround) it still builds the snapshot files
I have tried to make "test" regex of babel-loader more specific and exclude test files but it failed.
This is new project and I spent whole day trying to make it bundle only what is necesary. Drained all of my know-how, my google know-how and know-how of the poeple I know or I work with. Hope that SO will be smarter :)
I have the following in a file initialize.js:
import App from './components/App';
import './styles/application.less';
document.addEventListener('DOMContentLoaded', () => {
const app = new App();
app.start();
});
In webpack.config.js I have:
'use strict';
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const ProvidePlugin = webpack.ProvidePlugin;
const ModuleConcatenationPlugin = webpack.optimize.ModuleConcatenationPlugin;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const extractLess = new ExtractTextPlugin({
filename: 'app.css',
});
const webpackCommon = {
entry: {
app: ['./app/initialize']
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader?presets[]=es2015'
}]
}, {
test: /\.hbs$/,
use: {
loader: 'handlebars-loader'
}
}, {
test: /\.less$/,
exclude: /node_modules/,
use: extractLess.extract({
use: [{
loader: 'css-loader'
}, {
loader: 'less-loader'
}],
// use style-loader in development
fallback: 'style-loader'
}),
}]
},
output: {
filename: 'app.js',
path: path.join(__dirname, './public'),
publicPath: '/'
},
plugins: [
extractLess,
new CopyWebpackPlugin([{
from: './app/assets/index.html',
to: './index.html'
}]),
new ProvidePlugin({
$: 'jquery',
_: 'underscore'
}),
new ModuleConcatenationPlugin(),
],
};
module.exports = merge(webpackCommon, {
devtool: '#inline-source-map',
devServer: {
contentBase: path.join(__dirname, 'public'),
compress: true,
port: 9000
}
});
I tried removing the the plugins and the contents of application.less, but I keep getting this error:
ERROR in ./node_modules/css-loader!./node_modules/less-loader/dist/cjs.js!./app/styles/application.less
Module build failed: TypeError: Super expression must either be null or a function, not undefined
at ...
# ./app/styles/application.less 4:14-127
# ./app/initialize.js
If I replace that LESS file with a CSS one and update the config it works fine, so I guess the problem has to do with less-loader.
I'm using Webpack 3.4.1, Style Loader 0.18.2, LESS Loader 4.0.5, Extract Text Webpack Plugin 3.0.0 and CSS Loader css-loader.
My bad, I didn't notice I was using an old less version. That was the culprit. Just updated it to 2.7.2 and the problem is gone.
https://babeljs.io/docs/usage/polyfill/#usage-in-browser
I did not understand the lines on the documentation page under:
Usage in Browser heading
can someone help me with what else is required:
Below are my code snippets:
I'm using storybook as a boilerplate:
webpack.config.js file:
entry: [
'babel-polyfill',
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs
]
index.js file:
import 'babel-polyfill';
import React from 'react';
Is there some other files also where I need to add babel-polyfill related code.
require('babel-polyfill');
var path = require('path');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
var getClientEnvironment = require('./env');
var paths = require('./paths');
var publicPath = '/';
var publicUrl = '';
var env = getClientEnvironment(publicUrl);
module.exports = {
devtool: 'cheap-module-source-map',
entry: ['babel-polyfill',
require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
paths.appIndexJs
],
output: {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/bundle.js',
publicPath: publicPath
},
resolve: {
fallback: paths.nodePaths,
extensions: ['.js', '.json', '.jsx', ''],
alias: {
'react-native': 'react-native-web'
}
},
module: {
// First, run the linter.
// It's important to do this before Babel processes the JS.
preLoaders: [{
test: /\.(js|jsx)$/,
loader: 'eslint',
include: paths.appSrc,
}],
loaders: [{
exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/],
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: 'babel',
query: {
cacheDirectory: true
}
}, {
test: /\.css$/,
loader: 'style!css?importLoaders=1!postcss'
}, {
test: /\.json$/,
loader: 'json'
}
]
},
// We use PostCSS for autoprefixing only.
postcss: function() {
return [
autoprefixer({
browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway
]
}),
];
},
plugins: [
new InterpolateHtmlPlugin({
PUBLIC_URL: publicUrl
}),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
new webpack.DefinePlugin(env),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
There are two ways to get this code into your browser.
1 - Include the babel-polyfill module in the webpack bundle
2 - Load it as an external script in your html
Webpack - adding bundle dependencies with entry arrays
Put an array as the entry point to make the babel-polyfill module available to your bundle as an export.
With webpack.config.js, add babel-polyfill to your entry array.
The webpack docs explain how an entry array is handled:
What happens when you pass an array to entry? Passing an array of file
paths to the entry property creates what is known as a "multi-main
entry". This is useful when you would like to inject multiple
dependent files together and graph their dependencies into one
"chunk".
Webpack.config.js
require("babel-polyfill");
var config = {
devtool: 'cheap-module-eval-source-map',
entry: {
main: [
// configuration for babel6
['babel-polyfill', './src/js/main.js']
]
},
}
Alternative to Webpack - load babel-polyfill as an external script in the browser html
The alternative to using webpack would mean including the module as an external script in your html. It will then be available to code in the browser but the webpack bundle won't be directly aware of it.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.22.0/polyfill.js"></script>
I have the following simple webpack.config.js file:
var webpack = require("webpack"); //LINE OF INTEREST
module.exports = {
entry: ["./main.js"],
output: {
path: "./build",
filename: "bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' }
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
};
When I comment out the top line defining webpack, I get an error because I reference webpack when defining the ProvidePlugin. When I include that 1st line however I get the following cryptic error:
ERROR in (webpack)/package.json
Module parse failed: /var/www/html/node_modules/webpack/package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:9)
First of all, I'm unsure why my node_modules folder is my apache root directory. Second of all, why am I getting a parse error? Isn't the package.json file something that was installed via npm? How could there be syntax errors in it?
webpack.config.js
const webpack = require("webpack");
const path = require('path');
module.exports = {
entry: "./main.js",
output: {
path: path.resolve(__dirname, 'build'),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
};
Working Example For more Information
How do you use Webpack and AngularJS together, and how about template loading and on demand fetching of resources?
An example of a well written webpack.config.js file for this purpose would be very much appreciated.
All code snippets displayed here can be accessed at this github repo. Code has been generously adapted from this packetloop git repo.
webpack.config.json
var path = require('path');
var ResolverPlugin = require("webpack/lib/ResolverPlugin");
var config = {
context: __dirname,
entry: ['webpack/hot/dev-server', './app/app.js'],
output: {
path: './build',
filename: 'bundle.js'
},
module: {
loaders: [{
test: /\.css$/,
loader: "style!css-loader"
}, {
test: /\.scss$/,
loader: "style!css!sass?outputStyle=expanded"
}, {
test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/,
loader: "file"
}, {
test: /\.html$/,
loader: "ngtemplate?relativeTo=" + path.join(__dirname, 'app/') + "!raw"
}]
},
// Let webpack know where the module folders are for bower and node_modules
// This lets you write things like - require('bower/<plugin>') anywhere in your code base
resolve: {
modulesDirectories: ['node_modules', 'lib/bower_components'],
alias: {
'npm': __dirname + '/node_modules',
'vendor': __dirname + '/app/vendor/',
'bower': __dirname + '/lib/bower_components'
}
},
plugins: [
// This is to help webpack know that it has to load the js file in bower.json#main
new ResolverPlugin(
new ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
)
]
};
module.exports = config;
To import AngularJS into the main app.js you do the following:
app/vendor/angular.js
'use strict';
if (!global.window.angular) {
require('bower/angular/angular');
}
var angular = global.window.angular;
module.exports = angular;
And then use it in app.js like so,
app.js
...
var angular = require('vendor/angular');
// Declare app level module
var app = angular.module('myApp', []);
...
Is the following correct? Is there an easier way to do this? I've seen a few (not a lot by any standards) posts which listed another method.
From this reddit post comment
// Add to webpack.config.js#module#loaders array
{
test: /[\/]angular\.js$/,
loader: "exports?angular"
}
There is also another plugin which is in development right now, at stackfull/angular-seed. It seems to be in the right direction, but is really really hard to use right now.
Webpack is way awesome, but the lack of documentation and samples are killing it.
You can just require angular in all modules (files) where you need it. I have a github repository with example how to do that (also using webpack for build). In the example ES6 import syntax is used but it shouldnt matter, you can use standard require() instead.
Example:
import 'bootstrap/dist/css/bootstrap.min.css';
import './app.css';
import bootstrap from 'bootstrap';
import angular from 'angular';
import uirouter from 'angular-ui-router';
import { routing} from './app.config';
import common from './common/common.module';
import featureA from './feature-a/feature-a.module';
import featureB from './feature-b/feature-b.module';
const app = angular
.module('app', [uirouter, common, featureA, featureB])
.config(routing);
I am starting with Angular + Flux with Webpack so may be I can help you with some things.
Basically I am installing everything with NPM, it has module export system, so it works like nothing. (You can use export-loader, but why if you do not need to.)
My webpack.config.js looks like this:
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require("html-webpack-plugin");
var nodeModulesDir = path.resolve(__dirname, './node_modules');
// Some of my dependencies that I want
// to skip from building in DEV environment
var deps = [
'angular/angular.min.js',
...
];
var config = {
context: path.resolve(__dirname, './app'),
entry: ['webpack/hot/dev-server', './main.js'],
resolve: {
alias: {}
},
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js'
},
// This one I am using to define test dependencies
// directly in the modules
plugins: [
new webpack.DefinePlugin({
ON_TEST: process.env.NODE_ENV === 'test'
})
],
module: {
preLoaders: [
{test: /\.coffee$/, loader: "coffeelint", exclude: [nodeModulesDir]}
],
loaders: [
{test: /\.js$/, loader: 'ng-annotate', exclude: [nodeModulesDir]},
{test: /\.coffee$/, loader: 'coffee', exclude: [nodeModulesDir]},
...
],
noParse: []
},
devtool: 'source-map'
};
if (process.env.NODE_ENV === 'production') {
config.entry = {
app: path.resolve(__dirname, './app/main.js'),
vendors: ['angular']
};
// config.output.path = path.resolve(__dirname, './dist');
config.output = {
path: path.resolve(__dirname, "./dist"),
filename: "app.[hash].js",
hash: true
};
config.plugins.push(new webpack.optimize.UglifyJsPlugin());
config.plugins.push(new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.[hash].js'));
config.plugins.push(new HtmlWebpackPlugin({
title: 'myApp',
template: path.resolve(__dirname, './app/index.html'),
inject: 'body'
}));
delete config.devtool;
}
else {
deps.forEach(function (dep) {
var depPath = path.resolve(nodeModulesDir, dep);
config.resolve.alias[dep.split(path.sep)[0]] = depPath;
config.module.noParse.push(depPath);
});
}
module.exports = config;
My main.js looks like this:
var angular = require('angular');
if(ON_TEST) {
require('angular-mocks/angular-mocks');
}
require('./index.coffee');
And index.coffee containt main angular module:
ngModule = angular.module 'myApp', []
require('./directive/example.coffee')(ngModule)