Przeglądaj źródła

Merge pull request #28760 from overleaf/dp-hackathon-knip

Add Knip and remove a bunch of unused code

GitOrigin-RevId: 42ab99fc65973c883d2361e0027e7181767e714e
David 10 miesięcy temu
rodzic
commit
584572bb40
43 zmienionych plików z 600 dodań i 656 usunięć
  1. 517 10
      package-lock.json
  2. 0 1
      services/web/config/settings.defaults.js
  3. 0 2
      services/web/frontend/extracted-translations.json
  4. 0 0
      services/web/frontend/js/features/editor-left-menu/components/settings-menu-dropdown.tsx
  5. 0 35
      services/web/frontend/js/features/file-tree/util/safe-path.ts
  6. 0 35
      services/web/frontend/js/features/history/utils/wait-for.ts
  7. 0 16
      services/web/frontend/js/features/ide-react/editor/event-log.ts
  8. 0 10
      services/web/frontend/js/features/ide-redesign/components/error-logs/old-error-pane.tsx
  9. 1 0
      services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx
  10. 1 1
      services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx
  11. 0 30
      services/web/frontend/js/features/project-list/components/dropdown/menu-item-button.tsx
  12. 0 29
      services/web/frontend/js/features/review-panel/components/track-changes-toggle.tsx
  13. 0 70
      services/web/frontend/js/features/settings/components/linking/enable-widget.tsx
  14. 0 16
      services/web/frontend/js/features/source-editor/components/table-generator/toolbar/column-width-modal/column-width.ts
  15. 0 4
      services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts
  16. 1 35
      services/web/frontend/js/features/source-editor/extensions/vertical-overflow.ts
  17. 0 5
      services/web/frontend/js/features/source-editor/extensions/visual/html-elements.ts
  18. 0 12
      services/web/frontend/js/features/source-editor/hooks/use-codemirror-content-height.ts
  19. 0 30
      services/web/frontend/js/features/source-editor/hooks/use-codemirror-measurement.ts
  20. 0 1
      services/web/frontend/js/features/source-editor/utils/file.ts
  21. 0 4
      services/web/frontend/js/features/source-editor/utils/sub-view.ts
  22. 1 61
      services/web/frontend/js/features/source-editor/utils/tree-operations/ancestors.ts
  23. 1 35
      services/web/frontend/js/features/source-editor/utils/tree-operations/common.ts
  24. 0 8
      services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts
  25. 1 6
      services/web/frontend/js/features/source-editor/utils/tree-query.ts
  26. 1 1
      services/web/frontend/js/features/subscription/components/dashboard/redirect-alerts.tsx
  27. 1 1
      services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/writefull-bundle-management-modal.tsx
  28. 0 15
      services/web/frontend/js/features/subscription/components/dashboard/states/active/contact-support-to-change-group-plan.tsx
  29. 0 27
      services/web/frontend/js/features/subscription/components/dashboard/states/active/pending-additional-licenses.tsx
  30. 0 12
      services/web/frontend/js/features/utils/swapModal.js
  31. 1 0
      services/web/frontend/js/shared/components/labs/labs-experiments-widget.tsx
  32. 1 1
      services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx
  33. 0 1
      services/web/frontend/js/shared/hooks/use-async-with-cancel.ts
  34. 0 54
      services/web/frontend/js/shared/hooks/use-remind-me-later.ts
  35. 0 31
      services/web/frontend/js/shared/svgs/grammarly-logo.tsx
  36. 0 31
      services/web/frontend/js/shared/svgs/writefull-logo.tsx
  37. 0 4
      services/web/frontend/js/utils/operations.ts
  38. 0 17
      services/web/frontend/js/utils/service-worker-cleanup.js
  39. 1 0
      services/web/frontend/js/utils/splitTestUtils.ts
  40. 66 0
      services/web/knip.ts
  41. 0 2
      services/web/locales/en.json
  42. 4 1
      services/web/package.json
  43. 2 2
      services/web/test/frontend/features/project-list/fixtures/projects-data.ts

+ 517 - 10
package-lock.json

@@ -10524,6 +10524,40 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/@emnapi/core": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
+      "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/wasi-threads": "1.1.0",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
+      "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/wasi-threads": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+      "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
     "node_modules/@esbuild/aix-ppc64": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
@@ -13205,6 +13239,19 @@
         "node": ">= 10"
       }
     },
+    "node_modules/@napi-rs/wasm-runtime": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.5.tgz",
+      "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/core": "^1.5.0",
+        "@emnapi/runtime": "^1.5.0",
+        "@tybys/wasm-util": "^0.10.1"
+      }
+    },
     "node_modules/@nicolo-ribaudo/chokidar-2": {
       "version": "2.1.8-no-fsevents.3",
       "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
@@ -14904,6 +14951,275 @@
       "resolved": "services/web",
       "link": true
     },
