Prechádzať zdrojové kódy

Upgrade to webpack v5 (#7249)

GitOrigin-RevId: 69dc5f9aad3e045cc6904e3663e9965094eced76
Alf Eaton 4 rokov pred
rodič
commit
69a2283984

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 283 - 475
package-lock.json


+ 1 - 1
services/web/Makefile

@@ -436,7 +436,7 @@ lint: lint_flag_res_send_usage
 lint_flag_res_send_usage:
 	bin/lint_flag_res_send_usage
 
-lint: typecheck_frontend
+#lint: typecheck_frontend
 typecheck_frontend:
 	npx tsc --noEmit
 

+ 1 - 1
services/web/babel.config.json

@@ -32,7 +32,7 @@
         [
           "@babel/env",
           {
-            "targets": { "node": "12.21" },
+            "targets": { "node": "14.18" },
             "useBuiltIns": "usage",
             // This version must be aligned with the `core-js` version in `package.json`
             "corejs": { "version": 3.6 }

+ 5 - 3
services/web/cypress/plugins/index.js

@@ -1,17 +1,19 @@
 module.exports = (on, config) => {
   if (config.testingType === 'component') {
     const { startDevServer } = require('@cypress/webpack-dev-server')
-    const merge = require('webpack-merge')
+    const { merge } = require('webpack-merge')
     const path = require('path')
     const devConfig = require('../../webpack.config.dev')
 
     const webpackConfig = merge(devConfig, {
       devServer: {
-        contentBase: path.join(__dirname, '../../../../public'),
-        stats: 'none',
+        static: path.join(__dirname, '../../../../public'),
       },
+      stats: 'none',
     })
 
+    delete webpackConfig.devServer.client
+
     on('dev-server:start', options => {
       return startDevServer({ options, webpackConfig })
     })

+ 17 - 17
services/web/frontend/js/features/pdf-preview/util/pdf-js-versions.js

@@ -1,46 +1,47 @@
-// NOTE: using "legacy" build as main build requires webpack v5
-// import PDFJS from 'pdfjs-dist/webpack'
-
 // To add a new version, copy and adjust one of the `importPDFJS*` functions below,
 // add the variant to the "switch" statement, and add to `pdfjsVersions` in webpack.config.js
 
-import 'core-js/features/promise/all-settled' // polyfill for Promise.allSettled (used by pdf.js)
+import 'core-js/stable/global-this' // polyfill for globalThis (used by pdf.js)
+import 'core-js/stable/promise/all-settled' // polyfill for Promise.allSettled (used by pdf.js)
 import getMeta from '../../../utils/meta'
+import { createWorker } from '../../../utils/worker'
 
 async function importPDFJS210() {
   const cMapUrl = '/js/pdfjs-dist210/cmaps/'
   const imageResourcesPath = '/images/pdfjs-dist210'
 
-  const [PDFJS, PDFJSViewer, { default: PDFJSWorker }] = await Promise.all([
+  const [PDFJS, PDFJSViewer] = await Promise.all([
     import('pdfjs-dist210/legacy/build/pdf'),
     import('pdfjs-dist210/legacy/web/pdf_viewer'),
-    import('pdfjs-dist210/legacy/build/pdf.worker'),
     import('pdfjs-dist210/legacy/web/pdf_viewer.css'),
   ])
 
-  if (typeof window !== 'undefined' && 'Worker' in window) {
-    PDFJS.GlobalWorkerOptions.workerPort = new PDFJSWorker()
-  }
+  createWorker(() => {
+    PDFJS.GlobalWorkerOptions.workerPort = new Worker(
+      new URL('pdfjs-dist210/legacy/build/pdf.worker.js', import.meta.url)
+    )
+  })
 
-  return { PDFJS, PDFJSViewer, PDFJSWorker, cMapUrl, imageResourcesPath }
+  return { PDFJS, PDFJSViewer, cMapUrl, imageResourcesPath }
 }
 
 async function importPDFJS213() {
   const cMapUrl = '/js/pdfjs-dist213/cmaps/'
   const imageResourcesPath = '/images/pdfjs-dist213'
 
-  const [PDFJS, PDFJSViewer, { default: PDFJSWorker }] = await Promise.all([
+  const [PDFJS, PDFJSViewer] = await Promise.all([
     import('pdfjs-dist213/legacy/build/pdf'),
     import('pdfjs-dist213/legacy/web/pdf_viewer'),
-    import('pdfjs-dist213/legacy/build/pdf.worker'),
     import('pdfjs-dist213/legacy/web/pdf_viewer.css'),
   ])
 
-  if (typeof window !== 'undefined' && 'Worker' in window) {
-    PDFJS.GlobalWorkerOptions.workerPort = new PDFJSWorker()
-  }
+  createWorker(() => {
+    PDFJS.GlobalWorkerOptions.workerPort = new Worker(
+      new URL('pdfjs-dist213/legacy/build/pdf.worker.js', import.meta.url)
+    )
+  })
 
-  return { PDFJS, PDFJSViewer, PDFJSWorker, cMapUrl, imageResourcesPath }
+  return { PDFJS, PDFJSViewer, cMapUrl, imageResourcesPath }
 }
 
 async function importPDFJS() {
@@ -52,7 +53,6 @@ async function importPDFJS() {
 
     case '210':
     case 'default':
-    default:
       return importPDFJS210()
   }
 }

+ 5 - 3
services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.js

@@ -12,12 +12,14 @@ export default class PDFJSWrapper {
   }
 
   async init() {
-    const { PDFJS, PDFJSViewer, PDFJSWorker, cMapUrl, imageResourcesPath } =
-      await import('./pdf-js-versions').then(m => m.default)
+    const { PDFJS, PDFJSViewer, cMapUrl, imageResourcesPath } = await import(
+      './pdf-js-versions'
+    ).then(m => {
+      return m.default
+    })
 
     this.PDFJS = PDFJS
     this.PDFJSViewer = PDFJSViewer
-    this.PDFJSWorker = PDFJSWorker
     this.cMapUrl = cMapUrl
     this.imageResourcesPath = imageResourcesPath
 

+ 2 - 1
services/web/frontend/js/ide/editor/directives/aceEditor.js

@@ -31,7 +31,8 @@ const SearchBox = ace.require('ace/ext/searchbox')
 // Set the base path that ace will fetch modes/snippets/workers from
 if (window.aceBasePath !== '') {
   syntaxValidationEnabled = true
-  ace.config.set('basePath', `${window.aceBasePath}`)
+  ace.config.set('basePath', window.aceBasePath)
+  ace.config.set('workerPath', window.aceBasePath)
 } else {
   syntaxValidationEnabled = false
 }

+ 6 - 0
services/web/frontend/js/utils/worker.js

@@ -0,0 +1,6 @@
+export const createWorker = callback => {
+  const webpackPublicPath = __webpack_public_path__
+  __webpack_public_path__ = '/'
+  callback()
+  __webpack_public_path__ = webpackPublicPath
+}

+ 10 - 14
services/web/frontend/stylesheets/app/admin-hub.less

@@ -1,16 +1,3 @@
-.circle(@size, @background_color) {
-  display: inline-block;
-  background-color: @background_color;
-  border-radius: 50%;
-  width: @size;
-  height: @size;
-  text-align: center;
-  padding-top: @size / 6.4;
-  img {
-    height: @size - @size / 3.2;
-  }
-}
-
 .hub-header {
   h2 {
     display: inline-block;
@@ -39,7 +26,16 @@
   display: none;
 }
 .hub-circle {
-  .circle(160px, @accent-color-secondary);
+  display: inline-block;
+  background-color: @accent-color-secondary;
+  border-radius: 50%;
+  width: 160px;
+  height: 160px;
+  text-align: center;
+  //padding-top: 160px / 6.4;
+  img {
+    height: 160px - 160px / 3.2;
+  }
   padding-top: 50px;
   color: white;
 }

+ 4 - 3
services/web/frontend/stylesheets/app/editor.less

@@ -101,7 +101,8 @@
     .full-size;
     left: 20px;
     content: '';
-    background: url(/img/ol-brand/overleaf-o-grey.svg) center / 200px no-repeat;
+    background: url(../../../public/img/ol-brand/overleaf-o-grey.svg) center /
+      200px no-repeat;
     opacity: 0.2;
     pointer-events: none;
   }
@@ -276,9 +277,9 @@ Ace
   .spelling-highlight {
     z-index: 3;
     position: absolute;
-    background-image: url(/img/spellcheck-underline.png);
+    background-image: url(../../../public/img/spellcheck-underline.png);
     @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
-      background-image: url(/img/spellcheck-underline@2x.png);
+      background-image: url(../../../public/img/spellcheck-underline@2x.png);
       background-size: 5px 4px;
     }
     background-repeat: repeat-x;

+ 6 - 5
services/web/frontend/stylesheets/app/editor/review-panel.less

@@ -947,7 +947,8 @@
 
 .review-icon {
   display: inline-block;
-  background: url('/img/ol-icons/review-icon-dark-theme.svg') top/30px no-repeat;
+  background: url('../../../../public/img/ol-icons/review-icon-dark-theme.svg')
+    top/30px no-repeat;
   width: 30px;
 
   &::before {
@@ -957,15 +958,15 @@
 
 button when (@is-overleaf-light = true) {
   .review-icon {
-    background: url('/img/ol-icons/review-icon-light-theme.svg') top/30px
-      no-repeat;
+    background: url('../../../../public/img/ol-icons/review-icon-light-theme.svg')
+      top/30px no-repeat;
   }
 
   &.active,
   &:active {
     .review-icon {
-      background: url('/img/ol-icons/review-icon-dark-theme.svg') top/30px
-        no-repeat;
+      background: url('../../../../public/img/ol-icons/review-icon-dark-theme.svg')
+        top/30px no-repeat;
     }
   }
 }

+ 1 - 1
services/web/frontend/stylesheets/app/editor/rich-text.less

@@ -247,7 +247,7 @@
   }
 
   .spelling-error {
-    background-image: url(/img/spellcheck-underline.png);
+    background-image: url(../../../../public/img/spellcheck-underline.png);
     background-repeat: repeat-x;
     background-position: bottom;
   }

+ 5 - 5
services/web/frontend/stylesheets/app/homepage.less

@@ -13,7 +13,7 @@
   }
   .img {
     max-width: 420px;
-    background-image: url('/img/ol_plus_sl.png');
+    background-image: url('../../../public/img/ol_plus_sl.png');
     background-size: 100%;
     background-repeat: no-repeat;
     margin: 20px auto 0;
@@ -129,7 +129,7 @@
     max-width: 960px;
     .img {
       max-width: 960px;
-      background-image: url('/img/homepage.png');
+      background-image: url('../../../public/img/homepage.png');
       background-size: 100%;
       background-repeat: no-repeat;
       margin: auto;
@@ -142,7 +142,7 @@
       only screen and (min-resolution: 192dpi),
       only screen and (min-resolution: 2dppx) {
       .img {
-        background-image: url('/img/homepage@2x.png');
+        background-image: url('../../../public/img/homepage@2x.png');
       }
     }
   }
@@ -241,13 +241,13 @@
 }
 
 .pattern-container {
-  background: url('/img/pattern-home.png') repeat #f1f1f1;
+  background: url('../../../public/img/pattern-home.png') repeat #f1f1f1;
   border-top: 1px solid @gray-lightest;
   border-bottom: 1px solid @gray-lightest;
 }
 
 .pattern-grid {
-  background: url('/img/grid.png') repeat @content-alt-bg-color;
+  background: url('../../../public/img/grid.png') repeat @content-alt-bg-color;
   border-top: 1px solid @gray-lighter;
   border-bottom: 1px solid @gray-lighter;
 }

+ 5 - 5
services/web/frontend/stylesheets/app/login-register.less

@@ -117,7 +117,7 @@
   position: absolute;
   top: 4px;
   left: 4px;
-  background: #fff url(/img/brand/lion.svg) center/20px no-repeat;
+  background: #fff url(../../../public/img/brand/lion.svg) center/20px no-repeat;
   border-radius: 99999px;
   width: 26px;
   height: 26px;
@@ -128,16 +128,16 @@
 }
 
 .login-btn-icon-ieee {
-  background-image: url(/img/other-brands/logo_ieee.svg);
+  background-image: url(../../../public/img/other-brands/logo_ieee.svg);
 }
 .login-btn-icon-google {
-  background-image: url(/img/other-brands/logo_google.svg);
+  background-image: url(../../../public/img/other-brands/logo_google.svg);
 }
 .login-btn-icon-twitter {
-  background-image: url(/img/other-brands/logo_twitter.svg);
+  background-image: url(../../../public/img/other-brands/logo_twitter.svg);
 }
 .login-btn-icon-orcid {
-  background-image: url(/img/other-brands/logo_orcid.svg);
+  background-image: url(../../../public/img/other-brands/logo_orcid.svg);
 }
 .login-btn-icon-sharelatex {
   background-size: 22px;

+ 1 - 1
services/web/frontend/stylesheets/app/sprites.less

@@ -1,5 +1,5 @@
 .sprite-icon {
-  background-image: url('/img/sprite.png');
+  background-image: url('../../../public/img/sprite.png');
 }
 
 .sprite-icon-ko {

+ 3 - 3
services/web/frontend/stylesheets/core/ol-light-variables.less

@@ -76,8 +76,8 @@
 @toolbar-btn-hover-bg-color: @ol-blue-gray-0;
 @toolbar-icon-btn-color: @ol-blue-gray-3;
 @toolbar-icon-btn-hover-color: @ol-blue-gray-3;
-@editor-header-logo-background: url(/img/ol-brand/overleaf-o.svg) center /
-  contain no-repeat;
+@editor-header-logo-background: url(../../../public/img/ol-brand/overleaf-o.svg)
+  center / contain no-repeat;
 @project-name-color: @ol-blue-gray-3;
 @project-rename-link-color: @ol-blue-gray-3;
 @project-rename-link-color-hover: @ol-blue-gray-4;
@@ -97,7 +97,7 @@
 @navbar-title-color: @ol-blue-gray-1;
 @navbar-title-color-hover: @ol-blue-gray-2;
 @navbar-default-color: @ol-blue-gray-3;
-@navbar-brand-image-url: url(/img/ol-brand/overleaf.svg);
+@navbar-brand-image-url: url(../../../public/img/ol-brand/overleaf.svg);
 
 @navbar-subdued-color: @ol-blue-gray-3;
 @navbar-subdued-hover-bg: @ol-blue-gray-1;

+ 5 - 5
services/web/frontend/stylesheets/core/variables.less

@@ -48,11 +48,11 @@
 @accent-color-primary: @ol-green;
 @accent-color-secondary: @ol-dark-green;
 
-@editor-header-logo-background: url(/img/ol-brand/overleaf-o-white.svg) center /
-  contain no-repeat;
+@editor-header-logo-background: url(../../../public/img/ol-brand/overleaf-o-white.svg)
+  center / contain no-repeat;
 @editor-loading-logo-padding-top: 115.44%;
-@editor-loading-logo-background-url: url(/img/ol-brand/overleaf-o-grey.svg);
-@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg);
+@editor-loading-logo-background-url: url(../../../public/img/ol-brand/overleaf-o-grey.svg);
+@editor-loading-logo-foreground-url: url(../../../public/img/ol-brand/overleaf-o.svg);
 @editor-search-count-color: @ol-blue-gray-4;
 
 //== Scaffolding
@@ -897,7 +897,7 @@
 @navbar-subdued-color: #fff;
 @navbar-subdued-hover-bg: #fff;
 @navbar-subdued-hover-color: @ol-green;
-@navbar-brand-image-url: url(/img/ol-brand/overleaf-white.svg);
+@navbar-brand-image-url: url(../../../public/img/ol-brand/overleaf-white.svg);
 
 @dropdown-divider-margin: 6px;
 @dropdown-item-padding: 4px 20px;

+ 22 - 23
services/web/package.json

@@ -19,7 +19,7 @@
     "test:karma:single": "karma start --no-auto-watch --single-run",
     "start": "node $NODE_APP_OPTIONS app.js",
     "nodemon": "nodemon $NODE_APP_OPTIONS --config nodemon.json",
-    "webpack": "webpack-dev-server --config webpack.config.dev.js",
+    "webpack": "webpack serve --config webpack.config.dev.js",
     "webpack:production": "webpack --config webpack.config.prod.js",
     "webpack:profile": "webpack --config webpack.config.prod.js --profile --json > stats.json",
     "format": "prettier --list-different $PWD/'**/*.{js,ts,tsx}'",
@@ -210,8 +210,8 @@
   "devDependencies": {
     "@babel/register": "^7.14.5",
     "@cypress/react": "^5.12.1",
-    "@cypress/webpack-preprocessor": "^5.11.1",
     "@cypress/webpack-dev-server": "^1.8.0",
+    "@cypress/webpack-preprocessor": "^5.11.1",
     "@juggle/resize-observer": "^3.3.1",
     "@testing-library/cypress": "^8.0.2",
     "@testing-library/dom": "^7.31.2",
@@ -229,7 +229,7 @@
     "acorn-walk": "^7.1.1",
     "angular-mocks": "~1.8.0",
     "autoprefixer": "^9.7.6",
-    "babel-loader": "^8.2.2",
+    "babel-loader": "^8.2.4",
     "babel-plugin-angularjs-annotate": "^0.10.0",
     "babel-plugin-macros": "^3.1.0",
     "c8": "^7.2.0",
@@ -239,8 +239,9 @@
     "chaid": "^1.0.2",
     "cheerio": "^1.0.0-rc.3",
     "content-disposition": "^0.5.0",
-    "copy-webpack-plugin": "^5.1.1",
-    "css-loader": "^3.5.2",
+    "copy-webpack-plugin": "^10.2.4",
+    "css-loader": "^6.7.1",
+    "css-minimizer-webpack-plugin": "^3.4.1",
     "cypress": "^9.5.1",
     "es6-promise": "^4.2.8",
     "escodegen": "^2.0.0",
@@ -259,35 +260,33 @@
     "eslint-plugin-react": "^7.27.0",
     "eslint-plugin-react-hooks": "^4.3.0",
     "eslint-plugin-standard": "^5.0.0",
-    "expose-loader": "^0.7.5",
+    "expose-loader": "^3.1.0",
     "fetch-mock": "^9.10.2",
-    "file-loader": "^5.0.2",
     "glob": "^7.1.6",
     "handlebars-loader": "^1.7.1",
     "i18next-scanner": "^3.0.0",
-    "jsdom": "^16.7.0",
+    "jsdom": "^19.0.0",
     "jsdom-global": "^3.0.2",
-    "karma": "^5.0.2",
+    "karma": "^6.3.17",
     "karma-chai-sinon": "^0.1.5",
     "karma-chrome-launcher": "^3.1.0",
     "karma-mocha": "^2.0.0",
     "karma-mocha-reporter": "^2.2.5",
     "karma-requirejs": "^1.1.0",
-    "karma-webpack": "^4.0.2",
+    "karma-webpack": "^5.0.0",
     "less": "^3.11.1",
-    "less-loader": "^6.0.0",
+    "less-loader": "^10.2.0",
     "less-plugin-autoprefix": "^2.0.0",
     "mensch": "^0.3.4",
-    "mini-css-extract-plugin": "^0.8.0",
+    "mini-css-extract-plugin": "^2.6.0",
     "mkdirp": "0.5.1",
     "mocha": "^8.4.0",
     "mock-fs": "^5.1.2",
     "nock": "^13.1.1",
     "node-fetch": "^2.6.7",
     "nodemon": "^2.0.6",
-    "optimize-css-assets-webpack-plugin": "^5.0.3",
     "pirates": "^4.0.1",
-    "postcss-loader": "^3.0.0",
+    "postcss-loader": "^6.2.1",
     "prettier": "2.5.1",
     "requirejs": "^2.3.6",
     "samlp": "^3.4.1",
@@ -296,16 +295,16 @@
     "sinon-chai": "^3.7.0",
     "sinon-mongoose": "^2.3.0",
     "socket.io-mock": "^1.3.1",
-    "terser-webpack-plugin": "^2.3.6",
+    "terser-webpack-plugin": "^5.3.1",
     "timekeeper": "^2.2.0",
-    "to-string-loader": "^1.1.6",
+    "to-string-loader": "^1.2.0",
     "typescript": "^4.5.5",
-    "val-loader": "^1.1.1",
-    "webpack": "^4.44.2",
-    "webpack-assets-manifest": "^4.0.6",
-    "webpack-cli": "^3.3.11",
-    "webpack-dev-server": "^3.11.0",
-    "webpack-merge": "^4.2.2",
-    "worker-loader": "^2.0.0"
+    "val-loader": "^4.0.0",
+    "webpack": "^5.71.0",
+    "webpack-assets-manifest": "^5.1.0",
+    "webpack-cli": "^4.9.2",
+    "webpack-dev-server": "^4.7.4",
+    "webpack-merge": "^5.8.0",
+    "worker-loader": "^3.0.8"
   }
 }

+ 1 - 1
services/web/tsconfig.json

@@ -1,7 +1,7 @@
 {
   "compilerOptions": {
     "target": "esnext" /* Specify ECMAScript target version */,
-    "module": "commonjs" /* Specify module code generation */,
+    "module": "es2020" /* Specify module code generation */,
     "allowJs": true /* Allow JavaScript files to be compiled. */,
     //    "checkJs": true /* Report errors in .js files. */,
     "jsx": "preserve" /* Specify JSX code generation */,

+ 2 - 0
services/web/types/globals.d.ts

@@ -0,0 +1,2 @@
+// eslint-disable-next-line camelcase,no-unused-vars
+declare let __webpack_public_path__: string

+ 21 - 14
services/web/webpack.config.dev.js

@@ -1,5 +1,5 @@
 const webpack = require('webpack')
-const merge = require('webpack-merge')
+const { merge } = require('webpack-merge')
 const MiniCssExtractPlugin = require('mini-css-extract-plugin')
 
 const base = require('./webpack.config')
@@ -28,19 +28,26 @@ module.exports = merge(base, {
     // Expose dev server at www.dev-overleaf.com
     host: '0.0.0.0',
     port: 3808,
-    public: 'www.dev-overleaf.com:443',
-
-    // Customise output to the (node) console
-    stats: {
-      colors: true, // Enable some coloured highlighting
-      // Hide some overly verbose output
-      performance: false, // Disable as code is uncompressed in dev mode
-      hash: false,
-      version: false,
-      chunks: false,
-      modules: false,
-      // Hide copied assets from output
-      excludeAssets: [/^js\/ace/, /^js\/libs/, /^js\/cmaps/],
+    client: {
+      webSocketURL: 'auto://0.0.0.0:0/ws',
+    },
+    allowedHosts: '.dev-overleaf.com',
+    setupMiddlewares(middlewares, devServer) {
+      devServer.app.get('/status', (req, res) => res.send('webpack is up'))
+      return middlewares
     },
   },
+
+  // Customise output to the (node) console
+  stats: {
+    colors: true, // Enable some coloured highlighting
+    // Hide some overly verbose output
+    performance: false, // Disable as code is uncompressed in dev mode
+    hash: false,
+    version: false,
+    chunks: false,
+    modules: false,
+    // Hide copied assets from output
+    excludeAssets: [/^js\/ace/, /^js\/libs/, /^js\/cmaps/],
+  },
 })

+ 88 - 126
services/web/webpack.config.js

@@ -9,7 +9,6 @@ const PackageVersions = require('./app/src/infrastructure/PackageVersions')
 
 // Generate a hash of entry points, including modules
 const entryPoints = {
-  serviceWorker: './frontend/js/serviceWorker.js',
   main: './frontend/js/main.js',
   ide: './frontend/js/ide.js',
   'ide-detached': './frontend/js/ide-detached.js',
@@ -19,6 +18,13 @@ const entryPoints = {
   'light-style': './frontend/stylesheets/light-style.less',
 }
 
+// ServiceWorker at /serviceWorker.js
+entryPoints.serviceWorker = {
+  import: './frontend/js/serviceWorker.js',
+  publicPath: '/',
+  filename: 'serviceWorker.js',
+}
+
 // Add entrypoints for each "page"
 glob
   .sync(path.join(__dirname, 'modules/*/frontend/js/pages/**/*.js'))
@@ -72,7 +78,7 @@ module.exports = {
     publicPath: '/',
 
     // By default write into js directory
-    filename: 'js/[name].js',
+    filename: 'js/[name]-[contenthash].js',
 
     // Output as UMD bundle (allows main JS to import with CJS, AMD or global
     // style code bundles
@@ -101,36 +107,7 @@ module.exports = {
             },
           },
         ],
-      },
-      {
-        // Wrap worker scripts in a Web Worker
-        test: /\.worker\.js$/,
-        use: [
-          {
-            loader: 'worker-loader',
-            options: {
-              // Write into js directory (note: customising this is not possible
-              // with pdfjs-dist/webpack auto loader)
-              name: 'js/[name].[contenthash].js',
-              // Override dynamically-set publicPath to explicitly use root.
-              // This prevents a security problem where the Worker - normally
-              // loaded from a CDN - has cross-origin issues, by forcing it to not
-              // be loaded from the CDN
-              publicPath: '/',
-            },
-          },
-        ],
-      },
-      {
-        test: /serviceWorker.js$/,
-        use: [
-          {
-            loader: 'worker-loader',
-            options: {
-              name: 'serviceWorker.js',
-            },
-          },
-        ],
+        type: 'javascript/auto',
       },
       {
         // Pass Less files through less-loader/css-loader/mini-css-extract-
@@ -145,9 +122,9 @@ module.exports = {
             // Runs autoprefixer on CSS via postcss
             loader: 'postcss-loader',
             options: {
-              // Uniquely identifies the postcss plugin (required by webpack)
-              ident: 'postcss',
-              plugins: [require('autoprefixer')],
+              postcssOptions: {
+                plugins: ['autoprefixer'],
+              },
             },
           },
           // Compiles the Less syntax to CSS
@@ -162,32 +139,18 @@ module.exports = {
       {
         // Load fonts
         test: /\.(woff|woff2)$/,
-        use: [
-          {
-            loader: 'file-loader',
-            options: {
-              // Output to public/font
-              outputPath: 'fonts',
-              publicPath: '/fonts/',
-              name: '[name].[ext]',
-            },
-          },
-        ],
+        type: 'asset/resource',
+        generator: {
+          filename: 'fonts/[name]-[contenthash][ext]',
+        },
       },
       {
         // Load images (static files)
         test: /\.(svg|gif|png|jpg|pdf)$/,
-        use: [
-          {
-            loader: 'file-loader',
-            options: {
-              // Output to public/images
-              outputPath: 'images',
-              publicPath: '/images/',
-              name: '[name].[ext]',
-            },
-          },
-        ],
+        type: 'asset/resource',
+        generator: {
+          filename: 'images/[name]-[contenthash][ext]',
+        },
       },
       {
         // These options are necessary for handlebars to have access to helper
@@ -234,21 +197,9 @@ module.exports = {
         use: [
           {
             loader: 'expose-loader',
-            options: 'jQuery',
-          },
-          {
-            loader: 'expose-loader',
-            options: '$',
-          },
-        ],
-      },
-      {
-        // Expose angular global variable
-        test: require.resolve('angular'),
-        use: [
-          {
-            loader: 'expose-loader',
-            options: 'angular',
+            options: {
+              exposes: ['$', 'jQuery'],
+            },
           },
         ],
       },
@@ -269,13 +220,8 @@ module.exports = {
     },
     symlinks: false,
     extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
-  },
-
-  // Split out files into separate (derived) bundles if they are shared between
-  // multiple (explicit) bundles, according to some webpack heuristics
-  optimization: {
-    splitChunks: {
-      chunks: 'all',
+    fallback: {
+      events: require.resolve('events'),
     },
   },
 
@@ -285,6 +231,13 @@ module.exports = {
     new WebpackAssetsManifest({
       entrypoints: true,
       publicPath: true,
+      output: 'manifest.json',
+    }),
+
+    // Ensure that process.env.RESET_APP_DATA_TIMER is defined, to avoid an error.
+    // https://github.com/algolia/algoliasearch-client-javascript/issues/756
+    new webpack.EnvironmentPlugin({
+      RESET_APP_DATA_TIMER: '120000',
     }),
 
     // Prevent moment from loading (very large) locale files that aren't used
@@ -294,62 +247,71 @@ module.exports = {
     }),
 
     // Copy the required files for loading MathJax from MathJax NPM package
-    new CopyPlugin(
-      [
-        { from: 'MathJax.js', to: 'js/libs/mathjax' },
-        { from: 'config/**/*', to: 'js/libs/mathjax' },
-        { from: 'extensions/**/*', to: 'js/libs/mathjax' },
-        { from: 'localization/en/**/*', to: 'js/libs/mathjax' },
-        { from: 'jax/output/HTML-CSS/fonts/TeX/**/*', to: 'js/libs/mathjax' },
-        { from: 'jax/output/HTML-CSS/**/*.js', to: 'js/libs/mathjax' },
-        { from: 'jax/element/**/*', to: 'js/libs/mathjax' },
-        { from: 'jax/input/**/*', to: 'js/libs/mathjax' },
-        { from: 'fonts/HTML-CSS/TeX/woff/*', to: 'js/libs/mathjax' },
-      ],
-      {
-        context: mathjaxDir,
-      }
-    ),
-
-    new CopyPlugin(
-      [
+    new CopyPlugin({
+      patterns: [
+        { from: 'MathJax.js', to: 'js/libs/mathjax', context: mathjaxDir },
+        { from: 'config/**/*', to: 'js/libs/mathjax', context: mathjaxDir },
+        {
+          from: 'extensions/**/*',
+          globOptions: {
+            // https://github.com/mathjax/MathJax/issues/2403
+            ignore: ['**/mathmaps/*.js'],
+          },
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
+        {
+          from: 'localization/en/**/*',
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
+        {
+          from: 'jax/output/HTML-CSS/fonts/TeX/**/*',
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
+        {
+          from: 'jax/output/HTML-CSS/**/*.js',
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
+        {
+          from: 'jax/element/**/*',
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
+        { from: 'jax/input/**/*', to: 'js/libs/mathjax', context: mathjaxDir },
+        {
+          from: 'fonts/HTML-CSS/TeX/woff/*',
+          to: 'js/libs/mathjax',
+          context: mathjaxDir,
+        },
         {
           from: 'libs/sigma-master',
           to: 'js/libs/sigma-master',
+          context: vendorDir,
         },
-      ],
-      {
-        context: vendorDir,
-      }
-    ),
-
-    new CopyPlugin(
-      [
         {
           from: 'src-min-noconflict',
           to: `js/ace-${PackageVersions.version.ace}/`,
+          context: aceDir,
         },
-      ],
-      {
-        context: aceDir,
-      }
-    ),
+        ...pdfjsVersions.flatMap(version => {
+          const dir = getModuleDirectory(version)
 
-    new CopyPlugin([
-      ...pdfjsVersions.flatMap(version => {
-        const dir = getModuleDirectory(version)
+          // Copy CMap files (used to provide support for non-Latin characters)
+          // and static images from pdfjs-dist package to build output.
 
-        // Copy CMap files (used to provide support for non-Latin characters)
-        // and static images from pdfjs-dist package to build output.
-
-        return [
-          { from: `${dir}/cmaps`, to: `js/${version}/cmaps` },
-          {
-            from: `${dir}/legacy/web/images`,
-            to: `images/${version}`,
-          },
-        ]
-      }),
-    ]),
+          return [
+            { from: `cmaps`, to: `js/${version}/cmaps`, context: dir },
+            {
+              from: `legacy/web/images`,
+              to: `images/${version}`,
+              context: dir,
+            },
+          ]
+        }),
+      ],
+    }),
   ],
 }

+ 5 - 32
services/web/webpack.config.prod.js

@@ -1,13 +1,11 @@
-const merge = require('webpack-merge')
+const { merge } = require('webpack-merge')
 const TerserPlugin = require('terser-webpack-plugin')
-const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
+const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
 const MiniCssExtractPlugin = require('mini-css-extract-plugin')
 
 const base = require('./webpack.config')
 
-// Use "smart" merge: attempts to combine loaders targeting the same file type,
-// overriding the base config
-module.exports = merge.smart(
+module.exports = merge(
   base,
   {
     mode: 'production',
@@ -15,31 +13,6 @@ module.exports = merge.smart(
     // Enable a full source map. Generates a comment linking to the source map
     devtool: 'hidden-source-map',
 
-    output: {
-      // Override filename to include hash for immutable caching
-      filename: 'js/[name]-[chunkhash].js',
-    },
-
-    module: {
-      rules: [
-        {
-          // Override base font loading to add hash to filename so that we can
-          // use "immutable" caching
-          test: /\.(woff|woff2)$/,
-          use: [
-            {
-              loader: 'file-loader',
-              options: {
-                outputPath: 'fonts',
-                publicPath: '/fonts/',
-                name: '[name]-[hash]-1.[ext]',
-              },
-            },
-          ],
-        },
-      ],
-    },
-
     optimization: {
       // Minify JS (with Terser) and CSS (with cssnano)
       minimizer: [
@@ -49,7 +22,7 @@ module.exports = merge.smart(
             keep_fnames: /(Error|Exception)$/,
           },
         }),
-        new OptimizeCssAssetsPlugin(),
+        new CssMinimizerPlugin(),
       ],
     },
 
@@ -58,7 +31,7 @@ module.exports = merge.smart(
       new MiniCssExtractPlugin({
         // Output to public/stylesheets directory and append hash for immutable
         // caching
-        filename: 'stylesheets/[name]-[chunkhash].css',
+        filename: 'stylesheets/[name]-[contenthash].css',
       }),
     ],
   },

+ 8 - 6
services/web/webpack.config.test.js

@@ -1,14 +1,16 @@
 const MiniCssExtractPlugin = require('mini-css-extract-plugin')
-const merge = require('webpack-merge')
+const { merge } = require('webpack-merge')
 
 const base = require('./webpack.config')
 
-module.exports = merge(base, {
+const config = merge(base, {
   mode: 'development',
 
   plugins: [new MiniCssExtractPlugin()],
-
-  // Karma configures entry & output for us, so disable these
-  entry: null,
-  output: null,
 })
+
+// Karma configures entry & output for us, so disable these
+delete config.entry
+delete config.output
+
+module.exports = config

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov