Reputation: 390
I have a project set up that uses rspack with VueJS 3 and I would like to be able to use CSS modules within the single file components. However I am unable to get this to work and I don't understand why.
My rspack.config.js file is as follows:
const rspack = require('@rspack/core')
const refreshPlugin = require('@rspack/plugin-react-refresh')
const isDev = process.env.NODE_ENV === 'development'
const { VueLoaderPlugin } = require('vue-loader');
/**
* @type {import('@rspack/cli').Configuration}
*/
module.exports = {
context: __dirname,
entry: {
main: './src/index.ts',
},
devServer: {
historyApiFallback: true,
port: 8081,
},
resolve: {
extensions: ['.js','.ts','.vue','.json']
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
{
test: /\.svg$/,
//type: 'asset',
loader: 'svg-inline-loader'
},
{
test: /\.css$/,
use: [
{
loader: "vue-style-loader",
options: {
esModule: true
}
},
{
loader: "css-loader",
options: {
module: {
localIdentName: "[local]__[hash:base64:8]",
namedExport: false
}
}
}
],
type: "javascript/auto",
},
{
test: /\.(jsx?|tsx?)$/,
use: [
{
loader: 'builtin:swc-loader',
options: {
sourceMap: true,
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
development: isDev,
refresh: isDev,
},
},
},
env: {
targets: [
'chrome >= 87',
'edge >= 88',
'firefox >= 78',
'safari >= 14',
],
},
},
},
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new rspack.container.ModuleFederationPlugin({
name: 'ActiveComms',
filename: 'remoteEntry.js',
remotes: {
Account: 'Account@http://localhost:8080/remoteEntry.js',
//vite_host: 'vite_host@http://localhost:8084/assets/remoteEntry.js'
},
exposes: {
'./moduleB': './src/components/moduleB.vue',
'./NavbarLinks': './src/components/NavbarLinks.vue',
'./OrangeBoxComms': './src/components/OrangeBoxComms.vue'
},
shared: {
vue: { eager: true }
},
}),
new rspack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
new rspack.ProgressPlugin({}),
new rspack.HtmlRspackPlugin({
template: './src/index.html',
}),
isDev ? new refreshPlugin() : null,
].filter(Boolean),
}
My tsconfig.json is as follows:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [],
"paths": {
"@/*": ["src/*"]
},
"lib": ["ESNext", "DOM", "DOM.Iterable", "ScriptHost"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
My package.json is as follows:
{
"name": "rs-host",
"version": "1.0.0",
"scripts": {
"build": "NODE_ENV=production rspack build",
"build:dev": "NODE_ENV=development rspack build",
"build:start": "cd dist && rspack serve",
"start": "NODE_ENV=development rspack serve"
},
"devDependencies": {
"@rspack/cli": "0.5.4",
"@rspack/core": "0.5.4",
"@rspack/plugin-react-refresh": "0.5.4",
"@types/vue": "^2.0.0",
"autoprefixer": "^10.1.0",
"css-loader": "^7.1.1",
"react-refresh": "^0.14.0",
"style-loader": "^4.0.0",
"svg-inline-loader": "^0.8.2",
"vue-loader": "^16.8.1",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.6.14"
},
"dependencies": {
"vue": "^3.2.19"
}
}
And an example of one of my single file components is as follows:
<template>
<div :class="$style.contentArea">
<div v-if="tab == 'default'">
<div>Name: rs-host</div>
<div>Framework: vue3</div>
<div>Language: TypeScript</div>
<div>CSS: Empty CSS</div>
<h2>ActiveComm's Server</h2>
</div>
<ChannelManagement v-if="tab == 'ChannelManagement'" />
<FeatureSelections v-if="tab == 'FeatureSelections'" />
<RadioBridgeConfiguration v-if="tab == 'RadioBridgeConfiguration'" />
</div>
</template>
<script lang="ts">
import * as Vue from 'vue';
import WebUtil from './util/WebUtil';
import ChannelManagement from './pages/ChannelManagement.vue';
import FeatureSelections from './pages/FeatureSelections.vue';
import RadioBridgeConfiguration from './pages/RadioBridgeConfiguration.vue';
export default {
components: {
ChannelManagement,
FeatureSelections,
RadioBridgeConfiguration
},
created() {
let tabParam : string | null = WebUtil.getUrlParam('tab');
if(tabParam) {
this.tab = tabParam;
}
},
data() : {
tab : string
} {
return {
tab: 'default'
}
},
methods: {
},
computed: {
},
props: [
]
}
</script>
<style module lang="css">
#contentArea {
position: absolute;
left: 280px;
right: 0px;
top: 0px;
height: 100vh;
overflow-y: auto;
}
.contentArea {
position: absolute;
left: 280px;
right: 0px;
top: 0px;
height: 100vh;
overflow-y: auto;
}
</style>
When the div in the single file component is rendered, it has a class attribute with no value like so: <div class>
.
I'd like to know what I'm doing incorrectly so that I can have css modules working correctly.
Upvotes: 1
Views: 278
Reputation: 390
I'm not sure why what I had before wasn't working, but I finally got it to work as expected with the following rspack.config.js.
const rspack = require('@rspack/core')
const refreshPlugin = require('@rspack/plugin-react-refresh')
const isDev = process.env.NODE_ENV === 'development'
const { VueLoaderPlugin } = require('vue-loader');
/**
* @type {import('@rspack/cli').Configuration}
*/
module.exports = {
context: __dirname,
entry: {
main: './src/index.ts',
},
devServer: {
historyApiFallback: true,
port: 8081,
},
resolve: {
extensions: ['.js','.ts','.vue','.json']
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
{
test: /\.svg$/,
//type: 'asset',
loader: 'svg-inline-loader'
},
{
test: /\.module\.css$/i,
type: "css/module", // this is enabled by default for module.css, so you don't need to specify it
},
{
test: /\.css$/,
use: [
"vue-style-loader",
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[local]__[hash:base64:8]",
namedExport: false,
},
},
},
],
},
{
test: /\.(jsx?|tsx?)$/,
use: [
{
loader: 'builtin:swc-loader',
options: {
sourceMap: true,
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
development: isDev,
refresh: isDev,
},
},
},
env: {
targets: [
'chrome >= 87',
'edge >= 88',
'firefox >= 78',
'safari >= 14',
],
},
},
},
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new rspack.container.ModuleFederationPlugin({
name: 'ActiveComms',
filename: 'remoteEntry.js',
remotes: {
Account: 'Account@http://localhost:8080/remoteEntry.js',
//vite_host: 'vite_host@http://localhost:8084/assets/remoteEntry.js'
},
exposes: {
'./moduleB': './src/components/moduleB.vue',
'./NavbarLinks': './src/components/NavbarLinks.vue',
'./PinkBox': './src/components/PinkBox.vue',
'./OrangeBoxComms': './src/components/OrangeBox.vue'
},
shared: {
vue: { eager: true }
},
}),
new rspack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
new rspack.ProgressPlugin({}),
new rspack.HtmlRspackPlugin({
template: './src/index.html',
}),
isDev ? new refreshPlugin() : null,
].filter(Boolean),
}
The main difference in the new config is that the rule for .css is changed to:
{
test: /\.css$/,
use: [
"vue-style-loader",
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[local]__[hash:base64:8]",
namedExport: false,
},
},
},
],
},
Upvotes: 2