+    "node_modules/@oxc-resolver/binding-android-arm-eabi": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.8.4.tgz",
+      "integrity": "sha512-6BjMji0TcvQfJ4EoSunOSyu/SiyHKficBD0V3Y0NxF0beaNnnZ7GYEi2lHmRNnRCuIPK8IuVqQ6XizYau+CkKw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-android-arm64": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.8.4.tgz",
+      "integrity": "sha512-SxF4X6rzCBS9XNPXKZGoIHIABjfGmtQpEgRBDzpDHx5VTuLAUmwLTHXnVBAZoX5bmnhF79RiMElavzFdJ2cA1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-darwin-arm64": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.8.4.tgz",
+      "integrity": "sha512-8zWeERrzgscAniE6kh1TQ4E7GJyglYsvdoKrHYLBCbHWD+0/soffiwAYxZuckKEQSc2RXMSPjcu+JTCALaY0Dw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-darwin-x64": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.8.4.tgz",
+      "integrity": "sha512-BUwggKz8Hi5uEQ0AeVTSun1+sp4lzNcItn+L7fDsHu5Cx0Zueuo10BtVm+dIwmYVVPL5oGYOeD0fS7MKAazKiw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-freebsd-x64": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.8.4.tgz",
+      "integrity": "sha512-fPO5TQhnn8gA6yP4o49lc4Gn8KeDwAp9uYd4PlE3Q00JVqU6cY9WecDhYHrWtiFcyoZ8UVBlIxuhRqT/DP4Z4A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.8.4.tgz",
+      "integrity": "sha512-QuNbdUaVGiP0W0GrXsvCDZjqeL4lZGU7aXlx/S2tCvyTk3wh6skoiLJgqUf/eeqXfUPnzTfntYqyfolzCAyBYA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.8.4.tgz",
+      "integrity": "sha512-p/zLMfza8OsC4BDKxqeZ9Qel+4eA/oiMSyKLRkMrTgt6OWQq1d5nHntjfG35Abcw4ev6Q9lRU3NOW5hj0xlUbw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.8.4.tgz",
+      "integrity": "sha512-bvJF9wWxF1+a5YZATlS5JojpOMC7OsnTatA6sXVHoOb7MIigjledYB5ZMAeRrnWWexRMiEX3YSaA46oSfOzmOg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-arm64-musl": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.8.4.tgz",
+      "integrity": "sha512-gf4nwGBfu+EFwOn5p7/T7VF4jmIdfodwJS9MRkOBHvuAm3LQgCX7O6d3Y80mm0TV7ZMRD/trfW628rHfd5++vQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.8.4.tgz",
+      "integrity": "sha512-T120R5GIzRd41rYWWKCI6cSYrZjmRQzf3X4xeE1WX396Uabz5DX8KU7RnVHihSK+KDxccCVOFBxcH3ITd+IEpw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.8.4.tgz",
+      "integrity": "sha512-PVG7SxBFFjAaQ76p9O/0Xt5mTBlziRwpck+6cRNhy/hbWY/hSt8BFfPqw0EDSfnl40Uuh+NPsHFMnaWWyxbQEg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-riscv64-musl": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.8.4.tgz",
+      "integrity": "sha512-L0OklUhM2qLGaKvPSyKmwWpoijfc++VJtPyVgz031ShOXyo0WjD0ZGzusyJMsA1a/gdulAmN6CQ/0Sf4LGXEcw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-s390x-gnu": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.8.4.tgz",
+      "integrity": "sha512-18Ajz5hqO4cRGuoHzLFUsIPod9GIaIRDiXFg2m6CS3NgVdHx7iCZscplYH7KtjdE42M8nGWYMyyq5BOk7QVgPw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-x64-gnu": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.8.4.tgz",
+      "integrity": "sha512-uHvH4RyYBdQ/lFGV9H+R1ScHg6EBnAhE3mnX+u+mO/btnalvg7j80okuHf8Qw0tLQiP5P1sEBoVeE6zviXY9IA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-linux-x64-musl": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.8.4.tgz",
+      "integrity": "sha512-X5z44qh5DdJfVhcqXAQFTDFUpcxdpf6DT/lHL5CFcdQGIZxatjc7gFUy05IXPI9xwfq39RValjJBvFovUk9XBw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-wasm32-wasi": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.8.4.tgz",
+      "integrity": "sha512-z3906y+cd8RRhBGNwHRrRAFxnKjXsBeL3+rdQjZpBrUyrhhsaV5iKD/ROx64FNJ9GjL/9mfon8A5xx/McYIqHA==",
+      "cpu": [
+        "wasm32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@napi-rs/wasm-runtime": "^1.0.5"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.8.4.tgz",
+      "integrity": "sha512-70vXFs74uA3X5iYOkpclbkWlQEF+MI325uAQ+Or2n8HJip2T0SEmuBlyw/sRL2E8zLC4oocb+1g25fmzlDVkmg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-win32-ia32-msvc": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.8.4.tgz",
+      "integrity": "sha512-SEOUAzTvr+nyMia3nx1dMtD7YUxZwuhQ3QAPnxy21261Lj0yT3JY4EIfwWH54lAWWfMdRSRRMFuGeF/dq7XjEw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@oxc-resolver/binding-win32-x64-msvc": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.8.4.tgz",
+      "integrity": "sha512-1gARIQsOPOU7LJ7jvMyPmZEVMapL/PymeG3J7naOdLZDrIZKX6CTvgawJmETYKt+8icP8M6KbBinrVkKVqFd+A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
     "node_modules/@paralleldrive/cuid2": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
@@ -18830,6 +19146,17 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/@tybys/wasm-util": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+      "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
     "node_modules/@types/accepts": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
@@ -19386,12 +19713,12 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "24.3.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz",
-      "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==",
+      "version": "24.5.2",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
+      "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
       "license": "MIT",
       "dependencies": {
-        "undici-types": "~7.10.0"
+        "undici-types": "~7.12.0"
       }
     },
     "node_modules/@types/node-fetch": {
@@ -19413,9 +19740,9 @@
       }
     },
     "node_modules/@types/node/node_modules/undici-types": {
-      "version": "7.10.0",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
-      "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+      "version": "7.12.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
+      "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
       "license": "MIT"
     },
     "node_modules/@types/normalize-package-data": {
@@ -30562,6 +30889,42 @@
         "node": ">=0.4.x"
       }
     },
+    "node_modules/formatly": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz",
+      "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fd-package-json": "^2.0.0"
+      },
+      "bin": {
+        "formatly": "bin/index.mjs"
+      },
+      "engines": {
+        "node": ">=18.3.0"
+      }
+    },
+    "node_modules/formatly/node_modules/fd-package-json": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz",
+      "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "walk-up-path": "^4.0.0"
+      }
+    },
+    "node_modules/formatly/node_modules/walk-up-path": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz",
+      "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
     "node_modules/formdata-node": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.3.2.tgz",
