webpack.common.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /* eslint-disable camelcase */
  2. const { VueLoaderPlugin } = require('vue-loader')
  3. const path = require('path')
  4. const BabelLoaderExcludeNodeModulesExcept = require('babel-loader-exclude-node-modules-except')
  5. const webpack = require('webpack')
  6. const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
  7. const WorkboxPlugin = require('workbox-webpack-plugin')
  8. const modules = require('./webpack.modules.js')
  9. const formatOutputFromModules = (modules) => {
  10. // merge all configs into one object, and use AppID to generate the fileNames
  11. // with the following format:
  12. // AppId-fileName: path/to/js-file.js
  13. const moduleEntries = Object.keys(modules).map(moduleKey => {
  14. const module = modules[moduleKey]
  15. const entries = Object.keys(module).map(entryKey => {
  16. const entry = module[entryKey]
  17. return { [`${moduleKey}-${entryKey}`]: entry }
  18. })
  19. return Object.assign({}, ...Object.values(entries))
  20. })
  21. return Object.assign({}, ...Object.values(moduleEntries))
  22. }
  23. const modulesToBuild = () => {
  24. const MODULE = process?.env?.MODULE
  25. if (MODULE) {
  26. if (!modules[MODULE]) {
  27. throw new Error(`No module "${MODULE}" found`)
  28. }
  29. return formatOutputFromModules({
  30. [MODULE]: modules[MODULE],
  31. })
  32. }
  33. return formatOutputFromModules(modules)
  34. }
  35. module.exports = {
  36. entry: modulesToBuild(),
  37. output: {
  38. // Step away from the src folder and extract to the js folder
  39. path: path.join(__dirname, 'dist'),
  40. // Let webpack determine automatically where it's located
  41. publicPath: 'auto',
  42. filename: '[name].js?v=[contenthash]',
  43. chunkFilename: '[name]-[id].js?v=[contenthash]',
  44. // Make sure sourcemaps have a proper path and do not
  45. // leak local paths https://github.com/webpack/webpack/issues/3603
  46. devtoolNamespace: 'nextcloud',
  47. devtoolModuleFilenameTemplate(info) {
  48. const rootDir = process?.cwd()
  49. const rel = path.relative(rootDir, info.absoluteResourcePath)
  50. return `webpack:///nextcloud/${rel}`
  51. },
  52. clean: {
  53. keep: /icons\.css/, // Keep static icons css
  54. },
  55. },
  56. module: {
  57. rules: [
  58. {
  59. test: /davclient/,
  60. loader: 'exports-loader',
  61. options: {
  62. type: 'commonjs',
  63. exports: 'dav',
  64. },
  65. },
  66. {
  67. test: /\.css$/,
  68. use: ['style-loader', 'css-loader'],
  69. },
  70. {
  71. test: /\.scss$/,
  72. use: ['style-loader', 'css-loader', 'sass-loader'],
  73. },
  74. {
  75. test: /\.vue$/,
  76. loader: 'vue-loader',
  77. exclude: BabelLoaderExcludeNodeModulesExcept([
  78. 'vue-material-design-icons',
  79. 'emoji-mart-vue-fast',
  80. ]),
  81. },
  82. {
  83. test: /\.tsx?$/,
  84. use: [
  85. 'babel-loader',
  86. {
  87. // Fix TypeScript syntax errors in Vue
  88. loader: 'ts-loader',
  89. options: {
  90. transpileOnly: true,
  91. },
  92. },
  93. ],
  94. exclude: BabelLoaderExcludeNodeModulesExcept([]),
  95. },
  96. {
  97. test: /\.js$/,
  98. loader: 'babel-loader',
  99. // automatically detect necessary packages to
  100. // transpile in the node_modules folder
  101. exclude: BabelLoaderExcludeNodeModulesExcept([
  102. '@nextcloud/dialogs',
  103. '@nextcloud/event-bus',
  104. '@nextcloud/vue-dashboard',
  105. 'davclient.js',
  106. 'nextcloud-vue-collections',
  107. 'p-finally',
  108. 'p-limit',
  109. 'p-locate',
  110. 'p-queue',
  111. 'p-timeout',
  112. 'p-try',
  113. 'semver',
  114. 'striptags',
  115. 'toastify-js',
  116. 'v-tooltip',
  117. 'yocto-queue',
  118. ]),
  119. },
  120. {
  121. test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf)$/,
  122. type: 'asset/inline',
  123. },
  124. {
  125. test: /\.handlebars/,
  126. loader: 'handlebars-loader',
  127. },
  128. {
  129. resourceQuery: /raw/,
  130. type: 'asset/source',
  131. },
  132. ],
  133. },
  134. optimization: {
  135. splitChunks: {
  136. automaticNameDelimiter: '-',
  137. cacheGroups: {
  138. vendors: {
  139. // split every dependency into one bundle
  140. test: /[\\/]node_modules[\\/]/,
  141. enforce: true,
  142. // necessary to keep this name to properly inject it
  143. // see OC_Template.php
  144. name: 'core-common',
  145. chunks: 'all',
  146. },
  147. },
  148. },
  149. },
  150. plugins: [
  151. new VueLoaderPlugin(),
  152. new NodePolyfillPlugin(),
  153. new webpack.ProvidePlugin({
  154. // Provide jQuery to jquery plugins as some are loaded before $ is exposed globally.
  155. // We need to provide the path to node_moduels as otherwise npm link will fail due
  156. // to tribute.js checking for jQuery in @nextcloud/vue
  157. jQuery: path.resolve(path.join(__dirname, 'node_modules/jquery')),
  158. // Shim ICAL to prevent using the global object (window.ICAL).
  159. // The library ical.js heavily depends on instanceof checks which will
  160. // break if two separate versions of the library are used (e.g. bundled one
  161. // and global one).
  162. ICAL: 'ical.js',
  163. }),
  164. new WorkboxPlugin.GenerateSW({
  165. swDest: 'preview-service-worker.js',
  166. clientsClaim: true,
  167. skipWaiting: true,
  168. exclude: [/.*/], // don't do pre-caching
  169. inlineWorkboxRuntime: true,
  170. sourcemap: false,
  171. // Increase perfs with less logging
  172. disableDevLogs: true,
  173. // Define runtime caching rules.
  174. runtimeCaching: [{
  175. // Match any preview file request
  176. // /apps/files_trashbin/preview?fileId=156380&a=1
  177. // /core/preview?fileId=155842&a=1
  178. urlPattern: /^.*\/(apps|core)(\/[a-z-_]+)?\/preview.*/i,
  179. // Apply a strategy.
  180. handler: 'CacheFirst',
  181. options: {
  182. // Use a custom cache name.
  183. cacheName: 'previews',
  184. // Only cache 10000 images.
  185. expiration: {
  186. maxAgeSeconds: 3600 * 24 * 7, // one week
  187. maxEntries: 10000,
  188. },
  189. },
  190. }],
  191. }),
  192. ],
  193. externals: {
  194. OC: 'OC',
  195. OCA: 'OCA',
  196. OCP: 'OCP',
  197. },
  198. resolve: {
  199. alias: {
  200. // make sure to use the handlebar runtime when importing
  201. handlebars: 'handlebars/runtime',
  202. vue$: path.resolve('./node_modules/vue'),
  203. },
  204. extensions: ['*', '.ts', '.js', '.vue'],
  205. extensionAlias: {
  206. /**
  207. * Resolve TypeScript files when using fully-specified esm import paths
  208. * https://github.com/webpack/webpack/issues/13252
  209. */
  210. '.js': ['.js', '.ts'],
  211. },
  212. symlinks: true,
  213. fallback: {
  214. fs: false,
  215. },
  216. },
  217. }