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

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???

Related

Webpack 5 not showing images

I have a vue 3 app where I set up webpack 5 eventually. As I see I eliminated all my issues now the only thing remains is that it does not load my svg-s and images when I run the webpack serve script in dev mode. How can I configure webpack so that it would bundle my svg-s from the scr/assets folder?
my webpack config:
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const env = process.env.NODE_ENV || 'development';
const mode = process.env.VUE_APP_MODE || 'not-fullstack';
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
alias: {
vue: '#vue/runtime-dom',
'#': path.join(__dirname, 'src'),
},
fallback: { crypto: false, stream: false },
},
module: {
rules: [
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
},
],
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[hash].[ext]',
},
type: 'asset/resource',
},
{
test: /\.svg$/i,
use: [
{
loader: 'url-loader',
options: {
encoding: false,
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, './public/index.html'),
}),
new VueLoaderPlugin(),
new CopyPlugin([
{ noErrorOnMissing: true, from: './src/assets', to: 'images' },
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(env),
'process.env.VUE_APP_MODE': JSON.stringify(mode),
}),
],
devServer: {
historyApiFallback: true,
compress: true,
port: 3000,
},
};
And I reference my svg-s in my Vue component like this:
<img src="../../assets/logo.svg" alt="logo" />
As you can see I tried with the copy-webpack-plugin, maybe I just configured it wrong.
Thank you for your help in advance!
The config in webpack5 changed a lot
{
loader: 'url-loader',
options: {
encoding: false,
},
This is not working anymore. The url-loader is even removed from the npm packages.
The current way is found in the documentation
https://webpack.js.org/guides/asset-management/#loading-images

split style[hash].css into dynamically loaded chunks

I am trying to split code of styles into smaller chunks due to the size of generated output. I am developing using react so i solved javascript loading with React.lazy().
Lets say that there is one big application with 50 different views you can open. Not all views are available to all users.
What i did in my router is:
...
const view = React.lazy(() => import("./views/view"));
...
...
<view />
...
What this did is divided entire application to 50 separate js files that are dynamically loaded which is good.
It however did not divide styles the same way. So lets look into one of the views:
import React, { Component } from "react";
import "../../scss/view.scss";
class view extends Component {
...
}
export default view;
So each view has its own scss files dedicated for this view, which makes them virtually standalone. The problem is that webpack is still producing only one big style[hash].css file.
Webpack config:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "index.html"
});
const extractTextPlugin = new ExtractTextPlugin({
filename: "css/style.[hash:8].css",
allChunks: true
});
module.exports = {
output: {
filename: "js/main.[hash:8].js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader"
},
{
test: /\.(png|jpg|gif|ico|svg|eot|ttf|woff|woff2)$/,
loader: "url-loader",
options: {
limit: 25000,
outputPath: "/assets/",
name: "[name].[hash:8].[ext]"
}
},
{
test: /\.(scss|sass|css)$/i,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{ loader: "css-loader" },
{ loader: "postcss-loader", options: { sourceMap: true } },
{ loader: "sass-loader", options: { sourceMap: true } }
]
})
}
]
},
resolve: {
extensions: [".js", ".jsx", ".json"]
},
plugins: [htmlPlugin, extractTextPlugin],
devServer: {
historyApiFallback: {
rewrites: [{ from: /^\/$/, to: "/index.html" }]
}
}
};
Is there any way to make styles also produced and loaded dynamically respective to their js file, so for example if i open view 10 it loads 10.main[hash].js and 10.style[hash].css ?
Answer to this problem is to replace deprecated extract-text-webpack-plugin package that is actually missing this functionality (splitting styles into respective chunks).
Instead mini-css-extract-plugin should be used and it automatically works.
updated working webpack config file:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "index.html",
});
const miniCssExtractPlugin = new MiniCssExtractPlugin({
filename: "css/style.[hash:8].css",
chunkFilename: "css/[id].style.[hash:8].css",
});
module.exports = {
output: {
filename: "js/main.[hash:8].js",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.(png|jpg|jpeg|gif|ico|svg|eot|ttf|woff|woff2)$/,
loader: "url-loader",
options: {
limit: 25000,
outputPath: "/assets/",
name: "[name].[hash:8].[ext]",
},
},
{
test: /\.(scss|sass|css)$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
],
},
resolve: {
extensions: [".js", ".jsx", ".json"],
},
plugins: [htmlPlugin, miniCssExtractPlugin],
devServer: {
historyApiFallback: {
rewrites: [{ from: /^\/$/, to: "/index.html" }],
},
},
};

