How to load CSS files with webpack - javascript

Im trying to have my CSS file in the src folder copied into the dist folder on build - Im currently using a HTML file in my dist - ie im not using a javascript file so dont want the styles to be created as javascript.I just need the files to be copied into the dist. Heres my current config that isnt working.
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devServer:{
port:8000
},
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
{
test: /\.css$/i,
use: [ 'style-loader','css-loader'],
},
{
test: /\.(png|jpe?g|gif|glb|gltf|typeface.json)$/i,
loader: 'file-loader',
options: {
publicPath: './',
name: '[name].[ext]'
},
},
]
},
plugins: [
new htmlWebpackPlugin({
template: './src/index.html',
filename: './index.html'
})
]
}
I then use a link tag to add the css into the index.html file eg
<link rel="stylesheet" href="./style.css">
But this just adds javascript to the HTML file it builds in the dist.
How can i do this?
***EDIT: Im trying to add CSS files without merging them into my Javascript files

You can try using MiniCssExtractPlugin. As written in the documentation:
This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
To use it, you can first install it, import it, add a loader and a plugin to your webpack config. Here's an example (don't forget to note the order must be correct):
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
module: {
rules: [
...
{
test: /\.css$/i,
use: ['style-loader', MiniCssExtractPlugin.loader, 'css-loader'],
},
...
],
},
plugins: [
...
new MiniCssExtractPlugin(),
],
};
It will also add an automatic link tag to your css files in index.html results.

Related

PurgeCss remove necessary css with .ejs files

I'm using in my project bootstrap and webpack. I'm importing bootstrap library from source files, in this way I'm able to import only css and js component I need. However I want more advanced optimization for my css and I'm using purgecss too. Purgecss needs to point out pages that have html or injects html, into the content option of the plugin. I have this configuration in my
webpack.production.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
//prettier-ignore
plugins:[autoprefixer(),
purgecss({
content: [
"./src/**/index.ejs",
//"./dist/**/index.html",
//"./dist/**/*.js",
"./node_modules/bootstrap/js/src/popover.js",
"./src/**/*.js",
],
})],
},
},
},
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
autoprefixer(),
purgecss({
content: [
"./src/**/index.ejs",
//"./dist/**/index.html",
//"./dist/**/*.js",
"./node_modules/bootstrap/js/src/popover.js",
"./src/**/*.js",
],
}),
],
},
},
},
"sass-loader",
],
},
I'm using purgecss with postcss.
However the purgecss plugin seems doesn't recognize my index.ejs file and vital css is removed . If I cheat and I point out to my purgecss plugin the index.html file generated in the dist folder in the previous build it's all right. Then, purgecss can't handle .ejs files? Is there some workaround?
(Note that I have to indicate to the plugin the popover.js of the bootstrap library otherwise the css of the popover isn't included because it's a dynamic component not present in the html page but is added by popover.js)
(Note that my example is a minimal reproducible example to illustrate the problem)

mini-css-extract-plugin does not load css if it is not declared via dynamic import

I have very basic webpack + mini-css-extract-plugin project (you can found it here).
Here is webpack.config.js:
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
resolve: {
modules: [path.resolve(process.cwd(), 'node_modules')]
},
module: {
rules: [
// file loader allows to copy file to the build folder and form proper url
// usually images are used from css files, see css loader below
{
test: /\.png$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name: "_assets/[name].[ext]"
}
}
]
},
// css files are processed to copy any dependent resources like images
// then they copied to the build folder and inserted via link tag
{
test: /\.css$/i,
sideEffects: true,
exclude: /node_modules/,
// for tests we use simplified raw-loader for css files
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// public path has changed so url(...) inside css files use relative pathes
// like: url(../_assets/image.png) instead of absolute urls
publicPath: '../',
}
},
'css-loader'
]
}
]
},
plugins: [
// plugin helps to properly process css files which are imported from the source code
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '_assets/[name].css',
chunkFilename: '_assets/[id].css'
})
],
entry: {
'test': "./src/test"
},
output: {
path: path.resolve(process.cwd(), `public`),
publicPath: '',
filename: '[name].js',
chunkFilename: '_chunks/chunk.[name].js'
}
};
main entry file test.js:
import './css/testCss.css';
console.log('Loaded');
When i run webpack build i got the following output structure:
/
|-test.js
|-_assets/
| |-test.css
When i include this js bundle into html i would expect that test.js bundle will load test.css file dynamically but this is not the case - js bundle works ok, but css file is not loaded at all.
It is only loaded when i modify source of the test.js like so:
import('./css/testCss.css'); // <--------- NOTE: dynamic import here
console.log('Loaded');
in this case after webpack build i got the following output:
/
|-test.js
|-_assets/
| |-0.css
|-_chunks/
| |-chunk.0.js
and when i load this js bundle in html - it loads both chink.0.js and 0.css
MAIN QUESTION: Is dynamic import the only correct way to include css into my js files via mini-css-extract-plugin?
Because in documentation they say yo use normal static import like import "./test.css"
my environement info:
node version: v14.12.0
webpack version: 4.44.1 (also tested on 5.2.0)
mini-css-extract-plugin version 1.1.2

Sourcemap to original SCSS file