@@ -34931,6 +35294,84 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
+    "node_modules/knip": {
+      "version": "5.64.1",
+      "resolved": "https://registry.npmjs.org/knip/-/knip-5.64.1.tgz",
+      "integrity": "sha512-80XnLsyeXuyxj1F4+NBtQFHxaRH0xWRw8EKwfQ6EkVZZ0bSz/kqqan08k/Qg8ajWsFPhFq+0S2RbLCBGIQtuOg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/webpro"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/knip"
+        }
+      ],
+      "license": "ISC",
+      "dependencies": {
+        "@nodelib/fs.walk": "^1.2.3",
+        "fast-glob": "^3.3.3",
+        "formatly": "^0.3.0",
+        "jiti": "^2.6.0",
+        "js-yaml": "^4.1.0",
+        "minimist": "^1.2.8",
+        "oxc-resolver": "^11.8.3",
+        "picocolors": "^1.1.1",
+        "picomatch": "^4.0.1",
+        "smol-toml": "^1.4.1",
+        "strip-json-comments": "5.0.2",
+        "zod": "^4.1.11"
+      },
+      "bin": {
+        "knip": "bin/knip.js",
+        "knip-bun": "bin/knip-bun.js"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18",
+        "typescript": ">=5.0.4 <7"
+      }
+    },
+    "node_modules/knip/node_modules/jiti": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz",
+      "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jiti": "lib/jiti-cli.mjs"
+      }
+    },
+    "node_modules/knip/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/knip/node_modules/strip-json-comments": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.2.tgz",
+      "integrity": "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/known-css-properties": {
       "version": "0.30.0",
       "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz",
@@ -37630,6 +38071,22 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/napi-postinstall": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz",
+      "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "napi-postinstall": "lib/cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/napi-postinstall"
+      }
+    },
     "node_modules/native-promise-only": {
       "version": "0.8.1",
       "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
@@ -38573,6 +39030,41 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/oxc-resolver": {
+      "version": "11.8.4",
+      "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.8.4.tgz",
+      "integrity": "sha512-qpimS3tHHEf+kgESMAme+q+rj7aCzMya00u9YdKOKyX2o7q4lozjPo6d7ZTTi979KHEcVOPWdNTueAKdeNq72w==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "napi-postinstall": "^0.3.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/Boshen"
+      },
+      "optionalDependencies": {
+        "@oxc-resolver/binding-android-arm-eabi": "11.8.4",
+        "@oxc-resolver/binding-android-arm64": "11.8.4",
+        "@oxc-resolver/binding-darwin-arm64": "11.8.4",
+        "@oxc-resolver/binding-darwin-x64": "11.8.4",
+        "@oxc-resolver/binding-freebsd-x64": "11.8.4",
+        "@oxc-resolver/binding-linux-arm-gnueabihf": "11.8.4",
+        "@oxc-resolver/binding-linux-arm-musleabihf": "11.8.4",
+        "@oxc-resolver/binding-linux-arm64-gnu": "11.8.4",
+        "@oxc-resolver/binding-linux-arm64-musl": "11.8.4",
+        "@oxc-resolver/binding-linux-ppc64-gnu": "11.8.4",
+        "@oxc-resolver/binding-linux-riscv64-gnu": "11.8.4",
+        "@oxc-resolver/binding-linux-riscv64-musl": "11.8.4",
+        "@oxc-resolver/binding-linux-s390x-gnu": "11.8.4",
+        "@oxc-resolver/binding-linux-x64-gnu": "11.8.4",
+        "@oxc-resolver/binding-linux-x64-musl": "11.8.4",
+        "@oxc-resolver/binding-wasm32-wasi": "11.8.4",
+        "@oxc-resolver/binding-win32-arm64-msvc": "11.8.4",
+        "@oxc-resolver/binding-win32-ia32-msvc": "11.8.4",
+        "@oxc-resolver/binding-win32-x64-msvc": "11.8.4"
+      }
+    },
     "node_modules/p-event": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz",
@@ -44670,6 +45162,19 @@
         "npm": ">= 3.0.0"
       }
     },