How to ignore error js script tag from html on running webpack with HtmlWebpackPlugin?

How can i ignore the script tag in Html webpack plugin?
Because I have added this
<script src="cordova.js"/>
tag into my index.html template anonymously for my app development.
See my configuration in Webpack.config.js:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "www"),
filename: "./js/build/[name].build.js",
chunkFilename: "./js/build/[name].build.js",
// publicPath: "./js/build/",
},
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./",
},
},
"css-loader?url=false",
"sass-loader",
],
},
{
test: /\.html$/,
use: ["html-loader"],
},
{
test: /\.(svg|png|jpeg|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "res",
},
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "./css/build/[name].css",
// chunkFilename: "../css/[id].css"
}),
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
// new BundleAnalyzerPlugin()
],
// devtool: "inline-source-map",
};
I just wanted to ignore the script on production and I have researched this many times but unfortunately I don't see any solution
So, if I got the problem right, you want to ignore a certain resource when bundling for production.
The html-loader has an option to filter sources based on attribute, attribute value, context file and anything you have available in you webpack.config.js.
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// Assuming you are passing arguments to your webpack config like argv.mode
const PRODUCTION = true;
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "www"),
filename: "./js/build/[name].build.js",
chunkFilename: "./js/build/[name].build.js",
// publicPath: "./js/build/",
},
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./",
},
},
"css-loader?url=false",
"sass-loader",
],
},
{
test: /\.html$/,
use: {
loader: "html-loader",
options: {
sources: {
urlFilter: (attribute, value, resourcePath) => {
if (PRODUCTION && /cordova.js$/.test(value)) {
return false;
}
return true;
},
}
},
},
},
{
test: /\.(svg|png|jpeg|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "res",
},
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "./css/build/[name].css",
// chunkFilename: "../css/[id].css"
}),
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
// new BundleAnalyzerPlugin()
],
// devtool: "inline-source-map",
};
In the html module rule, I added an options property to html-loader with an example how you could ignore a certain resource with urlFilter. Check the html-loader docs on filtering resources
You will need to get a reference to the variables you passed to your cli like --mode eg. line 5-6. (Not in the current example) See webpack docs on env vars.

Webpack recompiles CSS but does not update

Using webpack 4.1.1 and webpack-dev-server I have the following webpack.dev.js file
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const src = path.resolve(__dirname, "../");
const build = path.resolve(__dirname, "../../build");
const components = path.resolve(__dirname, "../components");
const icons = path.resolve(__dirname, "../static/icons");
const images = path.resolve(__dirname, "../static/images");
const context = path.resolve(__dirname, 'src');
module.exports = {
mode: 'development',
entry: `${src}/index.js`,
output: {
filename: "bundle.js",
path: build
},
resolve: {
alias: {
components: components,
icons: icons,
images: images
}
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
path: build
}),
new HtmlWebpackPlugin({
template: './src/index.html'
}),
],
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/,
query: {
plugins: [
"transform-react-jsx",
[
"react-css-modules",
{
context
}
]
]
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader:'css-loader',
options:{
modules: true
}
}
]},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
},
},
],
},
{
test: /\.svg/,
use: {
loader: 'svg-url-loader',
options: {}
}
}
]
},
};
My issue is that when I update a CSS file, i watch the terminal, it says it compiles successfully, however I don't see the changes in the browser. Even when I navigate directly do localhost:8080/main.css, the old CSS file is still shown. The only way I can get the CSS to successfully recompile is to stop the dev server and start it all over again.
FWIW, here is my startup script:
$ ./node_modules/.bin/webpack-dev-server --config ./src/config/webpack.dev.js --content-base build/ --content-base src/static
Can someone tell me what I'm missing here?

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

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>

Categories

Resources