You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
7.6 KiB

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