How can I load a url (path) of a picture or others in a file of your build (prod) with webpack - javascript

I have some questions about webpack and the interaction with the path of libraries.
This is my webpack-config.js:
var path = require('path'),
webpack = require("webpack"),
libPath = path.join(__dirname, 'lib'),
distPath = path.join(__dirname, 'dist'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
os = require('os'),
ImageminPlugin = require('imagemin-webpack-plugin').default,
OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'),
CopyWebpackPlugin = require('copy-webpack-plugin'),
webpackUglifyJsPlugin = require('webpack-uglify-js-plugin'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'); // To be deleted when webpack will accept the flag --progress in the devserver and not only in the command line
var cf = {
entry: path.join(libPath, 'index.js'),
output: {
path: path.join(distPath),
filename: 'bundle-[hash:6].js'
},
resolveLoader: { root: path.join(__dirname, 'node_modules') },
module: {
loaders: [
{
test: /\.html$/,
loader: 'file?name=lib/templates/[name]-[hash].html'
},
{
test: /\.scss$/,
loaders: ['style', 'css', 'sass', 'resolve-url', 'sass?sourceMap']
},
{
test: /\.css$/,
loaders: ["style-loader", "css-loader"]
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000&mimetype=application/font-woff&&name=lib/font/[name]-[hash].[ext]'
},
{
test: /\.jpe?g$|\.gif$|\.png$/,
loaders: ['file?hash=sha512&digest=hex&name=lib/img/[name]-[hash].[ext]',
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'] // I write for the moment lib/img because i have not the choice but i don't want write in the same folder that the dev env
},
{
test: /\.ttf$|\.eot$|\.wav$|\.svg$/,
loader: "file?name=lib/font/[name]-[hash].[ext]"
},
{
test: /\.json$/,
loaders: ["raw-loader", "file?name=lib/languages/[name]-[hash].[ext]"]
},
{
test: /\.js$/,
exclude: /(node_modules)/,
loaders: ['ng-annotate?add=true', 'babel']
}
]
},
devServer: {
port: 3001,
compress:true,
colors:true
},
plugins: [
// HtmlWebpackPlugin: Simplifies creation of HTML files to serve your webpack bundles : https://www.npmjs.com/package/html-webpack-plugin
new HtmlWebpackPlugin({
inject: true,
filename: 'index.html',
title: 'MY_CUSTOM_APP',
template: path.join(libPath, 'index.html')
}),
// Deduplication: find duplicate dependencies & prevents duplicate inclusion : https://github.com/webpack/docs/wiki/optimization#deduplication
new webpack.optimize.DedupePlugin(),
// OccurenceOrderPlugin: Assign the module and chunk ids by occurrence count. : https://webpack.github.io/docs/list-of-plugins.html#occurenceorderplugin
new webpack.optimize.OccurenceOrderPlugin(),
new OptimizeCssAssetsPlugin({
cssProcessor: require('cssnano'),
cssProcessorOptions: { discardComments: {removeAll: true } },
canPrint: true
}),
new ImageminPlugin({
disable: false,
optipng: {
optimizationLevel: 3
},
gifsicle: {
optimizationLevel: 1
},
jpegtran: {
progressive: false
},
svgo: {
},
pngquant: null, // pngquant is not run unless you pass options here
plugins: []
}),
// a faire uniquement en prod
new webpackUglifyJsPlugin ({
cacheFolder: path.resolve(__dirname, 'dist/cached_uglify/'),
sourceMap: false,
minimize: false,
compressor: {
warnings: false
}
}),
new ProgressBarPlugin({format: ' build [:bar] ' + (':percent') + ' (:elapsed seconds)'})
]
};
module.exports = cf;
This is my call of angular directive:
<complete-block
header-img="./lib/img/image.jpg"
body-title="{{ 'BODY_TITLE' | translate }}"
body-text="{{ 'LOREM_1' | translate }}"></complete-block>
My problem is about the url for the picture.
In my dist build ( production), I have only a root folder dist and then under it, I have the img folder.
In more, the picture has a hash so finally I have something like that: dist/img/mypicture-56567465456354354.png instead of lib/img/mypicture.png.
How can I load it with the hash in the directive and also if it's not the same path between the dev env and the prod env ?
Sorry if it's trivial, all of this is new for me and I didn't find a solution about it :/
Thank you ! :)
Edit 1 (07/01/2017) : My work is opensource, so you can find it on github here: https://github.com/kevincaradant/web-template-webpack
The picture / link is here :
.../lib/components/home/home.component.js
.../lib/components/home/home.html

You need to require the image in the js file. It will give you the path of the image to use in the code. eg :
var image = require('./lib/img/image.jpg'); // returns 'dist/img/mypicture-56567465456354354.png'
<complete-block
header-img="{{ image }}"
body-title="{{ 'BODY_TITLE' | translate }}"
body-text="{{ 'LOREM_1' | translate }}"></complete-block>

Related

Font urls path is wrong and includes css path after WebPack Build

I can't figure out the right combo here to make this all happy. I'm having MiniCssExtractPlugin create css or less files encountered.
I am importing a less and a css file in src/client/index.tsx:
import './less/master.less';
import '../../ext/ink-3.1.10/css/ink.min.css';
webpack.config.js is then doing its magic with MiniCssExtractPlugin:
Note: the reasons I have publicPath: '' for the css loader is because when I had it as publicPath: '/lib/css', it was appending that to the font url so by removing that the font-urls no longer had the issue at least after webpack created dist of the underlying problem I still see at runtime. Honestly I don't know if I even need to set publicPath here anyway because it defaults to "" anyway.
Changing it then to publicPath: '', stripped out the /lib/css part from /lib/css/lib/assets/fonts and I thought that fixed my issue but then during runtime it's still trying to hit fonts via /lib/css/lib/assets/fonts which baffles me
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const isProduction = process.env.NODE_ENV === 'production';
const html = () => {
return new HtmlWebPackPlugin({
template: path.resolve(__dirname, 'src/client', 'index.html'),
filename: 'index.html',
hash: true,
});
};
const copyAllOtherDistFiles = () => {
return new CopyPlugin({
patterns: [
{ from: 'src/client/assets', to: 'lib/assets' },
{ from: 'package.json', to: './' },
{ from: 'ext/ink-3.1.10/js/ink-all.min.js', to: 'lib/js' },
{ from: 'ext/ink-3.1.10/js/autoload.min.js', to: 'lib/js' },
{ from: 'ext/js/jquery-2.2.3.min.js', to: 'lib/js' },
{ from: 'feed.xml', to: './' },
{
from: 'src/shared',
to: './shared',
globOptions: {
ignore: ['**/*suppressed.json'],
},
},
],
});
};
module.exports = {
entry: './src/client/index.tsx',
output: {
filename: 'scripts/app.[hash].bundle.js',
publicPath: '',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
devtool: isProduction ? '' : 'eval-cheap-source-map',
devServer: {
writeToDisk: true,
contentBase: path.resolve(__dirname, 'dist'),
port: 8080,
},
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
},
},
},
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(tsx|ts)?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
},
],
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '',
},
},
'css-loader',
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: {
outputPath: 'lib/assets/fonts',
},
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['url-loader'],
},
],
},
plugins: [
new CleanWebpackPlugin(),
copyAllOtherDistFiles(),
new MiniCssExtractPlugin({
filename: isProduction ? 'lib/css/[name].[hash].css' : 'lib/css/main.css',
}),
html(),
],
};
api.js
const compression = require('compression'),
express = require('express'),
historyApi = require('connect-history-api-fallback'),
oneYear = 31536000;
module.exports = express()
.use(compression())
.on('error', (err: string) => {
console.log(err);
})
.use(historyApi())
.use(
express.static('dist', {
maxage: oneYear,
})
)
.use((_req: any, res: any) => {
res.send('Sorry, Page Not Found');
});
So the issue comes up during runtime with ink.min.css because it's referencing some fonts that I also processed with file-loader as you can see above. The problem is when I run the site in Dev mode via NODE_ENV=development webpack-dev-server --open I end up with a few requests for those fonts because it's appending /lib/css/lib/assets/fonts instead of just /lib/assets/fonts during runtime:
The weird thing about this though is when I go to my dist folder, when I look at styles.main.css (which apparently it renamed ink.min.css to this for some odd reason, the url to the fonts don't have that extra /lib/css so I don't understand why when my browser loads it's trying to reference it with that extra part still:
Another thing that's kinda weird is I do see images loading but when I look at the source in the browser, I don't see my assets folder. I know dist definitely contains it, but it doesn't show it here:
I don't understand why this fixed it. This entire thing feels hacky to me.
I changed it to publicPath: '../../'
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../../',
},
},
'css-loader',
],
I guess I'm loading my .css file and then I'm under the context of /lib/css already so my font urls have /lib/css I guess? and I need to for some reason have MiniCssExtractPlugin have a public path at the root? I don't get it.
Also now I see the assets folder, wt???

