├── .changeset ├── README.md └── config.json ├── .cleanmodules-default ├── .gitattributes ├── .github └── workflows │ ├── changesets.yml │ └── checks.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __mocks__ └── fs │ ├── index.ts │ └── promises.ts ├── bin └── cli.js ├── biome.json ├── images ├── dry-run.png ├── large-project.png └── small-project.png ├── package.json ├── pnpm-lock.yaml ├── src ├── __test__ │ ├── getMockedFileStructure.ts │ └── path.serializer.ts ├── analyze.test.ts ├── analyze.ts ├── clean.test.ts ├── clean.ts ├── cli │ ├── cli.ts │ ├── commands │ │ ├── analyze.command.ts │ │ └── clean.command.ts │ ├── helpers │ │ └── base.command.ts │ └── utils │ │ └── terminal.ts ├── index.ts ├── shared.ts └── utils │ ├── filesystem.test.ts │ ├── filesystem.ts │ ├── glob.test.ts │ └── glob.ts ├── tsconfig.json ├── tsup.config.ts ├── vitest.config.ts └── vitest.setup.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.1.1/schema.json", 3 | "changelog": ["changesets-changelog-clean", { "repo": "duniul/clean-modules" }], 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.cleanmodules-default: -------------------------------------------------------------------------------- 1 | # 2 | # DIRECTORIES 3 | # Directory globs must end with either / or /* or /** 4 | # 5 | 6 | # test 7 | __tests__/ 8 | test/ 9 | tests/ 10 | powered-test/ 11 | 12 | # docs 13 | docs/ 14 | doc/ 15 | 16 | # IDE 17 | .idea/ 18 | .vscode/ 19 | 20 | # examples 21 | example/ 22 | examples/ 23 | 24 | # code coverage 25 | coverage/ 26 | .nyc_output/ 27 | .nycrc/ 28 | 29 | # CI/CD 30 | .circleci/ 31 | .github/ 32 | 33 | # git 34 | .git/ 35 | 36 | # other 37 | website/ 38 | .husky/ 39 | 40 | # 41 | # FILE EXTENSIONS 42 | # 43 | 44 | # IDE 45 | *.iml 46 | 47 | # tests 48 | *.test.* 49 | 50 | # source maps 51 | *.map 52 | 53 | # docs/text 54 | *.@(md|mkd|markdown|mdown) 55 | *.todo 56 | *.mustache 57 | *.@(adoc|asciidoc) 58 | *.DOCS 59 | 60 | # compiled 61 | # include .ts files but not .d.ts 62 | \!(*.d).ts 63 | *.coffee 64 | 65 | # compressed 66 | *.zip 67 | *.7z 68 | *.rar 69 | *.tar 70 | *.tgz 71 | 72 | # images 73 | *.@(jpg|jpeg) 74 | *.png 75 | *.gif 76 | 77 | # compiled 78 | *.h 79 | *.c 80 | *.hpp 81 | *.cpp 82 | *.o 83 | *.mk 84 | 85 | # other 86 | *.log 87 | *.tlog 88 | *.patch 89 | *.sln 90 | *.pdb 91 | *.jst 92 | *.swp 93 | *.lock 94 | *.vcxproj*(.*) 95 | *.orig 96 | *.rej 97 | 98 | # 99 | # FILES 100 | # 101 | 102 | # build scripts/files 103 | makefile*(.*) 104 | gemfile*(.*) 105 | gulpfile*(.*) 106 | gruntfile*(.*) 107 | rakefile*(.*) 108 | 109 | # CI/CD 110 | Jenkinsfile 111 | .travis.yml 112 | .gitlab-ci.yml 113 | .appveyor.yml 114 | circle.yml 115 | .nojekyll 116 | 117 | # Mac OS 118 | .DS_Store 119 | 120 | # Windows 121 | Desktop.ini 122 | Thumbs.db 123 | 124 | # linters 125 | .eslintrc*(.*) 126 | eslint.config.@(cjs|js|mjs) 127 | stylelint.config.js 128 | .stylelintrc*(.*) 129 | .htmllintrc*(.*) 130 | htmllint.js 131 | .jshintrc*(.*) 132 | .lint 133 | tslint.json 134 | .lintignore 135 | .jshintignore 136 | .eslintignore 137 | 138 | # prettier 139 | .prettierrc*(.*) 140 | prettier.config.js 141 | 142 | # testing 143 | \!(utils)/test.js 144 | jest.config.js 145 | karma.conf.js 146 | wallaby.js 147 | wallaby.conf.js 148 | .coveralls.yml 149 | .airtap.yml 150 | 151 | # docs 152 | readme*(.*) 153 | changelog*(.*) 154 | changes*(.*) 155 | authors*(.*) 156 | contributors*(.*) 157 | contributing*(.*) 158 | notice*(.*) 159 | copying*(.*) 160 | 161 | # licenses 162 | licen@(s|c)e 163 | licen@(s|c)e-mit 164 | licen@(s|c)e.txt 165 | licen@(s|c)e-mit.txt 166 | 167 | # typescript 168 | .tsbuildinfo 169 | 170 | # npm 171 | .npmrc 172 | 173 | # ignore files 174 | .*ignore 175 | 176 | # yarn 177 | yarn.lock 178 | .yarnclean 179 | .yarn-metadata.json 180 | 181 | # git 182 | .gitmodules 183 | .gitattributes 184 | 185 | # configs 186 | .npmrc 187 | .babelrc 188 | .editorconfig 189 | .tern-project 190 | .flowconfig 191 | .documentup.json 192 | .yo-rc.json 193 | _config.yml 194 | bower.json 195 | renovate.json 196 | .vimrc*(.*) 197 | .zuul.yml 198 | .istanbul.yml 199 | .nycrc 200 | .jscsrc 201 | 202 | # versioning 203 | .bmp.yml 204 | .release-it.@(cjs|js|json|toml|ts|yaml|yml) 205 | 206 | # other 207 | .dir-locals.el 208 | binding.gyp 209 | cakefile 210 | component.json 211 | composer.json 212 | contributors 213 | .jamignore 214 | .codecov.yml 215 | pom.xml 216 | OSSMETADATA 217 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.jpg binary 3 | *.png binary 4 | *.gif binary 5 | -------------------------------------------------------------------------------- /.github/workflows/changesets.yml: -------------------------------------------------------------------------------- 1 | name: 🆕 Release management 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '.vscode/**' 9 | - 'images/**' 10 | - '*.md' 11 | 12 | concurrency: ${{ github.workflow }}-${{ github.ref }} 13 | 14 | permissions: 15 | contents: write 16 | pull-requests: write 17 | 18 | jobs: 19 | main: 20 | name: Publish changed packages 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: 🏗 Setup repo 24 | uses: actions/checkout@v4 25 | 26 | - name: 🏗 Setup pnpm 27 | uses: pnpm/action-setup@v2 28 | with: 29 | version: 8 30 | 31 | - name: 🏗 Setup Node 32 | uses: actions/setup-node@v3 33 | with: 34 | node-version: 18.x 35 | cache: pnpm 36 | 37 | - name: 📦 Install dependencies 38 | run: pnpm install 39 | 40 | - name: 🆕 Publish to npm or create release PR 41 | id: changesets 42 | uses: changesets/action@v1 43 | with: 44 | title: '🆕 Upcoming release' 45 | commit: 'chore: bump version' 46 | version: pnpm run version 47 | publish: pnpm run release 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 51 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: 🕵️ Checks 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths-ignore: 7 | - '.vscode/**' 8 | - 'images/**' 9 | - '*.md' 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | build: 16 | name: 🧱 Build 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | steps: 21 | - name: 🏗 Setup repo 22 | uses: actions/checkout@v4 23 | 24 | - name: 🏗 Setup pnpm 25 | uses: pnpm/action-setup@v2 26 | with: 27 | version: 8 28 | 29 | - name: 🏗 Setup Node 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: 18.x 33 | cache: pnpm 34 | 35 | - name: 📦 Install dependencies 36 | run: pnpm install 37 | 38 | - name: 🧱 Build project 39 | run: pnpm build 40 | 41 | test: 42 | name: 🧪 Test 43 | strategy: 44 | matrix: 45 | os: [ubuntu-latest, macos-latest, windows-latest] 46 | node-version: [14.x, 18.x] 47 | 48 | runs-on: ${{ matrix.os }} 49 | 50 | steps: 51 | - name: 🏗 Setup repo 52 | uses: actions/checkout@v4 53 | 54 | - name: 🏗 Setup pnpm 55 | uses: pnpm/action-setup@v2 56 | with: 57 | version: 8 58 | 59 | - name: 🏗 Setup Node 60 | uses: actions/setup-node@v3 61 | with: 62 | node-version: 18.x 63 | cache: pnpm 64 | 65 | - name: 📦 Install dependencies 66 | run: pnpm install 67 | 68 | - name: 🧪 Run tests 69 | run: pnpm test 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/* 113 | !.yarn/patches 114 | !.yarn/releases 115 | !.yarn/plugins 116 | !.yarn/sdks 117 | !.yarn/versions 118 | .pnp.* 119 | 120 | # Built files 121 | lib 122 | .tsbuildinfo 123 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true, 4 | "printWidth": 100, 5 | "arrowParens": "avoid" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Make VSCode warn about same commit lengths for everyone 3 | "git.inputValidation": "warn", 4 | "git.inputValidationLength": 100, 5 | "git.inputValidationSubjectLength": 72, 6 | 7 | // Insert correct new lines and remove redundant whitespace to keep POSIX-compliant 8 | "files.insertFinalNewline": true, 9 | "files.trimFinalNewlines": true, 10 | "files.trimTrailingWhitespace": true, 11 | 12 | // Exclude frequently updated/large files that we usually don't care about 13 | "files.watcherExclude": { 14 | // VSCode defaults 15 | "**/.git/objects/**": true, 16 | "**/.git/subtree-cache/**": true, 17 | "**/node_modules/**": true, 18 | "**/.hg/store/**": true, 19 | 20 | // custom 21 | "**/dist/**": true, 22 | "**/lib/**": true, 23 | "**/coverage/**": true 24 | }, 25 | 26 | // Treat .cleanmodules files as ignore files 27 | "files.associations": { 28 | ".cleanmodules*": "ignore", 29 | }, 30 | 31 | // Ensure we use the same formatter for files 32 | "[javascript]": { 33 | "editor.defaultFormatter": "esbenp.prettier-vscode" 34 | }, 35 | "[typescript]": { 36 | "editor.defaultFormatter": "esbenp.prettier-vscode" 37 | }, 38 | "[json]": { 39 | "editor.defaultFormatter": "esbenp.prettier-vscode" 40 | }, 41 | "[jsonc]": { 42 | "editor.defaultFormatter": "esbenp.prettier-vscode" 43 | }, 44 | "[markdown]": { 45 | "editor.defaultFormatter": "esbenp.prettier-vscode", 46 | "files.trimTrailingWhitespace": false // Markdown uses trailing whitespace for line breaks 47 | }, 48 | 49 | // Use same TypeScript version as repo rather than local user version 50 | "typescript.tsdk": "node_modules/typescript/lib", 51 | } 52 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # clean-modules 2 | 3 | ## 3.1.1 4 | 5 | ### Patch Changes 6 | 7 | - Respect `directory` option in `analyze` command. _[`529502e`](https://github.com/duniul/clean-modules/commit/529502e1de3da64b87dd40f631b0abd6f91879b7) [@duniul](https://github.com/duniul)_ 8 | 9 | ## 3.1.0 10 | 11 | ### Minor Changes 12 | 13 | - Add default patterns for GitHub Pages, JetBrains, airtap, husky, bmp, release-it, Windows, and GNU COPYING. _[`#35`](https://github.com/duniul/clean-modules/pull/35) [`89fcb28`](https://github.com/duniul/clean-modules/commit/89fcb2862ab07bbd52cded0288b3bf694a974238) [@sdavids](https://github.com/sdavids)_ 14 | 15 | ### Patch Changes 16 | 17 | - Include up to 2 fraction digits when reporting reduced size. _[`89dd555`](https://github.com/duniul/clean-modules/commit/89dd5554b44e8229ba8be7d6a2472d4c14bd9c02) [@duniul](https://github.com/duniul)_ 18 | 19 | ## 3.0.5 20 | 21 | ### Patch Changes 22 | 23 | - Add `adoc` to the AsciiDoc glob in `.cleanmodules-default`. _[`#32`](https://github.com/duniul/clean-modules/pull/32) [`e0b0239`](https://github.com/duniul/clean-modules/commit/e0b0239ea1fbddde9382ead635716a8d2253a41b) [@sdavids](https://github.com/sdavids)_ 24 | - Add glob for OSSMETADATA to `.cleanmodules-default`. _[`#30`](https://github.com/duniul/clean-modules/pull/30) [`cf70c0b`](https://github.com/duniul/clean-modules/commit/cf70c0b326320078a08d91a98a9f4216100477c4) [@sdavids](https://github.com/sdavids)_ 25 | - Add glob for new ESLint config files to `.cleanmodules-default`. _[`#28`](https://github.com/duniul/clean-modules/pull/28) [`680825a`](https://github.com/duniul/clean-modules/commit/680825ae8b554bc047627c51c032e01990f4b9f6) [@sdavids](https://github.com/sdavids)_ 26 | 27 | ## 3.0.4 28 | 29 | ### Patch Changes 30 | 31 | - Add `.stylelintrc*(.*)` to `.cleanmodules-default`. _[`#24`](https://github.com/duniul/clean-modules/pull/24) [`31fb7eb`](https://github.com/duniul/clean-modules/commit/31fb7eb5a2807a54888f60d1d704b1ef565d1d9d) [@SukkaW](https://github.com/SukkaW)_ 32 | 33 | ## 3.0.3 34 | 35 | ### Patch Changes 36 | 37 | - Fix `import.meta.url` not being transpiled to an equivalent value for CJS builds. _[`#22`](https://github.com/duniul/clean-modules/pull/22) [`efa65b5`](https://github.com/duniul/clean-modules/commit/efa65b5aa011053f57279eb9233c83331549e3f8) [@duniul](https://github.com/duniul)_ 38 | 39 | ## 3.0.2 40 | 41 | (includes 3.0.0 and 3.0.1, which were unpublished) 42 | 43 | ### Major Changes 44 | 45 | - **BREAKING** Replace `-i,--include` and `-e,--exclude` with globs passed as positional arguments. This makes them consistent with the glob file patterns. _[`de47cf2`](https://github.com/duniul/clean-modules/commit/de47cf22adb5a44507a0dde1caa763fd98c16eba) [@duniul](https://github.com/duniul)_ 46 | 47 | To migrate, move included and excluded globs to the end of the command, and prefix any exclusion globs with `!`. 48 | 49 | ```sh 50 | # before 51 | clean-modules --include "foo" "bar" --exclude "baz" "qux" 52 | 53 | # after 54 | clean-modules "foo" "bar" "!baz" "!qux" 55 | ``` 56 | 57 | - **BREAKING** Replace old programmatic API with one that better correspond to the CLI commands. See the README for information on how to import them. _[`6c8dfff`](https://github.com/duniul/clean-modules/commit/6c8dfff6f151d56a661a7759d36e35162889afad) [@duniul](https://github.com/duniul)_ 58 | - **BREAKING** Drop support for Node 12, require Node >= 14. _[`0ebf930`](https://github.com/duniul/clean-modules/commit/0ebf9307371cbd6d4a966139f020a8d1a9e0d0a1) [@duniul](https://github.com/duniul)_ 59 | 60 | ### Patch Changes 61 | 62 | - Replace `yargs` with `clipanion` for CLI parsing. _[`de47cf2`](https://github.com/duniul/clean-modules/commit/de47cf22adb5a44507a0dde1caa763fd98c16eba) [@duniul](https://github.com/duniul)_ 63 | - Don't remove `tsconfig.json` files by default, as they can be shared. _[`17603eb`](https://github.com/duniul/clean-modules/commit/17603ebcdd9d27d46abdce4805e8dfbe0deae3ae) [@duniul](https://github.com/duniul)_ 64 | - Update `pretty-bytes`, `pretty-ms` and `supports-color`. _[`e198b46`](https://github.com/duniul/clean-modules/commit/e198b468174afa9b72fe160a553a659bfe255bf0) [@duniul](https://github.com/duniul)_ 65 | - Remove `arg` as a runtime dependency. _[`95b0dcf`](https://github.com/duniul/clean-modules/commit/95b0dcf0693b6c14635497c866d717ae89820299) [@duniul](https://github.com/duniul)_ 66 | - Try to avoid test util files when cleaning up test files. _[`cf7ede5`](https://github.com/duniul/clean-modules/commit/cf7ede5037e865851afff1e3b22502e5fb165fca) [@duniul](https://github.com/duniul)_ 67 | - Fix issue where `--glob-file` could be passed as an array, causing a crash. _[`de47cf2`](https://github.com/duniul/clean-modules/commit/de47cf22adb5a44507a0dde1caa763fd98c16eba) [@duniul](https://github.com/duniul)_ 68 | 69 | ## 2.0.6 70 | 71 | ### Patch Changes 72 | 73 | - Reduce `console` instead of `console.log` _[`#16`](https://github.com/duniul/clean-modules/pull/16) [`a0077d5`](https://github.com/duniul/clean-modules/commit/a0077d579b17650b284c4a5bc4f452e66356a423) [@Miikis](https://github.com/Miikis)_ 74 | 75 | ## 2.0.5 76 | 77 | ### Patch Changes 78 | 79 | - Fix path issues on Windows. _[`#14`](https://github.com/duniul/clean-modules/pull/14) [`6a7fb5c`](https://github.com/duniul/clean-modules/commit/6a7fb5c015d9f1869fa8b016d63c8cd390a5e2a1) [@duniul](https://github.com/duniul)_ 80 | 81 | ## 2.0.4 82 | 83 | ### Patch Changes 84 | 85 | - Remove custom help and version options in favor of yargs builtins. _[`#10`](https://github.com/duniul/clean-modules/pull/10) [`0f5fa14`](https://github.com/duniul/clean-modules/commit/0f5fa148b81a6bca1650430596ebc34779a4b126) [@duniul](https://github.com/duniul)_ 86 | 87 | ## 2.0.3 88 | 89 | ### Patch Changes 90 | 91 | - Wrap globs with parantheses to prevent issues with scoped packages. _[`31b35c4`](https://github.com/duniul/clean-modules/commit/31b35c4e7aa2bc7ffc76300bb9177c43f794940a) [@duniul](https://github.com/duniul)_ 92 | - Improve globs for files with optional file extensions. _[`a71a8e4`](https://github.com/duniul/clean-modules/commit/a71a8e4ca29a35e74806267e379a85c2e5764721) [@duniul](https://github.com/duniul)_ 93 | - Bump dependencies _[`735bf95`](https://github.com/duniul/clean-modules/commit/735bf9586bac7fab59f01170bf192090de274903) [@duniul](https://github.com/duniul)_ 94 | - Make files included by dir globs excludable. _[`6d4eceb`](https://github.com/duniul/clean-modules/commit/6d4ecebe33034be2a2997ebb93d8c1cb012f363a) [@duniul](https://github.com/duniul)_ 95 | - Change `--directory` to expect a string. _[`#7`](https://github.com/duniul/clean-modules/pull/7) [`8e77f83`](https://github.com/duniul/clean-modules/commit/8e77f830c5b523d47906f87c8e68a988e55f5cdf) [@ImedAdel](https://github.com/ImedAdel)_ 96 | 97 | ## 2.0.2 98 | 99 | ### Patch Changes 100 | 101 | - Use existing `.cleanmodules` file by default. _[`403045d`](https://github.com/duniul/clean-modules/commit/403045d275c36f2c27f13646fdb45ed53902b01a) [@duniul](https://github.com/duniul)_ 102 | 103 | ## 2.0.1 104 | 105 | ### Patch Changes 106 | 107 | - Include `.cleanmodules-default` file. _[`61decf7`](https://github.com/duniul/clean-modules/commit/61decf7fd9b635f35daba10a58e7464c4db26b4a) [@duniul](https://github.com/duniul)_ 108 | 109 | # 2.0.0 110 | 111 | ### BREAKING CHANGES 112 | 113 | - Node versions lower than 12 are no longer supported. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`d6c66f2`](https://github.com/duniul/clean-modules/commit/d6c66f2ab75ec03a573b848c396d74316fc085d6) [@duniul](https://github.com/duniul)_ 114 | - A path to `node_modules` should now be supplied to the `--directory` option instead of a positional. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ 115 | - The `--analyze` option has been replaced by a separate command. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ 116 | 117 | ### Minor Changes 118 | 119 | - Add glob file support. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`43832f0`](https://github.com/duniul/clean-modules/commit/43832f08582ef55f33c7ee481c949a267a8f8a1d) [@duniul](https://github.com/duniul)_ 120 | 121 | ## 1.0.4 122 | 123 | ### Patch Changes 124 | 125 | - Bump dependencies. _[`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ 126 | - Don't fail on invalid file paths. _[`#2`](https://github.com/duniul/clean-modules/pull/2) [`541ba1b`](https://github.com/duniul/clean-modules/commit/541ba1b3ca033b90df414fdcf6cea5f655daf3ae) [@duniul](https://github.com/duniul)_ 127 | 128 | ## 1.0.3 129 | 130 | ### Patch Changes 131 | 132 | - Restrict license glob. _[`17bc9c0`](https://github.com/duniul/clean-modules/commit/17bc9c029f8197a7cb4514fd11eef32023855243) [@duniul](https://github.com/duniul)_ 133 | 134 | ## 1.0.2 135 | 136 | ### Patch Changes 137 | 138 | - Add correct bin link. _[`75283b3`](https://github.com/duniul/clean-modules/commit/75283b3b0e5a42597e90209f60f85e83fc7429d7) [@duniul](https://github.com/duniul)_ 139 | 140 | ## 1.0.1 141 | 142 | ### Patch Changes 143 | 144 | - Set `engines` to Node >=10. _[`75de40e`](https://github.com/duniul/clean-modules/commit/75de40eca44847cefb269b2b36ce2f36b27a93ca) [@duniul](https://github.com/duniul)_ 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2020, Daniel Grefberg 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clean-modules 🧹 2 | 3 | 4 | 5 | 6 | 7 | 8 | > Clean up/prune unnecessary files and reduce the size of your `node_modules` directory. Useful for 9 | > CI caches or for reducing the size of serverless functions. 10 | 11 | - 🧹 Removes directories and files that are unnecessary and **safe to remove in production** 12 | - 🛠 Easily customizable through glob patterns (either through [CLI args](#positionals) or a 13 | [configuration file](#glob-patterns-and-configuration-file)) 14 | - 📁 Cleans up empty directories after removing files 15 | - ⚡️ Super fast, but still written in Node 16 | - 🔍 Allows analyzing results, like which pattern excluded which file 17 | - 🧑‍💻 Supports both CLI and programmatic usage 18 | 19 |

20 | Small project 21 | Large project 22 | Dry run 23 |

24 | 25 | ## Table of contents 26 | 27 |
Click to open 28 | 29 | - [Quick start](#quick-start) 30 | - [Installation](#installation) 31 | - [Commands](#commands) 32 | - [`clean-modules clean` (default command) 🧹](#clean-modules-clean-default-command-) 33 | - [Usage](#usage) 34 | - [Positionals](#positionals) 35 | - [Options](#options) 36 | - [`--directory | -D`](#--directory---d) 37 | - [`--glob-file | -f`](#--glob-file---f) 38 | - [`--no-defaults | -n`](#--no-defaults---n) 39 | - [`--keep-empty | -k`](#--keep-empty---k) 40 | - [`--dry-run | -d`](#--dry-run---d) 41 | - [`--json | -j`](#--json---j) 42 | - [`--yes | -y`](#--yes---y) 43 | - [`clean-modules analyze` 🔎](#clean-modules-analyze-) 44 | - [Usage](#usage-1) 45 | - [Positionals](#positionals-1) 46 | - [Options](#options-1) 47 | - [Example output](#example-output) 48 | - [Glob patterns and configuration file](#glob-patterns-and-configuration-file) 49 | - [Example configuration file](#example-configuration-file) 50 | - [Default globs](#default-globs) 51 | - [Common extra inclusions](#common-extra-inclusions) 52 | - [Common extra exclusions](#common-extra-exclusions) 53 | - [Programmatic usage](#programmatic-usage) 54 | - [Alternatives](#alternatives) 55 | - [Comparisons](#comparisons) 56 | - [clean-modules (this project)](#clean-modules-this-project) 57 | - [yarn autoclean](#yarn-autoclean) 58 | - [modclean](#modclean) 59 | - [node-prune](#node-prune) 60 | - [nm-prune](#nm-prune) 61 | 62 |
63 | 64 | ## Quick start 65 | 66 | Simply run the command in the directory where your `node_modules` are: 67 | 68 | ```bash 69 | clean-modules 70 | ``` 71 | 72 | You can also pass any options that you need, like custom globs or a path to a specific 73 | `node_modules` directory. 74 | 75 | ```bash 76 | clean-modules --directory "path/to/node_modules" "extra/included/file/glob.txt" "!extra/excluded/glob.ts 77 | ``` 78 | 79 | Check out the [command](#clean-modules-clean-default-command-🧹) section for all available options. 80 | 81 | ## Installation 82 | 83 | `clean-modules` can be installed globally if you only want to use it as a CLI tool. You can also 84 | install it locally if you want to use it in a package command. 85 | 86 | ```bash 87 | # global 88 | npm install --global clean-modules 89 | 90 | # dev dependency 91 | npm install --save-dev clean-modules 92 | ``` 93 | 94 | ## Commands 95 | 96 | ### `clean-modules clean` (default command) 🧹 97 | 98 | The default command, cleans up your `node_modules` based on a set of _most likely_ safe glob 99 | patterns and removes empty directories. 100 | 101 | #### Usage 102 | 103 | ```bash 104 | clean-modules [options] <...globs> 105 | ``` 106 | 107 | #### Positionals 108 | 109 | Extra glob patterns can be passed as positional arguments. By default they are combined with the globs loaded from the the default globs file and any custom globs file passed through the [`--glob-file`](#glob-file---f) option. 110 | 111 | For information on how the globs are parsed, see the [Glob patterns](#glob-patterns-and-configuration-file) section. 112 | 113 | **Example**: 114 | 115 | ```bash 116 | # include all TypeScript declaration files and @types folders 117 | clean-modules "**/*.d.ts" "**/@types/**" 118 | 119 | # exclude all sourcemap files and PNG files 120 | clean-modules "!**/*.map.js" "!**/*.png" 121 | 122 | # include .d.ts files but exclude PNG files 123 | clean-modules "**/*.d.ts" "!**/*.png" 124 | ``` 125 | 126 | #### Options 127 | 128 | ##### `--directory | -D` 129 | 130 | `string` [default: `./node_modules`] 131 | 132 | Accepts a path to a directory to run the script on. 133 | 134 | **Example**: 135 | 136 | ```bash 137 | clean-modules --directory "path/to/custom/node_modules" 138 | ``` 139 | 140 | ##### `--glob-file | -f` 141 | 142 | `string` [default: `./.cleanmodules`] 143 | 144 | Accepts a path to a file from which clean-modules should read any custom globs. See the 145 | [glob patterns](#glob-patterns-and-configuration-file) section for information about how the glob 146 | file works and what patterns work. 147 | 148 | ##### `--no-defaults | -n` 149 | 150 | `boolean` 151 | 152 | Excludes all default globs, using only custom globs added through the 153 | [glob file](#glob-patterns-and-configuration-file) or by the include or exclude options. Useful if 154 | you want complete control over what files to include. 155 | 156 | See the [`.cleanmodules-default`](./cleanmodules-default) to see what patterns are included by 157 | default by default. 158 | 159 | ##### `--keep-empty | -k` 160 | 161 | `boolean` 162 | 163 | Skips removing empty folders after removing files. 164 | 165 | ##### `--dry-run | -d` 166 | 167 | `boolean` 168 | 169 | Runs the script and prints the result without actually removing any files. Does not count the number 170 | of removed empty directories. 171 | 172 | ##### `--json | -j` 173 | 174 | `boolean` 175 | 176 | Only logs a final JSON dump at the end of the script (useful for logs or services). 177 | 178 | ##### `--yes | -y` 179 | 180 | `boolean` 181 | 182 | Skips the confirmation prompt at the start of the script. 183 | 184 | ### `clean-modules analyze` 🔎 185 | 186 | Compiles a list of all files that would be included by `clean-modules` and gives a breakdown of: 187 | 188 | - exact file path 189 | - what glob patterns it was included by 190 | - how the patterns were formatted and passed along to `picomatch` 191 | - if the file was included by default 192 | 193 | #### Usage 194 | 195 | ```bash 196 | clean-modules analyze [options] 197 | ``` 198 | 199 | Because of the amount of data it can be useful to pipe it somewhere, like: 200 | 201 | ```bash 202 | clean-modules analyze >> clean-modules-result.json 203 | ``` 204 | 205 | #### Positionals 206 | 207 | The `analyze` command accepts the same type of positionals as the [default command](). 208 | 209 | #### Options 210 | 211 | The `analyze` command accepts several of the default command's options and applies them in the same 212 | way: 213 | 214 | - [`--directory`](#directory---d) 215 | - [`--glob-file`](#glob-file---f) 216 | - [`--no-defaults`](#no-defaults---n) 217 | 218 | #### Example output 219 | 220 | ```json 221 | [ 222 | { 223 | "filePath": "/Users/me/projects/foo/node_modules/dependency/__tests__/test1.js", 224 | "includedByDefault": true, 225 | "includedByGlobs": [ 226 | { 227 | "original": "__tests__/", 228 | "derived": "/Users/me/projects/foo/node_modules/dependency/**/__tests__/**" 229 | } 230 | ] 231 | } 232 | // ... 233 | ] 234 | ``` 235 | 236 | ## Glob patterns and configuration file 237 | 238 | clean-modules accepts globs from either a configuration file (`.cleanmodules` next to 239 | `node_modules` by default) or CLI arguments. It uses 240 | [`.gitignore`-like](https://www.atlassian.com/git/tutorials/saving-changes/gitignore) glob patterns 241 | that are converted to valid [`picomatch`](https://github.com/micromatch/picomatch) globs, which is 242 | what is used to match file paths under the hood. 243 | 244 | **Differences from regular gitignore syntax:** 245 | 246 | - To include a directory the pattern _must_ end with `/`, `/*` or `/**` 247 | - This is to prevent directories matching common file names from being included by the globs. 248 | - Casing is ignored. 249 | 250 | **Like with .gitignore, globs should use forward-slashes as separators on all operative systems (including Windows)!** 251 | 252 | ### Example configuration file 253 | 254 | ```ignore 255 | # this is a comment (starts with a #) 256 | 257 | # to include include directories, end patterns with / or /* or /** 258 | dep1/ 259 | dep1/* 260 | dep2/** 261 | 262 | # files are matched in any directory by default 263 | **/*.test.js 264 | # is the same as 265 | *.test.js 266 | 267 | # use a leading / to include a file or directory at a specific place 268 | /dep4/this/specific/directory/** 269 | /dep4/this/specific/file.js 270 | 271 | # to exclude a path, prepend it with a ! 272 | !/not/this/directory/ 273 | !not-me.js 274 | 275 | # to use leading exclamation marks without excluding, escape them 276 | \!(*.d).ts 277 | ``` 278 | 279 | ### Default globs 280 | 281 | The default globs can be found in the [`.cleanmodules-default`](./.cleanmodules-default) file. It 282 | only contains inclusions (as exclusions would override custom inclusions) and consists of a large 283 | list of the most common files that are safe to remove. 284 | 285 | That said, it's impossible to guarantee that none of the files are needed, and you might need to do 286 | custom exclusions depending on what packages you use. 287 | 288 | #### Common extra inclusions 289 | 290 | - `**/*.d.ts`: **If you don't use TypeScript.** TypeScript declaration files take up a lot of space 291 | in your `node_modules` folder, but they are most likely required to build your application. Useful 292 | locally even if you don't use TypeScript since they can be parsed by your IDE. 293 | 294 | #### Common extra exclusions 295 | 296 | - `!**/*.map.js`: **If you are running `clean-modules` locally or need source files in production.** 297 | `clean-modules` removes sourcemap files by default since they take up a lot of space and does not 298 | break builds when removed. They can be nice to have though, especially while developing. 299 | 300 | ## Programmatic usage 301 | 302 | Clean modules can be used programmatically too! 303 | 304 | Simply import the corresponding function from the package: 305 | 306 | ````ts 307 | import { clean, analyze } from 'clean-modules'; 308 | 309 | // analyze, all options are optional 310 | const analyzeResult = await analyze({ 311 | directory: '/path/to/node_modules', 312 | globFile: '/path/to/.cleanmodules', 313 | globs: ['**/*.js'], 314 | noDefaults: false, 315 | }); 316 | 317 | // clean, all options are optional 318 | const cleanResult = await clean({ 319 | directory: '/path/to/node_modules', 320 | globFile: '/path/to/.cleanmodules', 321 | globs: ['**/*.js'], 322 | noDefaults: false, 323 | keepEmpty: false, 324 | dryRun: false, 325 | }); 326 | ```` 327 | 328 | ## Alternatives 329 | 330 | The most common issues I found with available tools are: 331 | 332 | - They only allow inclusion/exclusion of file names, not file paths. This prevents you from e.g. 333 | excluding subdirectories of a specific folder, like `@types/react-native`. 334 | - They include too many, or too few, patterns by default. Lots of them also include patterns like 335 | `*.ts` by default, which breaks TypeScript declaration files on build. 336 | - They are too slow (only relevant when running on large projects) 337 | - They are abandoned or don't respond to issues. 338 | 339 | ### Comparisons 340 | 341 | #### clean-modules (this project) 342 | 343 | - ✅ Inclusion/exclusion through file path globs 344 | - ✅ Fast 345 | - ✅ Safe list of files and folders included by default (for production use) 346 | - ✅ Cleans up empty directories 347 | 348 | #### [yarn autoclean](https://classic.yarnpkg.com/en/docs/cli/autoclean/) 349 | 350 | - ✅ Included with Yarn by default 351 | - ✅ Inclusion/exclusion through globs 352 | - ⛔️ Very slow compared to alternatives 353 | - ⛔️ Runs on every install, both locally and in CI 354 | - ⛔️ Small list of files included by default 355 | 356 | #### [modclean](https://github.com/ModClean/modclean) 357 | 358 | - ✅ Cleans up empty directories 359 | - ✅ Safe list of files and folders included by default 360 | - ⛔️ Slow, only slightly faster than `yarn clean` 361 | - ⛔️ Only allows inclusion/exclusion by file name globs, not file path 362 | - ⛔️ Complains about empty folders that doesn't exist 363 | - ⛔️ Abandoned 364 | 365 | #### [node-prune](https://github.com/tj/node-prune) 366 | 367 | - ✅ Fastest option available 368 | - ⛔️ Written in Go and might require separate binary download 369 | - ⛔️ Removes some dangerous files by default (like `.d.ts` files and `assets` folder) 370 | - ⛔️ Only allows inclusion/exclusion by file name globs, not file path 371 | - ⛔️ Globs are very limited since it uses Go's `filepath.Match` 372 | - ⛔️ Does not remove empty folders 373 | 374 | #### [nm-prune](https://github.com/tuananh/node-prune) 375 | 376 | - ✅ Fast 377 | - ⛔️ Removes some dangerous files by default (like `.d.ts` files and `assets` folder) 378 | - ⛔️ Only allows inclusion/exclusion by file name, not file paths or globs 379 | - ⛔️ Does not remove empty folders 380 | - ⛔️ Small list of files included by default 381 | -------------------------------------------------------------------------------- /__mocks__/fs/index.ts: -------------------------------------------------------------------------------- 1 | import { fs } from 'memfs'; 2 | 3 | module.exports = fs; 4 | -------------------------------------------------------------------------------- /__mocks__/fs/promises.ts: -------------------------------------------------------------------------------- 1 | import { fs } from 'memfs'; 2 | 3 | module.exports = fs 4 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | // requires built files to avoid resetting file permissions between builds 4 | import '../dist/cli/cli.js'; 5 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "formatter": { 4 | "enabled": false 5 | }, 6 | "organizeImports": { 7 | "enabled": false 8 | }, 9 | "linter": { 10 | "enabled": true, 11 | "rules": { 12 | "suspicious": { 13 | "noExplicitAny": "off" 14 | }, 15 | "style": { 16 | "useTemplate": "off", 17 | "noUnusedTemplateLiteral": "off" 18 | } 19 | } 20 | }, 21 | "files": { 22 | "ignore": ["**/*.json"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /images/dry-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duniul/clean-modules/cb2bb80e188510db7b06d880137ecce346ecf889/images/dry-run.png -------------------------------------------------------------------------------- /images/large-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duniul/clean-modules/cb2bb80e188510db7b06d880137ecce346ecf889/images/large-project.png -------------------------------------------------------------------------------- /images/small-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duniul/clean-modules/cb2bb80e188510db7b06d880137ecce346ecf889/images/small-project.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clean-modules", 3 | "author": "Daniel Grefberg ", 4 | "version": "3.1.1", 5 | "description": "Clean up/prune unnecessary files and reduce the size of your `node_modules` directory. Useful for CI caches or for reducing the size of serverless functions.", 6 | "repository": "https://github.com/duniul/clean-modules.git", 7 | "homepage": "https://github.com/duniul/clean-modules#readme", 8 | "bugs": { 9 | "url": "https://github.com/duniul/clean-modules/issues" 10 | }, 11 | "keywords": [ 12 | "node", 13 | "node-modules", 14 | "npm", 15 | "yarn", 16 | "pnpm", 17 | "cli-tool", 18 | "ci" 19 | ], 20 | "license": "ISC", 21 | "engines": { 22 | "node": ">=14" 23 | }, 24 | "files": [ 25 | "dist", 26 | ".cleanmodules-default", 27 | "!.tsbuildinfo" 28 | ], 29 | "packageManager": "pnpm@9.11.0", 30 | "type": "module", 31 | "exports": { 32 | ".": { 33 | "import": { 34 | "types": "./dist/index.d.ts", 35 | "default": "./dist/index.js" 36 | }, 37 | "require": { 38 | "types": "./dist/index.d.cts", 39 | "default": "./dist/index.cjs" 40 | } 41 | } 42 | }, 43 | "main": "dist/index.cjs", 44 | "module": "dist/index.js", 45 | "types": "dist/index.d.ts", 46 | "bin": "bin/cli.js", 47 | "scripts": { 48 | "cli": "tsx src/cli/cli.ts", 49 | "build": "tsup", 50 | "dev": "tsc --watch", 51 | "test": "vitest", 52 | "prepublishOnly": "pnpm build", 53 | "lint": "biome check src && prettier --check src", 54 | "lint:fix": "biome check --apply src && prettier --write src", 55 | "version": "changeset version", 56 | "release": "changeset publish" 57 | }, 58 | "dependencies": { 59 | "clipanion": "^3.2.1", 60 | "picomatch": "^2.3.0", 61 | "pretty-bytes": "^6.1.0", 62 | "pretty-ms": "^8.0.0", 63 | "supports-color": "^9.4.0" 64 | }, 65 | "devDependencies": { 66 | "@biomejs/biome": "^1.9.2", 67 | "@changesets/cli": "^2.27.8", 68 | "@tsconfig/node14": "^14.1.2", 69 | "@types/mock-fs": "4.13.4", 70 | "@types/node": "16", 71 | "@types/picomatch": "2.3.4", 72 | "@types/supports-color": "8.1.3", 73 | "changesets-changelog-clean": "^1.3.0", 74 | "memfs": "^4.12.0", 75 | "prettier": "3.3.3", 76 | "prettier-plugin-organize-imports": "4.1.0", 77 | "tslib": "^2.7.0", 78 | "tsup": "^8.3.0", 79 | "tsx": "^4.19.1", 80 | "typescript": "5.6.2", 81 | "vitest": "^2.1.1" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | clipanion: 12 | specifier: ^3.2.1 13 | version: 3.2.1(typanion@3.14.0) 14 | picomatch: 15 | specifier: ^2.3.0 16 | version: 2.3.1 17 | pretty-bytes: 18 | specifier: ^6.1.0 19 | version: 6.1.1 20 | pretty-ms: 21 | specifier: ^8.0.0 22 | version: 8.0.0 23 | supports-color: 24 | specifier: ^9.4.0 25 | version: 9.4.0 26 | devDependencies: 27 | '@biomejs/biome': 28 | specifier: ^1.9.2 29 | version: 1.9.2 30 | '@changesets/cli': 31 | specifier: ^2.27.8 32 | version: 2.27.8 33 | '@tsconfig/node14': 34 | specifier: ^14.1.2 35 | version: 14.1.2 36 | '@types/mock-fs': 37 | specifier: 4.13.4 38 | version: 4.13.4 39 | '@types/node': 40 | specifier: '16' 41 | version: 16.18.82 42 | '@types/picomatch': 43 | specifier: 2.3.4 44 | version: 2.3.4 45 | '@types/supports-color': 46 | specifier: 8.1.3 47 | version: 8.1.3 48 | changesets-changelog-clean: 49 | specifier: ^1.3.0 50 | version: 1.3.0 51 | memfs: 52 | specifier: ^4.12.0 53 | version: 4.12.0 54 | prettier: 55 | specifier: 3.3.3 56 | version: 3.3.3 57 | prettier-plugin-organize-imports: 58 | specifier: 4.1.0 59 | version: 4.1.0(prettier@3.3.3)(typescript@5.6.2) 60 | tslib: 61 | specifier: ^2.7.0 62 | version: 2.7.0 63 | tsup: 64 | specifier: ^8.3.0 65 | version: 8.3.0(postcss@8.4.35)(supports-color@9.4.0)(tsx@4.19.1)(typescript@5.6.2) 66 | tsx: 67 | specifier: ^4.19.1 68 | version: 4.19.1 69 | typescript: 70 | specifier: 5.6.2 71 | version: 5.6.2 72 | vitest: 73 | specifier: ^2.1.1 74 | version: 2.1.1(@types/node@16.18.82)(supports-color@9.4.0) 75 | 76 | packages: 77 | 78 | '@babel/runtime@7.23.9': 79 | resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} 80 | engines: {node: '>=6.9.0'} 81 | 82 | '@biomejs/biome@1.9.2': 83 | resolution: {integrity: sha512-4j2Gfwft8Jqp1X0qLYvK4TEy4xhTo4o6rlvJPsjPeEame8gsmbGQfOPBkw7ur+7/Z/f0HZmCZKqbMvR7vTXQYQ==} 84 | engines: {node: '>=14.21.3'} 85 | hasBin: true 86 | 87 | '@biomejs/cli-darwin-arm64@1.9.2': 88 | resolution: {integrity: sha512-rbs9uJHFmhqB3Td0Ro+1wmeZOHhAPTL3WHr8NtaVczUmDhXkRDWScaxicG9+vhSLj1iLrW47itiK6xiIJy6vaA==} 89 | engines: {node: '>=14.21.3'} 90 | cpu: [arm64] 91 | os: [darwin] 92 | 93 | '@biomejs/cli-darwin-x64@1.9.2': 94 | resolution: {integrity: sha512-BlfULKijNaMigQ9GH9fqJVt+3JTDOSiZeWOQtG/1S1sa8Lp046JHG3wRJVOvekTPL9q/CNFW1NVG8J0JN+L1OA==} 95 | engines: {node: '>=14.21.3'} 96 | cpu: [x64] 97 | os: [darwin] 98 | 99 | '@biomejs/cli-linux-arm64-musl@1.9.2': 100 | resolution: {integrity: sha512-ZATvbUWhNxegSALUnCKWqetTZqrK72r2RsFD19OK5jXDj/7o1hzI1KzDNG78LloZxftrwr3uI9SqCLh06shSZw==} 101 | engines: {node: '>=14.21.3'} 102 | cpu: [arm64] 103 | os: [linux] 104 | 105 | '@biomejs/cli-linux-arm64@1.9.2': 106 | resolution: {integrity: sha512-T8TJuSxuBDeQCQzxZu2o3OU4eyLumTofhCxxFd3+aH2AEWVMnH7Z/c3QP1lHI5RRMBP9xIJeMORqDQ5j+gVZzw==} 107 | engines: {node: '>=14.21.3'} 108 | cpu: [arm64] 109 | os: [linux] 110 | 111 | '@biomejs/cli-linux-x64-musl@1.9.2': 112 | resolution: {integrity: sha512-CjPM6jT1miV5pry9C7qv8YJk0FIZvZd86QRD3atvDgfgeh9WQU0k2Aoo0xUcPdTnoz0WNwRtDicHxwik63MmSg==} 113 | engines: {node: '>=14.21.3'} 114 | cpu: [x64] 115 | os: [linux] 116 | 117 | '@biomejs/cli-linux-x64@1.9.2': 118 | resolution: {integrity: sha512-T0cPk3C3Jr2pVlsuQVTBqk2qPjTm8cYcTD9p/wmR9MeVqui1C/xTVfOIwd3miRODFMrJaVQ8MYSXnVIhV9jTjg==} 119 | engines: {node: '>=14.21.3'} 120 | cpu: [x64] 121 | os: [linux] 122 | 123 | '@biomejs/cli-win32-arm64@1.9.2': 124 | resolution: {integrity: sha512-2x7gSty75bNIeD23ZRPXyox6Z/V0M71ObeJtvQBhi1fgrvPdtkEuw7/0wEHg6buNCubzOFuN9WYJm6FKoUHfhg==} 125 | engines: {node: '>=14.21.3'} 126 | cpu: [arm64] 127 | os: [win32] 128 | 129 | '@biomejs/cli-win32-x64@1.9.2': 130 | resolution: {integrity: sha512-JC3XvdYcjmu1FmAehVwVV0SebLpeNTnO2ZaMdGCSOdS7f8O9Fq14T2P1gTG1Q29Q8Dt1S03hh0IdVpIZykOL8g==} 131 | engines: {node: '>=14.21.3'} 132 | cpu: [x64] 133 | os: [win32] 134 | 135 | '@changesets/apply-release-plan@7.0.5': 136 | resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} 137 | 138 | '@changesets/assemble-release-plan@6.0.4': 139 | resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} 140 | 141 | '@changesets/changelog-git@0.2.0': 142 | resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} 143 | 144 | '@changesets/cli@2.27.8': 145 | resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} 146 | hasBin: true 147 | 148 | '@changesets/config@3.0.3': 149 | resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} 150 | 151 | '@changesets/errors@0.2.0': 152 | resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} 153 | 154 | '@changesets/get-dependents-graph@2.1.2': 155 | resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} 156 | 157 | '@changesets/get-github-info@0.6.0': 158 | resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} 159 | 160 | '@changesets/get-release-plan@4.0.4': 161 | resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} 162 | 163 | '@changesets/get-version-range-type@0.4.0': 164 | resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} 165 | 166 | '@changesets/git@3.0.1': 167 | resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} 168 | 169 | '@changesets/logger@0.1.1': 170 | resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} 171 | 172 | '@changesets/parse@0.4.0': 173 | resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} 174 | 175 | '@changesets/pre@2.0.1': 176 | resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} 177 | 178 | '@changesets/read@0.6.1': 179 | resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} 180 | 181 | '@changesets/should-skip-package@0.1.1': 182 | resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} 183 | 184 | '@changesets/types@4.1.0': 185 | resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} 186 | 187 | '@changesets/types@6.0.0': 188 | resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} 189 | 190 | '@changesets/write@0.3.2': 191 | resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} 192 | 193 | '@esbuild/aix-ppc64@0.19.12': 194 | resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} 195 | engines: {node: '>=12'} 196 | cpu: [ppc64] 197 | os: [aix] 198 | 199 | '@esbuild/aix-ppc64@0.23.1': 200 | resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} 201 | engines: {node: '>=18'} 202 | cpu: [ppc64] 203 | os: [aix] 204 | 205 | '@esbuild/android-arm64@0.19.12': 206 | resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} 207 | engines: {node: '>=12'} 208 | cpu: [arm64] 209 | os: [android] 210 | 211 | '@esbuild/android-arm64@0.23.1': 212 | resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} 213 | engines: {node: '>=18'} 214 | cpu: [arm64] 215 | os: [android] 216 | 217 | '@esbuild/android-arm@0.19.12': 218 | resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} 219 | engines: {node: '>=12'} 220 | cpu: [arm] 221 | os: [android] 222 | 223 | '@esbuild/android-arm@0.23.1': 224 | resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} 225 | engines: {node: '>=18'} 226 | cpu: [arm] 227 | os: [android] 228 | 229 | '@esbuild/android-x64@0.19.12': 230 | resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} 231 | engines: {node: '>=12'} 232 | cpu: [x64] 233 | os: [android] 234 | 235 | '@esbuild/android-x64@0.23.1': 236 | resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} 237 | engines: {node: '>=18'} 238 | cpu: [x64] 239 | os: [android] 240 | 241 | '@esbuild/darwin-arm64@0.19.12': 242 | resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} 243 | engines: {node: '>=12'} 244 | cpu: [arm64] 245 | os: [darwin] 246 | 247 | '@esbuild/darwin-arm64@0.23.1': 248 | resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} 249 | engines: {node: '>=18'} 250 | cpu: [arm64] 251 | os: [darwin] 252 | 253 | '@esbuild/darwin-x64@0.19.12': 254 | resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} 255 | engines: {node: '>=12'} 256 | cpu: [x64] 257 | os: [darwin] 258 | 259 | '@esbuild/darwin-x64@0.23.1': 260 | resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} 261 | engines: {node: '>=18'} 262 | cpu: [x64] 263 | os: [darwin] 264 | 265 | '@esbuild/freebsd-arm64@0.19.12': 266 | resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} 267 | engines: {node: '>=12'} 268 | cpu: [arm64] 269 | os: [freebsd] 270 | 271 | '@esbuild/freebsd-arm64@0.23.1': 272 | resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} 273 | engines: {node: '>=18'} 274 | cpu: [arm64] 275 | os: [freebsd] 276 | 277 | '@esbuild/freebsd-x64@0.19.12': 278 | resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} 279 | engines: {node: '>=12'} 280 | cpu: [x64] 281 | os: [freebsd] 282 | 283 | '@esbuild/freebsd-x64@0.23.1': 284 | resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} 285 | engines: {node: '>=18'} 286 | cpu: [x64] 287 | os: [freebsd] 288 | 289 | '@esbuild/linux-arm64@0.19.12': 290 | resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} 291 | engines: {node: '>=12'} 292 | cpu: [arm64] 293 | os: [linux] 294 | 295 | '@esbuild/linux-arm64@0.23.1': 296 | resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} 297 | engines: {node: '>=18'} 298 | cpu: [arm64] 299 | os: [linux] 300 | 301 | '@esbuild/linux-arm@0.19.12': 302 | resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} 303 | engines: {node: '>=12'} 304 | cpu: [arm] 305 | os: [linux] 306 | 307 | '@esbuild/linux-arm@0.23.1': 308 | resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} 309 | engines: {node: '>=18'} 310 | cpu: [arm] 311 | os: [linux] 312 | 313 | '@esbuild/linux-ia32@0.19.12': 314 | resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} 315 | engines: {node: '>=12'} 316 | cpu: [ia32] 317 | os: [linux] 318 | 319 | '@esbuild/linux-ia32@0.23.1': 320 | resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} 321 | engines: {node: '>=18'} 322 | cpu: [ia32] 323 | os: [linux] 324 | 325 | '@esbuild/linux-loong64@0.19.12': 326 | resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} 327 | engines: {node: '>=12'} 328 | cpu: [loong64] 329 | os: [linux] 330 | 331 | '@esbuild/linux-loong64@0.23.1': 332 | resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} 333 | engines: {node: '>=18'} 334 | cpu: [loong64] 335 | os: [linux] 336 | 337 | '@esbuild/linux-mips64el@0.19.12': 338 | resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} 339 | engines: {node: '>=12'} 340 | cpu: [mips64el] 341 | os: [linux] 342 | 343 | '@esbuild/linux-mips64el@0.23.1': 344 | resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} 345 | engines: {node: '>=18'} 346 | cpu: [mips64el] 347 | os: [linux] 348 | 349 | '@esbuild/linux-ppc64@0.19.12': 350 | resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} 351 | engines: {node: '>=12'} 352 | cpu: [ppc64] 353 | os: [linux] 354 | 355 | '@esbuild/linux-ppc64@0.23.1': 356 | resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} 357 | engines: {node: '>=18'} 358 | cpu: [ppc64] 359 | os: [linux] 360 | 361 | '@esbuild/linux-riscv64@0.19.12': 362 | resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} 363 | engines: {node: '>=12'} 364 | cpu: [riscv64] 365 | os: [linux] 366 | 367 | '@esbuild/linux-riscv64@0.23.1': 368 | resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} 369 | engines: {node: '>=18'} 370 | cpu: [riscv64] 371 | os: [linux] 372 | 373 | '@esbuild/linux-s390x@0.19.12': 374 | resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} 375 | engines: {node: '>=12'} 376 | cpu: [s390x] 377 | os: [linux] 378 | 379 | '@esbuild/linux-s390x@0.23.1': 380 | resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} 381 | engines: {node: '>=18'} 382 | cpu: [s390x] 383 | os: [linux] 384 | 385 | '@esbuild/linux-x64@0.19.12': 386 | resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} 387 | engines: {node: '>=12'} 388 | cpu: [x64] 389 | os: [linux] 390 | 391 | '@esbuild/linux-x64@0.23.1': 392 | resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} 393 | engines: {node: '>=18'} 394 | cpu: [x64] 395 | os: [linux] 396 | 397 | '@esbuild/netbsd-x64@0.19.12': 398 | resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} 399 | engines: {node: '>=12'} 400 | cpu: [x64] 401 | os: [netbsd] 402 | 403 | '@esbuild/netbsd-x64@0.23.1': 404 | resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} 405 | engines: {node: '>=18'} 406 | cpu: [x64] 407 | os: [netbsd] 408 | 409 | '@esbuild/openbsd-arm64@0.23.1': 410 | resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} 411 | engines: {node: '>=18'} 412 | cpu: [arm64] 413 | os: [openbsd] 414 | 415 | '@esbuild/openbsd-x64@0.19.12': 416 | resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} 417 | engines: {node: '>=12'} 418 | cpu: [x64] 419 | os: [openbsd] 420 | 421 | '@esbuild/openbsd-x64@0.23.1': 422 | resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} 423 | engines: {node: '>=18'} 424 | cpu: [x64] 425 | os: [openbsd] 426 | 427 | '@esbuild/sunos-x64@0.19.12': 428 | resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} 429 | engines: {node: '>=12'} 430 | cpu: [x64] 431 | os: [sunos] 432 | 433 | '@esbuild/sunos-x64@0.23.1': 434 | resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} 435 | engines: {node: '>=18'} 436 | cpu: [x64] 437 | os: [sunos] 438 | 439 | '@esbuild/win32-arm64@0.19.12': 440 | resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} 441 | engines: {node: '>=12'} 442 | cpu: [arm64] 443 | os: [win32] 444 | 445 | '@esbuild/win32-arm64@0.23.1': 446 | resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} 447 | engines: {node: '>=18'} 448 | cpu: [arm64] 449 | os: [win32] 450 | 451 | '@esbuild/win32-ia32@0.19.12': 452 | resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} 453 | engines: {node: '>=12'} 454 | cpu: [ia32] 455 | os: [win32] 456 | 457 | '@esbuild/win32-ia32@0.23.1': 458 | resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} 459 | engines: {node: '>=18'} 460 | cpu: [ia32] 461 | os: [win32] 462 | 463 | '@esbuild/win32-x64@0.19.12': 464 | resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} 465 | engines: {node: '>=12'} 466 | cpu: [x64] 467 | os: [win32] 468 | 469 | '@esbuild/win32-x64@0.23.1': 470 | resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} 471 | engines: {node: '>=18'} 472 | cpu: [x64] 473 | os: [win32] 474 | 475 | '@isaacs/cliui@8.0.2': 476 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 477 | engines: {node: '>=12'} 478 | 479 | '@jridgewell/gen-mapping@0.3.3': 480 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 481 | engines: {node: '>=6.0.0'} 482 | 483 | '@jridgewell/resolve-uri@3.1.2': 484 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 485 | engines: {node: '>=6.0.0'} 486 | 487 | '@jridgewell/set-array@1.1.2': 488 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 489 | engines: {node: '>=6.0.0'} 490 | 491 | '@jridgewell/sourcemap-codec@1.4.15': 492 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 493 | 494 | '@jridgewell/sourcemap-codec@1.5.0': 495 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 496 | 497 | '@jridgewell/trace-mapping@0.3.22': 498 | resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} 499 | 500 | '@jsonjoy.com/base64@1.1.2': 501 | resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} 502 | engines: {node: '>=10.0'} 503 | peerDependencies: 504 | tslib: '2' 505 | 506 | '@jsonjoy.com/json-pack@1.1.0': 507 | resolution: {integrity: sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==} 508 | engines: {node: '>=10.0'} 509 | peerDependencies: 510 | tslib: '2' 511 | 512 | '@jsonjoy.com/util@1.3.0': 513 | resolution: {integrity: sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==} 514 | engines: {node: '>=10.0'} 515 | peerDependencies: 516 | tslib: '2' 517 | 518 | '@manypkg/find-root@1.1.0': 519 | resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} 520 | 521 | '@manypkg/get-packages@1.1.3': 522 | resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} 523 | 524 | '@nodelib/fs.scandir@2.1.5': 525 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 526 | engines: {node: '>= 8'} 527 | 528 | '@nodelib/fs.stat@2.0.5': 529 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 530 | engines: {node: '>= 8'} 531 | 532 | '@nodelib/fs.walk@1.2.8': 533 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 534 | engines: {node: '>= 8'} 535 | 536 | '@pkgjs/parseargs@0.11.0': 537 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 538 | engines: {node: '>=14'} 539 | 540 | '@rollup/rollup-android-arm-eabi@4.23.0': 541 | resolution: {integrity: sha512-8OR+Ok3SGEMsAZispLx8jruuXw0HVF16k+ub2eNXKHDmdxL4cf9NlNpAzhlOhNyXzKDEJuFeq0nZm+XlNb1IFw==} 542 | cpu: [arm] 543 | os: [android] 544 | 545 | '@rollup/rollup-android-arm64@4.23.0': 546 | resolution: {integrity: sha512-rEFtX1nP8gqmLmPZsXRMoLVNB5JBwOzIAk/XAcEPuKrPa2nPJ+DuGGpfQUR0XjRm8KjHfTZLpWbKXkA5BoFL3w==} 547 | cpu: [arm64] 548 | os: [android] 549 | 550 | '@rollup/rollup-darwin-arm64@4.23.0': 551 | resolution: {integrity: sha512-ZbqlMkJRMMPeapfaU4drYHns7Q5MIxjM/QeOO62qQZGPh9XWziap+NF9fsqPHT0KzEL6HaPspC7sOwpgyA3J9g==} 552 | cpu: [arm64] 553 | os: [darwin] 554 | 555 | '@rollup/rollup-darwin-x64@4.23.0': 556 | resolution: {integrity: sha512-PfmgQp78xx5rBCgn2oYPQ1rQTtOaQCna0kRaBlc5w7RlA3TDGGo7m3XaptgitUZ54US9915i7KeVPHoy3/W8tA==} 557 | cpu: [x64] 558 | os: [darwin] 559 | 560 | '@rollup/rollup-linux-arm-gnueabihf@4.23.0': 561 | resolution: {integrity: sha512-WAeZfAAPus56eQgBioezXRRzArAjWJGjNo/M+BHZygUcs9EePIuGI1Wfc6U/Ki+tMW17FFGvhCfYnfcKPh18SA==} 562 | cpu: [arm] 563 | os: [linux] 564 | 565 | '@rollup/rollup-linux-arm-musleabihf@4.23.0': 566 | resolution: {integrity: sha512-v7PGcp1O5XKZxKX8phTXtmJDVpE20Ub1eF6w9iMmI3qrrPak6yR9/5eeq7ziLMrMTjppkkskXyxnmm00HdtXjA==} 567 | cpu: [arm] 568 | os: [linux] 569 | 570 | '@rollup/rollup-linux-arm64-gnu@4.23.0': 571 | resolution: {integrity: sha512-nAbWsDZ9UkU6xQiXEyXBNHAKbzSAi95H3gTStJq9UGiS1v+YVXwRHcQOQEF/3CHuhX5BVhShKoeOf6Q/1M+Zhg==} 572 | cpu: [arm64] 573 | os: [linux] 574 | 575 | '@rollup/rollup-linux-arm64-musl@4.23.0': 576 | resolution: {integrity: sha512-5QT/Di5FbGNPaVw8hHO1wETunwkPuZBIu6W+5GNArlKHD9fkMHy7vS8zGHJk38oObXfWdsuLMogD4sBySLJ54g==} 577 | cpu: [arm64] 578 | os: [linux] 579 | 580 | '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': 581 | resolution: {integrity: sha512-Sefl6vPyn5axzCsO13r1sHLcmPuiSOrKIImnq34CBurntcJ+lkQgAaTt/9JkgGmaZJ+OkaHmAJl4Bfd0DmdtOQ==} 582 | cpu: [ppc64] 583 | os: [linux] 584 | 585 | '@rollup/rollup-linux-riscv64-gnu@4.23.0': 586 | resolution: {integrity: sha512-o4QI2KU/QbP7ZExMse6ULotdV3oJUYMrdx3rBZCgUF3ur3gJPfe8Fuasn6tia16c5kZBBw0aTmaUygad6VB/hQ==} 587 | cpu: [riscv64] 588 | os: [linux] 589 | 590 | '@rollup/rollup-linux-s390x-gnu@4.23.0': 591 | resolution: {integrity: sha512-+bxqx+V/D4FGrpXzPGKp/SEZIZ8cIW3K7wOtcJAoCrmXvzRtmdUhYNbgd+RztLzfDEfA2WtKj5F4tcbNPuqgeg==} 592 | cpu: [s390x] 593 | os: [linux] 594 | 595 | '@rollup/rollup-linux-x64-gnu@4.23.0': 596 | resolution: {integrity: sha512-I/eXsdVoCKtSgK9OwyQKPAfricWKUMNCwJKtatRYMmDo5N859tbO3UsBw5kT3dU1n6ZcM1JDzPRSGhAUkxfLxw==} 597 | cpu: [x64] 598 | os: [linux] 599 | 600 | '@rollup/rollup-linux-x64-musl@4.23.0': 601 | resolution: {integrity: sha512-4ZoDZy5ShLbbe1KPSafbFh1vbl0asTVfkABC7eWqIs01+66ncM82YJxV2VtV3YVJTqq2P8HMx3DCoRSWB/N3rw==} 602 | cpu: [x64] 603 | os: [linux] 604 | 605 | '@rollup/rollup-win32-arm64-msvc@4.23.0': 606 | resolution: {integrity: sha512-+5Ky8dhft4STaOEbZu3/NU4QIyYssKO+r1cD3FzuusA0vO5gso15on7qGzKdNXnc1gOrsgCqZjRw1w+zL4y4hQ==} 607 | cpu: [arm64] 608 | os: [win32] 609 | 610 | '@rollup/rollup-win32-ia32-msvc@4.23.0': 611 | resolution: {integrity: sha512-0SPJk4cPZQhq9qA1UhIRumSE3+JJIBBjtlGl5PNC///BoaByckNZd53rOYD0glpTkYFBQSt7AkMeLVPfx65+BQ==} 612 | cpu: [ia32] 613 | os: [win32] 614 | 615 | '@rollup/rollup-win32-x64-msvc@4.23.0': 616 | resolution: {integrity: sha512-lqCK5GQC8fNo0+JvTSxcG7YB1UKYp8yrNLhsArlvPWN+16ovSZgoehlVHg6X0sSWPUkpjRBR5TuR12ZugowZ4g==} 617 | cpu: [x64] 618 | os: [win32] 619 | 620 | '@tsconfig/node14@14.1.2': 621 | resolution: {integrity: sha512-1vncsbfCZ3TBLPxesRYz02Rn7SNJfbLoDVkcZ7F/ixOV6nwxwgdhD1mdPcc5YQ413qBJ8CvMxXMFfJ7oawjo7Q==} 622 | 623 | '@types/estree@1.0.6': 624 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 625 | 626 | '@types/mock-fs@4.13.4': 627 | resolution: {integrity: sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==} 628 | 629 | '@types/node@12.20.55': 630 | resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} 631 | 632 | '@types/node@16.18.82': 633 | resolution: {integrity: sha512-pcDZtkx9z8XYV+ius2P3Ot2VVrcYOfXffBQUBuiszrlUzKSmoDYqo+mV+IoL8iIiIjjtOMvNSmH1hwJ+Q+f96Q==} 634 | 635 | '@types/picomatch@2.3.4': 636 | resolution: {integrity: sha512-0so8lU8O5zatZS/2Fi4zrwks+vZv7e0dygrgEZXljODXBig97l4cPQD+9LabXfGJOWwoRkTVz6Q4edZvD12UOA==} 637 | 638 | '@types/semver@7.5.7': 639 | resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==} 640 | 641 | '@types/supports-color@8.1.3': 642 | resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==} 643 | 644 | '@vitest/expect@2.1.1': 645 | resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} 646 | 647 | '@vitest/mocker@2.1.1': 648 | resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} 649 | peerDependencies: 650 | '@vitest/spy': 2.1.1 651 | msw: ^2.3.5 652 | vite: ^5.0.0 653 | peerDependenciesMeta: 654 | msw: 655 | optional: true 656 | vite: 657 | optional: true 658 | 659 | '@vitest/pretty-format@2.1.1': 660 | resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} 661 | 662 | '@vitest/runner@2.1.1': 663 | resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} 664 | 665 | '@vitest/snapshot@2.1.1': 666 | resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} 667 | 668 | '@vitest/spy@2.1.1': 669 | resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} 670 | 671 | '@vitest/utils@2.1.1': 672 | resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} 673 | 674 | ansi-colors@4.1.3: 675 | resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} 676 | engines: {node: '>=6'} 677 | 678 | ansi-regex@5.0.1: 679 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 680 | engines: {node: '>=8'} 681 | 682 | ansi-regex@6.0.1: 683 | resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} 684 | engines: {node: '>=12'} 685 | 686 | ansi-styles@4.3.0: 687 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 688 | engines: {node: '>=8'} 689 | 690 | ansi-styles@6.2.1: 691 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 692 | engines: {node: '>=12'} 693 | 694 | any-promise@1.3.0: 695 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 696 | 697 | anymatch@3.1.3: 698 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 699 | engines: {node: '>= 8'} 700 | 701 | argparse@1.0.10: 702 | resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 703 | 704 | array-union@2.1.0: 705 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 706 | engines: {node: '>=8'} 707 | 708 | assertion-error@2.0.1: 709 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 710 | engines: {node: '>=12'} 711 | 712 | balanced-match@1.0.2: 713 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 714 | 715 | better-path-resolve@1.0.0: 716 | resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} 717 | engines: {node: '>=4'} 718 | 719 | binary-extensions@2.2.0: 720 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 721 | engines: {node: '>=8'} 722 | 723 | brace-expansion@2.0.1: 724 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 725 | 726 | braces@3.0.2: 727 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 728 | engines: {node: '>=8'} 729 | 730 | bundle-require@5.0.0: 731 | resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} 732 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 733 | peerDependencies: 734 | esbuild: '>=0.18' 735 | 736 | cac@6.7.14: 737 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 738 | engines: {node: '>=8'} 739 | 740 | chai@5.1.1: 741 | resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} 742 | engines: {node: '>=12'} 743 | 744 | changesets-changelog-clean@1.3.0: 745 | resolution: {integrity: sha512-3bGEYg+SuzS4TizAVwGxkiV+c/IMQI7+JPnrEApbvGeb5//c2cfcLG9pteNqpySVY6BNkpCFMM4dpZucnCPxtQ==} 746 | 747 | chardet@0.7.0: 748 | resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} 749 | 750 | check-error@2.1.1: 751 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 752 | engines: {node: '>= 16'} 753 | 754 | chokidar@3.6.0: 755 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 756 | engines: {node: '>= 8.10.0'} 757 | 758 | ci-info@3.9.0: 759 | resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} 760 | engines: {node: '>=8'} 761 | 762 | clipanion@3.2.1: 763 | resolution: {integrity: sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==} 764 | peerDependencies: 765 | typanion: '*' 766 | 767 | color-convert@2.0.1: 768 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 769 | engines: {node: '>=7.0.0'} 770 | 771 | color-name@1.1.4: 772 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 773 | 774 | commander@4.1.1: 775 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 776 | engines: {node: '>= 6'} 777 | 778 | consola@3.2.3: 779 | resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} 780 | engines: {node: ^14.18.0 || >=16.10.0} 781 | 782 | cross-spawn@5.1.0: 783 | resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} 784 | 785 | cross-spawn@7.0.3: 786 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 787 | engines: {node: '>= 8'} 788 | 789 | dataloader@1.4.0: 790 | resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} 791 | 792 | debug@4.3.7: 793 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} 794 | engines: {node: '>=6.0'} 795 | peerDependencies: 796 | supports-color: '*' 797 | peerDependenciesMeta: 798 | supports-color: 799 | optional: true 800 | 801 | deep-eql@5.0.2: 802 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 803 | engines: {node: '>=6'} 804 | 805 | detect-indent@6.1.0: 806 | resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} 807 | engines: {node: '>=8'} 808 | 809 | dir-glob@3.0.1: 810 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 811 | engines: {node: '>=8'} 812 | 813 | eastasianwidth@0.2.0: 814 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 815 | 816 | emoji-regex@8.0.0: 817 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 818 | 819 | emoji-regex@9.2.2: 820 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 821 | 822 | enquirer@2.4.1: 823 | resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} 824 | engines: {node: '>=8.6'} 825 | 826 | esbuild@0.19.12: 827 | resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} 828 | engines: {node: '>=12'} 829 | hasBin: true 830 | 831 | esbuild@0.23.1: 832 | resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} 833 | engines: {node: '>=18'} 834 | hasBin: true 835 | 836 | esprima@4.0.1: 837 | resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 838 | engines: {node: '>=4'} 839 | hasBin: true 840 | 841 | estree-walker@3.0.3: 842 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 843 | 844 | execa@5.1.1: 845 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 846 | engines: {node: '>=10'} 847 | 848 | extendable-error@0.1.7: 849 | resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} 850 | 851 | external-editor@3.1.0: 852 | resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} 853 | engines: {node: '>=4'} 854 | 855 | fast-glob@3.3.2: 856 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} 857 | engines: {node: '>=8.6.0'} 858 | 859 | fastq@1.17.1: 860 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 861 | 862 | fdir@6.4.0: 863 | resolution: {integrity: sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==} 864 | peerDependencies: 865 | picomatch: ^3 || ^4 866 | peerDependenciesMeta: 867 | picomatch: 868 | optional: true 869 | 870 | fill-range@7.0.1: 871 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 872 | engines: {node: '>=8'} 873 | 874 | find-up@4.1.0: 875 | resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 876 | engines: {node: '>=8'} 877 | 878 | foreground-child@3.1.1: 879 | resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} 880 | engines: {node: '>=14'} 881 | 882 | fs-extra@7.0.1: 883 | resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} 884 | engines: {node: '>=6 <7 || >=8'} 885 | 886 | fs-extra@8.1.0: 887 | resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} 888 | engines: {node: '>=6 <7 || >=8'} 889 | 890 | fsevents@2.3.3: 891 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 892 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 893 | os: [darwin] 894 | 895 | get-func-name@2.0.2: 896 | resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} 897 | 898 | get-stream@6.0.1: 899 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 900 | engines: {node: '>=10'} 901 | 902 | get-tsconfig@4.8.1: 903 | resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} 904 | 905 | glob-parent@5.1.2: 906 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 907 | engines: {node: '>= 6'} 908 | 909 | glob@10.3.10: 910 | resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} 911 | engines: {node: '>=16 || 14 >=14.17'} 912 | hasBin: true 913 | 914 | globby@11.1.0: 915 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 916 | engines: {node: '>=10'} 917 | 918 | graceful-fs@4.2.11: 919 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 920 | 921 | human-id@1.0.2: 922 | resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} 923 | 924 | human-signals@2.1.0: 925 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 926 | engines: {node: '>=10.17.0'} 927 | 928 | hyperdyperid@1.2.0: 929 | resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} 930 | engines: {node: '>=10.18'} 931 | 932 | iconv-lite@0.4.24: 933 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 934 | engines: {node: '>=0.10.0'} 935 | 936 | ignore@5.3.1: 937 | resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} 938 | engines: {node: '>= 4'} 939 | 940 | is-binary-path@2.1.0: 941 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 942 | engines: {node: '>=8'} 943 | 944 | is-extglob@2.1.1: 945 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 946 | engines: {node: '>=0.10.0'} 947 | 948 | is-fullwidth-code-point@3.0.0: 949 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 950 | engines: {node: '>=8'} 951 | 952 | is-glob@4.0.3: 953 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 954 | engines: {node: '>=0.10.0'} 955 | 956 | is-number@7.0.0: 957 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 958 | engines: {node: '>=0.12.0'} 959 | 960 | is-stream@2.0.1: 961 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 962 | engines: {node: '>=8'} 963 | 964 | is-subdir@1.2.0: 965 | resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} 966 | engines: {node: '>=4'} 967 | 968 | is-windows@1.0.2: 969 | resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} 970 | engines: {node: '>=0.10.0'} 971 | 972 | isexe@2.0.0: 973 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 974 | 975 | jackspeak@2.3.6: 976 | resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} 977 | engines: {node: '>=14'} 978 | 979 | joycon@3.1.1: 980 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 981 | engines: {node: '>=10'} 982 | 983 | js-yaml@3.14.1: 984 | resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} 985 | hasBin: true 986 | 987 | jsonfile@4.0.0: 988 | resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} 989 | 990 | lilconfig@3.1.2: 991 | resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} 992 | engines: {node: '>=14'} 993 | 994 | lines-and-columns@1.2.4: 995 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 996 | 997 | load-tsconfig@0.2.5: 998 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 999 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 1000 | 1001 | locate-path@5.0.0: 1002 | resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 1003 | engines: {node: '>=8'} 1004 | 1005 | lodash.sortby@4.7.0: 1006 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 1007 | 1008 | lodash.startcase@4.4.0: 1009 | resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} 1010 | 1011 | loupe@3.1.1: 1012 | resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} 1013 | 1014 | lru-cache@10.2.0: 1015 | resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} 1016 | engines: {node: 14 || >=16.14} 1017 | 1018 | lru-cache@4.1.5: 1019 | resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} 1020 | 1021 | lru-cache@6.0.0: 1022 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 1023 | engines: {node: '>=10'} 1024 | 1025 | magic-string@0.30.11: 1026 | resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} 1027 | 1028 | memfs@4.12.0: 1029 | resolution: {integrity: sha512-74wDsex5tQDSClVkeK1vtxqYCAgCoXxx+K4NSHzgU/muYVYByFqa+0RnrPO9NM6naWm1+G9JmZ0p6QHhXmeYfA==} 1030 | engines: {node: '>= 4.0.0'} 1031 | 1032 | merge-stream@2.0.0: 1033 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 1034 | 1035 | merge2@1.4.1: 1036 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1037 | engines: {node: '>= 8'} 1038 | 1039 | micromatch@4.0.5: 1040 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 1041 | engines: {node: '>=8.6'} 1042 | 1043 | mimic-fn@2.1.0: 1044 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 1045 | engines: {node: '>=6'} 1046 | 1047 | minimatch@9.0.3: 1048 | resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} 1049 | engines: {node: '>=16 || 14 >=14.17'} 1050 | 1051 | minipass@7.0.4: 1052 | resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} 1053 | engines: {node: '>=16 || 14 >=14.17'} 1054 | 1055 | mri@1.2.0: 1056 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 1057 | engines: {node: '>=4'} 1058 | 1059 | ms@2.1.3: 1060 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1061 | 1062 | mz@2.7.0: 1063 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 1064 | 1065 | nanoid@3.3.7: 1066 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 1067 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1068 | hasBin: true 1069 | 1070 | node-fetch@2.7.0: 1071 | resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} 1072 | engines: {node: 4.x || >=6.0.0} 1073 | peerDependencies: 1074 | encoding: ^0.1.0 1075 | peerDependenciesMeta: 1076 | encoding: 1077 | optional: true 1078 | 1079 | normalize-path@3.0.0: 1080 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1081 | engines: {node: '>=0.10.0'} 1082 | 1083 | npm-run-path@4.0.1: 1084 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 1085 | engines: {node: '>=8'} 1086 | 1087 | object-assign@4.1.1: 1088 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1089 | engines: {node: '>=0.10.0'} 1090 | 1091 | onetime@5.1.2: 1092 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 1093 | engines: {node: '>=6'} 1094 | 1095 | os-tmpdir@1.0.2: 1096 | resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} 1097 | engines: {node: '>=0.10.0'} 1098 | 1099 | outdent@0.5.0: 1100 | resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} 1101 | 1102 | p-filter@2.1.0: 1103 | resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} 1104 | engines: {node: '>=8'} 1105 | 1106 | p-limit@2.3.0: 1107 | resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 1108 | engines: {node: '>=6'} 1109 | 1110 | p-locate@4.1.0: 1111 | resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 1112 | engines: {node: '>=8'} 1113 | 1114 | p-map@2.1.0: 1115 | resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} 1116 | engines: {node: '>=6'} 1117 | 1118 | p-try@2.2.0: 1119 | resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 1120 | engines: {node: '>=6'} 1121 | 1122 | package-manager-detector@0.2.0: 1123 | resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} 1124 | 1125 | parse-ms@3.0.0: 1126 | resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} 1127 | engines: {node: '>=12'} 1128 | 1129 | path-exists@4.0.0: 1130 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1131 | engines: {node: '>=8'} 1132 | 1133 | path-key@3.1.1: 1134 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1135 | engines: {node: '>=8'} 1136 | 1137 | path-scurry@1.10.1: 1138 | resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} 1139 | engines: {node: '>=16 || 14 >=14.17'} 1140 | 1141 | path-type@4.0.0: 1142 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 1143 | engines: {node: '>=8'} 1144 | 1145 | pathe@1.1.2: 1146 | resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} 1147 | 1148 | pathval@2.0.0: 1149 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 1150 | engines: {node: '>= 14.16'} 1151 | 1152 | picocolors@1.1.0: 1153 | resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} 1154 | 1155 | picomatch@2.3.1: 1156 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1157 | engines: {node: '>=8.6'} 1158 | 1159 | picomatch@4.0.2: 1160 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 1161 | engines: {node: '>=12'} 1162 | 1163 | pify@4.0.1: 1164 | resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} 1165 | engines: {node: '>=6'} 1166 | 1167 | pirates@4.0.6: 1168 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 1169 | engines: {node: '>= 6'} 1170 | 1171 | postcss-load-config@6.0.1: 1172 | resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 1173 | engines: {node: '>= 18'} 1174 | peerDependencies: 1175 | jiti: '>=1.21.0' 1176 | postcss: '>=8.0.9' 1177 | tsx: ^4.8.1 1178 | yaml: ^2.4.2 1179 | peerDependenciesMeta: 1180 | jiti: 1181 | optional: true 1182 | postcss: 1183 | optional: true 1184 | tsx: 1185 | optional: true 1186 | yaml: 1187 | optional: true 1188 | 1189 | postcss@8.4.35: 1190 | resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} 1191 | engines: {node: ^10 || ^12 || >=14} 1192 | 1193 | prettier-plugin-organize-imports@4.1.0: 1194 | resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==} 1195 | peerDependencies: 1196 | prettier: '>=2.0' 1197 | typescript: '>=2.9' 1198 | vue-tsc: ^2.1.0 1199 | peerDependenciesMeta: 1200 | vue-tsc: 1201 | optional: true 1202 | 1203 | prettier@2.8.8: 1204 | resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} 1205 | engines: {node: '>=10.13.0'} 1206 | hasBin: true 1207 | 1208 | prettier@3.3.3: 1209 | resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} 1210 | engines: {node: '>=14'} 1211 | hasBin: true 1212 | 1213 | pretty-bytes@6.1.1: 1214 | resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} 1215 | engines: {node: ^14.13.1 || >=16.0.0} 1216 | 1217 | pretty-ms@8.0.0: 1218 | resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} 1219 | engines: {node: '>=14.16'} 1220 | 1221 | pseudomap@1.0.2: 1222 | resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} 1223 | 1224 | punycode@2.3.1: 1225 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1226 | engines: {node: '>=6'} 1227 | 1228 | queue-microtask@1.2.3: 1229 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1230 | 1231 | read-yaml-file@1.1.0: 1232 | resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} 1233 | engines: {node: '>=6'} 1234 | 1235 | readdirp@3.6.0: 1236 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1237 | engines: {node: '>=8.10.0'} 1238 | 1239 | regenerator-runtime@0.14.1: 1240 | resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} 1241 | 1242 | resolve-from@5.0.0: 1243 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 1244 | engines: {node: '>=8'} 1245 | 1246 | resolve-pkg-maps@1.0.0: 1247 | resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 1248 | 1249 | reusify@1.0.4: 1250 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1251 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1252 | 1253 | rollup@4.23.0: 1254 | resolution: {integrity: sha512-vXB4IT9/KLDrS2WRXmY22sVB2wTsTwkpxjB8Q3mnakTENcYw3FRmfdYDy/acNmls+lHmDazgrRjK/yQ6hQAtwA==} 1255 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1256 | hasBin: true 1257 | 1258 | run-parallel@1.2.0: 1259 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1260 | 1261 | safer-buffer@2.1.2: 1262 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 1263 | 1264 | semver@7.6.0: 1265 | resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} 1266 | engines: {node: '>=10'} 1267 | hasBin: true 1268 | 1269 | shebang-command@1.2.0: 1270 | resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} 1271 | engines: {node: '>=0.10.0'} 1272 | 1273 | shebang-command@2.0.0: 1274 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1275 | engines: {node: '>=8'} 1276 | 1277 | shebang-regex@1.0.0: 1278 | resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} 1279 | engines: {node: '>=0.10.0'} 1280 | 1281 | shebang-regex@3.0.0: 1282 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1283 | engines: {node: '>=8'} 1284 | 1285 | siginfo@2.0.0: 1286 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 1287 | 1288 | signal-exit@3.0.7: 1289 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 1290 | 1291 | signal-exit@4.1.0: 1292 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1293 | engines: {node: '>=14'} 1294 | 1295 | slash@3.0.0: 1296 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1297 | engines: {node: '>=8'} 1298 | 1299 | source-map-js@1.0.2: 1300 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1301 | engines: {node: '>=0.10.0'} 1302 | 1303 | source-map@0.8.0-beta.0: 1304 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 1305 | engines: {node: '>= 8'} 1306 | 1307 | spawndamnit@2.0.0: 1308 | resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} 1309 | 1310 | sprintf-js@1.0.3: 1311 | resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} 1312 | 1313 | stackback@0.0.2: 1314 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 1315 | 1316 | std-env@3.7.0: 1317 | resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} 1318 | 1319 | string-width@4.2.3: 1320 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1321 | engines: {node: '>=8'} 1322 | 1323 | string-width@5.1.2: 1324 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1325 | engines: {node: '>=12'} 1326 | 1327 | strip-ansi@6.0.1: 1328 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1329 | engines: {node: '>=8'} 1330 | 1331 | strip-ansi@7.1.0: 1332 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1333 | engines: {node: '>=12'} 1334 | 1335 | strip-bom@3.0.0: 1336 | resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 1337 | engines: {node: '>=4'} 1338 | 1339 | strip-final-newline@2.0.0: 1340 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 1341 | engines: {node: '>=6'} 1342 | 1343 | sucrase@3.35.0: 1344 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1345 | engines: {node: '>=16 || 14 >=14.17'} 1346 | hasBin: true 1347 | 1348 | supports-color@9.4.0: 1349 | resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} 1350 | engines: {node: '>=12'} 1351 | 1352 | term-size@2.2.1: 1353 | resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} 1354 | engines: {node: '>=8'} 1355 | 1356 | thenify-all@1.6.0: 1357 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 1358 | engines: {node: '>=0.8'} 1359 | 1360 | thenify@3.3.1: 1361 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1362 | 1363 | thingies@1.21.0: 1364 | resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} 1365 | engines: {node: '>=10.18'} 1366 | peerDependencies: 1367 | tslib: ^2 1368 | 1369 | tinybench@2.9.0: 1370 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 1371 | 1372 | tinyexec@0.3.0: 1373 | resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} 1374 | 1375 | tinyglobby@0.2.9: 1376 | resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} 1377 | engines: {node: '>=12.0.0'} 1378 | 1379 | tinypool@1.0.1: 1380 | resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} 1381 | engines: {node: ^18.0.0 || >=20.0.0} 1382 | 1383 | tinyrainbow@1.2.0: 1384 | resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} 1385 | engines: {node: '>=14.0.0'} 1386 | 1387 | tinyspy@3.0.2: 1388 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 1389 | engines: {node: '>=14.0.0'} 1390 | 1391 | tmp@0.0.33: 1392 | resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} 1393 | engines: {node: '>=0.6.0'} 1394 | 1395 | to-regex-range@5.0.1: 1396 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1397 | engines: {node: '>=8.0'} 1398 | 1399 | tr46@0.0.3: 1400 | resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} 1401 | 1402 | tr46@1.0.1: 1403 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 1404 | 1405 | tree-dump@1.0.2: 1406 | resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} 1407 | engines: {node: '>=10.0'} 1408 | peerDependencies: 1409 | tslib: '2' 1410 | 1411 | tree-kill@1.2.2: 1412 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 1413 | hasBin: true 1414 | 1415 | ts-interface-checker@0.1.13: 1416 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1417 | 1418 | tslib@2.7.0: 1419 | resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} 1420 | 1421 | tsup@8.3.0: 1422 | resolution: {integrity: sha512-ALscEeyS03IomcuNdFdc0YWGVIkwH1Ws7nfTbAPuoILvEV2hpGQAY72LIOjglGo4ShWpZfpBqP/jpQVCzqYQag==} 1423 | engines: {node: '>=18'} 1424 | hasBin: true 1425 | peerDependencies: 1426 | '@microsoft/api-extractor': ^7.36.0 1427 | '@swc/core': ^1 1428 | postcss: ^8.4.12 1429 | typescript: '>=4.5.0' 1430 | peerDependenciesMeta: 1431 | '@microsoft/api-extractor': 1432 | optional: true 1433 | '@swc/core': 1434 | optional: true 1435 | postcss: 1436 | optional: true 1437 | typescript: 1438 | optional: true 1439 | 1440 | tsx@4.19.1: 1441 | resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} 1442 | engines: {node: '>=18.0.0'} 1443 | hasBin: true 1444 | 1445 | typanion@3.14.0: 1446 | resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} 1447 | 1448 | typescript@5.6.2: 1449 | resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} 1450 | engines: {node: '>=14.17'} 1451 | hasBin: true 1452 | 1453 | universalify@0.1.2: 1454 | resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} 1455 | engines: {node: '>= 4.0.0'} 1456 | 1457 | vite-node@2.1.1: 1458 | resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} 1459 | engines: {node: ^18.0.0 || >=20.0.0} 1460 | hasBin: true 1461 | 1462 | vite@5.1.3: 1463 | resolution: {integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==} 1464 | engines: {node: ^18.0.0 || >=20.0.0} 1465 | hasBin: true 1466 | peerDependencies: 1467 | '@types/node': ^18.0.0 || >=20.0.0 1468 | less: '*' 1469 | lightningcss: ^1.21.0 1470 | sass: '*' 1471 | stylus: '*' 1472 | sugarss: '*' 1473 | terser: ^5.4.0 1474 | peerDependenciesMeta: 1475 | '@types/node': 1476 | optional: true 1477 | less: 1478 | optional: true 1479 | lightningcss: 1480 | optional: true 1481 | sass: 1482 | optional: true 1483 | stylus: 1484 | optional: true 1485 | sugarss: 1486 | optional: true 1487 | terser: 1488 | optional: true 1489 | 1490 | vitest@2.1.1: 1491 | resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} 1492 | engines: {node: ^18.0.0 || >=20.0.0} 1493 | hasBin: true 1494 | peerDependencies: 1495 | '@edge-runtime/vm': '*' 1496 | '@types/node': ^18.0.0 || >=20.0.0 1497 | '@vitest/browser': 2.1.1 1498 | '@vitest/ui': 2.1.1 1499 | happy-dom: '*' 1500 | jsdom: '*' 1501 | peerDependenciesMeta: 1502 | '@edge-runtime/vm': 1503 | optional: true 1504 | '@types/node': 1505 | optional: true 1506 | '@vitest/browser': 1507 | optional: true 1508 | '@vitest/ui': 1509 | optional: true 1510 | happy-dom: 1511 | optional: true 1512 | jsdom: 1513 | optional: true 1514 | 1515 | webidl-conversions@3.0.1: 1516 | resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} 1517 | 1518 | webidl-conversions@4.0.2: 1519 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 1520 | 1521 | whatwg-url@5.0.0: 1522 | resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} 1523 | 1524 | whatwg-url@7.1.0: 1525 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 1526 | 1527 | which@1.3.1: 1528 | resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} 1529 | hasBin: true 1530 | 1531 | which@2.0.2: 1532 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1533 | engines: {node: '>= 8'} 1534 | hasBin: true 1535 | 1536 | why-is-node-running@2.3.0: 1537 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1538 | engines: {node: '>=8'} 1539 | hasBin: true 1540 | 1541 | wrap-ansi@7.0.0: 1542 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1543 | engines: {node: '>=10'} 1544 | 1545 | wrap-ansi@8.1.0: 1546 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1547 | engines: {node: '>=12'} 1548 | 1549 | yallist@2.1.2: 1550 | resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} 1551 | 1552 | yallist@4.0.0: 1553 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 1554 | 1555 | snapshots: 1556 | 1557 | '@babel/runtime@7.23.9': 1558 | dependencies: 1559 | regenerator-runtime: 0.14.1 1560 | 1561 | '@biomejs/biome@1.9.2': 1562 | optionalDependencies: 1563 | '@biomejs/cli-darwin-arm64': 1.9.2 1564 | '@biomejs/cli-darwin-x64': 1.9.2 1565 | '@biomejs/cli-linux-arm64': 1.9.2 1566 | '@biomejs/cli-linux-arm64-musl': 1.9.2 1567 | '@biomejs/cli-linux-x64': 1.9.2 1568 | '@biomejs/cli-linux-x64-musl': 1.9.2 1569 | '@biomejs/cli-win32-arm64': 1.9.2 1570 | '@biomejs/cli-win32-x64': 1.9.2 1571 | 1572 | '@biomejs/cli-darwin-arm64@1.9.2': 1573 | optional: true 1574 | 1575 | '@biomejs/cli-darwin-x64@1.9.2': 1576 | optional: true 1577 | 1578 | '@biomejs/cli-linux-arm64-musl@1.9.2': 1579 | optional: true 1580 | 1581 | '@biomejs/cli-linux-arm64@1.9.2': 1582 | optional: true 1583 | 1584 | '@biomejs/cli-linux-x64-musl@1.9.2': 1585 | optional: true 1586 | 1587 | '@biomejs/cli-linux-x64@1.9.2': 1588 | optional: true 1589 | 1590 | '@biomejs/cli-win32-arm64@1.9.2': 1591 | optional: true 1592 | 1593 | '@biomejs/cli-win32-x64@1.9.2': 1594 | optional: true 1595 | 1596 | '@changesets/apply-release-plan@7.0.5': 1597 | dependencies: 1598 | '@changesets/config': 3.0.3 1599 | '@changesets/get-version-range-type': 0.4.0 1600 | '@changesets/git': 3.0.1 1601 | '@changesets/should-skip-package': 0.1.1 1602 | '@changesets/types': 6.0.0 1603 | '@manypkg/get-packages': 1.1.3 1604 | detect-indent: 6.1.0 1605 | fs-extra: 7.0.1 1606 | lodash.startcase: 4.4.0 1607 | outdent: 0.5.0 1608 | prettier: 2.8.8 1609 | resolve-from: 5.0.0 1610 | semver: 7.6.0 1611 | 1612 | '@changesets/assemble-release-plan@6.0.4': 1613 | dependencies: 1614 | '@changesets/errors': 0.2.0 1615 | '@changesets/get-dependents-graph': 2.1.2 1616 | '@changesets/should-skip-package': 0.1.1 1617 | '@changesets/types': 6.0.0 1618 | '@manypkg/get-packages': 1.1.3 1619 | semver: 7.6.0 1620 | 1621 | '@changesets/changelog-git@0.2.0': 1622 | dependencies: 1623 | '@changesets/types': 6.0.0 1624 | 1625 | '@changesets/cli@2.27.8': 1626 | dependencies: 1627 | '@changesets/apply-release-plan': 7.0.5 1628 | '@changesets/assemble-release-plan': 6.0.4 1629 | '@changesets/changelog-git': 0.2.0 1630 | '@changesets/config': 3.0.3 1631 | '@changesets/errors': 0.2.0 1632 | '@changesets/get-dependents-graph': 2.1.2 1633 | '@changesets/get-release-plan': 4.0.4 1634 | '@changesets/git': 3.0.1 1635 | '@changesets/logger': 0.1.1 1636 | '@changesets/pre': 2.0.1 1637 | '@changesets/read': 0.6.1 1638 | '@changesets/should-skip-package': 0.1.1 1639 | '@changesets/types': 6.0.0 1640 | '@changesets/write': 0.3.2 1641 | '@manypkg/get-packages': 1.1.3 1642 | '@types/semver': 7.5.7 1643 | ansi-colors: 4.1.3 1644 | ci-info: 3.9.0 1645 | enquirer: 2.4.1 1646 | external-editor: 3.1.0 1647 | fs-extra: 7.0.1 1648 | mri: 1.2.0 1649 | outdent: 0.5.0 1650 | p-limit: 2.3.0 1651 | package-manager-detector: 0.2.0 1652 | picocolors: 1.1.0 1653 | resolve-from: 5.0.0 1654 | semver: 7.6.0 1655 | spawndamnit: 2.0.0 1656 | term-size: 2.2.1 1657 | 1658 | '@changesets/config@3.0.3': 1659 | dependencies: 1660 | '@changesets/errors': 0.2.0 1661 | '@changesets/get-dependents-graph': 2.1.2 1662 | '@changesets/logger': 0.1.1 1663 | '@changesets/types': 6.0.0 1664 | '@manypkg/get-packages': 1.1.3 1665 | fs-extra: 7.0.1 1666 | micromatch: 4.0.5 1667 | 1668 | '@changesets/errors@0.2.0': 1669 | dependencies: 1670 | extendable-error: 0.1.7 1671 | 1672 | '@changesets/get-dependents-graph@2.1.2': 1673 | dependencies: 1674 | '@changesets/types': 6.0.0 1675 | '@manypkg/get-packages': 1.1.3 1676 | picocolors: 1.1.0 1677 | semver: 7.6.0 1678 | 1679 | '@changesets/get-github-info@0.6.0': 1680 | dependencies: 1681 | dataloader: 1.4.0 1682 | node-fetch: 2.7.0 1683 | transitivePeerDependencies: 1684 | - encoding 1685 | 1686 | '@changesets/get-release-plan@4.0.4': 1687 | dependencies: 1688 | '@changesets/assemble-release-plan': 6.0.4 1689 | '@changesets/config': 3.0.3 1690 | '@changesets/pre': 2.0.1 1691 | '@changesets/read': 0.6.1 1692 | '@changesets/types': 6.0.0 1693 | '@manypkg/get-packages': 1.1.3 1694 | 1695 | '@changesets/get-version-range-type@0.4.0': {} 1696 | 1697 | '@changesets/git@3.0.1': 1698 | dependencies: 1699 | '@changesets/errors': 0.2.0 1700 | '@manypkg/get-packages': 1.1.3 1701 | is-subdir: 1.2.0 1702 | micromatch: 4.0.5 1703 | spawndamnit: 2.0.0 1704 | 1705 | '@changesets/logger@0.1.1': 1706 | dependencies: 1707 | picocolors: 1.1.0 1708 | 1709 | '@changesets/parse@0.4.0': 1710 | dependencies: 1711 | '@changesets/types': 6.0.0 1712 | js-yaml: 3.14.1 1713 | 1714 | '@changesets/pre@2.0.1': 1715 | dependencies: 1716 | '@changesets/errors': 0.2.0 1717 | '@changesets/types': 6.0.0 1718 | '@manypkg/get-packages': 1.1.3 1719 | fs-extra: 7.0.1 1720 | 1721 | '@changesets/read@0.6.1': 1722 | dependencies: 1723 | '@changesets/git': 3.0.1 1724 | '@changesets/logger': 0.1.1 1725 | '@changesets/parse': 0.4.0 1726 | '@changesets/types': 6.0.0 1727 | fs-extra: 7.0.1 1728 | p-filter: 2.1.0 1729 | picocolors: 1.1.0 1730 | 1731 | '@changesets/should-skip-package@0.1.1': 1732 | dependencies: 1733 | '@changesets/types': 6.0.0 1734 | '@manypkg/get-packages': 1.1.3 1735 | 1736 | '@changesets/types@4.1.0': {} 1737 | 1738 | '@changesets/types@6.0.0': {} 1739 | 1740 | '@changesets/write@0.3.2': 1741 | dependencies: 1742 | '@changesets/types': 6.0.0 1743 | fs-extra: 7.0.1 1744 | human-id: 1.0.2 1745 | prettier: 2.8.8 1746 | 1747 | '@esbuild/aix-ppc64@0.19.12': 1748 | optional: true 1749 | 1750 | '@esbuild/aix-ppc64@0.23.1': 1751 | optional: true 1752 | 1753 | '@esbuild/android-arm64@0.19.12': 1754 | optional: true 1755 | 1756 | '@esbuild/android-arm64@0.23.1': 1757 | optional: true 1758 | 1759 | '@esbuild/android-arm@0.19.12': 1760 | optional: true 1761 | 1762 | '@esbuild/android-arm@0.23.1': 1763 | optional: true 1764 | 1765 | '@esbuild/android-x64@0.19.12': 1766 | optional: true 1767 | 1768 | '@esbuild/android-x64@0.23.1': 1769 | optional: true 1770 | 1771 | '@esbuild/darwin-arm64@0.19.12': 1772 | optional: true 1773 | 1774 | '@esbuild/darwin-arm64@0.23.1': 1775 | optional: true 1776 | 1777 | '@esbuild/darwin-x64@0.19.12': 1778 | optional: true 1779 | 1780 | '@esbuild/darwin-x64@0.23.1': 1781 | optional: true 1782 | 1783 | '@esbuild/freebsd-arm64@0.19.12': 1784 | optional: true 1785 | 1786 | '@esbuild/freebsd-arm64@0.23.1': 1787 | optional: true 1788 | 1789 | '@esbuild/freebsd-x64@0.19.12': 1790 | optional: true 1791 | 1792 | '@esbuild/freebsd-x64@0.23.1': 1793 | optional: true 1794 | 1795 | '@esbuild/linux-arm64@0.19.12': 1796 | optional: true 1797 | 1798 | '@esbuild/linux-arm64@0.23.1': 1799 | optional: true 1800 | 1801 | '@esbuild/linux-arm@0.19.12': 1802 | optional: true 1803 | 1804 | '@esbuild/linux-arm@0.23.1': 1805 | optional: true 1806 | 1807 | '@esbuild/linux-ia32@0.19.12': 1808 | optional: true 1809 | 1810 | '@esbuild/linux-ia32@0.23.1': 1811 | optional: true 1812 | 1813 | '@esbuild/linux-loong64@0.19.12': 1814 | optional: true 1815 | 1816 | '@esbuild/linux-loong64@0.23.1': 1817 | optional: true 1818 | 1819 | '@esbuild/linux-mips64el@0.19.12': 1820 | optional: true 1821 | 1822 | '@esbuild/linux-mips64el@0.23.1': 1823 | optional: true 1824 | 1825 | '@esbuild/linux-ppc64@0.19.12': 1826 | optional: true 1827 | 1828 | '@esbuild/linux-ppc64@0.23.1': 1829 | optional: true 1830 | 1831 | '@esbuild/linux-riscv64@0.19.12': 1832 | optional: true 1833 | 1834 | '@esbuild/linux-riscv64@0.23.1': 1835 | optional: true 1836 | 1837 | '@esbuild/linux-s390x@0.19.12': 1838 | optional: true 1839 | 1840 | '@esbuild/linux-s390x@0.23.1': 1841 | optional: true 1842 | 1843 | '@esbuild/linux-x64@0.19.12': 1844 | optional: true 1845 | 1846 | '@esbuild/linux-x64@0.23.1': 1847 | optional: true 1848 | 1849 | '@esbuild/netbsd-x64@0.19.12': 1850 | optional: true 1851 | 1852 | '@esbuild/netbsd-x64@0.23.1': 1853 | optional: true 1854 | 1855 | '@esbuild/openbsd-arm64@0.23.1': 1856 | optional: true 1857 | 1858 | '@esbuild/openbsd-x64@0.19.12': 1859 | optional: true 1860 | 1861 | '@esbuild/openbsd-x64@0.23.1': 1862 | optional: true 1863 | 1864 | '@esbuild/sunos-x64@0.19.12': 1865 | optional: true 1866 | 1867 | '@esbuild/sunos-x64@0.23.1': 1868 | optional: true 1869 | 1870 | '@esbuild/win32-arm64@0.19.12': 1871 | optional: true 1872 | 1873 | '@esbuild/win32-arm64@0.23.1': 1874 | optional: true 1875 | 1876 | '@esbuild/win32-ia32@0.19.12': 1877 | optional: true 1878 | 1879 | '@esbuild/win32-ia32@0.23.1': 1880 | optional: true 1881 | 1882 | '@esbuild/win32-x64@0.19.12': 1883 | optional: true 1884 | 1885 | '@esbuild/win32-x64@0.23.1': 1886 | optional: true 1887 | 1888 | '@isaacs/cliui@8.0.2': 1889 | dependencies: 1890 | string-width: 5.1.2 1891 | string-width-cjs: string-width@4.2.3 1892 | strip-ansi: 7.1.0 1893 | strip-ansi-cjs: strip-ansi@6.0.1 1894 | wrap-ansi: 8.1.0 1895 | wrap-ansi-cjs: wrap-ansi@7.0.0 1896 | 1897 | '@jridgewell/gen-mapping@0.3.3': 1898 | dependencies: 1899 | '@jridgewell/set-array': 1.1.2 1900 | '@jridgewell/sourcemap-codec': 1.4.15 1901 | '@jridgewell/trace-mapping': 0.3.22 1902 | 1903 | '@jridgewell/resolve-uri@3.1.2': {} 1904 | 1905 | '@jridgewell/set-array@1.1.2': {} 1906 | 1907 | '@jridgewell/sourcemap-codec@1.4.15': {} 1908 | 1909 | '@jridgewell/sourcemap-codec@1.5.0': {} 1910 | 1911 | '@jridgewell/trace-mapping@0.3.22': 1912 | dependencies: 1913 | '@jridgewell/resolve-uri': 3.1.2 1914 | '@jridgewell/sourcemap-codec': 1.4.15 1915 | 1916 | '@jsonjoy.com/base64@1.1.2(tslib@2.7.0)': 1917 | dependencies: 1918 | tslib: 2.7.0 1919 | 1920 | '@jsonjoy.com/json-pack@1.1.0(tslib@2.7.0)': 1921 | dependencies: 1922 | '@jsonjoy.com/base64': 1.1.2(tslib@2.7.0) 1923 | '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) 1924 | hyperdyperid: 1.2.0 1925 | thingies: 1.21.0(tslib@2.7.0) 1926 | tslib: 2.7.0 1927 | 1928 | '@jsonjoy.com/util@1.3.0(tslib@2.7.0)': 1929 | dependencies: 1930 | tslib: 2.7.0 1931 | 1932 | '@manypkg/find-root@1.1.0': 1933 | dependencies: 1934 | '@babel/runtime': 7.23.9 1935 | '@types/node': 12.20.55 1936 | find-up: 4.1.0 1937 | fs-extra: 8.1.0 1938 | 1939 | '@manypkg/get-packages@1.1.3': 1940 | dependencies: 1941 | '@babel/runtime': 7.23.9 1942 | '@changesets/types': 4.1.0 1943 | '@manypkg/find-root': 1.1.0 1944 | fs-extra: 8.1.0 1945 | globby: 11.1.0 1946 | read-yaml-file: 1.1.0 1947 | 1948 | '@nodelib/fs.scandir@2.1.5': 1949 | dependencies: 1950 | '@nodelib/fs.stat': 2.0.5 1951 | run-parallel: 1.2.0 1952 | 1953 | '@nodelib/fs.stat@2.0.5': {} 1954 | 1955 | '@nodelib/fs.walk@1.2.8': 1956 | dependencies: 1957 | '@nodelib/fs.scandir': 2.1.5 1958 | fastq: 1.17.1 1959 | 1960 | '@pkgjs/parseargs@0.11.0': 1961 | optional: true 1962 | 1963 | '@rollup/rollup-android-arm-eabi@4.23.0': 1964 | optional: true 1965 | 1966 | '@rollup/rollup-android-arm64@4.23.0': 1967 | optional: true 1968 | 1969 | '@rollup/rollup-darwin-arm64@4.23.0': 1970 | optional: true 1971 | 1972 | '@rollup/rollup-darwin-x64@4.23.0': 1973 | optional: true 1974 | 1975 | '@rollup/rollup-linux-arm-gnueabihf@4.23.0': 1976 | optional: true 1977 | 1978 | '@rollup/rollup-linux-arm-musleabihf@4.23.0': 1979 | optional: true 1980 | 1981 | '@rollup/rollup-linux-arm64-gnu@4.23.0': 1982 | optional: true 1983 | 1984 | '@rollup/rollup-linux-arm64-musl@4.23.0': 1985 | optional: true 1986 | 1987 | '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': 1988 | optional: true 1989 | 1990 | '@rollup/rollup-linux-riscv64-gnu@4.23.0': 1991 | optional: true 1992 | 1993 | '@rollup/rollup-linux-s390x-gnu@4.23.0': 1994 | optional: true 1995 | 1996 | '@rollup/rollup-linux-x64-gnu@4.23.0': 1997 | optional: true 1998 | 1999 | '@rollup/rollup-linux-x64-musl@4.23.0': 2000 | optional: true 2001 | 2002 | '@rollup/rollup-win32-arm64-msvc@4.23.0': 2003 | optional: true 2004 | 2005 | '@rollup/rollup-win32-ia32-msvc@4.23.0': 2006 | optional: true 2007 | 2008 | '@rollup/rollup-win32-x64-msvc@4.23.0': 2009 | optional: true 2010 | 2011 | '@tsconfig/node14@14.1.2': {} 2012 | 2013 | '@types/estree@1.0.6': {} 2014 | 2015 | '@types/mock-fs@4.13.4': 2016 | dependencies: 2017 | '@types/node': 16.18.82 2018 | 2019 | '@types/node@12.20.55': {} 2020 | 2021 | '@types/node@16.18.82': {} 2022 | 2023 | '@types/picomatch@2.3.4': {} 2024 | 2025 | '@types/semver@7.5.7': {} 2026 | 2027 | '@types/supports-color@8.1.3': {} 2028 | 2029 | '@vitest/expect@2.1.1': 2030 | dependencies: 2031 | '@vitest/spy': 2.1.1 2032 | '@vitest/utils': 2.1.1 2033 | chai: 5.1.1 2034 | tinyrainbow: 1.2.0 2035 | 2036 | '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.1.3(@types/node@16.18.82))': 2037 | dependencies: 2038 | '@vitest/spy': 2.1.1 2039 | estree-walker: 3.0.3 2040 | magic-string: 0.30.11 2041 | optionalDependencies: 2042 | vite: 5.1.3(@types/node@16.18.82) 2043 | 2044 | '@vitest/pretty-format@2.1.1': 2045 | dependencies: 2046 | tinyrainbow: 1.2.0 2047 | 2048 | '@vitest/runner@2.1.1': 2049 | dependencies: 2050 | '@vitest/utils': 2.1.1 2051 | pathe: 1.1.2 2052 | 2053 | '@vitest/snapshot@2.1.1': 2054 | dependencies: 2055 | '@vitest/pretty-format': 2.1.1 2056 | magic-string: 0.30.11 2057 | pathe: 1.1.2 2058 | 2059 | '@vitest/spy@2.1.1': 2060 | dependencies: 2061 | tinyspy: 3.0.2 2062 | 2063 | '@vitest/utils@2.1.1': 2064 | dependencies: 2065 | '@vitest/pretty-format': 2.1.1 2066 | loupe: 3.1.1 2067 | tinyrainbow: 1.2.0 2068 | 2069 | ansi-colors@4.1.3: {} 2070 | 2071 | ansi-regex@5.0.1: {} 2072 | 2073 | ansi-regex@6.0.1: {} 2074 | 2075 | ansi-styles@4.3.0: 2076 | dependencies: 2077 | color-convert: 2.0.1 2078 | 2079 | ansi-styles@6.2.1: {} 2080 | 2081 | any-promise@1.3.0: {} 2082 | 2083 | anymatch@3.1.3: 2084 | dependencies: 2085 | normalize-path: 3.0.0 2086 | picomatch: 2.3.1 2087 | 2088 | argparse@1.0.10: 2089 | dependencies: 2090 | sprintf-js: 1.0.3 2091 | 2092 | array-union@2.1.0: {} 2093 | 2094 | assertion-error@2.0.1: {} 2095 | 2096 | balanced-match@1.0.2: {} 2097 | 2098 | better-path-resolve@1.0.0: 2099 | dependencies: 2100 | is-windows: 1.0.2 2101 | 2102 | binary-extensions@2.2.0: {} 2103 | 2104 | brace-expansion@2.0.1: 2105 | dependencies: 2106 | balanced-match: 1.0.2 2107 | 2108 | braces@3.0.2: 2109 | dependencies: 2110 | fill-range: 7.0.1 2111 | 2112 | bundle-require@5.0.0(esbuild@0.23.1): 2113 | dependencies: 2114 | esbuild: 0.23.1 2115 | load-tsconfig: 0.2.5 2116 | 2117 | cac@6.7.14: {} 2118 | 2119 | chai@5.1.1: 2120 | dependencies: 2121 | assertion-error: 2.0.1 2122 | check-error: 2.1.1 2123 | deep-eql: 5.0.2 2124 | loupe: 3.1.1 2125 | pathval: 2.0.0 2126 | 2127 | changesets-changelog-clean@1.3.0: 2128 | dependencies: 2129 | '@changesets/get-github-info': 0.6.0 2130 | '@changesets/types': 6.0.0 2131 | transitivePeerDependencies: 2132 | - encoding 2133 | 2134 | chardet@0.7.0: {} 2135 | 2136 | check-error@2.1.1: {} 2137 | 2138 | chokidar@3.6.0: 2139 | dependencies: 2140 | anymatch: 3.1.3 2141 | braces: 3.0.2 2142 | glob-parent: 5.1.2 2143 | is-binary-path: 2.1.0 2144 | is-glob: 4.0.3 2145 | normalize-path: 3.0.0 2146 | readdirp: 3.6.0 2147 | optionalDependencies: 2148 | fsevents: 2.3.3 2149 | 2150 | ci-info@3.9.0: {} 2151 | 2152 | clipanion@3.2.1(typanion@3.14.0): 2153 | dependencies: 2154 | typanion: 3.14.0 2155 | 2156 | color-convert@2.0.1: 2157 | dependencies: 2158 | color-name: 1.1.4 2159 | 2160 | color-name@1.1.4: {} 2161 | 2162 | commander@4.1.1: {} 2163 | 2164 | consola@3.2.3: {} 2165 | 2166 | cross-spawn@5.1.0: 2167 | dependencies: 2168 | lru-cache: 4.1.5 2169 | shebang-command: 1.2.0 2170 | which: 1.3.1 2171 | 2172 | cross-spawn@7.0.3: 2173 | dependencies: 2174 | path-key: 3.1.1 2175 | shebang-command: 2.0.0 2176 | which: 2.0.2 2177 | 2178 | dataloader@1.4.0: {} 2179 | 2180 | debug@4.3.7(supports-color@9.4.0): 2181 | dependencies: 2182 | ms: 2.1.3 2183 | optionalDependencies: 2184 | supports-color: 9.4.0 2185 | 2186 | deep-eql@5.0.2: {} 2187 | 2188 | detect-indent@6.1.0: {} 2189 | 2190 | dir-glob@3.0.1: 2191 | dependencies: 2192 | path-type: 4.0.0 2193 | 2194 | eastasianwidth@0.2.0: {} 2195 | 2196 | emoji-regex@8.0.0: {} 2197 | 2198 | emoji-regex@9.2.2: {} 2199 | 2200 | enquirer@2.4.1: 2201 | dependencies: 2202 | ansi-colors: 4.1.3 2203 | strip-ansi: 6.0.1 2204 | 2205 | esbuild@0.19.12: 2206 | optionalDependencies: 2207 | '@esbuild/aix-ppc64': 0.19.12 2208 | '@esbuild/android-arm': 0.19.12 2209 | '@esbuild/android-arm64': 0.19.12 2210 | '@esbuild/android-x64': 0.19.12 2211 | '@esbuild/darwin-arm64': 0.19.12 2212 | '@esbuild/darwin-x64': 0.19.12 2213 | '@esbuild/freebsd-arm64': 0.19.12 2214 | '@esbuild/freebsd-x64': 0.19.12 2215 | '@esbuild/linux-arm': 0.19.12 2216 | '@esbuild/linux-arm64': 0.19.12 2217 | '@esbuild/linux-ia32': 0.19.12 2218 | '@esbuild/linux-loong64': 0.19.12 2219 | '@esbuild/linux-mips64el': 0.19.12 2220 | '@esbuild/linux-ppc64': 0.19.12 2221 | '@esbuild/linux-riscv64': 0.19.12 2222 | '@esbuild/linux-s390x': 0.19.12 2223 | '@esbuild/linux-x64': 0.19.12 2224 | '@esbuild/netbsd-x64': 0.19.12 2225 | '@esbuild/openbsd-x64': 0.19.12 2226 | '@esbuild/sunos-x64': 0.19.12 2227 | '@esbuild/win32-arm64': 0.19.12 2228 | '@esbuild/win32-ia32': 0.19.12 2229 | '@esbuild/win32-x64': 0.19.12 2230 | 2231 | esbuild@0.23.1: 2232 | optionalDependencies: 2233 | '@esbuild/aix-ppc64': 0.23.1 2234 | '@esbuild/android-arm': 0.23.1 2235 | '@esbuild/android-arm64': 0.23.1 2236 | '@esbuild/android-x64': 0.23.1 2237 | '@esbuild/darwin-arm64': 0.23.1 2238 | '@esbuild/darwin-x64': 0.23.1 2239 | '@esbuild/freebsd-arm64': 0.23.1 2240 | '@esbuild/freebsd-x64': 0.23.1 2241 | '@esbuild/linux-arm': 0.23.1 2242 | '@esbuild/linux-arm64': 0.23.1 2243 | '@esbuild/linux-ia32': 0.23.1 2244 | '@esbuild/linux-loong64': 0.23.1 2245 | '@esbuild/linux-mips64el': 0.23.1 2246 | '@esbuild/linux-ppc64': 0.23.1 2247 | '@esbuild/linux-riscv64': 0.23.1 2248 | '@esbuild/linux-s390x': 0.23.1 2249 | '@esbuild/linux-x64': 0.23.1 2250 | '@esbuild/netbsd-x64': 0.23.1 2251 | '@esbuild/openbsd-arm64': 0.23.1 2252 | '@esbuild/openbsd-x64': 0.23.1 2253 | '@esbuild/sunos-x64': 0.23.1 2254 | '@esbuild/win32-arm64': 0.23.1 2255 | '@esbuild/win32-ia32': 0.23.1 2256 | '@esbuild/win32-x64': 0.23.1 2257 | 2258 | esprima@4.0.1: {} 2259 | 2260 | estree-walker@3.0.3: 2261 | dependencies: 2262 | '@types/estree': 1.0.6 2263 | 2264 | execa@5.1.1: 2265 | dependencies: 2266 | cross-spawn: 7.0.3 2267 | get-stream: 6.0.1 2268 | human-signals: 2.1.0 2269 | is-stream: 2.0.1 2270 | merge-stream: 2.0.0 2271 | npm-run-path: 4.0.1 2272 | onetime: 5.1.2 2273 | signal-exit: 3.0.7 2274 | strip-final-newline: 2.0.0 2275 | 2276 | extendable-error@0.1.7: {} 2277 | 2278 | external-editor@3.1.0: 2279 | dependencies: 2280 | chardet: 0.7.0 2281 | iconv-lite: 0.4.24 2282 | tmp: 0.0.33 2283 | 2284 | fast-glob@3.3.2: 2285 | dependencies: 2286 | '@nodelib/fs.stat': 2.0.5 2287 | '@nodelib/fs.walk': 1.2.8 2288 | glob-parent: 5.1.2 2289 | merge2: 1.4.1 2290 | micromatch: 4.0.5 2291 | 2292 | fastq@1.17.1: 2293 | dependencies: 2294 | reusify: 1.0.4 2295 | 2296 | fdir@6.4.0(picomatch@4.0.2): 2297 | optionalDependencies: 2298 | picomatch: 4.0.2 2299 | 2300 | fill-range@7.0.1: 2301 | dependencies: 2302 | to-regex-range: 5.0.1 2303 | 2304 | find-up@4.1.0: 2305 | dependencies: 2306 | locate-path: 5.0.0 2307 | path-exists: 4.0.0 2308 | 2309 | foreground-child@3.1.1: 2310 | dependencies: 2311 | cross-spawn: 7.0.3 2312 | signal-exit: 4.1.0 2313 | 2314 | fs-extra@7.0.1: 2315 | dependencies: 2316 | graceful-fs: 4.2.11 2317 | jsonfile: 4.0.0 2318 | universalify: 0.1.2 2319 | 2320 | fs-extra@8.1.0: 2321 | dependencies: 2322 | graceful-fs: 4.2.11 2323 | jsonfile: 4.0.0 2324 | universalify: 0.1.2 2325 | 2326 | fsevents@2.3.3: 2327 | optional: true 2328 | 2329 | get-func-name@2.0.2: {} 2330 | 2331 | get-stream@6.0.1: {} 2332 | 2333 | get-tsconfig@4.8.1: 2334 | dependencies: 2335 | resolve-pkg-maps: 1.0.0 2336 | 2337 | glob-parent@5.1.2: 2338 | dependencies: 2339 | is-glob: 4.0.3 2340 | 2341 | glob@10.3.10: 2342 | dependencies: 2343 | foreground-child: 3.1.1 2344 | jackspeak: 2.3.6 2345 | minimatch: 9.0.3 2346 | minipass: 7.0.4 2347 | path-scurry: 1.10.1 2348 | 2349 | globby@11.1.0: 2350 | dependencies: 2351 | array-union: 2.1.0 2352 | dir-glob: 3.0.1 2353 | fast-glob: 3.3.2 2354 | ignore: 5.3.1 2355 | merge2: 1.4.1 2356 | slash: 3.0.0 2357 | 2358 | graceful-fs@4.2.11: {} 2359 | 2360 | human-id@1.0.2: {} 2361 | 2362 | human-signals@2.1.0: {} 2363 | 2364 | hyperdyperid@1.2.0: {} 2365 | 2366 | iconv-lite@0.4.24: 2367 | dependencies: 2368 | safer-buffer: 2.1.2 2369 | 2370 | ignore@5.3.1: {} 2371 | 2372 | is-binary-path@2.1.0: 2373 | dependencies: 2374 | binary-extensions: 2.2.0 2375 | 2376 | is-extglob@2.1.1: {} 2377 | 2378 | is-fullwidth-code-point@3.0.0: {} 2379 | 2380 | is-glob@4.0.3: 2381 | dependencies: 2382 | is-extglob: 2.1.1 2383 | 2384 | is-number@7.0.0: {} 2385 | 2386 | is-stream@2.0.1: {} 2387 | 2388 | is-subdir@1.2.0: 2389 | dependencies: 2390 | better-path-resolve: 1.0.0 2391 | 2392 | is-windows@1.0.2: {} 2393 | 2394 | isexe@2.0.0: {} 2395 | 2396 | jackspeak@2.3.6: 2397 | dependencies: 2398 | '@isaacs/cliui': 8.0.2 2399 | optionalDependencies: 2400 | '@pkgjs/parseargs': 0.11.0 2401 | 2402 | joycon@3.1.1: {} 2403 | 2404 | js-yaml@3.14.1: 2405 | dependencies: 2406 | argparse: 1.0.10 2407 | esprima: 4.0.1 2408 | 2409 | jsonfile@4.0.0: 2410 | optionalDependencies: 2411 | graceful-fs: 4.2.11 2412 | 2413 | lilconfig@3.1.2: {} 2414 | 2415 | lines-and-columns@1.2.4: {} 2416 | 2417 | load-tsconfig@0.2.5: {} 2418 | 2419 | locate-path@5.0.0: 2420 | dependencies: 2421 | p-locate: 4.1.0 2422 | 2423 | lodash.sortby@4.7.0: {} 2424 | 2425 | lodash.startcase@4.4.0: {} 2426 | 2427 | loupe@3.1.1: 2428 | dependencies: 2429 | get-func-name: 2.0.2 2430 | 2431 | lru-cache@10.2.0: {} 2432 | 2433 | lru-cache@4.1.5: 2434 | dependencies: 2435 | pseudomap: 1.0.2 2436 | yallist: 2.1.2 2437 | 2438 | lru-cache@6.0.0: 2439 | dependencies: 2440 | yallist: 4.0.0 2441 | 2442 | magic-string@0.30.11: 2443 | dependencies: 2444 | '@jridgewell/sourcemap-codec': 1.5.0 2445 | 2446 | memfs@4.12.0: 2447 | dependencies: 2448 | '@jsonjoy.com/json-pack': 1.1.0(tslib@2.7.0) 2449 | '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) 2450 | tree-dump: 1.0.2(tslib@2.7.0) 2451 | tslib: 2.7.0 2452 | 2453 | merge-stream@2.0.0: {} 2454 | 2455 | merge2@1.4.1: {} 2456 | 2457 | micromatch@4.0.5: 2458 | dependencies: 2459 | braces: 3.0.2 2460 | picomatch: 2.3.1 2461 | 2462 | mimic-fn@2.1.0: {} 2463 | 2464 | minimatch@9.0.3: 2465 | dependencies: 2466 | brace-expansion: 2.0.1 2467 | 2468 | minipass@7.0.4: {} 2469 | 2470 | mri@1.2.0: {} 2471 | 2472 | ms@2.1.3: {} 2473 | 2474 | mz@2.7.0: 2475 | dependencies: 2476 | any-promise: 1.3.0 2477 | object-assign: 4.1.1 2478 | thenify-all: 1.6.0 2479 | 2480 | nanoid@3.3.7: {} 2481 | 2482 | node-fetch@2.7.0: 2483 | dependencies: 2484 | whatwg-url: 5.0.0 2485 | 2486 | normalize-path@3.0.0: {} 2487 | 2488 | npm-run-path@4.0.1: 2489 | dependencies: 2490 | path-key: 3.1.1 2491 | 2492 | object-assign@4.1.1: {} 2493 | 2494 | onetime@5.1.2: 2495 | dependencies: 2496 | mimic-fn: 2.1.0 2497 | 2498 | os-tmpdir@1.0.2: {} 2499 | 2500 | outdent@0.5.0: {} 2501 | 2502 | p-filter@2.1.0: 2503 | dependencies: 2504 | p-map: 2.1.0 2505 | 2506 | p-limit@2.3.0: 2507 | dependencies: 2508 | p-try: 2.2.0 2509 | 2510 | p-locate@4.1.0: 2511 | dependencies: 2512 | p-limit: 2.3.0 2513 | 2514 | p-map@2.1.0: {} 2515 | 2516 | p-try@2.2.0: {} 2517 | 2518 | package-manager-detector@0.2.0: {} 2519 | 2520 | parse-ms@3.0.0: {} 2521 | 2522 | path-exists@4.0.0: {} 2523 | 2524 | path-key@3.1.1: {} 2525 | 2526 | path-scurry@1.10.1: 2527 | dependencies: 2528 | lru-cache: 10.2.0 2529 | minipass: 7.0.4 2530 | 2531 | path-type@4.0.0: {} 2532 | 2533 | pathe@1.1.2: {} 2534 | 2535 | pathval@2.0.0: {} 2536 | 2537 | picocolors@1.1.0: {} 2538 | 2539 | picomatch@2.3.1: {} 2540 | 2541 | picomatch@4.0.2: {} 2542 | 2543 | pify@4.0.1: {} 2544 | 2545 | pirates@4.0.6: {} 2546 | 2547 | postcss-load-config@6.0.1(postcss@8.4.35)(tsx@4.19.1): 2548 | dependencies: 2549 | lilconfig: 3.1.2 2550 | optionalDependencies: 2551 | postcss: 8.4.35 2552 | tsx: 4.19.1 2553 | 2554 | postcss@8.4.35: 2555 | dependencies: 2556 | nanoid: 3.3.7 2557 | picocolors: 1.1.0 2558 | source-map-js: 1.0.2 2559 | 2560 | prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.2): 2561 | dependencies: 2562 | prettier: 3.3.3 2563 | typescript: 5.6.2 2564 | 2565 | prettier@2.8.8: {} 2566 | 2567 | prettier@3.3.3: {} 2568 | 2569 | pretty-bytes@6.1.1: {} 2570 | 2571 | pretty-ms@8.0.0: 2572 | dependencies: 2573 | parse-ms: 3.0.0 2574 | 2575 | pseudomap@1.0.2: {} 2576 | 2577 | punycode@2.3.1: {} 2578 | 2579 | queue-microtask@1.2.3: {} 2580 | 2581 | read-yaml-file@1.1.0: 2582 | dependencies: 2583 | graceful-fs: 4.2.11 2584 | js-yaml: 3.14.1 2585 | pify: 4.0.1 2586 | strip-bom: 3.0.0 2587 | 2588 | readdirp@3.6.0: 2589 | dependencies: 2590 | picomatch: 2.3.1 2591 | 2592 | regenerator-runtime@0.14.1: {} 2593 | 2594 | resolve-from@5.0.0: {} 2595 | 2596 | resolve-pkg-maps@1.0.0: {} 2597 | 2598 | reusify@1.0.4: {} 2599 | 2600 | rollup@4.23.0: 2601 | dependencies: 2602 | '@types/estree': 1.0.6 2603 | optionalDependencies: 2604 | '@rollup/rollup-android-arm-eabi': 4.23.0 2605 | '@rollup/rollup-android-arm64': 4.23.0 2606 | '@rollup/rollup-darwin-arm64': 4.23.0 2607 | '@rollup/rollup-darwin-x64': 4.23.0 2608 | '@rollup/rollup-linux-arm-gnueabihf': 4.23.0 2609 | '@rollup/rollup-linux-arm-musleabihf': 4.23.0 2610 | '@rollup/rollup-linux-arm64-gnu': 4.23.0 2611 | '@rollup/rollup-linux-arm64-musl': 4.23.0 2612 | '@rollup/rollup-linux-powerpc64le-gnu': 4.23.0 2613 | '@rollup/rollup-linux-riscv64-gnu': 4.23.0 2614 | '@rollup/rollup-linux-s390x-gnu': 4.23.0 2615 | '@rollup/rollup-linux-x64-gnu': 4.23.0 2616 | '@rollup/rollup-linux-x64-musl': 4.23.0 2617 | '@rollup/rollup-win32-arm64-msvc': 4.23.0 2618 | '@rollup/rollup-win32-ia32-msvc': 4.23.0 2619 | '@rollup/rollup-win32-x64-msvc': 4.23.0 2620 | fsevents: 2.3.3 2621 | 2622 | run-parallel@1.2.0: 2623 | dependencies: 2624 | queue-microtask: 1.2.3 2625 | 2626 | safer-buffer@2.1.2: {} 2627 | 2628 | semver@7.6.0: 2629 | dependencies: 2630 | lru-cache: 6.0.0 2631 | 2632 | shebang-command@1.2.0: 2633 | dependencies: 2634 | shebang-regex: 1.0.0 2635 | 2636 | shebang-command@2.0.0: 2637 | dependencies: 2638 | shebang-regex: 3.0.0 2639 | 2640 | shebang-regex@1.0.0: {} 2641 | 2642 | shebang-regex@3.0.0: {} 2643 | 2644 | siginfo@2.0.0: {} 2645 | 2646 | signal-exit@3.0.7: {} 2647 | 2648 | signal-exit@4.1.0: {} 2649 | 2650 | slash@3.0.0: {} 2651 | 2652 | source-map-js@1.0.2: {} 2653 | 2654 | source-map@0.8.0-beta.0: 2655 | dependencies: 2656 | whatwg-url: 7.1.0 2657 | 2658 | spawndamnit@2.0.0: 2659 | dependencies: 2660 | cross-spawn: 5.1.0 2661 | signal-exit: 3.0.7 2662 | 2663 | sprintf-js@1.0.3: {} 2664 | 2665 | stackback@0.0.2: {} 2666 | 2667 | std-env@3.7.0: {} 2668 | 2669 | string-width@4.2.3: 2670 | dependencies: 2671 | emoji-regex: 8.0.0 2672 | is-fullwidth-code-point: 3.0.0 2673 | strip-ansi: 6.0.1 2674 | 2675 | string-width@5.1.2: 2676 | dependencies: 2677 | eastasianwidth: 0.2.0 2678 | emoji-regex: 9.2.2 2679 | strip-ansi: 7.1.0 2680 | 2681 | strip-ansi@6.0.1: 2682 | dependencies: 2683 | ansi-regex: 5.0.1 2684 | 2685 | strip-ansi@7.1.0: 2686 | dependencies: 2687 | ansi-regex: 6.0.1 2688 | 2689 | strip-bom@3.0.0: {} 2690 | 2691 | strip-final-newline@2.0.0: {} 2692 | 2693 | sucrase@3.35.0: 2694 | dependencies: 2695 | '@jridgewell/gen-mapping': 0.3.3 2696 | commander: 4.1.1 2697 | glob: 10.3.10 2698 | lines-and-columns: 1.2.4 2699 | mz: 2.7.0 2700 | pirates: 4.0.6 2701 | ts-interface-checker: 0.1.13 2702 | 2703 | supports-color@9.4.0: {} 2704 | 2705 | term-size@2.2.1: {} 2706 | 2707 | thenify-all@1.6.0: 2708 | dependencies: 2709 | thenify: 3.3.1 2710 | 2711 | thenify@3.3.1: 2712 | dependencies: 2713 | any-promise: 1.3.0 2714 | 2715 | thingies@1.21.0(tslib@2.7.0): 2716 | dependencies: 2717 | tslib: 2.7.0 2718 | 2719 | tinybench@2.9.0: {} 2720 | 2721 | tinyexec@0.3.0: {} 2722 | 2723 | tinyglobby@0.2.9: 2724 | dependencies: 2725 | fdir: 6.4.0(picomatch@4.0.2) 2726 | picomatch: 4.0.2 2727 | 2728 | tinypool@1.0.1: {} 2729 | 2730 | tinyrainbow@1.2.0: {} 2731 | 2732 | tinyspy@3.0.2: {} 2733 | 2734 | tmp@0.0.33: 2735 | dependencies: 2736 | os-tmpdir: 1.0.2 2737 | 2738 | to-regex-range@5.0.1: 2739 | dependencies: 2740 | is-number: 7.0.0 2741 | 2742 | tr46@0.0.3: {} 2743 | 2744 | tr46@1.0.1: 2745 | dependencies: 2746 | punycode: 2.3.1 2747 | 2748 | tree-dump@1.0.2(tslib@2.7.0): 2749 | dependencies: 2750 | tslib: 2.7.0 2751 | 2752 | tree-kill@1.2.2: {} 2753 | 2754 | ts-interface-checker@0.1.13: {} 2755 | 2756 | tslib@2.7.0: {} 2757 | 2758 | tsup@8.3.0(postcss@8.4.35)(supports-color@9.4.0)(tsx@4.19.1)(typescript@5.6.2): 2759 | dependencies: 2760 | bundle-require: 5.0.0(esbuild@0.23.1) 2761 | cac: 6.7.14 2762 | chokidar: 3.6.0 2763 | consola: 3.2.3 2764 | debug: 4.3.7(supports-color@9.4.0) 2765 | esbuild: 0.23.1 2766 | execa: 5.1.1 2767 | joycon: 3.1.1 2768 | picocolors: 1.1.0 2769 | postcss-load-config: 6.0.1(postcss@8.4.35)(tsx@4.19.1) 2770 | resolve-from: 5.0.0 2771 | rollup: 4.23.0 2772 | source-map: 0.8.0-beta.0 2773 | sucrase: 3.35.0 2774 | tinyglobby: 0.2.9 2775 | tree-kill: 1.2.2 2776 | optionalDependencies: 2777 | postcss: 8.4.35 2778 | typescript: 5.6.2 2779 | transitivePeerDependencies: 2780 | - jiti 2781 | - supports-color 2782 | - tsx 2783 | - yaml 2784 | 2785 | tsx@4.19.1: 2786 | dependencies: 2787 | esbuild: 0.23.1 2788 | get-tsconfig: 4.8.1 2789 | optionalDependencies: 2790 | fsevents: 2.3.3 2791 | 2792 | typanion@3.14.0: {} 2793 | 2794 | typescript@5.6.2: {} 2795 | 2796 | universalify@0.1.2: {} 2797 | 2798 | vite-node@2.1.1(@types/node@16.18.82)(supports-color@9.4.0): 2799 | dependencies: 2800 | cac: 6.7.14 2801 | debug: 4.3.7(supports-color@9.4.0) 2802 | pathe: 1.1.2 2803 | vite: 5.1.3(@types/node@16.18.82) 2804 | transitivePeerDependencies: 2805 | - '@types/node' 2806 | - less 2807 | - lightningcss 2808 | - sass 2809 | - stylus 2810 | - sugarss 2811 | - supports-color 2812 | - terser 2813 | 2814 | vite@5.1.3(@types/node@16.18.82): 2815 | dependencies: 2816 | esbuild: 0.19.12 2817 | postcss: 8.4.35 2818 | rollup: 4.23.0 2819 | optionalDependencies: 2820 | '@types/node': 16.18.82 2821 | fsevents: 2.3.3 2822 | 2823 | vitest@2.1.1(@types/node@16.18.82)(supports-color@9.4.0): 2824 | dependencies: 2825 | '@vitest/expect': 2.1.1 2826 | '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.1.3(@types/node@16.18.82)) 2827 | '@vitest/pretty-format': 2.1.1 2828 | '@vitest/runner': 2.1.1 2829 | '@vitest/snapshot': 2.1.1 2830 | '@vitest/spy': 2.1.1 2831 | '@vitest/utils': 2.1.1 2832 | chai: 5.1.1 2833 | debug: 4.3.7(supports-color@9.4.0) 2834 | magic-string: 0.30.11 2835 | pathe: 1.1.2 2836 | std-env: 3.7.0 2837 | tinybench: 2.9.0 2838 | tinyexec: 0.3.0 2839 | tinypool: 1.0.1 2840 | tinyrainbow: 1.2.0 2841 | vite: 5.1.3(@types/node@16.18.82) 2842 | vite-node: 2.1.1(@types/node@16.18.82)(supports-color@9.4.0) 2843 | why-is-node-running: 2.3.0 2844 | optionalDependencies: 2845 | '@types/node': 16.18.82 2846 | transitivePeerDependencies: 2847 | - less 2848 | - lightningcss 2849 | - msw 2850 | - sass 2851 | - stylus 2852 | - sugarss 2853 | - supports-color 2854 | - terser 2855 | 2856 | webidl-conversions@3.0.1: {} 2857 | 2858 | webidl-conversions@4.0.2: {} 2859 | 2860 | whatwg-url@5.0.0: 2861 | dependencies: 2862 | tr46: 0.0.3 2863 | webidl-conversions: 3.0.1 2864 | 2865 | whatwg-url@7.1.0: 2866 | dependencies: 2867 | lodash.sortby: 4.7.0 2868 | tr46: 1.0.1 2869 | webidl-conversions: 4.0.2 2870 | 2871 | which@1.3.1: 2872 | dependencies: 2873 | isexe: 2.0.0 2874 | 2875 | which@2.0.2: 2876 | dependencies: 2877 | isexe: 2.0.0 2878 | 2879 | why-is-node-running@2.3.0: 2880 | dependencies: 2881 | siginfo: 2.0.0 2882 | stackback: 0.0.2 2883 | 2884 | wrap-ansi@7.0.0: 2885 | dependencies: 2886 | ansi-styles: 4.3.0 2887 | string-width: 4.2.3 2888 | strip-ansi: 6.0.1 2889 | 2890 | wrap-ansi@8.1.0: 2891 | dependencies: 2892 | ansi-styles: 6.2.1 2893 | string-width: 5.1.2 2894 | strip-ansi: 7.1.0 2895 | 2896 | yallist@2.1.2: {} 2897 | 2898 | yallist@4.0.0: {} 2899 | -------------------------------------------------------------------------------- /src/__test__/getMockedFileStructure.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { vi } from 'vitest'; 3 | import { DEFAULT_GLOBS_FILE_PATH } from '../shared.js'; 4 | 5 | export async function getMockedFileStructure(): Promise> { 6 | const actualFs = await vi.importActual('fs/promises'); 7 | const defaultGlobs = (await actualFs.readFile(DEFAULT_GLOBS_FILE_PATH)).toString(); 8 | 9 | return { 10 | [DEFAULT_GLOBS_FILE_PATH]: defaultGlobs, 11 | node_modules: { 12 | dep1: { 13 | __tests__: { 14 | 'test1.js': '.', 15 | 'test2.js': '.', 16 | }, 17 | 'a-dir': { 18 | 'doc.md': '.', 19 | }, 20 | '.npmrc': '.', 21 | }, 22 | dep2: { 23 | 'CHANGELOG.md': '.', 24 | 'file.js': '.', 25 | }, 26 | dep3: { 27 | deeply: { 28 | nested: { 29 | 'file.ext': '.', 30 | }, 31 | }, 32 | }, 33 | dep4: { 34 | 'nonDefaultFile.ext': '.', 35 | }, 36 | }, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/__test__/path.serializer.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest'; 2 | 3 | function normalizePathPart(str: string) { 4 | return str.replace(/\\/g, '/'); // replace windows backslashes 5 | } 6 | 7 | function normalizePath(str: string) { 8 | const normalizedCwd = normalizePathPart(process.cwd()); 9 | const normalizedStr = normalizePathPart(str); 10 | return normalizedStr.replace(normalizedCwd, ''); 11 | } 12 | 13 | function shouldNormalizePath(val: any) { 14 | if (typeof val === 'string') { 15 | return normalizePath(val) !== val; 16 | } 17 | 18 | return false; 19 | } 20 | 21 | type Serializer = Parameters[0]; 22 | 23 | export const pathSerializer: Serializer = { 24 | serialize(val, config, indentation, depth, refs, printer) { 25 | if (typeof val === 'string') { 26 | const normalizedVal = normalizePath(val); 27 | return printer(normalizedVal, config, indentation, depth, refs); 28 | } 29 | 30 | if (typeof val === 'object') { 31 | const normalizedVal = Object.fromEntries( 32 | Object.entries(val).map(([key, value]) => [ 33 | key, 34 | typeof value === 'string' ? normalizePath(value) : value, 35 | ]) 36 | ); 37 | 38 | return printer(normalizedVal, config, indentation, depth, refs); 39 | } 40 | 41 | return val; 42 | }, 43 | test(val) { 44 | return shouldNormalizePath(val); 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/analyze.test.ts: -------------------------------------------------------------------------------- 1 | import { vol } from 'memfs'; 2 | import { beforeEach, describe, expect, it } from 'vitest'; 3 | import { getMockedFileStructure } from './__test__/getMockedFileStructure.js'; 4 | import { analyze } from './analyze.js'; 5 | 6 | const fileStructure = await getMockedFileStructure(); 7 | 8 | beforeEach(async () => { 9 | vol.fromNestedJSON(fileStructure); 10 | }); 11 | 12 | describe(analyze.name, () => { 13 | it('returns information about the files that would be removed', async () => { 14 | const result = await analyze(); 15 | expect(result).toMatchInlineSnapshot(` 16 | [ 17 | { 18 | "filePath": "/node_modules/dep1/.npmrc", 19 | "includedByDefault": true, 20 | "includedByGlobs": [ 21 | { 22 | "derived": "/node_modules/**/.npmrc", 23 | "original": ".npmrc", 24 | }, 25 | { 26 | "derived": "/node_modules/**/.npmrc", 27 | "original": ".npmrc", 28 | }, 29 | ], 30 | }, 31 | { 32 | "filePath": "/node_modules/dep2/CHANGELOG.md", 33 | "includedByDefault": true, 34 | "includedByGlobs": [ 35 | { 36 | "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", 37 | "original": "*.@(md|mkd|markdown|mdown)", 38 | }, 39 | { 40 | "derived": "/node_modules/**/changelog*(.*)", 41 | "original": "changelog*(.*)", 42 | }, 43 | ], 44 | }, 45 | { 46 | "filePath": "/node_modules/dep1/__tests__/test1.js", 47 | "includedByDefault": true, 48 | "includedByGlobs": [ 49 | { 50 | "derived": "/node_modules/**/__tests__/**", 51 | "original": "__tests__/", 52 | }, 53 | ], 54 | }, 55 | { 56 | "filePath": "/node_modules/dep1/__tests__/test2.js", 57 | "includedByDefault": true, 58 | "includedByGlobs": [ 59 | { 60 | "derived": "/node_modules/**/__tests__/**", 61 | "original": "__tests__/", 62 | }, 63 | ], 64 | }, 65 | { 66 | "filePath": "/node_modules/dep1/a-dir/doc.md", 67 | "includedByDefault": true, 68 | "includedByGlobs": [ 69 | { 70 | "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", 71 | "original": "*.@(md|mkd|markdown|mdown)", 72 | }, 73 | ], 74 | }, 75 | ] 76 | `); 77 | }); 78 | 79 | it('accepts custom globs', async () => { 80 | const result = await analyze({ globs: ['**/nonDefaultFile.ext'] }); 81 | expect(result).toMatchInlineSnapshot(` 82 | [ 83 | { 84 | "filePath": "/node_modules/dep1/.npmrc", 85 | "includedByDefault": true, 86 | "includedByGlobs": [ 87 | { 88 | "derived": "/node_modules/**/.npmrc", 89 | "original": ".npmrc", 90 | }, 91 | { 92 | "derived": "/node_modules/**/.npmrc", 93 | "original": ".npmrc", 94 | }, 95 | ], 96 | }, 97 | { 98 | "filePath": "/node_modules/dep2/CHANGELOG.md", 99 | "includedByDefault": true, 100 | "includedByGlobs": [ 101 | { 102 | "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", 103 | "original": "*.@(md|mkd|markdown|mdown)", 104 | }, 105 | { 106 | "derived": "/node_modules/**/changelog*(.*)", 107 | "original": "changelog*(.*)", 108 | }, 109 | ], 110 | }, 111 | { 112 | "filePath": "/node_modules/dep4/nonDefaultFile.ext", 113 | "includedByDefault": false, 114 | "includedByGlobs": [ 115 | { 116 | "derived": "/node_modules/**/**/nonDefaultFile.ext", 117 | "original": "**/nonDefaultFile.ext", 118 | }, 119 | ], 120 | }, 121 | { 122 | "filePath": "/node_modules/dep1/__tests__/test1.js", 123 | "includedByDefault": true, 124 | "includedByGlobs": [ 125 | { 126 | "derived": "/node_modules/**/__tests__/**", 127 | "original": "__tests__/", 128 | }, 129 | ], 130 | }, 131 | { 132 | "filePath": "/node_modules/dep1/__tests__/test2.js", 133 | "includedByDefault": true, 134 | "includedByGlobs": [ 135 | { 136 | "derived": "/node_modules/**/__tests__/**", 137 | "original": "__tests__/", 138 | }, 139 | ], 140 | }, 141 | { 142 | "filePath": "/node_modules/dep1/a-dir/doc.md", 143 | "includedByDefault": true, 144 | "includedByGlobs": [ 145 | { 146 | "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", 147 | "original": "*.@(md|mkd|markdown|mdown)", 148 | }, 149 | ], 150 | }, 151 | ] 152 | `); 153 | }); 154 | 155 | it('allows skipping default globs', async () => { 156 | const result = await analyze({ noDefaults: true, globs: ['**/nonDefaultFile.ext'] }); 157 | expect(result).toMatchInlineSnapshot(` 158 | [ 159 | { 160 | "filePath": "/node_modules/dep4/nonDefaultFile.ext", 161 | "includedByDefault": false, 162 | "includedByGlobs": [ 163 | { 164 | "derived": "/node_modules/**/**/nonDefaultFile.ext", 165 | "original": "**/nonDefaultFile.ext", 166 | }, 167 | ], 168 | }, 169 | ] 170 | `); 171 | }); 172 | 173 | it('uses custom glob file if provided', async () => { 174 | const customGlobFile = '.custom-glob-file'; 175 | vol.fromNestedJSON({ ...fileStructure, [customGlobFile]: '**.md\n**/nonDefaultFile.ext' }); 176 | 177 | const result = await analyze({ noDefaults: true, globFile: customGlobFile }); 178 | expect(result).toMatchInlineSnapshot(` 179 | [ 180 | { 181 | "filePath": "/node_modules/dep2/CHANGELOG.md", 182 | "includedByDefault": true, 183 | "includedByGlobs": [ 184 | { 185 | "derived": "/node_modules/**/**.md", 186 | "original": "**.md", 187 | }, 188 | ], 189 | }, 190 | { 191 | "filePath": "/node_modules/dep4/nonDefaultFile.ext", 192 | "includedByDefault": false, 193 | "includedByGlobs": [ 194 | { 195 | "derived": "/node_modules/**/**/nonDefaultFile.ext", 196 | "original": "**/nonDefaultFile.ext", 197 | }, 198 | ], 199 | }, 200 | { 201 | "filePath": "/node_modules/dep1/a-dir/doc.md", 202 | "includedByDefault": true, 203 | "includedByGlobs": [ 204 | { 205 | "derived": "/node_modules/**/**.md", 206 | "original": "**.md", 207 | }, 208 | ], 209 | }, 210 | ] 211 | `); 212 | }); 213 | }); 214 | -------------------------------------------------------------------------------- /src/analyze.ts: -------------------------------------------------------------------------------- 1 | import { SharedOptions, sharedDefaultOptions } from './shared.js'; 2 | import { 3 | findFilesByGlobLists, 4 | getGlobLists, 5 | makeGlobMatcher, 6 | optimizeGlobLists, 7 | parseDefaultGlobsFile, 8 | toAbsoluteGlobLists, 9 | toPosixPath, 10 | } from './utils/glob.js'; 11 | 12 | export interface GlobVersions { 13 | /** The original glob, as provided by the glob file or user. */ 14 | original: string; 15 | /** The glob as it was derived by clean-modules and passed to picomatch. */ 16 | derived: string; 17 | } 18 | 19 | export interface AnalyzeResult { 20 | /** The absolute path to the file. */ 21 | filePath: string; 22 | /** Whether the file was included by clean-modules' default globs. */ 23 | includedByDefault: boolean; 24 | /** List of globs that included the file. */ 25 | includedByGlobs: GlobVersions[]; 26 | } 27 | 28 | export type AnalyzeOptions = SharedOptions; 29 | 30 | /** 31 | * Helps determining why a file is included by the `clean` operation without removing any files. Extra globs can be passed as positional args. 32 | * @param options analyze options 33 | * @returns list of files that were included by the clean operation and what globs they were included by 34 | */ 35 | export async function analyze(options: AnalyzeOptions = {}): Promise { 36 | const mergedOptions = { ...sharedDefaultOptions, ...options }; 37 | const { globs, noDefaults, globFile, directory } = mergedOptions; 38 | const nodeModulesPath = directory || sharedDefaultOptions.directory; 39 | 40 | const globLists = await getGlobLists({ globs, noDefaults, globFile }); 41 | const includedFiles = await findFilesByGlobLists(nodeModulesPath, globLists); 42 | 43 | const defaultGlobs = toAbsoluteGlobLists( 44 | optimizeGlobLists(await parseDefaultGlobsFile()), 45 | nodeModulesPath 46 | ); 47 | 48 | const includedByDefaultMatcher = makeGlobMatcher(defaultGlobs.included, { 49 | ignore: defaultGlobs.excluded, 50 | }); 51 | 52 | const globMatchers = globLists.included.map((glob, index) => { 53 | const absoluteGlob = toPosixPath(nodeModulesPath) + '/' + glob; 54 | const matcher = makeGlobMatcher(absoluteGlob); 55 | return { original: globLists.originalIncluded[index], derived: absoluteGlob, matcher }; 56 | }); 57 | 58 | const analyzedResults = includedFiles.map(filePath => { 59 | const includedByDefault = includedByDefaultMatcher(filePath); 60 | const includedByGlobs: { original: string; derived: string }[] = []; 61 | 62 | for (const { original, derived, matcher } of globMatchers) { 63 | if (matcher(filePath)) { 64 | includedByGlobs.push({ original, derived }); 65 | } 66 | } 67 | 68 | return { filePath, includedByDefault, includedByGlobs }; 69 | }); 70 | 71 | return analyzedResults; 72 | } 73 | -------------------------------------------------------------------------------- /src/clean.test.ts: -------------------------------------------------------------------------------- 1 | import { vol } from 'memfs'; 2 | import { beforeEach, describe, expect, it } from 'vitest'; 3 | import { getMockedFileStructure } from './__test__/getMockedFileStructure.js'; 4 | import { clean } from './clean.js'; 5 | 6 | const fileStructure = await getMockedFileStructure(); 7 | 8 | beforeEach(async () => { 9 | vol.fromNestedJSON(fileStructure); 10 | }); 11 | 12 | describe(clean.name, () => { 13 | it('cleans up expected files by default', async () => { 14 | const result = await clean(); 15 | expect(result).toMatchInlineSnapshot(` 16 | { 17 | "files": [ 18 | "/node_modules/dep1/.npmrc", 19 | "/node_modules/dep2/CHANGELOG.md", 20 | "/node_modules/dep1/__tests__/test1.js", 21 | "/node_modules/dep1/__tests__/test2.js", 22 | "/node_modules/dep1/a-dir/doc.md", 23 | ], 24 | "reducedSize": 5, 25 | "removedEmptyDirs": 3, 26 | } 27 | `); 28 | }); 29 | 30 | it('accepts custom globs', async () => { 31 | const result = await clean({ globs: ['**/nonDefaultFile.ext'] }); 32 | expect(result).toMatchInlineSnapshot(` 33 | { 34 | "files": [ 35 | "/node_modules/dep1/.npmrc", 36 | "/node_modules/dep2/CHANGELOG.md", 37 | "/node_modules/dep4/nonDefaultFile.ext", 38 | "/node_modules/dep1/__tests__/test1.js", 39 | "/node_modules/dep1/__tests__/test2.js", 40 | "/node_modules/dep1/a-dir/doc.md", 41 | ], 42 | "reducedSize": 6, 43 | "removedEmptyDirs": 4, 44 | } 45 | `); 46 | }); 47 | 48 | it('allows skipping default globs', async () => { 49 | const result = await clean({ noDefaults: true, globs: ['**/nonDefaultFile.ext'] }); 50 | expect(result).toMatchInlineSnapshot(` 51 | { 52 | "files": [ 53 | "/node_modules/dep4/nonDefaultFile.ext", 54 | ], 55 | "reducedSize": 1, 56 | "removedEmptyDirs": 1, 57 | } 58 | `); 59 | }); 60 | 61 | it('uses custom glob file if provided', async () => { 62 | const customGlobFile = '.custom-glob-file'; 63 | vol.fromNestedJSON({ ...fileStructure, [customGlobFile]: '**.md\n**/nonDefaultFile.ext' }); 64 | 65 | const result = await clean({ noDefaults: true, globFile: customGlobFile }); 66 | expect(result).toMatchInlineSnapshot(` 67 | { 68 | "files": [ 69 | "/node_modules/dep2/CHANGELOG.md", 70 | "/node_modules/dep4/nonDefaultFile.ext", 71 | "/node_modules/dep1/a-dir/doc.md", 72 | ], 73 | "reducedSize": 3, 74 | "removedEmptyDirs": 2, 75 | } 76 | `); 77 | }); 78 | 79 | it('keeps empty directories if specified', async () => { 80 | const result = await clean({ keepEmpty: true }); 81 | expect(result.removedEmptyDirs).toBe(0); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/clean.ts: -------------------------------------------------------------------------------- 1 | import { SharedOptions, sharedDefaultOptions } from './shared.js'; 2 | import { removeEmptyDirs, removeFiles } from './utils/filesystem.js'; 3 | import { findFilesByGlobLists, getGlobLists } from './utils/glob.js'; 4 | 5 | export const defaultCleanOptions: Required = { 6 | ...sharedDefaultOptions, 7 | dryRun: false, 8 | keepEmpty: false, 9 | }; 10 | 11 | export interface CleanResult { 12 | /** List of all files that were cleaned up. */ 13 | files: string[]; 14 | /** How many bytes of data that was removed when cleaning. */ 15 | reducedSize: number; 16 | /** The number of empty directories that were cleaned up after the operation. */ 17 | removedEmptyDirs: number; 18 | } 19 | 20 | export interface CleanOptions extends SharedOptions { 21 | /** Whether to skip actually deleting any files and just perform a dry run of the cleaning operation. */ 22 | dryRun?: boolean; 23 | /** Whether to keep empty directories around after cleaning. */ 24 | keepEmpty?: boolean; 25 | } 26 | 27 | /** 28 | * Removes unnecessary files to reduce the size of a node_modules directory. 29 | * @param options clean options 30 | * @returns summary of the clean operation 31 | */ 32 | export async function clean(options: CleanOptions = {}): Promise { 33 | const mergedOptions = { ...defaultCleanOptions, ...options }; 34 | const { globs, noDefaults, globFile, directory, dryRun, keepEmpty } = mergedOptions; 35 | 36 | const globLists = await getGlobLists({ globs, noDefaults, globFile }); 37 | const files = await findFilesByGlobLists(directory, globLists); 38 | const reducedSize = await removeFiles(files, { dryRun }); 39 | const removedEmptyDirs = dryRun || keepEmpty ? 0 : await removeEmptyDirs(files); 40 | 41 | return { files, reducedSize, removedEmptyDirs }; 42 | } 43 | -------------------------------------------------------------------------------- /src/cli/cli.ts: -------------------------------------------------------------------------------- 1 | import { Builtins, Cli } from 'clipanion'; 2 | import { createRequire } from 'node:module'; 3 | import path from 'node:path'; 4 | import { fileDir } from '../utils/filesystem.js'; 5 | import { AnalyzeCommand } from './commands/analyze.command.js'; 6 | import { CleanCommand } from './commands/clean.command.js'; 7 | 8 | const [_node, _app, ...args] = process.argv; 9 | const esmRequire = createRequire(import.meta.url); 10 | const cliDir = fileDir(import.meta.url); 11 | const { name, version } = esmRequire(path.resolve(cliDir, '..', '..', 'package.json')); 12 | 13 | const cli = new Cli({ 14 | binaryLabel: name, 15 | binaryVersion: version, 16 | binaryName: name, 17 | }); 18 | 19 | cli.register(Builtins.HelpCommand); 20 | cli.register(Builtins.VersionCommand); 21 | cli.register(CleanCommand); 22 | cli.register(AnalyzeCommand); 23 | 24 | cli.runExit(args); 25 | -------------------------------------------------------------------------------- /src/cli/commands/analyze.command.ts: -------------------------------------------------------------------------------- 1 | import { analyze } from '../../analyze.js'; 2 | import { BaseCommand } from '../helpers/base.command.js'; 3 | 4 | export class AnalyzeCommand extends BaseCommand { 5 | static override paths = [['analyze']]; 6 | static override usage = { 7 | description: 8 | 'Helps determining why a file is included by the clean command without removing any files. Extra globs can be passed as positional args.', 9 | }; 10 | 11 | async execute(): Promise { 12 | const analyzeResults = await analyze({ 13 | directory: this.directory, 14 | globs: this.globs, 15 | noDefaults: this.noDefaults, 16 | globFile: this.globFile, 17 | }); 18 | 19 | console.log(JSON.stringify(analyzeResults, null, 2)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/cli/commands/clean.command.ts: -------------------------------------------------------------------------------- 1 | import { Command, Option } from 'clipanion'; 2 | import prettyBytes from 'pretty-bytes'; 3 | import prettyMs from 'pretty-ms'; 4 | import { clean } from '../../clean.js'; 5 | import { BaseCommand } from '../helpers/base.command.js'; 6 | import { bold, green, makeLogger, yellow, yesOrNo } from '../utils/terminal.js'; 7 | 8 | export class CleanCommand extends BaseCommand { 9 | static override paths = [['clean'], Command.Default]; 10 | static override usage = { 11 | description: 12 | 'Default command. Removes unnecessary files to reduce the size of your node_modules directory. Extra globs can be passed as positional args.', 13 | }; 14 | 15 | keepEmpty = Option.Boolean('-k,--keep-empty', false, { 16 | description: 'Skips removing empty folders after removing contents', 17 | }); 18 | 19 | dryRun = Option.Boolean('-d,--dry-run', false, { 20 | description: 'Logs files that would be removed without removing any files', 21 | }); 22 | 23 | silent = Option.Boolean('-s,--silent', false, { 24 | description: 'Does not log anything to console (unless --json is enabled)', 25 | }); 26 | 27 | yes = Option.Boolean('-y,--yes', false, { 28 | description: 'Skips the confirmation prompt at the start of the script', 29 | }); 30 | 31 | json = Option.Boolean('-j,--json', false, { 32 | description: 'Output results as JSON', 33 | }); 34 | 35 | async execute(): Promise { 36 | const logger = makeLogger({ disabled: this.json || this.silent }); 37 | 38 | logger.log(bold('clean-modules'), this.dryRun ? yellow('(dry run)') : ''); 39 | 40 | if (!this.yes && !this.dryRun) { 41 | const warning = `\nPreparing to clean node_modules at: ${this.directory}\nAre you sure you want to continue? (Y/N) `; 42 | const confirmed = await yesOrNo(yellow(warning)); 43 | 44 | if (!confirmed) { 45 | process.exit(0); 46 | } 47 | } 48 | 49 | logger.log('\nCleaning up node_modules...'); 50 | 51 | const cleanupStart = new Date().getTime(); 52 | 53 | const { files, reducedSize, removedEmptyDirs } = await clean({ 54 | globs: this.globs, 55 | noDefaults: this.noDefaults, 56 | globFile: this.globFile, 57 | directory: this.directory, 58 | dryRun: this.dryRun, 59 | }); 60 | 61 | const cleanupDuration = new Date().getTime() - cleanupStart; 62 | logger.log(green(`Done in ${prettyMs(cleanupDuration)}!`)); 63 | 64 | if (this.json) { 65 | const output: Record = { 66 | removedFiles: files.length, 67 | reducedSize, 68 | removedEmptyDirs, 69 | duration: cleanupDuration, 70 | dryRun: this.dryRun, 71 | }; 72 | 73 | console.log(JSON.stringify(output, null, 2)); 74 | } else { 75 | logger.log(bold('\nResults:')); 76 | logger.log( 77 | '- size reduced:', 78 | green( 79 | prettyBytes(reducedSize || 0, { 80 | space: true, 81 | minimumFractionDigits: 1, 82 | maximumFractionDigits: 2, 83 | }) 84 | ) 85 | ); 86 | logger.log('- files removed:', green(files.length)); 87 | logger.log('- empty dirs removed:', green(removedEmptyDirs || 0)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/cli/helpers/base.command.ts: -------------------------------------------------------------------------------- 1 | import { Command, Option } from 'clipanion'; 2 | import { sharedDefaultOptions } from '../../shared.js'; 3 | 4 | /** 5 | * Shared options for all commands. 6 | */ 7 | export abstract class BaseCommand extends Command { 8 | directory = Option.String('-D,--directory', sharedDefaultOptions.directory, { 9 | description: 'Path to node_modules', 10 | }); 11 | 12 | globFile = Option.String('-f,--glob-file', sharedDefaultOptions.globFile, { 13 | description: 'Path to a custom glob file', 14 | }); 15 | 16 | noDefaults = Option.Boolean('-n,--no-defaults', sharedDefaultOptions.noDefaults, { 17 | description: 'Only includes/excludes globs specified by a custom glob file or CLI arguments', 18 | }); 19 | 20 | globs = Option.Rest({ name: 'globs' }); 21 | } 22 | -------------------------------------------------------------------------------- /src/cli/utils/terminal.ts: -------------------------------------------------------------------------------- 1 | import readline from 'readline'; 2 | import supportsColor from 'supports-color'; 3 | 4 | type DisabledConsole = Console; 5 | 6 | /** 7 | * Creates a conditional console logger. 8 | */ 9 | export function makeLogger({ disabled }: { disabled: boolean }): Console | DisabledConsole { 10 | if (disabled) { 11 | const noop = () => undefined; 12 | return Object.keys(console).reduce((disabledConsole, key) => { 13 | disabledConsole[key as keyof Console] = noop as any; 14 | return disabledConsole; 15 | }, {} as DisabledConsole); 16 | } 17 | 18 | return console; 19 | } 20 | 21 | /** 22 | * Prompts the user with a yes/no question and waits for the answer. 23 | */ 24 | export async function yesOrNo(query: string): Promise { 25 | const rl = readline.createInterface({ 26 | input: process.stdin, 27 | output: process.stdout, 28 | }); 29 | 30 | return new Promise(resolve => 31 | rl.question(query, (answer: string) => { 32 | rl.close(); 33 | resolve(/ye?s?/i.test(answer)); 34 | }) 35 | ); 36 | } 37 | 38 | /** 39 | * Simple string colorizer factory. 40 | */ 41 | function colorizer(colorCode: string) { 42 | if (!supportsColor.stdout) { 43 | return (text: string | number) => String(text); 44 | } 45 | 46 | return (text: string | number) => `\x1b[${colorCode}m${text}\x1b[0m`; 47 | } 48 | 49 | export const bold = colorizer('1'); 50 | export const green = colorizer('32'); 51 | export const yellow = colorizer('33'); 52 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './analyze.js'; 2 | export * from './clean.js'; 3 | export { type SharedOptions } from './shared.js'; 4 | -------------------------------------------------------------------------------- /src/shared.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fileDir } from './utils/filesystem.js'; 3 | 4 | export const DEFAULT_GLOBS_FILE_PATH = path.resolve( 5 | fileDir(import.meta.url), 6 | '..', 7 | '.cleanmodules-default' 8 | ); 9 | 10 | export interface SharedOptions { 11 | /** The directory to clean, usually node_modules. */ 12 | directory?: string; 13 | /** Path to a custom glob file. Uses `.cleanmodules` by default. */ 14 | globFile?: string; 15 | /** Whether or not to include clean-modules' default globs. */ 16 | noDefaults?: boolean; 17 | /** List of custom globs to include or exclude. */ 18 | globs?: string[] | undefined; 19 | } 20 | 21 | export const sharedDefaultOptions: Required = { 22 | directory: path.resolve(process.cwd(), 'node_modules'), 23 | globFile: '.cleanmodules', 24 | noDefaults: false, 25 | globs: [], 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/filesystem.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { vol } from 'memfs'; 3 | import path from 'path'; 4 | import { afterEach, beforeEach, describe, expect, it, MockedFunction, vi } from 'vitest'; 5 | import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; 6 | import { 7 | crawlDirFast, 8 | crawlDirWithChecks, 9 | fileExists, 10 | forEachDirentAsync, 11 | readDirectory, 12 | removeEmptyDirs, 13 | removeEmptyDirsUp, 14 | removeFiles, 15 | } from './filesystem.js'; 16 | 17 | describe('file exists', () => { 18 | beforeEach(() => { 19 | vol.fromNestedJSON({ 20 | testdir: { 21 | foo: '.', 22 | bar: '.', 23 | }, 24 | }); 25 | }); 26 | 27 | it('returns true if the file exists', async () => { 28 | const result = await fileExists('testdir/foo'); 29 | expect(result).toBe(true); 30 | }); 31 | 32 | it('returns false if the file does not exists', async () => { 33 | const result = await fileExists('testdir/foo'); 34 | expect(result).toBe(true); 35 | }); 36 | 37 | it("throws any error that isn't ENOENT", async () => { 38 | const statSpy = vi.spyOn(fs.promises, 'stat').mockImplementation(() => { 39 | throw new Error('not an ENOENT!'); 40 | }); 41 | 42 | await expect(fileExists('testdir/foo')).rejects.toThrow('not an ENOENT!'); 43 | 44 | statSpy.mockRestore(); 45 | }); 46 | }); 47 | 48 | describe('forEachDirentAsync', () => { 49 | beforeEach(() => { 50 | vol.fromNestedJSON({ 51 | testdir: { 52 | foo: { 'foo-bar': '' }, 53 | bar: '', 54 | baz: '', 55 | }, 56 | }); 57 | }); 58 | 59 | it('runs action with dirents for each item in a directory', async () => { 60 | const action = vi.fn(); 61 | await forEachDirentAsync('testdir', action); 62 | 63 | expect(action).toHaveBeenCalledTimes(3); 64 | expect(action).toHaveBeenCalledWith( 65 | expect.objectContaining({ name: 'foo' }), 66 | expect.anything(), 67 | expect.anything() 68 | ); 69 | 70 | expect(action).toHaveBeenCalledWith( 71 | expect.objectContaining({ name: 'bar' }), 72 | expect.anything(), 73 | expect.anything() 74 | ); 75 | 76 | expect(action).toHaveBeenCalledWith( 77 | expect.objectContaining({ name: 'baz' }), 78 | expect.anything(), 79 | expect.anything() 80 | ); 81 | }); 82 | }); 83 | 84 | describe('readDirectory', () => { 85 | beforeEach(() => { 86 | vol.fromNestedJSON({ 'parent/empty': { 'foo.txt': '', 'bar.txt': '' } }); 87 | }); 88 | 89 | it('returns list of files in directory', async () => { 90 | expect(await readDirectory('parent/empty')).toEqual( 91 | expect.arrayContaining(['foo.txt', 'bar.txt']) 92 | ); 93 | }); 94 | 95 | it('returns empty array if directory does not exist', async () => { 96 | expect(await readDirectory('parent/invalid')).toEqual([]); 97 | }); 98 | }); 99 | 100 | describe('removeEmptyDirsUp', () => { 101 | beforeEach(() => { 102 | vol.fromNestedJSON({ 103 | a0: { 104 | b0: { 105 | c0: { 106 | d0: { 107 | e0: {}, 108 | }, 109 | }, 110 | c1: 'its a file', 111 | }, 112 | }, 113 | }); 114 | }); 115 | 116 | it('recursively removes empty directories up in the file tree', async () => { 117 | const checkedDirs = new Set(); 118 | await removeEmptyDirsUp(checkedDirs, 'a0/b0/c0/d0/e0'); 119 | 120 | expect(Array.from(checkedDirs)).toEqual(['a0/b0/c0/d0/e0', 'a0/b0/c0/d0', 'a0/b0/c0', 'a0/b0']); 121 | 122 | // dirs no longer exist 123 | expect(fs.existsSync('a0/b0/c0/d0/e0')).toEqual(false); 124 | expect(fs.existsSync('a0/b0/c0/d0')).toEqual(false); 125 | expect(fs.existsSync('a0/b0/c0')).toEqual(false); 126 | expect(fs.existsSync('a0/b0')).toEqual(true); 127 | expect(fs.existsSync('a0')).toEqual(true); 128 | }); 129 | 130 | it('does not throw if path is invalid', async () => { 131 | const checkedDirs = new Set(); 132 | expect(async () => await removeEmptyDirsUp(checkedDirs, 'invalid/path')).not.toThrow(); 133 | }); 134 | }); 135 | 136 | describe('removeEmptyDirs', () => { 137 | beforeEach(async () => { 138 | const fileStructure = await getMockedFileStructure(); 139 | vol.fromNestedJSON(fileStructure); 140 | }); 141 | 142 | it('cleans up empty parent dirs for provided files', async () => { 143 | const filePaths = [ 144 | 'node_modules/dep1/__tests__/test1.js', 145 | 'node_modules/dep1/a-dir/doc.md', 146 | 'node_modules/dep2/CHANGELOG.md', 147 | 'node_modules/dep2/file.js', 148 | ]; 149 | 150 | // remove files before testing 151 | for (const filePath of filePaths) { 152 | fs.unlinkSync(filePath); 153 | } 154 | 155 | await removeEmptyDirs(filePaths); 156 | 157 | expect(fs.existsSync('node_modules/dep1/__tests__')).toBe(true); // not empty and not removed 158 | expect(fs.existsSync('node_modules/dep1/a-dir')).toBe(false); // empty and removed 159 | expect(fs.existsSync('node_modules/dep2')).toBe(false); // empty and removed 160 | }); 161 | 162 | it('does not throw if path is invalid', async () => { 163 | const filePaths = ['invalid/path/2', 'invalid/path/2']; 164 | expect(async () => await removeEmptyDirs(filePaths)).not.toThrow(); 165 | }); 166 | }); 167 | 168 | describe('crawlDirFast', () => { 169 | beforeEach(() => { 170 | vol.fromNestedJSON({ 171 | a0: { 172 | b0: { 173 | c0: { 174 | d0: { 175 | e0: {}, 176 | }, 177 | d1: { 178 | e0: { 179 | f0: 'f0', 180 | }, 181 | }, 182 | d2: 'd2', 183 | }, 184 | c1: 'c1', 185 | c2: 'c2', 186 | }, 187 | }, 188 | a1: 'a1', 189 | a2: 'a2', 190 | }); 191 | }); 192 | 193 | it('appends all nested file paths to the provided array', async () => { 194 | const filePaths: string[] = []; 195 | await crawlDirFast(filePaths, 'a0'); 196 | expect(filePaths).toEqual([ 197 | path.join('a0', 'b0', 'c1'), 198 | path.join('a0', 'b0', 'c2'), 199 | path.join('a0', 'b0', 'c0', 'd2'), 200 | path.join('a0', 'b0', 'c0', 'd1', 'e0', 'f0'), 201 | ]); 202 | }); 203 | 204 | it('does not throw if path is invalid', async () => { 205 | const filePaths: string[] = []; 206 | expect(async () => await crawlDirFast(filePaths, 'invalid/path')).not.toThrow(); 207 | }); 208 | }); 209 | 210 | describe('crawlDirWithChecks', () => { 211 | beforeEach(() => { 212 | vol.fromNestedJSON({ 213 | a0: { 214 | b0: { 215 | c0: { 216 | d0: { 217 | e0: {}, 218 | }, 219 | d1: { 220 | e0: { 221 | f0: 'f0', 222 | }, 223 | }, 224 | d2: 'd2', 225 | }, 226 | c1: 'c1', 227 | c2: 'c2', 228 | }, 229 | }, 230 | a1: 'a1', 231 | a2: 'a2', 232 | }); 233 | }); 234 | 235 | it('runs check functions on each nested item', async () => { 236 | const filePaths: string[] = []; 237 | const checkDir = vi.fn(() => false); 238 | const checkFile = vi.fn(() => true); 239 | 240 | await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); 241 | expect(checkDir).toHaveBeenCalledTimes(6); 242 | expect(checkFile).toHaveBeenCalledTimes(4); 243 | }); 244 | 245 | it('includes full dir without checking remaining items if checkDir returns true', async () => { 246 | const filePaths: string[] = []; 247 | const checkDir = vi.fn(() => true); 248 | const checkFile = vi.fn(() => true); 249 | 250 | await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); 251 | 252 | expect(filePaths).toEqual([ 253 | path.join('a0', 'b0', 'c1'), 254 | path.join('a0', 'b0', 'c2'), 255 | path.join('a0', 'b0', 'c0', 'd2'), 256 | path.join('a0', 'b0', 'c0', 'd1', 'e0', 'f0'), 257 | ]); 258 | expect(checkDir).toHaveBeenCalledTimes(1); 259 | expect(checkFile).toHaveBeenCalledTimes(0); 260 | }); 261 | 262 | it('skips file if checkFile function returns false', async () => { 263 | const filePaths: string[] = []; 264 | const checkDir = vi.fn(() => false); 265 | const checkFile = vi.fn(() => false); 266 | 267 | await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); 268 | 269 | expect(filePaths).toEqual([]); 270 | expect(checkFile).toHaveBeenCalledTimes(4); 271 | }); 272 | 273 | it('does not throw if path is invalid', async () => { 274 | const filePaths: string[] = []; 275 | const checkDir = vi.fn(() => false); 276 | const checkFile = vi.fn(() => false); 277 | 278 | expect( 279 | async () => await crawlDirWithChecks(filePaths, 'invalid/path', checkDir, checkFile) 280 | ).not.toThrow(); 281 | }); 282 | }); 283 | 284 | describe('removeFiles', () => { 285 | beforeEach(async () => { 286 | const fileStructure = await getMockedFileStructure(); 287 | vol.fromNestedJSON(fileStructure); 288 | }); 289 | 290 | it('removes files at provided file paths', async () => { 291 | const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md']; 292 | 293 | // files are initially there 294 | expect(fs.existsSync(filePaths[0])).toBe(true); 295 | expect(fs.existsSync(filePaths[1])).toBe(true); 296 | 297 | await removeFiles(filePaths); 298 | 299 | // then they are not 300 | expect(fs.existsSync(filePaths[0])).toBe(false); 301 | expect(fs.existsSync(filePaths[1])).toBe(false); 302 | }); 303 | 304 | it('does not remove files during dry runs', async () => { 305 | const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md']; 306 | 307 | await removeFiles(filePaths, { dryRun: true }); 308 | 309 | expect(fs.existsSync(filePaths[0])).toBe(true); 310 | expect(fs.existsSync(filePaths[1])).toBe(true); 311 | }); 312 | 313 | it('does not throw if path is invalid', async () => { 314 | const filePaths = ['/invalid/path/2', '/invalid/path/2']; 315 | expect(async () => await removeFiles(filePaths)).not.toThrow(); 316 | }); 317 | }); 318 | -------------------------------------------------------------------------------- /src/utils/filesystem.ts: -------------------------------------------------------------------------------- 1 | import fs, { Dirent } from 'fs'; 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | 5 | export type DirentAction = (dirent: Dirent) => void; 6 | export type CheckPathFunc = (nextPath: string) => boolean; 7 | 8 | function hasErrorCode(error: any, code: string): boolean { 9 | return error?.code === code; 10 | } 11 | 12 | /** 13 | * Check if a file exists without throwing. 14 | */ 15 | export async function fileExists(filePath: string): Promise { 16 | try { 17 | await fs.promises.stat(filePath); 18 | 19 | return true; 20 | } catch (error: unknown) { 21 | if (hasErrorCode(error, 'ENOENT')) { 22 | return false; 23 | } 24 | 25 | throw error; 26 | } 27 | } 28 | 29 | /** 30 | * Asynchronously loop through each file in a directory, passing the dirents for each file to the provided action. 31 | */ 32 | export async function forEachDirentAsync(dirPath: string, action: DirentAction): Promise { 33 | let dirFiles: Dirent[] = []; 34 | 35 | try { 36 | dirFiles = await fs.promises.readdir(dirPath, { withFileTypes: true }); 37 | } catch (error) { 38 | // do nothing 39 | } 40 | 41 | await Promise.all(dirFiles.map(action)); 42 | } 43 | 44 | /** 45 | * Get the file paths inside a directory without throwing. 46 | */ 47 | export async function readDirectory(dirPath: string): Promise { 48 | try { 49 | const files = await fs.promises.readdir(dirPath); 50 | return files; 51 | } catch (error: unknown) { 52 | if (hasErrorCode(error, 'ENOENT') || hasErrorCode(error, 'ENOTDIR')) { 53 | return []; 54 | } 55 | 56 | throw error; 57 | } 58 | } 59 | 60 | /** 61 | * Remove empty directories, recursively travelling up in the file until the first non-empty directory is reached. 62 | */ 63 | export async function removeEmptyDirsUp( 64 | checkedDirs: Set, 65 | dirPath: string, 66 | count = 0 67 | ): Promise { 68 | if (!checkedDirs.has(dirPath)) { 69 | const files = await readDirectory(dirPath); 70 | const emptyDir = files.length === 0; 71 | checkedDirs.add(dirPath); 72 | 73 | if (emptyDir) { 74 | try { 75 | await fs.promises.rmdir(dirPath); 76 | // biome-ignore lint/style/noParameterAssign: recursive function 77 | count++; 78 | } catch (error) { 79 | // do nothing 80 | } 81 | 82 | const parentDir = path.dirname(dirPath); 83 | // biome-ignore lint/style/noParameterAssign: recursive function 84 | count = await removeEmptyDirsUp(checkedDirs, parentDir, count); 85 | } 86 | } 87 | 88 | return count; 89 | } 90 | 91 | /** 92 | * Deeply removes empty directories for each file path. 93 | * @param filePaths the file paths to remove empty directories for 94 | * @returns the number of empty directories removed 95 | */ 96 | export async function removeEmptyDirs(filePaths: string[]): Promise { 97 | let removedEmptyDirs = 0; 98 | 99 | await Promise.all( 100 | filePaths.map(async filePath => { 101 | const removedParentDirs = await removeEmptyDirsUp(new Set(), path.dirname(filePath)); 102 | removedEmptyDirs += removedParentDirs; 103 | }) 104 | ); 105 | 106 | return removedEmptyDirs; 107 | } 108 | 109 | /** 110 | * Find all files in a directory as fast as possible, without any extra checks or validations. 111 | */ 112 | export async function crawlDirFast(filePaths: string[], dirPath: string): Promise { 113 | await forEachDirentAsync(dirPath, async dirent => { 114 | const nextPath = dirPath + path.sep + dirent.name; 115 | 116 | if (dirent.isDirectory()) { 117 | await crawlDirFast(filePaths, nextPath); 118 | } else { 119 | filePaths.push(nextPath); 120 | } 121 | }); 122 | } 123 | 124 | /** 125 | * Crawl files and validate them against glob patterns. 126 | */ 127 | export async function crawlDirWithChecks( 128 | filePaths: string[], // Mutate array to avoid losing speed on spreading 129 | dirPath: string, 130 | checkDir: CheckPathFunc, 131 | checkFile: CheckPathFunc 132 | ): Promise { 133 | await forEachDirentAsync(dirPath, async nextPathDirent => { 134 | const nextPath = dirPath + path.sep + nextPathDirent.name; 135 | 136 | if (nextPathDirent.isDirectory()) { 137 | if (checkDir(nextPath)) { 138 | // If a full directory matches, include all of it. 139 | await crawlDirFast(filePaths, nextPath); 140 | } else { 141 | // Keep recursively checking each directory 142 | await crawlDirWithChecks(filePaths, nextPath, checkDir, checkFile); 143 | } 144 | } else if (checkFile(nextPath)) { 145 | filePaths.push(nextPath); 146 | } 147 | }); 148 | 149 | return filePaths; 150 | } 151 | 152 | export type RemoveFilesOptions = { 153 | dryRun?: boolean; 154 | }; 155 | 156 | /** 157 | * Removes files and returns the total size of the removed files. 158 | * @param filePaths the file paths to remove 159 | * @param options dryRun: if true, don't actually remove the files 160 | * @returns the total size of the removed files 161 | */ 162 | export async function removeFiles( 163 | filePaths: string[], 164 | options: RemoveFilesOptions = {} 165 | ): Promise { 166 | let reducedSize = 0; 167 | 168 | await Promise.all( 169 | filePaths.map(async filePath => { 170 | try { 171 | const fileStats = await fs.promises.stat(filePath); 172 | 173 | if (!options.dryRun) { 174 | await fs.promises.unlink(filePath); 175 | } 176 | 177 | reducedSize += fileStats.size; 178 | } catch (error) { 179 | // do nothing 180 | } 181 | }) 182 | ); 183 | 184 | return reducedSize; 185 | } 186 | 187 | /** 188 | * Get directory of the file directory, like CommonJS `__dirname`. 189 | * @example const thisFilesDir = fileDir(import.meta.url); 190 | */ 191 | export function fileDir(importMetaUrl: string) { 192 | return path.dirname(fileURLToPath(importMetaUrl)); 193 | } 194 | -------------------------------------------------------------------------------- /src/utils/glob.test.ts: -------------------------------------------------------------------------------- 1 | import { vol } from 'memfs'; 2 | import path from 'path'; 3 | import pm from 'picomatch'; 4 | import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; 5 | import { 6 | DEFAULT_PICO_OPTIONS, 7 | findFilesByGlobLists, 8 | formatGlob, 9 | GlobLists, 10 | initGlobLists, 11 | makeGlobMatcher, 12 | mergeGlobLists, 13 | optimizeGlobLists, 14 | optimizeGlobs, 15 | parseGlobsFile, 16 | processGlobs, 17 | toAbsoluteGlobLists, 18 | updateGlobLists, 19 | wrapGlobs, 20 | } from './glob.js'; 21 | import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; 22 | 23 | describe('makeGlobMatcher', () => { 24 | it('creates a picomatch globber with default options', () => { 25 | const pattern = '**/**'; 26 | 27 | const globber = makeGlobMatcher(pattern); 28 | 29 | expect(globber).toBeInstanceOf(Function); 30 | expect(globber).toHaveProperty('name', 'matcher'); 31 | }); 32 | }); 33 | 34 | describe('updateGlobLists', () => { 35 | const mockGlobLists: GlobLists = { 36 | ...initGlobLists(), 37 | included: ['foo'], 38 | includedDirs: ['bar'], 39 | excluded: ['baz'], 40 | }; 41 | 42 | it('runs passed function for correct files', () => { 43 | const mockFn = vi.fn(); 44 | 45 | updateGlobLists(mockGlobLists, mockFn); 46 | 47 | expect(mockFn).toHaveBeenCalledWith(mockGlobLists.included, 'included'); 48 | expect(mockFn).toHaveBeenCalledWith(mockGlobLists.includedDirs, 'includedDirs'); 49 | expect(mockFn).toHaveBeenCalledWith(mockGlobLists.excluded, 'excluded'); 50 | }); 51 | 52 | it('applies result of passed function to each field', () => { 53 | const result = updateGlobLists(mockGlobLists, (globs, key) => [...globs, key]); 54 | const expectedResult: GlobLists = { 55 | ...mockGlobLists, 56 | included: [...mockGlobLists.included, 'included'], 57 | includedDirs: [...(mockGlobLists.includedDirs || []), 'includedDirs'], 58 | excluded: [...mockGlobLists.excluded, 'excluded'], 59 | }; 60 | 61 | expect(result).toEqual(expectedResult); 62 | }); 63 | }); 64 | 65 | describe('mergeGlobLists', () => { 66 | it('merges glob lists', () => { 67 | const result = mergeGlobLists( 68 | { 69 | ...initGlobLists(), 70 | included: ['foo'], 71 | includedDirs: ['foo'], 72 | excluded: ['foo'], 73 | }, 74 | { 75 | ...initGlobLists(), 76 | included: ['bar'], 77 | includedDirs: ['bar'], 78 | excluded: ['bar'], 79 | } 80 | ); 81 | 82 | expect(result).toEqual({ 83 | ...initGlobLists(), 84 | included: ['foo', 'bar'], 85 | includedDirs: ['foo', 'bar'], 86 | excluded: ['foo', 'bar'], 87 | }); 88 | }); 89 | }); 90 | 91 | describe('toAbsoluteGlobLists', () => { 92 | it('prepends globs with absolute paths on all platforms', () => { 93 | const result = toAbsoluteGlobLists( 94 | { 95 | ...initGlobLists(), 96 | included: ['bar'], 97 | includedDirs: ['bar'], 98 | excluded: ['bar'], 99 | }, 100 | '/foo' 101 | ); 102 | 103 | expect(result).toEqual({ 104 | ...initGlobLists(), 105 | included: ['/foo/bar'], 106 | includedDirs: ['/foo/bar'], 107 | excluded: ['/foo/bar'], 108 | }); 109 | }); 110 | }); 111 | 112 | describe('wrapGlobs', () => { 113 | it('wraps globs into a single glob', () => { 114 | const result = wrapGlobs(['**/foo', '**/bar', '*/baz']); 115 | expect(result).toEqual('@((**/foo)|(**/bar)|(*/baz))'); 116 | }); 117 | 118 | it('prepends an optional prefix to the result', () => { 119 | const result = wrapGlobs(['**/foo', '**/bar', '*/baz'], 'hello'); 120 | expect(result).toEqual('hello@((**/foo)|(**/bar)|(*/baz))'); 121 | }); 122 | }); 123 | 124 | describe('optimizeGlobs', () => { 125 | it('splits globs by leading characters and merges into two globs', () => { 126 | const result = optimizeGlobs(['**/some', '*/where', 'over', '**/the', '/rainbow']); 127 | expect(result).toEqual(['**/@((some)|(the))', '@((*/where)|(over)|(/rainbow))']); 128 | }); 129 | 130 | it('normalizes leading globstars', () => { 131 | const result = optimizeGlobs(['**/**/**/one', '**/**/two', '**/three', '/**/four']); 132 | expect(result).toEqual(['**/@((one)|(two)|(three)|(four))']); 133 | }); 134 | }); 135 | 136 | describe('optimizeGlobLists', () => { 137 | it('splits globs by leading characters and merges into two globs', () => { 138 | const result = optimizeGlobLists({ 139 | ...initGlobLists(), 140 | included: ['**/wrapMe/**', '**/andMe/**', 'notMe/**', '*/andNotMe.js', '/andNotMeEither.ts'], 141 | includedDirs: ['**/wrapMe', '**/andMe', 'notMe'], 142 | excluded: ['**/wrapMe/**', '**/andMe/**', 'notMe/**'], 143 | }); 144 | 145 | expect(result).toMatchInlineSnapshot(` 146 | { 147 | "excluded": [ 148 | "**/@((wrapMe/**)|(andMe/**))", 149 | "@((notMe/**))", 150 | ], 151 | "included": [ 152 | "**/@((wrapMe/**)|(andMe/**))", 153 | "@((notMe/**)|(*/andNotMe.js)|(/andNotMeEither.ts))", 154 | ], 155 | "includedDirs": [ 156 | "**/@((wrapMe)|(andMe))", 157 | "@((notMe))", 158 | ], 159 | "originalIncluded": [], 160 | } 161 | `); 162 | }); 163 | }); 164 | 165 | describe('formatGlob', () => { 166 | it('trims globs', () => { 167 | const result = formatGlob(' foo/** '); 168 | expect(result).toEqual('**/foo/**'); 169 | }); 170 | 171 | it('adds globstars to globs not starting with a slash', () => { 172 | const result = formatGlob('test.ts'); 173 | expect(result).toEqual('**/test.ts'); 174 | }); 175 | 176 | it('replaces escaped leading exclamation marks', () => { 177 | const result = formatGlob('\\!(*.d).ts'); 178 | expect(result).toEqual('**/!(*.d).ts'); 179 | }); 180 | 181 | it('removes leading slashes', () => { 182 | const result = formatGlob('/path/to/file'); 183 | expect(result).toEqual('path/to/file'); 184 | }); 185 | 186 | it('appends globstars to directory globs', () => { 187 | const result = formatGlob('/path/to/directory/'); 188 | expect(result).toEqual('path/to/directory/**'); 189 | }); 190 | }); 191 | 192 | describe('processGlobs', () => { 193 | it('formats globs and splits them into include/exclude glob lists', () => { 194 | const result = processGlobs(['**/test', '!test.js', '**/path/to/directory/', '*.ext']); 195 | expect(result).toMatchInlineSnapshot(` 196 | { 197 | "excluded": [ 198 | "**/test.js", 199 | ], 200 | "included": [ 201 | "**/**/test", 202 | "**/**/path/to/directory/**", 203 | "**/*.ext", 204 | ], 205 | "includedDirs": [ 206 | "**/**/path/to/directory", 207 | ], 208 | "originalIncluded": [ 209 | "**/test", 210 | "**/path/to/directory/", 211 | "*.ext", 212 | ], 213 | } 214 | `); 215 | }); 216 | 217 | it('removes trailing globstars from dir globs', () => { 218 | const result = processGlobs(['**/foo/**', '/bar/**']); 219 | expect(result.includedDirs).toEqual(['**/**/foo', 'bar']); 220 | }); 221 | 222 | it('filters empty strings', () => { 223 | const result = processGlobs(['foo', '', '', '', 'bar']); 224 | expect(result.included).toEqual(['**/foo', '**/bar']); 225 | }); 226 | 227 | it('keeps original inclusion globs', () => { 228 | const original = ['**/test', '**/path/to/directory/', '*.ext']; 229 | const result = processGlobs(original); 230 | expect(result.originalIncluded).toEqual(original); 231 | }); 232 | }); 233 | 234 | describe('parseGlobsFile', () => { 235 | const globFilePath = process.cwd() + '/.cleanmodules'; 236 | 237 | it('loads globs from a glob file', async () => { 238 | const globFile = ` 239 | # this is a comment 240 | __test__/ 241 | !goodstuff/ 242 | 243 | */dep/Makefile 244 | 245 | *.ts 246 | !*.d.ts 247 | *.ext 248 | `; 249 | 250 | vol.fromNestedJSON({ [globFilePath]: globFile }); 251 | const result = await parseGlobsFile(globFilePath); 252 | 253 | expect(result).toMatchInlineSnapshot(` 254 | { 255 | "excluded": [ 256 | "**/goodstuff/**", 257 | "**/*.d.ts", 258 | ], 259 | "included": [ 260 | "**/__test__/**", 261 | "**/*/dep/Makefile", 262 | "**/*.ts", 263 | "**/*.ext", 264 | ], 265 | "includedDirs": [ 266 | "**/__test__", 267 | ], 268 | "originalIncluded": [ 269 | "__test__/", 270 | "*/dep/Makefile", 271 | "*.ts", 272 | "*.ext", 273 | ], 274 | } 275 | `); 276 | }); 277 | 278 | it('removes comments', async () => { 279 | const globFile = ` 280 | # this is a comment 281 | # this too 282 | # and this 283 | path/to/something 284 | `; 285 | 286 | vol.fromNestedJSON({ [globFilePath]: globFile }); 287 | const result = await parseGlobsFile(globFilePath); 288 | expect(result.included).toEqual(['**/path/to/something']); 289 | expect(result.excluded).toEqual([]); 290 | }); 291 | 292 | it('removes empty lines', async () => { 293 | const globFile = ` 294 | 295 | path/to/something 296 | 297 | `; 298 | 299 | vol.fromNestedJSON({ [globFilePath]: globFile }); 300 | const result = await parseGlobsFile(globFilePath); 301 | expect(result.included).toEqual(['**/path/to/something']); 302 | expect(result.excluded).toEqual([]); 303 | }); 304 | 305 | it('adds lines starting with an exclamation point to excluded globs', async () => { 306 | const globFile = ` 307 | !excludeMe 308 | `; 309 | 310 | vol.fromNestedJSON({ [globFilePath]: globFile }); 311 | const result = await parseGlobsFile(globFilePath); 312 | expect(result.included).toEqual([]); 313 | expect(result.excluded).toEqual(['**/excludeMe']); 314 | }); 315 | }); 316 | 317 | describe('findFilesByGlobLists', async () => { 318 | const fileStructure = await getMockedFileStructure(); 319 | const nodeModulesPath = 'node_modules'; 320 | 321 | beforeEach(async () => { 322 | vol.fromNestedJSON(fileStructure); 323 | }); 324 | 325 | it('includes dirs', async () => { 326 | const result = await findFilesByGlobLists(nodeModulesPath, { 327 | ...initGlobLists(), 328 | includedDirs: ['**/__tests__', '**/dep3'], 329 | }); 330 | 331 | expect(result).toEqual([ 332 | path.join('node_modules', 'dep1', '__tests__', 'test1.js'), 333 | path.join('node_modules', 'dep1', '__tests__', 'test2.js'), 334 | path.join('node_modules', 'dep3', 'deeply', 'nested', 'file.ext'), 335 | ]); 336 | }); 337 | 338 | it('includes files', async () => { 339 | const result = await findFilesByGlobLists(nodeModulesPath, { 340 | ...initGlobLists(), 341 | included: ['**/deeply/nested/file.ext', '**/dep4/**'], 342 | }); 343 | 344 | expect(result).toEqual([ 345 | path.join('node_modules', 'dep4', 'nonDefaultFile.ext'), 346 | path.join('node_modules', 'dep3', 'deeply', 'nested', 'file.ext'), 347 | ]); 348 | }); 349 | 350 | it('can exclude files and dirs by glob patterns', async () => { 351 | const result = await findFilesByGlobLists(nodeModulesPath, { 352 | ...initGlobLists(), 353 | included: ['**/*.js'], 354 | excluded: ['**/test*.js'], 355 | }); 356 | 357 | expect(result).toEqual([path.join('node_modules', 'dep2', 'file.js')]); 358 | }); 359 | }); 360 | -------------------------------------------------------------------------------- /src/utils/glob.ts: -------------------------------------------------------------------------------- 1 | import { promises as fsAsync } from 'fs'; 2 | import path from 'path'; 3 | import pm, { PicomatchOptions } from 'picomatch'; 4 | import { DEFAULT_GLOBS_FILE_PATH } from '../shared.js'; 5 | import { crawlDirWithChecks, fileExists } from './filesystem.js'; 6 | 7 | export function initGlobLists(): GlobLists { 8 | return { excluded: [], included: [], includedDirs: [], originalIncluded: [] }; 9 | } 10 | 11 | export const DEFAULT_PICO_OPTIONS: Partial = { 12 | dot: true, 13 | nocase: true, 14 | strictSlashes: true, 15 | }; 16 | 17 | export interface GlobberPicoOptions { 18 | dot?: boolean; 19 | regex?: boolean; 20 | nocase?: boolean; 21 | ignore?: string | string[]; 22 | cwd?: string; 23 | } 24 | 25 | export type GlobFunc = (filePath: string, test?: boolean) => boolean; 26 | 27 | /** 28 | * Creates a picomatch matcher from a set of globs. 29 | */ 30 | export function makeGlobMatcher( 31 | globs: string | string[], 32 | picoOptions?: GlobberPicoOptions 33 | ): GlobFunc { 34 | return pm(globs, { ...DEFAULT_PICO_OPTIONS, ...picoOptions }); 35 | } 36 | 37 | export interface GlobLists { 38 | excluded: string[]; 39 | included: string[]; 40 | includedDirs: string[]; 41 | originalIncluded: string[]; 42 | } 43 | 44 | /** 45 | * Runs an action on all editable lists in a GlobLists object, and returns the updated object. 46 | */ 47 | export function updateGlobLists( 48 | globLists: GlobLists, 49 | updateFunc: (globs: string[], key: keyof GlobLists) => string[] 50 | ): GlobLists { 51 | const { included, includedDirs, excluded } = globLists; 52 | 53 | return { 54 | ...globLists, 55 | included: updateFunc(included, 'included'), 56 | includedDirs: includedDirs ? updateFunc(includedDirs, 'includedDirs') : includedDirs, 57 | excluded: updateFunc(excluded, 'excluded'), 58 | }; 59 | } 60 | 61 | /** 62 | * Merges all arrays of two GlobLists objects. 63 | */ 64 | export function mergeGlobLists(globListsA: GlobLists, globListsB: GlobLists): GlobLists { 65 | return { 66 | ...updateGlobLists(globListsA, (globs, key) => [...globs, ...(globListsB[key] || [])]), 67 | originalIncluded: [...globListsA.originalIncluded, ...globListsB.originalIncluded], 68 | }; 69 | } 70 | 71 | /** Replaces path with forward slashes as separators if necessary. Globs should always have POSIX separators, even on Windows. */ 72 | export function toPosixPath(pathStr: string): string { 73 | return path.sep === '/' ? pathStr : pathStr.replace(/\\/g, '/'); 74 | } 75 | 76 | /** 77 | * Prepends an absolute path to all editable lists in a GlobLists object. 78 | */ 79 | export function toAbsoluteGlobLists( 80 | globLists: GlobLists, 81 | absoluteNodeModulesPath: string 82 | ): GlobLists { 83 | const absolutePathWithPosixSeparator = toPosixPath(absoluteNodeModulesPath); 84 | 85 | return updateGlobLists(globLists, globs => 86 | globs.map(glob => absolutePathWithPosixSeparator + '/' + glob) 87 | ); 88 | } 89 | 90 | /** 91 | * Wraps globs into one conditional glob (like `@((one/glob/path)|(another/glob/path))`). 92 | */ 93 | export function wrapGlobs(globs: string[], prefix?: string): string { 94 | return `${prefix || ''}@(${globs.map(glob => '(' + glob + ')').join('|')})`; 95 | } 96 | 97 | const GLOBSTAR_START_REGEX = /^(\/?\*\*\/)+/; 98 | 99 | /** 100 | * Optimizes globs by merging them into groups and removing unnecessary globstars. 101 | */ 102 | export function optimizeGlobs(globs: string[]): string[] { 103 | const globstarStartGlobs: string[] = []; 104 | const fixedPathGlobs: string[] = []; 105 | 106 | for (const glob of globs) { 107 | if (glob.match(GLOBSTAR_START_REGEX)) { 108 | globstarStartGlobs.push(glob); 109 | } else { 110 | fixedPathGlobs.push(glob); 111 | } 112 | } 113 | 114 | const result: string[] = []; 115 | 116 | if (globstarStartGlobs.length) { 117 | const withoutGlobstar = globstarStartGlobs.map(glob => glob.replace(GLOBSTAR_START_REGEX, '')); 118 | result.push(wrapGlobs(withoutGlobstar, '**/')); 119 | } 120 | 121 | if (fixedPathGlobs.length) { 122 | result.push(wrapGlobs(fixedPathGlobs)); 123 | } 124 | 125 | return result; 126 | } 127 | 128 | /** 129 | * Runs optimizeGlobs() on all editable lists in a GlobLists object. 130 | */ 131 | export function optimizeGlobLists(globLists: GlobLists): GlobLists { 132 | return updateGlobLists(globLists, optimizeGlobs); 133 | } 134 | 135 | const EXCLAMATION_START = /^\s*!/; // Globs starting with ! 136 | const DIR_GLOB_REGEX = /\/\**$/; // Globs ending with /, /* or /** 137 | const EXCLUDED_GLOB_REGEX = /^!/; // Globs starting with ! 138 | const ESCAPED_NEGATIVE_GLOB_REGEX = /^\\!/; // Globs starting with \! 139 | const NOT_FIXED_START_GLOB_REGEX = /^([^/])/; // Globs not starting with / 140 | const FIXED_START_GLOB_REGEX = /^\//; // Globs starting with / 141 | 142 | /** 143 | * Formats and standardizes globs to make them suitable for picomatch. 144 | */ 145 | export function formatGlob(glob: string): string { 146 | return glob 147 | .trim() 148 | .replace(EXCLUDED_GLOB_REGEX, '') // remove leading exclamation marks 149 | .replace(ESCAPED_NEGATIVE_GLOB_REGEX, '!') // replace escaped leading exclamation mark with real one 150 | .replace(DIR_GLOB_REGEX, '/**') // normalize dir globs to all end with /** 151 | .replace(NOT_FIXED_START_GLOB_REGEX, '**/$1') // add globstars to globs not starting with / 152 | .replace(FIXED_START_GLOB_REGEX, ''); // remove fixed path indicators 153 | } 154 | 155 | /** 156 | * Parses clean-modules/gitignore-like globs and converts them into picomatch compatible globs. 157 | */ 158 | export function processGlobs(globs: string[]): GlobLists { 159 | const globLists = initGlobLists(); 160 | 161 | for (const glob of globs) { 162 | const isExcluded = !!glob.match(EXCLAMATION_START); 163 | const formattedGlob = formatGlob(glob); 164 | 165 | if (!formattedGlob) { 166 | continue; 167 | } 168 | 169 | if (isExcluded) { 170 | globLists.excluded.push(formattedGlob); 171 | } else { 172 | if (formattedGlob.endsWith('/**')) { 173 | globLists.includedDirs.push(formattedGlob.replace(DIR_GLOB_REGEX, '')); 174 | } 175 | 176 | globLists.included.push(formattedGlob); 177 | globLists.originalIncluded.push(glob.trim()); 178 | } 179 | } 180 | 181 | return globLists; 182 | } 183 | 184 | const COMMENT_OR_EMPTY_REGEX = /^\s*(#|$)/; 185 | 186 | /** 187 | * Parses a clean-modules glob file, filtering comments and separating globs that should be included or excluded. 188 | */ 189 | export async function parseGlobsFile(filePath: string): Promise { 190 | let fileContents: string; 191 | 192 | try { 193 | fileContents = (await fsAsync.readFile(filePath, { encoding: 'utf-8' })).toString() || ''; 194 | } catch (error) { 195 | console.error(`Failed to read glob file (${filePath})`); 196 | throw error; 197 | } 198 | 199 | const fileGlobs = 200 | fileContents.split(/\r?\n/).filter(line => !line.match(COMMENT_OR_EMPTY_REGEX)) || []; 201 | 202 | return processGlobs(fileGlobs); 203 | } 204 | 205 | /** 206 | * Parses clean-modules' default glob file. 207 | */ 208 | export async function parseDefaultGlobsFile(): Promise { 209 | return parseGlobsFile(DEFAULT_GLOBS_FILE_PATH); 210 | } 211 | 212 | export interface GetGlobListsOptions { 213 | noDefaults?: boolean | undefined; 214 | globFile?: string | undefined; 215 | globs?: string[] | undefined; 216 | } 217 | 218 | /** 219 | * Parses and combines globs from all possible sources. 220 | */ 221 | export async function getGlobLists({ 222 | noDefaults, 223 | globFile, 224 | globs, 225 | }: GetGlobListsOptions): Promise { 226 | const globListsToMerge: GlobLists[] = []; 227 | 228 | if (!noDefaults) { 229 | const defaultGlobLists = await parseDefaultGlobsFile(); 230 | globListsToMerge.push(defaultGlobLists); 231 | } 232 | 233 | if (globFile && (await fileExists(globFile))) { 234 | const userFileGlobLists = await parseGlobsFile(globFile); 235 | globListsToMerge.push(userFileGlobLists); 236 | } 237 | 238 | if (globs?.length) { 239 | const argGlobsLists = processGlobs(globs); 240 | globListsToMerge.push(argGlobsLists); 241 | } 242 | 243 | return globListsToMerge.reduce( 244 | (mergedGlobLists, globLists) => mergeGlobLists(mergedGlobLists, globLists), 245 | initGlobLists() 246 | ); 247 | } 248 | 249 | /** 250 | * Finds all files matching the given glob lists in the given directory. 251 | * @param directory the directory to search in 252 | * @param globLists the glob lists to match against 253 | * @returns a promise that resolves to an array of matching file paths 254 | */ 255 | export async function findFilesByGlobLists( 256 | directory: string, 257 | globLists: GlobLists 258 | ): Promise { 259 | const { included, includedDirs, excluded } = toAbsoluteGlobLists( 260 | optimizeGlobLists(globLists), 261 | directory 262 | ); 263 | 264 | const picoOptions = { ignore: excluded }; 265 | const checkDir = includedDirs?.length ? makeGlobMatcher(includedDirs, picoOptions) : () => false; 266 | const checkFile = makeGlobMatcher(included, picoOptions); 267 | 268 | let filesToRemove = await crawlDirWithChecks([], directory, checkDir, checkFile); 269 | 270 | if (excluded.length) { 271 | // make another pass to ensure that files included by dir globs are 272 | // matched with excluded globs too 273 | filesToRemove = filesToRemove.filter(file => checkFile(file)); 274 | } 275 | 276 | return filesToRemove; 277 | } 278 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": ["@tsconfig/node14/tsconfig.json"], 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "strict": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "exactOptionalPropertyTypes": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitReturns": true, 12 | "noImplicitOverride": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | format: ['cjs', 'esm'], 5 | entry: ['src/index.ts', 'src/cli/cli.ts'], 6 | outDir: 'dist', 7 | target: 'node14', 8 | splitting: true, 9 | sourcemap: false, 10 | clean: true, 11 | shims: true, 12 | dts: true, 13 | }); 14 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | clearMocks: true, 6 | setupFiles: ['vitest.setup.ts'], 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { vol } from 'memfs'; 2 | import { afterEach, expect, vi } from 'vitest'; 3 | import { pathSerializer } from './src/__test__/path.serializer.js'; 4 | 5 | vi.mock('fs'); 6 | vi.mock('fs/promises'); 7 | 8 | expect.addSnapshotSerializer(pathSerializer); 9 | 10 | afterEach(() => { 11 | vol.reset(); 12 | }); 13 | --------------------------------------------------------------------------------