I am trying to bootstrap a new react app and use the react-toolbox library and webpack. I am unable to get both react-toolbox's styles and my own app's styles to load.
The way I'm used to importing scss files is from the react files/views/components they go with, so they are located in the same folder. So if I have a react component file called header.js, in the same directory there is header.scss. Header.js calls import './header.scss'. In webpack, what I previously used to load scss was:
{
test: /\.s?css$/i,
loader: 'style!css!sass?' +
'includePaths[]=' + (path.resolve(__dirname, './node_modules')),
},
But when I include react-toolbox, this setup completely excludes react-toolbox's styles. I found this issue https://github.com/react-toolbox/react-toolbox/issues/121 where mattgi recommends this webpack-config:
{
test : /(\.scss|\.css)$/,
include : path.join(__dirname, '../../', 'src'),
loaders : [ 'style', 'css', 'sass' ]
},
{
test : /(\.scss|\.css)$/,
include : /(node_modules)\/react-toolbox/,
loaders : [
require.resolve('style-loader'),
require.resolve('css-loader') + '?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
require.resolve('sass-loader') + '?sourceMap',
]
},
This resolves the react-toolbox styles not loading, but then when I try to import my own scss files in a js file, webpack throws this error for the scss file: You may need an appropriate loader to handle this file type. (I have sass-loader installed).
In addition, if I include the scss file in the same directory with the same name (some-react-class.js and some-react-class.scss), the containing component of SomeReactClass that is importing some-react-class.js imports it as an object instead of a function which makes it seem like it is importing the scss instead of the js.
Help :(
Try to omit "include" property like this:
{
test: /(\.css|\.scss)$/,
loader: 'style!css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass?sourceMap'
}
This loader should include your *.scss and those from react-toolbox.
Seems hacky - but I did this to get it to work:
{
test: /\.s?css$/,
loaders: ['style', 'css', 'sass'],
exclude: /(node_modules)\/react-toolbox/
},
{
test : /(\.scss|\.css)$/,
include : /(node_modules)\/react-toolbox/,
loaders : [
require.resolve('style-loader'),
require.resolve('css-loader') + '?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
require.resolve('sass-loader') + '?sourceMap'
]
},
Bootstrap styles and react-toolbox styles work - but I'm having a hell of a time adding a file to override the default sass variables via toolbox-loader. Not sure if the issue is related to this hackiness...ugh headache
Related
I have a vue.js (version 2.4.4) application built with webpack (version 3.6.0) and vue-loader (version 13.0.5).
In the .vue files, I need to modify the url contained in the src attribute of the <img> tags according to the environment of my application.
In the development environment, the images must come from the
application folder, with a relative path: "/src/images/exemple.png"
In the production environment, the images must come from a cdn, with
an absolute path: "https://my-cdn.net/images/exemple.png"
In the "webpack.config.js" file, I already differentiate the different environments using "process.env.NODE_ENV", like this:
const isDev = process.env.NODE_ENV === 'dev';
But I don't know how to modify the src attribute of the img tags in my .vue files with vue-loader (or with something else).
For information, here is the definition of the vue-loader in the "webpack.config.js" file:
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
'scss': [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
}
}
Is there a simple way to do this?
Piggybacking off of #Michael Levy's answer:
I am currently having this issue with Vue 3, #vue/cli 4.5.10, and webpack. I've solved it after much research.
Webpack configurations go into vue.config.js, where there is a lot of abstraction. To fine tune control, you can use chain webpack configs. To help you, use Vue Inspect when you are trying to access specific loaders via chaining.
$ vue inspect > output.js
That will give you a nice list of all the loaders that vue-cli is using.
For example - to modify webpack image options within vue.config.js, you can use vue inspect > output.js, search the output.js file, and discover the loader that's managing images.
Which is: /* config.module.rule('images').use('url-loader') */
To answer the question - in your vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule("images")
.use("url-loader")
.tap((options) => {
options.name = "images/[name].[ext]";
options.publicPath = isDev ? __webpack_public_path__ : 'https://my-cdn.net/';
return options;
});
},
};
Vue-loader is preconfigured to handle src attributes in Vue single file components as you can see here. So for example <img src="../image.png"> in the template is transformed into:
createElement('img', {
attrs: {
src: require('../image.png') // this is now a module request
}
})
What Webpack do with this require depends on configured loaders. Usual there is a file-loader configured. It looks like this (from project generated by Vue CLI + simplified):
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
]
}
]
}
Loader is responsible for copying your file into dist directory and returning public URI, which will be inserted into src attribute.
So what you want can be configured here, by specifying right options. For example:
options: {
name: 'images/[name].[ext]'
publicPath: isDev ? __webpack_public_path__ : 'https://my-cdn.net/'
}
Just take content of dist/images directory after the build and deploy it so it is accessible by https://my-cdn.net/images and it should work....
You can create an alias for/src/images and alter the url at transpile time based on the environment:
{
//..
resolve: {
alias: {
'/src/images': isDev ? '/src/images' : process.env.IMAGE_CDN + '/images'
}
}
}
Another way to handle this would be to use DefinePlugin to create a global variable that each of your images reference.
module.exports = {
chainWebpack: config => {
console.log('\n')
console.log('Setting global variables:')
console.log(`__YOUR_GLOBAL_CONSTANT__: ${JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)}`)
console.log('\n')
config
.plugin('provide')
.use(require('webpack').DefinePlugin, [{
__YOUR_GLOBAL_CONSTANT__: JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)
}])
}
}
The example above is utilizing a vue.config.js file, but the strategy should be pretty similar. Also, if you're using something like eslint, you'll need to specify the variable in the globals section as readonly.
Webpack config:
For a .svg I use config:{ test: /\.svg$/, use: ['svgr/webpack'] }
For .scss I use css-loader, postcss-loader and sass-loader
Folder structure:
I have folder structure that looks like this:
- App
-- styles
--- globals.scss // Here I import my partials
--- partials
---- _my_partial.scss
-- icons
--- svgs
---- my_icon.svg
svgr loader:
I like svgr loader as it allows me to just import my icon and use it as React component:
import MyIcon from './icons/svgs/my_icon.svg';
...
<MyIcon />
The actual problem:
I was fine with this approach but I have to get one of the svgs as a background-image, so inside _my_partial.scss I wrote:
background-image: url(../icons/svgs/my_icon.svg);
I am up just one folder in this url as when being up two, it complained that it cannot resolve it - I guess this is because I import my partials in my globals.scss.
With this setup all I get in the browser is:
GET http://localhost:3005/[object%20Module] 404 (Not Found)
svgr/webpack turns your svg into react components, so when using svg into scss it's actually an object / react component. Change svgr/webpack to file-loader in order to use that. If you want to still use both, you could try something like:
{ test: /\.react.svg$/, use: ['svgr/webpack'] }
{ test: /\.svg$/, use: ['file-loader'] }
then rename all the svg's that you want as React components to filename.react.svg and the rest just leave with .svg.
I haven't tested this though :)
UPDATE: Looking at the documentation (section: Handle SVG in CSS, Sass or Less), it seems you can use svgr/webpack with file-loader:
https://github.com/smooth-code/svgr/tree/master/packages/webpack
{
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
issuer: {
test: /\.jsx?$/
},
use: ['babel-loader', '#svgr/webpack', 'url-loader']
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url-loader'
},
}
Either way, you probably need to make a few changes to fit in your needs but it supports it :)
I am using HTMLWebpackPlugin and in my template I have an img tag:
<img src="./images/logo/png">
If you notice, here I am using a relative path thinking webpack will trigger the file loader that's configured in my webpack.config.js file but after compilation I get the exact same src attribute in my html:
<img src="./images/logo/png">
How can I trigger webpack to dynamically replace these relative paths with, well whatever I've configured in my webpack configuration?
I'm not a webpack expert, but i got it to work by doing this:
<img src="<%=require('./src/assets/logo.png')%>">
Plugin config
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
}),
According to the docs: https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md
By default (if you don't specify any loader in any way) a fallback
lodash loader kicks in.
The <%= %> signifies a lodash template
Under the hood it is using a webpack child compilation which inherits
all loaders from your main configuration.
Calling require on your img path will then call the file loader.
You may run into some path issues, but it should work.
Using html-loader with HTML webpack plugin worked for me.
module: {
rules: [
{
test: /\.(html)$/,
use: ['html-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
You should use the CopyWebpackPlugin.
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins:[
....
new CopyWebpackPlugin({'patterns': [
{from:'./src/assets/images', to:'images'}
]}),
....
]
This is copy the src/assets/images to your `distfolder/images'.
You could use url-loader in your webpack config to add images below a certain limit encoded as base64 uri's in your final bundle and images above the limit as regular image tags (relative to the publicPath)
module.rules.push({
test: /\.(png|jp(e*)g|gif)$/,
exclude: /node_modules/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
publicPath: "/"
}
}]
})
I ran into this issue while following the Getting Started guide that Webpack provides. I was using the template code from the guide and bundling images. But then when migrating an existing vanilla html/js/css project to use Webpack, I discovered that in order to use the template HTML loading like I wanted -- with paths to image resource contained in the template -- I had to remove the asset loader usage from my webpack.config.js for the html-loader to properly resolve the new hashed paths it was creating in dist
To use Webpack doc syntax, remove the lines prefixed with "-" and add the lines prefixed with "+"
module: {
rules: [
{
- test: /\.(png|svg|jpg|jpeg|gif)$/i,
- type: 'asset/resource',
+ test: /\.(html)$/,
+ use: ['html-loader'],
}
]
}
I'm starting with Vue.js and Webpack and I'm having some doubts about how to correctly import and reference my fonts, CSS, and node_modules correctly.
I started my application using vue-cli, and here's the resultant structure:
build
config
node_modules
src
--assets
--components
--router
static
And here's my webpack.base.conf file:
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'#': resolve('src')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
First of all, where is the correct place to put my custom CSS and images? I'm currently putting them inside assets/css and assets/img, respectively (I created these folders). Is it correct?
I also have some CSS and fonts from external libraries (Bootstrap and Font Awesome, for example) that I have installed via NPM. They're located at node_modules.
If I'm not wrong, Webpack transforms, and copies these files to another location. How can I reference them on my Vue files and CSS files?
Using import './assets/css/style.css'import '../node_modules/path/to/bootstrap.min.css' works (at least in production), but should I be using another path?
Inside my custom CSS files, I reference some fonts from an external library using:
src: url('/node_modules/roboto-fontface/fonts/Roboto/Roboto-LightItalic.eot')
The code compiles, but when I open the page in the browser, I receive a 404 error when searching for these fonts. How should I be referencing these fonts in my custom CSS?
First of all, where is the correct place to put my custom css and images? I'm currently putting them inside assets/css and assets/img, respectively (I created these folders). Is it correct?
This is kind of a subjective question, but the short answer is yes.
The cli tool already created this for you, defined some stuff in the Webpack config files, so why not use it?
Using import './assets/css/style.css'import '../node_modules/path/to/bootstrap.min.css' works (at least in production), but should I be using another path?
Webpack embeds the css into the it's JS file, so if you don't import it Webpack will not know about it.
Here is an example with loading images dynamically
<ul id="albums">
<li v-for="album in albums">
<img :src="LoadImage(album.data.imagefile)" />
</li>
</ul>
if you'll just hand the src binding the artwork file it will fail to load it, so we hand the image file name to a method that goes like this
LoadImage(filename) {
const image = require('#/assets/img/' + filename)
return image
}
now inside the method we load the image from the assets folder ( using the # notation that was configured in the webpack.base.conf file under resolve.alias )
So yes, using the import/require functions are the way to go for Webpack to get to know your files.
Inside my custom css files, I reference some fonts from an external library using:
src: url('/node_modules/roboto-fontface/fonts/Roboto/Roboto-LightItalic.eot')
The code compiles, but when I open the page in the browser, I recieve a 404 Error when searching for these fonts. How should I be referencing these fonts on my custom css?
It's best that you'll copy everything you want in your dist folder in your src folder. I'm not really sure, never tried it, but looking at the webpack.prod.conf file it looks like it will only copy files from the src/assets folder.
Regarding the font not loading, this is a bit different since the url-loader will handle the files, so you have to think of it from the browser point of view, and reference it like a url path.
here is an something i have in one of my components
#font-face {
font-family: BebasNeue;
src: url('./assets/fonts/BebasNeue.otf');
}
See how i didn't used the # notation to reference it from the src folder? no need for urls.
I'm guessing you already answered this question and if not, Hope that helps!
You can ask questions here http://chat.vuejs.org/ and get answers from the community and the core team.
Quck solution
what i did comment #font-face {} section and build and added custom css to style file
So I have a SCSS file called style.scss. And I have an ES6 file called script.js. Both these files are in a ./src/ directory.
I want to use Webpack to take the style.scss, convert it to CSS and stick it into a folder called ./dist/ with the filename style.css. I can actually make that happen.
I also want Webpack to take the script.js, transpile to ES5, and put that in to the same ./dist/ folder with a file called script.js. I can also make that happen.
What I cannot do, is make both happen simultaneously. Is this something that Webpack can even do? In this instance I don't want all my styles putting into my JS, I specifically want them to be kept in separate files like a standard website (as that's what this is, really).
The closest I have got is this:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
script: './src/script.js',
style: './src/style.scss'
},
output: {
path: './dist',
filename: '[name].js'
},
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin('style.css')
] }
But this gives me a script.js file, a style.css file and ALSO a style.js file which is a mess.
Any suggestions?
You don't want to create a separate entry point for the CSS. You can either import the style.scss in your script.js or you can add it to the same entry. An entry can also be an array and webpack will include them in the same bundle, well the CSS is extracted anyway so there won't really be any additional JavaScript. Your entry would be as follows:
entry: {
script: ['./src/script.js', './src/style.scss']
},