Is it possible to just compile SCSS to CSS in vue app? - vue.js

I'm trying to use some dynamic styling in my Vue.JS application. The idea is to build SCSS themes to static CSS and use it as in my template to switch themes.
Since I'm using bulma as css framework all my styles look the same as:
theme-1.scss
$primary: wheat;
#import 'bulma/bulma';
The current solution is just a bootstrapped vue application with default webpack config. The only time I was able to build scss to css and use it further was the following config:
const path = require('path');
module.exports = {
mode: 'development',
entry: {
functions: [
'./src/index.js',
'./src/scss/theme1.scss',
'./src/scss/theme2.scss'
],
},
output: {
path: path.resolve(__dirname, 'src'),
filename: 'dist/js/[name].js',
},
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'file-loader',
options: {
name: 'dist/css/[name].css',
}
},
{
loader: 'sass-loader'
}
]
}
]
},
};
Unfortunately I can't use multiple entries in vue app. And it also seems to me is not the right way to work with single page application
Since i'm absolutely new to webpack and vue-loader I think that I do everything wrong and think in the wrong way. Maybe some plugin exists which do definitely the same thing as I want to?

Here is the solution for this issue:
in my vue.config.js I made the following:
module.exports = {
chainWebpack: config => {
config.entry('theme') // you can add here as much themes as you want
.add('./src/theme.scss')
.end();
},
css: {
extract: {
filename: '[name].css', // to have a name related to a theme
chunkFilename: 'css/[name].css'
},
modules: false,
sourceMap: true
},
}

I've typically not used file-loader in my CSS setup; instead I've always used a style-loader, css-loader with MiniCSSExtractPlugin:
https://github.com/webpack-contrib/mini-css-extract-plugin#advanced-configuration-example
Give that setup configuration for MiniCSSExtractPlugin a review.
MiniCSSExtractPlugin is a nice to have, so the most important thing to note is that you are missing the css-loader (which should be used instead of file-loader).

Related

Vue Storybook doesn't compile with Pug components

I am trying to add storybook to my project, which written using pug. Unfortunately, storybook stops to compile if I create story with component on pug. If I change template lang to html (and template itself), all works just fine.
I had this same issue last night when adding storybook to an existing Vue/Pug project.
Storybook supports extending it's webpack config.
First open the file .storybook/main.js in your project directory.
Here you can add a function for webpackFinal to the object, which received the webpack config as an argument, and u can push additional loaders into the rules.
More info here in the storybook docs:
https://storybook.js.org/docs/vue/configure/webpack
Here is my file where i have added pug and sass support:
const path = require('path');
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
],
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Make whatever fine-grained changes you need
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
config.module.rules.push(
{
test: /\.pug$/,
use: [
{ loader: 'pug-plain-loader' }
]
}
);
// Return the altered config
return config;
},
}

Webpack Vue component with css <style> tags fails to build with Module parse failed: Unexpected token

Starting with a clean vue project, I was having issues building .vue components from PrimeVue.
These are ready made components and should really not fail to build.
Everytime I try to build, it fails to do so, and seems to fail with the line pointer at the start of the CSS styles.
ERROR in ./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css& (./node_modules/vue-loader/lib??vue-loader-options!./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css&) 340:0
Module parse failed: Unexpected token (340:0)
File was processed with these loaders:
* ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
|
> .p-slider {
| position: relative;
| }
# ./node_modules/primevue/components/slider/Slider.vue?vue&type=style&index=0&lang=css& 1:0-119 1:135-138 1:140-256 1:140-256
# ./node_modules/primevue/components/slider/Slider.vue
# ./node_modules/primevue/slider.js
# ./myproject/components/Test.js
# ./myproject/components/App.js
# ./myproject/main.js
This is my webpack config file:
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
mode: 'development',
entry: 'main.js',
output: {
filename: 'main.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
};
What is causing this error, as I am importing the components correctly as stated by the PrimeVue documentation.
Setting a rule in the webpack config to send the .vue files for processing to vue-loader is not enough.
You need to specify how to handle .css files too, and this then gets applied to tags in a .vue file as well. Without this rule, it will not know what to do with the <style> blocks, even if you dont plan on using the .css file part of this.
Update the rules section of the webpack.config.js with the following:
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
},
// this will apply to both plain `.css` files
// AND `<style>` blocks in `.vue` files
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
Also make sure that vue-style-loader and css-loader are installed in package.json.
More information can be found at the manual installation section of the vue-loader documentation, specifically the code example under 'A more complete example webpack config will look like this.'
I would highly recommend to cache the .vue files because they will slow down your build time in big projects.
// snippet from https://github.com/unic/darvin-webpack-boilerplate/blob/master/webpack/settings/javascript-vue/index.js
const {VueLoaderPlugin} = require('vue-loader');
const webpack = require('webpack');
const path = require('path');
const ROOT_PATH = process.cwd();
const CACHE_PATH = path.join(ROOT_PATH, 'tmp/cache');
const VUE_VERSION = require('vue/package.json').version;
const VUE_LOADER_VERSION = require('vue-loader/package.json').version;
const dev = {
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
cacheDirectory: path.join(CACHE_PATH, 'vue-loader'),
cacheIdentifier: [
process.env.NODE_ENV || 'development',
webpack.version,
VUE_VERSION,
VUE_LOADER_VERSION,
].join('|'),
},
}
]
},
plugins: [
new VueLoaderPlugin(),
],
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json'],
},
};

Is there a way to disable filenameHashing only for specific resources (images) in webpack?

