webpack.common.js 6.2 KB

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