webpack.common.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. 'davclient.js',
  105. 'nextcloud-vue-collections',
  106. 'p-finally',
  107. 'p-limit',
  108. 'p-locate',
  109. 'p-queue',
  110. 'p-timeout',
  111. 'p-try',
  112. 'semver',
  113. 'striptags',
  114. 'toastify-js',
  115. 'v-tooltip',
  116. 'yocto-queue',
  117. ]),
  118. },
  119. {
  120. test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf)$/,
  121. type: 'asset/inline',
  122. },
  123. {
  124. test: /\.handlebars/,
  125. loader: 'handlebars-loader',
  126. },
  127. {
  128. resourceQuery: /raw/,
  129. type: 'asset/source',
  130. },
  131. ],
  132. },
  133. optimization: {
  134. splitChunks: {
  135. automaticNameDelimiter: '-',
  136. minChunks: 3, // minimum number of chunks that must share the module
  137. cacheGroups: {
  138. vendors: {
  139. // split every dependency into one bundle
  140. test: /[\\/]node_modules[\\/]/,
  141. // necessary to keep this name to properly inject it
  142. // see OC_Template.php
  143. name: 'core-common',
  144. chunks: 'all',
  145. },
  146. },
  147. },
  148. },
  149. plugins: [
  150. new VueLoaderPlugin(),
  151. new NodePolyfillPlugin(),
  152. new webpack.ProvidePlugin({
  153. // Provide jQuery to jquery plugins as some are loaded before $ is exposed globally.
  154. // We need to provide the path to node_moduels as otherwise npm link will fail due
  155. // to tribute.js checking for jQuery in @nextcloud/vue
  156. jQuery: path.resolve(path.join(__dirname, 'node_modules/jquery')),
  157. // Shim ICAL to prevent using the global object (window.ICAL).
  158. // The library ical.js heavily depends on instanceof checks which will
  159. // break if two separate versions of the library are used (e.g. bundled one
  160. // and global one).
  161. ICAL: 'ical.js',
  162. }),
  163. new WorkboxPlugin.GenerateSW({
  164. swDest: 'preview-service-worker.js',
  165. clientsClaim: true,
  166. skipWaiting: true,
  167. exclude: [/.*/], // don't do pre-caching
  168. inlineWorkboxRuntime: true,
  169. sourcemap: false,
  170. // Increase perfs with less logging
  171. disableDevLogs: true,
  172. // Define runtime caching rules.
  173. runtimeCaching: [{
  174. // Match any preview file request
  175. // /apps/files_trashbin/preview?fileId=156380&a=1
  176. // /core/preview?fileId=155842&a=1
  177. urlPattern: /^.*\/(apps|core)(\/[a-z-_]+)?\/preview.*/i,
  178. // Apply a strategy.
  179. handler: 'CacheFirst',
  180. options: {
  181. // Use a custom cache name.
  182. cacheName: 'previews',
  183. // Only cache 10000 images.
  184. expiration: {
  185. maxAgeSeconds: 3600 * 24 * 7, // one week
  186. maxEntries: 10000,
  187. },
  188. },
  189. }],
  190. }),
  191. ],
  192. externals: {
  193. OC: 'OC',
  194. OCA: 'OCA',
  195. OCP: 'OCP',
  196. },
  197. resolve: {
  198. alias: {
  199. // make sure to use the handlebar runtime when importing
  200. handlebars: 'handlebars/runtime',
  201. vue$: path.resolve('./node_modules/vue'),
  202. },
  203. extensions: ['*', '.ts', '.js', '.vue'],
  204. extensionAlias: {
  205. /**
  206. * Resolve TypeScript files when using fully-specified esm import paths
  207. * https://github.com/webpack/webpack/issues/13252
  208. */
  209. '.js': ['.js', '.ts'],
  210. },
  211. symlinks: true,
  212. fallback: {
  213. fs: false,
  214. },
  215. },
  216. }