Webpack style-loader / css-loader: url() path resolution not working

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'
},

How to change path of bundled files inside the newly generated html file

See use-gulp-to-select-and-move-directories-and-their-files
, I changed my html file from dist folder to root, but I don't know how to change to path of bundled file inside the html file
I used follwing gulp task to move my html file
var filesToMove = [
'dist/index.html',
];
gulp.task('move', function(){
gulp.src(filesToMove, { base: './' })
.pipe(gulp.dest('./'));
});
This is the location where I want to change path of bundled file
<link rel="shortcut icon" href="favicon.ico"><link href="app.e6b85da4b9b36d023c5e.css" rel="stylesheet"></head>
And one more place
<script type="text/javascript" src="polyfills.e6b85da4b9b36d023c5e.js"></script><script type="text/javascript" src="app.e6b85da4b9b36d023c5e.js"></script><script type="text/javascript" src="ts-file.e6b85da4b9b36d023c5e.js"></script></body>
**This is my webpack file.Here i gave my dist file file kocation **
const webpack = require('webpack')
const webpackMerge = require('webpack-merge')
const CompressionPlugin = require('compression-webpack-plugin');
const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin')
const { AotPlugin } = require('#ngtools/webpack')
const commonConfig = require('./webpack.common.js')
const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin');
const helpers = require('./helpers')
const ENV = process.env.NODE_ENV = process.env.ENV = 'production'
module.exports = webpackMerge(commonConfig(), {
output: {
path: helpers.root('dist'),
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
plugins: [
new AotPlugin({
tsConfigPath: helpers.root('tsconfig-aot.json'),
entryModule: helpers.root('src', 'app', 'app.module#AppModule')
}),
new LoaderOptionsPlugin({
minimize: true,
debug: false,
options: {
htmlLoader: {
minimize: false
}
}
}),
new UglifyJsPlugin({
beautify: false,
output: {
comments: false
},
mangle: {
screw_ie8: true
},
compress: {
screw_ie8: true,
warnings: false,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
negate_iife: false
},
}),
// new CompressionPlugin({
// asset: '[path].gz[query]',
// algorithm: 'gzip',
// test: /\.js$|\.css$|\.html$/,
// threshold: 10240,
// minRatio: 0.8
// }),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
})
]
})
this is my common webpack file
const webpack = require('webpack')
const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const helpers = require('./helpers')
module.exports = () => {
const isProd = process.env.ENV === 'production'
return {
// Array of extensions that will be used to resolve modules
resolve: {
extensions: ['.ts', '.js', '.json'],
modules: [
helpers.root('src'),
helpers.root('node_modules')
],
},
// The entry point for the bundle
entry: {
'polyfills': helpers.root('src', 'polyfills.ts'),
'ts-file':[
helpers.root('src','app','common','samplelist.ts'),
helpers.root('src','app','common','shared.module.ts'),
helpers.root('src','app','app.module.ts')
],
'app': [
helpers.root('src', isProd ? 'main-aot.ts' : 'main-jit.ts'),
helpers.root('src', 'assets', 'styles', 'main.scss')
]
},
module: {
rules: [
// Compiles all .ts files
{
test: /\.ts$/,
use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader'],
exclude: /\.spec\.ts$/
},
// Injects all html templates into their components and loads referenced assets
{
test: /\.html$/,
use: 'html-loader',
exclude: helpers.root('src', 'index.html')
},
// Copies all images and fonts into dist/assets
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot)$/,
use: 'file-loader?name=assets/[name].[ext]'
},
// Puts all styles from assets/styles/main.scss in a separate file
{
test: /\.scss$/,
use: ExtractTextPlugin.extract(['css-loader', 'sass-loader']),
exclude: helpers.root('src', 'app')
},
// Injects all angular styles into their components
{
test: /\.scss$/,
use: ['raw-loader', 'sass-loader'],
include: helpers.root('src', 'app')
},
// To string and css loader support for *.css files (from Angular components)
{
test: /\.css$/,
use: ['to-string-loader', 'css-loader'],
include: helpers.root('node_modules')
},
// Loads all "required" json files into their components
{
test: /\.json$/,
use: 'json-loader'
}
]
},
plugins: [
// File name for the extracted styles
new ExtractTextPlugin(`[name]${isProd ? '.[hash]' : ''}.css`),
// Identifies common modules and puts them into a commons chunk
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'polyfills']
}),
// Provides context to Angular's use of System.import
// See: https://github.com/AngularClass/angular2-webpack-starter/issues/993#issuecomment-283423040
new ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)#angular/,
helpers.root('src')
),
// Generates a HTML5 file that includes all webpack bundles
new HtmlWebpackPlugin({
template: helpers.root('src', 'index.html'),
favicon: helpers.root('src', 'favicon.ico')
}),
// Copies all i18n files into dist/i18n
// new CopyWebpackPlugin([{
// from: helpers.root('src', 'i18n'),
// to: 'i18n'
// }]),
],
performance: {
hints: false
}
}
}

