webpack.config.js 12 KB

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