Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ jobs:
run: npm ci
- name: Build frontend
working-directory: nextcloud/apps/mail
run: npm run build -- --fail-on-warnings
run: npm run build
- name: Install stunnel (tiny https proxy)
run: sudo apt-get install -y stunnel
- name: Start php server and https proxy
Expand Down
2 changes: 1 addition & 1 deletion .nextcloudignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ codecov.yml
/psalm.xml
/rector.php
/renovate.json
/rspack.config.js
/screenshots
/src
/tests
Expand All @@ -42,6 +43,5 @@ codecov.yml
/vendor/*/*/test
/vendor/*/*/tests
/vendor/*/*/test_data
/webpack.*
/vitest.config.*
/patches
7,004 changes: 3,984 additions & 3,020 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 11 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@
"license": "AGPL-3.0-only",
"author": "Christoph Wurst <christoph@winzerhof-wurst.at>",
"scripts": {
"build": "NODE_ENV=production webpack --progress --config webpack.prod.js",
"dev": "NODE_ENV=development webpack --config webpack.dev.js",
"analyze": "RSDOCTOR=true rspack build --env production",
"build": "rspack build --env production",
"dev": "rspack build --env development",
"postinstall": "patch-package",
"lint": "eslint --ext .js,.ts,.vue .",
"lint:fix": "eslint --ext .js,.ts,.vue --fix .",
"stats": "rspack build --env production --json js/stats.json",
"stylelint": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\"",
"stylelint:fix": "stylelint \"css/*.css\" \"css/*.scss\" \"src/**/*.scss\" \"src/**/*.vue\" --fix",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:unit": "vitest --run",
"test:unit:watch": "vitest --watch",
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js"
"watch": "rspack build --env development --watch"
},
"browserslist": [
"extends @nextcloud/browserslist-config"
Expand Down Expand Up @@ -84,33 +86,25 @@
"@nextcloud/eslint-config": "^9.0.0-rc.8",
"@nextcloud/stylelint-config": "^3.2.1",
"@playwright/test": "^1.59.1",
"@rsdoctor/rspack-plugin": "^1.5.9",
"@rspack/cli": "^2.0.0",
"@rspack/core": "^2.0.0",
"@rspack/dev-server": "^2.0.1",
"@rspack/plugin-node-polyfill": "^0.5.8",
"@vitejs/plugin-vue2": "^2.3.4",
"@vue/test-utils": "^1.3.6",
"autoprefixer": "^10.5.0",
"babel-loader": "^10.1.1",
"babel-loader-exclude-node-modules-except": "^1.2.4",
"css-loader": "^7.1.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-vitest-globals": "^1.6.1",
"file-loader": "^6.2.0",
"jsdom": "^29.1.1",
"node-polyfill-webpack-plugin": "^3.0.0",
"patch-package": "^8.0.1",
"postcss": "^8.5.13",
"postcss-loader": "^4.3.0",
"sass": "^1.99.0",
"sass-loader": "^16.0.7",
"style-loader": "^4.0.0",
"svg-inline-loader": "^0.8.2",
"ts-loader": "^9.5.7",
"typescript": "^5.9.3",
"url-loader": "^4.1.1",
"vitest": "^3.2.4",
"vue-loader": "^15.11.1",
"vue-template-compiler": "^2.7.16",
"webpack": "^5.106.2",
"webpack-cli": "^6.0.1",
"webpack-merge": "^5.10.0"
"webpack": "^5.102.1"
},
"engines": {
"node": "^24.0.0",
Expand Down
236 changes: 236 additions & 0 deletions rspack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

const browserslistConfig = require('@nextcloud/browserslist-config')
const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin')
const { defineConfig } = require('@rspack/cli')
const { CssExtractRspackPlugin, LightningCssMinimizerRspackPlugin, DefinePlugin, ProgressPlugin, SwcJsMinimizerRspackPlugin, IgnorePlugin } = require('@rspack/core')

Check warning on line 9 in rspack.config.js

View workflow job for this annotation

GitHub Actions / NPM lint

'CssExtractRspackPlugin' is assigned a value but never used
const NodePolyfillPlugin = require('@rspack/plugin-node-polyfill')
const browserslist = require('browserslist')
const path = require('node:path')
const { VueLoaderPlugin } = require('vue-loader')

// browserslist-rs does not support baseline queries yet
// Manually resolving the browserslist config to the list of browsers with minimal versions
// See: https://github.com/browserslist/browserslist-rs/issues/40
const browsers = browserslist(browserslistConfig)
const minBrowserVersion = browsers
.map((str) => str.split(' '))
.reduce((minVersion, [browser, version]) => {
minVersion[browser] = minVersion[browser] ? Math.min(minVersion[browser], parseFloat(version)) : parseFloat(version)
return minVersion
}, {})
const targets = Object.entries(minBrowserVersion).map(([browser, version]) => `${browser} >=${version}`).join(',')

const transpilePackages = [
'js-base64',
'p-limit',
'p-defer',
'p-queue',
'p-try',
'parseley',
'selderee',
'yocto-queue',
]

function shouldExcludeFromJsTranspile(resourcePath) {
if (!resourcePath.includes(`${path.sep}node_modules${path.sep}`)) {
return false
}

return !transpilePackages.some((moduleName) => resourcePath.includes(`${path.sep}node_modules${path.sep}${moduleName}${path.sep}`))
}

module.exports = defineConfig((env) => {
const appName = process.env.npm_package_name
const appVersion = process.env.npm_package_version

const mode = (env.development && 'development') || (env.production && 'production') || process.env.NODE_ENV || 'production'
const isDev = mode === 'development'
process.env.NODE_ENV = mode

console.info('Building', appName, appVersion, '\n')

Check failure on line 54 in rspack.config.js

View workflow job for this annotation

GitHub Actions / NPM lint

Unexpected console statement

return {
target: 'web',
mode,
devtool: isDev ? 'cheap-source-map' : 'source-map',
stats: 'normal',

entry: {
mail: path.join(__dirname, 'src', 'main.js'),
oauthpopup: path.join(__dirname, 'src', 'main-oauth-popup.js'),
settings: path.join(__dirname, 'src', 'main-settings.js'),
htmlresponse: path.join(__dirname, 'src', 'html-response.js'),
},

output: {
path: path.resolve('./js'),
filename: '[name].js',
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
publicPath: 'auto',
assetModuleFilename: '[name].[ext]?[contenthash]',
clean: true,
devtoolNamespace: appName,
devtoolModuleFilenameTemplate(info) {
const rootDir = process.cwd()
const rel = path.relative(rootDir, info.absoluteResourcePath)
return `webpack:///${appName}/${rel}`
},
},

optimization: {
chunkIds: 'named',
splitChunks: {
automaticNameDelimiter: '-',
cacheGroups: {
defaultVendors: {
reuseExistingChunk: true,
},
},
},
minimize: !isDev,
minimizer: [
new SwcJsMinimizerRspackPlugin({
minimizerOptions: {
targets,
},
}),
new LightningCssMinimizerRspackPlugin({
minimizerOptions: {
targets,
},
}),
],
},

module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader',
],
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader',
],
},
{
test: /\.[cm]?js$/,
exclude: shouldExcludeFromJsTranspile,
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'ecmascript',
},
},
env: {
targets,
},
},
type: 'javascript/auto',
},
{
test: /\.ts$/,
exclude: [/node_modules/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
},
},
env: {
targets,
},
},
type: 'javascript/auto',
},
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
type: 'asset',
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.wasm$/i,
type: 'asset/resource',
},
{
test: /\.tflite$/i,
type: 'asset/resource',
},
{
resourceQuery: /raw/,
type: 'asset/source',
},
{
resourceQuery: /url$/,
type: 'asset/resource',
},
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
type: 'asset/source',
},
],
},

plugins: [
new ProgressPlugin(),
new VueLoaderPlugin(),
new NodePolyfillPlugin(),
new DefinePlugin({
appName: JSON.stringify(appName),
appVersion: JSON.stringify(appVersion),
// Vue compile time flags
// See: https://vuejs.org/api/compile-time-flags.html#compile-time-flags
// See: https://github.com/vuejs/core/blob/v3.5.24/packages/vue/README.md#bundler-build-feature-flags
// > The build will work without configuring these flags,
// > however it is strongly recommended to properly configure them in order to get proper tree-shaking in the final bundle
// Unlike Vite plugin, vue-loader does not do this automatically for Webpack
// Although documentation says, it is optional, sometimes it breaks with:
// ReferenceError: __VUE_PROD_DEVTOOLS__ is not defined
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
}),
new IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
],

resolve: {
extensions: ['*', '.tsx', '.ts', '.js', '.vue', '.json'],
symlinks: false,
alias: {
ckeditor5$: path.resolve(__dirname, 'node_modules', 'ckeditor5', 'dist', 'ckeditor5.js'),
'@': path.resolve(__dirname, 'src'),
},
fallback: {
fs: false,
},
},

cache: true,
}
})
Loading
Loading