webpack : css points to wrong image folder in prod - css

When I build my project for production I get this structure :
build/static/
js/bundle.js
css/bundle.css
media/ *all my images*
All the images are extracted fine and work except for the images extracted from the css like :
cursor: url(../assets/myImage.png)
It is copied to the right folder static/media/myImage.png
but the url in the css points to static/css/static/media/myImage.png
Here is my webpack config for prod :
entry: {
app: path.join(__dirname, 'src/App.js'),
vendor: ["react", "react-dom"],
},
output: {
path: "./build/",
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js'
},
module: {
loaders: [
{
test: /\.sass$/,
loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss!sass')
},
{
test: /\.(jpg|png|svg)(\?.*)?$/,
loader: 'file',
query: {
name: 'static/media/[name].[hash:8].[ext]'
}
},
]
},
plugins: [
new ExtractTextPlugin('static/css/[name].[contenthash:8].css',
{allChunks: true}, {publicPath: './'})
]
};
The css is extracted to static/css but when it extracts the images it nests the paths in the code so instead of pointing to static/media/ it gets nested and produces static/css + static/media
How can I get the correct path in my css ?
Thank you.

Related

How to generate javascript file, which can be loaded via script tag by a non nextjs web page

In my current project we are migrating an application to nextjs. Now I would like to inject a react component into a legacy page through including a script, which is using code from the new nextjs application. Her a simplified version of the script:
function injectCode() {
const container = document.getElementById("container")
const reactRoot = createRoot(container)
reactRoot.render(<ExistingComponent />)
}
document.addEventListener('load', injectCode)
How can I generate a js file, that can be included into a webpage not rendered by nextjs? Best would be to generate the file with next build, but directly running webpack would be ok as well.
Until now I tried to write my own webpack config and just run webpack directly. It would be ok if I run webpack once and check the generated file into git. Therefore the generated file is put into the public folder.
This is not the only webpack config I tried, but after too many iterations solving one problem and getting the next I gave up thinking there is probably an easier solution.
module.exports = {
entry: './script.tsx',
module: {
rules: [
{
test: /\.(ts|tsx)?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.(ts|tsx)?$/,
include: path.resolve(__dirname, 'lib'),
use: [
{
loader: 'ts-loader',
},
],
},
{
test: /\.(ts|tsx)?$/,
include: path.resolve(__dirname, 'components'),
use: [
{
loader: 'ts-loader',
},
],
},
{
test: /\.(ts|tsx)?$/,
include: path.resolve(__dirname, 'single-header-footer'),
use: [
{
loader: 'ts-loader',
},
],
},
{
test: /\.(js)?$/,
include: path.resolve(__dirname, 'node-modules'),
use: [
{
loader: 'ts-loader',
},
],
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
path: path.resolve(__dirname, 'public'),
filename: 'next-js-component.js',
},
}
I am probably missing a lot of settings which are required.
The second thing I tried was extending the webpack configuration within the next.config.js file:
...
webpack: (config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }) => {
const originalEntry = config.entry
config.entry = async () => {
const entries = await originalEntry()
entries['next-js-component'] = ['script.tsx']
return entries
}
return config
},
...
This creates a chunk file .next/static/chunks/next-js-component-dc0ae8d2d7a87ec9.js. But I don't know how to use this file. Probably I need to add some other code into the page which loads the created chunk and additional chunks.
The header and footer of the legacy pages are already rendered by the next.js app and are included into the page via server side include. So I might be able to use something like the HtmlWebpackPlugin to inject the correct script tag into the header, which is then included into the legacy page.

How to stop WebPack from including CSS file in the html even though it is already included in the produced js file

I am using WebPack to build a chrome extension. I import the css file in the popup.js file and it correctly styles my page but bundler also inserts the css file to the html which is unnecessary if I know correctly since it is injected to the output js file. Because the css file is included in the hmtl, I get a "Failed to load resource: net::ERR_FILE_NOT_FOUND". I want to know how to exclude the CSS file from the html and basically dont have the following line.
<link rel="stylesheet" href="popup-styles.css" />
This is my webpack.config.js file.
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: {
popup: "./src/popup.js",
background: "./src/background.js",
content: "./src/content.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
},
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/popup.html",
filename: "popup.html",
chunks: ["popup"],
}),
new CopyPlugin({
patterns: [{ from: "public" }],
}),
],
};

Multiple Tailwind CSS classes having multiple Webpack entry points