+    "node_modules/smol-toml": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz",
+      "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/cyyynthia"
+      }
+    },
     "node_modules/snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -50694,9 +51199,9 @@
       }
     },
     "node_modules/zod": {
-      "version": "4.1.8",
-      "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
-      "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
+      "version": "4.1.11",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz",
+      "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==",
       "license": "MIT",
       "funding": {
         "url": "https://github.com/sponsors/colinhacks"
@@ -52810,6 +53315,7 @@
         "@types/express": "^4.17.23",
         "@types/mocha": "^9.1.0",
         "@types/mocha-each": "^2.0.4",
+        "@types/node": "^24.5.2",
         "@types/react": "^18.3.20",
         "@types/react-color": "^3.0.13",
         "@types/react-dom": "^18.3.6",
@@ -52893,6 +53399,7 @@
         "jscodeshift": "^17.0.0",
         "jsdom": "^19.0.0",
         "jsdom-global": "^3.0.2",
+        "knip": "^5.64.1",
         "match-sorter": "^6.2.0",
         "mathjax": "^3.2.2",
         "mediatr-ts": "^2.0.1",

+ 0 - 1
services/web/config/settings.defaults.js

@@ -978,7 +978,6 @@ module.exports = {
     tprFileViewRefreshButton: [],
     tprFileViewNotOriginalImporter: [],
     contactUsModal: [],
-    editorToolbarButtons: [],
     sourceEditorExtensions: [],
     sourceEditorComponents: [],
     pdfLogEntryHeaderActionComponents: [],

+ 0 - 2
services/web/frontend/extracted-translations.json

@@ -101,7 +101,6 @@
   "added_by_on": "",
   "adding": "",
   "additional_certificate": "",
-  "additional_licenses": "",
   "address_line_1": "",
   "address_second_line_optional": "",
   "adjust_column_width": "",
@@ -334,7 +333,6 @@
   "connection_lost_with_unsaved_changes": "",
   "contact_group_admin": "",
   "contact_sales": "",
-  "contact_support_to_change_group_subscription": "",
   "contact_us": "",
   "contacting_the_sales_team": "",
   "continue": "",

+ 0 - 0
services/web/frontend/js/features/editor-left-menu/components/settings-menu-dropdown.tsx


+ 0 - 35
services/web/frontend/js/features/file-tree/util/safe-path.ts

@@ -58,17 +58,6 @@ prototype\
 
 const MAX_PATH = 1024 // Maximum path length, in characters. This is fairly arbitrary.
 
-export function clean(filename: string): string {
-  filename = filename.replace(BADCHAR_RX, '_')
-  // for BADFILE_RX replace any matches with an equal number of underscores
-  filename = filename.replace(BADFILE_RX, match =>
-    new Array(match.length + 1).join('_')
-  )
-  // replace blocked filenames 'prototype' with '@prototype'
-  filename = filename.replace(BLOCKEDFILE_RX, '@$1')
-  return filename
-}
-
 export function isCleanFilename(filename: string): boolean {
   return (
     isAllowedLength(filename) &&
@@ -81,30 +70,6 @@ export function isBlockedFilename(filename: string): boolean {
   return BLOCKEDFILE_RX.test(filename)
 }
 
-// returns whether a full path is 'clean' - e.g. is a full or relative path
-// that points to a file, and each element passes the rules in 'isCleanFilename'
-export function isCleanPath(path: string): boolean {
-  const elements = path.split('/')
-
-  const lastElementIsEmpty = elements[elements.length - 1].length === 0
-  if (lastElementIsEmpty) {
-    return false
-  }
-
-  for (const element of Array.from(elements)) {
-    if (element.length > 0 && !isCleanFilename(element)) {
-      return false
-    }
-  }
-
-  // check for a top-level reserved name
-  if (BLOCKEDFILE_RX.test(path.replace(/^\/?/, ''))) {
-    return false
-  } // remove leading slash if present
-
-  return true
-}
-
 export function isAllowedLength(pathname: string): boolean {
   return pathname.length > 0 && pathname.length <= MAX_PATH
 }

+ 0 - 35
services/web/frontend/js/features/history/utils/wait-for.ts

@@ -1,35 +0,0 @@
-import { debugConsole } from '@/utils/debugging'
-
-export function waitFor<T>(
-  testFunction: () => T,
-  timeout: number,
-  pollInterval = 500
-): Promise<T> {
-  const iterationLimit = Math.floor(timeout / pollInterval)
-  let iterations = 0
-
-  return new Promise<T>((resolve, reject) => {
-    const tryIteration = () => {
-      if (iterations > iterationLimit) {
-        const err = new Error(
-          `waiting too long, ${JSON.stringify({ timeout, pollInterval })}`
-        )
-        debugConsole.error(err)
-        reject(err)
-        return
-      }
-
-      iterations += 1
-      const result = testFunction()
-
-      if (result) {
-        resolve(result)
-        return
-      }
-
-      setTimeout(tryIteration, pollInterval)
-    }
-
-    tryIteration()
-  })
-}

+ 0 - 16
services/web/frontend/js/features/ide-react/editor/event-log.ts

@@ -1,16 +0,0 @@
-import { debugConsole } from '@/utils/debugging'
-
-type EditorEvent = { type: string; meta: unknown; date: Date }
-
-// Record events and then do nothing with them.
-export class EventLog {
-  private recentEvents: EditorEvent[] = []
-
-  pushEvent = (type: string, meta: unknown = {}) => {
-    debugConsole.log('event', type, meta)
-    this.recentEvents.push({ type, meta, date: new Date() })
-    if (this.recentEvents.length > 100) {
-      return this.recentEvents.shift()
-    }
-  }
-}

+ 0 - 10
services/web/frontend/js/features/ide-redesign/components/error-logs/old-error-pane.tsx

@@ -1,10 +0,0 @@
-import PdfLogsViewer from '@/features/pdf-preview/components/pdf-logs-viewer'
-import { PdfPreviewProvider } from '@/features/pdf-preview/components/pdf-preview-provider'
-
-export default function OldErrorPane() {
-  return (
-    <PdfPreviewProvider>
-      <PdfLogsViewer alwaysVisible />
-    </PdfPreviewProvider>
-  )
-}

+ 1 - 0
services/web/frontend/js/features/ide-redesign/components/tooltip-promo.tsx

@@ -6,6 +6,7 @@ import classNames from 'classnames'
 import { useCallback, useEffect } from 'react'
 import { Overlay, OverlayProps, Popover } from 'react-bootstrap'
 
+/** @knipignore keep this file around even when there is no current promo using it */
 export default function TooltipPromotion({
   target,
   tutorialKey,

+ 1 - 1
services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx

@@ -1,4 +1,4 @@
-import { useTutorial } from '@/shared/hooks/promotions/use-tutorial'
+import useTutorial from '@/shared/hooks/promotions/use-tutorial'
 import { useEditorContext } from '@/shared/context/editor-context'
 import { useProjectContext } from '@/shared/context/project-context'
 import OLNotification from '@/shared/components/ol/ol-notification'

+ 0 - 30
services/web/frontend/js/features/project-list/components/dropdown/menu-item-button.tsx

@@ -1,30 +0,0 @@
-import { ReactNode } from 'react'
-
-type MenuItemButtonProps = {
-  children: ReactNode
-  onClick?: (e?: React.MouseEvent) => void
-  className?: string
-  afterNode?: React.ReactNode
-}
-
-export default function MenuItemButton({
-  children,
-  onClick,
-  className,
-  afterNode,
-  ...buttonProps
-}: MenuItemButtonProps) {
-  return (
-    <li role="presentation" className={className}>
-      <button
-        className="menu-item-button"
-        role="menuitem"
-        onClick={onClick}
-        {...buttonProps}
-      >
-        {children}
-      </button>
-      {afterNode}
-    </li>
-  )
-}

+ 0 - 29
services/web/frontend/js/features/review-panel/components/track-changes-toggle.tsx

@@ -1,29 +0,0 @@
-import OLFormSwitch from '@/shared/components/ol/ol-form-switch'
-
-type TrackChangesToggleProps = {
-  id: string
-  description: string
-  disabled: boolean
-  handleToggle: () => void
-  value: boolean
-}
-
-function TrackChangesToggle({
-  id,
-  description,
-  disabled,
-  handleToggle,
-  value,
-}: TrackChangesToggleProps) {
-  return (
-    <OLFormSwitch
-      id={`input-switch-${id}`}
-      disabled={disabled}
-      onChange={handleToggle}
-      checked={value}
-      label={description}
-    />
-  )
-}
-
-export default TrackChangesToggle

+ 0 - 70
services/web/frontend/js/features/settings/components/linking/enable-widget.tsx

@@ -1,79 +1,11 @@
-import { ReactNode } from 'react'
 import { useTranslation } from 'react-i18next'
 import { sendMB } from '@/infrastructure/event-tracking'
-import OLBadge from '@/shared/components/ol/ol-badge'
 import OLButton from '@/shared/components/ol/ol-button'
 
 function trackUpgradeClick() {
   sendMB('settings-upgrade-click')
 }
 
-type EnableWidgetProps = {
-  logo: ReactNode
-  title: string
-  description: string
-  helpPath: string
-  helpTextOverride?: string
-  hasFeature?: boolean
-  isPremiumFeature?: boolean
-  statusIndicator?: ReactNode
-  children?: ReactNode
-  linked?: boolean
-  handleLinkClick: () => void
-  handleUnlinkClick: () => void
-  disabled?: boolean
-}
-
-export function EnableWidget({
-  logo,
-  title,
-  description,
-  helpPath,
-  helpTextOverride,
-  hasFeature,
-  isPremiumFeature,
-  statusIndicator,
-  linked,
-  handleLinkClick,
-  handleUnlinkClick,
-  children,
-  disabled,
-}: EnableWidgetProps) {
-  const { t } = useTranslation()
-  const helpText = helpTextOverride || t('learn_more')
-
-  return (
-    <div className="settings-widget-container">
-      <div>{logo}</div>
-      <div className="description-container">
-        <div className="title-row">
-          <h4>{title}</h4>
-          {!hasFeature && isPremiumFeature && (
-            <OLBadge bg="info">{t('premium_feature')}</OLBadge>
-          )}
-        </div>
-        <p className="small">
-          {description}{' '}
-          <a href={helpPath} target="_blank" rel="noreferrer">
-            {helpText}
-          </a>
-        </p>
-        {children}
-        {hasFeature && statusIndicator}
-      </div>
-      <div>
-        <ActionButton
-          hasFeature={hasFeature}
-          linked={linked}
-          handleUnlinkClick={handleUnlinkClick}
-          handleLinkClick={handleLinkClick}
-          disabled={disabled}
-        />
-      </div>
-    </div>
-  )
-}
-
 type ActionButtonProps = {
   hasFeature?: boolean
   linked?: boolean
@@ -128,5 +60,3 @@ export function ActionButton({
     )
   }
 }
-
-export default EnableWidget

+ 0 - 16
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/column-width-modal/column-width.ts

@@ -27,19 +27,3 @@ type AbsoluteWidth = {
 }
 
 export type WidthSelection = PercentageWidth | CustomWidth | AbsoluteWidth
-
-export const isPercentageWidth = (
-  width: WidthSelection
-): width is PercentageWidth => {
-  return width.unit === '%'
-}
-
-export const isAbsoluteWidth = (
-  width: WidthSelection
-): width is AbsoluteWidth => {
-  return (ABSOLUTE_UNITS as readonly string[]).includes(width.unit)
-}
-
-export const isCustomWidth = (width: WidthSelection): width is CustomWidth => {
-  return width.unit === 'custom'
-}

+ 0 - 4
services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts

@@ -21,10 +21,6 @@ export const addNewCommentRangeEffect = StateEffect.define<Range<Decoration>>()
 
 export const removeNewCommentRangeEffect = StateEffect.define<string>()
 
-export const textSelectedEffect = StateEffect.define<null>()
-
-export const removeReviewPanelTooltipEffect = StateEffect.define()
-
 const mouseDownEffect = StateEffect.define()
 const mouseUpEffect = StateEffect.define()
 const mouseDownStateField = StateField.define<boolean>({

+ 1 - 35
services/web/frontend/js/features/source-editor/extensions/vertical-overflow.ts

@@ -1,15 +1,8 @@
-import {
-  Extension,
-  Facet,
-  StateEffect,
-  StateField,
-  TransactionSpec,
-} from '@codemirror/state'
+import { Extension, Facet, StateEffect, StateField } from '@codemirror/state'
 import {
   Decoration,
   EditorView,
   ViewPlugin,
-  ViewUpdate,
   WidgetType,
 } from '@codemirror/view'
 
@@ -222,30 +215,3 @@ const topPaddingDecoration = EditorView.decorations.compute(
     ])
   }
 )
-
-export function setVerticalOverflow(padding: VerticalPadding): TransactionSpec {
-  return {
-    effects: [setOverflowPaddingEffect.of(padding)],
-  }
-}
-
-export function updateSetsVerticalOverflow(update: ViewUpdate): boolean {
-  return update.transactions.some(tr => {
-    return tr.effects.some(effect => effect.is(setOverflowPaddingEffect))
-  })
-}
-
-export function updateChangesTopPadding(update: ViewUpdate): boolean {
-  return (
-    update.state.field(overflowPaddingState).top !==
-    update.startState.field(overflowPaddingState).top
-  )
-}
-
-export function editorVerticalTopPadding(view: EditorView): number {
-  return view.state.field(overflowPaddingState, false)?.top ?? 0
-}
-
-export function editorOverflowPadding(view: EditorView) {
-  return view.state.field(overflowPaddingState, false)
-}

+ 0 - 5
services/web/frontend/js/features/source-editor/extensions/visual/html-elements.ts

@@ -88,11 +88,6 @@ const inlineElements = new Set([
 export const isInlineElement = (node: Node): node is HTMLElement =>
   inlineElements.has(node.nodeName)
 
-const codeElements = new Set(['CODE', 'PRE'])
-
-export const isCodeElement = (node: Node): node is HTMLElement =>
-  codeElements.has(node.nodeName)
-
 const keepEmptyBlockElements = new Set(['TD', 'TH', 'CANVAS', 'DT', 'DD', 'HR'])
 
 export const shouldRemoveEmptyBlockElement = (

+ 0 - 12
services/web/frontend/js/features/source-editor/hooks/use-codemirror-content-height.ts

@@ -1,12 +0,0 @@
-import { EditorView } from '@codemirror/view'
-import useCodeMirrorMeasurement from './use-codemirror-measurement'
-
-// view.contentHeight, which is measured for us by CodeMirror and is what the
-// gutters use, is sometimes a pixel or so short of the full height of the
-// editor content, which leaves a small gap at the bottom, so use the DOM
-// scrollHeight property instead.
-const measureContentHeight = (view: EditorView) => view.contentDOM.scrollHeight
-
-export default function useCodeMirrorContentHeight() {
-  return useCodeMirrorMeasurement('content-height', measureContentHeight)
-}

+ 0 - 30
services/web/frontend/js/features/source-editor/hooks/use-codemirror-measurement.ts

@@ -1,30 +0,0 @@
-import { useCallback, useState } from 'react'
-import { useCodeMirrorViewContext } from '../components/codemirror-context'
-import { EditorView } from '@codemirror/view'
-import useEventListener from '../../../shared/hooks/use-event-listener'
-
-export default function useCodeMirrorMeasurement(
-  key: string,
-  measure: (view: EditorView) => number
-) {
-  const view = useCodeMirrorViewContext()
-  const [measurement, setMeasurement] = useState(() => measure(view))
-
-  useEventListener(
-    'editor:geometry-change',
-    useCallback(() => {
-      view.requestMeasure({
-        key,
-        read: () => measure(view),
-        write(value) {
-          // wrap the React state setter in a timeout so it doesn't run inside the CodeMirror update cycle
-          window.setTimeout(() => {
-            setMeasurement(value)
-          })
-        },
-      })
-    }, [view, measure, key])
-  )
-
-  return measurement
-}

+ 0 - 1
services/web/frontend/js/features/source-editor/utils/file.ts

@@ -44,7 +44,6 @@ function filterByType(type: 'file' | 'doc' | 'folder') {
 }
 
 export const filterFiles = filterByType('file')
-export const filterDocs = filterByType('doc')
 export const filterFolders = filterByType('folder')
 
 const IMAGE_FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'pdf']

+ 0 - 4
services/web/frontend/js/features/source-editor/utils/sub-view.ts

@@ -1,4 +0,0 @@
-import { SubView } from '../../../../../types/review-panel/review-panel'
-
-export const isCurrentFileView = (view: SubView) => view === 'cur_file'
-export const isOverviewView = (view: SubView) => view === 'overview'

+ 1 - 61
services/web/frontend/js/features/source-editor/utils/tree-operations/ancestors.ts

@@ -1,68 +1,8 @@
-import { ensureSyntaxTree, syntaxTree } from '@codemirror/language'
+import { syntaxTree } from '@codemirror/language'
 import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state'
 import { SyntaxNode, Tree } from '@lezer/common'
 import { ListEnvironment } from '../../lezer-latex/latex.terms.mjs'
 
-const HUNDRED_MS = 100
-
-export type AncestorItem = {
-  node: SyntaxNode
-  label: string
-  type?: string
-  from: number
-  to: number
-}
-
-/**
- * Get the stack of 'ancestor' nodes at the given position.
- * The first element is the most distant ancestor, while the last element
- * is the node at the position.
- */
-export function getAncestorStack(
-  state: EditorState,
-  pos: number
-): AncestorItem[] | null {
-  const tree = ensureSyntaxTree(state, pos, HUNDRED_MS)
-
-  if (!tree) {
-    return null
-  }
-
-  const stack: AncestorItem[] = []
-  const selectedNode = tree.resolve(pos, 0)
-
-  let node: SyntaxNode | null = selectedNode
-  while (node) {
-    const name = node.type.name
-    switch (name) {
-      case 'Environment':
-        {
-          const data: AncestorItem = {
-            node,
-            label: name,
-            from: node.from,
-            to: node.to,
-          }
-
-          const child = node.getChild('EnvNameGroup')
-          if (child) {
-            data.type = state.doc.sliceString(child.from + 1, child.to - 1)
-          }
-          stack.push(data)
-        }
-        break
-
-      default:
-        stack.push({ node, label: name, from: node.from, to: node.to })
-        break
-    }
-
-    node = node.parent
-  }
-
-  return stack.reverse()
-}
-
 export const wrappedNodeOfType = (
   state: EditorState,
   range: SelectionRange,

+ 1 - 35
services/web/frontend/js/features/source-editor/utils/tree-operations/common.ts

@@ -1,29 +1,9 @@
 import { ensureSyntaxTree } from '@codemirror/language'
 import { EditorState } from '@codemirror/state'
-import { IterMode, SyntaxNode, SyntaxNodeRef, Tree } from '@lezer/common'
+import { SyntaxNode } from '@lezer/common'
 
 const HUNDRED_MS = 100
 
-export function iterateDescendantsOf(
-  tree: Tree,
-  ancestors: (string | number)[],
-  spec: {
-    enter(node: SyntaxNodeRef): boolean | void
-    leave?(node: SyntaxNodeRef): void
-    from?: number | undefined
-    to?: number | undefined
-    mode?: IterMode | undefined
-  }
-) {
-  const filteredEnter = (node: SyntaxNodeRef): boolean | void => {
-    if (!ancestors.some(x => node.type.is(x))) {
-      return false
-    }
-    return spec.enter(node)
-  }
-  tree.iterate({ ...spec, enter: filteredEnter })
-}
-
 export const previousSiblingIs = (
   state: EditorState,
   pos: number,
@@ -38,20 +18,6 @@ export const previousSiblingIs = (
   return previousNode?.type.name === expectedName
 }
 
-export const nextSiblingIs = (
-  state: EditorState,
-  pos: number,
-  expectedName: string
-): boolean | null => {
-  const tree = ensureSyntaxTree(state, pos, HUNDRED_MS)
-  if (!tree) {
-    return null
-  }
-  const thisNode = tree.resolve(pos)
-  const previousNode = thisNode?.nextSibling
-  return previousNode?.type.name === expectedName
-}
-
 export const getOptionalArgumentText = (
   state: EditorState,
   optionalArgumentNode: SyntaxNode

+ 0 - 8
services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts

@@ -297,11 +297,3 @@ export function parseFigureData(
     graphicsCommandArguments,
   })
 }
-
-export const getBeginEnvSuffix = (state: EditorState, node: SyntaxNode) => {
-  const argumentNode = node
-    .getChild('OptionalArgument')
-    ?.getChild('ShortOptionalArg')
-
-  return argumentNode && state.sliceDoc(argumentNode.from, argumentNode.to)
-}

+ 1 - 6
services/web/frontend/js/features/source-editor/utils/tree-query.ts

@@ -6,11 +6,7 @@ export type {
   Outline,
 } from './tree-operations/outline'
 
-export {
-  iterateDescendantsOf,
-  previousSiblingIs,
-  nextSiblingIs,
-} from './tree-operations/common'
+export { previousSiblingIs } from './tree-operations/common'
 
 export {
   cursorIsAtBeginEnvironment,
@@ -19,7 +15,6 @@ export {
 } from './tree-operations/environments'
 
 export {
-  getAncestorStack,
   ancestorNodeOfType,
   ancestorOfNodeWithType,
   getBibkeyArgumentNode,

+ 1 - 1
services/web/frontend/js/features/subscription/components/dashboard/redirect-alerts.tsx

@@ -1,7 +1,7 @@
 import { useTranslation } from 'react-i18next'
 import OLNotification from '@/shared/components/ol/ol-notification'
 
-export function RedirectAlerts() {
+function RedirectAlerts() {
   const queryParams = new URLSearchParams(window.location.search)
   const redirectReason = queryParams.get('redirect-reason')
   const { t } = useTranslation()

+ 1 - 1
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/writefull-bundle-management-modal.tsx

@@ -112,7 +112,7 @@ function WritefullGrantedAddOn({
   )
 }
 
-export function WritefullManagedBundleAddOn() {
+function WritefullManagedBundleAddOn() {
   const { setModalIdShown } = useSubscriptionDashboardContext()
   const handleManageOnWritefull = () => setModalIdShown('manage-on-writefull')
   return (

+ 0 - 15
services/web/frontend/js/features/subscription/components/dashboard/states/active/contact-support-to-change-group-plan.tsx

@@ -1,15 +0,0 @@
-import { Trans } from 'react-i18next'
-
-export function ContactSupportToChangeGroupPlan() {
-  return (
-    <p>
-      <Trans
-        i18nKey="contact_support_to_change_group_subscription"
-        components={[
-          // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
-          <a href="/contact" />,
-        ]}
-      />
-    </p>
-  )
-}

+ 0 - 27
services/web/frontend/js/features/subscription/components/dashboard/states/active/pending-additional-licenses.tsx

@@ -1,27 +0,0 @@
-import { Trans } from 'react-i18next'
-
-export function PendingAdditionalLicenses({
-  additionalLicenses,
-  totalLicenses,
-}: {
-  additionalLicenses: number
-  totalLicenses: number
-}) {
-  return (
-    <Trans
-      i18nKey="additional_licenses"
-      values={{
-        additionalLicenses,
-        totalLicenses,
-      }}
-      shouldUnescape
-      tOptions={{ interpolation: { escapeValue: true } }}
-      components={[
-        // eslint-disable-next-line react/jsx-key
-        <strong />,
-        // eslint-disable-next-line react/jsx-key
-        <strong />,
-      ]}
-    />
-  )
-}

+ 0 - 12
services/web/frontend/js/features/utils/swapModal.js

@@ -1,12 +0,0 @@
-export function swapModal(selectorBefore, selectorAfter) {
-  const modalBefore = $(selectorBefore)
-  const modalAfter = $(selectorAfter)
-
-  // Disable the fade-out + fade-in animation when swapping the forms.
-  modalBefore.removeClass('fade')
-  modalAfter.removeClass('fade')
-  modalAfter.modal()
-  modalBefore.modal('hide')
-  modalBefore.addClass('fade')
-  modalAfter.addClass('fade')
-}

+ 1 - 0
services/web/frontend/js/shared/components/labs/labs-experiments-widget.tsx

@@ -19,6 +19,7 @@ type IntegrationLinkingWidgetProps = {
   setOptedIn: (optedIn: boolean) => void
 }
 
+/** @knipignore */
 export function LabsExperimentWidget({
   logo,
   title,

+ 1 - 1
services/web/frontend/js/shared/hooks/promotions/use-tutorial.tsx

@@ -4,7 +4,7 @@ import { postJSON } from '@/infrastructure/fetch-json'
 import { debugConsole } from '@/utils/debugging'
 import { useEditorContext } from '@/shared/context/editor-context'
 
-export const useTutorial = (
+const useTutorial = (
   tutorialKey: string,
   eventData: Record<string, any> = {}
 ) => {

+ 0 - 1
services/web/frontend/js/shared/hooks/use-async-with-cancel.ts

@@ -121,5 +121,4 @@ function useAsync<T = any, E extends Error | FetchError = Error>(
 }
 
 export default useAsync
-export type UseAsyncReturnType = ReturnType<typeof useAsync>
 export { useAsync, abortError }

+ 0 - 54
services/web/frontend/js/shared/hooks/use-remind-me-later.ts

@@ -1,54 +0,0 @@
-import { useState, useCallback, useEffect } from 'react'
-import customLocalStorage from '@/infrastructure/local-storage'
-import usePersistedState from '@/shared/hooks/use-persisted-state'
-
-/**
- * @typedef {Object} RemindMeLater
- * @property {boolean} stillDissmissed - whether the user has dismissed the notification, or if the notification is still withing the 1 day reminder period
- * @property {function} remindThemLater - saves that the user has dismissed the notification for 1 day in local storage
- * @property {function} saveDismissed - saves that the user has dismissed the notification in local storage
- */
-
-/**
- *
- * @param {string} key the unique key used to keep track of what popup is currently being shown (usually the component name)
- * @param {string} notificationLocation what page the notification originates from (eg, the editor page, project page, etc)
- * @returns {RemindMeLater} an object containing whether the notification is still dismissed, and functions to remind the user later or save that they have dismissed the notification
- */
-export default function useRemindMeLater(
-  key: string,
-  notificationLocation: string = 'editor'
-) {
-  const [dismissedUntil, setDismissedUntil] = usePersistedState<
-    Date | undefined
-  >(`${notificationLocation}.has_dismissed_${key}_until`)
-
-  const [stillDissmissed, setStillDismissed] = useState(true)
-
-  useEffect(() => {
-    const alertDismissed = customLocalStorage.getItem(
-      `${notificationLocation}.has_dismissed_${key}`
-    )
-
-    const isStillDismissed = Boolean(
-      dismissedUntil && new Date(dismissedUntil) > new Date()
-    )
-
-    setStillDismissed(alertDismissed || isStillDismissed)
-  }, [setStillDismissed, dismissedUntil, key, notificationLocation])
-
-  const remindThemLater = useCallback(() => {
-    const until = new Date()
-    until.setDate(until.getDate() + 1) // 1 day
-    setDismissedUntil(until)
-  }, [setDismissedUntil])
-
-  const saveDismissed = useCallback(() => {
-    customLocalStorage.setItem(
-      `${notificationLocation}.has_dismissed_${key}`,
-      true
-    )
-  }, [key, notificationLocation])
-
-  return { stillDissmissed, remindThemLater, saveDismissed }
-}

Plik diff jest za duży
+ 0 - 31
services/web/frontend/js/shared/svgs/grammarly-logo.tsx


Plik diff jest za duży
+ 0 - 31
services/web/frontend/js/shared/svgs/writefull-logo.tsx


+ 0 - 4
services/web/frontend/js/utils/operations.ts

@@ -22,10 +22,6 @@ export const isInsertChange = (
   change: Change<EditOperation>
 ): change is Change<InsertOperation> => isInsertOperation(change.op)
 
-export const isCommentChange = (
-  change: Change<CommentOperation>
-): change is Change<CommentOperation> => isCommentOperation(change.op)
-
 export const isDeleteChange = (
   change: Change<EditOperation>
 ): change is Change<DeleteOperation> => isDeleteOperation(change.op)

+ 0 - 17
services/web/frontend/js/utils/service-worker-cleanup.js

@@ -1,17 +0,0 @@
-export function cleanupServiceWorker() {
-  try {
-    navigator.serviceWorker
-      .getRegistrations()
-      .catch(() => {
-        // fail silently if permission not given (e.g. SecurityError)
-        return []
-      })
-      .then(registrations => {
-        registrations.forEach(worker => {
-          worker.unregister()
-        })
-      })
-  } catch (e) {
-    // fail silently if service worker are not available (on the navigator)
-  }
-}

+ 1 - 0
services/web/frontend/js/utils/splitTestUtils.ts

@@ -8,6 +8,7 @@ export function getSplitTestVariant(name: string, fallback?: string) {
   return getMeta('ol-splitTestVariants')?.[name] || fallback
 }
 
+/** @knipignore */
 export function parseIntFromSplitTest(name: string, defaultValue: number) {
   const v = getMeta('ol-splitTestVariants')?.[name]
   const n = parseInt(v, 10)

+ 66 - 0
services/web/knip.ts

@@ -0,0 +1,66 @@
+/**
+ * Configuration for knip, a tool to find unused exports in a project.
+ *
+ * To run knip, use the command: bin/npm -w services/web run knip
+ *
+ * It currently only runs in the frontend web service, but could be
+ * adapted to run in the backend as well.
+ *
+ * This is an initial version built in a hackathon. It is useful but not yet complete.
+ * In future we should add it to our CI checks but we need to make it more robust first.
+ */
+
+import type { KnipConfig } from 'knip'
+import Path from 'path'
+import settings from './config/settings.webpack'
+
+const moduleEntryPoints = Object.values(settings.overleafModuleImports).flatMap(
+  paths =>
+    paths.map(path => {
+      const cleanedPath = path.replace(Path.join(__dirname, '/'), '')
+      if (cleanedPath.match(/\.(js|jsx|ts|tsx?)$/)) {
+        return cleanedPath
+      }
+
+      return `${cleanedPath}.{js,jsx,ts,tsx}`
+    })
+)
+
+const knipConfig: KnipConfig = {
+  $schema: 'https://unpkg.com/knip@5/schema.json',
+  entry: [
+    'frontend/js/pages/**/*.{js,jsx,ts,tsx}',
+    'frontend/stories/**/*.{js,jsx,ts,tsx}',
+    'test/frontend/**/*.{js,jsx,ts,tsx}',
+    'modules/**/test/**/*.{js,jsx,ts,tsx}',
+    'modules/**/*.test.{js,jsx,ts,tsx}',
+    'modules/**/stories/**/*.{js,jsx,ts,tsx}',
+    // TODO: update this when I work out how writefull entry points work
+    'modules/writefull/**/*.{js,jsx,ts,tsx}',
+    'types/window.ts',
+    // Workers are loaded dynamically so we explicitly include all workers
+    // here until we work out a way to follow the dynamic imports
+    'modules/**/*.worker*',
+    'frontend/js/**/*.worker*',
+    ...moduleEntryPoints,
+  ],
+  project: [
+    'frontend/js/**/*.{js,jsx,ts,tsx}',
+    'modules/**/frontend/**/*.{js,jsx,ts,tsx}',
+    '!frontend/js/.storybook/**/*.{js,jsx,ts,tsx}',
+  ],
+  ignore: [
+    // TODO: Files that we should keep around even when unused.
+    // I would like to do this in the file itself but I can't seem
+    // to work out a way to do that (@knipignore only works for
+    // individual exports rather than whole files)
+    'frontend/js/shared/components/labs/labs-experiments-widget.tsx',
+    'frontend/js/features/ide-redesign/components/tooltip-promo.tsx',
+  ],
+  ignoreExportsUsedInFile: true,
+  ignoreBinaries: ['.*'],
+  ignoreDependencies: ['.*'],
+  tags: ['-knipignore'],
+}
+
+export default knipConfig

+ 0 - 2
services/web/locales/en.json

@@ -117,7 +117,6 @@
   "added_by_on": "Added by __name__ on __date__",
   "adding": "Adding",
   "additional_certificate": "Additional certificate",
-  "additional_licenses": "Your subscription includes <0>__additionalLicenses__</0> additional license(s) for a total of <1>__totalLicenses__</1> licenses.",
   "address": "Address",
   "address_line_1": "Address",
   "address_second_line_optional": "Address second line (optional)",
@@ -432,7 +431,6 @@
   "contact_message_label": "Message",
   "contact_sales": "Contact sales",
   "contact_support": "Contact Support",
-  "contact_support_to_change_group_subscription": "Please <0>contact Support</0> if you wish to change your group subscription.",
   "contact_us": "Contact us",
   "contact_us_lowercase": "Contact us",
   "contacting_the_sales_team": "Contacting the Sales team",

+ 4 - 1
services/web/package.json

@@ -71,7 +71,8 @@
     "local:test:acceptance": "npm run local:test:acceptance:app && npm run local:test:acceptance:modules",
     "local:test:unit": "npm run test:unit:all",
     "local:test:frontend": "npm run test:frontend",
-    "local:test": "npm run local:test:unit && npm run local:test:frontend && npm run local:test:acceptance"
+    "local:test": "npm run local:test:unit && npm run local:test:frontend && npm run local:test:acceptance",
+    "knip": "knip --config knip.ts"
   },
   "browserslist": [
     "defaults and supports woff2",
@@ -255,6 +256,7 @@
     "@types/express": "^4.17.23",
     "@types/mocha": "^9.1.0",
     "@types/mocha-each": "^2.0.4",
+    "@types/node": "^24.5.2",
     "@types/react": "^18.3.20",
     "@types/react-color": "^3.0.13",
     "@types/react-dom": "^18.3.6",
@@ -338,6 +340,7 @@
     "jscodeshift": "^17.0.0",
     "jsdom": "^19.0.0",
     "jsdom-global": "^3.0.2",
+    "knip": "^5.64.1",
     "match-sorter": "^6.2.0",
     "mathjax": "^3.2.2",
     "mediatr-ts": "^2.0.1",

+ 2 - 2
services/web/test/frontend/features/project-list/fixtures/projects-data.ts

@@ -94,9 +94,9 @@ export const trashedAndNotOwnedProject = {
   owner: users.picard,
 } as Project
 
-export const sharedProject = archiveableProject
+export const sharedProject = { ...archiveableProject }
 
-export const ownedProject = copyableProject
+export const ownedProject = { ...copyableProject }
 
 export const projectsData: Array<Project> = [
   copyableProject,

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików