mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-13 21:53:43 +01:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3503a7d887 | ||
|
|
77b37b7cf8 | ||
|
|
047268b79d | ||
|
|
527f60316a | ||
|
|
b6eafbcc0a | ||
|
|
ddd2789933 | ||
|
|
7e91256ae3 | ||
|
|
ea2c644695 | ||
|
|
281174eee9 | ||
|
|
de65f2b785 | ||
|
|
9d9fbf06c4 | ||
|
|
39ae15bda0 | ||
|
|
f9d7148b6c | ||
|
|
dbc3bbe9aa | ||
|
|
6495cb3c76 | ||
|
|
76e9829b73 | ||
|
|
8ef6ad5c8e | ||
|
|
bbcdcce617 | ||
|
|
66b2de0750 | ||
|
|
7615dbef54 | ||
|
|
0ff64f51b5 | ||
|
|
4a4957651f | ||
|
|
3414417298 | ||
|
|
cafe99d116 | ||
|
|
3a609993be | ||
|
|
bcf43a4582 | ||
|
|
1ee25c02b1 | ||
|
|
0e3af1c01b | ||
|
|
cd8e28b462 | ||
|
|
035f7c894c | ||
|
|
173bcce718 | ||
|
|
e56d52905b | ||
|
|
d19eb59548 | ||
|
|
7513da4518 | ||
|
|
8c1a06eb94 | ||
|
|
c345c0c940 | ||
|
|
c0651bc13d |
@@ -1,25 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.{pug,coffee}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{pug,patch}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Jenkinsfile]
|
||||
insert_final_newline = false
|
||||
max_line_length = off
|
||||
@@ -0,0 +1,44 @@
|
||||
<!--
|
||||
|
||||
Note: If you are using www.overleaf.com and have a problem,
|
||||
or if you would like to request a new feature please contact
|
||||
the support team at support@overleaf.com
|
||||
|
||||
This form should only be used to report bugs in the
|
||||
Community Edition release of Overleaf.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!-- BUG REPORT TEMPLATE -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!-- Describe the steps leading up to when / where you found the bug. -->
|
||||
<!-- Screenshots may be helpful here. -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behaviour
|
||||
<!-- What should have happened when you completed the steps above? -->
|
||||
|
||||
## Observed Behaviour
|
||||
<!-- What actually happened when you completed the steps above? -->
|
||||
<!-- Screenshots may be helpful here. -->
|
||||
|
||||
## Context
|
||||
<!-- How has this issue affected you? What were you trying to accomplish? -->
|
||||
|
||||
## Technical Info
|
||||
<!-- Provide any technical details that may be applicable (or N/A if not applicable). -->
|
||||
|
||||
* URL:
|
||||
* Browser Name and version:
|
||||
* Operating System and version (desktop or mobile):
|
||||
* Signed in as:
|
||||
* Project and/or file:
|
||||
|
||||
## Analysis
|
||||
<!--- Optionally, document investigation of / suggest a fix for the bug, e.g. 'comes from this line / commit' -->
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report a bug
|
||||
title: ''
|
||||
labels: type:bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Note: This form should only be used to report bugs in the
|
||||
Extended CE release of Overleaf.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!-- BUG REPORT TEMPLATE -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!-- Describe the steps leading up to when / where you found the bug. -->
|
||||
<!-- Screenshots may be helpful here. -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behaviour
|
||||
<!-- What should have happened when you completed the steps above? -->
|
||||
|
||||
## Observed Behaviour
|
||||
<!-- What actually happened when you completed the steps above? -->
|
||||
<!-- Screenshots may be helpful here. -->
|
||||
|
||||
## Context
|
||||
<!-- How has this issue affected you? What were you trying to accomplish? -->
|
||||
|
||||
## Technical Info
|
||||
<!-- Provide any technical details that may be applicable (or N/A if not applicable). -->
|
||||
|
||||
* URL:
|
||||
* Browser Name and version:
|
||||
* Operating System and version (desktop or mobile):
|
||||
* Signed in as:
|
||||
* Project and/or file:
|
||||
|
||||
## Analysis
|
||||
<!--- Optionally, document investigation of / suggest a fix for the bug, e.g. 'comes from this line / commit' -->
|
||||
@@ -4,3 +4,8 @@
|
||||
|
||||
## Related issues / Pull Requests
|
||||
<!-- Fixes #xyz, Contributes to #xyz, Related to #xyz-->
|
||||
|
||||
|
||||
## Contributor Agreement
|
||||
|
||||
- [ ] I confirm I have signed the [Contributor License Agreement](https://github.com/overleaf/overleaf/blob/master/CONTRIBUTING.md#contributor-license-agreement)
|
||||
|
||||
+17
-7
@@ -1,33 +1,43 @@
|
||||
Contributing to Overleaf CE+
|
||||
============================
|
||||
Contributing to Overleaf
|
||||
==========================
|
||||
|
||||
Thank you for reading this! If you'd like to report a bug or join in the development
|
||||
of Overleaf CE+, then here are some notes on how to do that.
|
||||
of Overleaf, then here are some notes on how to do that.
|
||||
|
||||
Reporting bugs and opening issues
|
||||
---------------------------------
|
||||
|
||||
If you'd like to report a bug or open an issue, please **[check if there is an existing issue](https://github.com/yu-i-i/overleaf-cep).**
|
||||
If you'd like to report a bug or open an issue, please **[check if there is an existing issue](https://github.com/overleaf/overleaf/issues).**
|
||||
If there is then please add any more information that you have, or give it a 👍.
|
||||
|
||||
When submitting an issue please describe the issue as clearly as possible, including how to
|
||||
reproduce the bug, which situations it appears in, what you expected to happen, and what actually happens.
|
||||
If you can include a screenshot for front end issues that is very helpful.
|
||||
|
||||
**Note**: If you are using [www.overleaf.com](www.overleaf.com) and have a problem, or if you would like to request a new feature, please contact the Support team at support@overleaf.com. Raise an issue here only to report bugs in the Community Edition release of Overleaf.
|
||||
|
||||
Pull Requests
|
||||
-------------
|
||||
|
||||
See [Overleaf wiki](https://github.com/overleaf/overleaf/wiki)
|
||||
See [our wiki](https://github.com/overleaf/overleaf/wiki)
|
||||
for how to manage the Overleaf development environment and for our developer guidelines.
|
||||
|
||||
We love pull requests, so be bold with them! Don't be afraid of going ahead
|
||||
and changing something, or adding a new feature. We're very happy to work with you
|
||||
to get your changes merged into Overleaf.
|
||||
|
||||
If you're looking for something to work on, have a look at the [open issues](https://github.com/yu-i-i/overleaf-cep/issues).
|
||||
If you're looking for something to work on, have a look at the [open issues](https://github.com/overleaf/overleaf/issues).
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
Please see [Overleaf security policy](https://github.com/overleaf/overleaf/security/policy) if you would like to report a potential security vulnerability.
|
||||
Please see [our security policy](https://github.com/overleaf/overleaf/security/policy) if you would like to report a potential security vulnerability.
|
||||
|
||||
Contributor License Agreement
|
||||
-----------------------------
|
||||
|
||||
Before we can accept any contributions of code, we need you to agree to our
|
||||
[Contributor License Agreement](https://docs.google.com/forms/d/e/1FAIpQLSef79XH3mb7yIiMzZw-yALEegS-wyFetvjTiNBfZvf_IHD2KA/viewform?usp=sf_link).
|
||||
This is to ensure that you own the copyright of your contribution, and that you
|
||||
agree to give us a license to use it in both the open source version, and the version
|
||||
of Overleaf running at www.overleaf.com, which may have additional changes.
|
||||
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
# CREDITS
|
||||
|
||||
This project includes ideas and adapted code from several open-source projects.\
|
||||
In most cases the original code has been modified, optimized, or extended.
|
||||
|
||||
## Symbol palette
|
||||
|
||||
The symbol palette feature is based on the original Overleaf implementation:
|
||||
|
||||
https://github.com/overleaf/web/tree/master/frontend/js/features/symbol-palette
|
||||
|
||||
The original code was slightly improved, particularly in the parts related to keyboard input.
|
||||
|
||||
## LDAP authentication
|
||||
|
||||
LDAP authentication was inspired by:
|
||||
|
||||
https://github.com/smhaller/ldap-overleaf-sl
|
||||
|
||||
The project provided the idea and part of the implementation for adding LDAP users to the user's contacts.
|
||||
|
||||
## Autocomplete of reference keys
|
||||
|
||||
Autocomplete of reference keys is based on:
|
||||
|
||||
https://github.com/lcpu-club/overleaf
|
||||
|
||||
The implementation largely follows the referenced code, except that a different `.bib` file parser is used.
|
||||
|
||||
## Real-time track changes and comments
|
||||
|
||||
The Track Changes and Comments feature exists largely in the original Overleaf codebase.\
|
||||
The missing parts were implemented based on:
|
||||
|
||||
https://github.com/ertuil/overleaf
|
||||
|
||||
The referenced code was fixed, optimized, and extended.
|
||||
|
||||
## Sandboxed compiles
|
||||
|
||||
The Sandboxed Compiles feature was until recently largely present in the original Overleaf codebase.\
|
||||
The missing parts were implemented in this project.
|
||||
|
||||
## Import file from external URL
|
||||
|
||||
The "From External URL" feature exists in the original Overleaf code.\
|
||||
The missing proxy component was implemented in this project.
|
||||
|
||||
## Git integration
|
||||
|
||||
Git integration is based on:
|
||||
|
||||
https://github.com/ayaka-notes/overleaf-pro/tree/feat-git-bridge
|
||||
|
||||
The backend was completely rewritten.\
|
||||
The frontend uses the referenced code, which was optimized and improved.
|
||||
|
||||
## Original Features
|
||||
|
||||
The following features are original work implemented in this project:
|
||||
|
||||
- Template Gallery
|
||||
- Advanced administrator tools for managing user accounts and projects
|
||||
- SAML authentication
|
||||
- OpenID Connect authentication
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Thanks to the users of the project for valuable feedback, suggestions,
|
||||
and help in identifying and fixing bugs.
|
||||
+1
-2
@@ -42,7 +42,7 @@ To do this, use the included `bin/dev` script:
|
||||
bin/dev
|
||||
```
|
||||
|
||||
This will start all services using `node --watch`, which will automatically monitor the code and restart the services as necessary.
|
||||
This will start all services using `nodemon`, which will automatically monitor the code and restart the services as necessary.
|
||||
|
||||
To improve performance, you can start only a subset of the services in development mode by providing a space-separated list to the `bin/dev` script:
|
||||
|
||||
@@ -80,7 +80,6 @@ each service:
|
||||
| `references` | 9238 |
|
||||
| `history-v1` | 9239 |
|
||||
| `project-history` | 9240 |
|
||||
| `linked-url-proxy` | 9241 |
|
||||
|
||||
To attach to a service using Chrome's _remote debugging_, go to
|
||||
<chrome://inspect/> and make sure _Discover network targets_ is checked. Next
|
||||
|
||||
@@ -6,20 +6,15 @@ DOCUMENT_UPDATER_HOST=document-updater
|
||||
FILESTORE_HOST=filestore
|
||||
GRACEFUL_SHUTDOWN_DELAY_SECONDS=0
|
||||
HISTORY_V1_HOST=history-v1
|
||||
HISTORY_REDIS_HOST=redis
|
||||
LINKED_URL_PROXY_HOST=linked-url-proxy
|
||||
LISTEN_ADDRESS=0.0.0.0
|
||||
MONGO_HOST=mongo
|
||||
MONGO_URL=mongodb://mongo/sharelatex?directConnection=true
|
||||
NOTIFICATIONS_HOST=notifications
|
||||
PROJECT_HISTORY_HOST=project-history
|
||||
QUEUES_REDIS_HOST=redis
|
||||
DSMP_REDIS_HOST=redis
|
||||
REALTIME_HOST=real-time
|
||||
REDIS_HOST=redis
|
||||
REFERENCES_HOST=references
|
||||
SESSION_SECRET=foo
|
||||
V1_HISTORY_HOST=history-v1
|
||||
WEBPACK_HOST=webpack
|
||||
WEB_API_PASSWORD=overleaf
|
||||
WEB_API_USER=overleaf
|
||||
|
||||
@@ -79,26 +79,15 @@ services:
|
||||
- ../services/history-v1/knexfile.js:/overleaf/services/history-v1/knexfile.js
|
||||
- ../services/history-v1/migrations:/overleaf/services/history-v1/migrations
|
||||
|
||||
linked-url-proxy:
|
||||
command: ["node", "--watch", "app.mjs"]
|
||||
environment:
|
||||
- NODE_OPTIONS=--inspect=0.0.0.0:9229
|
||||
ports:
|
||||
- "127.0.0.1:9241:9229"
|
||||
volumes:
|
||||
- ../services/linked-url-proxy/app:/overleaf/services/linked-url-proxy/app
|
||||
- ../services/linked-url-proxy/config:/overleaf/services/linked-url-proxy/config
|
||||
- ../services/linked-url-proxy/app.mjs:/overleaf/services/linked-url-proxy/app.mjs
|
||||
|
||||
notifications:
|
||||
command: ["node", "--watch", "app.ts"]
|
||||
command: ["node", "--watch", "app.js"]
|
||||
environment:
|
||||
- NODE_OPTIONS=--inspect=0.0.0.0:9229
|
||||
ports:
|
||||
- "127.0.0.1:9236:9229"
|
||||
volumes:
|
||||
- ../services/notifications/app:/overleaf/services/notifications/app
|
||||
- ../services/notifications/app.ts:/overleaf/services/notifications/app.ts
|
||||
- ../services/notifications/app.js:/overleaf/services/notifications/app.js
|
||||
- ../services/notifications/config:/overleaf/services/notifications/config
|
||||
|
||||
project-history:
|
||||
@@ -135,7 +124,7 @@ services:
|
||||
- ../services/references/app.js:/overleaf/services/references/app.js
|
||||
|
||||
web:
|
||||
command: ["node", "--watch", "app.mjs", "--watch-locales"]
|
||||
command: ["node", "--watch", "app.js", "--watch-locales"]
|
||||
environment:
|
||||
- NODE_OPTIONS=--inspect=0.0.0.0:9229
|
||||
ports:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
volumes:
|
||||
clsi-cache:
|
||||
clsi-output:
|
||||
filestore-public-files:
|
||||
filestore-template-files:
|
||||
filestore-uploads:
|
||||
@@ -25,16 +26,15 @@ services:
|
||||
env_file:
|
||||
- dev.env
|
||||
environment:
|
||||
- DOCKER_RUNNER=true
|
||||
- TEXLIVE_IMAGE=texlive-full # docker build texlive -t texlive-full
|
||||
- SANDBOXED_COMPILES=true
|
||||
- SANDBOXED_COMPILES_HOST_DIR_COMPILES=${PWD}/compiles
|
||||
- SANDBOXED_COMPILES_HOST_DIR_OUTPUT=${PWD}/output
|
||||
- COMPILES_HOST_DIR=${PWD}/compiles
|
||||
user: root
|
||||
volumes:
|
||||
- ${PWD}/compiles:/overleaf/services/clsi/compiles
|
||||
- ${PWD}/output:/overleaf/services/clsi/output
|
||||
- ${DOCKER_SOCKET_PATH:-/var/run/docker.sock}:/var/run/docker.sock
|
||||
- clsi-cache:/overleaf/services/clsi/cache
|
||||
- clsi-output:/overleaf/services/clsi/output
|
||||
|
||||
contacts:
|
||||
build:
|
||||
@@ -69,6 +69,7 @@ services:
|
||||
- filestore-public-files:/overleaf/services/filestore/public_files
|
||||
- filestore-template-files:/overleaf/services/filestore/template_files
|
||||
- filestore-uploads:/overleaf/services/filestore/uploads
|
||||
- filestore-user-files:/overleaf/services/filestore/user_files
|
||||
|
||||
history-v1:
|
||||
build:
|
||||
@@ -86,13 +87,6 @@ services:
|
||||
volumes:
|
||||
- history-v1-buckets:/buckets
|
||||
|
||||
linked-url-proxy:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: services/linked-url-proxy/Dockerfile
|
||||
env_file:
|
||||
- dev.env
|
||||
|
||||
mongo:
|
||||
image: mongo:6.0
|
||||
command: --replSet overleaf
|
||||
@@ -129,7 +123,7 @@ services:
|
||||
dockerfile: services/real-time/Dockerfile
|
||||
env_file:
|
||||
- dev.env
|
||||
|
||||
|
||||
redis:
|
||||
image: redis:5
|
||||
ports:
|
||||
@@ -153,7 +147,7 @@ services:
|
||||
- dev.env
|
||||
environment:
|
||||
- APP_NAME=Overleaf Community Edition
|
||||
- ENABLED_LINKED_FILE_TYPES=project_file,project_output_file
|
||||
- ENABLED_LINKED_FILE_TYPES=project_file,project_output_file,url
|
||||
- EMAIL_CONFIRMATION_DISABLED=true
|
||||
- NODE_ENV=development
|
||||
- OVERLEAF_ALLOW_PUBLIC_ACCESS=true
|
||||
@@ -171,7 +165,6 @@ services:
|
||||
- document-updater
|
||||
- filestore
|
||||
- history-v1
|
||||
- linked-url-proxy
|
||||
- notifications
|
||||
- project-history
|
||||
- real-time
|
||||
@@ -187,18 +180,3 @@ services:
|
||||
- "127.0.0.1:80:3808"
|
||||
volumes:
|
||||
- ./webpack.config.dev-env.js:/overleaf/services/web/webpack.config.dev-env.js
|
||||
|
||||
git-bridge:
|
||||
image: quay.io/sharelatex/git-bridge:latest
|
||||
expose:
|
||||
- "8000"
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
GIT_BRIDGE_API_BASE_URL: "http://web:3000/api/v0"
|
||||
GIT_BRIDGE_OAUTH2_SERVER: "http://web:3000"
|
||||
GIT_BRIDGE_POSTBACK_BASE_URL: "http://git-bridge:8000"
|
||||
GIT_BRIDGE_ROOT_DIR: "/data/git-bridge"
|
||||
LOG_LEVEL: "DEBUG"
|
||||
volumes:
|
||||
- ./data/git-bridge:/data/git-bridge
|
||||
|
||||
@@ -14,11 +14,6 @@ module.exports = merge(base, {
|
||||
target: 'http://real-time:3026',
|
||||
ws: true,
|
||||
},
|
||||
{
|
||||
context: '/git/**',
|
||||
target: 'http://git-bridge:8000',
|
||||
pathRewrite: { '^/git': '' }
|
||||
},
|
||||
{
|
||||
context: ['!**/*.js', '!**/*.css', '!**/*.json'],
|
||||
target: 'http://web:3000',
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 71 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 525 KiB After Width: | Height: | Size: 1.0 MiB |
+5
-12
@@ -1,3 +1,4 @@
|
||||
version: '2.2'
|
||||
services:
|
||||
sharelatex:
|
||||
restart: always
|
||||
@@ -31,7 +32,7 @@ services:
|
||||
OVERLEAF_REDIS_HOST: redis
|
||||
REDIS_HOST: redis
|
||||
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file,url'
|
||||
|
||||
# Enables Thumbnail generation using ImageMagick
|
||||
ENABLE_CONVERSIONS: 'true'
|
||||
@@ -72,19 +73,11 @@ services:
|
||||
## Server Pro ##
|
||||
################
|
||||
|
||||
## The Community Edition is intended for use in environments where all users are trusted and is not appropriate for
|
||||
## scenarios where isolation of users is required. Sandboxed Compiles are not available in the Community Edition,
|
||||
## so the following environment variables must be commented out to avoid compile issues.
|
||||
##
|
||||
## Sandboxed Compiles: https://docs.overleaf.com/on-premises/configuration/overleaf-toolkit/server-pro-only-configuration/sandboxed-compiles
|
||||
## Sandboxed Compiles: https://github.com/overleaf/overleaf/wiki/Server-Pro:-Sandboxed-Compiles
|
||||
SANDBOXED_COMPILES: 'true'
|
||||
### Bind-mount source for /var/lib/overleaf/data/compiles inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR_COMPILES: '/home/user/sharelatex_data/data/compiles'
|
||||
### Bind-mount source for /var/lib/overleaf/data/output inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR_OUTPUT: '/home/user/sharelatex_data/data/output'
|
||||
### Backwards compatibility (before Server Pro 5.5)
|
||||
DOCKER_RUNNER: 'true'
|
||||
SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
|
||||
### Bind-mount source for /var/lib/overleaf/data/compiles inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR: '/home/user/sharelatex_data/data/compiles'
|
||||
|
||||
## Works with test LDAP server shown at bottom of docker compose
|
||||
# OVERLEAF_LDAP_URL: 'ldap://ldap:389'
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,46 @@
|
||||
compileFolder
|
||||
|
||||
Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store?
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
|
||||
/node_modules/*
|
||||
data/*/*
|
||||
|
||||
**.swp
|
||||
|
||||
/log.json
|
||||
hash_folder
|
||||
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ui": "bdd",
|
||||
"recursive": "true",
|
||||
"reporter": "spec",
|
||||
"require": "test/setup.js"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
access-token-encryptor
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
"test:ci": "npm run test:unit",
|
||||
@@ -22,8 +22,6 @@
|
||||
"chai": "^4.3.6",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"sandboxed-module": "^2.0.4",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: `reports/junit-mocha-${process.env.MOCHA_GREP}.xml`,
|
||||
includePending: true,
|
||||
jenkinsMode: true,
|
||||
output: true,
|
||||
},
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-unnecessary-trans': require('./no-unnecessary-trans'),
|
||||
'prefer-kebab-url': require('./prefer-kebab-url'),
|
||||
'should-unescape-trans': require('./should-unescape-trans'),
|
||||
'no-generated-editor-themes': require('./no-generated-editor-themes'),
|
||||
'require-script-runner': require('./require-script-runner'),
|
||||
'require-vi-doMock-valid-path': require('./require-vi-doMock-valid-path'),
|
||||
'require-loading-label': require('./require-loading-label'),
|
||||
},
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'error',
|
||||
docs: {
|
||||
description:
|
||||
'Prohibit CodeMirror themes that are generated in a function',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
':matches(ArrowFunctionExpression, FunctionDeclaration, FunctionExpression) CallExpression > MemberExpression[object.name="EditorView"]:matches([property.name="theme"],[property.name="baseTheme"])'(
|
||||
node
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
message: `EditorView.theme and EditorView.baseTheme each add CSS to the page for every instance of the theme. Store the theme in a variable and reuse it instead.`,
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
fixable: 'code',
|
||||
docs: {
|
||||
description: 'Prohibit Trans with no components or values',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
'JSXOpeningElement[name.name="Trans"]'(node) {
|
||||
const attributes = new Map(
|
||||
node.attributes.map(attr => [attr.name.name, attr])
|
||||
)
|
||||
|
||||
if (!attributes.has('components')) {
|
||||
if (node.parent.children.length > 0) {
|
||||
context.report({
|
||||
node,
|
||||
message: `Trans components must not have child elements`,
|
||||
})
|
||||
} else if (attributes.has('values')) {
|
||||
context.report({
|
||||
node,
|
||||
message: `Use t('…') when there are no components`,
|
||||
})
|
||||
} else {
|
||||
context.report({
|
||||
node,
|
||||
message: `Use t('…') when there are no components`,
|
||||
fix(fixer) {
|
||||
const i18nKey = attributes.get('i18nKey').value.value
|
||||
|
||||
// Note: Prettier can fix indentation
|
||||
return fixer.replaceText(node.parent, `{t('${i18nKey}')}`)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "@overleaf/eslint-plugin",
|
||||
"version": "0.1.0",
|
||||
"author": "Overleaf (https://www.overleaf.com)",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"eslint": "^8.51.0",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "^8.50.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node rules.test.js"
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// URL parts should be kebab-case, but we didn't have this rule in the past.
|
||||
// The ESLint rule `prefer-kebab-url` will ignore these "legacy" URL parts.
|
||||
|
||||
const ignoreWords = {
|
||||
snake: new Set([
|
||||
'clear_saml_data',
|
||||
'confirm_link',
|
||||
'confirm_university_domain',
|
||||
'create_recurly_account',
|
||||
'current_history_content',
|
||||
'current_user',
|
||||
'default_email',
|
||||
'disable_managed_users',
|
||||
'doc_snapshot',
|
||||
'enable_history_ranges_support',
|
||||
'features_override',
|
||||
'generate_password_reset_url',
|
||||
'get_assignment',
|
||||
'get_clone',
|
||||
'health_check',
|
||||
'institutional_emails',
|
||||
'latest_template',
|
||||
'link_after_saml_response',
|
||||
'linked_file',
|
||||
'metrics_segmentation',
|
||||
'new_users',
|
||||
'no_autostart_post_gateway',
|
||||
'personal_info',
|
||||
'planned_maintenance',
|
||||
'refresh_features',
|
||||
'register_admin',
|
||||
'register_ldap_admin',
|
||||
'register_saml_admin',
|
||||
'restore_file',
|
||||
'revert_file',
|
||||
'saved_vers',
|
||||
'send_test_email',
|
||||
'session_maintenance',
|
||||
'set_in_session',
|
||||
'sign_in_to_link',
|
||||
'split_test',
|
||||
'sso_configuration_test',
|
||||
'sso_email',
|
||||
'sso_enrollment',
|
||||
'track_changes',
|
||||
'update_admin',
|
||||
'user_details',
|
||||
]),
|
||||
camel: new Set([
|
||||
'addWorkflowScope',
|
||||
'aiErrorAssistant',
|
||||
'beginAuth',
|
||||
'brandVariationId',
|
||||
'closeEditor',
|
||||
'completeRegistration',
|
||||
'deactivateOldProjects',
|
||||
'deletedSubscription',
|
||||
'disconnectAllUsers',
|
||||
'editingSession',
|
||||
'emailSubscription',
|
||||
'enableManagedUsers',
|
||||
'externalCollaboration',
|
||||
'flushProjectToTpds',
|
||||
'indexAll',
|
||||
'offboardManagedUser',
|
||||
'openEditor',
|
||||
'perfTest',
|
||||
'pollDropboxForUser',
|
||||
'resendInvite',
|
||||
'resendManagedUserInvite',
|
||||
'salesContactForm',
|
||||
'showSupport',
|
||||
]),
|
||||
other: new Set([
|
||||
'Project',
|
||||
'disableSSO',
|
||||
'enableSSO',
|
||||
'resendSSOLinkInvite',
|
||||
'usersCSV',
|
||||
]),
|
||||
}
|
||||
|
||||
module.exports = { ignoreWords }
|
||||
@@ -1,91 +0,0 @@
|
||||
const _ = require('lodash')
|
||||
const { ignoreWords } = require('./prefer-kebab-url-ignore')
|
||||
|
||||
const removeTextBetweenBrackets = text => {
|
||||
while (text.includes('[') || text.includes('(')) {
|
||||
text = text.replaceAll(/\[[^[\]]*]/g, '')
|
||||
text = text.replaceAll(/\([^()]*\)/g, '')
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
const shouldIgnoreWord = str =>
|
||||
str.includes(':') ||
|
||||
str.includes('(') ||
|
||||
str === '*' ||
|
||||
str.match(/^[a-z0-9.]+$/) ||
|
||||
ignoreWords.snake.has(str) ||
|
||||
ignoreWords.camel.has(str) ||
|
||||
ignoreWords.other.has(str)
|
||||
|
||||
const getSuggestion = routePath => {
|
||||
if (typeof routePath === 'string') {
|
||||
const kebabed = routePath
|
||||
.split('/')
|
||||
.map(word => (shouldIgnoreWord(word) ? word : _.kebabCase(word)))
|
||||
.join('/')
|
||||
return kebabed === routePath ? null : `'${kebabed}'`
|
||||
}
|
||||
|
||||
if (routePath instanceof RegExp) {
|
||||
const words = removeTextBetweenBrackets(routePath.source).match(/[\w-]+/g)
|
||||
if (!words) return routePath
|
||||
|
||||
let newSource = routePath.source
|
||||
for (const word of words) {
|
||||
if (!shouldIgnoreWord(word)) {
|
||||
newSource = newSource.replaceAll(
|
||||
new RegExp(`\\b${word}\\b`, 'g'),
|
||||
_.kebabCase(word)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const kebabed = new RegExp(newSource, routePath.flags)
|
||||
return kebabed.source.toString() === routePath.source.toString()
|
||||
? null
|
||||
: kebabed
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
fixable: 'code',
|
||||
hasSuggestions: true,
|
||||
docs: {
|
||||
description: 'Enforce using kebab-case for URL paths',
|
||||
},
|
||||
},
|
||||
create: context => ({
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
node.arguments[0]?.type === 'Literal' &&
|
||||
[/app/i, /router/i].some(callee =>
|
||||
typeof callee === 'string'
|
||||
? node.callee.object.name === callee
|
||||
: callee.test(node.callee.object.name)
|
||||
) &&
|
||||
['get', 'post', 'put', 'delete'].includes(node.callee.property.name)
|
||||
) {
|
||||
const routePath = node.arguments[0].value
|
||||
|
||||
const suggestion = getSuggestion(routePath)
|
||||
|
||||
if (suggestion) {
|
||||
context.report({
|
||||
node: node.arguments[0],
|
||||
message: 'Route path should be in kebab-case.',
|
||||
suggest: [
|
||||
{
|
||||
desc: `Change to kebab-case: ${suggestion}`,
|
||||
fix: fixer => fixer.replaceText(node.arguments[0], suggestion),
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
fixable: null,
|
||||
docs: {
|
||||
description:
|
||||
'Require loadingLabel prop when isLoading is specified on OLButton',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
'JSXOpeningElement[name.name="OLButton"]'(node) {
|
||||
const attributes = new Map(
|
||||
node.attributes.map(attr => [attr.name?.name, attr])
|
||||
)
|
||||
|
||||
const isLoadingAttr = attributes.get('isLoading')
|
||||
const loadingLabelAttr = attributes.get('loadingLabel')
|
||||
|
||||
if (isLoadingAttr && !loadingLabelAttr) {
|
||||
const isLoadingValue = isLoadingAttr.value
|
||||
|
||||
if (
|
||||
!isLoadingValue ||
|
||||
(isLoadingValue.type === 'JSXExpressionContainer' &&
|
||||
isLoadingValue.expression.type === 'Literal' &&
|
||||
isLoadingValue.expression.value === true)
|
||||
) {
|
||||
context.report({
|
||||
node: isLoadingAttr,
|
||||
message:
|
||||
'Button with isLoading prop must also specify loadingLabel',
|
||||
})
|
||||
} else if (
|
||||
isLoadingValue.type === 'JSXExpressionContainer' &&
|
||||
isLoadingValue.expression.type !== 'Literal'
|
||||
) {
|
||||
context.report({
|
||||
node: isLoadingAttr,
|
||||
message:
|
||||
'Button with isLoading prop must also specify loadingLabel',
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Require Script Runner for scripts',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
let hasImport = false
|
||||
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
if (node.source.value.endsWith('lib/ScriptRunner.mjs')) {
|
||||
hasImport = true
|
||||
}
|
||||
},
|
||||
'Program:exit'() {
|
||||
if (!hasImport) {
|
||||
context.report({
|
||||
loc: { line: 1, column: 0 },
|
||||
message:
|
||||
'Please use Script Runner for scripts. Refer to the developer manual (https://manual.dev-overleaf.com/development/code/web_scripts/#monitor-script-execution-and-usage-with-script-runner) for more information.',
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
const path = require('node:path')
|
||||
const fs = require('node:fs')
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Ensure vi.doMock first argument is a resolvable path.',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: '',
|
||||
},
|
||||
fixable: 'code',
|
||||
hasSuggestions: true,
|
||||
schema: [],
|
||||
messages: {
|
||||
unresolvablePath:
|
||||
'The path "{{pathValue}}" in vi.doMock() cannot be resolved relative to the current file.',
|
||||
notAStringLiteral:
|
||||
'The first argument of vi.doMock() must be (or resolve to) a string literal representing a path.',
|
||||
noArguments: 'vi.doMock() called with no arguments.',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const currentFilePath = context.getFilename()
|
||||
// ESLint can sometimes pass <text> or <input> for snippets not in a file
|
||||
if (currentFilePath === '<text>' || currentFilePath === '<input>') {
|
||||
return {}
|
||||
}
|
||||
const currentDirectory = path.dirname(currentFilePath)
|
||||
|
||||
function canResolve(modulePath) {
|
||||
try {
|
||||
require.resolve(path.resolve(currentDirectory, modulePath))
|
||||
return true
|
||||
} catch (e) {
|
||||
const absolutePath = path.resolve(currentDirectory, modulePath)
|
||||
const extensions = [
|
||||
'',
|
||||
'.js',
|
||||
'.mjs',
|
||||
'.ts',
|
||||
'.jsx',
|
||||
'.tsx',
|
||||
'.json',
|
||||
'.node',
|
||||
'/index.js',
|
||||
'/index.ts',
|
||||
] // Add common extensions
|
||||
for (const ext of extensions) {
|
||||
if (fs.existsSync(absolutePath + ext)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
node.callee.object.type === 'Identifier' &&
|
||||
node.callee.object.name === 'vi' &&
|
||||
node.callee.property.type === 'Identifier' &&
|
||||
node.callee.property.name === 'doMock'
|
||||
) {
|
||||
if (node.arguments.length === 0) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'noArguments',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const firstArg = node.arguments[0]
|
||||
let pathValue = firstArg.value
|
||||
|
||||
if (
|
||||
firstArg.type !== 'Literal' ||
|
||||
typeof firstArg.value !== 'string'
|
||||
) {
|
||||
if (firstArg.type === 'Identifier') {
|
||||
const variable = context
|
||||
.getScope()
|
||||
.variables.find(v => v.name === firstArg.name)
|
||||
if (
|
||||
variable &&
|
||||
variable.defs.length > 0 &&
|
||||
variable.defs[0].node.init &&
|
||||
variable.defs[0].node.init.type === 'Literal' &&
|
||||
typeof variable.defs[0].node.init.value === 'string'
|
||||
) {
|
||||
pathValue = variable.defs[0].node.init.value
|
||||
if (canResolve(pathValue)) {
|
||||
return
|
||||
}
|
||||
// If the first argument was a variable that didn't resolve then we can't auto-fix it
|
||||
}
|
||||
}
|
||||
context.report({
|
||||
node: firstArg,
|
||||
messageId: 'notAStringLiteral',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!pathValue.startsWith('.')) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!canResolve(pathValue)) {
|
||||
const mjsPath = pathValue.replace('.js', '.mjs')
|
||||
const additionalReportOptions = {}
|
||||
if (canResolve(mjsPath)) {
|
||||
additionalReportOptions.fix = fixer =>
|
||||
fixer.replaceText(firstArg, `'${mjsPath}'`)
|
||||
additionalReportOptions.suggest = [
|
||||
{
|
||||
desc: `Replace with "${pathValue.replace('.js', '.mjs')}"`,
|
||||
fix: fixer => fixer.replaceText(firstArg, `'${mjsPath}'`),
|
||||
},
|
||||
]
|
||||
}
|
||||
context.report({
|
||||
node: firstArg,
|
||||
messageId: 'unresolvablePath',
|
||||
data: {
|
||||
pathValue,
|
||||
},
|
||||
...additionalReportOptions,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
const { RuleTester } = require('eslint')
|
||||
const preferKebabUrl = require('./prefer-kebab-url')
|
||||
const noUnnecessaryTrans = require('./no-unnecessary-trans')
|
||||
const shouldUnescapeTrans = require('./should-unescape-trans')
|
||||
const noGeneratedEditorThemes = require('./no-generated-editor-themes')
|
||||
const viDoMockValidPath = require('./require-vi-doMock-valid-path')
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
})
|
||||
|
||||
ruleTester.run('prefer-kebab-url', preferKebabUrl, {
|
||||
valid: [
|
||||
{ code: `app.get('/foo-bar')` },
|
||||
{ code: `app.get('/foo-bar/:id')` },
|
||||
{ code: `router.post('/foo-bar')` },
|
||||
{ code: `router.get('/foo-bar/:id/:name/:age')` },
|
||||
{ code: `webRouter.get('/foo-bar/:user_id/(ProjectName)/get-info')` },
|
||||
{ code: `webApp.post('/foo-bar/:user_id/(ProjectName)/get-info')` },
|
||||
{
|
||||
code: `router.get(/^\\/download\\/project\\/([^/]*)\\/output\\/output\\.pdf$/)`,
|
||||
},
|
||||
{
|
||||
code: `webRouter.get(/^\\/project\\/([^/]*)\\/user\\/([0-9a-f]+)\\/build\\/([0-9a-f-]+)\\/output\\/(.*)$/)`,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `app.get('/fooBar')`,
|
||||
errors: [{ message: 'Route path should be in kebab-case.' }],
|
||||
},
|
||||
{
|
||||
code: `app.get('/fooBar/:id')`,
|
||||
errors: [{ message: 'Route path should be in kebab-case.' }],
|
||||
},
|
||||
{
|
||||
code: `webRouter.get('/foo_bar/:id/FooBar/:name/fooBar')`,
|
||||
errors: [{ message: 'Route path should be in kebab-case.' }],
|
||||
},
|
||||
{
|
||||
code: `router.get(/^\\/downLoad\\/pro-ject\\/([^/]*)\\/OutPut\\/out-put\\.pdf$/)`,
|
||||
errors: [{ message: 'Route path should be in kebab-case.' }],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
ruleTester.run('no-unnecessary-trans', noUnnecessaryTrans, {
|
||||
valid: [
|
||||
{ code: `<Trans i18nKey="test" components={{ strong: <strong/> }}/>` },
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `<Trans i18nKey="test" values={{ test: 'foo '}}/>`,
|
||||
errors: [{ message: `Use t('…') when there are no components` }],
|
||||
},
|
||||
{
|
||||
code: `<Trans i18nKey="test" />`,
|
||||
errors: [{ message: `Use t('…') when there are no components` }],
|
||||
output: `{t('test')}`,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
ruleTester.run('should-unescape-trans', shouldUnescapeTrans, {
|
||||
valid: [
|
||||
{
|
||||
code: `<Trans i18nKey="test" components={{ strong: <strong/> }}/>`,
|
||||
},
|
||||
{
|
||||
code: `<Trans i18nKey="test" values={{ foo: 'bar' }} components={{ strong: <strong/> }} shouldUnescape tOptions={{ interpolation: { escapeValue: true } }}/>`,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `<Trans i18nKey="test" values={{ foo: 'bar' }} components={{ strong: <strong/> }} />`,
|
||||
errors: [{ message: 'Trans with values must have shouldUnescape' }],
|
||||
output: `<Trans i18nKey="test" values={{ foo: 'bar' }}\nshouldUnescape components={{ strong: <strong/> }} />`,
|
||||
},
|
||||
{
|
||||
code: `<Trans i18nKey="test" values={{ foo: 'bar' }} components={{ strong: <strong/> }} shouldUnescape />`,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'Trans with shouldUnescape must have tOptions.interpolation.escapeValue',
|
||||
},
|
||||
],
|
||||
output: `<Trans i18nKey="test" values={{ foo: 'bar' }} components={{ strong: <strong/> }} shouldUnescape\ntOptions={{ interpolation: { escapeValue: true } }} />`,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const noGeneratedEditorThemesError =
|
||||
'EditorView.theme and EditorView.baseTheme each add CSS to the page for every instance of the theme. Store the theme in a variable and reuse it instead.'
|
||||
ruleTester.run('no-generated-editor-themes', noGeneratedEditorThemes, {
|
||||
valid: [
|
||||
{
|
||||
code: `EditorView.theme({ '.cm-editor': { color: 'black' } })`,
|
||||
},
|
||||
{
|
||||
code: `const theme = EditorView.theme({ '.cm-editor': { color: 'black' } })`,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: `function createTheme() { return EditorView.theme({ '.cm-editor': { color: 'black' } }) }`,
|
||||
errors: [
|
||||
{
|
||||
message: noGeneratedEditorThemesError,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `() => EditorView.theme({ '.cm-editor': { color: 'black' } })`,
|
||||
errors: [
|
||||
{
|
||||
message: noGeneratedEditorThemesError,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `class Foo { createTheme() { return EditorView.theme({ '.cm-editor': { color: 'black' } }) } }`,
|
||||
errors: [
|
||||
{
|
||||
message: noGeneratedEditorThemesError,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
ruleTester.run('domock-require-valid-path', viDoMockValidPath, {
|
||||
valid: [
|
||||
{
|
||||
code: 'vi.doMock("./require-vi-doMock-valid-path.js")',
|
||||
filename: __filename,
|
||||
},
|
||||
{
|
||||
code: 'const filename = "./require-vi-doMock-valid-path.js"; vi.doMock(filename);',
|
||||
filename: __filename,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: "vi.doMock('./require-vi-doMock-valid-path2')",
|
||||
filename: __filename,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'The path "./require-vi-doMock-valid-path2" in vi.doMock() cannot be resolved relative to the current file.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'const filename = "./require-vi-doMock-valid-path2.js"; vi.doMock(filename);',
|
||||
filename: __filename,
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'The first argument of vi.doMock() must be (or resolve to) a string literal representing a path.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -1,60 +0,0 @@
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
fixable: 'code',
|
||||
docs: {
|
||||
description: 'Ensure that Trans with values has shouldUnescape',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
'JSXOpeningElement[name.name="Trans"]'(node) {
|
||||
const attributes = new Map(
|
||||
node.attributes.map(attr => [attr.name.name, attr])
|
||||
)
|
||||
|
||||
if (attributes.has('values') && !attributes.has('shouldUnescape')) {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Trans with values must have shouldUnescape',
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(
|
||||
attributes.get('values'),
|
||||
'\nshouldUnescape' // Note: Prettier can fix indentation
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (attributes.has('values') && attributes.has('shouldUnescape')) {
|
||||
const tOptions = attributes.get('tOptions')
|
||||
if (!tOptions) {
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Trans with shouldUnescape must have tOptions.interpolation.escapeValue',
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(
|
||||
attributes.get('shouldUnescape'),
|
||||
'\ntOptions={{ interpolation: { escapeValue: true } }}' // Note: Prettier can fix indentation
|
||||
)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
const property = tOptions.value.expression.properties
|
||||
.find(p => p.key.name === 'interpolation')
|
||||
?.value.properties.find(p => p.key.name === 'escapeValue')
|
||||
|
||||
if (property?.value.value !== true) {
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Trans with shouldUnescape must have tOptions.interpolation.escapeValue set to true',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ui": "bdd",
|
||||
"recursive": "true",
|
||||
"reporter": "spec",
|
||||
"require": "test/setup.js"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
fetch-utils
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -23,11 +23,11 @@ async function fetchJson(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchJsonWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
fetchOpts.headers = fetchOpts.headers ?? {}
|
||||
fetchOpts.headers.Accept = fetchOpts.headers.Accept ?? 'application/json'
|
||||
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -53,8 +53,8 @@ async function fetchStream(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchStreamWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, abortController, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts, abortController } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
@@ -76,8 +76,8 @@ async function fetchStreamWithResponse(url, opts = {}) {
|
||||
* @throws {RequestFailedError} if the response has a failure status code
|
||||
*/
|
||||
async function fetchNothing(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -108,9 +108,9 @@ async function fetchRedirect(url, opts = {}) {
|
||||
* @throws {RequestFailedError} if the response has a non redirect status code or missing Location header
|
||||
*/
|
||||
async function fetchRedirectWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
fetchOpts.redirect = 'manual'
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (response.status < 300 || response.status >= 400) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -142,8 +142,8 @@ async function fetchString(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchStringWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -178,14 +178,13 @@ function parseOpts(opts) {
|
||||
|
||||
const abortController = new AbortController()
|
||||
fetchOpts.signal = abortController.signal
|
||||
let detachSignal = () => {}
|
||||
if (opts.signal) {
|
||||
detachSignal = abortOnSignal(abortController, opts.signal)
|
||||
abortOnSignal(abortController, opts.signal)
|
||||
}
|
||||
if (opts.body instanceof Readable) {
|
||||
abortOnDestroyedRequest(abortController, fetchOpts.body)
|
||||
}
|
||||
return { fetchOpts, abortController, detachSignal }
|
||||
return { fetchOpts, abortController }
|
||||
}
|
||||
|
||||
function setupJsonBody(fetchOpts, json) {
|
||||
@@ -209,9 +208,6 @@ function abortOnSignal(abortController, signal) {
|
||||
abortController.abort(signal.reason)
|
||||
}
|
||||
signal.addEventListener('abort', listener)
|
||||
return () => {
|
||||
signal.removeEventListener('abort', listener)
|
||||
}
|
||||
}
|
||||
|
||||
function abortOnDestroyedRequest(abortController, stream) {
|
||||
@@ -230,12 +226,11 @@ function abortOnDestroyedResponse(abortController, response) {
|
||||
})
|
||||
}
|
||||
|
||||
async function performRequest(url, fetchOpts, detachSignal) {
|
||||
async function performRequest(url, fetchOpts) {
|
||||
let response
|
||||
try {
|
||||
response = await fetch(url, fetchOpts)
|
||||
} catch (err) {
|
||||
detachSignal()
|
||||
if (fetchOpts.body instanceof Readable) {
|
||||
fetchOpts.body.destroy()
|
||||
}
|
||||
@@ -244,7 +239,6 @@ async function performRequest(url, fetchOpts, detachSignal) {
|
||||
method: fetchOpts.method ?? 'GET',
|
||||
})
|
||||
}
|
||||
response.body.on('close', detachSignal)
|
||||
if (fetchOpts.body instanceof Readable) {
|
||||
response.body.on('close', () => {
|
||||
if (!fetchOpts.body.readableEnded) {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
"test:ci": "npm run test:unit",
|
||||
@@ -16,14 +16,12 @@
|
||||
"author": "Overleaf (https://www.overleaf.com)",
|
||||
"license": "AGPL-3.0-only",
|
||||
"devDependencies": {
|
||||
"@types/node-fetch": "^2.6.13",
|
||||
"body-parser": "1.20.4",
|
||||
"@types/node-fetch": "^2.6.11",
|
||||
"body-parser": "^1.20.3",
|
||||
"chai": "^4.3.6",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"express": "4.22.1",
|
||||
"express": "^4.21.2",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: `reports/junit-mocha-${process.env.MOCHA_GREP}.xml`,
|
||||
includePending: true,
|
||||
jenkinsMode: true,
|
||||
output: true,
|
||||
},
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
const { expect } = require('chai')
|
||||
const fs = require('node:fs')
|
||||
const events = require('node:events')
|
||||
const { FetchError, AbortError } = require('node-fetch')
|
||||
const { Readable } = require('node:stream')
|
||||
const { pipeline } = require('node:stream/promises')
|
||||
const { once } = require('node:events')
|
||||
const { TestServer } = require('./helpers/TestServer')
|
||||
const selfsigned = require('selfsigned')
|
||||
@@ -206,31 +203,6 @@ describe('fetch-utils', function () {
|
||||
).to.be.rejectedWith(AbortError)
|
||||
expect(stream.destroyed).to.be.true
|
||||
})
|
||||
|
||||
it('detaches from signal on success', async function () {
|
||||
const signal = AbortSignal.timeout(10_000)
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const s = await fetchStream(this.url('/hello'), { signal })
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(1)
|
||||
await pipeline(s, fs.createWriteStream('/dev/null'))
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('detaches from signal on error', async function () {
|
||||
const signal = AbortSignal.timeout(10_000)
|
||||
for (let i = 0; i < 20; i++) {
|
||||
try {
|
||||
await fetchStream(this.url('/500'), { signal })
|
||||
} catch (err) {
|
||||
if (err instanceof RequestFailedError && err.response.status === 500)
|
||||
continue
|
||||
throw err
|
||||
} finally {
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('fetchNothing', function () {
|
||||
@@ -419,16 +391,9 @@ async function* infiniteIterator() {
|
||||
async function abortOnceReceived(func, server) {
|
||||
const controller = new AbortController()
|
||||
const promise = func(controller.signal)
|
||||
expect(events.getEventListeners(controller.signal, 'abort')).to.have.length(1)
|
||||
await once(server.events, 'request-received')
|
||||
controller.abort()
|
||||
try {
|
||||
return await promise
|
||||
} finally {
|
||||
expect(events.getEventListeners(controller.signal, 'abort')).to.have.length(
|
||||
0
|
||||
)
|
||||
}
|
||||
return await promise
|
||||
}
|
||||
|
||||
async function expectRequestAborted(req) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"require": "test/setup.js"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
logger
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -11,7 +11,7 @@ const LoggingManager = {
|
||||
/**
|
||||
* @param {string} name - The name of the logger
|
||||
*/
|
||||
initialize(name, options = {}) {
|
||||
initialize(name) {
|
||||
this.isProduction =
|
||||
(process.env.NODE_ENV || '').toLowerCase() === 'production'
|
||||
const isTest = (process.env.NODE_ENV || '').toLowerCase() === 'test'
|
||||
@@ -27,7 +27,7 @@ const LoggingManager = {
|
||||
req: Serializers.req,
|
||||
res: Serializers.res,
|
||||
},
|
||||
streams: options.streams ?? [this._getOutputStreamConfig()],
|
||||
streams: [this._getOutputStreamConfig()],
|
||||
})
|
||||
this._setupRingBuffer()
|
||||
this._setupLogLevelChecker()
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"test:ci": "npm run test:unit",
|
||||
"test:unit": "mocha --exit test/**/*.{js,cjs}",
|
||||
"types:check": "tsc --noEmit"
|
||||
@@ -28,8 +28,6 @@
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.6",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"sandboxed-module": "^2.0.4",
|
||||
"sinon": "^9.2.4",
|
||||
"sinon-chai": "^3.7.0",
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
const logger = require('./')
|
||||
const bunyan = require('bunyan')
|
||||
const serializers = require('./serializers')
|
||||
|
||||
function testLogRecorder() {
|
||||
const currentTest = this.currentTest
|
||||
for (const level of ['error', 'fatal']) {
|
||||
logger[level] = (info, msg) => {
|
||||
const entry = { level, ...info, msg }
|
||||
for (const [name, fn] of Object.entries(serializers)) {
|
||||
if (name in entry) entry[name] = fn(entry[name])
|
||||
}
|
||||
currentTest.consoleErrors = (currentTest.consoleErrors || []).concat(
|
||||
JSON.stringify(info, bunyan.safeCycles())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = testLogRecorder
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: `reports/junit-mocha-${process.env.MOCHA_GREP}.xml`,
|
||||
includePending: true,
|
||||
jenkinsMode: true,
|
||||
output: true,
|
||||
},
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
metrics
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -75,4 +75,4 @@ class RequestLogger {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { monitor, RequestLogger }
|
||||
module.exports.monitor = monitor
|
||||
|
||||
@@ -97,12 +97,6 @@ function histogram(key, value, buckets, labels = {}) {
|
||||
}
|
||||
|
||||
class Timer {
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {number} sampleRate
|
||||
* @param {Record<string, any>} labels
|
||||
* @param {Array<number>} buckets
|
||||
*/
|
||||
constructor(key, sampleRate = 1, labels = {}, buckets = undefined) {
|
||||
if (typeof sampleRate === 'object') {
|
||||
// called with (key, labels, buckets)
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* before any other module to support code instrumentation.
|
||||
*/
|
||||
|
||||
const metricsModuleImportStartTime = performance.now()
|
||||
|
||||
const APP_NAME = process.env.METRICS_APP_NAME || 'unknown'
|
||||
const BUILD_VERSION = process.env.BUILD_VERSION
|
||||
const ENABLE_PROFILE_AGENT = process.env.ENABLE_PROFILE_AGENT === 'true'
|
||||
@@ -105,5 +103,3 @@ function recordProcessStart() {
|
||||
const metrics = require('.')
|
||||
metrics.inc('process_startup')
|
||||
}
|
||||
|
||||
module.exports = { metricsModuleImportStartTime }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.1.0",
|
||||
"@google-cloud/profiler": "^6.0.3",
|
||||
"@google-cloud/profiler": "^6.0.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.39.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.41.2",
|
||||
@@ -24,17 +24,15 @@
|
||||
"bunyan": "^1.0.0",
|
||||
"chai": "^4.3.6",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"sandboxed-module": "^2.0.4",
|
||||
"sinon": "^9.2.4",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"test:unit": "mocha --exit test/**/*.{js,cjs}",
|
||||
"test:acceptance": "mocha --recursive --exit --grep=$MOCHA_GREP test/acceptance",
|
||||
"test:acceptance": "mocha --reporter spec --recursive --exit --grep=$MOCHA_GREP test/acceptance",
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: `reports/junit-mocha-${process.env.MOCHA_GREP}.xml`,
|
||||
includePending: true,
|
||||
jenkinsMode: true,
|
||||
output: true,
|
||||
},
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
// no setup yet
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -16,7 +16,6 @@ let VERBOSE_LOGGING
|
||||
let BATCH_RANGE_START
|
||||
let BATCH_RANGE_END
|
||||
let BATCH_MAX_TIME_SPAN_IN_MS
|
||||
let BATCHED_UPDATE_RUNNING = false
|
||||
|
||||
/**
|
||||
* @typedef {import("mongodb").Collection} Collection
|
||||
@@ -35,7 +34,6 @@ let BATCHED_UPDATE_RUNNING = false
|
||||
* @property {string} [BATCH_RANGE_START]
|
||||
* @property {string} [BATCH_SIZE]
|
||||
* @property {string} [VERBOSE_LOGGING]
|
||||
* @property {(progress: string) => Promise<void>} [trackProgress]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -211,71 +209,59 @@ async function batchedUpdate(
|
||||
update,
|
||||
projection,
|
||||
findOptions,
|
||||
batchedUpdateOptions = {}
|
||||
batchedUpdateOptions
|
||||
) {
|
||||
// only a single batchedUpdate can run at a time due to global variables
|
||||
if (BATCHED_UPDATE_RUNNING) {
|
||||
throw new Error('batchedUpdate is already running')
|
||||
ID_EDGE_PAST = await getIdEdgePast(collection)
|
||||
if (!ID_EDGE_PAST) {
|
||||
console.warn(
|
||||
`The collection ${collection.collectionName} appears to be empty.`
|
||||
)
|
||||
return 0
|
||||
}
|
||||
try {
|
||||
BATCHED_UPDATE_RUNNING = true
|
||||
ID_EDGE_PAST = await getIdEdgePast(collection)
|
||||
if (!ID_EDGE_PAST) {
|
||||
console.warn(
|
||||
`The collection ${collection.collectionName} appears to be empty.`
|
||||
)
|
||||
return 0
|
||||
}
|
||||
refreshGlobalOptionsForBatchedUpdate(batchedUpdateOptions)
|
||||
const { trackProgress = async progress => console.warn(progress) } =
|
||||
batchedUpdateOptions
|
||||
refreshGlobalOptionsForBatchedUpdate(batchedUpdateOptions)
|
||||
|
||||
findOptions = findOptions || {}
|
||||
findOptions.readPreference = READ_PREFERENCE_SECONDARY
|
||||
findOptions = findOptions || {}
|
||||
findOptions.readPreference = READ_PREFERENCE_SECONDARY
|
||||
|
||||
projection = projection || { _id: 1 }
|
||||
let nextBatch
|
||||
let updated = 0
|
||||
let start = BATCH_RANGE_START
|
||||
projection = projection || { _id: 1 }
|
||||
let nextBatch
|
||||
let updated = 0
|
||||
let start = BATCH_RANGE_START
|
||||
|
||||
while (start !== BATCH_RANGE_END) {
|
||||
let end = getNextEnd(start)
|
||||
nextBatch = await getNextBatch(
|
||||
collection,
|
||||
query,
|
||||
start,
|
||||
end,
|
||||
projection,
|
||||
findOptions
|
||||
)
|
||||
if (nextBatch.length > 0) {
|
||||
end = nextBatch[nextBatch.length - 1]._id
|
||||
updated += nextBatch.length
|
||||
while (start !== BATCH_RANGE_END) {
|
||||
let end = getNextEnd(start)
|
||||
nextBatch = await getNextBatch(
|
||||
collection,
|
||||
query,
|
||||
start,
|
||||
end,
|
||||
projection,
|
||||
findOptions
|
||||
)
|
||||
if (nextBatch.length > 0) {
|
||||
end = nextBatch[nextBatch.length - 1]._id
|
||||
updated += nextBatch.length
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
console.log(
|
||||
`Running update on batch with ids ${JSON.stringify(
|
||||
nextBatch.map(entry => entry._id)
|
||||
)}`
|
||||
)
|
||||
}
|
||||
await trackProgress(
|
||||
`Running update on batch ending ${renderObjectId(end)}`
|
||||
if (VERBOSE_LOGGING) {
|
||||
console.log(
|
||||
`Running update on batch with ids ${JSON.stringify(
|
||||
nextBatch.map(entry => entry._id)
|
||||
)}`
|
||||
)
|
||||
|
||||
if (typeof update === 'function') {
|
||||
await update(nextBatch)
|
||||
} else {
|
||||
await performUpdate(collection, nextBatch, update)
|
||||
}
|
||||
} else {
|
||||
console.error(`Running update on batch ending ${renderObjectId(end)}`)
|
||||
}
|
||||
|
||||
if (typeof update === 'function') {
|
||||
await update(nextBatch)
|
||||
} else {
|
||||
await performUpdate(collection, nextBatch, update)
|
||||
}
|
||||
await trackProgress(`Completed batch ending ${renderObjectId(end)}`)
|
||||
start = end
|
||||
}
|
||||
return updated
|
||||
} finally {
|
||||
BATCHED_UPDATE_RUNNING = false
|
||||
console.error(`Completed batch ending ${renderObjectId(end)}`)
|
||||
start = end
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
mongo-utils
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
const { cleanupTestDatabase, dropTestDatabase } = require('./test-utils')
|
||||
|
||||
module.exports = { cleanupTestDatabase, dropTestDatabase }
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"test:unit": "mocha --exit test/**/*.{js,cjs}",
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
"test:ci": "npm run test:unit",
|
||||
@@ -22,8 +22,6 @@
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.6",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"sandboxed-module": "^2.0.4",
|
||||
"sinon": "^9.2.4",
|
||||
"sinon-chai": "^3.7.0",
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @import { MongoClient } from 'mongodb'
|
||||
* @import { MongoClient as LegacyMongoClient } from 'mongodb-legacy'
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delete all data from the test Mongo database
|
||||
*
|
||||
* This doesn't drop the collections, so indexes are preserved.
|
||||
*
|
||||
* @param {MongoClient | LegacyMongoClient} mongoClient
|
||||
*/
|
||||
async function cleanupTestDatabase(mongoClient) {
|
||||
ensureTestDatabase(mongoClient)
|
||||
const db = mongoClient.db()
|
||||
const allCollections = await db.collections()
|
||||
const collections = allCollections.filter(
|
||||
coll => coll.collectionName !== 'migrations'
|
||||
)
|
||||
await Promise.all(collections.map(coll => coll.deleteMany({})))
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the test Monto database
|
||||
*
|
||||
* This drops the whole database, including indexes.
|
||||
*
|
||||
* @param {MongoClient | LegacyMongoClient } mongoClient
|
||||
*/
|
||||
async function dropTestDatabase(mongoClient) {
|
||||
ensureTestDatabase(mongoClient)
|
||||
await mongoClient.db().dropDatabase()
|
||||
}
|
||||
/**
|
||||
* Ensure that the given client is connected to a test database.
|
||||
*
|
||||
* This should be called before performing destructive operations on the test
|
||||
* database.
|
||||
*
|
||||
* @param {MongoClient | LegacyMongoClient } mongoClient
|
||||
*/
|
||||
function ensureTestDatabase(mongoClient) {
|
||||
const dbName = mongoClient.db().databaseName
|
||||
const env = process.env.NODE_ENV
|
||||
|
||||
if (dbName !== 'test-overleaf' || env !== 'test') {
|
||||
throw new Error(
|
||||
`Refusing to clear database '${dbName}' in environment '${env}'`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { cleanupTestDatabase, dropTestDatabase }
|
||||
@@ -0,0 +1,11 @@
|
||||
const chai = require('chai')
|
||||
const sinonChai = require('sinon-chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
// Chai configuration
|
||||
chai.should()
|
||||
chai.use(sinonChai)
|
||||
|
||||
SandboxedModule.configure({
|
||||
globals: { Buffer, JSON, console, process },
|
||||
})
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,5 @@
|
||||
.nyc_output
|
||||
coverage
|
||||
node_modules/
|
||||
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
o-error
|
||||
--dependencies=None
|
||||
--docker-repos=gcr.io/overleaf-ops
|
||||
--env-add=
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.18.0
|
||||
--pipeline-owner=🚉 Platform
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.5.0
|
||||
|
||||
+10
-27
@@ -1,34 +1,20 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* Light-weight helpers for handling JavaScript Errors in node.js and the
|
||||
* browser.
|
||||
*/
|
||||
class OError extends Error {
|
||||
/**
|
||||
* The error that is the underlying cause of this error
|
||||
*
|
||||
* @type {unknown}
|
||||
*/
|
||||
cause
|
||||
|
||||
/**
|
||||
* List of errors encountered as the callback chain is unwound
|
||||
*
|
||||
* @type {TaggedError[] | undefined}
|
||||
*/
|
||||
_oErrorTags
|
||||
|
||||
/**
|
||||
* @param {string} message as for built-in Error
|
||||
* @param {Object} [info] extra data to attach to the error
|
||||
* @param {unknown} [cause] the internal error that caused this error
|
||||
* @param {Error} [cause] the internal error that caused this error
|
||||
*/
|
||||
constructor(message, info, cause) {
|
||||
super(message)
|
||||
this.name = this.constructor.name
|
||||
if (info) this.info = info
|
||||
if (cause) this.cause = cause
|
||||
/** @private @type {Array<TaggedError> | undefined} */
|
||||
this._oErrorTags // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +31,7 @@ class OError extends Error {
|
||||
/**
|
||||
* Wrap the given error, which caused this error.
|
||||
*
|
||||
* @param {unknown} cause the internal error that caused this error
|
||||
* @param {Error} cause the internal error that caused this error
|
||||
* @return {this}
|
||||
*/
|
||||
withCause(cause) {
|
||||
@@ -79,16 +65,13 @@ class OError extends Error {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @template {unknown} E
|
||||
* @param {E} error the error to tag
|
||||
* @param {Error} error the error to tag
|
||||
* @param {string} [message] message with which to tag `error`
|
||||
* @param {Object} [info] extra data with wich to tag `error`
|
||||
* @return {E} the modified `error` argument
|
||||
* @return {Error} the modified `error` argument
|
||||
*/
|
||||
static tag(error, message, info) {
|
||||
const oError = /** @type {{ _oErrorTags: TaggedError[] | undefined }} */ (
|
||||
error
|
||||
)
|
||||
const oError = /** @type{OError} */ (error)
|
||||
|
||||
if (!oError._oErrorTags) oError._oErrorTags = []
|
||||
|
||||
@@ -119,7 +102,7 @@ class OError extends Error {
|
||||
*
|
||||
* If an info property is repeated, the last one wins.
|
||||
*
|
||||
* @param {unknown} error any error (may or may not be an `OError`)
|
||||
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
|
||||
* @return {Object}
|
||||
*/
|
||||
static getFullInfo(error) {
|
||||
@@ -146,7 +129,7 @@ class OError extends Error {
|
||||
* Return the `stack` property from `error`, including the `stack`s for any
|
||||
* tagged errors added with `OError.tag` and for any `cause`s.
|
||||
*
|
||||
* @param {unknown} error any error (may or may not be an `OError`)
|
||||
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
|
||||
* @return {string}
|
||||
*/
|
||||
static getFullStack(error) {
|
||||
@@ -160,7 +143,7 @@ class OError extends Error {
|
||||
stack += `\n${oError._oErrorTags.map(tag => tag.stack).join('\n')}`
|
||||
}
|
||||
|
||||
const causeStack = OError.getFullStack(oError.cause)
|
||||
const causeStack = oError.cause && OError.getFullStack(oError.cause)
|
||||
if (causeStack) {
|
||||
stack += '\ncaused by:\n' + indent(causeStack)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run --silent test",
|
||||
"lint": "eslint --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .cjs,.js,.jsx,.mjs,.ts .",
|
||||
"lint": "eslint --ext .js --ext .cjs --ext .ts --max-warnings 0 --format unix .",
|
||||
"lint:fix": "eslint --fix --ext .js --ext .cjs --ext .ts .",
|
||||
"test": "npm run lint && npm run format && npm run types:check && npm run test:unit",
|
||||
"format": "prettier --list-different $PWD/'**/*.{js,cjs,ts}'",
|
||||
"format:fix": "prettier --write $PWD/'**/*.{js,cjs,ts}'",
|
||||
@@ -32,11 +32,9 @@
|
||||
"repository": "github:overleaf/o-error",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.0",
|
||||
"@types/node": "^18.19.123",
|
||||
"@types/node": "^18.17.4",
|
||||
"chai": "^4.3.6",
|
||||
"mocha": "^11.1.0",
|
||||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
mochaFile: `reports/junit-mocha-${process.env.MOCHA_GREP}.xml`,
|
||||
includePending: true,
|
||||
jenkinsMode: true,
|
||||
output: true,
|
||||
},
|
||||
}
|
||||
+1
-6
@@ -1,7 +1,7 @@
|
||||
const { expect } = require('chai')
|
||||
const { promisify } = require('node:util')
|
||||
|
||||
const OError = require('../..')
|
||||
const OError = require('..')
|
||||
|
||||
const {
|
||||
expectError,
|
||||
@@ -268,11 +268,6 @@ describe('utils', function () {
|
||||
expect(OError.getFullInfo(null)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('works when given a string', function () {
|
||||
const err = 'not an error instance'
|
||||
expect(OError.getFullInfo(err)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('works on a normal error', function () {
|
||||
const err = new Error('foo')
|
||||
expect(OError.getFullInfo(err)).to.deep.equal({})
|
||||
+1
-9
@@ -1,6 +1,6 @@
|
||||
const { expect } = require('chai')
|
||||
|
||||
const OError = require('../..')
|
||||
const OError = require('..')
|
||||
const {
|
||||
expectError,
|
||||
expectFullStackWithoutStackFramesToEqual,
|
||||
@@ -35,14 +35,6 @@ describe('OError', function () {
|
||||
expect(err2.cause.message).to.equal('cause 2')
|
||||
})
|
||||
|
||||
it('accepts non-Error causes', function () {
|
||||
const err1 = new OError('foo', {}, 'not-an-error')
|
||||
expect(err1.cause).to.equal('not-an-error')
|
||||
|
||||
const err2 = new OError('foo').withCause('not-an-error')
|
||||
expect(err2.cause).to.equal('not-an-error')
|
||||
})
|
||||
|
||||
it('handles a custom error type with a cause', function () {
|
||||
function doSomethingBadInternally() {
|
||||
throw new Error('internal error')
|
||||
@@ -1 +0,0 @@
|
||||
// no setup yet
|
||||
@@ -0,0 +1,61 @@
|
||||
const { expect } = require('chai')
|
||||
|
||||
const OError = require('../..')
|
||||
|
||||
/**
|
||||
* @param {Error} e
|
||||
* @param {any} expected
|
||||
*/
|
||||
exports.expectError = function OErrorExpectError(e, expected) {
|
||||
expect(
|
||||
e.name,
|
||||
"error should set the name property to the error's name"
|
||||
).to.equal(expected.name)
|
||||
|
||||
expect(
|
||||
e instanceof expected.klass,
|
||||
'error should be an instance of the error type'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
e instanceof Error,
|
||||
'error should be an instance of the built-in Error type'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
require('node:util').types.isNativeError(e),
|
||||
'error should be recognised by util.types.isNativeError'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
e.toString(),
|
||||
'toString should return the default error message formatting'
|
||||
).to.equal(expected.message)
|
||||
|
||||
expect(e.stack, 'error should have a stack trace').to.not.be.empty
|
||||
|
||||
expect(
|
||||
/** @type {string} */ (e.stack).split('\n')[0],
|
||||
'stack should start with the default error message formatting'
|
||||
).to.match(new RegExp(`^${expected.name}:`))
|
||||
|
||||
expect(
|
||||
/** @type {string} */ (e.stack).split('\n')[1],
|
||||
'first stack frame should be the function where the error was thrown'
|
||||
).to.match(expected.firstFrameRx)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Error} error
|
||||
* @param {String[]} expected
|
||||
*/
|
||||
exports.expectFullStackWithoutStackFramesToEqual = function (error, expected) {
|
||||
const fullStack = OError.getFullStack(error)
|
||||
const fullStackWithoutFrames = fullStack
|
||||
.split('\n')
|
||||
.filter(line => !/^\s+at\s/.test(line))
|
||||
expect(
|
||||
fullStackWithoutFrames,
|
||||
'full stack without frames should equal'
|
||||
).to.deep.equal(expected)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
const { expect } = require('chai')
|
||||
|
||||
const OError = require('../../..')
|
||||
|
||||
/**
|
||||
* @param {Error} e
|
||||
* @param {any} expected
|
||||
*/
|
||||
exports.expectError = function OErrorExpectError(e, expected) {
|
||||
expect(
|
||||
e.name,
|
||||
"error should set the name property to the error's name"
|
||||
).to.equal(expected.name)
|
||||
|
||||
expect(
|
||||
e instanceof expected.klass,
|
||||
'error should be an instance of the error type'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
e instanceof Error,
|
||||
'error should be an instance of the built-in Error type'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
require('node:util').types.isNativeError(e),
|
||||
'error should be recognised by util.types.isNativeError'
|
||||
).to.be.true
|
||||
|
||||
expect(
|
||||
e.toString(),
|
||||
'toString should return the default error message formatting'
|
||||
).to.equal(expected.message)
|
||||
|
||||
expect(e.stack, 'error should have a stack trace').to.not.be.empty
|
||||
|
||||
expect(
|
||||
/** @type {string} */ (e.stack).split('\n')[0],
|
||||
'stack should start with the default error message formatting'
|
||||
).to.match(new RegExp(`^${expected.name}:`))
|
||||
|
||||
expect(
|
||||
/** @type {string} */ (e.stack).split('\n')[1],
|
||||
'first stack frame should be the function where the error was thrown'
|
||||
).to.match(expected.firstFrameRx)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Error} error
|
||||
* @param {String[]} expected
|
||||
*/
|
||||
exports.expectFullStackWithoutStackFramesToEqual = function (error, expected) {
|
||||
const fullStack = OError.getFullStack(error)
|
||||
const fullStackWithoutFrames = fullStack
|
||||
.split('\n')
|
||||
.filter(line => !/^\s+at\s/.test(line))
|
||||
expect(
|
||||
fullStackWithoutFrames,
|
||||
'full stack without frames should equal'
|
||||
).to.deep.equal(expected)
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.cjs",
|
||||
"**/*.ts"
|
||||
"**/*.cjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
*.swp
|
||||
|
||||
.npmrc
|
||||
@@ -1,13 +0,0 @@
|
||||
let reporterOptions = {}
|
||||
if (process.env.CI) {
|
||||
reporterOptions = {
|
||||
reporter: '/overleaf/node_modules/mocha-multi-reporters',
|
||||
'reporter-options': ['configFile=./test/mocha-multi-reporters.cjs'],
|
||||
}
|
||||
}
|
||||
const all = {
|
||||
require: 'test/setup.js',
|
||||
...reporterOptions,
|
||||
}
|
||||
|
||||
module.exports = all
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ui": "bdd",
|
||||
"recursive": "true",
|
||||
"reporter": "spec",
|
||||
"require": "./test/Init"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
22.18.0
|
||||
20.18.2
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user