webpack.config.js 7.8 KB

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