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

Add Knip and remove a bunch of unused code

GitOrigin-RevId: 42ab99fc65973c883d2361e0027e7181767e714e
This commit is contained in:
David
2025-10-14 10:40:27 +01:00
committed by Copybot
parent 2c7c0caf07
commit 584572bb40
43 changed files with 600 additions and 672 deletions
+517 -10
View File
@@ -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",
-1
View File
@@ -978,7 +978,6 @@ module.exports = {
tprFileViewRefreshButton: [],
tprFileViewNotOriginalImporter: [],
contactUsModal: [],
editorToolbarButtons: [],
sourceEditorExtensions: [],
sourceEditorComponents: [],
pdfLogEntryHeaderActionComponents: [],
@@ -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": "",
@@ -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
}
@@ -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()
})
}
@@ -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()
}
}
}
@@ -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>
)
}
@@ -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,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'
@@ -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>
)
}
@@ -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
@@ -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
@@ -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'
}
@@ -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,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)
}
@@ -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 = (
@@ -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)
}
@@ -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
}
@@ -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']
@@ -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,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,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
@@ -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)
}
@@ -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,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()
@@ -112,7 +112,7 @@ function WritefullGrantedAddOn({
)
}
export function WritefullManagedBundleAddOn() {
function WritefullManagedBundleAddOn() {
const { setModalIdShown } = useSubscriptionDashboardContext()
const handleManageOnWritefull = () => setModalIdShown('manage-on-writefull')
return (
@@ -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>
)
}
@@ -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 />,
]}
/>
)
}
@@ -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')
}
@@ -19,6 +19,7 @@ type IntegrationLinkingWidgetProps = {
setOptedIn: (optedIn: boolean) => void
}
/** @knipignore */
export function LabsExperimentWidget({
logo,
title,
@@ -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> = {}
) => {
@@ -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 }
@@ -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 }
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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)
@@ -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)
}
}
@@ -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
View File
@@ -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
-2
View File
@@ -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
View File
@@ -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",
@@ -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,