I'm using Webpack 4 with the plugins MiniCssExtractPlugin and OptimizeCSSAssetsPlugin to extract SCSS files and minify the main.css file in production. I have source mapping enabled in my config file like so:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
devtool: "source-map",
entry: {
style: './src/scss/main.scss'
},
output: {
path: path.resolve(__dirname, "dist"),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } }
]
},
{
test: /\.scss$/,
use: [
'style-loader',
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false, sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
sourceMap: true,
filename: "main.css"
}),
new UglifyJsPlugin({
sourceMap: true,
test: /\.js($|\?)/i,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
map: {
inline: true
}
}
}) ,
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
In the console, it does give the line where a CSS rules originates from, but in the bundled main.css file, rather than invididual scss files i.e. _layout.scss, _buttons.scss.
Question
What configuration do I add in order to display where the CSS rule originates from, rather then where it is located in the bundled main.css file?
As i can see on your sass-loader configuration, you're already getting the source maps for yous scss files (to verify that you need to have a file with the same name as you css, but with a .map on its name), so the only thing you will need is to peroperly configure your browser to load and use the source maps. This example uses Google Chrome:
Open dev tools
Click the gear icon or three dots to open settings (top right corner)
Under General (or Preferences), look for the “Sources” section. In that section, select “Enable CSS source maps”.
Make sure the accompanying “Auto-reload generated CSS” is also enabled (if available). This last step helps to reload the CSS when it changes. Think of it like a live reload feature for the CSS alone.
Navigate to your localhost server, inspect any element on your page.
In the developer tools, choose the Sources tab.
In the file tree on the left hand side, right-click your stylesheet and select “Map to file system resource…”. This will bring up the file search dialog. Select the appropriate file (your stylesheet). In some newer versions of Chrome, the file should be loaded automatically using the /*# sourceMappingURL=style.css.map */ reference
Restart the developer tools.
That should work. Let me know. Otherwise i will fine tune the quick tutorial.

HTML Webpack Plugin not creating correct index.html file

I am using HTMLWEBPACKPLUGIN to create an html file in the dist folder. I am using the template option of the plugin so that i can add a div in the html file for the root div where my react components will be injected.
Strangely, a hashed file is created with the correct code, and another file, index.html file is created with the name of the other file and the script junction to the bundle.js file instead of having one single html file
webpack.config.js File :
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "none",
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: __dirname + "/dist"
},
plugins: [new HtmlWebpackPlugin({template: './src/index.html'})],
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{
test: /\.tsx?$/,
loader: "ts-loader",
options:
{
configFile: 'tsconfig.client.json'
} ,
exclude: /node_modules/
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader" ,
exclude: /node_modules/
},
{
test: /\.html$/,
loader: "file-loader",
exclude: /node_modules/
},
]
},
};
index.html file :
31b0c63f79c4c085d45b3861fe75d263.html<script type="text/javascript" src="bundle.js"></script>
hashedFile.html :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
It's happening because you configured webpack to use file loader for html files.
The template plugin renders the file by importing it using a loader. By default, the loader returns the file contents. By using the file-loader, webpack thinks you want to obtain the url for the html file instead of it's content when the html webpack plugin renders index.html.
Maybe you can configure an include directory for the html plugin configuration for the source files, something like
{
test: /\.html$/,
loader: "file-loader",
include: path.resolve(__dirname, 'src')
}
You need to configure a name for your files: https://webpack.js.org/loaders/file-loader/#options
Check out 'name' in the table. It defaults to [hash].[ext]. If you want the name to remain the same as the actual file, set options: { name: '[name].[ext]' } in your file-loader webpack configuration.
Doc for setting name with a string: https://webpack.js.org/loaders/file-loader/#-string-

CSS images not copied to output folder in Webpack

I am using Webpack to bundle a number of js/css files in a site. I am bundling bootstrap.css and chosen.css as part of my bundles. In order to create the bundles, I have a main.js that I am using as an entry point to import all the other files that I will need. I am using file-loader to process font and image files and move them to the appropriate directories. I am using the ExtractTextPlugin with the css-loader and resolve-url-loader to create a separate css bundle from my js bundle.
My main.js is:
import 'bootstrap/dist/css/bootstrap.css';
import 'chosen-js/chosen.css';
import './datetimehelper.js';
import './deletelink.js';
import './dropdown.js';
My webpack.config.js is:
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './src/js/main.js',
output: {
filename: 'wwwroot/js/bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'images/[name].[ext]',
outputPath: 'wwwroot/'
}
}
]
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
outputPath: 'wwwroot/'
}
}
]
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader?url=false', 'resolve-url-loader'],
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: 'wwwroot/css/bundle.css'
})
]
};
With the above configuration, the font references in bootstrap.css are picked up, moved to the appropriate directory and the urls are fixed in the css bundle that is emitted. However, the images that are referenced in chosen.css are not being picked up. Can anyone tell me what I need to do to make the images work correctly? I've tried replacing file-loader with url-loader and no change. I've also tried importing the images in my main.js and they were moved, but the urls in the css bundle were not rewritten correctly.
Having path configured in output makes life a lot easier. That would serve as the base output folder and all other loaders/plugins can work relative to that. May be the files were copied but not to your intended directory. Please do take a look at WebpackBootstrap repo. The config copies as well as converts image paths properly.
I finally figured it out. In the rules, I had:
{
test: /\.css$/,
use: ExtractTextPlugin.extract('css-loader', 'resolve-url-loader')
}
Instead, it should be:
{
test: /\.css$/,
loader: ExtratTextPlugin.extract('css-loader', 'resolve-url-loader')
}
Not sure what the difference is between use and loader because I'm fairly new to Webpack, but in this case it makes all the difference.

Categories