Skip to content

Commit 4bbb63f

Browse files
committed
chore: Move to RSpack
Signed-off-by: Julius Knorr <jus@bitgrid.net>
1 parent 1704afe commit 4bbb63f

File tree

11 files changed

+4344
-1398
lines changed

11 files changed

+4344
-1398
lines changed

package-lock.json

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

package.json

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@
1010
"dist"
1111
],
1212
"scripts": {
13-
"build": "NODE_ENV=production NODE_OPTIONS='--max-old-space-size=4096' vite --mode production build",
14-
"dev": "NODE_ENV=development NODE_OPTIONS='--max-old-space-size=4096' vite --mode development build",
13+
"analyze": "rspack build --env production --analyze",
14+
"analyze:stats": "rspack build --env production --json js/stats.json",
15+
"build": "NODE_ENV=production rspack build --mode=production",
16+
"dev": "NODE_ENV=development rspack build --mode=development",
1517
"lint": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright",
1618
"lint:fix": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright --fix",
1719
"prettier": "prettier --check .",
1820
"prettier:change": "git diff HEAD --name-only | xargs prettier --write --no-error-on-unmatched-pattern",
1921
"prettier:fix": "prettier --write .",
20-
"serve": "BASE=${BASE:-/apps/text} NODE_ENV=development vite --mode development serve --host",
22+
"serve": "NODE_ENV=development rspack serve",
2123
"start:nextcloud": "node playwright/start-nextcloud-server.mjs",
2224
"test": "NODE_ENV=test vitest run",
2325
"test:coverage": "NODE_ENV=test vitest run --coverage",
2426
"test:cypress": "cd cypress && ./runLocal.sh run",
2527
"test:cypress:open": "cd cypress && ./runLocal.sh open",
26-
"watch": "NODE_ENV=development NODE_OPTIONS='--max-old-space-size=8192' vite --mode development build --watch"
28+
"watch": "NODE_ENV=development rspack build --mode=development --watch"
2729
},
2830
"browserslist": [
2931
"extends @nextcloud/browserslist-config"
@@ -110,14 +112,18 @@
110112
"@nextcloud/e2e-test-server": "^0.4.0",
111113
"@nextcloud/eslint-config": "^8.4.2",
112114
"@nextcloud/prettier-config": "^1.2.0",
113-
"@nextcloud/vite-config": "^1.7.2",
114115
"@playwright/test": "^1.57.0",
116+
"@rspack/cli": "^1.1.8",
117+
"@rspack/core": "^1.1.8",
118+
"@rspack/plugin-node-polyfill": "^0.5.8",
115119
"@types/markdown-it": "^14.1.2",
116120
"@vitejs/plugin-vue2": "^2.3.4",
117121
"@vitest/coverage-v8": "^4.0.16",
118122
"@vue/test-utils": "^1.3.0 <2",
119123
"@vue/tsconfig": "^0.5.1",
120124
"@vueuse/core": "^11.3.0",
125+
"browserslist": "^4.24.4",
126+
"css-loader": "^7.1.2",
121127
"cypress": "^15.8.1",
122128
"cypress-split": "^1.24.25",
123129
"cypress-vite": "^1.8.0",
@@ -127,12 +133,14 @@
127133
"jsdom": "^27.3.0",
128134
"prettier-plugin-organize-imports": "^4.3.0",
129135
"prosemirror-test-builder": "^1.1.1",
130-
"rollup-plugin-webpack-stats": "^2.1.8",
136+
"resolve-url-loader": "^5.0.0",
137+
"sass": "^1.83.4",
138+
"sass-loader": "^16.0.4",
131139
"typescript": "^5.9.3",
132-
"vite": "^7.3.0",
133-
"vite-plugin-commonjs": "^0.10.4",
140+
"vite-plugin-node-polyfills": "^0.24.0",
134141
"vitest": "^4.0.16",
135142
"vue-demi": "^0.14.10",
143+
"vue-loader": "^15.11.1",
136144
"vue-template-compiler": "^2.7.16",
137145
"vue-tsc": "^2.2.12"
138146
},

playwright/e2e/editor-api-create-table.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ test.describe('createTable API', () => {
1313

1414
// Load the editor API bundle
1515
await page.addScriptTag({
16-
url: '/apps/text/js/text-editor.mjs',
17-
type: 'module',
16+
url: '/apps/text/js/text-editor.js',
1817
})
1918
})
2019

playwright/e2e/editor-api.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ test.describe('editor API at window.OCA.Text - MarkdownContentEditor.vue without
1616

1717
// Load the editor API bundle
1818
await page.addScriptTag({
19-
url: '/apps/text/js/text-editor.mjs',
20-
type: 'module',
19+
url: '/apps/text/js/text-editor.js',
2120
})
2221
})
2322

