webpack.config.js 12 KB

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