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 : / \. v u e $ / ,
129+ loader : 'vue-loader' ,
130+ options : {
131+ experimentalInlineMatchResource : true ,
132+ } ,
133+ } ,
134+ {
135+ test : / \. c s s $ / ,
136+ use : [
137+ 'vue-style-loader' ,
138+ 'css-loader' ,
139+ ] ,
140+ } ,
141+ {
142+ test : / \. s c s s $ / ,
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 ? j s $ / ,
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 : / \. t s $ / ,
176+ exclude : [ / n o d e _ m o d u l e s / ] ,
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 : / \. ( p n g | j p e ? g | g i f | s v g | w e b p ) $ / i,
192+ type : 'asset' ,
193+ } ,
194+ {
195+ test : / \. ( w o f f 2 ? | e o t | t t f | o t f ) $ / i,
196+ type : 'asset/resource' ,
197+ } ,
198+ {
199+ resourceQuery : / r a w / ,
200+ type : 'asset/source' ,
201+ } ,
202+ {
203+ resourceQuery : / u r l $ / ,
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 : / ^ \. \/ l o c a l e $ / ,
222+ contextRegExp : / m o m e n t $ / ,
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+ } )
0 commit comments