Related
Using Webpack 4, I'm creating a bundle and a vendors bundle. Vendor bundle contains jQuery and the code bundle references jquery using 'import * from jquery'. This works perfectly.
However, I now need to use a 3rd party (already minified) javascript file. That file expects jquery to be exposed as '$' on the global window object. My bundle needs to be loaded after the new minified file, also.
So I have:
<script src='./libs/jquery-3.2.1.min.js'></script>
<script src='../vendor.js'></script>
<script src="./libs/newMinifiedFile.js"></script>
<script src="../bundle.js"></script>
as my current workaround. The static jquery file links the $ objects to the global namespace, and then the vendor.js file allows me to keep using 'import' in my bundle.
So, how do I only load jquery once, and use it in both ways? This seems to be a slightly different problem than most I've seen online because of how I'm loading things.
Here's a small example of my configs right now:
const config = (isDebug) => {
const isDevBuild = isDebug;
const extractCSS = new MiniCssExtractPlugin({filename: 'vendor.css'});
const sharedConfig = {
mode: isDevBuild ? 'development' : 'production',
stats: { modules: false },
resolve: {
extensions: [ '.js' ]
},
module: {
rules: [
{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' },
]
},
entry: {
vendor: [
'jquery'
],
},
output: {
publicPath: 'dist/',
filename: '[name].js',
library: '[name]_[hash]',
},
plugins: [
new webpack.NormalModuleReplacementPlugin(/\/iconv-loader$/, require.resolve('node-noop')) // Workaround for https://github.com/andris9/encoding/issues/16
]
};
const clientBundleConfig = merge(sharedConfig, {
output: {
path: path.join(__dirname, 'wwwroot', 'dist'),
pathinfo: false
},
module: {
rules: [
{
test: /\.css(\?|$)/, include: path.resolve(__dirname, "client"), exclude: [/webviewer/, /redux/, /helpers/],
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
minimize: isDevBuild,
sourceMap: isDevBuild
}
}
]
}
]
},
plugins: [
extractCSS,
new webpack.DllPlugin({
path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
name: '[name]_[hash]'
})
],
optimization: {
minimize: !isDevBuild,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
});
And in my normal config I use:
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require(path.join(__dirname, 'wwwroot', 'dist', 'vendor-manifest.json'))
})
So, I can find jquery in the global 'vendor_hash' object, but I can't get it to map it to $ no matter what I try (Most plugins seem to see that $ is never used in any of the code I'm supplying for the bundle and thus don't seem to include it). If I add something like this in my 'entry.js' file then it still doesn't work:
window.$ = window.jQuery = require("jquery");
There are a few SO posts about style-loader and css-loader, but despite this I have not been able to find a solution to my problem.
In short summary, when I #import css files in other css files, and the imported css contains url()s with relative paths, the paths are not resolved correctly.
Basically, the error message shows that Webpack ends up thinking the url() paths in the imported css are relative to src (main entry point), rather than being relative to the css file it it is imported into:
// css-one.scss
#import "./assets/open-iconic-master/font/css/open-iconic-bootstrap.css";
// open-iconic-bootstrap.css
#font-face {
src: url('../fonts/open-iconic.eot');
}
Error:
ERROR in ./src/main.scss
(./node_modules/css-loader??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/sass-loader/lib/loader.js??ref--5-3!./src/main.scss)
Module not found: Error: Can't resolve '../fonts/open-iconic.eot' in
'C:\Users\...\src'
# ./src/main.scss
(./node_modules/css-loader??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/sass-loader/lib/loader.js??ref--5-3!./src/main.scss) 7:106-141 7:172-207 # ./src/main.scss # ./src/index.js
What I Have Tried:
I have tried to use the convertToAbsoluteUrls flag in style-loader
I have tried to turn off all source maps (mentioned in style-loader docs)
My Webpack Config File (loaders are at the bottom):
const path = require('path');
const webpack = require('webpack'); // for webpack built-in plugins
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
// const WriteFilePlugin = require('write-file-webpack-plugin');
// const ManifestPlugin = require('webpack-manifest-plugin');
// const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const PATHS = {
// when using __dirname, resolve and join gives same result,
// because __dirname is absolute path to directory of this file.
// OK to use no slashes,
// both resolve and join adds platform-specific separators by default
src: path.resolve(__dirname, 'src'),
dist: path.resolve(__dirname, 'dist'),
build: path.resolve(__dirname, 'build'),
test: path.resolve(__dirname, 'test')
};
const NAMES = {
// JS FILES
index: 'index',
print: 'print',
// Chrome Extension Development
popup: 'popup',
options: 'options',
background: 'background',
contentScript: 'contentScript',
// FOLDERS
assets: 'assets',
utilities: 'utilities',
images: 'images',
fonts: 'fonts',
include: 'include'
};
const FILE_PATHS = {
// JS
indexJs: `${path.join(PATHS.src, NAMES.index)}.js`,
printJs: `${path.join(PATHS.src, NAMES.print)}.js`,
// Chrome Extension Development
popupJs: `${path.join(PATHS.src, NAMES.popup)}.js`,
optionsJs: `${path.join(PATHS.src, NAMES.options)}.js`,
backgroundJs: `${path.join(PATHS.src, NAMES.background)}.js`,
contentScriptJs: `${path.join(
PATHS.src,
NAMES.include,
NAMES.contentScript
)}.js`,
// HTML
indexHtml: `${path.join(PATHS.src, NAMES.index)}.html`,
printHtml: `${path.join(PATHS.src, NAMES.print)}.html`,
// Chrome Extension Development
popupHtml: `${path.join(PATHS.src, NAMES.popup)}.html`,
optionsHtml: `${path.join(PATHS.src, NAMES.options)}.html`,
backgroundHtml: `${path.join(PATHS.src, NAMES.background)}.html`
};
// Third-party (vendor) libraries to include
// const VENDORS = ['react', 'bootstrap', 'lodash', 'jQuery']; // Relative paths to node_modules
// Note: These are relative
const ASSETS = {
images: path.join(NAMES.assets, NAMES.images),
fonts: path.join(NAMES.assets, NAMES.fonts)
};
// CleanWebpackPlugin config
const pathsToClean = [PATHS.dist, PATHS.build];
const cleanOptions = {
root: __dirname,
exclude: ['shared.js'],
verbose: true,
dry: false
};
// CopyWebpackPlugin config
const copyPattern = [
// {
// from: NAMES.assets,
// to: NAMES.assets
// },
// {
// from: path.join(NAMES.include, 'contentScript.css')
// },
// {
// from: 'manifest.json',
// transform(content, copyPath) {
// // generates the manifest file using the package.json informations
// return Buffer.from(
// JSON.stringify({
// ...JSON.parse(content.toString())
// // description: env.npm_package_description,
// // version: env.npm_package_version
// })
// );
// }
// }
];
const copyOptions = {
// ignore: ['*.js'],
context: PATHS.src
};
module.exports = (env = {}) => {
// webpack injects env variable, into webpack config.
// perfect to check for production.
// remember to specify --env.production in command
// (if in production mode).
const isProduction = env.production === true;
return {
entry: {
index: FILE_PATHS.indexJs
// Chrome Extension Development
// popup: FILE_PATHS.popupJs,
// contentScript: FILE_PATHS.contentScriptJs
// options: FILE_PATHS.optionsJs,
// background: FILE_PATHS.backgroundJs,
// vendor: VENDORS
},
mode: isProduction ? 'production' : 'development',
devtool: isProduction ? 'source-map' : 'inline-source-map',
optimization: {
splitChunks: {
chunks: 'all'
}
},
output: {
filename: isProduction ? '[name].[chunkhash:8].js' : '[name].js',
// chunkFilename determine name of non-entry chunk files,
// for example dynamic imports in the app
chunkFilename: isProduction ? '[name].[chunkhash:8].js' : '[name].js',
path: PATHS.dist
},
plugins: [
// new webpack.SourceMapDevToolPlugin({
// filename: '[file].map',
// exclude: ['vendor', 'runtime']
// }),
new webpack.DefinePlugin({
// specifies environment variable for dependencies.
// does not apply to browser runtime environment
// (process.env is provisioned by Node)
'process.env.NODE_ENV': isProduction ?
JSON.stringify('production') :
JSON.stringify('development')
}),
// new BundleAnalyzerPlugin(),
new CleanWebpackPlugin(pathsToClean, cleanOptions),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
// does not work with Hot Module Replacement (HMR)
// allows HMR in development (will only use this plugin in production)
filename: isProduction ? '[name].[contenthash].css' : '[name].css',
chunkFilename: isProduction ? '[id].[contenthash].css' : '[id].css'
}),
new webpack.HashedModuleIdsPlugin(),
isProduction ?
new UglifyJSPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}) :
() => {},
new CopyWebpackPlugin(copyPattern, copyOptions),
// new WriteFilePlugin(),
new HtmlWebpackPlugin({
template: FILE_PATHS.indexHtml,
filename: `${NAMES.index}.html`
})
// new HtmlWebpackPlugin({
// template: FILE_PATHS.popupHtml,
// filename: `${NAMES.popup}.html`,
// excludeChunks: [NAMES.contentScript]
// In dev mode, chunks excluded vendor chunk (which holds CSS).
// Above check fixes it.
// }),
// new HtmlWebpackPlugin({
// filename: `${NAMES.contentScript}.html`,
// excludeChunks: [NAMES.popup, 'runtime'] // Runtime only needed in one HTML
// }),
// new HtmlWebpackPlugin({
// template: FILE_PATHS.optionsHtml,
// filename: `${NAMES.options}.html`,
// chunks: isProduction ? [NAMES.options] : ''
// }),
// new HtmlWebpackPlugin({
// template: FILE_PATHS.backgroundHtml,
// filename: `${NAMES.background}.html`,
// chunks: isProduction ? [NAMES.background] : ''
// }),
// no need for CSS minimization here <-- Done by PostCSS (cssnano)
// new InlineManifestWebpackPlugin(),
// new ManifestPlugin({fileName: 'webpack-manifest.json'}),
],
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.s?[ac]ss$/,
exclude: /node_modules/,
use: [
isProduction ?
MiniCssExtractPlugin.loader :
{
// creates style nodes from JS strings
loader: 'style-loader',
options: {
sourceMap: true,
convertToAbsoluteUrls: true
}
},
{
// CSS to CommonJS (resolves CSS imports into exported CSS strings)
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 2
}
},
{
loader: 'postcss-loader',
options: {
config: {
ctx: {
cssnext: {},
cssnano: {},
autoprefixer: {}
}
},
sourceMap: true
}
},
{
// compiles Sass to CSS
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[hash:4].[ext]',
outputPath: ASSETS.images
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[hash:4].[ext]',
outputPath: ASSETS.fonts
}
}]
},
{
test: /\.(csv|tsv)$/,
use: ['csv-loader']
},
{
test: /\.xml$/,
use: ['xml-loader']
},
{
test: /\.(html)$/,
use: {
loader: 'html-loader',
options: {
interpolate: 'require',
minimize: true
}
}
}
// {
// test: /\.tsx?$/,
// exclude: /(node_modules|bower_components)/,
// use: 'ts-loader'
// }
]
},
devServer: {
// contentBase: path.join(__dirname, 'dist'),
contentBase: PATHS.dist,
compress: false,
port: 8080,
open: false
}
};
};
it took me around 5 days of work to understand how this webpack mess works. I have to be honest I can say that this is one of those things that I really do not understand why they are "defacto" tools of the moment. I can't understand how difficult it can be just to make the config files work as it should, in gulp took me 1 hour to do the same.
My problem was that all the url() rules (including fonts and images) were being loaded by css-loader as [object Module], and they where exported by file-loader but never loaded, so if I added ?url=false to the css-loader it never copied the files and export them. I have to say this was a totally PITA, but I got it working, and I hope it works for somebody else in the world, this was made with webpack 4.
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["#babel/env"] }
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
},
}
},
{
loader: 'file-loader',
options:{
name: '[name].[ext]',
outputPath: 'images/',
publicPath: 'images/'
}
},
'url-loader?limit=100000'
],
},
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
]
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader?url=false'},
{ loader: 'sass-loader', options: { sourceMap: true } }
],
},
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "",
filename: "bundle.js"
},
devServer: {
contentBase: path.join(__dirname, "dist/"),
port: 3000,
publicPath: "http://localhost:3000/dist/",
hotOnly: true
},
plugins: [ new MiniCssExtractPlugin(),
new CopyPlugin([{ from: 'src/images/', to: 'images/' }]),
new CopyPlugin([{ from: 'src/fonts/', to: 'fonts/' }]),
new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i }),
new HtmlWebpackPlugin({
hash: true,
template: './src/index.html',
filename: './index.html' //relative to root of the application
}),
]
};
You can turn off processing of url() rules, btw. I have no idea, why this is a default behavior.
{
loader: 'css-loader',
options: {
...
url: false,
}
},
I was able to solve the problem myself. In case it could help others in the future, please find the solution below.
First of all, if you are using both postcss-loader with the postcss-import plugin, AND css-loader, turn off / delete the postcss-import plugin. You do not need more than one tool that resolves #import rules. This is not really a problem if the order of loaders is correct, but you might as well remove it.
In the sass-loader docs, you can read the following:
Since Sass/libsass does not provide url rewriting, all linked assets
must be relative to the output.
If you're just generating CSS without passing it to the css-loader, it must be relative to your web root.
If you pass the generated CSS on to the css-loader, all urls must be relative to the entry-file (e.g. main.scss).
More likely you will be disrupted by this second issue. It is natural to expect relative references to be resolved against the .scss file in which they are specified (like in regular .css files). Thankfully there are two solutions to this problem:
Add the missing url rewriting using the resolve-url-loader. Place it before the sass-loader in the loader chain.
Library authors usually provide a variable to modify the asset path. bootstrap-sass for example has an $icon-font-path. Check out this working bootstrap example.
I decided to follow bullet two, and add in resolve-url-loader above sass-loader in the Webpack config. It now works as expected.
My final Webpack config (for now) looks like this:
{
test: /\.s?[ac]ss$/,
exclude: /node_modules/,
use: [
isProduction
? MiniCssExtractPlugin.loader
: {
// creates style nodes from JS strings
loader: 'style-loader',
options: {
sourceMap: true,
// convertToAbsoluteUrls: true
}
},
{
// CSS to CommonJS (resolves CSS imports into exported CSS strings)
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 2
// url: false,
// import: false
}
},
{
loader: 'postcss-loader',
options: {
config: {
ctx: {
cssnext: {},
cssnano: {},
autoprefixer: {}
}
},
sourceMap: true
}
},
{
loader: 'resolve-url-loader',
options: {
attempts: 1,
sourceMap: true
}
},
{
// compiles Sass to CSS
loader: 'sass-loader',
options: { sourceMap: true }
}
]
},
Side Notes
I noticed that source map paths under "no domain" in Chrome's debugger are repeated. If anyone figures out why, please do share
Remember to include the below side effects in package.json, so tree shaking, which happens in production mode, does not delete the extracted css
"sideEffects": [
".css",
".scss"
],
In webpack 5, you should refrain from using raw-loader and file-loader, instead use asset/source and asset/resource.
{
test: /\.txt$/,
type: 'asset/source',
},
{
test: /\.png$/,
type: 'asset/resource',
},
You can read more about assets on: https://webpack.js.org/guides/asset-modules/. It's described how for Webpack 4 and earlier you should use file-loader, and for Webpack5 and later you should use asset modules.
If anyone is struggling with this using Webpack 5 and is trying to upgrade from css-loader 5 to css-loader 6, you might need to check this issue where the poster has a similar problem to the OP:
With css-loader 5.2.7 the images in the input stylus were embedded as data-
URL in the output CSS. With css-loader 6, the images are instead moved
to the output directory.
See Notes here for how to upgrade to css-loader 6 - the salient points are:
using ~ is deprecated when the esModules option is enabled ... and can
be removed from your code
and
file-loader and url-loader are deprecated, please migrate on asset
modules, since v6 css-loader is generating new URL(...) syntax, it
enables by default built-in assets modules, i.e. type: 'asset' for all
url()
I've therefore done the following:
removed any '~' in my .scss files
i.e.
$font-path: "~/src/fonts" !default;
becomes
$font-path: "/src/fonts" !default;
I've also removed the 'file-loader' module completely.
All NPM packages are now up to date and URLs in CSS are working correctly.
IE is not compatible with new URL()
My issue was that webpack 5 changed everything with "Asset modules" apparently and broke file-loader.
Official docs explain it well: https://webpack.js.org/guides/asset-modules/
TL;DR you can fix it by adding type: 'javascript/auto' to the rule/loader
{
test: /\.(eot|ttf|woff2?)($|\?)/i,
loader: 'file-loader',
options: {
name: '[name]-[sha1:hash:hex:10].[ext]',
esModule: false,
},
type: 'javascript/auto'
},
I'm trying to integrate Syncfusions' Js library with an Aurelia project using the Aurelia Syncfusion Bridge, but i'm getting the following error when trying to load the plugin into my vendor package.
ERROR in dll vendor
Module not found: Error: Can't resolve 'syncfusion-javascript' in 'C:\Users\Liam\Downloads\aurelia-webpack1333503894'
# dll vendor
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const { AureliaPlugin, ModuleDependenciesPlugin } = require('aurelia-webpack-plugin');
const bundleOutputDir = './wwwroot/dist';
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
stats: { modules: false },
entry: { 'app': 'aurelia-bootstrapper' },
resolve: {
extensions: ['.ts', '.js'],
modules: ['ClientApp', 'node_modules'],
},
output: {
path: path.resolve(bundleOutputDir),
publicPath: 'dist/',
filename: '[name].js'
},
module: {
rules: [
{ test: /\.ts$/i, include: /ClientApp/, use: 'ts-loader?silent=true' },
{ test: /\.html$/i, use: 'html-loader' },
{ test: /\.css$/i, use: isDevBuild ? 'css-loader' : 'css-loader?minimize' },
{ test: /\.(png|jpg|jpeg|gif|cur|svg)$/, use: 'url-loader?limit=25000' },
{ test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader', query: { limit: 10000, mimetype: 'application/font-woff2' } },
{ test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader', query: { limit: 10000, mimetype: 'application/font-woff' } },
{ test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader' },
]
},
plugins: [
new webpack.DefinePlugin({ IS_DEV_BUILD: JSON.stringify(isDevBuild) }),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
}),
new AureliaPlugin({ aureliaApp: 'boot' }),
].concat(isDevBuild ? [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
] : [
new webpack.optimize.UglifyJsPlugin()
])
}];
}
webpack.config.js
var path = require('path');
var webpack = require('webpack');
const { AureliaPlugin, ModuleDependenciesPlugin } = require('aurelia-
webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var extractCSS = new ExtractTextPlugin('vendor.css');
module.exports = ({ prod } = {}) => {
const isDevBuild = !prod;
return [{
stats: { modules: false },
resolve: {
extensions: ['.js']
},
module: {
loaders: [
{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, loader: 'url-loader?limit=100000' },
{ test: /\.css(\?|$)/, loader: extractCSS.extract([isDevBuild ? 'css-loader' : 'css-loader?minimize']) }
]
},
entry: {
vendor: [
'aurelia-event-aggregator',
'aurelia-fetch-client',
'aurelia-framework',
'aurelia-history-browser',
'aurelia-logging-console',
'aurelia-pal-browser',
'aurelia-polyfills',
'aurelia-route-recognizer',
'aurelia-router',
'aurelia-templating-binding',
'aurelia-templating-resources',
'aurelia-templating-router',
'bootstrap',
'bootstrap/dist/css/bootstrap.css',
'jquery',
"aurelia-syncfusion-bridge",
"syncfusion-javascript"
],
},
output: {
path: path.join(__dirname, 'wwwroot', 'dist'),
publicPath: 'dist/',
filename: '[name].js',
library: '[name]_[hash]',
},
plugins: [
extractCSS,
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.DllPlugin({
path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
name: '[name]_[hash]'
}),
new ModuleDependenciesPlugin({
"aurelia-syncfusion-bridge": ["./grid/grid", "./grid/column"],
}),
].concat(isDevBuild ? [] : [
new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } })
])
}]
};
Thanks for your interest towards Syncfusion controls.
We recommend you to configure aurelia-syncfusion-bridge resources in webpack.config.js file. Because aurelia-syncfusion-bridge’s resources are traced by aurelia-webpack-plugin and included in app.bundle.
Suppose If we add this plugin in webpack.vendor.js, we need to bundle manually for every additional aurelia-syncfusion-bridge’s resources for proper bundling. Since, we recommend to configure our bridge in webpack.config.js, which will automatically bundle the bridge source along with application bundle.
We have prepared sample for the same and attached below.
Sample
Please let us know if you need further assistance on this.
Thanks,
Abinaya S
Greetings one and all,
I have been playing around with Bootstrap for Webpack, but I'm at the point of tearing my hair out. I have literally gone through loads of blog articles and they either use the 7 months outdated 'bootstrap-webpack' plugin (which, surprisingly does not work out of the box) or.. They include the Bootstrap files through import 'node_modules/*/bootstrap/css/bootstrap.css'.
Surely, there must be a cleaner and more efficient way of going about this?
This is my current webpack.config.js file:
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var autoprefixer = require('autoprefixer');
var path = require('path');
module.exports = {
entry: {
app: path.resolve(__dirname, 'src/js/main.js')
},
module: {
loaders: [{
test: /\.js[x]?$/,
loaders: ['babel-loader?presets[]=es2015&presets[]=react'],
exclude: /(node_modules|bower_components)/
}, {
test: /\.css$/,
loaders: ['style', 'css']
}, {
test: /\.scss$/,
loaders: ['style', 'css', 'postcss', 'sass']
}, {
test: /\.sass$/,
loader: 'style!css!sass?sourceMap'
},{
test: /\.less$/,
loaders: ['style', 'css', 'less']
}, {
test: /\.woff$/,
loader: "url-loader?limit=10000&mimetype=application/font-woff&name=[path][name].[ext]"
}, {
test: /\.woff2$/,
loader: "url-loader?limit=10000&mimetype=application/font-woff2&name=[path][name].[ext]"
}, {
test: /\.(eot|ttf|svg|gif|png)$/,
loader: "file-loader"
}]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '/js/bundle.js',
sourceMapFilename: '/js/bundle.map',
publicPath: '/'
},
plugins: [
new ExtractTextPlugin('style.css')
],
postcss: [
autoprefixer({
browsers: ['last 2 versions']
})
],
resolve: {
extensions: ['', '.js', '.sass'],
modulesDirectories: ['src', 'node_modules']
},
devServer: {
inline: true,
contentBase: './dist'
}
};
I could go and require('bootstrap') (with some way of getting jQuery in it to work), but.. I'm curious to what you all think and do.
Thanks in advance :)
I am not sure if this is the best way, but following work for me well with vue.js webapp. You can see the working code here.
I have included files required by bootstrap in index.html like this:
<head>
<meta charset="utf-8">
<title>Hey</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="/static/bootstrap.css" type="text/css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
<script href="/static/bootstrap.js"></script>
</head>
And this works, you can execute the repo. Why I went this way was I had to customise some config in bootstrap so I had to change the variables file and build the code of bootstrap which outputted me bootstrap.js and bootstrap.cssfiles, which I am using here.
There is an alternative way suggested here by using the npm package and a webpack customisation.
First install bootstrap in your project:
npm install bootstrap#4.0.0-alpha.5
And make sure you can use sass-loader in your components:
npm install sass-loader node-sass --save-dev
now go to your webpack config file and add a sassLoader object with the following:
sassLoader: {
includePaths: [
path.resolve(projectRoot, 'node_modules/bootstrap/scss/'),
],
},
projectRoot should just point to where you can navigate to node_packages from, in my case this is: path.resolve(__dirname, '../')
Now you can use bootstrap directly in your .vue files and webpack will compile it for you when you add the following:
<style lang="scss">
#import "bootstrap";
</style>
I highly recommend using bootstrap-loader. You add a config file (.bootstraprc in your root folder) where you can exclude the bootstrap elements you don't want and tell where your variables.scss and bootstrap.overrides.scss are. Define you SCSS variables, do your overrides, add your webpack entry and get on with your life.
I use webpack to build bootstrap directly from .less and .scss files. This allows customizing bootstrap by overriding the source in .less/.scss and still get all the benefits of webpack.
Your code is missing an entry point for any .css/.less/.scss files. You need to include an entry point for the compiled css files. For this entry point, I declare an array with const and then include paths inside the array to the source files I want webpack to compile.
Currently, I'm using bootstrap 3 with a base custom template and also a 2nd custom theme. The base template uses bootstrap .less file styling and it has specific source overrides written in .less files.
The 2nd custom theme uses .sass file styling and has similar overrides to bootstrap's base written in .scss files. So, I need to try to optimize all this styling for production (currently coming in about 400kb but that's a little heavy because we choose to avoid CDN's due to targeting use in China).
Below is a reference webpack.config.js which works to build from .less/.scss/.css files, and also does a few other things like build typescript modules and uses Babel for converting es6/typescript to browser compatible javascript. The output ultimately ends up in my /static/dist folder.
const path = require('path');
const webpack = require('webpack');
// plugins
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// take debug mode from the environment
const debug = (process.env.NODE_ENV !== 'production');
// Development asset host (webpack dev server)
const publicHost = debug ? 'http://localhost:9001' : '';
const rootAssetPath = path.join(__dirname, 'src');
const manifestOptions = {
publicPath: `${publicHost}/static/dist/`,
};
const babelLoader = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
'#babel/preset-env'
]
}
};
const app_css = [
// specific order generally matters
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'css', 'fonts', 'Roboto', 'css', 'fonts.css'),
path.join(__dirname, 'node_modules', 'font-awesome', 'css', 'font-awesome.css'),
// This is bootstrap 3.3.7 base styling writtin in .less
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'bootstrap.less'),
// bootstrap theme in .scss -> src\bp\folder\theme\src\scss\styles.scss
path.join(__dirname, 'src', 'bp', 'folder', 'theme', 'src', 'scss', 'styles.scss'),
// back to .less -> 'src/bootstrap-template1/assets/less/_main_full/core.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'core.less'),
// 'src/bootstrap-template1/assets/less/_main_full/components.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'components.less'),
//'src/bootstrap-template1/assets/less/_main_full/colors.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'colors.less'),
// <!-- syntax highlighting in .css --> src/bp/folder/static/css/pygments.css
path.join(__dirname, 'src', 'bp', 'folder', 'static', 'css', 'pygments.css'),
// <!-- lato/ptsans font we want to serve locally --> src/fonts/googlefonts.css'
path.join(__dirname, 'src', 'fonts', 'googlefonts.css'),
// a .css style -> 'src/bp/appbase/styles/md_table_generator.css'
path.join(__dirname, 'src', 'bp', 'appbase', 'styles', 'md_table_generator.css'),
// another .css style -> hopscotch 'src/libs/hopscotch/dist/css/hopscotch.min.css'
path.join(__dirname, 'src', 'libs', 'hopscotch', 'dist', 'css', 'hopscotch.min.css'),
//LAST final custom snippet styles to ensure they take priority 'src/css/style.css',
path.join(__dirname, 'src', 'css', 'style.css')
];
const vendor_js = [
//'core-js',
'whatwg-fetch',
];
const app_js = [
// a typescript file! :)
path.join(__dirname, 'src', 'typescript', 'libs', 'md-table', 'src', 'extension.ts'),
// base bootstrap 3.3.7 javascript
path.join(__dirname, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'),
path.join(__dirname, 'src', 'main', 'app.js'),
// src/bootstrap-template1/assets/js/plugins/forms/styling/uniform.min.js'
path.join(__dirname, 'node_modules', '#imanov', 'jquery.uniform', 'src', 'js', 'jquery.uniform.js'),
// src/bootstrap-template1/assets/js/plugins/ui/moment/moment.min.js'
];
function recursiveIssuer(m) {
if (m.issuer) {
return recursiveIssuer(m.issuer);
} else if (m.name) {
return m.name;
} else {
return false;
}
}
module.exports = {
context: process.cwd(), // to automatically find tsconfig.json
// context: __dirname,
entry: {
app_css,
vendor_js,
app_js,
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: `${publicHost}/static/dist/`,
chunkFilename: '[id].[hash:7].js',
filename: '[name].[hash:7].js'
},
resolve: {
extensions: [".webpack.js", ".web.js",".tsx", ".ts", ".js", ".css"],
alias: {
jquery$: path.resolve(__dirname, 'node_modules', 'jquery', 'dist', 'jquery.js'),
}
},
target: "web",
devtool: 'source-map',
devServer: {
// this devserver proxies all requests to my python development server except
// webpack compiled files in the `/static/dist` folder
clientLogLevel: 'warning',
contentBase: path.join(__dirname, './src'),
publicPath: 'dist',
open: true,
historyApiFallback: true,
stats: 'errors-only',
headers: {'Access-Control-Allow-Origin': '*'},
watchContentBase: true,
port: 9001,
proxy: {
'!(/dist/**/**.*)': {
target: 'http://127.0.0.1:8000',
},
},
},
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCssAssetsPlugin({})],
splitChunks: {
cacheGroups: {
appStyles: {
name: 'app',
// https://webpack.js.org/plugins/mini-css-extract-plugin/#extracting-css-based-on-entry
test: (m, c, entry = 'app') =>
m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true,
},
},
},
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery'
}),
// Strip all locales from moment.js except "zh-cn"
// ("en" is built into Moment and can’t be removed)
new MomentLocalesPlugin({
localesToKeep: ['zh-cn'],
}),
new ForkTsCheckerWebpackPlugin({
tslint: true, useTypescriptIncrementalApi: true
}),
new ForkTsCheckerNotifierWebpackPlugin({ title: 'TypeScript', excludeWarnings: false }),
new MiniCssExtractPlugin({
filename: '[name].[hash:7].css',
chunkFilename: '[id].[hash:7].css',
moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.[hash:7].css`
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: true
}),
new ManifestPlugin({
...manifestOptions
}),
].concat(debug ? [] : [
// production webpack plugins go here
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
}
}),
new ForkTsCheckerWebpackPlugin({
async: false,
useTypescriptIncrementalApi: true,
memoryLimit: 2048
}),
]),
module: {
rules: [
{
// jinja/nunjucks templates
test: /\.jinja2$/,
loader: 'jinja-loader',
query: {
root:'../templates'
}
},
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
use: [
babelLoader,
{
loader: 'ts-loader',
options:
{ // disable type checker - we will use it in
// fork-ts-checker-webpack-plugin
transpileOnly: true
}
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
babelLoader
]
},
{
test: /\.(html|jinja2)$/,
loader: 'raw-loader'
},
{
test: /\.(sc|sa|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
// only use if hmr is not working correctly
// reloadAll: true,
},
},
{
loader: "css-loader",
},
{
loader: "sass-loader"
},
]
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
// only use if hmr is not working correctly
// reloadAll: true,
},
},
{
loader: 'css-loader', // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
},
],
},
{
test: /\.(ttf|eot|svg|gif|ico)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[path][name].[hash:7].[ext]',
context: rootAssetPath
},
},
],
},
{
test: /\.(jpe?g|png)$/i,
loader: 'responsive-loader',
options: {
name: '[path][name].[hash:7].[ext]',
adapter: require('responsive-loader/sharp'),
context: rootAssetPath
}
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader:'url-loader',
options:{
limit: 10000,
mimetype: 'application/font-woff',
// name: ('fonts/[path][name].[hash:7].[ext]'),
name: ('fonts/[name].[hash:7].[ext]'),
}
},
{
test: require.resolve("jquery"),
use:[
{ loader: "expose-loader", options:"$" },
{ loader: "expose-loader", options:"jQuery" },
{ loader: "expose-loader", options:"jquery" }
]
}
]
},
};
Greetings one and all,
I have been playing around with Bootstrap for Webpack, but I'm at the point of tearing my hair out. I have literally gone through loads of blog articles and they either use the 7 months outdated 'bootstrap-webpack' plugin (which, surprisingly does not work out of the box) or.. They include the Bootstrap files through import 'node_modules/*/bootstrap/css/bootstrap.css'.
Surely, there must be a cleaner and more efficient way of going about this?
This is my current webpack.config.js file:
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var autoprefixer = require('autoprefixer');
var path = require('path');
module.exports = {
entry: {
app: path.resolve(__dirname, 'src/js/main.js')
},
module: {
loaders: [{
test: /\.js[x]?$/,
loaders: ['babel-loader?presets[]=es2015&presets[]=react'],
exclude: /(node_modules|bower_components)/
}, {
test: /\.css$/,
loaders: ['style', 'css']
}, {
test: /\.scss$/,
loaders: ['style', 'css', 'postcss', 'sass']
}, {
test: /\.sass$/,
loader: 'style!css!sass?sourceMap'
},{
test: /\.less$/,
loaders: ['style', 'css', 'less']
}, {
test: /\.woff$/,
loader: "url-loader?limit=10000&mimetype=application/font-woff&name=[path][name].[ext]"
}, {
test: /\.woff2$/,
loader: "url-loader?limit=10000&mimetype=application/font-woff2&name=[path][name].[ext]"
}, {
test: /\.(eot|ttf|svg|gif|png)$/,
loader: "file-loader"
}]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '/js/bundle.js',
sourceMapFilename: '/js/bundle.map',
publicPath: '/'
},
plugins: [
new ExtractTextPlugin('style.css')
],
postcss: [
autoprefixer({
browsers: ['last 2 versions']
})
],
resolve: {
extensions: ['', '.js', '.sass'],
modulesDirectories: ['src', 'node_modules']
},
devServer: {
inline: true,
contentBase: './dist'
}
};
I could go and require('bootstrap') (with some way of getting jQuery in it to work), but.. I'm curious to what you all think and do.
Thanks in advance :)
I am not sure if this is the best way, but following work for me well with vue.js webapp. You can see the working code here.
I have included files required by bootstrap in index.html like this:
<head>
<meta charset="utf-8">
<title>Hey</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="/static/bootstrap.css" type="text/css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
<script href="/static/bootstrap.js"></script>
</head>
And this works, you can execute the repo. Why I went this way was I had to customise some config in bootstrap so I had to change the variables file and build the code of bootstrap which outputted me bootstrap.js and bootstrap.cssfiles, which I am using here.
There is an alternative way suggested here by using the npm package and a webpack customisation.
First install bootstrap in your project:
npm install bootstrap#4.0.0-alpha.5
And make sure you can use sass-loader in your components:
npm install sass-loader node-sass --save-dev
now go to your webpack config file and add a sassLoader object with the following:
sassLoader: {
includePaths: [
path.resolve(projectRoot, 'node_modules/bootstrap/scss/'),
],
},
projectRoot should just point to where you can navigate to node_packages from, in my case this is: path.resolve(__dirname, '../')
Now you can use bootstrap directly in your .vue files and webpack will compile it for you when you add the following:
<style lang="scss">
#import "bootstrap";
</style>
I highly recommend using bootstrap-loader. You add a config file (.bootstraprc in your root folder) where you can exclude the bootstrap elements you don't want and tell where your variables.scss and bootstrap.overrides.scss are. Define you SCSS variables, do your overrides, add your webpack entry and get on with your life.
I use webpack to build bootstrap directly from .less and .scss files. This allows customizing bootstrap by overriding the source in .less/.scss and still get all the benefits of webpack.
Your code is missing an entry point for any .css/.less/.scss files. You need to include an entry point for the compiled css files. For this entry point, I declare an array with const and then include paths inside the array to the source files I want webpack to compile.
Currently, I'm using bootstrap 3 with a base custom template and also a 2nd custom theme. The base template uses bootstrap .less file styling and it has specific source overrides written in .less files.
The 2nd custom theme uses .sass file styling and has similar overrides to bootstrap's base written in .scss files. So, I need to try to optimize all this styling for production (currently coming in about 400kb but that's a little heavy because we choose to avoid CDN's due to targeting use in China).
Below is a reference webpack.config.js which works to build from .less/.scss/.css files, and also does a few other things like build typescript modules and uses Babel for converting es6/typescript to browser compatible javascript. The output ultimately ends up in my /static/dist folder.
const path = require('path');
const webpack = require('webpack');
// plugins
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// take debug mode from the environment
const debug = (process.env.NODE_ENV !== 'production');
// Development asset host (webpack dev server)
const publicHost = debug ? 'http://localhost:9001' : '';
const rootAssetPath = path.join(__dirname, 'src');
const manifestOptions = {
publicPath: `${publicHost}/static/dist/`,
};
const babelLoader = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
'#babel/preset-env'
]
}
};
const app_css = [
// specific order generally matters
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'css', 'fonts', 'Roboto', 'css', 'fonts.css'),
path.join(__dirname, 'node_modules', 'font-awesome', 'css', 'font-awesome.css'),
// This is bootstrap 3.3.7 base styling writtin in .less
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'bootstrap.less'),
// bootstrap theme in .scss -> src\bp\folder\theme\src\scss\styles.scss
path.join(__dirname, 'src', 'bp', 'folder', 'theme', 'src', 'scss', 'styles.scss'),
// back to .less -> 'src/bootstrap-template1/assets/less/_main_full/core.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'core.less'),
// 'src/bootstrap-template1/assets/less/_main_full/components.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'components.less'),
//'src/bootstrap-template1/assets/less/_main_full/colors.less',
path.join(__dirname, 'src', 'bootstrap-template1', 'assets', 'less', '_main_full', 'colors.less'),
// <!-- syntax highlighting in .css --> src/bp/folder/static/css/pygments.css
path.join(__dirname, 'src', 'bp', 'folder', 'static', 'css', 'pygments.css'),
// <!-- lato/ptsans font we want to serve locally --> src/fonts/googlefonts.css'
path.join(__dirname, 'src', 'fonts', 'googlefonts.css'),
// a .css style -> 'src/bp/appbase/styles/md_table_generator.css'
path.join(__dirname, 'src', 'bp', 'appbase', 'styles', 'md_table_generator.css'),
// another .css style -> hopscotch 'src/libs/hopscotch/dist/css/hopscotch.min.css'
path.join(__dirname, 'src', 'libs', 'hopscotch', 'dist', 'css', 'hopscotch.min.css'),
//LAST final custom snippet styles to ensure they take priority 'src/css/style.css',
path.join(__dirname, 'src', 'css', 'style.css')
];
const vendor_js = [
//'core-js',
'whatwg-fetch',
];
const app_js = [
// a typescript file! :)
path.join(__dirname, 'src', 'typescript', 'libs', 'md-table', 'src', 'extension.ts'),
// base bootstrap 3.3.7 javascript
path.join(__dirname, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'),
path.join(__dirname, 'src', 'main', 'app.js'),
// src/bootstrap-template1/assets/js/plugins/forms/styling/uniform.min.js'
path.join(__dirname, 'node_modules', '#imanov', 'jquery.uniform', 'src', 'js', 'jquery.uniform.js'),
// src/bootstrap-template1/assets/js/plugins/ui/moment/moment.min.js'
];
function recursiveIssuer(m) {
if (m.issuer) {
return recursiveIssuer(m.issuer);
} else if (m.name) {
return m.name;
} else {
return false;
}
}
module.exports = {
context: process.cwd(), // to automatically find tsconfig.json
// context: __dirname,
entry: {
app_css,
vendor_js,
app_js,
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: `${publicHost}/static/dist/`,
chunkFilename: '[id].[hash:7].js',
filename: '[name].[hash:7].js'
},
resolve: {
extensions: [".webpack.js", ".web.js",".tsx", ".ts", ".js", ".css"],
alias: {
jquery$: path.resolve(__dirname, 'node_modules', 'jquery', 'dist', 'jquery.js'),
}
},
target: "web",
devtool: 'source-map',
devServer: {
// this devserver proxies all requests to my python development server except
// webpack compiled files in the `/static/dist` folder
clientLogLevel: 'warning',
contentBase: path.join(__dirname, './src'),
publicPath: 'dist',
open: true,
historyApiFallback: true,
stats: 'errors-only',
headers: {'Access-Control-Allow-Origin': '*'},
watchContentBase: true,
port: 9001,
proxy: {
'!(/dist/**/**.*)': {
target: 'http://127.0.0.1:8000',
},
},
},
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCssAssetsPlugin({})],
splitChunks: {
cacheGroups: {
appStyles: {
name: 'app',
// https://webpack.js.org/plugins/mini-css-extract-plugin/#extracting-css-based-on-entry
test: (m, c, entry = 'app') =>
m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true,
},
},
},
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery'
}),
// Strip all locales from moment.js except "zh-cn"
// ("en" is built into Moment and can’t be removed)
new MomentLocalesPlugin({
localesToKeep: ['zh-cn'],
}),
new ForkTsCheckerWebpackPlugin({
tslint: true, useTypescriptIncrementalApi: true
}),
new ForkTsCheckerNotifierWebpackPlugin({ title: 'TypeScript', excludeWarnings: false }),
new MiniCssExtractPlugin({
filename: '[name].[hash:7].css',
chunkFilename: '[id].[hash:7].css',
moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.[hash:7].css`
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: true
}),
new ManifestPlugin({
...manifestOptions
}),
].concat(debug ? [] : [
// production webpack plugins go here
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
}
}),
new ForkTsCheckerWebpackPlugin({
async: false,
useTypescriptIncrementalApi: true,
memoryLimit: 2048
}),
]),
module: {
rules: [
{
// jinja/nunjucks templates
test: /\.jinja2$/,
loader: 'jinja-loader',
query: {
root:'../templates'
}
},
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
use: [
babelLoader,
{
loader: 'ts-loader',
options:
{ // disable type checker - we will use it in
// fork-ts-checker-webpack-plugin
transpileOnly: true
}
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
babelLoader
]
},
{
test: /\.(html|jinja2)$/,
loader: 'raw-loader'
},
{
test: /\.(sc|sa|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
// only use if hmr is not working correctly
// reloadAll: true,
},
},
{
loader: "css-loader",
},
{
loader: "sass-loader"
},
]
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
// only use if hmr is not working correctly
// reloadAll: true,
},
},
{
loader: 'css-loader', // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
},
],
},
{
test: /\.(ttf|eot|svg|gif|ico)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[path][name].[hash:7].[ext]',
context: rootAssetPath
},
},
],
},
{
test: /\.(jpe?g|png)$/i,
loader: 'responsive-loader',
options: {
name: '[path][name].[hash:7].[ext]',
adapter: require('responsive-loader/sharp'),
context: rootAssetPath
}
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader:'url-loader',
options:{
limit: 10000,
mimetype: 'application/font-woff',
// name: ('fonts/[path][name].[hash:7].[ext]'),
name: ('fonts/[name].[hash:7].[ext]'),
}
},
{
test: require.resolve("jquery"),
use:[
{ loader: "expose-loader", options:"$" },
{ loader: "expose-loader", options:"jQuery" },
{ loader: "expose-loader", options:"jquery" }
]
}
]
},
};