After building my website with Vue.js 2.6.2 using vue-cli, I encountered a problem with static resources (images in this case). Webpack bundles them up in the /img/ folder which is fine, but the images are given hashes like image_demo.25d62d92.png which is causing issues when trying to access those resources from an external source e.g. from another website.
There is an option for webpack to disable filenameHashing altogether, but that too great a sacrifice to not have the cache control for all the orher images on the website.
The desired solution is the option to have only some hand picked resources with their default names without the extra hash, while the other resources get the default hash for cache control.
Yes, there is a way. You will need to override the 'images' rule that vue-cli comes with.
vue inspect --rule images yields the following:
$ vue inspect --rule images
/* config.module.rule('images') */
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
]
}
(Protip: vue inspect is a useful tool for figuring out why things behave like they do when building with vue-cli)
All images
vue-cli recommends webpack-chain for 'advanced' configuration. I'm personally not a fan, but if you want to remove hashes for all images, you should probably modify the 'images' rule. Edit vue.config.js like so:
module.exports = {
// ...
chainWebpack: (config) => {
config.module
.rule("images")
.use("url-loader")
.loader("url-loader")
.tap((options) => {
options.fallback.options.name = "img/[name].[ext]"
return options
})
}
// ...
}
Specific images
In my case I wanted to remove hashes only for a specific group of images with a unique prefix, so I added the following to configureWebpack in vue.config.js:
module.exports = {
// ...
configureWebpack: {
module: {
rules: [
{
test: /unique-prefix-\d*\.png/, // <= modify this to suit your needs
use: [
{
loader: "url-loader",
options: {
limit: 4096,
fallback: {
loader: "file-loader",
options: {
name: "img/[name].[ext]", // <= note how the hash is removed
},
},
},
},
],
},
],
},
// ...
}
It could probably be done with webpack-chain as well, but I prefer the more vanilla Webpack config format.

How can I reference js file from node_modules folder in page head in nuxt.js

I have added package onesignal-emoji-picker to my nuxt.js vue project and I want to reference css and js files in my pages without copying them to static folder. css is included with ease in nuxt.config.js:
css: ['~/node_modules/onesignal-emoji-picker/lib/css/emoji.css']
but I could not manage to reference js files, head.script section seems to work only for external resources:
script: [
{
src: 'https://code.jquery.com/jquery-3.3.1.slim.min.js',
type: 'text/javascript'
},
{
src: '~/node_modules/onesignal-emoji-picker/lib/js/config.js',
type: 'text/javascript'
},
{
src: '~/node_modules/onesignal-emoji-picker/lib/js/util.js',
type: 'text/javascript'
},
{
src: '~/node_modules/onesignal-emoji-picker/lib/js/jquery.emojiarea.js',
type: 'text/javascript'
},
{
src: '~/node_modules/onesignal-emoji-picker/lib/js/emoji-picker.js',
type: 'text/javascript'
}
]
It seems to me that I should somehow tell webpack to include this files on build and reference them appropriately? How could I do it? Thanks!
In nuxt.config.js, simply use the plugins key for including any internal scripts:
plugins: [
{ src: "~/node_modules/onesignal-emoji-picker/lib/js/config.js", mode: "client" },
{ src: "~/node_modules/onesignal-emoji-picker/lib/js/util.js", mode: "client" },
{ src: "~/node_modules/onesignal-emoji-picker/lib/js/jquery.emojiarea.js", mode: "client" },
{ src: "~/node_modules/onesignal-emoji-picker/lib/js/emoji-picker.js", mode: "client" }
]
Note the mode: "client" option which means the files will only be run on the client, preventing issues on the server if the library is not compatible with server-side-rendering. You may want to remove the flag in other cases.
Edit the file ./server/index.js and add the express static route just before this line app.use(nuxt.render).
The final start() function should be like this
async function start () {
...
app.use("/node_modules", express.static(path.join(__dirname, '/../node_modules')))
// Give nuxt middleware to express
app.use(nuxt.render)
....
}
After this change you can add node_modules libraries inside the vue page header.
export default {
head:{
script: [
{ src: "/node_modules/three/build/three.min.js", type: 'text/javascript', charset: 'utf-8'}
]
},
}
try to use nodes path library. I think it's one of the nuxt dependency so it should be already installed, if not just install it.
https://www.npmjs.com/package/path
Then import at the top of your nuxt.config.js
const path = require('path')
....
css: [path.resolve(__dirname, 'node_modules/onesignal-emoji-picker/lib/css/emoji.css')]
same thing with scripts

How to conditionally apply different webpack loaders, depending on the source entry?

I have a React app built with webpack, I want to ignore css module loading on some conditions.
I have 2 configurations for webpack:
I need to load all css modules for the browser build (not a problem for now)
I need to load css modules for the server only when it's not the Server Side Rendering (SSR) app tree
Here is some pseudo code for illustration (btw the app is an OSS):
// webpack.browser.js (standard build)
{
entry: './client.jsx',
module: { loaders: [ { test: /\.scss$/, loaders: ['style', 'css', 'sass'] } ] },
}
// client.jsx
import App from './src/App.jsx';
// ...App includes styles
// webpack.server.js
{
entry: './server.js',
module: { loaders: [
{
test: /\.scss$/,
loaders: [
// may be conditionally load scss depending on which express middleware the app is builded
]
}
] },
}
// server.js
// ...
import App from './src/App.jsx';
import LandingPage from './src/LandingPage.jsx';
app.use('/app', function ssrMiddleware(req, res) {
// How to not load scss modules ?
const html = ReactDOMServer.renderToStaticMarkup(React.createElement(App));
res.send(html);
});
app.use('/', function websiteMiddleware(req, res) {
// How to load scss modules ?
const html = ReactDOMServer.renderToStaticMarkup(React.createElement(LandingPage));
res.send(html);
});
I'm aware about pithing loader, but I don't understand how it can solves my case.