Files
overleaf-cep/services/web/package.json
Jakob Ackermann fd647002f5 [monorepo] enable caching for eslint and prettier (#30967)
* [monorepo] enable caching for eslint/prettier/stylelint

* [monorepo] speed up prettier by swapping --list-different for --check

--list-different will print each file that it processes. We have a lot
of files in the monorepo. Using --check only prints mismatching files.

Co-authored-by: Rebeka <rebeka.dekany@overleaf.com>

* [monorepo] explicitly configure prettier cache-location

This is the default location. Prettier will only discover that location
if the top level node_modules folder is writable, which is not the case
in CI. We create the .cache folder outside of docker, writable to node
inside docker.

The proper fix would be in prettier, to only check for write access in
the cache folder. Something to raise/upstream another day.

* [monorepo] run top-level format/format_fix in a single container

With the cache in place, it is much faster to use a single container.
As there is a single shared cache file, concurrent processes may see a
partially (re-)written cache file and bail out.

- all in a single container: 24s
- previous with -j4: 41s
- previous with -j8: failed due to corrupted cache file

---------

Co-authored-by: Rebeka <rebeka.dekany@overleaf.com>
GitOrigin-RevId: 7850a3a980ae6c836393d97fe56a6316ffc3fa18
2026-02-06 09:05:44 +00:00

418 lines
18 KiB
JSON

{
"name": "@overleaf/web",
"description": "The HTTP front end for Overleaf",
"private": true,
"main": "app.mjs",
"directories": {
"public": "./public"
},
"scripts": {
"test:acceptance:run_dir": "mocha --recursive --timeout 25000 --grep=$MOCHA_GREP --require test/acceptance/bootstrap.js",
"test:acceptance:app": "npm run test:acceptance:run_dir -- test/acceptance/src",
"test:unit:run_dir": "bin/test_unit_run_dir",
"test:unit:all": "npm run test:unit:run_dir -- test/unit/src modules/*/test/unit/src",
"test:unit:all:silent": "npm run test:unit:all -- --reporter dot",
"test:unit:app": "npm run test:unit:run_dir -- test/unit/src",
"test:unit": "vitest run",
"test:unit:parallel": "vitest run --project=Parallel",
"test:unit:sequential": "vitest run --project=Sequential",
"test:unit:watch": "vitest",
"test:frontend": "NODE_ENV=test TZ=GMT mocha --recursive --timeout 5000 --exit --extension js,jsx,mjs,ts,tsx --grep=$MOCHA_GREP --require test/frontend/bootstrap.js --ignore '**/*.spec.{js,jsx,ts,tsx}' --ignore '**/helpers/**/*.{js,jsx,ts,tsx}' test/frontend modules/*/test/frontend",
"test:frontend:coverage": "c8 --all --include 'frontend/js' --include 'modules/*/frontend/js' --exclude 'frontend/js/vendor' --reporter=lcov --reporter=text-summary npm run test:frontend",
"test:writefull": "vitest --run --config modules/writefull/frontend/js/integration/vitest.config.ts",
"start": "node app.mjs",
"nodemon": "node --watch app.mjs --watch-locales",
"webpack": "webpack serve --config webpack.config.dev.js",
"webpack:production": "webpack --config webpack.config.prod.js",
"webpack:profile": "webpack --config webpack.config.prod.js --profile --json > stats.json",
"lint": "eslint --cache --cache-location ../../node_modules/.cache/eslint/ --max-warnings 0 --format unix --ext .js,.jsx,.mjs,.ts,.tsx .",
"lint:fix": "eslint --cache --cache-location ../../node_modules/.cache/eslint/ --fix --ext .js,.jsx,.mjs,.ts,.tsx .",
"lint:styles": "stylelint --cache --cache-location ../../node_modules/.cache/stylelint/ '**/*.scss'",
"lint:styles:fix": "stylelint --cache --cache-location ../../node_modules/.cache/stylelint/ '**/*.scss' --fix",
"type-check": "tsc --noEmit",
"type-check:backend": "tsc -p tsconfig.backend.json --noEmit",
"extract-translations": "i18next-scanner",
"convert-themes": "node frontend/js/features/source-editor/themes/scripts/convert.js",
"cypress:open-ct": "OVERLEAF_CONFIG=$PWD/config/settings.webpack.js cypress open --component",
"cypress:run-ct": "OVERLEAF_CONFIG=$PWD/config/settings.webpack.js cypress run --component --browser chrome",
"cypress:docker:open-ct": "DOCKER_USER=\"$(id -u):$(id -g)\" docker compose -f docker-compose.cypress.yml run --rm cypress run cypress:open-ct",
"cypress:docker:run-ct": "DOCKER_USER=\"$(id -u):$(id -g)\" docker compose -f docker-compose.cypress.yml run --rm cypress run cypress:run-ct --browser chrome",
"lezer-latex:generate": "node scripts/lezer-latex/generate.mjs",
"lezer-latex:run": "node scripts/lezer-latex/run.mjs",
"lezer-latex:benchmark": "node scripts/lezer-latex/benchmark.mjs",
"lezer-latex:benchmark-incremental": "node scripts/lezer-latex/test-incremental-parser.mjs",
"routes": "bin/routes",
"storybook": "storybook dev -p 6006 --no-open",
"build-storybook": "storybook build",
"build-unfilled-fonts": "node frontend/fonts/material-symbols/build-unfilled.mjs",
"precompile-pug": "node app/src/infrastructure/Views.mjs",
"local:nodemon": "set -a;. ../../config/dev-environment.env;. ./docker-compose.common.env;. ../../config/local-dev.env;. ./local-dev.env;. ../../config/local.env; set +a; echo $OVERLEAF_CONFIG; WEB_PORT=13000 LISTEN_ADDRESS=0.0.0.0 npm run nodemon",
"local:webpack": "set -a;. ../../config/dev-environment.env;. ./docker-compose.common.env;. ../../config/local-dev.env;. ./local-dev.env;. ../../config/local.env; set +a; PORT=13808 OVERLEAF_CONFIG=$(pwd)/config/settings.webpack.js npm run webpack",
"local:test:acceptance:run_dir": "set -a;. $(pwd)/docker-compose.common.env;. $(pwd)/local-test.env; set +a; npm run test:acceptance:run_dir",
"local:test:acceptance:run_app": "OVERLEAF_CONFIG=$(pwd)/test/acceptance/config/settings.test.${OVERLEAF_APP}.js npm run local:test:acceptance:run_dir -- $(pwd)/test/acceptance/src",
"local:test:acceptance:run_module": "if [ ! -d $(pwd)/modules/${MODULE}/test/acceptance ]; then echo \"Module '${MODULE}' does not have acceptance tests\"; exit 0; fi; OVERLEAF_CONFIG=$(pwd)/modules/${MODULE}/test/acceptance/config/settings.test.js BASE_CONFIG=$(pwd)/test/acceptance/config/settings.test.${OVERLEAF_APP}.js npm run local:test:acceptance:run_dir -- $(pwd)/modules/${MODULE}/test/acceptance",
"local:test:acceptance:run_modules": "OVERLEAF_CONFIG=$(pwd)/test/acceptance/config/settings.test.${OVERLEAF_APP}.js node $(pwd)/test/acceptance/getModuleTargets --name-only | xargs -n1 sh -c 'MODULE=$0 npm run local:test:acceptance:run_module || exit 255' $1",
"local:test:acceptance:app:saas": "OVERLEAF_APP=saas npm run local:test:acceptance:run_app",
"local:test:acceptance:app:server-pro": "OVERLEAF_APP=server-pro npm run local:test:acceptance:run_app",
"local:test:acceptance:app:server-ce": "OVERLEAF_APP=server-ce npm run local:test:acceptance:run_app",
"local:test:acceptance:app": "echo saas server-pro server-ce | xargs -n1 sh -c 'npm run local:test:acceptance:app:${0} || exit 255'",
"local:test:acceptance:modules:saas": "OVERLEAF_APP=saas npm run local:test:acceptance:run_modules",
"local:test:acceptance:modules:server-pro": "OVERLEAF_APP=server-pro npm run local:test:acceptance:run_modules",
"local:test:acceptance:modules:server-ce": "OVERLEAF_APP=server-ce npm run local:test:acceptance:run_modules",
"local:test:acceptance:modules": "echo saas server-ce server-pro | xargs -n1 sh -c 'npm run local:test:acceptance:modules:${0} || exit 255'",
"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",
"knip": "knip --config knip.ts"
},
"browserslist": [
"defaults and supports woff2",
"last 1 year",
"safari > 14"
],
"dependencies": {
"@ai-sdk/mcp": "^1.0.0",
"@ai-sdk/openai": "^3.0.0",
"@aws-sdk/client-ses": "^3.864.0",
"@contentful/rich-text-html-renderer": "^16.0.2",
"@contentful/rich-text-types": "^16.0.2",
"@customerio/cdp-analytics-node": "^0.3.9",
"@google-cloud/bigquery": "^6.0.1",
"@google-cloud/storage": "^6.10.1",
"@node-oauth/oauth2-server": "^5.1.0",
"@node-saml/passport-saml": "^5.1.0",
"@overleaf/access-token-encryptor": "*",
"@overleaf/fetch-utils": "*",
"@overleaf/logger": "*",
"@overleaf/metrics": "*",
"@overleaf/migrations": "*",
"@overleaf/mongo-utils": "*",
"@overleaf/o-error": "*",
"@overleaf/object-persistor": "*",
"@overleaf/promise-utils": "*",
"@overleaf/redis-wrapper": "*",
"@overleaf/settings": "*",
"@overleaf/stream-utils": "*",
"@overleaf/validation-tools": "*",
"@phosphor-icons/react": "^2.1.7",
"@phosphor-icons/webcomponents": "^2.1.5",
"@slack/webhook": "^7.0.2",
"@stripe/react-stripe-js": "^3.9.0",
"@stripe/stripe-js": "^7.7.0",
"@tanstack/react-table": "^8.21.3",
"@xmldom/xmldom": "^0.7.13",
"accepts": "^1.3.7",
"ai": "^6.0.2",
"ajv": "^8.12.0",
"archiver": "^5.3.0",
"async": "^3.2.5",
"base-x": "^4.0.1",
"basic-auth": "^2.0.1",
"bcrypt": "^6.0.0",
"body-parser": "1.20.4",
"bowser": "^2.11.0",
"bull": "^3.18.0",
"bunyan": "^1.8.15",
"cache-flow": "^1.9.0",
"connect-redis": "^6.1.3",
"content-disposition": "^0.5.0",
"contentful": "^10.8.5",
"cookie": "^0.2.3",
"cookie-parser": "1.4.6",
"crc-32": "^1.2.2",
"csurf": "^1.11.0",
"csv": "^6.2.5",
"dateformat": "1.0.4-1.2.3",
"ejs": "^3.1.10",
"email-addresses": "^5.0.0",
"eventsource-parser": "^1.1.2",
"express": "4.22.1",
"express-bearer-token": "^2.4.0",
"express-http-proxy": "^1.6.0",
"express-session": "^1.17.1",
"file-type": "^21.0.0",
"focus-trap-react": "^11.0.4",
"globby": "^5.0.0",
"helmet": "^6.0.1",
"https-proxy-agent": "^7.0.6",
"i18next": "^23.10.0",
"i18next-fs-backend": "^2.3.1",
"i18next-http-middleware": "^3.5.0",
"jose": "^4.3.8",
"json2csv": "^4.3.3",
"jsonwebtoken": "^9.0.3",
"lodash": "^4.17.19",
"lru-cache": "^7.10.1",
"marked": "^4.1.0",
"method-override": "^2.3.3",
"minimatch": "^7.4.2",
"minimist": "^1.2.7",
"mmmagic": "^0.5.3",
"moment": "^2.29.4",
"mongodb-legacy": "6.1.3",
"mongoose": "8.9.5",
"multer": "2.0.2",
"nocache": "^2.1.0",
"node-fetch": "^2.7.0",
"nodemailer": "^7.0.0",
"on-headers": "^1.0.2",
"otplib": "^12.0.1",
"overleaf-editor-core": "*",
"p-limit": "^2.3.0",
"p-props": "4.0.0",
"p-queue": "^8.1.0",
"parse-data-url": "^2.0.0",
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
"passport-ldapauth": "^2.1.4",
"passport-local": "^1.0.0",
"passport-oauth2": "^1.5.0",
"passport-orcid": "0.0.4",
"pug": "^3.0.3",
"pug-runtime": "^3.0.1",
"rate-limiter-flexible": "^2.4.1",
"recurly": "^4.0.0",
"referer-parser": "github:overleaf/nodejs-referer-parser#8b8b103762d05b7be4cfa2f810e1d408be67d7bb",
"request": "2.88.2",
"requestretry": "^7.1.0",
"sanitize-html": "^2.8.1",
"stripe": "^18.4.0",
"tough-cookie": "^4.0.0",
"tsscmp": "^1.0.6",
"uid-safe": "^2.1.5",
"utf-8-validate": "^5.0.2",
"valid-data-url": "^2.0.0",
"valid-url": "^1.0.9",
"xml-crypto": "^2.1.6",
"xml2js": "^0.6.2",
"xregexp": "^4.3.0",
"yauzl": "^2.10.0",
"zod": "^4.0.17",
"zod-validation-error": "^4.0.1"
},
"devDependencies": {
"5to6-codemod": "^1.8.0",
"@ai-sdk/react": "^3.0.2",
"@babel/core": "^7.28.5",
"@babel/plugin-proposal-decorators": "^7.28.0",
"@babel/preset-env": "^7.28.5",
"@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5",
"@babel/register": "^7.28.3",
"@codemirror/autocomplete": "github:overleaf/codemirror-autocomplete#6445cd056671c98d12d1c597ba705e11327ec4c5",
"@codemirror/commands": "6.10.1",
"@codemirror/lang-markdown": "6.5.0",
"@codemirror/language": "6.12.1",
"@codemirror/lint": "6.9.2",
"@codemirror/search": "github:overleaf/codemirror-search#04380a528c339cd4b78fb10b3ef017f657ec17bd",
"@codemirror/state": "6.5.4",
"@codemirror/view": "6.38.6",
"@floating-ui/react": "^0.27.5",
"@juggle/resize-observer": "^3.3.1",
"@lezer/common": "1.5.0",
"@lezer/generator": "1.8.0",
"@lezer/highlight": "1.2.3",
"@lezer/lr": "1.4.7",
"@lezer/markdown": "1.6.3",
"@overleaf/codemirror-tree-view": "^0.1.3",
"@overleaf/dictionaries": "https://github.com/overleaf/dictionaries/archive/refs/tags/v0.0.3.tar.gz",
"@overleaf/eslint-plugin": "*",
"@overleaf/ranges-tracker": "*",
"@overleaf/stream-utils": "*",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.16",
"@pollyjs/adapter-node-http": "^6.0.6",
"@pollyjs/core": "^6.0.6",
"@pollyjs/persister-fs": "^6.0.6",
"@replit/codemirror-emacs": "overleaf/codemirror-emacs#4394c03858f27053f8768258e9493866e06e938e",
"@replit/codemirror-indentation-markers": "overleaf/codemirror-indentation-markers#371ce3b56f453a392eb0d3b85ab019c185c68e1f",
"@replit/codemirror-vim": "overleaf/codemirror-vim#1bef138382d948018f3f9b8a4d7a70ab61774e4b",
"@sentry/browser": "7.46.0",
"@storybook/addon-a11y": "^8.6.15",
"@storybook/addon-designs": "^8.2.1",
"@storybook/addon-essentials": "^8.6.15",
"@storybook/addon-interactions": "^8.6.15",
"@storybook/addon-links": "^8.6.15",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-webpack5-compiler-babel": "^3.0.6",
"@storybook/cli": "^8.6.15",
"@storybook/react": "^8.6.15",
"@storybook/react-webpack5": "^8.6.15",
"@storybook/theming": "^8.6.15",
"@tailwindcss/container-queries": "^0.1.1",
"@testing-library/cypress": "^10.0.3",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.4.3",
"@types/algoliasearch": "^3.34.11",
"@types/bootstrap": "^5.2.10",
"@types/chai": "^4.3.0",
"@types/dateformat": "^5.0.3",
"@types/diff": "^5.2.3",
"@types/dom-speech-recognition": "^0.0.7",
"@types/events": "^3.0.3",
"@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",
"@types/react-google-recaptcha": "^2.1.9",
"@types/react-linkify": "^1.0.4",
"@types/react-syntax-highlighter": "^15.5.11",
"@types/recurly__recurly-js": "^4.38.0",
"@types/sinon-chai": "^3.2.12",
"@types/uuid": "^9.0.8",
"@uppy/core": "^3.8.0",
"@uppy/dashboard": "^3.7.1",
"@uppy/drag-drop": "^3.0.3",
"@uppy/file-input": "^3.0.4",
"@uppy/progress-bar": "^3.0.4",
"@uppy/react": "^3.2.1",
"@uppy/utils": "^5.7.0",
"@uppy/xhr-upload": "^3.6.0",
"@vitest/eslint-plugin": "1.1.44",
"@writefull/core": "^1.27.27",
"@writefull/ui": "^1.27.27",
"@writefull/utils": "^1.27.27",
"abort-controller": "^3.0.0",
"acorn": "^7.1.1",
"acorn-walk": "^7.1.1",
"algoliasearch": "^3.35.1",
"autoprefixer": "^10.4.16",
"axios": "^1.8.3",
"babel-loader": "^10.0.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"backbone": "^1.6.0",
"bootstrap": "^5.3.7",
"c8": "^7.2.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"chai-exclude": "^2.0.3",
"chart.js": "^4.0.1",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-datalabels": "^2.2.0",
"cheerio": "^1.0.0-rc.3",
"classnames": "^2.2.6",
"confusing-browser-globals": "^1.0.11",
"cookie-signature": "^1.2.1",
"copy-webpack-plugin": "^11.0.0",
"core-js": "~3.46.0",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"cypress": "13.13.2",
"cypress-multi-reporters": "^2.0.5",
"cypress-plugin-tab": "^1.0.5",
"d3": "^3.5.16",
"daterangepicker": "2.1.27",
"diff": "^5.1.0",
"dompurify": "^3.2.4",
"downshift": "^9.0.9",
"es6-promise": "^4.2.8",
"escodegen": "^2.0.0",
"eslint-config-standard-jsx": "^11.0.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-testing-library": "^7.1.1",
"eslint-plugin-unicorn": "^56.0.0",
"events": "^3.3.0",
"eventsource-client": "^1.1.4",
"fake-indexeddb": "^6.0.0",
"fast-fuzzy": "^1.12.0",
"fetch-mock": "^12.6.0",
"formik": "^2.2.9",
"fuse.js": "^7.0.0",
"glob": "^12.0.0",
"globals": "^16.2.0",
"handlebars": "^4.7.8",
"handlebars-loader": "^1.7.3",
"html-webpack-plugin": "^5.5.3",
"i18next-scanner": "^4.4.0",
"idb": "^8.0.0",
"inversify": "^6.2.2",
"jquery": "^3.7.1",
"jscodeshift": "^17.0.0",
"jsdom": "^27.4.0",
"jsdom-global": "^3.0.2",
"katex": "^0.16.25",
"knip": "^5.64.1",
"lottie-react": "^2.4.1",
"match-sorter": "^6.2.0",
"mathjax": "^3.2.2",
"mediatr-ts": "^2.0.1",
"mensch": "^0.3.4",
"micromark": "^4.0.0",
"mini-css-extract-plugin": "^2.7.6",
"mocha": "^11.1.0",
"mocha-each": "^2.0.1",
"mocha-junit-reporter": "^2.2.1",
"mocha-multi-reporters": "^1.5.1",
"mock-fs": "^5.1.2",
"nock": "^13.5.6",
"nvd3": "^1.8.6",
"p-reflect": "^3.1.0",
"path-browserify": "^1.0.1",
"pdfjs-dist": "5.1.91",
"pirates": "^4.0.1",
"postcss": "^8.4.31",
"postcss-loader": "^7.3.3",
"prop-types": "^15.7.2",
"qrcode": "^1.4.4",
"react": "^18.3.1",
"react-bootstrap": "^2.10.10",
"react-chartjs-2": "^5.0.1",
"react-color": "^2.19.3",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^5.0.0",
"react-google-recaptcha": "^3.1.0",
"react-i18next": "^13.3.1",
"react-linkify": "^1.0.0-alpha",
"react-refresh": "^0.14.0",
"react-resizable-panels": "^2.1.1",
"react-syntax-highlighter": "^15.6.6",
"reflect-metadata": "^0.2.2",
"rehype-harden": "^1.1.6",
"rehype-sanitize": "^6.0.0",
"resolve-url-loader": "^5.0.0",
"samlp": "^7.0.2",
"sandboxed-module": "overleaf/node-sandboxed-module#cafa2d60f17ce75cc023e6f296eb8de79d92d35d",
"sass": "^1.77.1",
"sass-loader": "^14.2.1",
"scroll-into-view-if-needed": "^2.2.25",
"sinon": "^7.5.0",
"sinon-chai": "^3.7.0",
"sinon-mongoose": "^2.3.0",
"storybook": "^8.6.15",
"streamdown": "^1.6.10",
"stylelint": "^16.26.1",
"stylelint-config-standard-scss": "^13.1.0",
"tailwindcss": "^3.4.4",
"terser-webpack-plugin": "^5.3.9",
"thread-loader": "^4.0.2",
"timekeeper": "^2.2.0",
"to-string-loader": "^1.2.0",
"ts-loader": "^9.5.2",
"tty-browserify": "^0.0.1",
"typescript": "^5.9.3",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"use-stick-to-bottom": "^1.1.1",
"uuid": "^9.0.1",
"vitest": "^4.0.0",
"w3c-keyname": "^2.2.8",
"webpack": "^5.98.0",
"webpack-assets-manifest": "^5.2.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.2.1",
"webpack-merge": "^5.10.0",
"yup": "^0.32.11",
"zustand": "^5.0.1"
}
}