├── .editorconfig ├── .github └── workflows │ ├── bb.yml │ └── main.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── index.js ├── lib ├── index.js └── list.js ├── license ├── package.json ├── readme.md ├── test.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/workflows/bb.yml: -------------------------------------------------------------------------------- 1 | name: bb 2 | on: 3 | issues: 4 | types: [opened, reopened, edited, closed, labeled, unlabeled] 5 | pull_request_target: 6 | types: [opened, reopened, edited, closed, labeled, unlabeled] 7 | jobs: 8 | main: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: unifiedjs/beep-boop-beta@main 12 | with: 13 | repo-token: ${{secrets.GITHUB_TOKEN}} 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: 3 | - pull_request 4 | - push 5 | jobs: 6 | main: 7 | name: ${{matrix.node}} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: ${{matrix.node}} 14 | - run: npm install 15 | - run: npm test 16 | - uses: codecov/codecov-action@v3 17 | strategy: 18 | matrix: 19 | node: 20 | - lts/gallium 21 | - node 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .DS_Store 4 | *.d.ts 5 | *.log 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-scripts=true 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.md 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('./lib/index.js').Options} Options 3 | */ 4 | 5 | export {default} from './lib/index.js' 6 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('nlcst').Root} Root 3 | * 4 | * @typedef {import('vfile').VFile} VFile 5 | */ 6 | 7 | /** 8 | * @typedef Options 9 | * Configuration. 10 | * @property {ReadonlyArray | null | undefined} [ignore] 11 | * Phrases *not* to warn about (optional). 12 | */ 13 | 14 | import {search} from 'nlcst-search' 15 | import {toString} from 'nlcst-to-string' 16 | import {findBefore} from 'unist-util-find-before' 17 | import {pointEnd, pointStart} from 'unist-util-position' 18 | import {list} from './list.js' 19 | 20 | /** @type {Readonly} */ 21 | const emptyOptions = {} 22 | /** @type {ReadonlyArray} */ 23 | const emptyIgnore = [] 24 | 25 | const verbs = new Set(['am', 'are', 'were', 'being', 'is', 'been', 'was', 'be']) 26 | 27 | /** 28 | * Check for passive voice. 29 | * 30 | * @param {Readonly | null | undefined} [options] 31 | * Configuration (optional). 32 | * @returns 33 | * Transform. 34 | */ 35 | export default function retextPassive(options) { 36 | const settings = options || emptyOptions 37 | const ignore = settings.ignore || emptyIgnore 38 | const phrases = 39 | ignore.length > 0 40 | ? list.filter(function (d) { 41 | return !ignore.includes(d) 42 | }) 43 | : [...list] 44 | 45 | /** 46 | * Transform. 47 | * 48 | * @param {Root} tree 49 | * Tree. 50 | * @param {VFile} file 51 | * File. 52 | * @returns {undefined} 53 | * Nothing. 54 | */ 55 | return function (tree, file) { 56 | search(tree, phrases, function (match, index, parent, phrase) { 57 | const before = findBefore(parent, index, 'WordNode') 58 | 59 | if (!before || !verbs.has(toString(before).toLowerCase())) { 60 | return 61 | } 62 | 63 | const start = pointStart(match[0]) 64 | const end = pointEnd(match[match.length - 1]) 65 | 66 | const message = file.message('Unexpected use of the passive voice', { 67 | ancestors: [parent], 68 | /* c8 ignore next -- hard to test */ 69 | place: start && end ? {start, end} : undefined, 70 | source: 'retext-passive', 71 | ruleId: phrase.replace(/\s+/g, '-').toLowerCase() 72 | }) 73 | 74 | message.actual = toString(match) 75 | message.expected = [] 76 | message.url = 'https://github.com/retextjs/retext-passive#readme' 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/list.js: -------------------------------------------------------------------------------- 1 | /** @type {ReadonlyArray} */ 2 | export const list = [ 3 | 'awoken', 4 | 'awoke', 5 | 'beaten', 6 | 'beat', 7 | 'begun', 8 | 'began', 9 | 'bent', 10 | 'bitten', 11 | 'bit', 12 | 'bled', 13 | 'blown', 14 | 'blew', 15 | 'broken', 16 | 'broke', 17 | 'brought', 18 | 'built', 19 | 'bought', 20 | 'caught', 21 | 'chosen', 22 | 'chose', 23 | 'dealt', 24 | 'done', 25 | 'did', 26 | 'drawn', 27 | 'drew', 28 | 'driven', 29 | 'drove', 30 | 'eaten', 31 | 'ate', 32 | 'fed', 33 | 'felt', 34 | 'fought', 35 | 'found', 36 | 'forbidden', 37 | 'forbade', 38 | 'forgotten', 39 | 'forgot', 40 | 'forgiven', 41 | 'forgave', 42 | 'frozen', 43 | 'froze', 44 | 'gotten', 45 | 'got', 46 | 'given', 47 | 'gave', 48 | 'ground', 49 | 'grinded', 50 | 'hung', 51 | 'heard', 52 | 'hidden', 53 | 'hid', 54 | 'hit', 55 | 'held', 56 | 'hurt', 57 | 'kept', 58 | 'known', 59 | 'knew', 60 | 'laid', 61 | 'led', 62 | 'let', 63 | 'lost', 64 | 'made', 65 | 'meant', 66 | 'met', 67 | 'paid', 68 | 'proven', 69 | 'proved', 70 | 'put', 71 | 'read', 72 | 'ridden', 73 | 'rode', 74 | 'rung', 75 | 'rang', 76 | 'run', 77 | 'ran', 78 | 'said', 79 | 'seen', 80 | 'saw', 81 | 'sold', 82 | 'sent', 83 | 'shaken', 84 | 'shook', 85 | 'shaved', 86 | 'shot', 87 | 'shown', 88 | 'shut', 89 | 'sung', 90 | 'sunk', 91 | 'slain', 92 | 'slew', 93 | 'slid', 94 | 'spoken', 95 | 'spoke', 96 | 'spent', 97 | 'spun', 98 | 'split', 99 | 'spread', 100 | 'stolen', 101 | 'stole', 102 | 'struck', 103 | 'swept', 104 | 'swung', 105 | 'taken', 106 | 'took', 107 | 'taught', 108 | 'torn', 109 | 'tore', 110 | 'told', 111 | 'thought', 112 | 'thrown', 113 | 'threw', 114 | 'undergone', 115 | 'underwent', 116 | 'understood', 117 | 'upset', 118 | 'woken', 119 | 'woke', 120 | 'worn', 121 | 'wore', 122 | 'won', 123 | 'withdrawn', 124 | 'withdrew', 125 | 'written', 126 | 'wrote', 127 | 'been', 128 | 'born', 129 | 'become', 130 | 'beset', 131 | 'bet', 132 | 'bid', 133 | 'bidden', 134 | 'bound', 135 | 'bred', 136 | 'broadcast', 137 | 'burnt', 138 | 'burst', 139 | 'cast', 140 | 'clung', 141 | 'come', 142 | 'cost', 143 | 'crept', 144 | 'cut', 145 | 'dug', 146 | 'dived', 147 | 'dreamt', 148 | 'drunk', 149 | 'fallen', 150 | 'fit', 151 | 'fled', 152 | 'flung', 153 | 'flown', 154 | 'foregone', 155 | 'forsaken', 156 | 'gone', 157 | 'grown', 158 | 'knelt', 159 | 'knit', 160 | 'leapt', 161 | 'learnt', 162 | 'left', 163 | 'lent', 164 | 'lain', 165 | 'lighted', 166 | 'misspelt', 167 | 'mistaken', 168 | 'mown', 169 | 'overcome', 170 | 'overdone', 171 | 'overtaken', 172 | 'overthrown', 173 | 'pled', 174 | 'quit', 175 | 'rid', 176 | 'risen', 177 | 'sawn', 178 | 'sought', 179 | 'set', 180 | 'sewn', 181 | 'shaven', 182 | 'shorn', 183 | 'shed', 184 | 'shone', 185 | 'shod', 186 | 'shrunk', 187 | 'sat', 188 | 'slept', 189 | 'slung', 190 | 'slit', 191 | 'smitten', 192 | 'sown', 193 | 'sped', 194 | 'spilt', 195 | 'spit', 196 | 'sprung', 197 | 'stood', 198 | 'stuck', 199 | 'stung', 200 | 'stunk', 201 | 'stridden', 202 | 'strung', 203 | 'striven', 204 | 'sworn', 205 | 'swollen', 206 | 'swum', 207 | 'thrived', 208 | 'thrust', 209 | 'trodden', 210 | 'upheld', 211 | 'woven', 212 | 'wed', 213 | 'wept', 214 | 'wound', 215 | 'withheld', 216 | 'withstood', 217 | 'wrung' 218 | ] 219 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2016 Titus Wormer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retext-passive", 3 | "version": "5.0.0", 4 | "description": "retext plugin to check for passive voice", 5 | "license": "MIT", 6 | "keywords": [ 7 | "passive", 8 | "plugin", 9 | "retext", 10 | "retext-plugin", 11 | "unified", 12 | "voice" 13 | ], 14 | "repository": "retextjs/retext-passive", 15 | "bugs": "https://github.com/retextjs/retext-passive/issues", 16 | "funding": { 17 | "type": "opencollective", 18 | "url": "https://opencollective.com/unified" 19 | }, 20 | "author": "Titus Wormer (https://wooorm.com)", 21 | "contributors": [ 22 | "Titus Wormer (https://wooorm.com)", 23 | "Sylvan Swierkosz " 24 | ], 25 | "sideEffects": false, 26 | "type": "module", 27 | "exports": "./index.js", 28 | "files": [ 29 | "lib/", 30 | "index.d.ts", 31 | "index.js" 32 | ], 33 | "dependencies": { 34 | "@types/nlcst": "^2.0.0", 35 | "nlcst-search": "^4.0.0", 36 | "nlcst-to-string": "^4.0.0", 37 | "unist-util-find-before": "^4.0.0", 38 | "unist-util-position": "^5.0.0", 39 | "vfile": "^6.0.0" 40 | }, 41 | "devDependencies": { 42 | "@types/node": "^20.0.0", 43 | "c8": "^8.0.0", 44 | "prettier": "^3.0.0", 45 | "remark-cli": "^11.0.0", 46 | "remark-preset-wooorm": "^9.0.0", 47 | "retext": "^9.0.0", 48 | "type-coverage": "^2.0.0", 49 | "typescript": "^5.0.0", 50 | "xo": "^0.56.0" 51 | }, 52 | "scripts": { 53 | "build": "tsc --build --clean && tsc --build && type-coverage", 54 | "format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix", 55 | "prepack": "npm run build && npm run format", 56 | "test": "npm run build && npm run format && npm run test-coverage", 57 | "test-api": "node --conditions development test.js", 58 | "test-coverage": "c8 --100 --check-coverage --reporter lcov npm run test-api" 59 | }, 60 | "prettier": { 61 | "bracketSpacing": false, 62 | "singleQuote": true, 63 | "semi": false, 64 | "tabWidth": 2, 65 | "trailingComma": "none", 66 | "useTabs": false 67 | }, 68 | "remarkConfig": { 69 | "plugins": [ 70 | "remark-preset-wooorm" 71 | ] 72 | }, 73 | "typeCoverage": { 74 | "atLeast": 100, 75 | "detail": true, 76 | "ignoreCatch": true, 77 | "strict": true 78 | }, 79 | "xo": { 80 | "prettier": true, 81 | "rules": { 82 | "unicorn/prefer-at": "off", 83 | "unicorn/prefer-string-replace-all": "off" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # retext-passive 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | [![Sponsors][sponsors-badge]][collective] 8 | [![Backers][backers-badge]][collective] 9 | [![Chat][chat-badge]][chat] 10 | 11 | **[retext][]** plugin to check for passive voice. 12 | 13 | ## Contents 14 | 15 | * [What is this?](#what-is-this) 16 | * [When should I use this?](#when-should-i-use-this) 17 | * [Install](#install) 18 | * [Use](#use) 19 | * [API](#api) 20 | * [`unified().use(retextPassive[, options])`](#unifieduseretextpassive-options) 21 | * [`Options`](#options) 22 | * [Messages](#messages) 23 | * [Types](#types) 24 | * [Compatibility](#compatibility) 25 | * [Related](#related) 26 | * [Contribute](#contribute) 27 | * [License](#license) 28 | 29 | ## What is this? 30 | 31 | This package is a [unified][] ([retext][]) plugin to check for the passive 32 | voice. 33 | It checks for certain verbs (`'am'`, `'are'`, `'were'`, `'being'`, `'is'`, 34 | `'been'`, `'was'`, or `'be'`), followed by a word in [`list.js`][file-list]. 35 | 36 | ## When should I use this? 37 | 38 | You can opt-into this plugin when you’re dealing with content that might contain 39 | weak language, and have authors that can fix that content. 40 | 41 | ## Install 42 | 43 | This package is [ESM only][esm]. 44 | In Node.js (version 16+), install with [npm][]: 45 | 46 | ```sh 47 | npm install retext-passive 48 | ``` 49 | 50 | In Deno with [`esm.sh`][esmsh]: 51 | 52 | ```js 53 | import retextPassive from 'https://esm.sh/retext-passive@5' 54 | ``` 55 | 56 | In browsers with [`esm.sh`][esmsh]: 57 | 58 | ```html 59 | 62 | ``` 63 | 64 | ## Use 65 | 66 | Say our document `example.txt` contains: 67 | 68 | ```txt 69 | He was withheld while we were being fed. 70 | ``` 71 | 72 | …and our module `example.js` contains: 73 | 74 | ```js 75 | import retextEnglish from 'retext-english' 76 | import retextPassive from 'retext-passive' 77 | import retextStringify from 'retext-stringify' 78 | import {read} from 'to-vfile' 79 | import {unified} from 'unified' 80 | import {reporter} from 'vfile-reporter' 81 | 82 | const file = await unified() 83 | .use(retextEnglish) 84 | .use(retextPassive) 85 | .use(retextStringify) 86 | .process(await read('example.txt')) 87 | 88 | console.error(reporter(file)) 89 | ``` 90 | 91 | …then running `node example.js` yields: 92 | 93 | ```txt 94 | example.txt 95 | 1:8-1:16 warning Unexpected use of the passive voice withheld retext-passive 96 | 1:37-1:40 warning Unexpected use of the passive voice fed retext-passive 97 | 98 | ⚠ 2 warnings 99 | ``` 100 | 101 | ## API 102 | 103 | This package exports no identifiers. 104 | The default export is [`retextPassive`][api-retext-passive]. 105 | 106 | ### `unified().use(retextPassive[, options])` 107 | 108 | Check for the passive voice. 109 | 110 | ###### Parameters 111 | 112 | * `options` ([`Options`][api-options], optional) 113 | — configuration 114 | 115 | ###### Returns 116 | 117 | Transform ([`Transformer`][unified-transformer]). 118 | 119 | ### `Options` 120 | 121 | Configuration (TypeScript type). 122 | 123 | ###### Fields 124 | 125 | * `ignore` (`Array`, optional) 126 | — phrases *not* to warn about 127 | 128 | ## Messages 129 | 130 | Each message is emitted as a [`VFileMessage`][vfile-message] on `file`, with 131 | `source` set to `'retext-passive'`, `ruleId` to any word in 132 | [`list.js`][file-list], `actual` to the unexpected phrase, and `expected` to an 133 | empty array. 134 | 135 | ## Types 136 | 137 | This package is fully typed with [TypeScript][]. 138 | It exports the additional type [`Options`][api-options]. 139 | 140 | ## Compatibility 141 | 142 | Projects maintained by the unified collective are compatible with maintained 143 | versions of Node.js. 144 | 145 | When we cut a new major release, we drop support for unmaintained versions of 146 | Node. 147 | This means we try to keep the current release line, `retext-passive@^5`, 148 | compatible with Node.js 16. 149 | 150 | ## Related 151 | 152 | * [`retext-equality`](https://github.com/retextjs/retext-equality) 153 | — check possible insensitive, inconsiderate language 154 | * [`retext-profanities`](https://github.com/retextjs/retext-profanities) 155 | — check for profane and vulgar wording 156 | * [`retext-simplify`](https://github.com/retextjs/retext-simplify) 157 | — check phrases for simpler alternatives 158 | 159 | ## Contribute 160 | 161 | See [`contributing.md`][contributing] in [`retextjs/.github`][health] for ways 162 | to get started. 163 | See [`support.md`][support] for ways to get help. 164 | 165 | This project has a [code of conduct][coc]. 166 | By interacting with this repository, organization, or community you agree to 167 | abide by its terms. 168 | 169 | ## License 170 | 171 | [MIT][license] © [Titus Wormer][author] 172 | 173 | 174 | 175 | [build-badge]: https://github.com/retextjs/retext-passive/workflows/main/badge.svg 176 | 177 | [build]: https://github.com/retextjs/retext-passive/actions 178 | 179 | [coverage-badge]: https://img.shields.io/codecov/c/github/retextjs/retext-passive.svg 180 | 181 | [coverage]: https://codecov.io/github/retextjs/retext-passive 182 | 183 | [downloads-badge]: https://img.shields.io/npm/dm/retext-passive.svg 184 | 185 | [downloads]: https://www.npmjs.com/package/retext-passive 186 | 187 | [size-badge]: https://img.shields.io/bundlejs/size/retext-passive 188 | 189 | [size]: https://bundlejs.com/?q=retext-passive 190 | 191 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 192 | 193 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 194 | 195 | [collective]: https://opencollective.com/unified 196 | 197 | [chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg 198 | 199 | [chat]: https://github.com/retextjs/retext/discussions 200 | 201 | [npm]: https://docs.npmjs.com/cli/install 202 | 203 | [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 204 | 205 | [esmsh]: https://esm.sh 206 | 207 | [typescript]: https://www.typescriptlang.org 208 | 209 | [health]: https://github.com/retextjs/.github 210 | 211 | [contributing]: https://github.com/retextjs/.github/blob/main/contributing.md 212 | 213 | [support]: https://github.com/retextjs/.github/blob/main/support.md 214 | 215 | [coc]: https://github.com/retextjs/.github/blob/main/code-of-conduct.md 216 | 217 | [license]: license 218 | 219 | [author]: https://wooorm.com 220 | 221 | [retext]: https://github.com/retextjs/retext 222 | 223 | [unified]: https://github.com/unifiedjs/unified 224 | 225 | [unified-transformer]: https://github.com/unifiedjs/unified#transformer 226 | 227 | [vfile-message]: https://github.com/vfile/vfile-message 228 | 229 | [file-list]: lib/list.js 230 | 231 | [api-options]: #options 232 | 233 | [api-retext-passive]: #unifieduseretextpassive-options 234 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict' 2 | import test from 'node:test' 3 | import {retext} from 'retext' 4 | import retextPassive from 'retext-passive' 5 | 6 | test('retext-passive', async function (t) { 7 | await t.test('should expose the public api', async function () { 8 | assert.deepEqual(Object.keys(await import('retext-passive')).sort(), [ 9 | 'default' 10 | ]) 11 | }) 12 | 13 | const doc = 'He was withheld while we were being fed.\nFed.\nThe fed.' 14 | const file = await retext().use(retextPassive).process(doc) 15 | 16 | await t.test('should emit a message w/ metadata', async function () { 17 | assert.deepEqual( 18 | JSON.parse(JSON.stringify({...file.messages[0], ancestors: []})), 19 | { 20 | ancestors: [], 21 | column: 8, 22 | fatal: false, 23 | message: 'Unexpected use of the passive voice', 24 | line: 1, 25 | name: '1:8-1:16', 26 | place: { 27 | start: {line: 1, column: 8, offset: 7}, 28 | end: {line: 1, column: 16, offset: 15} 29 | }, 30 | reason: 'Unexpected use of the passive voice', 31 | ruleId: 'withheld', 32 | source: 'retext-passive', 33 | actual: 'withheld', 34 | expected: [], 35 | url: 'https://github.com/retextjs/retext-passive#readme' 36 | } 37 | ) 38 | }) 39 | 40 | await t.test('should emit messasges', async function () { 41 | assert.deepEqual(file.messages.map(String), [ 42 | '1:8-1:16: Unexpected use of the passive voice', 43 | '1:37-1:40: Unexpected use of the passive voice' 44 | ]) 45 | }) 46 | 47 | await t.test('should `ignore`', async function () { 48 | const file = await retext() 49 | .use(retextPassive, {ignore: ['fed']}) 50 | .process(doc) 51 | 52 | assert.deepEqual(file.messages.map(String), [ 53 | '1:8-1:16: Unexpected use of the passive voice' 54 | ]) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": true, 4 | "customConditions": ["development"], 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "exactOptionalPropertyTypes": true, 8 | "lib": ["es2022"], 9 | "module": "node16", 10 | "strict": true, 11 | "target": "es2022" 12 | }, 13 | "exclude": ["coverage/", "node_modules/"], 14 | "include": ["**/*.js"] 15 | } 16 | --------------------------------------------------------------------------------