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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ yarn-error.log*
.marginalia

js/

# CSS extracted from the JS build (CssExtractRspackPlugin)
/css/

# packaged app
Expand Down
13 changes: 7 additions & 6 deletions .nextcloudignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
.editorconfig
.eslintrc.js
.git
.git-blame-ignore-revs
.github
Expand All @@ -12,10 +11,8 @@
.php-cs-fixer.cache
.php-cs-fixer.dist.php
.scrutinizer.yml
.stylelintignore
.stylelintrc
.tx
babel.config.js
babel.config.cjs
build
composer.json
composer.lock
Expand All @@ -30,7 +27,11 @@ phpunit.unit.xml
README.md
screenshots
src
stylelint.config.js
stylelint.config.cjs
tests
timezones
webpack.*
eslint.config.mjs
rspack.config.js
jest.config.cjs
playwright.config.js
tsconfig.json
41,185 changes: 12,401 additions & 28,784 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 14 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
],
"type": "module",
"scripts": {
"build": "vite build --mode production",
"dev": "vite build --mode development",
"build": "rspack build --env production",
"dev": "rspack build --env development",
"analyze": "RSDOCTOR=true rspack build --env production",
"stats": "rspack build --env production --json js/stats.json",
"lint": "eslint --ext .js,.ts,.vue src",
"lint:fix": "eslint --ext .js,.ts,.vue src --fix",
"stylelint": "stylelint \"src/**/*.scss\" \"src/**/*.vue\"",
Expand All @@ -36,7 +38,7 @@
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:watch": "jest --watch",
"watch": "vite build --mode development --watch"
"watch": "rspack build --env development --watch"
},
"browserslist": [
"extends @nextcloud/browserslist-config"
Expand Down Expand Up @@ -80,6 +82,7 @@
"virtua": "^0.50.0",
"vue": "^3.5.39",
"vue-cropperjs": "^5.0.0",
"vue-loader": "^17.4.2",
"vue-material-design-icons": "^5.3.1",
"vue-router": "^5.1.0",
"vuex": "^4.1.0"
Expand All @@ -90,18 +93,23 @@
"@nextcloud/eslint-config": "^9.0.0-rc.6",
"@nextcloud/stylelint-config": "^3.1.1",
"@nextcloud/typings": "^1.10.0",
"@nextcloud/vite-config": "^2.5.4",
"@playwright/test": "^1.61.1",
"@rsdoctor/rspack-plugin": "^1.6.1",
"@rspack/cli": "^2.1.8",
"@rspack/core": "^2.1.8",
"@rspack/plugin-node-polyfill": "^0.5.8",
"@types/jest": "^29.5.14",
"@vue/vue3-jest": "^29.2.6",
"babel-jest": "^29.7.0",
"css-loader": "^7.1.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^30.4.1",
"jest-transform-stub": "^2.0.0",
"sass": "^1.102.0",
"sass-loader": "^17.0.0",
"ts-jest": "^29.4.11",
"ts-loader": "^9.6.2",
"typescript": "^5.9.3",
"vite": "^7.3.6"
"typescript": "^5.9.3"
},
"engines": {
"node": "^24.0.0",
Expand Down
238 changes: 238 additions & 0 deletions rspack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/*
* 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')
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 = [
'p-limit',
'yocto-queue',
]

const 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')

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

entry: {
main: path.join(__dirname, 'src', 'main.js'),
'files-action': path.join(__dirname, 'src', 'files-action.js'),
'admin-settings': path.join(__dirname, 'src', 'admin-settings.js'),
oca: path.join(__dirname, 'src', 'oca.ts'),
},

output: {
path: path.resolve('./js'),
filename: `${appName}-[name].js?v=[contenthash]`,
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
publicPath: 'auto',
assetModuleFilename: '[name].[ext]?v=[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: [
{
loader: CssExtractRspackPlugin.loader,
},
'css-loader',
],
},
{
test: /\.scss$/,
use: [
{
loader: CssExtractRspackPlugin.loader,
},
'css-loader',
{
loader: 'sass-loader',
options: {
sassOptions: {
// Sass emits a BOM for CSS files with non-ASCII characters in production builds by default.
// PostCSS (used by css-loader) >=8.5.24 stopped stripping it, so extracted stylesheets end up
// with a stray BOM between merged files, breaking the first selector of each one.
// Safe to disable since every page already sets <meta charset="utf-8">.
// See: https://github.com/nextcloud-libraries/webpack-vue-config/pull/798
charset: false,
},
},
},
],
},
{
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',
},
{
resourceQuery: /raw/,
type: 'asset/source',
},
{
resourceQuery: /url$/,
type: 'asset/resource',
},
],
},

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$/,
}),
new CssExtractRspackPlugin({
filename: '../css/contacts-[name].css',
chunkFilename: '../css/[id].chunk.css',
ignoreOrder: true,
}),
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
],

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

cache: true,
}
})
2 changes: 0 additions & 2 deletions src/admin-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { createApp } from 'vue'
import AdminSettings from './components/AdminSettings.vue'
import LegacyGlobalMixin from './mixins/LegacyGlobalMixin.js'

import 'vite/modulepreload-polyfill'

document.addEventListener('DOMContentLoaded', main)

function main() {
Expand Down
2 changes: 0 additions & 2 deletions src/files-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { createApp } from 'vue'
import ConfirmationDialog from './components/ConfirmationDialog.vue'
import LegacyGlobalMixin from './mixins/LegacyGlobalMixin.js'

import 'vite/modulepreload-polyfill'

const mime = 'text/vcard'
const name = 'contacts-import'

Expand Down
1 change: 0 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import router from './router/index.js'
import logger from './services/logger.js'
import store from './store/index.js'

import 'vite/modulepreload-polyfill'
// Global scss sheets
import './css/contacts.scss'
// Dialogs css
Expand Down
1 change: 0 additions & 1 deletion src/oca.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import ReadOnlyContactDetails from './views/ReadOnlyContactDetails.vue'
import LegacyGlobalMixin from './mixins/LegacyGlobalMixin.js'
import store from './store/index.js'

import 'vite/modulepreload-polyfill'
// Global scss sheets
import './css/contacts.scss'
// Dialogs css
Expand Down
22 changes: 0 additions & 22 deletions vite.config.js

This file was deleted.

Loading