@@ -67,8 +66,7 @@ fileTest.describe(
6766

6867
// Load the editor API bundle
6968
await page.addScriptTag({
70-
url: '/apps/text/js/text-editor.mjs',
71-
type: 'module',
69+
url: '/apps/text/js/text-editor.js',
7270
})
7371
})
7472

rspack.config.mjs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import browserslistConfig from '@nextcloud/browserslist-config'
7+
import { defineConfig } from '@rspack/cli'
8+
import {
9+
CssExtractRspackPlugin,
10+
LightningCssMinimizerRspackPlugin,
11+
DefinePlugin,
12+
ProgressPlugin,
13+
SwcJsMinimizerRspackPlugin,
14+
} from '@rspack/core'
15+
import NodePolyfillPlugin from '@rspack/plugin-node-polyfill'
16+
import browserslist from 'browserslist'
17+
import path from 'node:path'
18+
import { fileURLToPath } from 'node:url'
19+
import { VueLoaderPlugin } from 'vue-loader'
20+
21+
const __filename = fileURLToPath(import.meta.url)
22+
const __dirname = path.dirname(__filename)
23+
24+
// browserslist-rs does not support baseline queries yet
25+
// Manually resolving the browserslist config to the list of browsers with minimal versions
26+
// See: https://github.com/browserslist/browserslist-rs/issues/40
27+
const browsers = browserslist(browserslistConfig)
28+
const minBrowserVersion = browsers
29+
.map((str) => str.split(' '))
30+
.reduce((minVersion, [browser, version]) => {
31+
minVersion[browser] = minVersion[browser]
32+
? Math.min(minVersion[browser], parseFloat(version))
33+
: parseFloat(version)
34+
return minVersion
35+
}, {})
36+
const targets = Object.entries(minBrowserVersion)
37+
.map(([browser, version]) => `${browser} >=${version}`)
38+
.join(',')
39+
40+
/** @type {import('@rspack/cli').RspackConfigFn} */
41+
const config = (env) => {
42+
const appName = process.env.npm_package_name
43+
const appVersion = process.env.npm_package_version
44+
45+
const mode =
46+
(env.development && 'development')
47+
|| (env.production && 'production')
48+
|| process.env.NODE_ENV
49+
|| 'production'
50+
const isDev = mode === 'development'
51+
process.env.NODE_ENV = mode
52+
53+
console.info('Building', appName, appVersion, '\n')
54+
55+
return {
56+
target: 'web',
57+
mode,
58+
devtool: isDev ? 'cheap-source-map' : 'source-map',
59+
60+
entry: {
61+
text: path.join(__dirname, 'src', 'main.js'),
62+
files: path.join(__dirname, 'src', 'files.ts'),
63+
public: path.join(__dirname, 'src', 'public.js'),
64+
viewer: path.join(__dirname, 'src', 'viewer.js'),
65+
editor: path.join(__dirname, 'src', 'editor.js'),
66+
init: path.join(__dirname, 'src', 'init.js'),
67+
},
68+
69+
output: {
70+
path: path.resolve('./js'),
71+
filename: `${appName}-[name].js?v=[contenthash]`,
72+
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
73+
// Set publicPath via __webpack_public_path__
74+
publicPath: 'auto',
75+
assetModuleFilename: '[name][ext]?v=[contenthash]',
76+
clean: true,
77+
devtoolNamespace: appName,
78+
// Make sure sourcemaps have a proper path and do not leak local paths
79+
devtoolModuleFilenameTemplate(info) {
80+
const rootDir = process.cwd()
81+
const rel = path.relative(rootDir, info.absoluteResourcePath)
82+
return `webpack:///${appName}/${rel}`
83+
},
84+
},
85+
86+
devServer: {
87+
hot: true,
88+
host: '127.0.0.1',
89+
port: 3000,
90+
allowedHosts: ['host.docker.internal', 'localhost', 'nextcloud.local'],
91+
client: {
92+
overlay: false,
93+
},
94+
devMiddleware: {
95+
writeToDisk: true,
96+
},
97+
headers: {
98+
'Access-Control-Allow-Origin': '*',
99+
},
100+
},
101+
102+
optimization: {
103+
chunkIds: 'named',
104+
splitChunks: {
105+
automaticNameDelimiter: '-',
106+
cacheGroups: {
107+
defaultVendors: {
108+
reuseExistingChunk: true,
109+
},
110+
// Make the emoji related dependencies a custom chunk
111+
'emoji-picker': {
112+
test: /[\\/]node_modules[\\/](emoji-mart-vue|emoji-datasource)/,
113+
name: 'emoji-picker',
114+
chunks: 'all',
115+
priority: 20,
116+
},
117+
},
118+
},
119+
minimize: !isDev,
120+
minimizer: [
121+
new SwcJsMinimizerRspackPlugin({
122+
minimizerOptions: {
123+
targets,
124+
},
125+
}),
126+
new LightningCssMinimizerRspackPlugin({
127+
minimizerOptions: {
128+
targets,
129+
},
130+
}),
131+
],
132+
},
133+
134+
module: {
135+
rules: [
136+
{
137+
test: /\.vue$/,
138+
loader: 'vue-loader',
139+
options: {
140+
experimentalInlineMatchResource: true,
141+
},
142+
},
143+
{
144+
test: /\.css$/,
145+
use: [
146+
{
147+
loader: CssExtractRspackPlugin.loader,
148+
},
149+
{
150+
loader: 'css-loader',
151+
options: {
152+
modules: {
153+
auto: true,
154+
localIdentName: isDev
155+
? '[path][name]__[local]'
156+
: '[hash:base64]',
157+
exportLocalsConvention: 'camelCase',
158+
},
159+
url: {
160+
filter: (url) => {
161+
// Let rspack handle absolute paths and data URLs
162+
return (
163+
!url.startsWith('/')
164+
&& !url.startsWith('data:')
165+
)
166+
},
167+
},
168+
},
169+
},
170+
],
171+
},
172+
{
173+
test: /\.scss$/,
174+
use: [
175+
{
176+
loader: CssExtractRspackPlugin.loader,
177+
},
178+
{
179+
loader: 'css-loader',
180+
options: {
181+
modules: {
182+
auto: true,
183+
localIdentName: isDev
184+
? '[path][name]__[local]'
185+
: '[hash:base64]',
186+
exportLocalsConvention: 'camelCase',
187+
},
188+
url: {
189+
filter: (url) => {
190+
// Let rspack handle absolute paths and data URLs
191+
return (
192+
!url.startsWith('/')
193+
&& !url.startsWith('data:')
194+
)
195+
},
196+
},
197+
},
198+
},
199+
{
200+
loader: 'resolve-url-loader',
201+
options: {
202+
sourceMap: true,
203+
},
204+
},
205+
{
206+
loader: 'sass-loader',
207+
options: {
208+
sourceMap: true,
209+
sassOptions: {
210+
includePaths: [path.resolve(__dirname)],
211+
},
212+
},
213+
},
214+
],
215+
},
216+
{
217+
test: /\.ts$/,
218+
exclude: [/node_modules/],
219+
loader: 'builtin:swc-loader',
220+
options: {
221+
jsc: {
222+
parser: {
223+
syntax: 'typescript',
224+
},
225+
},
226+
env: {
227+
targets,
228+
},
229+
},
230+
type: 'javascript/auto',
231+
},
232+
{
233+
test: /\.(png|jpe?g|gif|svg|webp)$/i,
234+
type: 'asset',
235+
},
236+
{
237+
test: /\.(woff2?|eot|ttf|otf)$/i,
238+
type: 'asset/resource',
239+
},
240+
{
241+
resourceQuery: /raw/,
242+
type: 'asset/source',
243+
},
244+
],
245+
},
246+
247+
plugins: [
248+
new ProgressPlugin(),
249+
250+
new VueLoaderPlugin(),
251+
252+
new NodePolyfillPlugin(),
253+
254+
new DefinePlugin({
255+
appName: JSON.stringify(appName),
256+
appVersion: JSON.stringify(appVersion),
257+
// Vue 2 compile time flags
258+
__VUE_OPTIONS_API__: true,
259+
__VUE_PROD_DEVTOOLS__: false,
260+
}),
261+
262+
new CssExtractRspackPlugin({
263+
filename: `../css/${appName}-[name].css?v=[contenthash]`,
264+
chunkFilename: `../css/${appName}-[name].css?v=[contenthash]`,
265+
ignoreOrder: true,
266+
}),
267+
],
268+
269+
resolve: {
270+
extensions: ['*', '.ts', '.js', '.vue', '.json'],
271+
symlinks: false,
272+
extensionAlias: {
273+
'.js': ['.js', '.ts'],
274+
},
275+
alias: {
276+
vue$: 'vue/dist/vue.runtime.esm.js',
277+
'@': path.resolve(__dirname, 'src'),
278+
},
279+
fallback: {
280+
fs: false,
281+
},
282+
},
283+
284+
cache: true,
285+
}
286+
}
287+
288+
export default defineConfig(config)

src/editor.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import {
1414
} from './components/Editor.provider.ts'
1515
import { ACTION_ATTACHMENT_PROMPT } from './components/Editor/MediaHandler.provider.js'
1616
import { openLink } from './helpers/links.js'
17-
// eslint-disable-next-line import/no-unresolved, n/no-missing-import
18-
import 'vite/modulepreload-polyfill'
1917

2018
const apiVersion = '1.3'
2119

0 commit comments

Comments
 (0)