webpack.common.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /* eslint-disable n/no-extraneous-require */
  2. /* eslint-disable camelcase */
  3. /**
  4. * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. const { VueLoaderPlugin } = require('vue-loader')
  8. const { readFileSync } = require('fs')
  9. const path = require('path')
  10. const BabelLoaderExcludeNodeModulesExcept = require('babel-loader-exclude-node-modules-except')
  11. const webpack = require('webpack')
  12. const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
  13. const WorkboxPlugin = require('workbox-webpack-plugin')
  14. const WebpackSPDXPlugin = require('./build/WebpackSPDXPlugin.js')
  15. const modules = require('./webpack.modules.js')
  16. const appVersion = readFileSync('./version.php').toString().match(/OC_Version.+\[([0-9]{2})/)?.[1] ?? 'unknown'
  17. const isDev = process.env.NODE_ENV === 'development'
  18. const formatOutputFromModules = (modules) => {
  19. // merge all configs into one object, and use AppID to generate the fileNames
  20. // with the following format:
  21. // AppId-fileName: path/to/js-file.js
  22. const moduleEntries = Object.keys(modules).map(moduleKey => {
  23. const module = modules[moduleKey]
  24. const entries = Object.keys(module).map(entryKey => {
  25. const entry = module[entryKey]
  26. return { [`${moduleKey}-${entryKey}`]: entry }
  27. })
  28. return Object.assign({}, ...Object.values(entries))
  29. })
  30. return Object.assign({}, ...Object.values(moduleEntries))
  31. }
  32. const modulesToBuild = () => {
  33. const MODULE = process?.env?.MODULE
  34. if (MODULE) {
  35. if (!modules[MODULE]) {
  36. throw new Error(`No module "${MODULE}" found`)
  37. }
  38. return formatOutputFromModules({
  39. [MODULE]: modules[MODULE],
  40. })
  41. }
  42. return formatOutputFromModules(modules)
  43. }
  44. const config = {
  45. entry: modulesToBuild(),
  46. output: {
  47. // Step away from the src folder and extract to the js folder
  48. path: path.join(__dirname, 'dist'),
  49. // Let webpack determine automatically where it's located
  50. publicPath: 'auto',
  51. filename: '[name].js?v=[contenthash]',
  52. chunkFilename: '[name]-[id].js?v=[contenthash]',
  53. // Make sure sourcemaps have a proper path and do not
  54. // leak local paths https://github.com/webpack/webpack/issues/3603
  55. devtoolNamespace: 'nextcloud',
  56. devtoolModuleFilenameTemplate(info) {
  57. const rootDir = process?.cwd()
  58. const rel = path.relative(rootDir, info.absoluteResourcePath)
  59. return `webpack:///nextcloud/${rel}`
  60. },
  61. clean: {
  62. keep: /icons\.css/, // Keep static icons css
  63. },
  64. },
  65. module: {
  66. rules: [
  67. {
  68. test: /davclient/,
  69. loader: 'exports-loader',
  70. options: {
  71. type: 'commonjs',
  72. exports: 'dav',
  73. },
  74. },
  75. {
  76. test: /\.css$/,
  77. use: ['style-loader', 'css-loader'],
  78. },
  79. {
  80. test: /\.scss$/,
  81. use: ['style-loader', 'css-loader', 'sass-loader'],
  82. },
  83. {
  84. test: /\.vue$/,
  85. loader: 'vue-loader',
  86. exclude: BabelLoaderExcludeNodeModulesExcept([
  87. 'vue-material-design-icons',
  88. 'emoji-mart-vue-fast',
  89. ]),
  90. },
  91. {
  92. test: /\.tsx?$/,
  93. use: [
  94. 'babel-loader',
  95. {
  96. // Fix TypeScript syntax errors in Vue
  97. loader: 'ts-loader',
  98. options: {
  99. transpileOnly: true,
  100. },
  101. },
  102. ],
  103. exclude: BabelLoaderExcludeNodeModulesExcept([]),
  104. },
  105. {
  106. test: /\.js$/,
  107. loader: 'babel-loader',
  108. // automatically detect necessary packages to
  109. // transpile in the node_modules folder
  110. exclude: BabelLoaderExcludeNodeModulesExcept([
  111. '@nextcloud/dialogs',
  112. '@nextcloud/event-bus',
  113. 'davclient.js',
  114. 'nextcloud-vue-collections',
  115. 'p-finally',
  116. 'p-limit',
  117. 'p-locate',
  118. 'p-queue',
  119. 'p-timeout',
  120. 'p-try',
  121. 'semver',
  122. 'striptags',
  123. 'toastify-js',
  124. 'v-tooltip',
  125. 'yocto-queue',
  126. ]),
  127. },
  128. {
  129. test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf)$/,
  130. type: 'asset/inline',
  131. },
  132. {
  133. test: /\.handlebars/,
  134. loader: 'handlebars-loader',
  135. },
  136. {
  137. resourceQuery: /raw/,
  138. type: 'asset/source',
  139. },
  140. ],
  141. },
  142. optimization: {
  143. splitChunks: {
  144. automaticNameDelimiter: '-',
  145. minChunks: 3, // minimum number of chunks that must share the module
  146. cacheGroups: {
  147. vendors: {
  148. // split every dependency into one bundle
  149. test: /[\\/]node_modules[\\/]/,
  150. // necessary to keep this name to properly inject it
  151. // see OC_Template.php
  152. name: 'core-common',
  153. chunks: 'all',
  154. },
  155. },
  156. },
  157. },
  158. plugins: [
  159. new VueLoaderPlugin(),
  160. new NodePolyfillPlugin(),
  161. new webpack.ProvidePlugin({
  162. // Provide jQuery to jquery plugins as some are loaded before $ is exposed globally.
  163. // We need to provide the path to node_moduels as otherwise npm link will fail due
  164. // to tribute.js checking for jQuery in @nextcloud/vue
  165. jQuery: path.resolve(path.join(__dirname, 'node_modules/jquery')),
  166. }),
  167. new WorkboxPlugin.GenerateSW({
  168. swDest: 'preview-service-worker.js',
  169. clientsClaim: true,
  170. skipWaiting: true,
  171. exclude: [/.*/], // don't do pre-caching
  172. inlineWorkboxRuntime: true,
  173. sourcemap: false,
  174. // Increase perfs with less logging
  175. disableDevLogs: true,
  176. // Define runtime caching rules.
  177. runtimeCaching: [{
  178. // Match any preview file request
  179. // /apps/files_trashbin/preview?fileId=156380&a=1
  180. // /core/preview?fileId=155842&a=1
  181. urlPattern: /^.*\/(apps|core)(\/[a-z-_]+)?\/preview.*/i,
  182. // Apply a strategy.
  183. handler: 'CacheFirst',
  184. options: {
  185. // Use a custom cache name.
  186. cacheName: 'previews',
  187. // Only cache 10000 images.
  188. expiration: {
  189. maxAgeSeconds: 3600 * 24 * 7, // one week
  190. maxEntries: 10000,
  191. },
  192. },
  193. }],
  194. }),
  195. // Make appName & appVersion available as a constants for '@nextcloud/vue' components
  196. new webpack.DefinePlugin({ appName: JSON.stringify('Nextcloud') }),
  197. new webpack.DefinePlugin({ appVersion: JSON.stringify(appVersion) }),
  198. // @nextcloud/moment since v1.3.0 uses `moment/min/moment-with-locales.js`
  199. // Which works only in Node.js and is not compatible with Webpack bundling
  200. // It has an unused function `localLocale` that requires locales by invalid relative path `./locale`
  201. // Though it is not used, Webpack tries to resolve it with `require.context` and fails
  202. new webpack.IgnorePlugin({
  203. resourceRegExp: /^\.\/locale$/,
  204. contextRegExp: /moment\/min$/,
  205. }),
  206. ],
  207. externals: {
  208. OC: 'OC',
  209. OCA: 'OCA',
  210. OCP: 'OCP',
  211. },
  212. resolve: {
  213. alias: {
  214. // make sure to use the handlebar runtime when importing
  215. handlebars: 'handlebars/runtime',
  216. vue$: path.resolve('./node_modules/vue'),
  217. },
  218. extensions: ['*', '.ts', '.js', '.vue'],
  219. extensionAlias: {
  220. /**
  221. * Resolve TypeScript files when using fully-specified esm import paths
  222. * https://github.com/webpack/webpack/issues/13252
  223. */
  224. '.js': ['.js', '.ts'],
  225. },
  226. symlinks: true,
  227. fallback: {
  228. fs: false,
  229. },
  230. },
  231. }
  232. // Generate reuse license files if not in development mode
  233. if (!isDev) {
  234. config.plugins.push(new WebpackSPDXPlugin({
  235. override: {
  236. select2: 'MIT',
  237. '@nextcloud/axios': 'GPL-3.0-or-later',
  238. '@nextcloud/vue': 'AGPL-3.0-or-later',
  239. 'nextcloud-vue-collections': 'AGPL-3.0-or-later',
  240. },
  241. }))
  242. config.optimization.minimizer = [{
  243. apply: (compiler) => {
  244. // Lazy load the Terser plugin
  245. const TerserPlugin = require('terser-webpack-plugin')
  246. new TerserPlugin({
  247. extractComments: false,
  248. terserOptions: {
  249. format: {
  250. comments: false,
  251. },
  252. compress: {
  253. passes: 2,
  254. },
  255. },
  256. }).apply(compiler)
  257. },
  258. }]
  259. }
  260. module.exports = config