"Can't resolve 'syncfusion-javascript'" Webpack - Aurelia

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

webpack dev server does not show the content

I have the following problem when running the webpack dev server:
when I run npm start, it show the following:
➜ directory git:(staging) ✗ npm start
directory #1.0.0 start directory
BUILD_DEV=1 BUILD_STAGING=1 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js
http://localhost:8080/
webpack result is served from /undefined/
content is served from
directory
404s will fallback to /index.html
Hash: 75773622412153d5f921
Version: webpack 1.12.11
Time: 43330ms
I guess the problem might because the following line:
webpack result is served from /undefined/
When I open the browser at http://localhost:8080/, it appear as follow:
Cannot GET /
and there is no thing in the console.
Do you have any ideas for this problem ?
UPDATE: WEBPACK CONFIG FILE
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const nodeModulesDir = path.resolve(__dirname, 'node_modules');
const deps = [
'moment/min/moment.min.js',
'underscore/underscore-min.js',
];
/* Include SASS path here if you want to this in your sass files:
* #import 'bourbon';
*/
const bourbon = require('node-bourbon').includePaths;
const TARGET = process.env.npm_lifecycle_event;
const ROOT_PATH = path.resolve(__dirname);
const SASS_DEPS = [bourbon].toString();
const BUILD_NUMBER = process.env.CI_BUILD_NUMBER;
const common = {
entry: path.resolve(ROOT_PATH, 'app/index.js'),
output: {
filename: BUILD_NUMBER + '-[hash].js',
path: path.resolve(ROOT_PATH, 'build'),
publicPath: `/${BUILD_NUMBER}/`,
},
module: {
loaders: [
{
test: /\.scss$/,
loaders: ['style', 'css', 'sass?includePaths[]=' + SASS_DEPS],
include: path.resolve(ROOT_PATH, 'app'),
},
{
test: /\.css$/,
loaders: [
'style',
'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
'sass?includePaths[]=' + SASS_DEPS,
'postcss'
],
include: path.resolve(ROOT_PATH),
exclude: /(pure\/grids|Grids).*\.css$/,
},
{
test: /(pure\/grids|Grids).*\.css$/,
loaders: [
'style',
'css',
'sass?includePaths[]=' + SASS_DEPS,
],
include: path.resolve(ROOT_PATH),
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000&minetype=application/font-woff',
},
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
},
{
test: /\.json$/,
loader: 'json',
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
template: path.resolve(ROOT_PATH, 'app/index.html'),
inject: 'body',
minify: {
removeComments: true,
collapseWhitespace: true,
},
}),
new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'false')),
__STAGING__: JSON.stringify(JSON.parse(process.env.BUILD_STAGING || 'false')),
__API_HOST__: JSON.stringify(process.env.BUILD_STAGING ? 'my.api' : 'my.api'),
}),
],
resolve: {
alias: {
'styles': path.resolve(ROOT_PATH, 'app/styles'),
},
extensions: ['', '.js', '.jsx', '.json'],
},
postcss: function() {
return [
require('postcss-import'),
require('autoprefixer'),
require('postcss-cssnext'),
]
}
};
if (TARGET === 'start' || !TARGET) {
module.exports = merge(common, {
output: {
filename: '[hash].js',
path: path.resolve(ROOT_PATH, 'build'),
publicPath: '/',
},
devtool: 'eval-source-map',
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: [
'react-hot',
'babel-loader'
],
include: path.resolve(ROOT_PATH, 'app'),
},
],
},
devServer: {
colors: true,
historyApiFallback: true,
hot: true,
inline: true,
progress: true,
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
});
} else if (TARGET === 'build' || TARGET === 'builds') {
const config = {
resolve: {
alias: {},
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
include: path.resolve(ROOT_PATH, 'app'),
},
],
noParse: [],
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
minimize: true,
compressor: {
screw_ie8: true,
warnings: false,
},
compress: {
warnings: false,
},
output: {
comments: false,
},
}),
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env': { 'NODE_ENV': JSON.stringify(process.env.NODE_ENV) },
}),
],
};
deps.forEach((dep) => {
const depPath = path.resolve(nodeModulesDir, dep);
config.resolve.alias[dep.split(path.sep)[0]] = depPath;
config.module.noParse.push(depPath);
});
module.exports = merge(common, config);
}
Same problem occurred to me when i started using babel-loader > 6.
It was fixed by adding contentBase in webpack dev server configuration.
In my case it looked like this
new WebPackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
contentBase: __dirname + '/public'
}).listen(3000, 'localhost')
I would be great to see your webpack config file to pin point the exact problem, but from the error message, there could be multiple problem
Make sure you are on the right port
Make sure your webpack config has
path and public path setup
Make sure you have contentBase setup as
well
Without seeing your webpack file and more concrete detail, it is quite hard to pinpoint the issue. But you can always go to https://webpack.github.io/docs/webpack-dev-server.html for information on how to set it up.

Categories

Resources