Problem statement
So I have a React project setup with webpack and tailwind CSS.
In my webpack config I have multiple entry point in order to generate different CSS and JS for each entry point.
The problem arises when I use the tailwind classes in my React components.
Let's suppose if I use a tailwind class bg-red-600 only in Component1(or entry point 1).
So after building the files through webpack the bg-red-600 will be present in all the entry point's generated CSS files(keep in mind I have just used this class in first entry point component only).
What it should be doing is only have bg-red-600 class in first component CSS file instead it is preset in all the CSS files even though I have not used it in any other place other than first component.
Hope I was able to made my point.
Thanks.
Webpack's entry points:
entry: {
app1: path.resolve(
__dirname,
'src/Component1'
),
app2: path.resolve(
__dirname,
'src/Component2'
),
},
Here is my solution:
/config folder with custom tailwind-xxx.config file for each entry js
eg. /config/tailwind-ConfirmButton.config.js:
module.exports = {
content: [
'./src/common/ConfirmButton/ConfirmButton.jsx',
],
// plugins: [require('#tailwindcss/forms')],
}
webpack.config.js
const postcssOpts = { postcssOptions: env => {
// here is the point
const component = env._module.context.slice(env._module.context.lastIndexOf('/') + 1)
return {
plugins: [
['tailwindcss', {
config: `./config/tailwind-${component}.config.js`,
}],
autoprefixer
]
}
}
}
...
entry: {
confirm: path.resolve(__dirname, './src/widgets/confirmButton.js'),
},
target: ['web', 'es5'], // <=== can be omitted as default is 'web'
output: {
filename: '[name]/tag.js',
path: path.resolve(__dirname, 'dist/exp'),
publicPath: './',
},
...
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: postcssOpts,
},
],
},
entry widget js
eg. /src/widgets/customButton.js:
...
render(
<ConfirmButton
expId={expId}
content={content}
confirmBtn={confirmBtn}
cancelBtn={cancelBtn}
field={field}
/>,
container
)
finally run weppack --mode=production

Webpack - Setting up relative path with mini-css-extract-plugin

In order to output my css file using mini-css-extract-plugin to a directory I did something like the following:
context: path.resolve(__dirname, "src"),
entry: {
"main": "./js/index.js"
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',
},
module: {
rules: [
{
test: /\.(png|jpg)/,
use: [
{
loader: "file-loader",
options: {
name: "images/[hash].[ext]"
}
},
]
},
{
test: /\.css$/,
use: ['MiniCss.loader', 'css-loader', 'postcss-loader']
},
plugins: [
new MiniCss({
filename: '[name].[hash].css',
})
]
}
and in css I have something like:
background: url(img/fold.svg) right 30% / 100% no-repeat;
Now the problem is that all images are referenced from css/dist instead of dist. I can solve the problem by setting a publicPath: '../' on MiniCss.loader, but then all images are referenced from .././images which works by virtue of relative path, but doesn't look "natural". Now my question is there a cleaner way to achieve this?

How to config the file path in webpack?

I'm new to webpack. I want to ask how can I custom a file path in webpack.
Here is the code I've tested:
(function () {
var webpack = require('webpack'),
path = require('path'),
BabiliPlugin = require('babili-webpack-plugin'),
ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
'site': ['./assets/js/site']
},
output: {
publicPath: '/assets/',
path: path.join(__dirname, 'wwwroot/assets/js'),
filename: '[name].js'
},
module: {
rules: [
{
test: /\.js$/i,
use: [{
loader: 'babel-loader',
query: {
presets: ['es2015', 'stage-1'],
compact: false
}
}]
},
{
test: /(\.(ttf|eot|svg)(\?[\s\S]+)?$)|(\.woff2?(\?v=\d+\.\d+\.\d+)?$)/,
use: [{
loader: 'file-loader',
options: {
name: '../fonts/[name].[ext]'
}
}]
},
{
test: /\.(png|jpe?g|bmp|gif|ico)/,
use: [{
loader: 'file-loader',
options: {
name: '../images/[name].[ext]'
}
}]
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract(['css-loader'])
}
]
},
plugins: [
new BabiliPlugin({}, { test: /\.min\.js$/ }),
new webpack.ProvidePlugin({
$: 'jquery'
}),
new ExtractTextPlugin('../css/bundle.css')
]
};
}());
I want to ask about these lines:
use: [{
loader: 'file-loader',
options: {
name: '../fonts/[name].[ext]'
}
}]
When I set the name starts with ../, it compiles exactly right except the url which is inside the file bundle.css, like this snapshot:
Then, I've tried to remove the characters ../ from the name, the fonts folder was created inside js folder (it's not my goal). But the url inside the file bundle.css folder is right (I want this path - /assets/fonts/...).
How can I edit it?
p/s: May you embed the two images to my question? it doesn't appear now. Thank you!
You may need to set a context in your loader's options. By default the current working directory is used.
I'm not sure what your src directory structure is, so I'm taking a guess.
use: [{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[ext]',
context: './src/assets/fonts/'
}
}]

Resources