webpack.config.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. const path = require('path');
  2. const CopyPlugin = require('copy-webpack-plugin');
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  4. // Generates a path to the output bundle to be loaded in the browser.
  5. const getOutputPath = (app, folder, filename) => {
  6. const exceptions = {
  7. 'documents': 'wagtaildocs',
  8. 'contrib/table_block': 'table_block',
  9. 'contrib/typed_table_block': 'typed_table_block',
  10. 'contrib/styleguide': 'wagtailstyleguide',
  11. 'contrib/modeladmin': 'wagtailmodeladmin',
  12. };
  13. const appLabel = exceptions[app] || `wagtail${app}`;
  14. return path.join('wagtail', app, 'static', appLabel, folder, filename);
  15. };
  16. // Mapping from package name to exposed global variable.
  17. const exposedDependencies = {
  18. 'focus-trap-react': 'FocusTrapReact',
  19. 'react': 'React',
  20. 'react-dom': 'ReactDOM',
  21. 'react-transition-group/CSSTransitionGroup': 'CSSTransitionGroup',
  22. 'draft-js': 'DraftJS',
  23. };
  24. module.exports = function exports(env, argv) {
  25. const isProduction = argv.mode === 'production';
  26. const entrypoints = {
  27. 'admin': [
  28. 'chooser-modal',
  29. 'chooser-widget',
  30. 'chooser-widget-telepath',
  31. 'comments',
  32. 'core',
  33. 'date-time-chooser',
  34. 'draftail',
  35. 'expanding-formset',
  36. 'filtered-select',
  37. 'lock-unlock-action',
  38. 'modal-workflow',
  39. 'page-chooser-modal',
  40. 'page-chooser',
  41. 'page-editor',
  42. 'preview-panel',
  43. 'privacy-switch',
  44. 'schedule-publishing',
  45. 'sidebar',
  46. 'task-chooser-modal',
  47. 'task-chooser',
  48. 'telepath/blocks',
  49. 'telepath/telepath',
  50. 'telepath/widgets',
  51. 'userbar',
  52. 'wagtailadmin',
  53. 'workflow-action',
  54. 'workflow-status',
  55. 'bulk-actions',
  56. ],
  57. 'images': [
  58. 'image-chooser',
  59. 'image-chooser-modal',
  60. 'image-chooser-telepath',
  61. ],
  62. 'documents': [
  63. 'document-chooser',
  64. 'document-chooser-modal',
  65. 'document-chooser-telepath',
  66. ],
  67. 'snippets': ['snippet-chooser', 'snippet-chooser-telepath'],
  68. 'contrib/table_block': ['table'],
  69. 'contrib/typed_table_block': ['typed_table_block'],
  70. };
  71. const entry = {};
  72. // eslint-disable-next-line no-restricted-syntax
  73. for (const [appName, moduleNames] of Object.entries(entrypoints)) {
  74. moduleNames.forEach((moduleName) => {
  75. entry[moduleName] = {
  76. import: [`./client/src/entrypoints/${appName}/${moduleName}.js`],
  77. filename: getOutputPath(appName, 'js', moduleName) + '.js',
  78. };
  79. });
  80. }
  81. const sassEntry = {};
  82. sassEntry[getOutputPath('admin', 'css', 'core')] = path.resolve(
  83. 'wagtail',
  84. 'admin',
  85. 'static_src',
  86. 'wagtailadmin',
  87. 'scss',
  88. 'core.scss',
  89. );
  90. sassEntry[getOutputPath('admin', 'css', 'panels/draftail')] = path.resolve(
  91. 'wagtail',
  92. 'admin',
  93. 'static_src',
  94. 'wagtailadmin',
  95. 'scss',
  96. 'panels',
  97. 'draftail.scss',
  98. );
  99. sassEntry[getOutputPath('admin', 'css', 'panels/streamfield')] = path.resolve(
  100. 'wagtail',
  101. 'admin',
  102. 'static_src',
  103. 'wagtailadmin',
  104. 'scss',
  105. 'panels',
  106. 'streamfield.scss',
  107. );
  108. sassEntry[getOutputPath('admin', 'css', 'userbar')] = path.resolve(
  109. 'wagtail',
  110. 'admin',
  111. 'static_src',
  112. 'wagtailadmin',
  113. 'scss',
  114. 'userbar.scss',
  115. );
  116. sassEntry[getOutputPath('contrib/styleguide', 'css', 'styleguide')] =
  117. path.resolve(
  118. 'wagtail',
  119. 'contrib',
  120. 'styleguide',
  121. 'static_src',
  122. 'wagtailstyleguide',
  123. 'scss',
  124. 'styleguide.scss',
  125. );
  126. sassEntry[
  127. getOutputPath('contrib/typed_table_block', 'css', 'typed_table_block')
  128. ] = path.resolve(
  129. 'wagtail',
  130. 'contrib',
  131. 'typed_table_block',
  132. 'static_src',
  133. 'typed_table_block',
  134. 'scss',
  135. 'typed_table_block.scss',
  136. );
  137. return {
  138. entry: {
  139. ...entry,
  140. ...sassEntry,
  141. },
  142. output: {
  143. path: path.resolve('.'),
  144. publicPath: '/static/',
  145. },
  146. resolve: {
  147. extensions: ['.ts', '.tsx', '.js'],
  148. // Some libraries import Node modules but don't use them in the browser.
  149. // Tell Webpack to provide empty mocks for them so importing them works.
  150. fallback: {
  151. fs: false,
  152. net: false,
  153. tls: false,
  154. },
  155. },
  156. externals: {
  157. jquery: 'jQuery',
  158. },
  159. plugins: [
  160. new MiniCssExtractPlugin({
  161. filename: '[name].css',
  162. }),
  163. new CopyPlugin({
  164. patterns: [
  165. {
  166. from: 'wagtail/admin/static_src/',
  167. to: 'wagtail/admin/static/',
  168. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  169. },
  170. {
  171. from: 'wagtail/documents/static_src/',
  172. to: 'wagtail/documents/static/',
  173. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  174. },
  175. {
  176. from: 'wagtail/embeds/static_src/',
  177. to: 'wagtail/embeds/static/',
  178. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  179. },
  180. {
  181. from: 'wagtail/images/static_src/',
  182. to: 'wagtail/images/static/',
  183. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  184. },
  185. {
  186. from: 'wagtail/search/static_src/',
  187. to: 'wagtail/search/static/',
  188. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  189. },
  190. {
  191. from: 'wagtail/users/static_src/',
  192. to: 'wagtail/users/static/',
  193. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  194. },
  195. {
  196. from: 'wagtail/contrib/settings/static_src/',
  197. to: 'wagtail/contrib/settings/static/',
  198. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  199. },
  200. {
  201. from: 'wagtail/contrib/modeladmin/static_src/',
  202. to: 'wagtail/contrib/modeladmin/static/',
  203. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  204. },
  205. ],
  206. }),
  207. ],
  208. module: {
  209. rules: [
  210. {
  211. test: /\.(js|ts)x?$/,
  212. loader: 'ts-loader',
  213. exclude: /node_modules/,
  214. },
  215. {
  216. // Legacy support for font icon loading, to be removed.
  217. test: /\.(woff)$/i,
  218. generator: {
  219. emit: false,
  220. filename: 'wagtailadmin/fonts/[name][ext]',
  221. },
  222. },
  223. {
  224. test: /\.(svg)$/i,
  225. type: 'asset/inline',
  226. },
  227. {
  228. test: /\.(scss|css)$/,
  229. use: [
  230. MiniCssExtractPlugin.loader,
  231. {
  232. loader: 'css-loader',
  233. options: {
  234. url: false,
  235. },
  236. },
  237. {
  238. loader: 'postcss-loader',
  239. options: {
  240. postcssOptions: {
  241. plugins: ['tailwindcss', 'autoprefixer', 'cssnano'],
  242. },
  243. },
  244. },
  245. 'sass-loader',
  246. ],
  247. },
  248. ].concat(
  249. Object.keys(exposedDependencies).map((name) => {
  250. const globalName = exposedDependencies[name];
  251. // Create expose-loader configs for each Wagtail dependency.
  252. return {
  253. test: require.resolve(name),
  254. use: [
  255. {
  256. loader: 'expose-loader',
  257. options: {
  258. exposes: {
  259. globalName,
  260. override: true,
  261. },
  262. },
  263. },
  264. ],
  265. };
  266. }),
  267. ),
  268. },
  269. optimization: {
  270. splitChunks: {
  271. cacheGroups: {
  272. vendor: {
  273. name: getOutputPath('admin', 'js', 'vendor'),
  274. chunks: 'initial',
  275. minChunks: 2,
  276. reuseExistingChunk: true,
  277. },
  278. },
  279. },
  280. },
  281. // See https://webpack.js.org/configuration/devtool/.
  282. devtool: isProduction ? false : 'eval-cheap-module-source-map',
  283. // For development mode only.
  284. watchOptions: {
  285. poll: 1000,
  286. aggregateTimeout: 300,
  287. },
  288. // Disable performance hints – currently there are much more valuable
  289. // optimizations for us to do outside of Webpack
  290. performance: {
  291. hints: false,
  292. },
  293. stats: {
  294. // Add chunk information (setting this to `false` allows for a less verbose output)
  295. chunks: false,
  296. // Add the hash of the compilation
  297. hash: false,
  298. // `webpack --colors` equivalent
  299. colors: true,
  300. // Add information about the reasons why modules are included
  301. reasons: false,
  302. // Add webpack version information
  303. version: false,
  304. },
  305. };
  306. };