-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebpack.config.js
More file actions
144 lines (134 loc) · 6.58 KB
/
Copy pathwebpack.config.js
File metadata and controls
144 lines (134 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: EUPL-1.2
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const webpackConfig = require('@nextcloud/webpack-vue-config')
const { VueLoaderPlugin } = require('vue-loader')
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
const buildMode = process.env.NODE_ENV
const isDev = buildMode === 'development'
webpackConfig.devtool = isDev ? 'cheap-source-map' : 'source-map'
webpackConfig.stats = {
colors: true,
modules: false,
}
const appId = 'petstore'
// Each Nextcloud Dashboard widget needs its own webpack entry-point so the
// widget's JS can be attached via `Util::addScript()` from PHP. Add a new
// line here for every widget you create alongside `lib/Dashboard/<Foo>Widget.php`.
webpackConfig.entry = {
main: {
import: path.join(__dirname, 'src', 'main.js'),
filename: appId + '-main.js',
},
adminSettings: {
import: path.join(__dirname, 'src', 'settings.js'),
filename: appId + '-settings.js',
},
exampleWidget: {
import: path.join(__dirname, 'src', 'exampleWidget.js'),
filename: appId + '-exampleWidget.js',
},
}
// Use local source when available (monorepo dev), otherwise fall back to npm package.
// Set NC_VUE_LOCAL=0 or USE_LOCAL_LIB=false to force the published npm package even when ../nextcloud-vue exists.
const localLib = path.resolve(__dirname, '../nextcloud-vue/src')
const useLocalLib = process.env.NC_VUE_LOCAL !== '0' && process.env.USE_LOCAL_LIB !== 'false' && fs.existsSync(localLib)
// Extend the base resolve config (preserves defaults from @nextcloud/webpack-vue-config)
webpackConfig.resolve = webpackConfig.resolve || {}
webpackConfig.resolve.modules = [path.resolve(__dirname, 'node_modules'), 'node_modules']
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'@': path.resolve(__dirname, 'src'),
...(useLocalLib ? { '@conduction/nextcloud-vue': localLib } : {}),
vue$: path.resolve(__dirname, 'node_modules/vue'),
// MANDATORY, not cosmetic. `@nextcloud/vue@9` declares a hard dependency on
// `vue-router@^5.1.0` while this app is on `vue-router@4`, so npm installs a
// SECOND copy under `node_modules/@nextcloud/vue/node_modules/vue-router`.
// Two router instances means the app's `useRouter()` and the ones inside
// @nextcloud/vue components resolve different singletons: navigation from a
// library component becomes a no-op with no error at all. Alias to the
// absolute FILE (a directory alias would let the nested copy win again for
// requests originating inside @nextcloud/vue).
'vue-router$': path.resolve(__dirname, 'node_modules/vue-router/dist/vue-router.mjs'),
pinia$: path.resolve(__dirname, 'node_modules/pinia'),
// ⚠️ These MUST be absolute FILE aliases, not directory aliases.
// `@nextcloud/vue@9` and `@nextcloud/dialogs@7` are exports-map-ONLY
// packages: their package.json has no `main` and no `module`, only
// `exports`. Webpack applies the exports map to PACKAGE requests, never to
// a request it has already rewritten into an absolute path — so a directory
// alias leaves it with nothing to resolve and the build dies with
// "Can't resolve '@nextcloud/vue'" from every single importer (234 errors
// on the first Vue 3 build here). The Vue-2 spelling worked only because
// `@nextcloud/vue@8` still shipped a `main`.
'@nextcloud/vue$': path.resolve(__dirname, 'node_modules/@nextcloud/vue/dist/index.mjs'),
'@nextcloud/dialogs$': path.resolve(__dirname, 'node_modules/@nextcloud/dialogs/dist/index.mjs'),
// Force the lib's transitive @nextcloud/axios import to resolve to
// the app's installed copy. Without the `$` exact-match suffix,
// webpack would walk up to the lib's own node_modules and load a
// second axios instance, breaking shared interceptors / CSRF tokens.
// Decidesk reference: commit ed34703c.
'@nextcloud/axios$': path.resolve(__dirname, 'node_modules/@nextcloud/axios'),
}
// Add SCSS rule to the existing module rules
webpackConfig.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
})
// Replace plugins to avoid duplicate VueLoaderPlugin (base config also registers one).
// CRITICAL: re-add the appName / appVersion DefinePlugin entries — without them
// every @nextcloud/vue widget mount logs `[ERROR] @nextcloud/vue: The library
// was used without setting / replacing the appName`. The base config sets these
// defines, but we lose them when we replace `webpackConfig.plugins` wholesale.
// See ADR-004 (Build / bundling) in hydra/openspec/architecture/.
webpackConfig.plugins = [
new VueLoaderPlugin(),
new NodePolyfillPlugin({ additionalAliases: ['process'] }),
new webpack.DefinePlugin({ appName: JSON.stringify(appId) }),
new webpack.DefinePlugin({ appVersion: JSON.stringify(process.env.npm_package_version) }),
]
// Share Vue + @nextcloud/vue + pinia + icons + @conduction/nextcloud-vue across
// every entry-point so each widget bundle no longer inlines its own ~3 MB
// framework copy. Stable filenames (no contenthash in the JS name) mean each
// widget's `Util::addScript` PHP call can reference the chunk directly without
// a manifest. The shared chunks load once on the page and stay cached across
// navigations between this app's pages.
//
// Each widget's PHP `load()` MUST attach the shared chunks before the per-widget
// bundle (see `lib/Dashboard/ExampleWidget.php`). Order in PHP:
// 1. <appId>-shared-vendor (Vue, pinia, icons)
// 2. <appId>-shared-nc-vue (@nextcloud/vue, @conduction/nextcloud-vue)
// 3. <appId>-<widget>Widget (your widget code)
// `Util::addScript` dedupes by (app, file) so eagerly loading every widget
// still emits each shared chunk exactly once.
webpackConfig.optimization = {
...(webpackConfig.optimization || {}),
splitChunks: {
...(webpackConfig.optimization?.splitChunks || {}),
chunks: 'all',
cacheGroups: {
default: false,
defaultVendors: false,
ncVue: {
name: appId + '-shared-nc-vue',
// Matches both node_modules entries AND the monorepo-dev alias
// `../nextcloud-vue/src/...` which webpack resolves outside
// node_modules when @conduction/nextcloud-vue is aliased to it.
test: /[\\/]node_modules[\\/](@nextcloud[\\/]vue|@conduction[\\/]nextcloud-vue)[\\/]|[\\/]nextcloud-vue[\\/]src[\\/]/,
priority: 30,
reuseExistingChunk: true,
enforce: true,
filename: appId + '-shared-nc-vue.js',
},
vendor: {
name: appId + '-shared-vendor',
test: /[\\/]node_modules[\\/](vue|vue-router|pinia|vue-material-design-icons|@vueuse|core-js)[\\/]/,
priority: 20,
reuseExistingChunk: true,
enforce: true,
filename: appId + '-shared-vendor.js',
},
},
},
}
module.exports = webpackConfig