I have created some custom components on top of vuetify components. In order to match my design I have overridden the vuetify sass variables by creating a .scss file in path src/scss/variables.scss and written custom .scss as well. This works absolutely fine but when I try to import the components in my storybook I am unable to see the overridden design.
// .storybook/main.js
const path = require("path");
module.exports = {
stories: [
// "../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
addons: [
"#storybook/addon-links",
{
name: "#storybook/addon-essentials",
options: {
docs: true
}
}
],
webpackFinal: async (config, { configType }) => {
config.resolve.alias["~storybook"] = path.resolve(__dirname);
config.resolve.alias["#"] = path.resolve(__dirname, "..", "src");
config.module.rules.push({
test: /\.s(c|a)ss$/,
use: [
"style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
implementation: require("sass")
prependData: "#import '#/scss/variables.scss';", // ISSUE IS HERE
sassOptions: {
indentedSyntax: false // Tried with both true and false
},
}
}
],
include: path.resolve(__dirname, "../")
});
return config;
}
};
Related
I write Vue2 app using Vuetify2. I need to add sass-loader to webpack as per docs: https://vuetifyjs.com/en/getting-started/installation/#webpack-install
So I need to edit webpack and want to do this using chainWebpack. According to docs I can do it in vue.config.js. The issue is that I don't know how to properly 'convert' regular webpack snippet to chaining. I need to chain this:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.s(c|a)ss$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
// Requires sass-loader#^7.0.0
options: {
implementation: require('sass'),
indentedSyntax: true // optional
},
// Requires >= sass-loader#^8.0.0
options: {
implementation: require('sass'),
sassOptions: {
indentedSyntax: true // optional
},
},
},
],
},
],
}
}
and this is my vue.config.js file where I tried to use chainWebpack (note that transpileDependencies was in this file before, I don't touch it and it must be here):
const { defineConfig } = require('#vue/cli-service')
module.exports = defineConfig({
transpileDependencies: [
'vuetify'
],
chainWebpack: config => {
config.module
.rule('sass')
.test(/\.s(c|a)ss$/)
.use('vue-style-loader','css-loader',)
.loader('sass-loader')
.tap(options => {
implementation: require('sass'),
sassOptions: {
indentedSyntax: true // optional
}
return options
})
}
})
However it doesn't work.
The existing configuration for compilation seems to have issues compiling some syntax used in sentry source code. The screenshot shows two places where the exception occurs.
Here is the configuration used
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const appDirectory = path.resolve(__dirname, '../')
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, 'index.web.js'),
path.resolve(appDirectory, 'src'),
path.resolve(appDirectory, 'prebundled'),
path.resolve(appDirectory, 'node_modules/react-native')
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager
presets: ['module:metro-react-native-babel-preset'],
// Re-write paths to import only the modules needed by the app
plugins: [
'react-native-web',
[
'module-resolver',
{
alias: {
'^react-native$': 'react-native-web'
}
}
]
]
}
}
}
// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
esModule: false
}
}
}
module.exports = {
entry: [
// load any web API polyfills
// path.resolve(appDirectory, 'polyfills-web.js'),
// your web-specific entry file
path.resolve(appDirectory, 'index.web.js')
],
output: {
filename: '[name].[contenthash].js',
publicPath: '/',
path: path.resolve(appDirectory, 'dist')
},
module: {
rules: [
babelLoaderConfiguration,
imageLoaderConfiguration,
{
test: /\.tsx?$/,
loader: 'ts-loader',
// exclude: /node_modules/,
options: {
configFile: 'tsconfig.web.json',
transpileOnly: true
}
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new webpack.IgnorePlugin({
resourceRegExp: /react-native-screens/
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, './index.html')
}),
new CopyPlugin({
patterns: [
{
context: __dirname,
from: './images/**/*',
to: '[path][name][ext]',
force: true
},
{ from: path.join(__dirname, './main.css'), to: './main.css' },
{
from: path.join(__dirname, './.well-known/apple-app-site-association'),
to: './.well-known/'
}
]
}),
new webpack.HotModuleReplacementPlugin()
],
resolve: {
alias: {
'react-native$': 'react-native-web',
'react-native-maps$': 'react-native-web-maps'
},
extensions: ['.web.tsx', '.web.ts', '.tsx', '.ts', '.web.js', '.js', '.json']
}
}
I tried implementing something that looks like showing in this documentation but it did not help.
https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/uploading/webpack/
You need to add #sentry/react-native to compile.
Update you webpack config like this:
...
const compileNodeModules = [
'#sentry/react-native'
]
const babelLoaderConfiguration = {
...
...compileNodeModules
}
In my vue.config.js file contains (ref: https://austingil.com/global-sass-vue-project/):
css: {
loaderOptions: {
sass: {
additionalData: `
#import "#/storybook-components/src/styles/utils/_variables.scss";
#import "#/storybook-components/src/styles/utils/_shadowMixins.scss";
`,
implementation: require('sass')
}
}
},
This allows me to use the sass variables within the vue components.
We have a central and shared storybook library for common components that was working perfectly, but now we share the variables it fails.
How can I share add the additionalData to the vue components in storybook? There is a vue.config file in the storybook project but I don't think it is being read...
The .storybook/main.js looks like (following the guides):
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = {
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, '../'),
});
// Return the altered config
return config;
},
typescript: {
check: false,
checkOptions: {},
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
],
"framework": "#storybook/vue"
};
So I assume the additionalData is meant to be added to the webpack final section, I just cannot see how?
As usual.. the rubber duck affect kicked in after posting the question. This was a very annoying one to resolve.
The following config worked for me, note the expansion of the rule for the sass-loader.
Additional note: webpack was fixed in the dev deps to "webpack":"^4.46.0"
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = {
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',
{
// Compiles Sass to CSS
loader: "sass-loader",
options: {
additionalData: `
#import "./src/styles/utils/_variables.scss";
#import "./src/styles/utils/_shadowMixins.scss";
`,
implementation: require('sass'),
},
},
],
include: path.resolve(__dirname, '../'),
});
// Return the altered config
return config;
},
typescript: {
check: false,
checkOptions: {},
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
],
"framework": "#storybook/vue"
};
I'm trying to create storybook on vue. My components written using sass. So, I made this in .storybook/main.js:
webpackFinal: (config) => {
config.module.rules.push({
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
})
return config
}
And the styles looks like this:
<style lang="sass" scoped>
button
background-color: red
</style>
So I'm getting this error when trying to compile:
SassError: Invalid CSS after "": expected 1 selector or at-rule, was "button"
on line 1 of C:\Code\testproj\src\components\UI\TestComponent.vue
And if I change my style to this:
<style lang="sass" scoped>
button {
background-color: red
}
</style>
All works, but that's not a sass syntax.
I was having this exact same issue and I was able to solve it. The issue is from the webpack config. If you're using SASS, your webpack.config.js file in your .storybook folder should look like this:
module.exports = ({ config }) => {
config.module.rules.push({
test: /\.sass$/,
use: [
require.resolve("vue-style-loader"),
require.resolve("css-loader"),
{
loader: require.resolve("sass-loader"),
options: {
sassOptions: {
indentedSyntax: true
}
}
}
],
});
return config;
};
And if you're using SCSS, then it should be like this:
module.exports = ({ config }) => {
config.module.rules.push({
test: /\.scss$/,
use: [
require.resolve("vue-style-loader"),
require.resolve("css-loader"),
require.resolve("sass-loader"),
],
});
return config;
};
I was able to figure this out while reading the Vue Loader Docs
The problem is that in your webpack config, you are telling webpack that only use sass-loader when de extension of your file was test: /.s[ac]ss$/i That is to say .sass or .scss. However the extension of your file is .vue, because you are using sass in the vue file of your component.
With that configuration, try to put your sass style in a .sass file and check if works
For anyone using React 17+ with Storybook 6.4.9+, I had a similar problem where the Storybook/webpack build was not including my SCSS files. This configuration in .storybook/main.js worked for me:
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
],
"framework": "#storybook/react",
webpackFinal: async (config) => {
// add SCSS support for CSS Modules
config.module.rules.push({
test: /\.scss$/,
use: [
require.resolve("style-loader"),
require.resolve("css-loader"),
require.resolve("sass-loader"),
],
});
return config;
}
}
I spend a lot of time to find better solution. It is may main.js config file for Storybook 6.4.9:
const path = require('path');
module.exports = {
"stories": [
"../src/**/*.stories.#(js|jsx|ts|tsx|mdx)"
],
"addons": [
"#storybook/addon-essentials",
"#storybook/addon-actions",
"#storybook/addon-controls",
"#storybook/addon-links",
{
name: '#storybook/preset-scss',
options: {
sassLoaderOptions: {
implementation: require('node-sass'), // ATTENTION: We need to use "node-sass" instead "sass/dart-sass"
sassOptions: {
indentedSyntax: true
},
},
}
},
"#storybook/addon-postcss",
],
"framework": "#storybook/vue",
features: {
babelModeV7: true,
},
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.
// ATTENTION: Need to preload "global.sass" style for all elements;
config.module.rules.map(rule => {
if (rule.test instanceof RegExp && rule.test.toString() === '/\\.s[ca]ss$/') {
rule.use.push({
loader: require.resolve('sass-resources-loader'),
options: {
resources: [
path.resolve(__dirname, '../src/styles/global.sass')
]
}
})
}
return rule
})
// ATTENTION: Need to compile "Pug" templates.
config.module.rules.push(
{
test: /\.pug$/,
oneOf: [
// this applies to `<template lang="pug">` in Vue components
{
resourceQuery: /^\?vue/,
use: ['pug-plain-loader']
},
// this applies to pug imports inside JavaScript
{
use: ['raw-loader', 'pug-plain-loader']
}
]
}
);
// Return the altered config
return config;
},
}
In my app I'm initializing a Vue app, which uses single file .vue components.
I use Webpack to bundle, and vue-loader + postcss-modules to generate scoped classes.
But for some reason I can't access the generated classes inside my components ($style object is empty). I'll explain the problem below and created this repo as an example.
My hello.vue component looks like this:
<template>
<div :class="$style.hello">
Hello World!
</div>
</template>
<script>
export default {
name: "hello",
created() {
console.log(this.$style); // <- empty object :(
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
hello.vue.json is generated as expected (CSS Modules mapping):
{"hello":"_hello_23p9g_17"}
Scoped styles are appended in the document head, and when using mini-css-extract-plugin it is bundled in app.css:
._hello_23p9g_17 {
background: lime;
}
Does anyone know what the problem is and possibly how to fix this?
Below my config files.
webpack.config.js (trimmed for readability)
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
entry: {
app: "./src/index.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build")
},
resolve: {
extensions: [".js", ".vue", ".json", ".css"],
alias: {
vue: "vue/dist/vue.esm.js"
}
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
},
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.css$/,
use: [
"vue-style-loader",
// MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
// modules: true,
importLoaders: 1
}
},
"postcss-loader"
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
}),
new VueLoaderPlugin()
]
};
postcss.config.js
module.exports = {
ident: "postcss",
plugins: {
"postcss-preset-env": { stage: 0 },
"postcss-modules": {}
}
};
EDIT:
FYI, setting modules: true in the css-loader options works in that it populates the $style object (see docs):
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
But in our app we use postcss-loader (as per docs) that takes care of all transformations including scoping. Enabling both modules: true and postcss-modules conflicts and breaks the classes/mapping (as expected).
In other words, I'm looking for a way to omit the modules: true option and enable css modules using postcss-modules instead.
Found a workaround: manually import the styles from the JSON file.
If anyone knows a better way please let me know :)
hello.vue.json
{"hello":"_hello_fgtjb_30"}
hello.vue
<template>
<div :class="style.hello">
Hello World!
</div>
</template>
<script>
import style from "./hello.vue.json";
export default {
name: "hello",
beforeCreate() {
this.style = style;
},
created() {
console.log(this.style);
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
This only works when using postcss-modules (loaded from config by postcss-loader in my case) instead of using modules: true:
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 1
}
},
"postcss-loader"
]
};
See this PR as a full example.