2
0

webpack.config.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  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. 'collapsible',
  29. 'comments',
  30. 'core',
  31. 'date-time-chooser',
  32. 'draftail',
  33. 'expanding-formset',
  34. 'filtered-select',
  35. 'lock-unlock-action',
  36. 'modal-workflow',
  37. 'page-chooser-modal',
  38. 'page-chooser',
  39. 'page-editor',
  40. 'privacy-switch',
  41. 'sidebar',
  42. 'sidebar-legacy',
  43. 'task-chooser-modal',
  44. 'task-chooser',
  45. 'telepath/blocks',
  46. 'telepath/telepath',
  47. 'telepath/widgets',
  48. 'userbar',
  49. 'wagtailadmin',
  50. 'workflow-action',
  51. 'workflow-status',
  52. 'bulk-actions',
  53. ],
  54. 'images': ['image-chooser', 'image-chooser-telepath'],
  55. 'documents': ['document-chooser', 'document-chooser-telepath'],
  56. 'snippets': ['snippet-chooser', 'snippet-chooser-telepath'],
  57. 'contrib/table_block': ['table'],
  58. 'contrib/typed_table_block': ['typed_table_block'],
  59. };
  60. const entry = {};
  61. for (const [appName, moduleNames] of Object.entries(entrypoints)) {
  62. moduleNames.forEach((moduleName) => {
  63. entry[moduleName] = {
  64. import: [`./client/src/entrypoints/${appName}/${moduleName}.js`],
  65. filename: getOutputPath(appName, 'js', moduleName) + '.js',
  66. };
  67. });
  68. }
  69. const sassEntry = {};
  70. sassEntry[getOutputPath('admin', 'css', 'core')] = path.resolve(
  71. 'wagtail',
  72. 'admin',
  73. 'static_src',
  74. 'wagtailadmin',
  75. 'scss',
  76. 'core.scss',
  77. );
  78. sassEntry[getOutputPath('admin', 'css', 'layouts/404')] = path.resolve(
  79. 'wagtail',
  80. 'admin',
  81. 'static_src',
  82. 'wagtailadmin',
  83. 'scss',
  84. 'layouts',
  85. '404.scss',
  86. );
  87. sassEntry[getOutputPath('admin', 'css', 'layouts/account')] = path.resolve(
  88. 'wagtail',
  89. 'admin',
  90. 'static_src',
  91. 'wagtailadmin',
  92. 'scss',
  93. 'layouts',
  94. 'account.scss',
  95. );
  96. sassEntry[getOutputPath('admin', 'css', 'layouts/compare-revisions')] =
  97. path.resolve(
  98. 'wagtail',
  99. 'admin',
  100. 'static_src',
  101. 'wagtailadmin',
  102. 'scss',
  103. 'layouts',
  104. 'compare-revisions.scss',
  105. );
  106. sassEntry[getOutputPath('admin', 'css', 'layouts/home')] = path.resolve(
  107. 'wagtail',
  108. 'admin',
  109. 'static_src',
  110. 'wagtailadmin',
  111. 'scss',
  112. 'layouts',
  113. 'home.scss',
  114. );
  115. sassEntry[getOutputPath('admin', 'css', 'layouts/login')] = path.resolve(
  116. 'wagtail',
  117. 'admin',
  118. 'static_src',
  119. 'wagtailadmin',
  120. 'scss',
  121. 'layouts',
  122. 'login.scss',
  123. );
  124. sassEntry[getOutputPath('admin', 'css', 'layouts/page-editor')] =
  125. path.resolve(
  126. 'wagtail',
  127. 'admin',
  128. 'static_src',
  129. 'wagtailadmin',
  130. 'scss',
  131. 'layouts',
  132. 'page-editor.scss',
  133. );
  134. sassEntry[getOutputPath('admin', 'css', 'layouts/report')] = path.resolve(
  135. 'wagtail',
  136. 'admin',
  137. 'static_src',
  138. 'wagtailadmin',
  139. 'scss',
  140. 'layouts',
  141. 'report.scss',
  142. );
  143. sassEntry[getOutputPath('admin', 'css', 'layouts/workflow-edit')] =
  144. path.resolve(
  145. 'wagtail',
  146. 'admin',
  147. 'static_src',
  148. 'wagtailadmin',
  149. 'scss',
  150. 'layouts',
  151. 'workflow-edit.scss',
  152. );
  153. sassEntry[getOutputPath('admin', 'css', 'layouts/workflow-progress')] =
  154. path.resolve(
  155. 'wagtail',
  156. 'admin',
  157. 'static_src',
  158. 'wagtailadmin',
  159. 'scss',
  160. 'layouts',
  161. 'workflow-progress.scss',
  162. );
  163. // sassEntry[getOutputPath('admin', 'css', 'normalize')] = path.resolve('wagtail', 'admin', 'static_src', 'wagtailadmin', 'css', 'normalize.css');
  164. sassEntry[getOutputPath('admin', 'css', 'panels/draftail')] = path.resolve(
  165. 'wagtail',
  166. 'admin',
  167. 'static_src',
  168. 'wagtailadmin',
  169. 'scss',
  170. 'panels',
  171. 'draftail.scss',
  172. );
  173. sassEntry[getOutputPath('admin', 'css', 'panels/streamfield')] = path.resolve(
  174. 'wagtail',
  175. 'admin',
  176. 'static_src',
  177. 'wagtailadmin',
  178. 'scss',
  179. 'panels',
  180. 'streamfield.scss',
  181. );
  182. sassEntry[getOutputPath('admin', 'css', 'sidebar')] = path.resolve(
  183. 'wagtail',
  184. 'admin',
  185. 'static_src',
  186. 'wagtailadmin',
  187. 'scss',
  188. 'sidebar.scss',
  189. );
  190. sassEntry[getOutputPath('admin', 'css', 'userbar')] = path.resolve(
  191. 'wagtail',
  192. 'admin',
  193. 'static_src',
  194. 'wagtailadmin',
  195. 'scss',
  196. 'userbar.scss',
  197. );
  198. sassEntry[getOutputPath('documents', 'css', 'add-multiple')] = path.resolve(
  199. 'wagtail',
  200. 'documents',
  201. 'static_src',
  202. 'wagtaildocs',
  203. 'scss',
  204. 'add-multiple.scss',
  205. );
  206. sassEntry[getOutputPath('images', 'css', 'add-multiple')] = path.resolve(
  207. 'wagtail',
  208. 'images',
  209. 'static_src',
  210. 'wagtailimages',
  211. 'scss',
  212. 'add-multiple.scss',
  213. );
  214. sassEntry[getOutputPath('images', 'css', 'focal-point-chooser')] =
  215. path.resolve(
  216. 'wagtail',
  217. 'images',
  218. 'static_src',
  219. 'wagtailimages',
  220. 'scss',
  221. 'focal-point-chooser.scss',
  222. );
  223. sassEntry[getOutputPath('users', 'css', 'groups_edit')] = path.resolve(
  224. 'wagtail',
  225. 'users',
  226. 'static_src',
  227. 'wagtailusers',
  228. 'scss',
  229. 'groups_edit.scss',
  230. );
  231. sassEntry[getOutputPath('contrib/styleguide', 'css', 'styleguide')] =
  232. path.resolve(
  233. 'wagtail',
  234. 'contrib',
  235. 'styleguide',
  236. 'static_src',
  237. 'wagtailstyleguide',
  238. 'scss',
  239. 'styleguide.scss',
  240. );
  241. sassEntry[getOutputPath('contrib/modeladmin', 'css', 'index')] = path.resolve(
  242. 'wagtail',
  243. 'contrib',
  244. 'modeladmin',
  245. 'static_src',
  246. 'wagtailmodeladmin',
  247. 'scss',
  248. 'index.scss',
  249. );
  250. sassEntry[getOutputPath('contrib/modeladmin', 'css', 'breadcrumbs_page')] =
  251. path.resolve(
  252. 'wagtail',
  253. 'contrib',
  254. 'modeladmin',
  255. 'static_src',
  256. 'wagtailmodeladmin',
  257. 'scss',
  258. 'breadcrumbs_page.scss',
  259. );
  260. sassEntry[getOutputPath('contrib/modeladmin', 'css', 'choose_parent_page')] =
  261. path.resolve(
  262. 'wagtail',
  263. 'contrib',
  264. 'modeladmin',
  265. 'static_src',
  266. 'wagtailmodeladmin',
  267. 'scss',
  268. 'choose_parent_page.scss',
  269. );
  270. sassEntry[
  271. getOutputPath('contrib/typed_table_block', 'css', 'typed_table_block')
  272. ] = path.resolve(
  273. 'wagtail',
  274. 'contrib',
  275. 'typed_table_block',
  276. 'static_src',
  277. 'typed_table_block',
  278. 'scss',
  279. 'typed_table_block.scss',
  280. );
  281. return {
  282. entry: {
  283. ...entry,
  284. ...sassEntry,
  285. },
  286. output: {
  287. path: path.resolve('.'),
  288. publicPath: '/static/',
  289. },
  290. resolve: {
  291. extensions: ['.ts', '.tsx', '.js'],
  292. // Some libraries import Node modules but don't use them in the browser.
  293. // Tell Webpack to provide empty mocks for them so importing them works.
  294. fallback: {
  295. fs: false,
  296. net: false,
  297. tls: false,
  298. },
  299. },
  300. externals: {
  301. jquery: 'jQuery',
  302. },
  303. plugins: [
  304. new MiniCssExtractPlugin({
  305. filename: '[name].css',
  306. }),
  307. new CopyPlugin({
  308. patterns: [
  309. {
  310. from: 'wagtail/admin/static_src/',
  311. to: 'wagtail/admin/static/',
  312. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  313. },
  314. {
  315. from: 'wagtail/documents/static_src/',
  316. to: 'wagtail/documents/static/',
  317. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  318. },
  319. {
  320. from: 'wagtail/embeds/static_src/',
  321. to: 'wagtail/embeds/static/',
  322. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  323. },
  324. {
  325. from: 'wagtail/images/static_src/',
  326. to: 'wagtail/images/static/',
  327. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  328. },
  329. {
  330. from: 'wagtail/search/static_src/',
  331. to: 'wagtail/search/static/',
  332. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  333. },
  334. {
  335. from: 'wagtail/snippets/static_src/',
  336. to: 'wagtail/snippets/static/',
  337. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  338. },
  339. {
  340. from: 'wagtail/users/static_src/',
  341. to: 'wagtail/users/static/',
  342. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  343. },
  344. {
  345. from: 'wagtail/contrib/settings/static_src/',
  346. to: 'wagtail/contrib/settings/static/',
  347. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  348. },
  349. {
  350. from: 'wagtail/contrib/modeladmin/static_src/',
  351. to: 'wagtail/contrib/modeladmin/static/',
  352. globOptions: { ignore: ['**/{app,scss}/**', '*.{css,txt}'] },
  353. },
  354. ],
  355. }),
  356. ],
  357. module: {
  358. rules: [
  359. {
  360. test: /\.(js|ts)x?$/,
  361. loader: 'ts-loader',
  362. exclude: /node_modules/,
  363. },
  364. {
  365. // Legacy support for font icon loading, to be removed.
  366. test: /\.(woff)$/i,
  367. generator: {
  368. emit: false,
  369. filename: 'wagtailadmin/fonts/[name][ext]',
  370. },
  371. },
  372. {
  373. test: /\.(svg)$/i,
  374. type: 'asset/inline',
  375. },
  376. {
  377. test: /\.(scss|css)$/,
  378. use: [
  379. MiniCssExtractPlugin.loader,
  380. 'css-loader',
  381. {
  382. loader: 'postcss-loader',
  383. options: {
  384. postcssOptions: {
  385. plugins: ['tailwindcss', 'autoprefixer', 'cssnano'],
  386. },
  387. },
  388. },
  389. 'sass-loader',
  390. ],
  391. },
  392. ].concat(
  393. Object.keys(exposedDependencies).map((name) => {
  394. const globalName = exposedDependencies[name];
  395. // Create expose-loader configs for each Wagtail dependency.
  396. return {
  397. test: require.resolve(name),
  398. use: [
  399. {
  400. loader: 'expose-loader',
  401. options: {
  402. exposes: {
  403. globalName,
  404. override: true,
  405. },
  406. },
  407. },
  408. ],
  409. };
  410. }),
  411. ),
  412. },
  413. optimization: {
  414. splitChunks: {
  415. cacheGroups: {
  416. vendor: {
  417. name: getOutputPath('admin', 'js', 'vendor'),
  418. chunks: 'initial',
  419. minChunks: 2,
  420. reuseExistingChunk: true,
  421. },
  422. },
  423. },
  424. },
  425. // See https://webpack.js.org/configuration/devtool/.
  426. devtool: isProduction ? false : 'eval-cheap-module-source-map',
  427. // For development mode only.
  428. watchOptions: {
  429. poll: 1000,
  430. aggregateTimeout: 300,
  431. },
  432. // Disable performance hints – currently there are much more valuable
  433. // optimizations for us to do outside of Webpack
  434. performance: {
  435. hints: false,
  436. },
  437. stats: {
  438. // Add chunk information (setting this to `false` allows for a less verbose output)
  439. chunks: false,
  440. // Add the hash of the compilation
  441. hash: false,
  442. // `webpack --colors` equivalent
  443. colors: true,
  444. // Add information about the reasons why modules are included
  445. reasons: false,
  446. // Add webpack version information
  447. version: false,
  448. },
  449. };
  450. };