Skip to content

Commit 9c0c4ac

Browse files
chore: test rspack
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
1 parent 857cd56 commit 9c0c4ac

8 files changed

Lines changed: 3308 additions & 6643 deletions

File tree

.github/workflows/node-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
- '**.vue'
5454
5555
test:
56-
runs-on: ubuntu-latest
56+
runs-on: ubuntu-latest-low
5757

5858
needs: changes
5959
if: needs.changes.outputs.src != 'false'

.github/workflows/package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on: pull_request
55

66
jobs:
77
release-tarball:
8-
runs-on: ubuntu-latest
8+
runs-on: ubuntu-latest-low
99
name: Build release tarball
1010
steps:
1111
- name: Checkout

package-lock.json

Lines changed: 3043 additions & 6543 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,23 @@
1919
"license": "agpl",
2020
"private": true,
2121
"scripts": {
22-
"build": "webpack --node-env production --progress",
23-
"dev": "webpack --node-env development --progress",
24-
"watch": "webpack --node-env development --progress --watch",
22+
"analyze": "RSDOCTOR=true rspack build --env production",
23+
"analyze:stats": "rspack build --env production --json js/stats.json",
24+
"build": "rspack build --env production",
25+
"dev": "rspack build --env development",
2526
"lint": "eslint --ext .js,.vue,.ts src",
2627
"lint:fix": "eslint --ext .js,.vue,.ts src --fix",
28+
"serve": "rspack dev --env development",
2729
"stylelint": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\"",
2830
"stylelint:fix": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\" --fix",
29-
"test:unit": "vitest --run",
30-
"test:unit:watch": "vitest --watch",
31-
"test:unit:coverage": "vitest --run --coverage",
31+
"test:unit": "LANG=C vitest --run",
32+
"test:unit:watch": "LANG=C vitest --watch",
33+
"test:unit:coverage": "LANG=C vitest run --coverage",
3234
"test:e2e": "playwright test",
3335
"test:e2e:ui": "playwright test --ui",
34-
"type-check": "tsc --noEmit"
36+
"ts:check": "tsc --noEmit",
37+
"type-check": "npm run ts:check",
38+
"watch": "rspack build --env development --watch"
3539
},
3640
"dependencies": {
3741
"@fullcalendar/core": "6.1.20",
@@ -84,26 +88,25 @@
8488
"npm": "^11.3.0"
8589
},
8690
"devDependencies": {
87-
"@babel/core": "^7.29.0",
88-
"@babel/preset-env": "^7.29.2",
89-
"@babel/preset-typescript": "^7.28.5",
90-
"@nextcloud/babel-config": "^1.3.0",
9191
"@nextcloud/browserslist-config": "^3.1.2",
9292
"@nextcloud/eslint-config": "^9.0.0-rc.8",
9393
"@nextcloud/stylelint-config": "^3.2.1",
9494
"@nextcloud/typings": "^1.10.0",
95-
"@nextcloud/webpack-vue-config": "^7.0.2",
95+
"@rsdoctor/rspack-plugin": "^1.5.9",
96+
"@rspack/cli": "^2.0.0",
97+
"@rspack/core": "^2.0.0",
98+
"@rspack/dev-server": "^2.0.1",
99+
"@rspack/plugin-node-polyfill": "^0.5.8",
96100
"@playwright/test": "^1.58.2",
97101
"@types/node": "^25.6.0",
98-
"@types/webpack": "^5.28.5",
99102
"@vitejs/plugin-vue": "^6.0.5",
100103
"@vitest/coverage-v8": "^4.1.2",
101104
"@vue/compiler-sfc": "^3.5.31",
102105
"@vue/test-utils": "^2.4.6",
103-
"babel-loader-exclude-node-modules-except": "^1.2.4",
106+
"css-loader": "^7.1.2",
104107
"jsdom": "^29.0.2",
105-
"resolve-url-loader": "^5.0.0",
106-
"ts-loader": "^9.5.7",
108+
"sass": "^1.93.2",
109+
"sass-loader": "^16.0.6",
107110
"typescript": "^6.0.2",
108111
"vitest": "^4.1.2",
109112
"vue-template-compiler": "^2.7.16",

rspack.config.js

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
const browserslistConfig = require('@nextcloud/browserslist-config')
7+
const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin')
8+
const { defineConfig } = require('@rspack/cli')
9+
const { DefinePlugin, IgnorePlugin, LightningCssMinimizerRspackPlugin, ProgressPlugin, SwcJsMinimizerRspackPlugin } = require('@rspack/core')
10+
const NodePolyfillPlugin = require('@rspack/plugin-node-polyfill')
11+
const browserslist = require('browserslist')
12+
const path = require('node:path')
13+
const { VueLoaderPlugin } = require('vue-loader')
14+
15+
// browserslist-rs does not support baseline queries yet
16+
// Manually resolving the browserslist config to the list of browsers with minimal versions
17+
// See: https://github.com/browserslist/browserslist-rs/issues/40
18+
const browsers = browserslist(browserslistConfig)
19+
const minBrowserVersion = browsers
20+
.map((str) => str.split(' '))
21+
.reduce((minVersion, [browser, version]) => {
22+
minVersion[browser] = minVersion[browser] ? Math.min(minVersion[browser], parseFloat(version)) : parseFloat(version)
23+
return minVersion
24+
}, {})
25+
const targets = Object.entries(minBrowserVersion).map(([browser, version]) => `${browser} >=${version}`).join(',')
26+
27+
const transpilePackages = [
28+
'p-limit',
29+
'p-defer',
30+
'p-queue',
31+
'p-try',
32+
'yocto-queue',
33+
]
34+
35+
const shouldExcludeFromJsTranspile = (resourcePath) => {
36+
if (!resourcePath.includes(`${path.sep}node_modules${path.sep}`)) {
37+
return false
38+
}
39+
40+
return !transpilePackages.some((moduleName) => resourcePath.includes(`${path.sep}node_modules${path.sep}${moduleName}${path.sep}`))
41+
}
42+
43+
module.exports = defineConfig((env) => {
44+
const appName = process.env.npm_package_name
45+
const appVersion = process.env.npm_package_version
46+
47+
const mode = (env.development && 'development') || (env.production && 'production') || process.env.NODE_ENV || 'production'
48+
const isDev = mode === 'development'
49+
process.env.NODE_ENV = mode
50+
51+
console.info('Building', appName, appVersion, '\n')
52+
53+
return {
54+
target: 'web',
55+
mode,
56+
devtool: isDev ? 'cheap-source-map' : 'source-map',
57+
stats: 'normal',
58+
59+
entry: {
60+
main: path.join(__dirname, 'src', 'main.js'),
61+
reference: path.join(__dirname, 'src', 'reference.js'),
62+
'contacts-menu': path.join(__dirname, 'src', 'contactsMenu.js'),
63+
'appointments-booking': path.join(__dirname, 'src', 'appointments', 'main-booking.js'),
64+
'appointments-confirmation': path.join(__dirname, 'src', 'appointments', 'main-confirmation.js'),
65+
'appointments-overview': path.join(__dirname, 'src', 'appointments', 'main-overview.js'),
66+
'proposal-public': path.join(__dirname, 'src', 'proposal-public.ts'),
67+
},
68+
69+
output: {
70+
path: path.resolve('./js'),
71+
filename: `${appName}-[name].js?v=[contenthash]`,
72+
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
73+
publicPath: 'auto',
74+
assetModuleFilename: '[name][ext]?v=[contenthash]',
75+
clean: true,
76+
devtoolNamespace: appName,
77+
devtoolModuleFilenameTemplate(info) {
78+
const rootDir = process.cwd()
79+
const rel = path.relative(rootDir, info.absoluteResourcePath)
80+
return `webpack:///${appName}/${rel}`
81+
},
82+
},
83+
84+
devServer: {
85+
hot: true,
86+
host: '127.0.0.1',
87+
port: 3000,
88+
allowedHosts: 'all',
89+
client: {
90+
overlay: false,
91+
},
92+
devMiddleware: {
93+
writeToDisk: true,
94+
},
95+
headers: {
96+
'Access-Control-Allow-Origin': '*',
97+
},
98+
},
99+
100+
optimization: {
101+
chunkIds: 'named',
102+
splitChunks: {
103+
automaticNameDelimiter: '-',
104+
cacheGroups: {
105+
defaultVendors: {
106+
reuseExistingChunk: true,
107+
},
108+
},
109+
},
110+
minimize: !isDev,
111+
minimizer: [
112+
new SwcJsMinimizerRspackPlugin({
113+
minimizerOptions: {
114+
targets,
115+
},
116+
}),
117+
new LightningCssMinimizerRspackPlugin({
118+
minimizerOptions: {
119+
targets,
120+
},
121+
}),
122+
],
123+
},
124+
125+
module: {
126+
rules: [
127+
{
128+
test: /\.vue$/,
129+
loader: 'vue-loader',
130+
options: {
131+
experimentalInlineMatchResource: true,
132+
},
133+
},
134+
{
135+
test: /\.css$/,
136+
use: [
137+
'vue-style-loader',
138+
'css-loader',
139+
],
140+
},
141+
{
142+
test: /\.scss$/,
143+
use: [
144+
'vue-style-loader',
145+
'css-loader',
146+
{
147+
loader: 'sass-loader',
148+
options: {
149+
sassOptions: {
150+
includePaths: [
151+
path.resolve(__dirname, './src/assets'),
152+
],
153+
},
154+
},
155+
},
156+
],
157+
},
158+
{
159+
test: /\.m?js$/,
160+
exclude: shouldExcludeFromJsTranspile,
161+
loader: 'builtin:swc-loader',
162+
options: {
163+
jsc: {
164+
parser: {
165+
syntax: 'ecmascript',
166+
},
167+
},
168+
env: {
169+
targets,
170+
},
171+
},
172+
type: 'javascript/auto',
173+
},
174+
{
175+
test: /\.ts$/,
176+
exclude: [/node_modules/],
177+
loader: 'builtin:swc-loader',
178+
options: {
179+
jsc: {
180+
parser: {
181+
syntax: 'typescript',
182+
},
183+
},
184+
env: {
185+
targets,
186+
},
187+
},
188+
type: 'javascript/auto',
189+
},
190+
{
191+
test: /\.(png|jpe?g|gif|svg|webp)$/i,
192+
type: 'asset',
193+
},
194+
{
195+
test: /\.(woff2?|eot|ttf|otf)$/i,
196+
type: 'asset/resource',
197+
},
198+
{
199+
resourceQuery: /raw/,
200+
type: 'asset/source',
201+
},
202+
{
203+
resourceQuery: /url$/,
204+
type: 'asset/resource',
205+
},
206+
],
207+
},
208+
209+
plugins: [
210+
new ProgressPlugin(),
211+
new VueLoaderPlugin(),
212+
new NodePolyfillPlugin(),
213+
new DefinePlugin({
214+
appName: JSON.stringify(appName),
215+
appVersion: JSON.stringify(appVersion),
216+
__VUE_OPTIONS_API__: true,
217+
__VUE_PROD_DEVTOOLS__: false,
218+
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
219+
}),
220+
new IgnorePlugin({
221+
resourceRegExp: /^\.\/locale$/,
222+
contextRegExp: /moment$/,
223+
}),
224+
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
225+
],
226+
227+
resolve: {
228+
extensions: ['*', '.ts', '.js', '.vue'],
229+
symlinks: false,
230+
alias: {
231+
'@': path.resolve(__dirname, 'src'),
232+
},
233+
fallback: {
234+
fs: false,
235+
},
236+
},
237+
238+
cache: true,
239+
}
240+
})

src/views/EditFull.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,13 @@ export default {
450450
{
451451
label: t('calendar', 'Discard changes'),
452452
variant: 'secondary',
453-
icon: atob(IconDelete.split(',')[1]),
453+
icon: IconDelete,
454454
callback: () => { this.cancel(true) },
455455
},
456456
{
457457
label: t('calendar', 'Cancel'),
458458
variant: 'primary',
459-
icon: atob(IconCancel.split(',')[1]),
459+
icon: IconCancel,
460460
callback: () => { this.closeCancelDialog() },
461461
},
462462
],

src/views/EditSimple.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,13 +374,13 @@ export default {
374374
{
375375
label: t('calendar', 'Discard changes'),
376376
variant: 'secondary',
377-
icon: atob(IconDelete.split(',')[1]),
377+
icon: IconDelete,
378378
callback: () => { this.cancel(true) },
379379
},
380380
{
381381
label: t('calendar', 'Cancel'),
382382
variant: 'primary',
383-
icon: atob(IconCancel.split(',')[1]),
383+
icon: IconCancel,
384384
callback: () => { this.closeCancelDialog() },
385385
},
386386
],

0 commit comments

Comments
 (0)