diff --git a/.documentup.json b/.documentup.json deleted file mode 100644 index 57fe30116..000000000 --- a/.documentup.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ShellJS", - "twitter": [ - "r2r" - ] -} diff --git a/.editorconfig b/.editorconfig index a35cd0dca..afe97de28 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,5 +10,8 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true +[*.js] +quote_type = single + [*.{md,markdown}] trim_trailing_whitespace = false diff --git a/.eslintrc.json b/.eslintrc.json index e712eeab2..4f40d77b8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,28 +2,47 @@ "env": { "node": true }, - "extends": "airbnb-base/legacy", + "extends": "airbnb-base", "rules": { - "comma-dangle": [2, "always-multiline"], - "global-require": 0, - "vars-on-top": 0, - "spaced-comment": [2, "always", { "markers": ["@", "@include"], "exceptions": ["@", "@commands"] }], - "no-param-reassign": 0, - "no-console": 0, - "curly": [2, "multi-line"], - "func-names": 0, - "quote-props": 0, - "no-underscore-dangle": 0, - "max-len": 0, - "no-use-before-define": 0, - "no-empty": 0, - "no-else-return": 0, - "no-throw-literal": 0, - "newline-per-chained-call": 0, - "consistent-return": 0, - "no-mixed-operators": 0, - "no-prototype-builtins": 0, - "new-cap": [2, { + "comma-dangle": ["error", "always-multiline"], + "consistent-return": "off", + "curly": ["error", "multi-line"], + "func-names": "off", + "function-call-argument-newline": "off", + "function-paren-newline": "off", + "global-require": "off", + "import/no-unresolved": "off", + "indent": "off", + "max-len": "off", + "newline-per-chained-call": "off", + "no-bitwise": "off", + "no-console": "off", + "no-continue": "off", + "no-else-return": "off", + "no-empty": "off", + "no-mixed-operators": "off", + "no-multi-spaces": "off", + "no-multiple-empty-lines": ["error", { "max": 2, "maxBOF": 0, "maxEOF": 0 } ], + "no-param-reassign": "off", + "no-plusplus": "off", + "no-prototype-builtins": "off", + "no-throw-literal": "off", + "no-underscore-dangle": "off", + "no-use-before-define": "off", + "no-var": "off", + "operator-linebreak": "off", + "prefer-arrow-callback": "off", + "prefer-destructuring": "off", + "prefer-numeric-literals": "off", + "prefer-object-spread": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "prefer-template": "off", + "quote-props": "off", + "spaced-comment": ["error", "always", { "markers": ["@", "@include"], "exceptions": ["@", "@commands"] }], + "strict": "off", + "vars-on-top": "off", + "new-cap": ["error", { "capIsNewExceptions": [ "ShellString" ]} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 13d777fbc..172af387f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,7 @@ + ### Node version (or tell us if you're using electron or some other framework): ### ShellJS version (the most recent version/Github branch you see the bug on): diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..4c96dd513 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,31 @@ +# ShellJS Security Policy + +Thank you for reaching out regarding the security of the ShellJS module! Please +note that this project is maintained on a best-effort basis, however I still +intend to prioritize reviewing and addressing security issues. + +## Supported Versions + +I generally only support the latest ShellJS release (see +https://www.npmjs.com/package/shelljs). My goal is to release security fixes as +patch releases on top of whatever was most recently shipped. + +If breaking changes have already landed on the main development branch, I may +apply the patch on the relevant release branch (ex. +[`0.8-release`](https://github.com/shelljs/shelljs/commits/0.8-release)) and +create a new release from there. + +## Reporting a Vulnerability + +Please report security vulnerabilities to ntfschr@gmail.com. I should respond +within a few days. Although it's not strictly required, it helps me out if you +can include any proof of concept exploit code, suggested fix, etc. + +**Please do not publicly disclose the suspected vulnerability** until I have a +chance to review your report. I'd like a chance to patch the code before the +issue is known to the public. + +Please **only** use this email for security issues. It's also OK to use the +email if you're legitimately unsure if this is a security issue (better safe +than sorry). But for all other non-security issues, please use the GitHub issue +tracker. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..6c207c2bb --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,36 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Node.js ${{ matrix.node-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + node-version: + - 18 + - 20 + - 22 + - 24 + - 26 + os: + - ubuntu-latest + - macos-latest + - windows-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: test with coverage + run: npm run test-with-coverage + - run: npm run lint + - run: npm run check-node-support + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v7 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 90f780524..000000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: c++ -sudo: false -env: - - NODE_VERSION="4" - - NODE_VERSION="5" - - NODE_VERSION="6" - - NODE_VERSION="7" - - NODE_VERSION="8" - -# keep this blank to make sure there are no before_install steps -before_install: - -install: - - rm -rf ~/.nvm - - git clone https://github.com/creationix/nvm.git ~/.nvm - - source ~/.nvm/nvm.sh - - nvm install $NODE_VERSION - - node --version - - npm install -os: - - linux - - osx -script: - - npm test - # make sure when the docs are generated nothing changes (a.k.a. the docs have already been generated) - - npm run gendocs - - npm run after-travis "Make sure to generate docs!" -after_success: - - codecov - -# Gitter -notifications: - email: false - webhooks: - urls: - - https://webhooks.gitter.im/e/743dc6b83b6dd05111ee - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: never # options: [always|never|change] default: always diff --git a/CHANGELOG.md b/CHANGELOG.md index f7427c2ab..c30d70896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,172 @@ # Change Log +## [v0.8.5](https://github.com/shelljs/shelljs/tree/v0.8.5) (2022-01-13) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.8.4...v0.8.5) + +This was a small security fix for [\#1058](https://github.com/shelljs/shelljs/issues/1058). + +## [v0.8.4](https://github.com/shelljs/shelljs/tree/v0.8.4) (2020-04-24) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.8.3...v0.8.4) + +Small patch release to fix a circular dependency warning in node v14. See [\#973](https://github.com/shelljs/shelljs/issues/973). + +## [v0.8.3](https://github.com/shelljs/shelljs/tree/v0.8.3) (2018-11-13) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.8.2...v0.8.3) + +**Closed issues:** + +- Shelljs print stderr to console even if exec-only "silent" is true [\#905](https://github.com/shelljs/shelljs/issues/905) +- refactor: remove common.state.tempDir [\#902](https://github.com/shelljs/shelljs/issues/902) +- Can't suppress stdout for echo [\#899](https://github.com/shelljs/shelljs/issues/899) +- exec\(\) doesn't apply the arguments correctly [\#895](https://github.com/shelljs/shelljs/issues/895) +- shell.exec\('npm pack'\) painfully slow [\#885](https://github.com/shelljs/shelljs/issues/885) +- shelljs.exec cannot find app.asar/node\_modules/shelljs/src/exec-child.js [\#881](https://github.com/shelljs/shelljs/issues/881) +- test infra: mocks and skipOnWin conflict [\#862](https://github.com/shelljs/shelljs/issues/862) +- Support for shell function completion on IDE [\#859](https://github.com/shelljs/shelljs/issues/859) +- echo command shows options in stdout [\#855](https://github.com/shelljs/shelljs/issues/855) +- silent does not always work [\#851](https://github.com/shelljs/shelljs/issues/851) +- Appveyor installs the latest npm, instead of the latest compatible npm [\#844](https://github.com/shelljs/shelljs/issues/844) +- Force symbolic link \(ln -sf\) does not overwrite/recreate existing destination [\#830](https://github.com/shelljs/shelljs/issues/830) +- inconsistent result when trying to echo to a file [\#798](https://github.com/shelljs/shelljs/issues/798) +- Prevent require\(\)ing executable-only files [\#789](https://github.com/shelljs/shelljs/issues/789) +- Cannot set property to of \[object String\] which has only a getter [\#752](https://github.com/shelljs/shelljs/issues/752) +- which\(\) should check executability before returning a value [\#657](https://github.com/shelljs/shelljs/issues/657) +- Bad encoding experience [\#456](https://github.com/shelljs/shelljs/issues/456) +- phpcs very slow [\#440](https://github.com/shelljs/shelljs/issues/440) +- Error shown when triggering a sigint during shelljs.exec if process.on sigint is defined [\#254](https://github.com/shelljs/shelljs/issues/254) +- `.to\(file\)` does not mute STDIO output [\#146](https://github.com/shelljs/shelljs/issues/146) +- Escaping shell arguments to exec\(\) [\#143](https://github.com/shelljs/shelljs/issues/143) +- Allow multiple string arguments for exec\(\) [\#103](https://github.com/shelljs/shelljs/issues/103) +- cp does not recursively copy from readonly location [\#98](https://github.com/shelljs/shelljs/issues/98) +- Handling permissions errors on file I/O [\#64](https://github.com/shelljs/shelljs/issues/64) + +**Merged pull requests:** + +- Add test case for sed on empty file [\#904](https://github.com/shelljs/shelljs/pull/904) ([wyardley](https://github.com/wyardley)) +- refactor: don't expose tempdir in common.state [\#903](https://github.com/shelljs/shelljs/pull/903) ([nfischer](https://github.com/nfischer)) +- chore\(ci\): fix codecov on travis [\#897](https://github.com/shelljs/shelljs/pull/897) ([nfischer](https://github.com/nfischer)) +- chore\(npm\): add ci-or-install script [\#896](https://github.com/shelljs/shelljs/pull/896) ([nfischer](https://github.com/nfischer)) +- Fix silent exec [\#892](https://github.com/shelljs/shelljs/pull/892) ([nfischer](https://github.com/nfischer)) +- chore\(appveyor\): run entire test matrix [\#886](https://github.com/shelljs/shelljs/pull/886) ([nfischer](https://github.com/nfischer)) +- docs: remove gitter badge [\#880](https://github.com/shelljs/shelljs/pull/880) ([nfischer](https://github.com/nfischer)) +- grep includes the i flag [\#876](https://github.com/shelljs/shelljs/pull/876) ([ppsleep](https://github.com/ppsleep)) +- Fix\(which\): match only executable files \(\#657\) [\#874](https://github.com/shelljs/shelljs/pull/874) ([termosa](https://github.com/termosa)) +- chore: rename some tests [\#871](https://github.com/shelljs/shelljs/pull/871) ([nfischer](https://github.com/nfischer)) +- Fix cp from readonly source [\#870](https://github.com/shelljs/shelljs/pull/870) ([nfischer](https://github.com/nfischer)) +- chore: bump dev dependencies and add package-lock [\#864](https://github.com/shelljs/shelljs/pull/864) ([nfischer](https://github.com/nfischer)) +- fix\(mocks\): fix conflict between mocks and skip [\#863](https://github.com/shelljs/shelljs/pull/863) ([nfischer](https://github.com/nfischer)) +- chore: output npm version in travis [\#850](https://github.com/shelljs/shelljs/pull/850) ([nfischer](https://github.com/nfischer)) +- Prevent require-ing bin/shjs [\#848](https://github.com/shelljs/shelljs/pull/848) ([freitagbr](https://github.com/freitagbr)) +- chore\(appveyor\): do not use latest npm [\#847](https://github.com/shelljs/shelljs/pull/847) ([nfischer](https://github.com/nfischer)) +- chore: update shelljs-release version [\#846](https://github.com/shelljs/shelljs/pull/846) ([nfischer](https://github.com/nfischer)) + +## [v0.8.2](https://github.com/shelljs/shelljs/tree/v0.8.2) (2018-05-08) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.8.1...v0.8.2) + +**Closed issues:** + +- High severity vulnerability in shelljs 0.8.1 [\#842](https://github.com/shelljs/shelljs/issues/842) +- Add test for ls\(\) on a symlink to a directory [\#795](https://github.com/shelljs/shelljs/issues/795) +- Harden shell.exec by writing the child process in a source file [\#782](https://github.com/shelljs/shelljs/issues/782) +- shell.exec\(\) doesn't respond correctly to config.fatal = true [\#735](https://github.com/shelljs/shelljs/issues/735) +- Merge 'exec: internal error' with ShellJSInternalError [\#734](https://github.com/shelljs/shelljs/issues/734) +- exec returning null from command [\#724](https://github.com/shelljs/shelljs/issues/724) +- Only Get Stderr from Exec [\#371](https://github.com/shelljs/shelljs/issues/371) +- Execute child.stdout.on before child.on\("exit"\) [\#224](https://github.com/shelljs/shelljs/issues/224) + +**Merged pull requests:** + +- Workaround codecov bug of miscalculation of coverage \(\#795\) [\#838](https://github.com/shelljs/shelljs/pull/838) ([dwi2](https://github.com/dwi2)) +- Update doc comments and regenerate README.md. [\#825](https://github.com/shelljs/shelljs/pull/825) ([Zearin](https://github.com/Zearin)) +- chore: update contributing guidelines [\#817](https://github.com/shelljs/shelljs/pull/817) ([nfischer](https://github.com/nfischer)) +- chore\(lint\): don't allow excess trailing newlines [\#816](https://github.com/shelljs/shelljs/pull/816) ([nfischer](https://github.com/nfischer)) +- Remove separate "internal error" from exec [\#802](https://github.com/shelljs/shelljs/pull/802) ([freitagbr](https://github.com/freitagbr)) + +## [v0.8.1](https://github.com/shelljs/shelljs/tree/v0.8.1) (2018-01-20) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.8.0...v0.8.1) + +**Closed issues:** + +- Exec failing with internal error when piping large output [\#818](https://github.com/shelljs/shelljs/issues/818) + +**Merged pull requests:** + +- Revert "refactor\(exec\): remove paramsFile \(\#807\)" [\#819](https://github.com/shelljs/shelljs/pull/819) ([nfischer](https://github.com/nfischer)) + +## [v0.8.0](https://github.com/shelljs/shelljs/tree/v0.8.0) (2018-01-12) +[Full Changelog](https://github.com/shelljs/shelljs/compare/v0.7.8...v0.8.0) + +**Closed issues:** + +- Snyk vulnerability DB reporting command injection vulnerability in ShellJS [\#810](https://github.com/shelljs/shelljs/issues/810) +- chore: upgrade nyc [\#803](https://github.com/shelljs/shelljs/issues/803) +- Update CI to use Node v9 [\#799](https://github.com/shelljs/shelljs/issues/799) +- Link to FAQ wiki section in our issue template [\#787](https://github.com/shelljs/shelljs/issues/787) +- Is it possible to get a js library\(file\) for ShellJS [\#776](https://github.com/shelljs/shelljs/issues/776) +- 48, [\#774](https://github.com/shelljs/shelljs/issues/774) +- 47 [\#773](https://github.com/shelljs/shelljs/issues/773) +- Exec function calls JSON.stringify on command [\#772](https://github.com/shelljs/shelljs/issues/772) +- getting different result from terminal and with shelljs [\#769](https://github.com/shelljs/shelljs/issues/769) +- test\(\) does not support -w and -x options [\#768](https://github.com/shelljs/shelljs/issues/768) +- Snyk "high severity" issue [\#766](https://github.com/shelljs/shelljs/issues/766) +- Snyk "high security [\#765](https://github.com/shelljs/shelljs/issues/765) +- ShellJS doesn't respect NPM Registry being set outside of it [\#761](https://github.com/shelljs/shelljs/issues/761) +- Run second shell script [\#756](https://github.com/shelljs/shelljs/issues/756) +- shelljs seems NOT compatible with nexe under CentOS 6.5 [\#754](https://github.com/shelljs/shelljs/issues/754) +- Feature request: pushd/popd -q option [\#753](https://github.com/shelljs/shelljs/issues/753) +- cat doesn't support '-n' option [\#750](https://github.com/shelljs/shelljs/issues/750) +- shelljs run xcodebuild error [\#749](https://github.com/shelljs/shelljs/issues/749) +- Add wrappers around fs.statSync and fs.lstatSync [\#745](https://github.com/shelljs/shelljs/issues/745) +- Improve coverage for exec\(\) [\#742](https://github.com/shelljs/shelljs/issues/742) +- Improve coverage for head\(\) [\#741](https://github.com/shelljs/shelljs/issues/741) +- shelljs is no longer used in PDF.js [\#737](https://github.com/shelljs/shelljs/issues/737) +- ls doesn't follow links to directories [\#733](https://github.com/shelljs/shelljs/issues/733) +- Add test for `ls regular-file.txt` [\#732](https://github.com/shelljs/shelljs/issues/732) +- Clean up common tests [\#714](https://github.com/shelljs/shelljs/issues/714) +- Cant get encoding buffer to work on exec [\#675](https://github.com/shelljs/shelljs/issues/675) +- Set up Codecov for the project [\#671](https://github.com/shelljs/shelljs/issues/671) +- ShellJS: internal error Error: EBUSY: resource busy or locked, lstat 'C:\pagefile.sys' [\#514](https://github.com/shelljs/shelljs/issues/514) +- Feature request: provide a way to skip option parsing [\#778](https://github.com/shelljs/shelljs/issues/778) +- Switch to os.homedir\(\) when we move to v4+ [\#683](https://github.com/shelljs/shelljs/issues/683) +- Drop support for v0.12 [\#647](https://github.com/shelljs/shelljs/issues/647) +- feature: echo -n [\#559](https://github.com/shelljs/shelljs/issues/559) +- Don't kill the node process upon unexpected error [\#483](https://github.com/shelljs/shelljs/issues/483) +- Echo doesn't return value ending in a trailing newline [\#476](https://github.com/shelljs/shelljs/issues/476) +- Synchronous exec stalls permenantly when there is an error/w the shell [\#7](https://github.com/shelljs/shelljs/issues/7) + +**Merged pull requests:** + +- docs: announce plugin API [\#812](https://github.com/shelljs/shelljs/pull/812) ([nfischer](https://github.com/nfischer)) +- chore: update CI to Node v9 [\#811](https://github.com/shelljs/shelljs/pull/811) ([nfischer](https://github.com/nfischer)) +- refactor\(exec\): remove paramsFile [\#807](https://github.com/shelljs/shelljs/pull/807) ([nfischer](https://github.com/nfischer)) +- chore: update nyc dependency [\#805](https://github.com/shelljs/shelljs/pull/805) ([nfischer](https://github.com/nfischer)) +- refactor: harden plugins against unknown options [\#804](https://github.com/shelljs/shelljs/pull/804) ([nfischer](https://github.com/nfischer)) +- chore\(eslint\): use words instead of numbers [\#797](https://github.com/shelljs/shelljs/pull/797) ([nfischer](https://github.com/nfischer)) +- Add note to issue template about FAQ [\#794](https://github.com/shelljs/shelljs/pull/794) ([freitagbr](https://github.com/freitagbr)) +- Remove codeFile parameter [\#791](https://github.com/shelljs/shelljs/pull/791) ([nfischer](https://github.com/nfischer)) +- Use execFileSync to launch child process [\#790](https://github.com/shelljs/shelljs/pull/790) ([nfischer](https://github.com/nfischer)) +- refactor\(exec\): move child process to source file [\#786](https://github.com/shelljs/shelljs/pull/786) ([nfischer](https://github.com/nfischer)) +- Remove unnecessary shell.error checks from common tests [\#785](https://github.com/shelljs/shelljs/pull/785) ([freitagbr](https://github.com/freitagbr)) +- Add a test for ls for a single file [\#784](https://github.com/shelljs/shelljs/pull/784) ([freitagbr](https://github.com/freitagbr)) +- Wrap fs.statSync and fs.lstatSync [\#783](https://github.com/shelljs/shelljs/pull/783) ([freitagbr](https://github.com/freitagbr)) +- chore: set AVA options [\#780](https://github.com/shelljs/shelljs/pull/780) ([nfischer](https://github.com/nfischer)) +- chore: clean up refs to unsupported node versions [\#779](https://github.com/shelljs/shelljs/pull/779) ([nfischer](https://github.com/nfischer)) +- Added `-q` \(quiet\) option to `push`, `popd`, `dirs` functions. [\#777](https://github.com/shelljs/shelljs/pull/777) ([alexreg](https://github.com/alexreg)) +- feat\(cat\): number output lines \(\#750\) [\#775](https://github.com/shelljs/shelljs/pull/775) ([gcca](https://github.com/gcca)) +- refactor\(test\): update AVA and refactor tests [\#760](https://github.com/shelljs/shelljs/pull/760) ([nfischer](https://github.com/nfischer)) +- chore: add skipOnWin and skipOnUnix test helpers [\#746](https://github.com/shelljs/shelljs/pull/746) ([nfischer](https://github.com/nfischer)) +- test\(exec\): add tests for coverage [\#744](https://github.com/shelljs/shelljs/pull/744) ([nfischer](https://github.com/nfischer)) +- test\(head\): improve coverage [\#743](https://github.com/shelljs/shelljs/pull/743) ([nfischer](https://github.com/nfischer)) +- Remove PDF.js mention from README.md [\#738](https://github.com/shelljs/shelljs/pull/738) ([voy](https://github.com/voy)) +- Provide an API to pass parameters which resemble options [\#792](https://github.com/shelljs/shelljs/pull/792) ([nfischer](https://github.com/nfischer)) +- Fix ls not following links to directories by default [\#764](https://github.com/shelljs/shelljs/pull/764) ([freitagbr](https://github.com/freitagbr)) +- Add "encoding" option to exec [\#763](https://github.com/shelljs/shelljs/pull/763) ([freitagbr](https://github.com/freitagbr)) +- Merge dev into master [\#731](https://github.com/shelljs/shelljs/pull/731) ([freitagbr](https://github.com/freitagbr)) +- Deprecate common.getUserHome, advise using os.homedir instead [\#725](https://github.com/shelljs/shelljs/pull/725) ([freitagbr](https://github.com/freitagbr)) +- Echo test mocks [\#708](https://github.com/shelljs/shelljs/pull/708) ([freitagbr](https://github.com/freitagbr)) +- Safely exit by throwing an error [\#546](https://github.com/shelljs/shelljs/pull/546) ([freitagbr](https://github.com/freitagbr)) +- chore\(make\): depreciate shelljs/make [\#431](https://github.com/shelljs/shelljs/pull/431) ([ariporad](https://github.com/ariporad)) + ## [v0.7.8](https://github.com/shelljs/shelljs/tree/v0.7.8) (2017-06-07) [Full Changelog](https://github.com/shelljs/shelljs/compare/v0.7.7...v0.7.8) @@ -31,12 +198,10 @@ **Merged pull requests:** - Add node 8 to CI [\#730](https://github.com/shelljs/shelljs/pull/730) ([freitagbr](https://github.com/freitagbr)) -- Deprecate common.getUserHome, advise using os.homedir instead [\#725](https://github.com/shelljs/shelljs/pull/725) ([freitagbr](https://github.com/freitagbr)) - fix\(mkdir\): improve error handling around files [\#721](https://github.com/shelljs/shelljs/pull/721) ([nfischer](https://github.com/nfischer)) - Properly handle directories as arguments [\#713](https://github.com/shelljs/shelljs/pull/713) ([nfischer](https://github.com/nfischer)) - Add common.buffer [\#710](https://github.com/shelljs/shelljs/pull/710) ([freitagbr](https://github.com/freitagbr)) - Fix common.expand error [\#709](https://github.com/shelljs/shelljs/pull/709) ([freitagbr](https://github.com/freitagbr)) -- Echo test mocks [\#708](https://github.com/shelljs/shelljs/pull/708) ([freitagbr](https://github.com/freitagbr)) - refactor: remove unnecessary common.js imports [\#703](https://github.com/shelljs/shelljs/pull/703) ([nfischer](https://github.com/nfischer)) - Fix \#631 throw error when overwriting recently created file [\#702](https://github.com/shelljs/shelljs/pull/702) ([uttpal](https://github.com/uttpal)) - Small clarification of verbose flag [\#691](https://github.com/shelljs/shelljs/pull/691) ([zommerfelds](https://github.com/zommerfelds)) @@ -56,13 +221,11 @@ - Difference between bash ls -R and ShellJS ls -R with symlinks [\#666](https://github.com/shelljs/shelljs/issues/666) - Refactor which\(\) \(too many repeated code blocks\) [\#656](https://github.com/shelljs/shelljs/issues/656) - find\(\) raises error when unable to find any files matching, expected to return empty array. [\#653](https://github.com/shelljs/shelljs/issues/653) -- Drop support for v0.12 [\#647](https://github.com/shelljs/shelljs/issues/647) - Reformat the markdown in RELEASE.md [\#642](https://github.com/shelljs/shelljs/issues/642) - rm -rf doesn't work if the directory contains an asar archive in Electron [\#618](https://github.com/shelljs/shelljs/issues/618) - Add support for other file types in rm [\#617](https://github.com/shelljs/shelljs/issues/617) - Feature request: ls -L option [\#563](https://github.com/shelljs/shelljs/issues/563) - How to send SIGINT signal to child process launched with exec [\#518](https://github.com/shelljs/shelljs/issues/518) -- exec doesnt seem to be working [\#480](https://github.com/shelljs/shelljs/issues/480) - feature request: option to add node\_modules to the path for shelljs scripts [\#469](https://github.com/shelljs/shelljs/issues/469) - high cpu usage during synchronous exec [\#167](https://github.com/shelljs/shelljs/issues/167) @@ -113,21 +276,18 @@ - write to file [\#568](https://github.com/shelljs/shelljs/issues/568) - Cannot figure out how to disable globbing for rm [\#567](https://github.com/shelljs/shelljs/issues/567) - Switch to the ava test framework [\#560](https://github.com/shelljs/shelljs/issues/560) -- feature: echo -n [\#559](https://github.com/shelljs/shelljs/issues/559) - Option not recognized [\#556](https://github.com/shelljs/shelljs/issues/556) - chore: add @freitagbr to LGTM maintainers [\#552](https://github.com/shelljs/shelljs/issues/552) - chore: set up dev branch [\#548](https://github.com/shelljs/shelljs/issues/548) - bug: cp\(\) doesn't always copy everything [\#547](https://github.com/shelljs/shelljs/issues/547) - User-friendly lint command [\#544](https://github.com/shelljs/shelljs/issues/544) - Lint warning [\#542](https://github.com/shelljs/shelljs/issues/542) -- Possible Regression: cp from 0.6.0 to 0.7.x version [\#538](https://github.com/shelljs/shelljs/issues/538) - chore: add nodejs v7 to CI [\#537](https://github.com/shelljs/shelljs/issues/537) - error.code is not always available [\#536](https://github.com/shelljs/shelljs/issues/536) - Add shx as a dependency for testing [\#525](https://github.com/shelljs/shelljs/issues/525) - Feature request: allow `common.error\(\)` to optionally not insert a prefix and optionally not print to console [\#523](https://github.com/shelljs/shelljs/issues/523) - Feature request: Add "shelljs.unlink" [\#519](https://github.com/shelljs/shelljs/issues/519) - Sed should allow a replacement string to contain `\1` for match groups [\#507](https://github.com/shelljs/shelljs/issues/507) -- Don't kill the node process upon unexpected error [\#483](https://github.com/shelljs/shelljs/issues/483) - Usage with neodoc [\#445](https://github.com/shelljs/shelljs/issues/445) - \[ Feature idea \] synchronous sleep command [\#441](https://github.com/shelljs/shelljs/issues/441) - Improve test coverage [\#347](https://github.com/shelljs/shelljs/issues/347) @@ -197,7 +357,6 @@ - Add Brandon Freitag to maintainers/contributors [\#553](https://github.com/shelljs/shelljs/pull/553) ([freitagbr](https://github.com/freitagbr)) - Get pipe tests running on Windows. [\#550](https://github.com/shelljs/shelljs/pull/550) ([binki](https://github.com/binki)) - fix: maxdepth doesn't limit total number of copies [\#549](https://github.com/shelljs/shelljs/pull/549) ([nfischer](https://github.com/nfischer)) -- Safely exit by throwing an error [\#546](https://github.com/shelljs/shelljs/pull/546) ([freitagbr](https://github.com/freitagbr)) - Fix lint warning [\#543](https://github.com/shelljs/shelljs/pull/543) ([freitagbr](https://github.com/freitagbr)) - chore: remove v0.10 from Travis CI [\#540](https://github.com/shelljs/shelljs/pull/540) ([nfischer](https://github.com/nfischer)) - chore: add Node v7 for CI [\#539](https://github.com/shelljs/shelljs/pull/539) ([nfischer](https://github.com/nfischer)) @@ -218,7 +377,6 @@ - ShellJS in Electron package don't find ffmpeg anymore [\#516](https://github.com/shelljs/shelljs/issues/516) - Exec issues with string option introduced in 0.7.4 [\#515](https://github.com/shelljs/shelljs/issues/515) - \[ Feature \] SSH command [\#435](https://github.com/shelljs/shelljs/issues/435) -- Synchronous exec stalls permenantly when there is an error/w the shell [\#7](https://github.com/shelljs/shelljs/issues/7) **Merged pull requests:** @@ -298,7 +456,6 @@ - Stdout is empty on Git log command [\#439](https://github.com/shelljs/shelljs/issues/439) - Cannot read toString of null when using execSync [\#415](https://github.com/shelljs/shelljs/issues/415) - cp -R dir/ target fails to copy hidden files in dir [\#140](https://github.com/shelljs/shelljs/issues/140) -- Adding callback to basic commands [\#102](https://github.com/shelljs/shelljs/issues/102) - \#mv Won't Work Across Disks [\#1](https://github.com/shelljs/shelljs/issues/1) **Merged pull requests:** @@ -320,7 +477,6 @@ - docs: comment code better to help contributors [\#437](https://github.com/shelljs/shelljs/pull/437) ([nfischer](https://github.com/nfischer)) - chore\(CI\): update appveyor [\#436](https://github.com/shelljs/shelljs/pull/436) ([nfischer](https://github.com/nfischer)) - chore: test against node v6 [\#433](https://github.com/shelljs/shelljs/pull/433) ([nfischer](https://github.com/nfischer)) -- chore\(make\): depreciate shelljs/make [\#431](https://github.com/shelljs/shelljs/pull/431) ([ariporad](https://github.com/ariporad)) - docs: warn that README contains newest features [\#410](https://github.com/shelljs/shelljs/pull/410) ([nfischer](https://github.com/nfischer)) ## [v0.7.0](https://github.com/shelljs/shelljs/tree/v0.7.0) (2016-04-25) @@ -329,8 +485,6 @@ **Closed issues:** - exec\('nohup node some.js &'\) [\#426](https://github.com/shelljs/shelljs/issues/426) -- cp copy to symlinked folder [\#414](https://github.com/shelljs/shelljs/issues/414) -- Invalid version number \(0.0.1alpha1\) [\#399](https://github.com/shelljs/shelljs/issues/399) - shelljs Breaks SemVer for Alpha and Pre-Release Versions [\#390](https://github.com/shelljs/shelljs/issues/390) - Copy not accepting source end with wildcards \* when using -r on v0.6.0 [\#389](https://github.com/shelljs/shelljs/issues/389) - Support globbing in `shjs` [\#388](https://github.com/shelljs/shelljs/issues/388) @@ -349,7 +503,6 @@ - "exec" causes LiveScript interpreter \(lsc\) to hang [\#160](https://github.com/shelljs/shelljs/issues/160) - Don't modify string prototype [\#159](https://github.com/shelljs/shelljs/issues/159) - `exec\(...\).to\(file\)` should work [\#154](https://github.com/shelljs/shelljs/issues/154) -- Would like to see more async variants for cp/rm etc [\#144](https://github.com/shelljs/shelljs/issues/144) - Can't install shelljs locally instead of globally [\#136](https://github.com/shelljs/shelljs/issues/136) - shelljs and node 0.10.28 [\#125](https://github.com/shelljs/shelljs/issues/125) - Use case for global installed shelljs [\#123](https://github.com/shelljs/shelljs/issues/123) @@ -453,7 +606,6 @@ - sed\(\) should accept multiple file arguments [\#231](https://github.com/shelljs/shelljs/issues/231) - shelljs.exec\('aaa && bbb'\) blocks [\#229](https://github.com/shelljs/shelljs/issues/229) - Consider creating a GitHub Organization with more maintainers [\#223](https://github.com/shelljs/shelljs/issues/223) -- Doesn't work inside Electron [\#220](https://github.com/shelljs/shelljs/issues/220) - \[idea\] Add chmodr function. [\#219](https://github.com/shelljs/shelljs/issues/219) - Execute a file [\#211](https://github.com/shelljs/shelljs/issues/211) - Where is standard error going to? [\#209](https://github.com/shelljs/shelljs/issues/209) @@ -465,7 +617,6 @@ - Cannot recursively list all \*.js files [\#162](https://github.com/shelljs/shelljs/issues/162) - exec\(\) breaks if executed in a deleted directory [\#157](https://github.com/shelljs/shelljs/issues/157) - shjs command always exits with zero code [\#133](https://github.com/shelljs/shelljs/issues/133) -- Windows failing tests [\#127](https://github.com/shelljs/shelljs/issues/127) - touch command [\#122](https://github.com/shelljs/shelljs/issues/122) - Symbolic links are broken! [\#100](https://github.com/shelljs/shelljs/issues/100) - interpret `--` as stdin [\#55](https://github.com/shelljs/shelljs/issues/55) @@ -579,7 +730,7 @@ - Breaking: Allow -- as args separators \(fixes \#188\) [\#207](https://github.com/shelljs/shelljs/pull/207) ([nzakas](https://github.com/nzakas)) - Update .travis.yml [\#190](https://github.com/shelljs/shelljs/pull/190) ([arturadib](https://github.com/arturadib)) -- Use new child\_process.execSync instead of busywaiting [\#189](https://github.com/shelljs/shelljs/pull/189) ([devTristan](https://github.com/devTristan)) +- Use new child\_process.execSync instead of busywaiting [\#189](https://github.com/shelljs/shelljs/pull/189) ([madd512](https://github.com/madd512)) - Update README.md: explains how to access "config" [\#145](https://github.com/shelljs/shelljs/pull/145) ([kerphi](https://github.com/kerphi)) - Fix to set state.error before throw the exception [\#120](https://github.com/shelljs/shelljs/pull/120) ([abdul-martinez](https://github.com/abdul-martinez)) - Add -l and -s support to grep. [\#116](https://github.com/shelljs/shelljs/pull/116) ([idearat](https://github.com/idearat)) @@ -797,4 +948,4 @@ ## [v0.0.2pre1](https://github.com/shelljs/shelljs/tree/v0.0.2pre1) (2012-03-03) -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16257e158..1559b4d77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,22 +5,21 @@ We love to receive bug reports (we're always trying to make ShellJS more stable). If you've found a bug, please follow these steps: - - Search for any issues that may have been created already. We often receive - duplicates, and cutting down on this is helpful. If someone else has already - reported it, please ping that issue thread. - - Let us know your version of NodeJS (`node -v`), your version of ShellJS (from - `package.json`), your OS, and any other useful information. - - Give an example ShellJS command to reproduce the error. + - Please try to cut down on duplicates. Please search for issues which have + already been reported (remember to search closed issues). + - Please see [`ISSUE_TEMPLATE.md`](.github/ISSUE_TEMPLATE.md) for more + information. ## Pull requests PRs are welcome! However, we ask that you follow a few guidelines: - Please add tests for all changes/new features. - - Make sure your code passes `npm test`. Please check the CI (both Appveyor and - Travis). If you can't figure out why something doesn't work, feel free to ask - for help. - - Make changes to the documentation *within the source files*, not in the - README. Then update the README by running `node scripts/generate-docs.js`. + - Make sure your code passes `npm test`. Please check the CI. If you can't + figure out why something doesn't work, feel free to ask for help. + - Make sure you conform to our style guidelines. You can run `npm run lint` to + check style, and `npm run lint -- --fix` to automatically fix some issues. + - Make documentation changes *within the source files*, not in the README. + Update the README with `npm run gendocs`. - Please keep your PR up to date (either via rebase or by pressing the "update branch" button on Github). diff --git a/LICENSE b/LICENSE index 0f0f119be..40a2bf650 100644 --- a/LICENSE +++ b/LICENSE @@ -1,26 +1,29 @@ +BSD 3-Clause License + Copyright (c) 2012, Artur Adib All rights reserved. -You may use this project under the terms of the New BSD license as follows: - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Artur Adib nor the - names of the contributors may be used to endorse or promote products - derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 51082f47c..a0de67655 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,19 @@ # ShellJS - Unix shell commands for Node.js -[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square)](https://gitter.im/shelljs/shelljs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Travis](https://img.shields.io/travis/shelljs/shelljs/master.svg?style=flat-square&label=unix)](https://travis-ci.org/shelljs/shelljs) -[![AppVeyor](https://img.shields.io/appveyor/ci/shelljs/shelljs/master.svg?style=flat-square&label=windows)](https://ci.appveyor.com/project/shelljs/shelljs/branch/master) -[![Codecov](https://img.shields.io/codecov/c/github/shelljs/shelljs/master.svg?style=flat-square&label=coverage)](https://codecov.io/gh/shelljs/shelljs) +[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/shelljs/shelljs/main.yml?style=flat-square&logo=github)](https://github.com/shelljs/shelljs/actions/workflows/main.yml) +[![Codecov](https://img.shields.io/codecov/c/github/shelljs/shelljs/main.svg?style=flat-square&label=coverage)](https://codecov.io/gh/shelljs/shelljs) [![npm version](https://img.shields.io/npm/v/shelljs.svg?style=flat-square)](https://www.npmjs.com/package/shelljs) [![npm downloads](https://img.shields.io/npm/dm/shelljs.svg?style=flat-square)](https://www.npmjs.com/package/shelljs) -ShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell +ShellJS is a portable **(Windows/Linux/macOS)** implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. You can also install it globally so you can run it from outside Node projects - say goodbye to those gnarly Bash scripts! -ShellJS is proudly tested on every node release since `v4`! +ShellJS is proudly tested on every LTS node release since `v18`! -The project is [unit-tested](http://travis-ci.org/shelljs/shelljs) and battle-tested in projects like: +The project is unit-tested and battle-tested in projects like: + [Firebug](http://getfirebug.com/) - Firefox's infamous debugger + [JSHint](http://jshint.com) & [ESLint](http://eslint.org/) - popular JavaScript linters @@ -48,13 +46,18 @@ $ shx touch foo/bar.txt $ shx rm -rf foo ``` +## Plugin API + +ShellJS now supports third-party plugins! You can learn more about using plugins +and writing your own ShellJS commands in [the +wiki](https://github.com/shelljs/shelljs/wiki/Using-ShellJS-Plugins). + ## A quick note about the docs For documentation on all the latest features, check out our [README](https://github.com/shelljs/shelljs). To read docs that are consistent with the latest release, check out [the npm -page](https://www.npmjs.com/package/shelljs) or -[shelljs.org](http://documentup.com/shelljs/shelljs). +page](https://www.npmjs.com/package/shelljs). ## Installing @@ -94,6 +97,16 @@ if (shell.exec('git commit -am "Auto-commit"').code !== 0) { } ``` +## Exclude options + +If you need to pass a parameter that looks like an option, you can do so like: + +```js +shell.grep('--', '-v', 'path/to/file'); // Search for "-v", no grep options + +shell.cp('-R', '-dir', 'outdir'); // If already using an option, you're done +``` + ## Global vs. Local We no longer recommend using a global-import for ShellJS (i.e. @@ -107,6 +120,13 @@ var shell = require('shelljs'); shell.echo('hello world'); ``` +Alternatively, we also support importing as a module with: + +```javascript +import shell from 'shelljs'; +shell.echo('hello world'); +``` + @@ -115,14 +135,18 @@ shell.echo('hello world'); All commands run synchronously, unless otherwise stated. All commands accept standard bash globbing characters (`*`, `?`, etc.), -compatible with the [node glob module](https://github.com/isaacs/node-glob). +compatible with [`fast-glob`](https://www.npmjs.com/package/fast-glob). For less-commonly used commands and features, please check out our [wiki page](https://github.com/shelljs/shelljs/wiki). -### cat(file [, file ...]) -### cat(file_array) +### cat([options,] file [, file ...]) +### cat([options,] file_array) + +Available options: + ++ `-n`: number all output lines Examples: @@ -132,14 +156,16 @@ var str = cat('file1', 'file2'); var str = cat(['file1', 'file2']); // same as above ``` -Returns a string containing the given file, or a concatenated string -containing the files if more than one file is given (a new line character is -introduced between each file). +Returns a [ShellString](#shellstringstr) containing the given file, or a +concatenated string containing the files if more than one file is given (a +new line character is introduced between each file). ### cd([dir]) + Changes to directory `dir` for the duration of the script. Changes to home -directory if no argument is supplied. +directory if no argument is supplied. Returns a +[ShellString](#shellstringstr) to indicate success or failure. ### chmod([options,] octal_mode || octal_string, file) @@ -148,7 +174,7 @@ directory if no argument is supplied. Available options: + `-v`: output a diagnostic for every file processed -+ `-c`: like verbose but report only when a change is made ++ `-c`: like verbose, but report only when a change is made + `-R`: change files and directories recursively Examples: @@ -165,21 +191,64 @@ absolute permissions in octal form or expressing the changes in symbols. This command tries to mimic the POSIX behavior as much as possible. Notable exceptions: -+ In symbolic modes, 'a-r' and '-r' are identical. No consideration is - given to the umask. -+ There is no "quiet" option since default behavior is to run silent. ++ In symbolic modes, `a-r` and `-r` are identical. No consideration is + given to the `umask`. ++ There is no "quiet" option, since default behavior is to run silent. ++ Windows OS uses a very different permission model than POSIX. `chmod()` + does its best on Windows, but there are limits to how file permissions can + be set. Note that WSL (Windows subsystem for Linux) **does** follow POSIX, + so cross-platform compatibility should not be a concern there. + +Returns a [ShellString](#shellstringstr) indicating success or failure. + + +### cmd(arg1[, arg2, ...] [, options]) + +Available options: + ++ `cwd: directoryPath`: change the current working directory only for this + cmd() invocation. ++ `maxBuffer: num`: Raise or decrease the default buffer size for + stdout/stderr. ++ `timeout`: Change the default timeout. + +Examples: + +```javascript +var version = cmd('node', '--version').stdout; +cmd('git', 'commit', '-am', `Add suport for node ${version}`); +console.log(cmd('echo', '1st arg', '2nd arg', '3rd arg').stdout) +console.log(cmd('echo', 'this handles ;, |, &, etc. as literal characters').stdout) +``` + +Executes the given command synchronously. This is intended as an easier +alternative for [exec()](#execcommand--options--callback), with better +security around globbing, comamnd injection, and variable expansion. This is +guaranteed to only run one external command, and won't give special +treatment for any shell characters (ex. this treats `|` as a literal +character, not as a shell pipeline). +This returns a [ShellString](#shellstringstr). + +By default, this performs globbing on all platforms, but you can disable +this with `set('-f')`. + +This **does not** support asynchronous mode. If you need asynchronous +command execution, check out [execa](https://www.npmjs.com/package/execa) or +the node builtin `child_process.execFile()` instead. ### cp([options,] source [, source ...], dest) ### cp([options,] source_array, dest) + Available options: + `-f`: force (default behavior) + `-n`: no-clobber -+ `-u`: only copy if source is newer than dest ++ `-u`: only copy if `source` is newer than `dest` + `-r`, `-R`: recursive + `-L`: follow symlinks + `-P`: don't follow symlinks ++ `-p`: preserve file mode, ownership, and timestamps Examples: @@ -190,7 +259,8 @@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above ``` -Copies files. +Copies files. Returns a [ShellString](#shellstringstr) indicating success +or failure. ### pushd([options,] [dir | '-N' | '+N']) @@ -198,10 +268,11 @@ Copies files. Available options: + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. ++ `-q`: Suppresses output to the console. Arguments: -+ `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. ++ `dir`: Sets the current working directory to the top of the stack, then executes the equivalent of `cd dir`. + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. @@ -213,13 +284,15 @@ pushd('/etc'); // Returns /etc /usr pushd('+1'); // Returns /usr /etc ``` -Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. +Save the current directory on the top of the directory stack and then `cd` to `dir`. With no arguments, `pushd` exchanges the top two directories. Returns an array of paths in the stack. + ### popd([options,] ['-N' | '+N']) Available options: -+ `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. ++ `-n`: Suppress the normal directory change when removing directories from the stack, so that only the stack is manipulated. ++ `-q`: Suppresses output to the console. Arguments: @@ -236,25 +309,28 @@ popd(); // '/usr' echo(process.cwd()); // '/usr' ``` -When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. +When no arguments are given, `popd` removes the top directory from the stack and performs a `cd` to the new top directory. The elements are numbered from 0, starting at the first directory listed with dirs (i.e., `popd` is equivalent to `popd +0`). Returns an array of paths in the stack. + ### dirs([options | '+N' | '-N']) Available options: + `-c`: Clears the directory stack by deleting all of the elements. ++ `-q`: Suppresses output to the console. Arguments: + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. -Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. +Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if `+N` or `-N` was specified. -See also: pushd, popd +See also: `pushd`, `popd` ### echo([options,] string [, string ...]) + Available options: + `-e`: interpret backslash escapes (default) @@ -268,18 +344,21 @@ var str = echo('hello world'); echo('-n', 'no newline at end'); ``` -Prints string to stdout, and returns string with additional utility methods -like `.to()`. +Prints `string` to stdout, and returns a [ShellString](#shellstringstr). ### exec(command [, options] [, callback]) -Available options (all `false` by default): + +Available options: + `async`: Asynchronous execution. If a callback is provided, it will be set to - `true`, regardless of the passed value. -+ `silent`: Do not echo program output to console. + `true`, regardless of the passed value (default: `false`). ++ `fatal`: Exit upon error (default: `false`). ++ `silent`: Do not echo program output to console (default: `false`). ++ `encoding`: Character encoding to use. Affects the values returned to stdout and stderr, and + what is written to stdout and stderr when not in silent mode (default: `'utf8'`). + and any option available to Node.js's - [child_process.exec()](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) + [`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) Examples: @@ -298,22 +377,24 @@ exec('some_long_running_process', function(code, stdout, stderr) { }); ``` -Executes the given `command` _synchronously_, unless otherwise specified. When in synchronous -mode, this returns a ShellString (compatible with ShellJS v0.6.x, which returns an object -of the form `{ code:..., stdout:... , stderr:... }`). Otherwise, this returns the child process -object, and the `callback` gets the arguments `(code, stdout, stderr)`. +Executes the given `command` _synchronously_, unless otherwise specified. +When in synchronous mode, this returns a [ShellString](#shellstringstr). +Otherwise, this returns the child process object, and the `callback` +receives the arguments `(code, stdout, stderr)`. Not seeing the behavior you want? `exec()` runs everything through `sh` by default (or `cmd.exe` on Windows), which differs from `bash`. If you need bash-specific behavior, try out the `{shell: 'path/to/bash'}` option. -**Note:** For long-lived processes, it's best to run `exec()` asynchronously as -the current synchronous implementation uses a lot of CPU. This should be getting -fixed soon. +**Security note:** as `shell.exec()` executes an arbitrary string in the +system shell, it is **critical** to properly sanitize user input to avoid +**command injection**. For more context, consult the [Security +Guidelines](https://github.com/shelljs/shelljs/wiki/Security-guidelines). ### find(path [, path ...]) ### find(path_array) + Examples: ```javascript @@ -322,32 +403,44 @@ find(['src', 'lib']); // same as above find('.').filter(function(file) { return file.match(/\.js$/); }); ``` -Returns array of all files (however deep) in the given paths. +Returns a [ShellString](#shellstringstr) (with array-like properties) of all +files (however deep) in the given paths. The main difference from `ls('-R', path)` is that the resulting file names -include the base directories, e.g. `lib/resources/file1` instead of just `file1`. +include the base directories (e.g., `lib/resources/file1` instead of just `file1`). ### grep([options,] regex_filter, file [, file ...]) ### grep([options,] regex_filter, file_array) + Available options: -+ `-v`: Inverse the sense of the regex and print the lines not matching the criteria. -+ `-l`: Print only filenames of matching files ++ `-v`: Invert `regex_filter` (only print non-matching lines). ++ `-l`: Print only filenames of matching files. ++ `-i`: Ignore case. ++ `-n`: Print line numbers. ++ `-B `: Show `` lines before each result. ++ `-A `: Show `` lines after each result. ++ `-C `: Show `` lines before and after each result. -B and -A override this option. Examples: ```javascript grep('-v', 'GLOBAL_VARIABLE', '*.js'); grep('GLOBAL_VARIABLE', '*.js'); +grep('-B', 3, 'GLOBAL_VARIABLE', '*.js'); +grep({ '-B': 3 }, 'GLOBAL_VARIABLE', '*.js'); +grep({ '-B': 3, '-C': 2 }, 'GLOBAL_VARIABLE', '*.js'); ``` -Reads input string from given files and returns a string containing all lines of the -file that match the given `regex_filter`. +Reads input string from given files and returns a +[ShellString](#shellstringstr) containing all lines of the @ file that match +the given `regex_filter`. ### head([{'-n': \},] file [, file ...]) ### head([{'-n': \},] file_array) + Available options: + `-n `: Show the first `` lines of the files @@ -360,10 +453,11 @@ var str = head('file1', 'file2'); var str = head(['file1', 'file2']); // same as above ``` -Read the start of a file. +Read the start of a `file`. Returns a [ShellString](#shellstringstr). ### ln([options,] source, dest) + Available options: + `-s`: symlink @@ -376,39 +470,50 @@ ln('file', 'newlink'); ln('-sf', 'file', 'existing'); ``` -Links source to dest. Use -f to force the link, should dest already exist. +Links `source` to `dest`. Use `-f` to force the link, should `dest` already +exist. Returns a [ShellString](#shellstringstr) indicating success or +failure. ### ls([options,] [path, ...]) ### ls([options,] path_array) + Available options: + `-R`: recursive + `-A`: all files (include files beginning with `.`, except for `.` and `..`) + `-L`: follow symlinks + `-d`: list directories themselves, not their contents -+ `-l`: list objects representing each file, each with fields containing `ls - -l` output fields. See - [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats) - for more info ++ `-l`: provides more details for each file. Specifically, each file is + represented by a structured object with separate fields for file + metadata (see + [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)). The + return value also overrides `.toString()` to resemble `ls -l`'s + output format for human readability, but programmatic usage should + depend on the stable object format rather than the `.toString()` + representation. Examples: ```javascript ls('projs/*.js'); +ls('projs/**/*.js'); // Find all js files recursively in projs ls('-R', '/users/me', '/tmp'); ls('-R', ['/users/me', '/tmp']); // same as above ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...} ``` -Returns array of files in the given path, or in current directory if no path provided. +Returns a [ShellString](#shellstringstr) (with array-like properties) of all +the files in the given `path`, or files in the current directory if no +`path` is provided. ### mkdir([options,] dir [, dir ...]) ### mkdir([options,] dir_array) + Available options: -+ `-p`: full path (will create intermediate dirs if necessary) ++ `-p`: full path (and create intermediate directories, if necessary) Examples: @@ -417,11 +522,13 @@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above ``` -Creates directories. +Creates directories. Returns a [ShellString](#shellstringstr) indicating +success or failure. ### mv([options ,] source [, source ...], dest') ### mv([options ,] source_array, dest') + Available options: + `-f`: force (default behavior) @@ -435,15 +542,18 @@ mv('file1', 'file2', 'dir/'); mv(['file1', 'file2'], 'dir/'); // same as above ``` -Moves files. +Moves `source` file(s) to `dest`. Returns a [ShellString](#shellstringstr) +indicating success or failure. ### pwd() -Returns the current directory. + +Returns the current directory as a [ShellString](#shellstringstr). ### rm([options,] file [, file ...]) ### rm([options,] file_array) + Available options: + `-f`: force @@ -457,24 +567,26 @@ rm('some_file.txt', 'another_file.txt'); rm(['some_file.txt', 'another_file.txt']); // same as above ``` -Removes files. +Removes files. Returns a [ShellString](#shellstringstr) indicating success +or failure. ### sed([options,] search_regex, replacement, file [, file ...]) ### sed([options,] search_regex, replacement, file_array) + Available options: -+ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ ++ `-i`: Replace contents of `file` in-place. _Note that no backups will be created!_ Examples: ```javascript sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); -sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); ``` -Reads an input string from `files` and performs a JavaScript `replace()` on the input -using the given search regex and replacement string or function. Returns the new string after replacement. +Reads an input string from `file`s, line by line, and performs a JavaScript `replace()` on +each of the lines from the input string using the given `search_regex` and `replacement` string or +function. Returns the new [ShellString](#shellstringstr) after replacement. Note: @@ -485,8 +597,13 @@ using the `$n` syntax: sed(/(\w+)\s(\w+)/, '$2, $1', 'file.txt'); ``` +Also, like unix `sed`, ShellJS `sed` runs replacements on each line from the input file +(split by '\n') separately, so `search_regex`es that span more than one line (or include '\n') +will not match anything and nothing will be replaced. + ### set(options) + Available options: + `+/-e`: exit upon error (`config.fatal`) @@ -500,14 +617,15 @@ set('-e'); // exit upon first error set('+e'); // this undoes a "set('-e')" ``` -Sets global configuration variables +Sets global configuration variables. ### sort([options,] file [, file ...]) ### sort([options,] file_array) + Available options: -+ `-r`: Reverse the result of comparisons ++ `-r`: Reverse the results + `-n`: Compare according to numerical value Examples: @@ -517,15 +635,17 @@ sort('foo.txt', 'bar.txt'); sort('-r', 'foo.txt'); ``` -Return the contents of the files, sorted line-by-line. Sorting multiple -files mixes their content, just like unix sort does. +Return the contents of the `file`s, sorted line-by-line as a +[ShellString](#shellstringstr). Sorting multiple files mixes their content +(just as unix `sort` does). ### tail([{'-n': \},] file [, file ...]) ### tail([{'-n': \},] file_array) + Available options: -+ `-n `: Show the last `` lines of the files ++ `-n `: Show the last `` lines of `file`s Examples: @@ -535,7 +655,7 @@ var str = tail('file1', 'file2'); var str = tail(['file1', 'file2']); // same as above ``` -Read the end of a file. +Read the end of a `file`. Returns a [ShellString](#shellstringstr). ### tempdir() @@ -551,6 +671,7 @@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.ht ### test(expression) + Available expression primaries: + `'-b', 'path'`: true if path is a block device @@ -566,10 +687,11 @@ Examples: ```javascript if (test('-d', path)) { /* do something with dir */ }; -if (!test('-f', path)) continue; // skip if it's a regular file +if (!test('-f', path)) continue; // skip if it's not a regular file ``` -Evaluates expression using the available primaries and returns corresponding value. +Evaluates `expression` using the available primaries and returns +corresponding boolean value. ### ShellString.prototype.to(file) @@ -581,8 +703,9 @@ cat('input.txt').to('output.txt'); ``` Analogous to the redirection operator `>` in Unix, but works with -ShellStrings (such as those returned by `cat`, `grep`, etc). _Like Unix -redirections, `to()` will overwrite any existing file!_ +`ShellStrings` (such as those returned by `cat`, `grep`, etc.). _Like Unix +redirections, `to()` will overwrite any existing file!_ Returns the same +[ShellString](#shellstringstr) this operated on, to support chaining. ### ShellString.prototype.toEnd(file) @@ -594,33 +717,42 @@ cat('input.txt').toEnd('output.txt'); ``` Analogous to the redirect-and-append operator `>>` in Unix, but works with -ShellStrings (such as those returned by `cat`, `grep`, etc). +`ShellStrings` (such as those returned by `cat`, `grep`, etc.). Returns the +same [ShellString](#shellstringstr) this operated on, to support chaining. ### touch([options,] file [, file ...]) ### touch([options,] file_array) + Available options: + `-a`: Change only the access time + `-c`: Do not create any files + `-m`: Change only the modification time -+ `-d DATE`: Parse DATE and use it instead of current time -+ `-r FILE`: Use FILE's times instead of current time ++ `{'-d': someDate}`, `{date: someDate}`: Use a `Date` instance (ex. `someDate`) + instead of current time ++ `{'-r': file}`, `{reference: file}`: Use `file`'s times instead of current + time Examples: ```javascript touch('source.js'); -touch('-c', '/path/to/some/dir/source.js'); -touch({ '-r': FILE }, '/path/to/some/dir/source.js'); +touch('-c', 'path/to/file.js'); +touch({ '-r': 'referenceFile.txt' }, 'path/to/file.js'); +touch({ '-d': new Date('December 17, 1995 03:24:00'), '-m': true }, 'path/to/file.js'); +touch({ date: new Date('December 17, 1995 03:24:00') }, 'path/to/file.js'); ``` -Update the access and modification times of each FILE to the current time. -A FILE argument that does not exist is created empty, unless -c is supplied. -This is a partial implementation of *[touch(1)](http://linux.die.net/man/1/touch)*. +Update the access and modification times of each file to the current time. +A file argument that does not exist is created empty, unless `-c` is supplied. +This is a partial implementation of +[`touch(1)`](http://linux.die.net/man/1/touch). Returns a +[ShellString](#shellstringstr) indicating success or failure. ### uniq([options,] [input, [output]]) + Available options: + `-i`: Ignore case while comparing @@ -635,7 +767,8 @@ uniq('-i', 'foo.txt'); uniq('-cd', 'foo.txt', 'bar.txt'); ``` -Filter adjacent matching lines from input +Filter adjacent matching lines from `input`. Returns a +[ShellString](#shellstringstr). ### which(command) @@ -646,38 +779,55 @@ Examples: var nodeExec = which('node'); ``` -Searches for `command` in the system's PATH. On Windows, this uses the +Searches for `command` in the system's `PATH`. On Windows, this uses the `PATHEXT` variable to append the extension if it's not already executable. -Returns string containing the absolute path to the command. +Returns a [ShellString](#shellstringstr) containing the absolute path to +`command`. ### exit(code) -Exits the current process with the given exit code. + +Exits the current process with the given exit `code`. ### error() + Tests if error occurred in the last command. Returns a truthy value if an -error returned and a falsy value otherwise. +error returned, or a falsy value otherwise. **Note**: do not rely on the return value to be an error message. If you need the last error message, use the `.stderr` attribute from the last command's return value instead. +### errorCode() + +Returns the error code from the last command. + + ### ShellString(str) Examples: ```javascript -var foo = ShellString('hello world'); +var foo = new ShellString('hello world'); ``` -Turns a regular string into a string-like object similar to what each -command returns. This has special methods, like `.to()` and `.toEnd()` +This is a dedicated type returned by most ShellJS methods, which wraps a +string (or array) value. This has all the string (or array) methods, but +also exposes extra methods: [`.to()`](#shellstringprototypetofile), +[`.toEnd()`](#shellstringprototypetoendfile), and all the pipe-able methods +(ex. `.cat()`, `.grep()`, etc.). This can be easily converted into a string +by calling `.toString()`. + +This type also exposes the corresponding command's stdout, stderr, and +return status code via the `.stdout` (string), `.stderr` (string), and +`.code` (number) properties respectively. ### env['VAR_NAME'] + Object containing environment variables (both getter and setter). Shortcut -to process.env. +to `process.env`. ### Pipes @@ -685,7 +835,7 @@ Examples: ```javascript grep('foo', 'file1.txt', 'file2.txt').sed(/o/g, 'a').to('output.txt'); -echo('files with o\'s in the name:\n' + ls().grep('o')); +echo("files with o's in the name:\n" + ls().grep('o')); cat('test.js').exec('node'); // pipe to exec() call ``` @@ -722,9 +872,9 @@ cp('this_file_does_not_exist', '/dev/null'); // throws Error here /* more commands... */ ``` -If `true` the script will throw a Javascript error when any shell.js +If `true`, the script will throw a Javascript error when any shell.js command encounters an error. Default is `false`. This is analogous to -Bash's `set -e` +Bash's `set -e`. ### config.verbose @@ -745,7 +895,14 @@ rm -rf foo.txt bar.txt exec echo hello ``` -### config.globOptions +### config.globOptions (deprecated) + +**Deprecated**: we recommend that you do not edit `config.globOptions`. +Support for this configuration option may be changed or removed in a future +ShellJS release. + +**Breaking change**: ShellJS v0.8.x uses `node-glob`. Starting with ShellJS +v0.9.x, `config.globOptions` is compatible with `fast-glob`. Example: @@ -753,7 +910,11 @@ Example: config.globOptions = {nodir: true}; ``` -Use this value for calls to `glob.sync()` instead of the default options. +`config.globOptions` changes how ShellJS expands glob (wildcard) +expressions. See +[fast-glob](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#options-3) +for available options. Be aware that modifying `config.globOptions` **may +break ShellJS functionality.** ### config.reset() @@ -768,7 +929,7 @@ shell.config.reset(); // reset to original state /* ... */ ``` -Reset shell.config to the defaults: +Reset `shell.config` to the defaults: ```javascript { @@ -783,6 +944,6 @@ Reset shell.config to the defaults: ## Team -| [![Nate Fischer](https://avatars.githubusercontent.com/u/5801521?s=130)](https://github.com/nfischer) | [![Brandon Freitag](https://avatars1.githubusercontent.com/u/5988055?v=3&s=130)](http://github.com/freitagbr) | -|:---:|:---:| -| [Nate Fischer](https://github.com/nfischer) | [Brandon Freitag](http://github.com/freitagbr) | +| [![Nate Fischer](https://avatars.githubusercontent.com/u/5801521?s=130)](https://github.com/nfischer) | +|:---:| +| [Nate Fischer](https://github.com/nfischer) | diff --git a/RELEASE.md b/RELEASE.md index 80aae7f7b..751cad391 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,6 +1,6 @@ # Release steps -1. Ensure master passes CI tests +1. Ensure main branch passes CI tests 2. Bump version, create tags, push, and release: - `$ npm run ` - `major` - breaking API changes @@ -10,5 +10,5 @@ - `$ npm run changelog` - Manually verify that the changelog makes sense - `$ git push` -4. Generate the documentup website by visiting - http://documentup.com/shelljs/shelljs/__recompile in your browser +4. Update https://github.com/shelljs/shelljs/releases to create a new release + from the latest git tag diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index ad698e448..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,30 +0,0 @@ -environment: - matrix: - - nodejs_version: '8' - - nodejs_version: '7' - - nodejs_version: '6' - - nodejs_version: '5' - - nodejs_version: '4' - -version: '{build}' - -# Install scripts. (runs after repo cloning) -install: - - ps: Install-Product node $env:nodejs_version - - npm -g install npm@latest - - set PATH=%APPDATA%\npm;%PATH% - - node --version - - npm --version - - npm install - -matrix: - fast_finish: true - -# No need for MSBuild on this project -build: off - -test_script: - - npm test - -on_success: - - npm run codecov -- -f coverage/lcov.info diff --git a/bin/shjs b/bin/shjs deleted file mode 100755 index 75ca58b9d..000000000 --- a/bin/shjs +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env node -require('../global'); - -if (process.argv.length < 3) { - console.log('ShellJS: missing argument (script name)'); - console.log(); - process.exit(1); -} - -var args, - scriptName = process.argv[2]; -env['NODE_PATH'] = __dirname + '/../..'; - -if (!scriptName.match(/\.js/) && !scriptName.match(/\.coffee/)) { - if (test('-f', scriptName + '.js')) - scriptName += '.js'; - if (test('-f', scriptName + '.coffee')) - scriptName += '.coffee'; -} - -if (!test('-f', scriptName)) { - console.log('ShellJS: script not found ('+scriptName+')'); - console.log(); - process.exit(1); -} - -args = process.argv.slice(3); - -for (var i = 0, l = args.length; i < l; i++) { - if (args[i][0] !== "-"){ - args[i] = '"' + args[i] + '"'; // fixes arguments with multiple words - } -} - -var path = require('path'); -var extensions = require('interpret').extensions; -var rechoir = require('rechoir'); -rechoir.prepare(extensions, scriptName); -require(require.resolve(path.resolve(process.cwd(), scriptName))); diff --git a/commands.js b/commands.js deleted file mode 100644 index f31adb214..000000000 --- a/commands.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = [ - 'cat', - 'cd', - 'chmod', - 'cp', - 'dirs', - 'echo', - 'exec', - 'find', - 'grep', - 'head', - 'ln', - 'ls', - 'mkdir', - 'mv', - 'pwd', - 'rm', - 'sed', - 'set', - 'sort', - 'tail', - 'tempdir', - 'test', - 'to', - 'toEnd', - 'touch', - 'uniq', - 'which', -]; diff --git a/global.js b/global.js index b232e66d5..e061f5a1e 100644 --- a/global.js +++ b/global.js @@ -1,12 +1,15 @@ /* eslint no-extend-native: 0 */ -var shell = require('./shell.js'); +var shell = require('./shell'); var common = require('./src/common'); + Object.keys(shell).forEach(function (cmd) { global[cmd] = shell[cmd]; }); var _to = require('./src/to'); + String.prototype.to = common.wrap('to', _to); var _toEnd = require('./src/toEnd'); + String.prototype.toEnd = common.wrap('toEnd', _toEnd); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..cdaeeed0a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6431 @@ +{ + "name": "shelljs", + "version": "0.10.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "shelljs", + "version": "0.10.0", + "license": "BSD-3-Clause", + "dependencies": { + "execa": "^5.1.1", + "fast-glob": "^3.3.2" + }, + "devDependencies": { + "ava": "^6.2.0", + "chalk": "^4.1.2", + "coffee-script": "^1.12.7", + "eslint": "^8.2.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.31.0", + "js-yaml": "^4.1.0", + "nyc": "^17.1.0", + "shelljs-changelog": "^0.2.6", + "shelljs-release": "^0.5.3", + "shx": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "dev": true, + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@vercel/nft": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.27.10.tgz", + "integrity": "sha512-zbaF9Wp/NsZtKLE4uVmL3FyfFwlpDyuymQM1kPbeT0mVOHKDQQNjnnfslB3REg3oZprmNFJuh3pkHBk2qAaizg==", + "dev": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0-rc.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true + }, + "node_modules/ava": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.2.0.tgz", + "integrity": "sha512-+GZk5PbyepjiO/68hzCZCUepQOQauKfNnI7sA4JukBTg97jD7E+tDKEA7OhGOGr6EorNNMM9+jqvgHVOTOzG4w==", + "dev": true, + "dependencies": { + "@vercel/nft": "^0.27.5", + "acorn": "^8.13.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^9.0.2", + "chalk": "^5.3.0", + "chunkd": "^2.0.1", + "ci-info": "^4.0.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.3.7", + "emittery": "^1.0.3", + "figures": "^6.1.0", + "globby": "^14.0.2", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.0.0", + "ms": "^2.1.3", + "p-map": "^7.0.2", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.1.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || >=23" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/ava/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001715", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", + "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true + }, + "node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", + "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)", + "dev": true, + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.144", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.144.tgz", + "integrity": "sha512-eJIaMRKeAzxfBSxtjYnoIAw/tdD6VIH6tHBZepZnAbE3Gyqqs5mGN87DvcldPUbVkIljTK8pY0CMcUljP64lfQ==", + "dev": true + }, + "node_modules/emittery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.1.0.tgz", + "integrity": "sha512-rsX7ktqARv/6UQDgMaLfIqUWAEzzbCQiVh7V9rhDXp6c37yoJcks12NVD+XPkgl4AEavmNhVfrhGoqYwIsMYYA==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/matcher/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/memoize": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz", + "integrity": "sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs-changelog": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/shelljs-changelog/-/shelljs-changelog-0.2.6.tgz", + "integrity": "sha512-+jghG+Cdksxc4bHyzQzSjWC+ULcUWX9SVzKBE8J6ShIb1yKAS2m10GNrqNquLS9uqSlGrRZPAyIXLxfrmmd+BA==", + "dev": true, + "dependencies": { + "shelljs": "^0.8.5", + "shelljs-plugin-sleep": "^0.2.1" + }, + "bin": { + "shelljs-changelog": "index.js" + } + }, + "node_modules/shelljs-plugin-sleep": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/shelljs-plugin-sleep/-/shelljs-plugin-sleep-0.2.2.tgz", + "integrity": "sha512-w4yozqtrxfqeybp1lrAPmcb4+RfLfkNtHqLMOGvIXuWxNcqDdttREX+DhXy3F8Xfp5ll+/WQLLEgJbH7RCHqWQ==", + "dev": true, + "engines": { + "node": ">=4.0.0" + }, + "optionalDependencies": { + "sleep": "^3.0.1" + }, + "peerDependencies": { + "shelljs": "^0.8.5" + } + }, + "node_modules/shelljs-release": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/shelljs-release/-/shelljs-release-0.5.3.tgz", + "integrity": "sha512-9L15opORTyWZSn7eIm+rmdo4hwgwf99bXqIAPS572KdX9KjqBcdex7FvOrCI8YxaAxosYfS73rVeJ22SkWIxGA==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "minimist": "^1.2.0", + "shelljs": "^0.8.5" + }, + "bin": { + "shelljs-release": "index.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs-release/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs-release/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs-release/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/shelljs-release/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/shelljs-release/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/shelljs-release/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs-release/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shx": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz", + "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.8", + "shelljs": "^0.9.2" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/shx/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/shx/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/shx/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/shx/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shx/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shx/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/shx/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shx/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shx/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shx/node_modules/shelljs": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz", + "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "fast-glob": "^3.3.2", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/shx/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sleep": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", + "integrity": "sha512-dtegFdlKDk05SQzlA29LVj8gzf01bTvtSOHCt+JwYRXEmx3qp85OZ+MpB65IYwxTNylwSmq8mOcsa4MitU8PpQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "nan": ">=2.0.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/supertap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/supertap/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "dev": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 6303ffe1c..243271d9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shelljs", - "version": "0.7.8", + "version": "0.10.0", "description": "Portable Unix shell commands for Node.js", "keywords": [ "shelljs", @@ -23,52 +23,66 @@ "license": "BSD-3-Clause", "homepage": "http://github.com/shelljs/shelljs", "main": "./shell.js", + "exports": { + ".": "./shell.js", + "./global": "./global.js", + "./global.js": "./global.js", + "./make": "./make.js", + "./make.js": "./make.js", + "./package": "./package.json", + "./package.json": "./package.json", + "./plugin": "./plugin.js", + "./plugin.js": "./plugin.js" + }, "files": [ - "commands.js", "global.js", "make.js", "plugin.js", "shell.js", - "bin", "src" ], "scripts": { + "check-node-support": "node scripts/check-node-support", "posttest": "npm run lint", - "test": "nyc --reporter=text --reporter=lcov ava --serial test/*.js", - "test-no-coverage": "ava --serial test/*.js", + "test": "ava", + "test-with-coverage": "nyc --reporter=text --reporter=lcov ava", "gendocs": "node scripts/generate-docs", "lint": "eslint .", - "after-travis": "travis-check-changes", "changelog": "shelljs-changelog", - "codecov": "codecov", "release:major": "shelljs-release major", "release:minor": "shelljs-release minor", "release:patch": "shelljs-release patch" }, - "bin": { - "shjs": "./bin/shjs" - }, "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "execa": "^5.1.1", + "fast-glob": "^3.3.2" + }, + "ava": { + "serial": true, + "workerThreads": false, + "powerAssert": false, + "files": [ + "test/*.js" + ], + "helpers": [ + "test/resources/**", + "test/utils/**" + ] }, "devDependencies": { - "ava": "^0.21.0", - "chalk": "^1.1.3", - "codecov": "^1.0.1", - "coffee-script": "^1.10.0", - "eslint": "^2.0.0", - "eslint-config-airbnb-base": "^3.0.0", - "eslint-plugin-import": "^1.11.1", - "nyc": "^10.0.0", - "shelljs-changelog": "^0.2.0", - "shelljs-release": "^0.2.0", - "shx": "^0.2.0", - "travis-check-changes": "^0.2.0" + "ava": "^6.2.0", + "chalk": "^4.1.2", + "coffee-script": "^1.12.7", + "eslint": "^8.2.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.31.0", + "js-yaml": "^4.1.0", + "nyc": "^17.1.0", + "shelljs-changelog": "^0.2.6", + "shelljs-release": "^0.5.3", + "shx": "^0.4.0" }, - "optionalDependencies": {}, "engines": { - "node": ">=4" + "node": ">=18" } } diff --git a/plugin.js b/plugin.js index f879ab320..2e1585092 100644 --- a/plugin.js +++ b/plugin.js @@ -1,4 +1,4 @@ -// Various utilties exposed to plugins +// Various utilities exposed to plugins require('./shell'); // Create the ShellJS instance (mandatory) diff --git a/scripts/check-node-support.js b/scripts/check-node-support.js new file mode 100755 index 000000000..df2065e17 --- /dev/null +++ b/scripts/check-node-support.js @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +var assert = require('assert'); +var path = require('path'); + +// eslint-disable-next-line import/no-extraneous-dependencies +var yaml = require('js-yaml'); + +var shell = require('..'); + +// This is the authoritative list of supported node versions. +var MIN_NODE_VERSION = 18; +var MAX_NODE_VERSION = 26; + +// Ideally this map should be empty, however we can pin node releases to +// specific versions if necessary to workaround bugs. See +// https://github.com/shelljs/shelljs/issues/1180. +var pinnedNodeVersions = { + // Format: + // majorVersionInt: 'full.node.version', + // Example: + // 22: '22.9.0', +}; + +function checkReadme(minNodeVersion) { + var start = ''; + var stop = ''; + var formattedMinVersion = '`v' + minNodeVersion + '`'; + var expectedReadmeRegex = new RegExp( + start + '\\s*' + formattedMinVersion + '\\s*' + stop, '', + ); + var readme = path.join(__dirname, '..', 'README.md'); + var match = shell.grep(expectedReadmeRegex, readme); + if (!match.toString().trim()) { + var msg = 'Update README to specify the min supported version. Look for "' + + start + '"'; + throw new Error(msg); + } +} + +function checkEngines(minNodeVersion, packageJson) { + var expectedEnginesNode = '>=' + minNodeVersion; + if (packageJson.engines.node !== expectedEnginesNode) { + var msg = 'Update package.json to fix the "engines" attribute'; + throw new Error(msg); + } +} + +function assertDeepEquals(arr1, arr2, msg) { + try { + assert.deepStrictEqual(arr1, arr2); + } catch (e) { + throw new Error(msg + '\n' + e); + } +} + +function range(start, stop) { + var ret = []; + for (var i = start; i <= stop; i += 2) { + if (i % 2 !== 0) { + console.warn('Warning: testing a non-LTS nodejs release: ' + i); + } + ret.push(i); + } + return ret; +} + +function checkGithubActions(minNodeVersion, maxNodeVersion, githubActionsYaml) { + var expectedVersions = range(minNodeVersion, maxNodeVersion); + expectedVersions = expectedVersions.map(function (majorVersion) { + return pinnedNodeVersions[majorVersion] || majorVersion; + }); + var msg = 'Check GitHub Actions node_js versions'; + assertDeepEquals(githubActionsYaml.jobs.test.strategy.matrix['node-version'], + expectedVersions, msg); +} + +try { + checkReadme(MIN_NODE_VERSION); + + var packageJson = require('../package.json'); + checkEngines(MIN_NODE_VERSION, packageJson); + + var githubActionsFileName = path.join(__dirname, '..', '.github', 'workflows', + 'main.yml'); + var githubActionsYaml = yaml.load(shell.cat(githubActionsFileName)); + checkGithubActions(MIN_NODE_VERSION, MAX_NODE_VERSION, githubActionsYaml); + + console.log('All files look good (this project supports LTS releases v' + + MIN_NODE_VERSION + '-v' + MAX_NODE_VERSION + ')!'); +} catch (e) { + console.error('Please check the files which declare our Node version'); + console.error('support, as something is out-of-sync. This script failed'); + console.error('specificaly because:'); + throw e; +} diff --git a/scripts/generate-docs.js b/scripts/generate-docs.js index 6f6b36de3..3942c28b4 100755 --- a/scripts/generate-docs.js +++ b/scripts/generate-docs.js @@ -1,35 +1,76 @@ #!/usr/bin/env node -/* globals cat, cd, echo, grep, sed, ShellString */ +/* globals cat, cd, echo, grep, ls, sed, ShellString */ require('../global'); -echo('Appending docs to README.md'); +var path = require('path'); -cd(__dirname + '/..'); +var COMMAND_PREFIX = '## Command reference'; +var COMMAND_SUFFIX = '\n## Team'; +var COMMAND_PATTERN = /## Command reference\n\n((?:.|\n)*)\n## Team/; +function extractCurrentDocs() { + var m = cat('README.md').match(COMMAND_PATTERN); + if (m) { + var docs = m[1]; + return docs; + } + /* istanbul ignore next */ + throw new Error('Unable to extract current docs'); +} +module.exports.extractCurrentDocs = extractCurrentDocs; -// Extract docs from shell.js -var docs = grep('^//@', 'shell.js'); +function generateNewDocs() { + // Extract docs from shell.js + var docs = grep('^//@', 'shell.js'); -// Insert the docs for all the registered commands -docs = docs.replace(/\/\/@commands\n/g, function () { - return require('../commands').map(function (commandName) { - var file = './src/' + commandName + '.js'; - return grep('^//@', file) + '\n'; - }).join(''); -}); + // Insert the docs for all the registered commands + var blocklist = [ + './src/common.js', + './src/error.js', + './src/errorCode.js', + ]; + docs = docs.replace(/\/\/@commands\n/g, function () { + return ls('./src/*.js').map(function (file) { + if (blocklist.includes(file)) { + return ''; + } + var commandDoc = grep('^//@', file).toString(); + if (commandDoc !== '') { + commandDoc += '\n'; + } + return commandDoc; + }).join(''); + }); -// Now extract docs from the remaining src/*.js files -docs = docs.replace(/\/\/@include (.+)/g, function (match, path) { - var file = path.match('.js$') ? path : path + '.js'; - return grep('^//@', file); -}); + // Now extract docs from the remaining src/*.js files + docs = docs.replace(/\/\/@include (.+)/g, function (match, filename) { + return grep('^//@', filename); + }); -// Remove '//@' -docs = docs.replace(/\/\/@ ?/g, ''); + // Remove '//@' + docs = docs.replace(/\/\/@ ?/g, ''); + return docs; +} +module.exports.generateNewDocs = generateNewDocs; -// Wipe out the old docs -ShellString(cat('README.md').replace(/## Command reference(.|\n)*\n## Team/, '## Command reference\n## Team')).to('README.md'); +/* istanbul ignore next */ +function main() { + echo('Appending docs to README.md'); -// Append new docs to README -sed('-i', /## Command reference/, '## Command reference\n\n' + docs, 'README.md'); + cd(path.join(__dirname, '..')); -echo('All done.'); + var docs = generateNewDocs(); + + // Wipe out the old docs + ShellString(cat('README.md').replace(COMMAND_PATTERN, + COMMAND_PREFIX + COMMAND_SUFFIX)).to('README.md'); + + // Append new docs to README + sed('-i', /## Command reference/, COMMAND_PREFIX + '\n\n' + docs, 'README.md'); + + echo('All done.'); +} + +/* istanbul ignore if */ +if (require.main === module) { + main(); +} diff --git a/shell.js b/shell.js index 2541a2036..8a3a67d30 100644 --- a/shell.js +++ b/shell.js @@ -8,10 +8,12 @@ var common = require('./src/common'); +module.exports = common.shell; + //@ //@ All commands run synchronously, unless otherwise stated. //@ All commands accept standard bash globbing characters (`*`, `?`, etc.), -//@ compatible with the [node glob module](https://github.com/isaacs/node-glob). +//@ compatible with [`fast-glob`](https://www.npmjs.com/package/fast-glob). //@ //@ For less-commonly used commands and features, please check out our [wiki //@ page](https://github.com/shelljs/shelljs/wiki). @@ -20,27 +22,76 @@ var common = require('./src/common'); // Include the docs for all the default commands //@commands -// Load all default commands -require('./commands').forEach(function (command) { - require('./src/' + command); -}); +// Load all default commands. We import these for their side effect of loading +// using the plugin architecture via `common.register()`. +require('./src/cat'); +require('./src/cd'); +require('./src/chmod'); +require('./src/cmd'); +require('./src/cp'); +require('./src/dirs'); +require('./src/echo'); +require('./src/exec'); +require('./src/exec-child'); // A hint to the bundler to keep exec-child.js +require('./src/find'); +require('./src/grep'); +require('./src/head'); +require('./src/ln'); +require('./src/ls'); +require('./src/mkdir'); +require('./src/mv'); +require('./src/popd'); +require('./src/pushd'); +require('./src/pwd'); +require('./src/rm'); +require('./src/sed'); +require('./src/set'); +require('./src/sort'); +require('./src/tail'); +require('./src/tempdir'); +require('./src/test'); +require('./src/to'); +require('./src/toEnd'); +require('./src/touch'); +require('./src/uniq'); +require('./src/which'); //@ //@ ### exit(code) -//@ Exits the current process with the given exit code. -exports.exit = process.exit; - -//@include ./src/error -exports.error = require('./src/error'); - -//@include ./src/common -exports.ShellString = common.ShellString; +//@ +//@ Exits the current process with the given exit `code`. +module.exports.exit = function exit(code) { + common.state.error = null; + common.state.errorCode = 0; + if (code) { + common.error('exit', { + continue: true, + code, + prefix: '', + silent: true, + fatal: false, + }); + process.exit(code); + } else { + process.exit(); + } +}; + +//@include ./src/error.js +module.exports.error = require('./src/error'); + +//@include ./src/errorCode.js +module.exports.errorCode = require('./src/errorCode'); + +//@include ./src/common.js +module.exports.ShellString = common.ShellString; //@ //@ ### env['VAR_NAME'] +//@ //@ Object containing environment variables (both getter and setter). Shortcut -//@ to process.env. -exports.env = process.env; +//@ to `process.env`. +module.exports.env = process.env; //@ //@ ### Pipes @@ -49,7 +100,7 @@ exports.env = process.env; //@ //@ ```javascript //@ grep('foo', 'file1.txt', 'file2.txt').sed(/o/g, 'a').to('output.txt'); -//@ echo('files with o\'s in the name:\n' + ls().grep('o')); +//@ echo("files with o's in the name:\n" + ls().grep('o')); //@ cat('test.js').exec('node'); // pipe to exec() call //@ ``` //@ @@ -61,7 +112,7 @@ exports.env = process.env; //@ ## Configuration //@ -exports.config = common.config; +module.exports.config = common.config; //@ //@ ### config.silent @@ -91,9 +142,9 @@ exports.config = common.config; //@ /* more commands... */ //@ ``` //@ -//@ If `true` the script will throw a Javascript error when any shell.js +//@ If `true`, the script will throw a Javascript error when any shell.js //@ command encounters an error. Default is `false`. This is analogous to -//@ Bash's `set -e` +//@ Bash's `set -e`. //@ //@ ### config.verbose @@ -116,7 +167,14 @@ exports.config = common.config; //@ ``` //@ -//@ ### config.globOptions +//@ ### config.globOptions (deprecated) +//@ +//@ **Deprecated**: we recommend that you do not edit `config.globOptions`. +//@ Support for this configuration option may be changed or removed in a future +//@ ShellJS release. +//@ +//@ **Breaking change**: ShellJS v0.8.x uses `node-glob`. Starting with ShellJS +//@ v0.9.x, `config.globOptions` is compatible with `fast-glob`. //@ //@ Example: //@ @@ -124,7 +182,11 @@ exports.config = common.config; //@ config.globOptions = {nodir: true}; //@ ``` //@ -//@ Use this value for calls to `glob.sync()` instead of the default options. +//@ `config.globOptions` changes how ShellJS expands glob (wildcard) +//@ expressions. See +//@ [fast-glob](https://github.com/mrmlnc/fast-glob?tab=readme-ov-file#options-3) +//@ for available options. Be aware that modifying `config.globOptions` **may +//@ break ShellJS functionality.** //@ //@ ### config.reset() @@ -140,7 +202,7 @@ exports.config = common.config; //@ /* ... */ //@ ``` //@ -//@ Reset shell.config to the defaults: +//@ Reset `shell.config` to the defaults: //@ //@ ```javascript //@ { diff --git a/src/cat.js b/src/cat.js index af1ad1d4d..ca264a9d1 100644 --- a/src/cat.js +++ b/src/cat.js @@ -1,13 +1,20 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('cat', _cat, { canReceivePipe: true, + cmdOptions: { + 'n': 'number', + }, }); //@ -//@ ### cat(file [, file ...]) -//@ ### cat(file_array) +//@ ### cat([options,] file [, file ...]) +//@ ### cat([options,] file_array) +//@ +//@ Available options: +//@ +//@ + `-n`: number all output lines //@ //@ Examples: //@ @@ -17,9 +24,9 @@ common.register('cat', _cat, { //@ var str = cat(['file1', 'file2']); // same as above //@ ``` //@ -//@ Returns a string containing the given file, or a concatenated string -//@ containing the files if more than one file is given (a new line character is -//@ introduced between each file). +//@ Returns a [ShellString](#shellstringstr) containing the given file, or a +//@ concatenated string containing the files if more than one file is given (a +//@ new line character is introduced between each file). function _cat(options, files) { var cat = common.readFromPipe(); @@ -30,13 +37,40 @@ function _cat(options, files) { files.forEach(function (file) { if (!fs.existsSync(file)) { common.error('no such file or directory: ' + file); - } else if (fs.statSync(file).isDirectory()) { + } else if (common.statFollowLinks(file).isDirectory()) { common.error(file + ': Is a directory'); } cat += fs.readFileSync(file, 'utf8'); }); + if (options.number) { + cat = addNumbers(cat); + } + return cat; } module.exports = _cat; + +function addNumbers(cat) { + var lines = cat.split('\n'); + var lastLine = lines.pop(); + + lines = lines.map(function (line, i) { + return numberedLine(i + 1, line); + }); + + if (lastLine.length) { + lastLine = numberedLine(lines.length + 1, lastLine); + } + lines.push(lastLine); + + return lines.join('\n'); +} + +function numberedLine(n, line) { + // GNU cat use six pad start number + tab. See http://lingrok.org/xref/coreutils/src/cat.c#57 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart + var number = (' ' + n).slice(-6) + '\t'; + return number + line; +} diff --git a/src/cd.js b/src/cd.js index c6f1ada33..1c6e73fff 100644 --- a/src/cd.js +++ b/src/cd.js @@ -1,4 +1,3 @@ -var fs = require('fs'); var os = require('os'); var common = require('./common'); @@ -6,8 +5,10 @@ common.register('cd', _cd, {}); //@ //@ ### cd([dir]) +//@ //@ Changes to directory `dir` for the duration of the script. Changes to home -//@ directory if no argument is supplied. +//@ directory if no argument is supplied. Returns a +//@ [ShellString](#shellstringstr) to indicate success or failure. function _cd(options, dir) { if (!dir) dir = os.homedir(); @@ -27,7 +28,7 @@ function _cd(options, dir) { // something went wrong, let's figure out the error var err; try { - fs.statSync(dir); // if this succeeds, it must be some sort of file + common.statFollowLinks(dir); // if this succeeds, it must be some sort of file err = 'not a directory: ' + dir; } catch (e2) { err = 'no such file or directory: ' + dir; diff --git a/src/chmod.js b/src/chmod.js index ce5659e35..b930cc764 100644 --- a/src/chmod.js +++ b/src/chmod.js @@ -1,6 +1,6 @@ -var common = require('./common'); var fs = require('fs'); var path = require('path'); +var common = require('./common'); var PERMS = (function (base) { return { @@ -39,7 +39,7 @@ common.register('chmod', _chmod, { //@ Available options: //@ //@ + `-v`: output a diagnostic for every file processed//@ -//@ + `-c`: like verbose but report only when a change is made//@ +//@ + `-c`: like verbose, but report only when a change is made//@ //@ + `-R`: change files and directories recursively//@ //@ //@ Examples: @@ -56,9 +56,15 @@ common.register('chmod', _chmod, { //@ This command tries to mimic the POSIX behavior as much as possible. //@ Notable exceptions: //@ -//@ + In symbolic modes, 'a-r' and '-r' are identical. No consideration is -//@ given to the umask. -//@ + There is no "quiet" option since default behavior is to run silent. +//@ + In symbolic modes, `a-r` and `-r` are identical. No consideration is +//@ given to the `umask`. +//@ + There is no "quiet" option, since default behavior is to run silent. +//@ + Windows OS uses a very different permission model than POSIX. `chmod()` +//@ does its best on Windows, but there are limits to how file permissions can +//@ be set. Note that WSL (Windows subsystem for Linux) **does** follow POSIX, +//@ so cross-platform compatibility should not be a concern there. +//@ +//@ Returns a [ShellString](#shellstringstr) indicating success or failure. function _chmod(options, mode, filePattern) { if (!filePattern) { if (options.length > 0 && options.charAt(0) === '-') { @@ -85,7 +91,7 @@ function _chmod(options, mode, filePattern) { if (options.recursive) { files = []; filePattern.forEach(function addFile(expandedFile) { - var stat = fs.lstatSync(expandedFile); + var stat = common.statNoFollowLinks(expandedFile); if (!stat.isSymbolicLink()) { files.push(expandedFile); @@ -108,21 +114,21 @@ function _chmod(options, mode, filePattern) { } // When recursing, don't follow symlinks. - if (options.recursive && fs.lstatSync(file).isSymbolicLink()) { + if (options.recursive && common.statNoFollowLinks(file).isSymbolicLink()) { return; } - var stat = fs.statSync(file); + var stat = common.statFollowLinks(file); var isDir = stat.isDirectory(); var perms = stat.mode; var type = perms & PERMS.TYPE_MASK; var newPerms = perms; - if (isNaN(parseInt(mode, 8))) { + if (Number.isNaN(parseInt(mode, 8))) { // parse options mode.split(',').forEach(function (symbolicMode) { - var pattern = /([ugoa]*)([=\+-])([rwxXst]*)/i; + var pattern = /([ugoa]*)([=+-])([rwxXst]*)/i; var matches = pattern.exec(symbolicMode); if (matches) { @@ -130,16 +136,16 @@ function _chmod(options, mode, filePattern) { var operator = matches[2]; var change = matches[3]; - var changeOwner = applyTo.indexOf('u') !== -1 || applyTo === 'a' || applyTo === ''; - var changeGroup = applyTo.indexOf('g') !== -1 || applyTo === 'a' || applyTo === ''; - var changeOther = applyTo.indexOf('o') !== -1 || applyTo === 'a' || applyTo === ''; + var changeOwner = applyTo.includes('u') || applyTo === 'a' || applyTo === ''; + var changeGroup = applyTo.includes('g') || applyTo === 'a' || applyTo === ''; + var changeOther = applyTo.includes('o') || applyTo === 'a' || applyTo === ''; - var changeRead = change.indexOf('r') !== -1; - var changeWrite = change.indexOf('w') !== -1; - var changeExec = change.indexOf('x') !== -1; - var changeExecDir = change.indexOf('X') !== -1; - var changeSticky = change.indexOf('t') !== -1; - var changeSetuid = change.indexOf('s') !== -1; + var changeRead = change.includes('r'); + var changeWrite = change.includes('w'); + var changeExec = change.includes('x'); + var changeExecDir = change.includes('X'); + var changeSticky = change.includes('t'); + var changeSetuid = change.includes('s'); if (changeExecDir && isDir) { changeExec = true; @@ -175,7 +181,7 @@ function _chmod(options, mode, filePattern) { // According to POSIX, when using = to explicitly set the // permissions, setuid and setgid can never be cleared. - if (fs.statSync(file).isDirectory()) { + if (common.statFollowLinks(file).isDirectory()) { newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; } break; @@ -204,7 +210,7 @@ function _chmod(options, mode, filePattern) { // POSIX rules are that setuid and setgid can only be added using numeric // form, but not cleared. - if (fs.statSync(file).isDirectory()) { + if (common.statFollowLinks(file).isDirectory()) { newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; } diff --git a/src/cmd.js b/src/cmd.js new file mode 100644 index 000000000..a00d6c497 --- /dev/null +++ b/src/cmd.js @@ -0,0 +1,138 @@ +var execa = require('execa'); +var common = require('./common'); + +var DEFAULT_MAXBUFFER_SIZE = 20 * 1024 * 1024; +var COMMAND_NOT_FOUND_ERROR_CODE = 127; + +common.register('cmd', _cmd, { + cmdOptions: null, + globStart: 1, + canReceivePipe: true, + wrapOutput: true, +}); + +function isCommandNotFound(execaResult) { + if (process.platform === 'win32') { + var str = 'is not recognized as an internal or external command'; + return execaResult.exitCode && execaResult.stderr.includes(str); + } + return execaResult.failed && execaResult.code === 'ENOENT'; +} + +function isExecaInternalError(result) { + if (typeof result.stdout !== 'string') return true; + if (typeof result.stderr !== 'string') return true; + if (typeof result.exitCode !== 'number') return true; + if (result.exitCode === 0 && result.failed) return true; + // Otherwise assume this executed correctly. The command may still have exited + // with non-zero status, but that's not due to anything execa did. + return false; +} + +//@ +//@ ### cmd(arg1[, arg2, ...] [, options]) +//@ +//@ Available options: +//@ +//@ + `cwd: directoryPath`: change the current working directory only for this +//@ cmd() invocation. +//@ + `maxBuffer: num`: Raise or decrease the default buffer size for +//@ stdout/stderr. +//@ + `timeout`: Change the default timeout. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var version = cmd('node', '--version').stdout; +//@ cmd('git', 'commit', '-am', `Add suport for node ${version}`); +//@ console.log(cmd('echo', '1st arg', '2nd arg', '3rd arg').stdout) +//@ console.log(cmd('echo', 'this handles ;, |, &, etc. as literal characters').stdout) +//@ ``` +//@ +//@ Executes the given command synchronously. This is intended as an easier +//@ alternative for [exec()](#execcommand--options--callback), with better +//@ security around globbing, comamnd injection, and variable expansion. This is +//@ guaranteed to only run one external command, and won't give special +//@ treatment for any shell characters (ex. this treats `|` as a literal +//@ character, not as a shell pipeline). +//@ This returns a [ShellString](#shellstringstr). +//@ +//@ By default, this performs globbing on all platforms, but you can disable +//@ this with `set('-f')`. +//@ +//@ This **does not** support asynchronous mode. If you need asynchronous +//@ command execution, check out [execa](https://www.npmjs.com/package/execa) or +//@ the node builtin `child_process.execFile()` instead. +function _cmd(options, command, commandArgs, userOptions) { + if (!command) { + common.error('Must specify a non-empty string as a command'); + } + + // `options` will usually not have a value: it's added by our commandline flag + // parsing engine. + commandArgs = [].slice.call(arguments, 2); + + // `userOptions` may or may not be provided. We need to check the last + // argument. If it's an object, assume it's meant to be passed as + // userOptions (since ShellStrings are already flattened to strings). + if (commandArgs.length === 0) { + userOptions = {}; + } else { + var lastArg = commandArgs.pop(); + if (common.isObject(lastArg)) { + userOptions = lastArg; + } else { + userOptions = {}; + commandArgs.push(lastArg); + } + } + + var pipe = common.readFromPipe(); + + // Some of our defaults differ from execa's defaults. These can be overridden + // by the user. + var defaultOptions = { + maxBuffer: DEFAULT_MAXBUFFER_SIZE, + stripFinalNewline: false, // Preserve trailing newlines for consistency with unix. + reject: false, // Use ShellJS's error handling system. + }; + + // For other options, we forbid the user from overriding them (either for + // correctness or security). + var requiredOptions = { + input: pipe, + shell: false, + }; + + var execaOptions = + Object.assign(defaultOptions, userOptions, requiredOptions); + + var result = execa.sync(command, commandArgs, execaOptions); + var stdout; + var stderr; + var code; + if (isCommandNotFound(result)) { + // This can happen if `command` is not an executable binary, or possibly + // under other conditions. + stdout = ''; + stderr = "'" + command + "': command not found"; + code = COMMAND_NOT_FOUND_ERROR_CODE; + } else if (isExecaInternalError(result)) { + // Catch-all: execa tried to run `command` but it encountered some error + // (ex. maxBuffer, timeout). + stdout = result.stdout || ''; + stderr = result.stderr || + `'${command}' encountered an error during execution`; + code = result.exitCode !== undefined && result.exitCode > 0 ? result.exitCode : 1; + } else { + // Normal exit: execa was able to execute `command` and get a return value. + stdout = result.stdout.toString(); + stderr = result.stderr.toString(); + code = result.exitCode; + } + + // Pass `continue: true` so we can specify a value for stdout. + if (code) common.error(stderr, code, { silent: true, continue: true }); + return new common.ShellString(stdout, stderr, code); +} +module.exports = _cmd; diff --git a/src/common.js b/src/common.js index 13753a12f..d46e3c9b8 100644 --- a/src/common.js +++ b/src/common.js @@ -1,11 +1,15 @@ -// Ignore warning about 'new String()' -/* eslint no-new-wrappers: 0 */ +// Ignore warning about 'new String()' and use of the Buffer constructor +/* eslint no-new-wrappers: "off", + no-buffer-constructor: "off" */ + 'use strict'; var os = require('os'); var fs = require('fs'); -var glob = require('glob'); -var shell = require('..'); +var glob = require('fast-glob'); + +var shell = {}; +exports.shell = shell; var shellMethods = Object.create(shell); @@ -27,13 +31,13 @@ var DEFAULT_CONFIG = { }; var config = { - reset: function () { + reset() { Object.assign(this, DEFAULT_CONFIG); if (!isElectron) { this.execPath = process.execPath; } }, - resetForTesting: function () { + resetForTesting() { this.reset(); this.silent = true; }, @@ -42,19 +46,16 @@ var config = { config.reset(); exports.config = config; +// Note: commands should generally consider these as read-only values. var state = { error: null, errorCode: 0, currentCmd: 'shell.js', - tempDir: null, }; exports.state = state; delete process.env.OLDPWD; // initially, there's no previous directory -// This is populated by calls to commonl.wrap() -var pipeMethods = []; - // Reliably test if something is any sort of javascript object function isObject(a) { return typeof a === 'object' && a !== null; @@ -80,7 +81,17 @@ function convertErrorOutput(msg) { } exports.convertErrorOutput = convertErrorOutput; -// Shows error message. Throws if config.fatal is true +// An exception class to help propagate command errors (e.g., non-zero exit +// status) up to the top-level. {@param value} should be a ShellString. +class CommandError extends Error { + constructor(value) { + super(value.toString()); + this.returnValue = value; + } +} +exports.CommandError = CommandError; // visible for testing + +// Shows error message. Throws if fatal is true (defaults to config.fatal, overridable with options.fatal) function error(msg, _code, options) { // Validate input if (typeof msg !== 'string') throw new Error('msg must be a string'); @@ -90,6 +101,7 @@ function error(msg, _code, options) { code: 1, prefix: state.currentCmd + ': ', silent: false, + fatal: config.fatal, }; if (typeof _code === 'number' && isObject(options)) { @@ -110,14 +122,15 @@ function error(msg, _code, options) { state.error += logEntry; // Throw an error, or log the entry - if (config.fatal) throw new Error(logEntry); + if (options.fatal) { + var err = new Error(logEntry); + err.code = options.code; + throw err; + } if (msg.length > 0 && !options.silent) log(logEntry); if (!options.continue) { - throw { - msg: 'earlyExit', - retValue: (new ShellString('', state.error, state.errorCode)), - }; + throw new CommandError(new ShellString('', state.error, state.errorCode)); } } exports.error = error; @@ -128,11 +141,19 @@ exports.error = error; //@ Examples: //@ //@ ```javascript -//@ var foo = ShellString('hello world'); +//@ var foo = new ShellString('hello world'); //@ ``` //@ -//@ Turns a regular string into a string-like object similar to what each -//@ command returns. This has special methods, like `.to()` and `.toEnd()` +//@ This is a dedicated type returned by most ShellJS methods, which wraps a +//@ string (or array) value. This has all the string (or array) methods, but +//@ also exposes extra methods: [`.to()`](#shellstringprototypetofile), +//@ [`.toEnd()`](#shellstringprototypetoendfile), and all the pipe-able methods +//@ (ex. `.cat()`, `.grep()`, etc.). This can be easily converted into a string +//@ by calling `.toString()`. +//@ +//@ This type also exposes the corresponding command's stdout, stderr, and +//@ return status code via the `.stdout` (string), `.stderr` (string), and +//@ `.code` (number) properties respectively. function ShellString(stdout, stderr, code) { var that; if (stdout instanceof Array) { @@ -162,13 +183,21 @@ exports.ShellString = ShellString; // Throws an error when passed a string that does not start with '-': // parseOptions('a', {'a':'alice'}); // throws function parseOptions(opt, map, errorOptions) { + errorOptions = errorOptions || {}; // Validate input if (typeof opt !== 'string' && !isObject(opt)) { - throw new Error('options must be strings or key-value pairs'); + throw new TypeError('options must be strings or key-value pairs'); } else if (!isObject(map)) { - throw new Error('parseOptions() internal error: map must be an object'); - } else if (errorOptions && !isObject(errorOptions)) { - throw new Error('parseOptions() internal error: errorOptions must be object'); + throw new TypeError('parseOptions() internal error: map must be an object'); + } else if (!isObject(errorOptions)) { + throw new TypeError( + 'parseOptions() internal error: errorOptions must be object', + ); + } + + if (opt === '--') { + // This means there are no options. + return {}; } // All options are false by default @@ -199,18 +228,25 @@ function parseOptions(opt, map, errorOptions) { options[optionName] = true; } } else { - error('option not recognized: ' + c, errorOptions || {}); + error('option not recognized: ' + c, errorOptions); } }); } else { // opt is an Object Object.keys(opt).forEach(function (key) { - // key is a string of the form '-r', '-d', etc. - var c = key[1]; - if (c in map) { - var optionName = map[c]; - options[optionName] = opt[key]; // assign the given value + if (key[0] === '-') { + // key is a string of the form '-r', '-d', etc. + var c = key[1]; + if (c in map) { + var optionName = map[c]; + options[optionName] = opt[key]; // assign the given value + } else { + error('option not recognized: ' + c, errorOptions); + } + } else if (key in options) { + // key is a "long option", so it should be the same + options[key] = opt[key]; } else { - error('option not recognized: ' + c, errorOptions || {}); + error('option not recognized: {' + key + ':...}', errorOptions); } }); } @@ -218,6 +254,78 @@ function parseOptions(opt, map, errorOptions) { } exports.parseOptions = parseOptions; +function globOptions() { + // These options are just to make fast-glob be compatible with POSIX (bash) + // wildcard behavior. + var defaultGlobOptions = { + onlyFiles: false, + followSymbolicLinks: false, + }; + + var newGlobOptions = Object.assign({}, config.globOptions); + var optionRenames = { + // node-glob's 'nodir' is not quote the same as fast-glob's 'onlyFiles'. + // Compatibility for this is implemented at the call site. + mark: 'markDirectories', + matchBase: 'baseNameMatch', + }; + Object.keys(optionRenames).forEach(function (oldKey) { + var newKey = optionRenames[oldKey]; + if (oldKey in config.globOptions) { + newGlobOptions[newKey] = config.globOptions[oldKey]; + } + }); + var invertedOptionRenames = { + nobrace: 'braceExpansion', + noglobstar: 'globstar', + noext: 'extglob', + nocase: 'caseSensitiveMatch', + }; + Object.keys(invertedOptionRenames).forEach(function (oldKey) { + var newKey = invertedOptionRenames[oldKey]; + if (oldKey in config.globOptions) { + newGlobOptions[newKey] = !config.globOptions[oldKey]; + } + }); + return Object.assign({}, defaultGlobOptions, newGlobOptions); +} + + +// Workaround for https://github.com/shelljs/shelljs/issues/1197 +// fast-glob's dependency glob-parent does not recognize the '?' wildcard in +// non-final path segments, causing those patterns to be treated as literal +// directory names. Replace standalone '?' with the equivalent character class +// '[^/]' which glob-parent handles correctly. +function convertQuestionMarkForGlob(pattern) { + if (pattern.indexOf('?') === -1) { + return pattern; + } + var result = ''; + var inBracket = false; + for (var i = 0; i < pattern.length; i++) { + var ch = pattern[i]; + if (ch === '\\' && i + 1 < pattern.length) { + // Escaped character: keep as-is + result += ch + pattern[i + 1]; + i++; + } else if (ch === '[' && !inBracket) { + inBracket = true; + result += ch; + } else if (ch === ']' && inBracket) { + inBracket = false; + result += ch; + } else if (ch === '?' && !inBracket && pattern[i + 1] === '(') { + // Extglob pattern ?(...) keep as-is + result += ch; + } else if (ch === '?' && !inBracket) { + result += '[^/]'; + } else { + result += ch; + } + } + return result; +} + // Expands wildcards with matching (ie. existing) file names. // For example: // expand(['file*.js']) = ['file1.js', 'file2.js', ...] @@ -233,14 +341,20 @@ function expand(list) { expanded.push(listEl); } else { var ret; + var globOpts = globOptions(); try { - ret = glob.sync(listEl, config.globOptions); - // if nothing matched, interpret the string literally - ret = ret.length > 0 ? ret : [listEl]; + ret = glob.sync(convertQuestionMarkForGlob(listEl), globOpts); } catch (e) { // if glob fails, interpret the string literally ret = [listEl]; } + // if nothing matched, interpret the string literally + ret = ret.length > 0 ? ret.sort() : [listEl]; + if (globOpts.nodir) { + ret = ret.filter(function (file) { + return !statNoFollowLinks(file).isDirectory(); + }); + } expanded = expanded.concat(ret); } }); @@ -277,6 +391,18 @@ function unlinkSync(file) { } exports.unlinkSync = unlinkSync; +// wrappers around common.statFollowLinks and common.statNoFollowLinks that clarify intent +// and improve readability +function statFollowLinks() { + return fs.statSync.apply(fs, arguments); +} +exports.statFollowLinks = statFollowLinks; + +function statNoFollowLinks() { + return fs.lstatSync.apply(fs, arguments); +} +exports.statNoFollowLinks = statNoFollowLinks; + // e.g. 'shelljs_a5f185d0443ca...' function randomFileName() { function randomHash(count) { @@ -298,9 +424,6 @@ exports.randomFileName = randomFileName; // command-logging, and other nice things function wrap(cmd, fn, options) { options = options || {}; - if (options.canReceivePipe) { - pipeMethods.push(cmd); - } return function () { var retValue = null; @@ -373,8 +496,8 @@ function wrap(cmd, fn, options) { retValue = fn.apply(this, args); } catch (e) { /* istanbul ignore else */ - if (e.msg === 'earlyExit') { - retValue = e.retValue; + if (e instanceof CommandError) { + retValue = e.returnValue; } else { throw e; // this is probably a bug that should be thrown up the call stack } @@ -387,7 +510,7 @@ function wrap(cmd, fn, options) { e.name = 'ShellJSInternalError'; throw e; } - if (config.fatal) throw e; + if (config.fatal || options.handlesFatalDynamically) throw e; } if (options.wrapOutput && @@ -411,22 +534,37 @@ exports.readFromPipe = _readFromPipe; var DEFAULT_WRAP_OPTIONS = { allowGlobbing: true, canReceivePipe: false, - cmdOptions: false, + cmdOptions: null, globStart: 1, + handlesFatalDynamically: false, pipeOnly: false, - unix: true, wrapOutput: true, - overWrite: false, + unix: true, }; +// This is populated during plugin registration +var pipeMethods = []; + // Register a new ShellJS command function _register(name, implementation, wrapOptions) { wrapOptions = wrapOptions || {}; + + // Validate options + Object.keys(wrapOptions).forEach(function (option) { + if (!DEFAULT_WRAP_OPTIONS.hasOwnProperty(option)) { + throw new Error("Unknown option '" + option + "'"); + } + if (typeof wrapOptions[option] !== typeof DEFAULT_WRAP_OPTIONS[option]) { + throw new TypeError("Unsupported type '" + typeof wrapOptions[option] + + "' for option '" + option + "'"); + } + }); + // If an option isn't specified, use the default wrapOptions = Object.assign({}, DEFAULT_WRAP_OPTIONS, wrapOptions); - if (shell[name] && !wrapOptions.overWrite) { - throw new Error('unable to overwrite `' + name + '` command'); + if (shell.hasOwnProperty(name)) { + throw new Error('Command `' + name + '` already exists'); } if (wrapOptions.pipeOnly) { @@ -435,5 +573,9 @@ function _register(name, implementation, wrapOptions) { } else { shell[name] = wrap(name, implementation, wrapOptions); } + + if (wrapOptions.canReceivePipe) { + pipeMethods.push(name); + } } exports.register = _register; diff --git a/src/cp.js b/src/cp.js index 99c97b2eb..abed9a987 100644 --- a/src/cp.js +++ b/src/cp.js @@ -11,6 +11,7 @@ common.register('cp', _cp, { 'r': 'recursive', 'L': 'followsymlink', 'P': 'noFollowsymlink', + 'p': 'preserve', }, wrapOutput: false, }); @@ -23,20 +24,26 @@ function copyFileSync(srcFile, destFile, options) { common.error('copyFileSync: no such file or directory: ' + srcFile); } + // Bail if src and dest are the same file to avoid data loss + if (path.relative(srcFile, destFile) === '') { + common.error("'" + destFile + "' and '" + srcFile + "' are the same file", { continue: true }); + return; + } + var isWindows = process.platform === 'win32'; // Check the mtimes of the files if the '-u' flag is provided try { - if (options.update && fs.statSync(srcFile).mtime < fs.statSync(destFile).mtime) { + if (options.update && common.statFollowLinks(srcFile).mtime < fs.statSync(destFile).mtime) { return; } } catch (e) { // If we're here, destFile probably doesn't exist, so just do a normal copy } - if (fs.lstatSync(srcFile).isSymbolicLink() && !options.followsymlink) { + if (common.statNoFollowLinks(srcFile).isSymbolicLink() && !options.followsymlink) { try { - fs.lstatSync(destFile); + common.statNoFollowLinks(destFile); common.unlinkSync(destFile); // re-link it } catch (e) { // it doesn't exist, so no work needs to be done @@ -51,6 +58,7 @@ function copyFileSync(srcFile, destFile, options) { var pos = 0; var fdr = null; var fdw = null; + var srcStat = common.statFollowLinks(srcFile); try { fdr = fs.openSync(srcFile, 'r'); @@ -60,7 +68,7 @@ function copyFileSync(srcFile, destFile, options) { } try { - fdw = fs.openSync(destFile, 'w'); + fdw = fs.openSync(destFile, 'w', srcStat.mode); } catch (e) { /* istanbul ignore next */ common.error('copyFileSync: could not write to dest file (code=' + e.code + '):' + destFile); @@ -72,10 +80,15 @@ function copyFileSync(srcFile, destFile, options) { pos += bytesRead; } + if (options.preserve) { + fs.fchownSync(fdw, srcStat.uid, srcStat.gid); + // Note: utimesSync does not work (rounds to seconds), but futimesSync has + // millisecond precision. + fs.futimesSync(fdw, srcStat.atime, srcStat.mtime); + } + fs.closeSync(fdr); fs.closeSync(fdw); - - fs.chmodSync(destFile, fs.statSync(srcFile).mode); } } @@ -96,11 +109,11 @@ function cpdirSyncRecursive(sourceDir, destDir, currentDepth, opts) { var isWindows = process.platform === 'win32'; - // Create the directory where all our junk is moving to; read the mode of the - // source directory and mirror it + // Create the directory where all our junk is moving to; read the mode/etc. of + // the source directory (we'll set this on the destDir at the end). + var checkDir = common.statFollowLinks(sourceDir); try { - var checkDir = fs.statSync(sourceDir); - fs.mkdirSync(destDir, checkDir.mode); + fs.mkdirSync(destDir); } catch (e) { // if the directory already exists, that's okay if (e.code !== 'EEXIST') throw e; @@ -111,7 +124,7 @@ function cpdirSyncRecursive(sourceDir, destDir, currentDepth, opts) { for (var i = 0; i < files.length; i++) { var srcFile = sourceDir + '/' + files[i]; var destFile = destDir + '/' + files[i]; - var srcFileStat = fs.lstatSync(srcFile); + var srcFileStat = common.statNoFollowLinks(srcFile); var symlinkFull; if (opts.followsymlink) { @@ -129,28 +142,33 @@ function cpdirSyncRecursive(sourceDir, destDir, currentDepth, opts) { } else if (srcFileStat.isSymbolicLink() && !opts.followsymlink) { symlinkFull = fs.readlinkSync(srcFile); try { - fs.lstatSync(destFile); + common.statNoFollowLinks(destFile); common.unlinkSync(destFile); // re-link it } catch (e) { // it doesn't exist, so no work needs to be done } fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null); } else if (srcFileStat.isSymbolicLink() && opts.followsymlink) { - srcFileStat = fs.statSync(srcFile); + srcFileStat = common.statFollowLinks(srcFile); if (srcFileStat.isDirectory()) { cpdirSyncRecursive(srcFile, destFile, currentDepth, opts); } else { copyFileSync(srcFile, destFile, opts); } + } else if (fs.existsSync(destFile) && opts.no_force) { + common.log('skipping existing file: ' + files[i]); } else { - /* At this point, we've hit a file actually worth copying... so copy it on over. */ - if (fs.existsSync(destFile) && opts.no_force) { - common.log('skipping existing file: ' + files[i]); - } else { - copyFileSync(srcFile, destFile, opts); - } + copyFileSync(srcFile, destFile, opts); } } // for files + + // finally change the mode for the newly created directory (otherwise, we + // couldn't add files to a read-only directory). + // var checkDir = common.statFollowLinks(sourceDir); + if (opts.preserve) { + fs.utimesSync(destDir, checkDir.atime, checkDir.mtime); + } + fs.chmodSync(destDir, checkDir.mode); } // cpdirSyncRecursive // Checks if cureent file was created recently @@ -162,7 +180,7 @@ function checkRecentCreated(sources, index) { } function cpcheckcycle(sourceDir, srcFile) { - var srcFileStat = fs.lstatSync(srcFile); + var srcFileStat = common.statNoFollowLinks(srcFile); if (srcFileStat.isSymbolicLink()) { // Do cycle check. For example: // $ mkdir -p 1/2/3/4 @@ -170,7 +188,7 @@ function cpcheckcycle(sourceDir, srcFile) { // $ ln -s ../../3 link // $ cd ../../../.. // $ cp -RL 1 copy - var cyclecheck = fs.statSync(srcFile); + var cyclecheck = common.statFollowLinks(srcFile); if (cyclecheck.isDirectory()) { var sourcerealpath = fs.realpathSync(sourceDir); var symlinkrealpath = fs.realpathSync(srcFile); @@ -186,14 +204,16 @@ function cpcheckcycle(sourceDir, srcFile) { //@ //@ ### cp([options,] source [, source ...], dest) //@ ### cp([options,] source_array, dest) +//@ //@ Available options: //@ //@ + `-f`: force (default behavior) //@ + `-n`: no-clobber -//@ + `-u`: only copy if source is newer than dest +//@ + `-u`: only copy if `source` is newer than `dest` //@ + `-r`, `-R`: recursive //@ + `-L`: follow symlinks //@ + `-P`: don't follow symlinks +//@ + `-p`: preserve file mode, ownership, and timestamps //@ //@ Examples: //@ @@ -204,7 +224,8 @@ function cpcheckcycle(sourceDir, srcFile) { //@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above //@ ``` //@ -//@ Copies files. +//@ Copies files. Returns a [ShellString](#shellstringstr) indicating success +//@ or failure. function _cp(options, sources, dest) { // If we're missing -R, it actually implies -L (unless -P is explicit) if (options.followsymlink) { @@ -223,7 +244,7 @@ function _cp(options, sources, dest) { } var destExists = fs.existsSync(dest); - var destStat = destExists && fs.statSync(dest); + var destStat = destExists && common.statFollowLinks(dest); // Dest is not existing dir, but multiple sources given if ((!destExists || !destStat.isDirectory()) && sources.length > 1) { @@ -241,7 +262,7 @@ function _cp(options, sources, dest) { common.error('no such file or directory: ' + src, { continue: true }); return; // skip file } - var srcStat = fs.statSync(src); + var srcStat = common.statFollowLinks(src); if (!options.noFollowsymlink && srcStat.isDirectory()) { if (!options.recursive) { // Non-Recursive @@ -253,9 +274,15 @@ function _cp(options, sources, dest) { path.join(dest, path.basename(src)) : dest; + if (path.relative(src, newDest) === '') { + // a directory cannot be copied into itself, but we want to continue copying other sources + common.error("'" + newDest + "' and '" + src + "' are the same file", { continue: true }); + return; + } + try { - fs.statSync(path.dirname(dest)); - cpdirSyncRecursive(src, newDest, 0, { no_force: options.no_force, followsymlink: options.followsymlink }); + common.statFollowLinks(path.dirname(dest)); + cpdirSyncRecursive(src, newDest, 0, options); } catch (e) { /* istanbul ignore next */ common.error("cannot create directory '" + dest + "': No such file or directory"); @@ -284,12 +311,6 @@ function _cp(options, sources, dest) { return; // skip file } - if (path.relative(src, thisDest) === '') { - // a file cannot be copied to itself, but we want to continue copying other files - common.error("'" + thisDest + "' and '" + src + "' are the same file", { continue: true }); - return; - } - copyFileSync(src, thisDest, options); } }); // forEach(src) diff --git a/src/dirs.js b/src/dirs.js index 3806c14f7..9b7251d0e 100644 --- a/src/dirs.js +++ b/src/dirs.js @@ -1,6 +1,6 @@ +var path = require('path'); var common = require('./common'); var _cd = require('./cd'); -var path = require('path'); common.register('dirs', _dirs, { wrapOutput: false, @@ -16,7 +16,7 @@ common.register('popd', _popd, { var _dirStack = []; function _isStackIndex(index) { - return (/^[\-+]\d+$/).test(index); + return (/^[-+]\d+$/).test(index); } function _parseStackIndex(index) { @@ -40,10 +40,11 @@ function _actualDirStack() { //@ Available options: //@ //@ + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. +//@ + `-q`: Suppresses output to the console. //@ //@ Arguments: //@ -//@ + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. +//@ + `dir`: Sets the current working directory to the top of the stack, then executes the equivalent of `cd dir`. //@ + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. //@ + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. //@ @@ -55,7 +56,7 @@ function _actualDirStack() { //@ pushd('+1'); // Returns /usr /etc //@ ``` //@ -//@ Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. +//@ Save the current directory on the top of the directory stack and then `cd` to `dir`. With no arguments, `pushd` exchanges the top two directories. Returns an array of paths in the stack. function _pushd(options, dir) { if (_isStackIndex(options)) { dir = options; @@ -64,6 +65,7 @@ function _pushd(options, dir) { options = common.parseOptions(options, { 'n': 'no-cd', + 'q': 'quiet', }); var dirs = _actualDirStack(); @@ -79,12 +81,10 @@ function _pushd(options, dir) { } else if (_isStackIndex(dir)) { var n = _parseStackIndex(dir); dirs = dirs.slice(n).concat(dirs.slice(0, n)); + } else if (options['no-cd']) { + dirs.splice(1, 0, dir); } else { - if (options['no-cd']) { - dirs.splice(1, 0, dir); - } else { - dirs.unshift(dir); - } + dirs.unshift(dir); } if (options['no-cd']) { @@ -95,16 +95,18 @@ function _pushd(options, dir) { } _dirStack = dirs; - return _dirs(''); + return _dirs(options.quiet ? '-q' : ''); } exports.pushd = _pushd; +//@ //@ //@ ### popd([options,] ['-N' | '+N']) //@ //@ Available options: //@ -//@ + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. +//@ + `-n`: Suppress the normal directory change when removing directories from the stack, so that only the stack is manipulated. +//@ + `-q`: Suppresses output to the console. //@ //@ Arguments: //@ @@ -121,7 +123,7 @@ exports.pushd = _pushd; //@ echo(process.cwd()); // '/usr' //@ ``` //@ -//@ When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. +//@ When no arguments are given, `popd` removes the top directory from the stack and performs a `cd` to the new top directory. The elements are numbered from 0, starting at the first directory listed with dirs (i.e., `popd` is equivalent to `popd +0`). Returns an array of paths in the stack. function _popd(options, index) { if (_isStackIndex(options)) { index = options; @@ -130,6 +132,7 @@ function _popd(options, index) { options = common.parseOptions(options, { 'n': 'no-cd', + 'q': 'quiet', }); if (!_dirStack.length) { @@ -146,25 +149,27 @@ function _popd(options, index) { _cd('', dir); } - return _dirs(''); + return _dirs(options.quiet ? '-q' : ''); } exports.popd = _popd; +//@ //@ //@ ### dirs([options | '+N' | '-N']) //@ //@ Available options: //@ //@ + `-c`: Clears the directory stack by deleting all of the elements. +//@ + `-q`: Suppresses output to the console. //@ //@ Arguments: //@ //@ + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. //@ + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. //@ -//@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. +//@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if `+N` or `-N` was specified. //@ -//@ See also: pushd, popd +//@ See also: `pushd`, `popd` function _dirs(options, index) { if (_isStackIndex(options)) { index = options; @@ -173,6 +178,7 @@ function _dirs(options, index) { options = common.parseOptions(options, { 'c': 'clear', + 'q': 'quiet', }); if (options.clear) { @@ -189,11 +195,15 @@ function _dirs(options, index) { index = stack.length + index; } - common.log(stack[index]); + if (!options.quiet) { + common.log(stack[index]); + } return stack[index]; } - common.log(stack.join(' ')); + if (!options.quiet) { + common.log(stack.join(' ')); + } return stack; } diff --git a/src/echo.js b/src/echo.js index 7229ce7f2..da37f438a 100644 --- a/src/echo.js +++ b/src/echo.js @@ -8,6 +8,7 @@ common.register('echo', _echo, { //@ //@ ### echo([options,] string [, string ...]) +//@ //@ Available options: //@ //@ + `-e`: interpret backslash escapes (default) @@ -21,8 +22,7 @@ common.register('echo', _echo, { //@ echo('-n', 'no newline at end'); //@ ``` //@ -//@ Prints string to stdout, and returns string with additional utility methods -//@ like `.to()`. +//@ Prints `string` to stdout, and returns a [ShellString](#shellstringstr). function _echo(opts) { // allow strings starting with '-', see issue #20 var messages = [].slice.call(arguments, opts ? 0 : 1); diff --git a/src/error.js b/src/error.js index 507c86ddd..b0ed59e12 100644 --- a/src/error.js +++ b/src/error.js @@ -2,8 +2,9 @@ var common = require('./common'); //@ //@ ### error() +//@ //@ Tests if error occurred in the last command. Returns a truthy value if an -//@ error returned and a falsy value otherwise. +//@ error returned, or a falsy value otherwise. //@ //@ **Note**: do not rely on the //@ return value to be an error message. If you need the last error message, use diff --git a/src/errorCode.js b/src/errorCode.js new file mode 100644 index 000000000..a1c7fd23f --- /dev/null +++ b/src/errorCode.js @@ -0,0 +1,10 @@ +var common = require('./common'); + +//@ +//@ ### errorCode() +//@ +//@ Returns the error code from the last command. +function errorCode() { + return common.state.errorCode; +} +module.exports = errorCode; diff --git a/src/exec-child.js b/src/exec-child.js new file mode 100644 index 000000000..e8446f6f0 --- /dev/null +++ b/src/exec-child.js @@ -0,0 +1,71 @@ +var childProcess = require('child_process'); +var fs = require('fs'); + +function main() { + var paramFilePath = process.argv[2]; + + var serializedParams = fs.readFileSync(paramFilePath, 'utf8'); + var params = JSON.parse(serializedParams); + + var cmd = params.command; + var execOptions = params.execOptions; + var pipe = params.pipe; + var stdoutFile = params.stdoutFile; + var stderrFile = params.stderrFile; + + function isMaxBufferError(err) { + var maxBufferErrorPattern = /^.*\bmaxBuffer\b.*exceeded.*$/; + if (err instanceof Error && err.message && + err.message.match(maxBufferErrorPattern)) { + // < v10 + // Error: stdout maxBuffer exceeded + return true; + } else if (err instanceof RangeError && err.message && + err.message.match(maxBufferErrorPattern)) { + // >= v10 + // RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stdout maxBuffer length + // exceeded + return true; + } + return false; + } + + var stdoutStream = fs.createWriteStream(stdoutFile); + var stderrStream = fs.createWriteStream(stderrFile); + + function appendError(message, code) { + stderrStream.write(message); + process.exitCode = code; + } + + var c = childProcess.exec(cmd, execOptions, function (err) { + if (!err) { + process.exitCode = 0; + } else if (isMaxBufferError(err)) { + appendError('maxBuffer exceeded', 1); + } else if (err.code === undefined && err.message) { + /* istanbul ignore next */ + appendError(err.message, 1); + } else if (err.code === undefined) { + /* istanbul ignore next */ + appendError('Unknown issue', 1); + } else { + process.exitCode = err.code; + } + }); + + c.stdout.pipe(stdoutStream); + c.stderr.pipe(stderrStream); + c.stdout.pipe(process.stdout); + c.stderr.pipe(process.stderr); + + if (pipe) { + c.stdin.end(pipe); + } +} + +// This file should only be executed. This module does not export anything. +/* istanbul ignore else */ +if (require.main === module) { + main(); +} diff --git a/src/exec.js b/src/exec.js index 6a2e1261a..0082f63ed 100644 --- a/src/exec.js +++ b/src/exec.js @@ -1,82 +1,92 @@ -var common = require('./common'); -var _tempDir = require('./tempdir'); -var _pwd = require('./pwd'); var path = require('path'); var fs = require('fs'); var child = require('child_process'); +var common = require('./common'); +var _tempDir = require('./tempdir').tempDir; +var _pwd = require('./pwd'); + +// Resolve exec-child.js path at module load time using require.resolve() so that +// bundlers (e.g. Next.js/webpack) do not break the path by replacing __dirname +// with the bundled output directory. require.resolve() always returns the real +// filesystem path of the module, regardless of bundler transformations. +var EXEC_CHILD_PATH = require.resolve('./exec-child.js'); var DEFAULT_MAXBUFFER_SIZE = 20 * 1024 * 1024; +var DEFAULT_ERROR_CODE = 1; common.register('exec', _exec, { unix: false, canReceivePipe: true, wrapOutput: false, + handlesFatalDynamically: true, }); -// Hack to run child_process.exec() synchronously (sync avoids callback hell) -// Uses a custom wait loop that checks for a flag file, created when the child process is done. -// (Can't do a wait loop that checks for internal Node variables/messages as -// Node is single-threaded; callbacks and other internal state changes are done in the -// event loop). +// We use this function to run `exec` synchronously while also providing realtime +// output. function execSync(cmd, opts, pipe) { if (!common.config.execPath) { - common.error('Unable to find a path to the node binary. Please manually set config.execPath'); + try { + common.error('Unable to find a path to the node binary. Please manually set config.execPath'); + } catch (e) { + if (opts.fatal) { + throw e; + } + + return; + } } var tempDir = _tempDir(); - var stdoutFile = path.resolve(tempDir + '/' + common.randomFileName()); - var stderrFile = path.resolve(tempDir + '/' + common.randomFileName()); - var codeFile = path.resolve(tempDir + '/' + common.randomFileName()); - var scriptFile = path.resolve(tempDir + '/' + common.randomFileName()); + var paramsFile = path.join(tempDir, common.randomFileName()); + var stderrFile = path.join(tempDir, common.randomFileName()); + var stdoutFile = path.join(tempDir, common.randomFileName()); opts = common.extend({ silent: common.config.silent, + fatal: common.config.fatal, // TODO(nfischer): this and the line above are probably unnecessary cwd: _pwd().toString(), env: process.env, maxBuffer: DEFAULT_MAXBUFFER_SIZE, + encoding: 'utf8', }, opts); - if (fs.existsSync(scriptFile)) common.unlinkSync(scriptFile); - if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile); + if (fs.existsSync(paramsFile)) common.unlinkSync(paramsFile); if (fs.existsSync(stderrFile)) common.unlinkSync(stderrFile); - if (fs.existsSync(codeFile)) common.unlinkSync(codeFile); - - var execCommand = JSON.stringify(common.config.execPath) + ' ' + JSON.stringify(scriptFile); - var script; + if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile); opts.cwd = path.resolve(opts.cwd); - var optString = JSON.stringify(opts); - - script = [ - "var child = require('child_process')", - " , fs = require('fs');", - 'var childProcess = child.exec(' + JSON.stringify(cmd) + ', ' + optString + ', function(err) {', - ' var fname = ' + JSON.stringify(codeFile) + ';', - ' if (!err) {', - ' fs.writeFileSync(fname, "0");', - ' } else if (err.code === undefined) {', - ' fs.writeFileSync(fname, "1");', - ' } else {', - ' fs.writeFileSync(fname, err.code.toString());', - ' }', - '});', - 'var stdoutStream = fs.createWriteStream(' + JSON.stringify(stdoutFile) + ');', - 'var stderrStream = fs.createWriteStream(' + JSON.stringify(stderrFile) + ');', - 'childProcess.stdout.pipe(stdoutStream, {end: false});', - 'childProcess.stderr.pipe(stderrStream, {end: false});', - 'childProcess.stdout.pipe(process.stdout);', - 'childProcess.stderr.pipe(process.stderr);', - ].join('\n') + - (pipe ? '\nchildProcess.stdin.end(' + JSON.stringify(pipe) + ');\n' : '\n') + - [ - 'var stdoutEnded = false, stderrEnded = false;', - 'function tryClosingStdout(){ if(stdoutEnded){ stdoutStream.end(); } }', - 'function tryClosingStderr(){ if(stderrEnded){ stderrStream.end(); } }', - "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosingStdout(); });", - "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosingStderr(); });", - ].join('\n'); - - fs.writeFileSync(scriptFile, script); + + var paramsToSerialize = { + command: cmd, + execOptions: opts, + pipe, + stdoutFile, + stderrFile, + }; + + // Create the files and ensure these are locked down (for read and write) to + // the current user. The main concerns here are: + // + // * If we execute a command which prints sensitive output, then + // stdoutFile/stderrFile must not be readable by other users. + // * paramsFile must not be readable by other users, or else they can read it + // to figure out the path for stdoutFile/stderrFile and create these first + // (locked down to their own access), which will crash exec() when it tries + // to write to the files. + function writeFileLockedDown(filePath, data) { + fs.writeFileSync(filePath, data, { + encoding: 'utf8', + mode: parseInt('600', 8), + }); + } + writeFileLockedDown(stdoutFile, ''); + writeFileLockedDown(stderrFile, ''); + writeFileLockedDown(paramsFile, JSON.stringify(paramsToSerialize)); + + var execArgs = [ + EXEC_CHILD_PATH, + paramsFile, + ]; /* istanbul ignore else */ if (opts.silent) { @@ -85,36 +95,45 @@ function execSync(cmd, opts, pipe) { opts.stdio = [0, 1, 2]; } + var code = 0; + // Welcome to the future try { - child.execSync(execCommand, opts); + // Bad things if we pass in a `shell` option to child_process.execFileSync, + // so we need to explicitly remove it here. + delete opts.shell; + + child.execFileSync(common.config.execPath, execArgs, opts); } catch (e) { - // Clean up immediately if we have an exception - try { common.unlinkSync(scriptFile); } catch (e2) {} - try { common.unlinkSync(stdoutFile); } catch (e2) {} - try { common.unlinkSync(stderrFile); } catch (e2) {} - try { common.unlinkSync(codeFile); } catch (e2) {} - throw e; + // Commands with non-zero exit code raise an exception. + code = e.status || DEFAULT_ERROR_CODE; } - // At this point codeFile exists, but it's not necessarily flushed yet. - // Keep reading it until it is. - var code = parseInt('', 10); - while (isNaN(code)) { - code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10); + // fs.readFileSync uses buffer encoding by default, so call + // it without the encoding option if the encoding is 'buffer'. + // Also, if the exec timeout is too short for node to start up, + // the files will not be created, so these calls will throw. + var stdout = ''; + var stderr = ''; + if (opts.encoding === 'buffer') { + stdout = fs.readFileSync(stdoutFile); + stderr = fs.readFileSync(stderrFile); + } else { + stdout = fs.readFileSync(stdoutFile, opts.encoding); + stderr = fs.readFileSync(stderrFile, opts.encoding); } - var stdout = fs.readFileSync(stdoutFile, 'utf8'); - var stderr = fs.readFileSync(stderrFile, 'utf8'); - // No biggie if we can't erase the files now -- they're in a temp dir anyway - try { common.unlinkSync(scriptFile); } catch (e) {} - try { common.unlinkSync(stdoutFile); } catch (e) {} + // and we locked down permissions (see the note above). + try { common.unlinkSync(paramsFile); } catch (e) {} try { common.unlinkSync(stderrFile); } catch (e) {} - try { common.unlinkSync(codeFile); } catch (e) {} + try { common.unlinkSync(stdoutFile); } catch (e) {} if (code !== 0) { - common.error('', code, { continue: true }); + // Note: `silent` should be unconditionally true to avoid double-printing + // the command's stderr, and to avoid printing any stderr when the user has + // set `shell.config.silent`. + common.error(stderr, code, { continue: true, silent: true, fatal: opts.fatal }); } var obj = common.ShellString(stdout, stderr, code); return obj; @@ -122,17 +141,16 @@ function execSync(cmd, opts, pipe) { // Wrapper around exec() to enable echoing output to console in real time function execAsync(cmd, opts, pipe, callback) { - var stdout = ''; - var stderr = ''; - opts = common.extend({ silent: common.config.silent, + fatal: common.config.fatal, // TODO(nfischer): this and the line above are probably unnecessary cwd: _pwd().toString(), env: process.env, maxBuffer: DEFAULT_MAXBUFFER_SIZE, + encoding: 'utf8', }, opts); - var c = child.exec(cmd, opts, function (err) { + var c = child.exec(cmd, opts, function (err, stdout, stderr) { if (callback) { if (!err) { callback(0, stdout, stderr); @@ -148,28 +166,27 @@ function execAsync(cmd, opts, pipe, callback) { if (pipe) c.stdin.end(pipe); - c.stdout.on('data', function (data) { - stdout += data; - if (!opts.silent) process.stdout.write(data); - }); - - c.stderr.on('data', function (data) { - stderr += data; - if (!opts.silent) process.stderr.write(data); - }); + if (!opts.silent) { + c.stdout.pipe(process.stdout); + c.stderr.pipe(process.stderr); + } return c; } //@ //@ ### exec(command [, options] [, callback]) -//@ Available options (all `false` by default): +//@ +//@ Available options: //@ //@ + `async`: Asynchronous execution. If a callback is provided, it will be set to -//@ `true`, regardless of the passed value. -//@ + `silent`: Do not echo program output to console. +//@ `true`, regardless of the passed value (default: `false`). +//@ + `fatal`: Exit upon error (default: `false`). +//@ + `silent`: Do not echo program output to console (default: `false`). +//@ + `encoding`: Character encoding to use. Affects the values returned to stdout and stderr, and +//@ what is written to stdout and stderr when not in silent mode (default: `'utf8'`). //@ + and any option available to Node.js's -//@ [child_process.exec()](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) +//@ [`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) //@ //@ Examples: //@ @@ -188,21 +205,21 @@ function execAsync(cmd, opts, pipe, callback) { //@ }); //@ ``` //@ -//@ Executes the given `command` _synchronously_, unless otherwise specified. When in synchronous -//@ mode, this returns a ShellString (compatible with ShellJS v0.6.x, which returns an object -//@ of the form `{ code:..., stdout:... , stderr:... }`). Otherwise, this returns the child process -//@ object, and the `callback` gets the arguments `(code, stdout, stderr)`. +//@ Executes the given `command` _synchronously_, unless otherwise specified. +//@ When in synchronous mode, this returns a [ShellString](#shellstringstr). +//@ Otherwise, this returns the child process object, and the `callback` +//@ receives the arguments `(code, stdout, stderr)`. //@ //@ Not seeing the behavior you want? `exec()` runs everything through `sh` //@ by default (or `cmd.exe` on Windows), which differs from `bash`. If you //@ need bash-specific behavior, try out the `{shell: 'path/to/bash'}` option. //@ -//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as -//@ the current synchronous implementation uses a lot of CPU. This should be getting -//@ fixed soon. +//@ **Security note:** as `shell.exec()` executes an arbitrary string in the +//@ system shell, it is **critical** to properly sanitize user input to avoid +//@ **command injection**. For more context, consult the [Security +//@ Guidelines](https://github.com/shelljs/shelljs/wiki/Security-guidelines). function _exec(command, options, callback) { options = options || {}; - if (!command) common.error('must specify command'); var pipe = common.readFromPipe(); @@ -219,17 +236,26 @@ function _exec(command, options, callback) { options = common.extend({ silent: common.config.silent, + fatal: common.config.fatal, async: false, }, options); - try { - if (options.async) { - return execAsync(command, options, pipe, callback); - } else { - return execSync(command, options, pipe); + if (!command) { + try { + common.error('must specify command'); + } catch (e) { + if (options.fatal) { + throw e; + } + + return; } - } catch (e) { - common.error('internal error'); + } + + if (options.async) { + return execAsync(command, options, pipe, callback); + } else { + return execSync(command, options, pipe); } } module.exports = _exec; diff --git a/src/find.js b/src/find.js index 76a16c4ee..80db9935b 100644 --- a/src/find.js +++ b/src/find.js @@ -1,13 +1,17 @@ -var fs = require('fs'); var path = require('path'); var common = require('./common'); var _ls = require('./ls'); -common.register('find', _find, {}); +common.register('find', _find, { + cmdOptions: { + 'L': 'link', + }, +}); //@ //@ ### find(path [, path ...]) //@ ### find(path_array) +//@ //@ Examples: //@ //@ ```javascript @@ -16,10 +20,11 @@ common.register('find', _find, {}); //@ find('.').filter(function(file) { return file.match(/\.js$/); }); //@ ``` //@ -//@ Returns array of all files (however deep) in the given paths. +//@ Returns a [ShellString](#shellstringstr) (with array-like properties) of all +//@ files (however deep) in the given paths. //@ //@ The main difference from `ls('-R', path)` is that the resulting file names -//@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`. +//@ include the base directories (e.g., `lib/resources/file1` instead of just `file1`). function _find(options, paths) { if (!paths) { common.error('no path specified'); @@ -36,13 +41,13 @@ function _find(options, paths) { list.push(file); } - // why not simply do ls('-R', paths)? because the output wouldn't give the base dirs - // to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory + // why not simply do `ls('-R', paths)`? because the output wouldn't give the base dirs + // to get the base dir in the output, we need instead `ls('-R', 'dir/*')` for every directory paths.forEach(function (file) { var stat; try { - stat = fs.statSync(file); + stat = common.statFollowLinks(file); } catch (e) { common.error('no such file or directory: ' + file); } @@ -50,7 +55,7 @@ function _find(options, paths) { pushFile(file); if (stat.isDirectory()) { - _ls({ recursive: true, all: true }, file).forEach(function (subfile) { + _ls({ recursive: true, all: true, link: options.link }, file).forEach(function (subfile) { pushFile(path.join(file, subfile)); }); } diff --git a/src/grep.js b/src/grep.js index 8cf53b157..cfc83e40e 100644 --- a/src/grep.js +++ b/src/grep.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('grep', _grep, { globStart: 2, // don't glob-expand the regex @@ -7,39 +7,91 @@ common.register('grep', _grep, { cmdOptions: { 'v': 'inverse', 'l': 'nameOnly', + 'i': 'ignoreCase', + 'n': 'lineNumber', + 'B': 'beforeContext', + 'A': 'afterContext', + 'C': 'context', }, }); //@ //@ ### grep([options,] regex_filter, file [, file ...]) //@ ### grep([options,] regex_filter, file_array) +//@ //@ Available options: //@ -//@ + `-v`: Inverse the sense of the regex and print the lines not matching the criteria. -//@ + `-l`: Print only filenames of matching files +//@ + `-v`: Invert `regex_filter` (only print non-matching lines). +//@ + `-l`: Print only filenames of matching files. +//@ + `-i`: Ignore case. +//@ + `-n`: Print line numbers. +//@ + `-B `: Show `` lines before each result. +//@ + `-A `: Show `` lines after each result. +//@ + `-C `: Show `` lines before and after each result. -B and -A override this option. //@ //@ Examples: //@ //@ ```javascript //@ grep('-v', 'GLOBAL_VARIABLE', '*.js'); //@ grep('GLOBAL_VARIABLE', '*.js'); +//@ grep('-B', 3, 'GLOBAL_VARIABLE', '*.js'); +//@ grep({ '-B': 3 }, 'GLOBAL_VARIABLE', '*.js'); +//@ grep({ '-B': 3, '-C': 2 }, 'GLOBAL_VARIABLE', '*.js'); //@ ``` //@ -//@ Reads input string from given files and returns a string containing all lines of the -//@ file that match the given `regex_filter`. +//@ Reads input string from given files and returns a +//@ [ShellString](#shellstringstr) containing all lines of the @ file that match +//@ the given `regex_filter`. function _grep(options, regex, files) { // Check if this is coming from a pipe var pipe = common.readFromPipe(); if (!files && !pipe) common.error('no paths given', 2); - files = [].slice.call(arguments, 2); + var idx = 2; + var contextError = ': invalid context length argument'; + // If the option has been found but not read, copy value from arguments + if (options.beforeContext === true) { + idx = 3; + options.beforeContext = Number(arguments[1]); + if (options.beforeContext < 0) { + common.error(options.beforeContext + contextError, 2); + } + } + if (options.afterContext === true) { + idx = 3; + options.afterContext = Number(arguments[1]); + if (options.afterContext < 0) { + common.error(options.afterContext + contextError, 2); + } + } + if (options.context === true) { + idx = 3; + options.context = Number(arguments[1]); + if (options.context < 0) { + common.error(options.context + contextError, 2); + } + } + // If before or after not given but context is, update values + if (typeof options.context === 'number') { + if (options.beforeContext === false) { + options.beforeContext = options.context; + } + if (options.afterContext === false) { + options.afterContext = options.context; + } + } + regex = arguments[idx - 1]; + files = [].slice.call(arguments, idx); if (pipe) { files.unshift('-'); } var grep = []; + if (options.ignoreCase) { + regex = new RegExp(regex, 'i'); + } files.forEach(function (file) { if (!fs.existsSync(file) && file !== '-') { common.error('no such file or directory: ' + file, 2, { continue: true }); @@ -53,15 +105,94 @@ function _grep(options, regex, files) { } } else { var lines = contents.split('\n'); - lines.forEach(function (line) { + var matches = []; + + lines.forEach(function (line, index) { var matched = line.match(regex); if ((options.inverse && !matched) || (!options.inverse && matched)) { - grep.push(line); + var lineNumber = index + 1; + var result = {}; + if (matches.length > 0) { + // If the last result intersects, combine them + var last = matches[matches.length - 1]; + var minimumLineNumber = Math.max( + 1, + lineNumber - options.beforeContext - 1, + ); + if ( + last.hasOwnProperty('' + lineNumber) || + last.hasOwnProperty('' + minimumLineNumber) + ) { + result = last; + } + } + result[lineNumber] = { + line, + match: true, + }; + if (options.beforeContext > 0) { + // Store the lines with their line numbers to check for overlap + lines + .slice(Math.max(index - options.beforeContext, 0), index) + .forEach(function (v, i, a) { + var lineNum = '' + (index - a.length + i + 1); + if (!result.hasOwnProperty(lineNum)) { + result[lineNum] = { line: v, match: false }; + } + }); + } + if (options.afterContext > 0) { + // Store the lines with their line numbers to check for overlap + lines + .slice( + index + 1, + Math.min(index + options.afterContext + 1, lines.length - 1), + ) + .forEach(function (v, i) { + var lineNum = '' + (index + 1 + i + 1); + if (!result.hasOwnProperty(lineNum)) { + result[lineNum] = { line: v, match: false }; + } + }); + } + // Only add the result if it's new + if (!matches.includes(result)) { + matches.push(result); + } } }); + + // Loop through the matches and add them to the output + Array.prototype.push.apply( + grep, + matches.map(function (result) { + return Object.entries(result) + .map(function (entry) { + var lineNumber = entry[0]; + var line = entry[1].line; + var match = entry[1].match; + return options.lineNumber + ? lineNumber + (match ? ':' : '-') + line + : line; + }) + .join('\n'); + }), + ); } }); - return grep.join('\n') + '\n'; + if (grep.length === 0 && common.state.errorCode !== 2) { + // We didn't hit the error above, but pattern didn't match + common.error('', { silent: true }); + } + + var separator = '\n'; + if ( + typeof options.beforeContext === 'number' || + typeof options.afterContext === 'number' + ) { + separator = '\n--\n'; + } + return grep.join(separator) + '\n'; } module.exports = _grep; diff --git a/src/head.js b/src/head.js index 2640b13b4..f3f4f2269 100644 --- a/src/head.js +++ b/src/head.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('head', _head, { canReceivePipe: true, @@ -33,6 +33,7 @@ function readSomeLines(file, numLines) { //@ //@ ### head([{'-n': \},] file [, file ...]) //@ ### head([{'-n': \},] file_array) +//@ //@ Available options: //@ //@ + `-n `: Show the first `` lines of the files @@ -45,7 +46,7 @@ function readSomeLines(file, numLines) { //@ var str = head(['file1', 'file2']); // same as above //@ ``` //@ -//@ Read the start of a file. +//@ Read the start of a `file`. Returns a [ShellString](#shellstringstr). function _head(options, files) { var head = []; var pipe = common.readFromPipe(); @@ -71,7 +72,7 @@ function _head(options, files) { if (!fs.existsSync(file)) { common.error('no such file or directory: ' + file, { continue: true }); return; - } else if (fs.statSync(file).isDirectory()) { + } else if (common.statFollowLinks(file).isDirectory()) { common.error("error reading '" + file + "': Is a directory", { continue: true, }); diff --git a/src/ln.js b/src/ln.js index 9b8beb9ec..1d3d0e745 100644 --- a/src/ln.js +++ b/src/ln.js @@ -11,6 +11,7 @@ common.register('ln', _ln, { //@ //@ ### ln([options,] source, dest) +//@ //@ Available options: //@ //@ + `-s`: symlink @@ -23,7 +24,9 @@ common.register('ln', _ln, { //@ ln('-sf', 'file', 'existing'); //@ ``` //@ -//@ Links source to dest. Use -f to force the link, should dest already exist. +//@ Links `source` to `dest`. Use `-f` to force the link, should `dest` already +//@ exist. Returns a [ShellString](#shellstringstr) indicating success or +//@ failure. function _ln(options, source, dest) { if (!source || !dest) { common.error('Missing and/or '); @@ -48,7 +51,7 @@ function _ln(options, source, dest) { var resolvedSourcePath = isAbsolute ? sourcePath : path.resolve(process.cwd(), path.dirname(dest), source); if (!fs.existsSync(resolvedSourcePath)) { common.error('Source file does not exist', { continue: true }); - } else if (isWindows && fs.statSync(resolvedSourcePath).isDirectory()) { + } else if (isWindows && common.statFollowLinks(resolvedSourcePath).isDirectory()) { linkType = 'junction'; } diff --git a/src/ls.js b/src/ls.js index bb1b6a7bd..7f32c6e09 100644 --- a/src/ls.js +++ b/src/ls.js @@ -1,9 +1,10 @@ var path = require('path'); var fs = require('fs'); +var glob = require('fast-glob'); var common = require('./common'); -var glob = require('glob'); -var globPatternRecursive = path.sep + '**'; +// glob patterns use the UNIX path seperator +var globPatternRecursive = '/**'; common.register('ls', _ls, { cmdOptions: { @@ -19,27 +20,35 @@ common.register('ls', _ls, { //@ //@ ### ls([options,] [path, ...]) //@ ### ls([options,] path_array) +//@ //@ Available options: //@ //@ + `-R`: recursive //@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`) //@ + `-L`: follow symlinks //@ + `-d`: list directories themselves, not their contents -//@ + `-l`: list objects representing each file, each with fields containing `ls -//@ -l` output fields. See -//@ [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats) -//@ for more info +//@ + `-l`: provides more details for each file. Specifically, each file is +//@ represented by a structured object with separate fields for file +//@ metadata (see +//@ [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)). The +//@ return value also overrides `.toString()` to resemble `ls -l`'s +//@ output format for human readability, but programmatic usage should +//@ depend on the stable object format rather than the `.toString()` +//@ representation. //@ //@ Examples: //@ //@ ```javascript //@ ls('projs/*.js'); +//@ ls('projs/**/*.js'); // Find all js files recursively in projs //@ ls('-R', '/users/me', '/tmp'); //@ ls('-R', ['/users/me', '/tmp']); // same as above //@ ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...} //@ ``` //@ -//@ Returns array of files in the given path, or in current directory if no path provided. +//@ Returns a [ShellString](#shellstringstr) (with array-like properties) of all +//@ the files in the given `path`, or files in the current directory if no +//@ `path` is provided. function _ls(options, paths) { if (options.all_deprecated) { // We won't support the -a option as it's hard to image why it's useful @@ -62,7 +71,7 @@ function _ls(options, paths) { relName = relName.replace(/\\/g, '/'); } if (options.long) { - stat = stat || (options.link ? fs.statSync(abs) : fs.lstatSync(abs)); + stat = stat || (options.link ? common.statFollowLinks(abs) : common.statNoFollowLinks(abs)); list.push(addLsAttributes(relName, stat)); } else { // list.push(path.relative(rel || '.', file)); @@ -74,7 +83,20 @@ function _ls(options, paths) { var stat; try { - stat = options.link ? fs.statSync(p) : fs.lstatSync(p); + stat = options.link ? common.statFollowLinks(p) : common.statNoFollowLinks(p); + // follow links to directories by default + if (stat.isSymbolicLink()) { + /* istanbul ignore next */ + // workaround for https://github.com/shelljs/shelljs/issues/795 + // codecov seems to have a bug that miscalculate this block as uncovered. + // but according to nyc report this block does get covered. + try { + var _stat = common.statFollowLinks(p); + if (_stat.isDirectory()) { + stat = _stat; + } + } catch (_) {} // bad symlink, treat it like a file + } } catch (e) { common.error('no such file or directory: ' + p, 2, { continue: true }); return; @@ -84,13 +106,20 @@ function _ls(options, paths) { if (stat.isDirectory() && !options.directory) { if (options.recursive) { // use glob, because it's simple - glob.sync(p + globPatternRecursive, { dot: options.all, follow: options.link }) - .forEach(function (item) { - // Glob pattern returns the directory itself and needs to be filtered out. - if (path.relative(p, item)) { - pushFile(item, path.relative(p, item)); - } - }); + glob.sync(p + globPatternRecursive, { + // These options are just to make fast-glob be compatible with POSIX + // (bash) wildcard behavior. + onlyFiles: false, + + // These options depend on the cmdOptions provided to ls. + dot: options.all, + followSymbolicLinks: options.link, + }).forEach(function (item) { + // Glob pattern returns the directory itself and needs to be filtered out. + if (path.relative(p, item)) { + pushFile(item, path.relative(p, item)); + } + }); } else if (options.all) { // use fs.readdirSync, because it's fast fs.readdirSync(p).forEach(function (item) { diff --git a/src/mkdir.js b/src/mkdir.js index 44b1b2162..021cad9c0 100644 --- a/src/mkdir.js +++ b/src/mkdir.js @@ -1,6 +1,6 @@ -var common = require('./common'); var fs = require('fs'); var path = require('path'); +var common = require('./common'); common.register('mkdir', _mkdir, { cmdOptions: { @@ -8,7 +8,7 @@ common.register('mkdir', _mkdir, { }, }); -// Recursively creates 'dir' +// Recursively creates `dir` function mkdirSyncRecursive(dir) { var baseDir = path.dirname(dir); @@ -19,25 +19,27 @@ function mkdirSyncRecursive(dir) { common.error('dirname() failed: [' + dir + ']'); } - // Base dir exists, no recursion necessary - if (fs.existsSync(baseDir)) { - fs.mkdirSync(dir, parseInt('0777', 8)); - return; - } - // Base dir does not exist, go recursive - mkdirSyncRecursive(baseDir); + if (!fs.existsSync(baseDir)) { + mkdirSyncRecursive(baseDir); + } - // Base dir created, can create dir - fs.mkdirSync(dir, parseInt('0777', 8)); + try { + // Base dir created, can create dir + fs.mkdirSync(dir, parseInt('0777', 8)); + } catch (e) { + // swallow error if dir already exists + if (e.code !== 'EEXIST' || common.statNoFollowLinks(dir).isFile()) { throw e; } + } } //@ //@ ### mkdir([options,] dir [, dir ...]) //@ ### mkdir([options,] dir_array) +//@ //@ Available options: //@ -//@ + `-p`: full path (will create intermediate dirs if necessary) +//@ + `-p`: full path (and create intermediate directories, if necessary) //@ //@ Examples: //@ @@ -46,7 +48,8 @@ function mkdirSyncRecursive(dir) { //@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above //@ ``` //@ -//@ Creates directories. +//@ Creates directories. Returns a [ShellString](#shellstringstr) indicating +//@ success or failure. function _mkdir(options, dirs) { if (!dirs) common.error('no paths given'); @@ -57,7 +60,7 @@ function _mkdir(options, dirs) { dirs.forEach(function (dir) { try { - var stat = fs.lstatSync(dir); + var stat = common.statNoFollowLinks(dir); if (!options.fullpath) { common.error('path already exists: ' + dir, { continue: true }); } else if (stat.isFile()) { @@ -95,5 +98,5 @@ function _mkdir(options, dirs) { } }); return ''; -} // mkdir +} // man arraykdir module.exports = _mkdir; diff --git a/src/mv.js b/src/mv.js index 6ebaa8a03..6e89e2fae 100644 --- a/src/mv.js +++ b/src/mv.js @@ -22,6 +22,7 @@ function checkRecentCreated(sources, index) { //@ //@ ### mv([options ,] source [, source ...], dest') //@ ### mv([options ,] source_array, dest') +//@ //@ Available options: //@ //@ + `-f`: force (default behavior) @@ -35,7 +36,8 @@ function checkRecentCreated(sources, index) { //@ mv(['file1', 'file2'], 'dir/'); // same as above //@ ``` //@ -//@ Moves files. +//@ Moves `source` file(s) to `dest`. Returns a [ShellString](#shellstringstr) +//@ indicating success or failure. function _mv(options, sources, dest) { // Get sources, dest if (arguments.length < 3) { @@ -51,7 +53,7 @@ function _mv(options, sources, dest) { } var exists = fs.existsSync(dest); - var stats = exists && fs.statSync(dest); + var stats = exists && common.statFollowLinks(dest); // Dest is not existing dir, but multiple sources given if ((!exists || !stats.isDirectory()) && sources.length > 1) { @@ -74,7 +76,7 @@ function _mv(options, sources, dest) { // When copying to '/path/dir': // thisDest = '/path/dir/file1' var thisDest = dest; - if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) { + if (fs.existsSync(dest) && common.statFollowLinks(dest).isDirectory()) { thisDest = path.normalize(dest + '/' + path.basename(src)); } @@ -107,8 +109,8 @@ function _mv(options, sources, dest) { // to perform a copy and then clean up the original file. If either the // copy or the rm fails with an exception, we should allow this // exception to pass up to the top level. - cp('-r', src, thisDest); - rm('-rf', src); + cp({ recursive: true }, src, thisDest); + rm({ recursive: true, force: true }, src); } } }); // forEach(src) diff --git a/src/pwd.js b/src/pwd.js index 38618518b..8527d8b38 100644 --- a/src/pwd.js +++ b/src/pwd.js @@ -7,7 +7,8 @@ common.register('pwd', _pwd, { //@ //@ ### pwd() -//@ Returns the current directory. +//@ +//@ Returns the current directory as a [ShellString](#shellstringstr). function _pwd() { var pwd = path.resolve(process.cwd()); return pwd; diff --git a/src/rm.js b/src/rm.js index 2ad6914b4..6bb57558b 100644 --- a/src/rm.js +++ b/src/rm.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('rm', _rm, { cmdOptions: { @@ -25,20 +25,19 @@ function rmdirSyncRecursive(dir, force, fromSymlink) { // Loop through and delete everything in the sub-tree after checking it for (var i = 0; i < files.length; i++) { var file = dir + '/' + files[i]; - var currFile = fs.lstatSync(file); + var currFile = common.statNoFollowLinks(file); if (currFile.isDirectory()) { // Recursive function back to the beginning rmdirSyncRecursive(file, force); - } else { // Assume it's a file - perhaps a try/catch belongs here? - if (force || isWriteable(file)) { - try { - common.unlinkSync(file); - } catch (e) { - /* istanbul ignore next */ - common.error('could not remove file (code ' + e.code + '): ' + file, { - continue: true, - }); - } + } else if (force || isWriteable(file)) { + // Assume it's a file - perhaps a try/catch belongs here? + try { + common.unlinkSync(file); + } catch (e) { + /* istanbul ignore next */ + common.error('could not remove file (code ' + e.code + '): ' + file, { + continue: true, + }); } } } @@ -116,7 +115,7 @@ function handleDirectory(file, options) { function handleSymbolicLink(file, options) { var stats; try { - stats = fs.statSync(file); + stats = common.statFollowLinks(file); } catch (e) { // symlink is broken, so remove the symlink itself common.unlinkSync(file); @@ -149,6 +148,7 @@ function handleFIFO(file) { //@ //@ ### rm([options,] file [, file ...]) //@ ### rm([options,] file_array) +//@ //@ Available options: //@ //@ + `-f`: force @@ -162,7 +162,8 @@ function handleFIFO(file) { //@ rm(['some_file.txt', 'another_file.txt']); // same as above //@ ``` //@ -//@ Removes files. +//@ Removes files. Returns a [ShellString](#shellstringstr) indicating success +//@ or failure. function _rm(options, files) { if (!files) common.error('no paths given'); @@ -175,7 +176,7 @@ function _rm(options, files) { var filepath = (file[file.length - 1] === '/') ? file.slice(0, -1) // remove the '/' so lstatSync can detect symlinks : file; - lstats = fs.lstatSync(filepath); // test for existence + lstats = common.statNoFollowLinks(filepath); // test for existence } catch (e) { // Path does not exist, no force flag given if (!options.force) { diff --git a/src/sed.js b/src/sed.js index 7d396e7e9..693652362 100644 --- a/src/sed.js +++ b/src/sed.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('sed', _sed, { globStart: 3, // don't glob-expand regexes @@ -12,19 +12,20 @@ common.register('sed', _sed, { //@ //@ ### sed([options,] search_regex, replacement, file [, file ...]) //@ ### sed([options,] search_regex, replacement, file_array) +//@ //@ Available options: //@ -//@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ +//@ + `-i`: Replace contents of `file` in-place. _Note that no backups will be created!_ //@ //@ Examples: //@ //@ ```javascript //@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); -//@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); //@ ``` //@ -//@ Reads an input string from `files` and performs a JavaScript `replace()` on the input -//@ using the given search regex and replacement string or function. Returns the new string after replacement. +//@ Reads an input string from `file`s, line by line, and performs a JavaScript `replace()` on +//@ each of the lines from the input string using the given `search_regex` and `replacement` string or +//@ function. Returns the new [ShellString](#shellstringstr) after replacement. //@ //@ Note: //@ @@ -34,6 +35,10 @@ common.register('sed', _sed, { //@ ```javascript //@ sed(/(\w+)\s(\w+)/, '$2, $1', 'file.txt'); //@ ``` +//@ +//@ Also, like unix `sed`, ShellJS `sed` runs replacements on each line from the input file +//@ (split by '\n') separately, so `search_regex`es that span more than one line (or include '\n') +//@ will not match anything and nothing will be replaced. function _sed(options, regex, replacement, files) { // Check if this is coming from a pipe var pipe = common.readFromPipe(); @@ -81,6 +86,10 @@ function _sed(options, regex, replacement, files) { } }); - return sed.join('\n'); + if (options.inplace) { + return ''; + } else { + return sed.join('\n'); + } } module.exports = _sed; diff --git a/src/set.js b/src/set.js index 238e23e4a..6f37bc9a9 100644 --- a/src/set.js +++ b/src/set.js @@ -7,6 +7,7 @@ common.register('set', _set, { //@ //@ ### set(options) +//@ //@ Available options: //@ //@ + `+/-e`: exit upon error (`config.fatal`) @@ -20,7 +21,7 @@ common.register('set', _set, { //@ set('+e'); // this undoes a "set('-e')" //@ ``` //@ -//@ Sets global configuration variables +//@ Sets global configuration variables. function _set(options) { if (!options) { var args = [].slice.call(arguments, 0); @@ -50,6 +51,5 @@ function _set(options) { common.config[key] = options[key]; } }); - return; } module.exports = _set; diff --git a/src/sort.js b/src/sort.js index 5dd8d75fc..66b042ce4 100644 --- a/src/sort.js +++ b/src/sort.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('sort', _sort, { canReceivePipe: true, @@ -41,9 +41,10 @@ function numericalCmp(a, b) { //@ //@ ### sort([options,] file [, file ...]) //@ ### sort([options,] file_array) +//@ //@ Available options: //@ -//@ + `-r`: Reverse the result of comparisons +//@ + `-r`: Reverse the results //@ + `-n`: Compare according to numerical value //@ //@ Examples: @@ -53,8 +54,9 @@ function numericalCmp(a, b) { //@ sort('-r', 'foo.txt'); //@ ``` //@ -//@ Return the contents of the files, sorted line-by-line. Sorting multiple -//@ files mixes their content, just like unix sort does. +//@ Return the contents of the `file`s, sorted line-by-line as a +//@ [ShellString](#shellstringstr). Sorting multiple files mixes their content +//@ (just as unix `sort` does). function _sort(options, files) { // Check if this is coming from a pipe var pipe = common.readFromPipe(); @@ -72,7 +74,7 @@ function _sort(options, files) { if (!fs.existsSync(file)) { common.error('no such file or directory: ' + file, { continue: true }); return accum; - } else if (fs.statSync(file).isDirectory()) { + } else if (common.statFollowLinks(file).isDirectory()) { common.error('read failed: ' + file + ': Is a directory', { continue: true, }); diff --git a/src/tail.js b/src/tail.js index e5a88055c..eee75c570 100644 --- a/src/tail.js +++ b/src/tail.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('tail', _tail, { canReceivePipe: true, @@ -11,9 +11,10 @@ common.register('tail', _tail, { //@ //@ ### tail([{'-n': \},] file [, file ...]) //@ ### tail([{'-n': \},] file_array) +//@ //@ Available options: //@ -//@ + `-n `: Show the last `` lines of the files +//@ + `-n `: Show the last `` lines of `file`s //@ //@ Examples: //@ @@ -23,7 +24,7 @@ common.register('tail', _tail, { //@ var str = tail(['file1', 'file2']); // same as above //@ ``` //@ -//@ Read the end of a file. +//@ Read the end of a `file`. Returns a [ShellString](#shellstringstr). function _tail(options, files) { var tail = []; var pipe = common.readFromPipe(); @@ -31,12 +32,20 @@ function _tail(options, files) { if (!files && !pipe) common.error('no paths given'); var idx = 1; + var plusOption = false; if (options.numLines === true) { idx = 2; + if (arguments[1][0] === '+') { + plusOption = true; + } options.numLines = Number(arguments[1]); } else if (options.numLines === false) { options.numLines = 10; } + // arguments[0] is a json object + if (arguments[0].numLines[0] === '+') { + plusOption = true; + } options.numLines = -1 * Math.abs(options.numLines); files = [].slice.call(arguments, idx); @@ -50,7 +59,7 @@ function _tail(options, files) { if (!fs.existsSync(file)) { common.error('no such file or directory: ' + file, { continue: true }); return; - } else if (fs.statSync(file).isDirectory()) { + } else if (common.statFollowLinks(file).isDirectory()) { common.error("error reading '" + file + "': Is a directory", { continue: true, }); @@ -68,12 +77,14 @@ function _tail(options, files) { shouldAppendNewline = false; } - tail = tail.concat(lines.slice(options.numLines)); + tail = tail.concat(plusOption ? lines.slice(-options.numLines - 1) : lines.slice(options.numLines)); }); if (shouldAppendNewline) { tail.push(''); // to add a trailing newline once we join } + return tail.join('\n'); } + module.exports = _tail; diff --git a/src/tempdir.js b/src/tempdir.js index b3a6cca38..b6f7796ee 100644 --- a/src/tempdir.js +++ b/src/tempdir.js @@ -1,6 +1,6 @@ -var common = require('./common'); var os = require('os'); var fs = require('fs'); +var common = require('./common'); common.register('tempdir', _tempDir, { allowGlobbing: false, @@ -11,7 +11,7 @@ common.register('tempdir', _tempDir, { function writeableDir(dir) { if (!dir || !fs.existsSync(dir)) return false; - if (!fs.statSync(dir).isDirectory()) return false; + if (!common.statFollowLinks(dir).isDirectory()) return false; var testFile = dir + '/' + common.randomFileName(); try { @@ -24,6 +24,8 @@ function writeableDir(dir) { } } +// Variable to cache the tempdir value for successive lookups. +var cachedTempDir; //@ //@ ### tempdir() @@ -37,10 +39,9 @@ function writeableDir(dir) { //@ Searches and returns string containing a writeable, platform-dependent temporary directory. //@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). function _tempDir() { - var state = common.state; - if (state.tempDir) return state.tempDir; // from cache + if (cachedTempDir) return cachedTempDir; - state.tempDir = writeableDir(os.tmpdir && os.tmpdir()) || // node 0.10+ + cachedTempDir = writeableDir(os.tmpdir()) || writeableDir(process.env.TMPDIR) || writeableDir(process.env.TEMP) || writeableDir(process.env.TMP) || @@ -54,6 +55,21 @@ function _tempDir() { writeableDir('/usr/tmp') || writeableDir('.'); // last resort - return state.tempDir; + return cachedTempDir; +} + +// Indicates if the tempdir value is currently cached. This is exposed for tests +// only. The return value should only be tested for truthiness. +function isCached() { + return cachedTempDir; } -module.exports = _tempDir; + +// Clears the cached tempDir value, if one is cached. This is exposed for tests +// only. +function clearCache() { + cachedTempDir = undefined; +} + +module.exports.tempDir = _tempDir; +module.exports.isCached = isCached; +module.exports.clearCache = clearCache; diff --git a/src/test.js b/src/test.js index d3d9c07a0..7e769083d 100644 --- a/src/test.js +++ b/src/test.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('test', _test, { cmdOptions: { @@ -19,6 +19,7 @@ common.register('test', _test, { //@ //@ ### test(expression) +//@ //@ Available expression primaries: //@ //@ + `'-b', 'path'`: true if path is a block device @@ -34,10 +35,11 @@ common.register('test', _test, { //@ //@ ```javascript //@ if (test('-d', path)) { /* do something with dir */ }; -//@ if (!test('-f', path)) continue; // skip if it's a regular file +//@ if (!test('-f', path)) continue; // skip if it's not a regular file //@ ``` //@ -//@ Evaluates expression using the available primaries and returns corresponding value. +//@ Evaluates `expression` using the available primaries and returns +//@ corresponding boolean value. function _test(options, path) { if (!path) common.error('no path given'); @@ -52,7 +54,7 @@ function _test(options, path) { if (options.link) { try { - return fs.lstatSync(path).isSymbolicLink(); + return common.statNoFollowLinks(path).isSymbolicLink(); } catch (e) { return false; } @@ -62,7 +64,7 @@ function _test(options, path) { if (options.exists) return true; - var stats = fs.statSync(path); + var stats = common.statFollowLinks(path); if (options.block) return stats.isBlockDevice(); diff --git a/src/to.js b/src/to.js index d3d9e37be..e4b064f49 100644 --- a/src/to.js +++ b/src/to.js @@ -1,6 +1,6 @@ -var common = require('./common'); var fs = require('fs'); var path = require('path'); +var common = require('./common'); common.register('to', _to, { pipeOnly: true, @@ -17,8 +17,9 @@ common.register('to', _to, { //@ ``` //@ //@ Analogous to the redirection operator `>` in Unix, but works with -//@ ShellStrings (such as those returned by `cat`, `grep`, etc). _Like Unix -//@ redirections, `to()` will overwrite any existing file!_ +//@ `ShellStrings` (such as those returned by `cat`, `grep`, etc.). _Like Unix +//@ redirections, `to()` will overwrite any existing file!_ Returns the same +//@ [ShellString](#shellstringstr) this operated on, to support chaining. function _to(options, file) { if (!file) common.error('wrong arguments'); diff --git a/src/toEnd.js b/src/toEnd.js index dc165fe8d..dc30e6264 100644 --- a/src/toEnd.js +++ b/src/toEnd.js @@ -1,6 +1,6 @@ -var common = require('./common'); var fs = require('fs'); var path = require('path'); +var common = require('./common'); common.register('toEnd', _toEnd, { pipeOnly: true, @@ -17,7 +17,8 @@ common.register('toEnd', _toEnd, { //@ ``` //@ //@ Analogous to the redirect-and-append operator `>>` in Unix, but works with -//@ ShellStrings (such as those returned by `cat`, `grep`, etc). +//@ `ShellStrings` (such as those returned by `cat`, `grep`, etc.). Returns the +//@ same [ShellString](#shellstringstr) this operated on, to support chaining. function _toEnd(options, file) { if (!file) common.error('wrong arguments'); diff --git a/src/touch.js b/src/touch.js index b672b2d25..a26858648 100644 --- a/src/touch.js +++ b/src/touch.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); common.register('touch', _touch, { cmdOptions: { @@ -14,25 +14,32 @@ common.register('touch', _touch, { //@ //@ ### touch([options,] file [, file ...]) //@ ### touch([options,] file_array) +//@ //@ Available options: //@ //@ + `-a`: Change only the access time //@ + `-c`: Do not create any files //@ + `-m`: Change only the modification time -//@ + `-d DATE`: Parse DATE and use it instead of current time -//@ + `-r FILE`: Use FILE's times instead of current time +//@ + `{'-d': someDate}`, `{date: someDate}`: Use a `Date` instance (ex. `someDate`) +//@ instead of current time +//@ + `{'-r': file}`, `{reference: file}`: Use `file`'s times instead of current +//@ time //@ //@ Examples: //@ //@ ```javascript //@ touch('source.js'); -//@ touch('-c', '/path/to/some/dir/source.js'); -//@ touch({ '-r': FILE }, '/path/to/some/dir/source.js'); +//@ touch('-c', 'path/to/file.js'); +//@ touch({ '-r': 'referenceFile.txt' }, 'path/to/file.js'); +//@ touch({ '-d': new Date('December 17, 1995 03:24:00'), '-m': true }, 'path/to/file.js'); +//@ touch({ date: new Date('December 17, 1995 03:24:00') }, 'path/to/file.js'); //@ ``` //@ -//@ Update the access and modification times of each FILE to the current time. -//@ A FILE argument that does not exist is created empty, unless -c is supplied. -//@ This is a partial implementation of *[touch(1)](http://linux.die.net/man/1/touch)*. +//@ Update the access and modification times of each file to the current time. +//@ A file argument that does not exist is created empty, unless `-c` is supplied. +//@ This is a partial implementation of +//@ [`touch(1)`](http://linux.die.net/man/1/touch). Returns a +//@ [ShellString](#shellstringstr) indicating success or failure. function _touch(opts, files) { if (!files) { common.error('no files given'); @@ -103,7 +110,7 @@ module.exports = _touch; function tryStatFile(filePath) { try { - return fs.statSync(filePath); + return common.statFollowLinks(filePath); } catch (e) { return null; } diff --git a/src/uniq.js b/src/uniq.js index 8c0f04010..580270651 100644 --- a/src/uniq.js +++ b/src/uniq.js @@ -1,5 +1,5 @@ -var common = require('./common'); var fs = require('fs'); +var common = require('./common'); // add c spaces to the left of str function lpad(c, str) { @@ -21,6 +21,7 @@ common.register('uniq', _uniq, { //@ //@ ### uniq([options,] [input, [output]]) +//@ //@ Available options: //@ //@ + `-i`: Ignore case while comparing @@ -35,7 +36,8 @@ common.register('uniq', _uniq, { //@ uniq('-cd', 'foo.txt', 'bar.txt'); //@ ``` //@ -//@ Filter adjacent matching lines from input +//@ Filter adjacent matching lines from `input`. Returns a +//@ [ShellString](#shellstringstr). function _uniq(options, input, output) { // Check if this is coming from a pipe var pipe = common.readFromPipe(); @@ -45,17 +47,17 @@ function _uniq(options, input, output) { if (!fs.existsSync(input)) { common.error(input + ': No such file or directory'); - } else if (fs.statSync(input).isDirectory()) { + } else if (common.statFollowLinks(input).isDirectory()) { common.error("error reading '" + input + "'"); } } - if (output && fs.existsSync(output) && fs.statSync(output).isDirectory()) { + if (output && fs.existsSync(output) && common.statFollowLinks(output).isDirectory()) { common.error(output + ': Is a directory'); } - var lines = (input ? fs.readFileSync(input, 'utf8') : pipe). - trimRight(). - split('\n'); + var lines = (input ? fs.readFileSync(input, 'utf8') : pipe) + .trimRight() + .split('\n'); var compare = function (a, b) { return options.ignoreCase ? diff --git a/src/which.js b/src/which.js index a5f9e15eb..8ac7b77e5 100644 --- a/src/which.js +++ b/src/which.js @@ -1,6 +1,6 @@ -var common = require('./common'); var fs = require('fs'); var path = require('path'); +var common = require('./common'); common.register('which', _which, { allowGlobbing: false, @@ -9,17 +9,37 @@ common.register('which', _which, { }, }); -// XP's system default value for PATHEXT system variable, just in case it's not +// XP's system default value for `PATHEXT` system variable, just in case it's not // set on Windows. var XP_DEFAULT_PATHEXT = '.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh'; -// Cross-platform method for splitting environment PATH variables +// For earlier versions of NodeJS that doesn't have a list of constants (< v6) +var FILE_EXECUTABLE_MODE = 1; + +function isWindowsPlatform() { + return process.platform === 'win32'; +} + +// Cross-platform method for splitting environment `PATH` variables function splitPath(p) { return p ? p.split(path.delimiter) : []; } +// Tests are running all cases for this func but it stays uncovered by codecov due to unknown reason +/* istanbul ignore next */ +function isExecutable(pathName) { + try { + // TODO(node-support): replace with fs.constants.X_OK once remove support for node < v6 + fs.accessSync(pathName, FILE_EXECUTABLE_MODE); + } catch (err) { + return false; + } + return true; +} + function checkPath(pathName) { - return fs.existsSync(pathName) && !fs.statSync(pathName).isDirectory(); + return fs.existsSync(pathName) && !common.statFollowLinks(pathName).isDirectory() + && (isWindowsPlatform() || isExecutable(pathName)); } //@ @@ -31,20 +51,20 @@ function checkPath(pathName) { //@ var nodeExec = which('node'); //@ ``` //@ -//@ Searches for `command` in the system's PATH. On Windows, this uses the +//@ Searches for `command` in the system's `PATH`. On Windows, this uses the //@ `PATHEXT` variable to append the extension if it's not already executable. -//@ Returns string containing the absolute path to the command. +//@ Returns a [ShellString](#shellstringstr) containing the absolute path to +//@ `command`. function _which(options, cmd) { if (!cmd) common.error('must specify command'); - var isWindows = process.platform === 'win32'; - var pathEnv = process.env.path || process.env.Path || process.env.PATH; - var pathArray = splitPath(pathEnv); + var isWindows = isWindowsPlatform(); + var pathArray = splitPath(process.env.PATH); var queryMatches = []; // No relative/absolute paths provided? - if (cmd.indexOf('/') === -1) { + if (!cmd.includes('/')) { // Assume that there are no extensions to append to queries (this is the // case for unix) var pathExtArray = ['']; @@ -66,8 +86,8 @@ function _which(options, cmd) { attempt = attempt.toUpperCase(); } - var match = attempt.match(/\.[^<>:"/\|?*.]+$/); - if (match && pathExtArray.indexOf(match[0]) >= 0) { // this is Windows-only + var match = attempt.match(/\.[^<>:"/|?*.]+$/); + if (match && pathExtArray.includes(match[0])) { // this is Windows-only // The user typed a query with the file extension, like // `which('node.exe')` if (checkPath(attempt)) { diff --git a/test/.eslintrc.json b/test/.eslintrc.json index fd8099cb3..1e28f5360 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -4,23 +4,40 @@ }, "extends": "airbnb-base", "rules": { - "import/no-mutable-exports": 0, - "global-require": 0, - "vars-on-top": 0, - "spaced-comment": [2, "always", { "markers": ["@", "@include"], "exceptions": ["@"] }], - "no-param-reassign": 0, - "no-console": 0, - "curly": 0, - "no-var": 2, - "prefer-const": 2, - "prefer-template": 0, - "prefer-arrow-callback": 0, - "no-underscore-dangle": 0, - "max-len": 0, - "new-cap": [2, { + "arrow-parens": "off", + "comma-dangle": "off", + "curly": "off", + "function-call-argument-newline": "off", + "function-paren-newline": "off", + "global-require": "off", + "import/no-dynamic-require": "off", + "import/no-mutable-exports": "off", + "import/no-unresolved": "off", + "indent": "off", + "max-len": "off", + "no-bitwise": "off", + "no-console": "off", + "no-multiple-empty-lines": "off", + "no-param-reassign": "off", + "no-plusplus": "off", + "no-underscore-dangle": "off", + "no-var": "error", + "operator-linebreak": "off", + "prefer-arrow-callback": "off", + "prefer-const": "error", + "prefer-destructuring": "off", + "prefer-numeric-literals": "off", + "prefer-template": "off", + "spaced-comment": ["error", "always", { "markers": ["@", "@include"], "exceptions": ["@"] }], + "vars-on-top": "off", + "new-cap": ["error", { "capIsNewExceptions": [ "ShellString" - ]} - ] + ] + }], + "quotes": ["error", "single", { + "avoidEscape": true, + "allowTemplateLiterals": true + }] } } diff --git a/test/cat.js b/test/cat.js index c61905f6a..e077b9be9 100644 --- a/test/cat.js +++ b/test/cat.js @@ -1,8 +1,8 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); shell.config.silent = true; @@ -64,3 +64,56 @@ test('glob', t => { t.truthy(result.search('test1') > -1); // file order might be random t.truthy(result.search('test2') > -1); }); + +test('without EOF', t => { + const result = shell.cat('test/resources/cat/file3'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), 'test3'); +}); + +test('empty', t => { + const result = shell.cat('test/resources/cat/file5'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ''); +}); + +// +// With numbers +// + +test('simple with numbers', t => { + const result = shell.cat('-n', 'test/resources/cat/file1'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ' 1\ttest1\n'); +}); + +test('simple twelve lines file with numbers', t => { + const result = shell.cat('-n', 'test/resources/cat/file4'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ' 1\ttest4-01\n 2\ttest4-02\n 3\ttest4-03\n 4\ttest4-04\n 5\ttest4-05\n 6\ttest4-06\n 7\ttest4-07\n 8\ttest4-08\n 9\ttest4-09\n 10\ttest4-10\n 11\ttest4-11\n 12\ttest4-12\n'); +}); + +test('multiple with numbers', t => { + const result = shell.cat('-n', 'test/resources/cat/file2', 'test/resources/cat/file1'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ' 1\ttest2\n 2\ttest1\n'); +}); + +test('simple numbers without EOF', t => { + const result = shell.cat('-n', 'test/resources/cat/file3'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ' 1\ttest3'); +}); + +test('multiple numbers without EOF', t => { + const result = shell.cat('-n', 'test/resources/cat/file3', 'test/resources/cat/file2', 'test/resources/cat/file1'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), ' 1\ttest3test2\n 2\ttest1\n'); +}); diff --git a/test/cd.js b/test/cd.js index c472973da..53404e249 100644 --- a/test/cd.js +++ b/test/cd.js @@ -1,11 +1,11 @@ -import fs from 'fs'; -import os from 'os'; -import path from 'path'; +const fs = require('fs'); +const os = require('os'); +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); const cur = shell.pwd().toString(); diff --git a/test/chmod.js b/test/chmod.js index 7b617ab3a..660972c91 100644 --- a/test/chmod.js +++ b/test/chmod.js @@ -1,9 +1,10 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); let TMP; const BITMASK = parseInt('777', 8); @@ -26,7 +27,7 @@ test('invalid permissions', t => { let result = shell.chmod('blah'); t.truthy(shell.error()); t.is(result.code, 1); - result = shell.chmod('893', `${TMP}/chmod`); // invalid permissions - mode must be in octal + result = shell.chmod('893', `${TMP}/chmod`); // invalid permissions - mode must be in octal t.truthy(shell.error()); t.is(result.code, 1); }); @@ -36,13 +37,13 @@ test('Basic usage with octal codes', t => { let result = shell.chmod('755', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('755', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('644', 8) ); }); @@ -53,7 +54,7 @@ test('symbolic mode', t => { let result = shell.chmod('o+x', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('007', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('007', 8), parseInt('005', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -66,7 +67,7 @@ test('symbolic mode, without group', t => { let result = shell.chmod('+x', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('755', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -79,13 +80,13 @@ test('Test setuid', t => { let result = shell.chmod('u+s', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('4000', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('4000', 8), parseInt('4000', 8) ); result = shell.chmod('u-s', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('644', 8) ); @@ -97,7 +98,7 @@ test('Test setuid', t => { result = shell.chmod('755', `${TMP}/chmod/c`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/c`).mode & parseInt('4000', 8), + common.statFollowLinks(`${TMP}/chmod/c`).mode & parseInt('4000', 8), parseInt('4000', 8) ); result = shell.chmod('u-s', `${TMP}/chmod/c`); @@ -110,13 +111,13 @@ test('Test setgid', t => { let result = shell.chmod('g+s', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('2000', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('2000', 8), parseInt('2000', 8) ); result = shell.chmod('g-s', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('644', 8) ); }); @@ -127,16 +128,16 @@ test('Test sticky bit', t => { let result = shell.chmod('+t', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('1000', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('1000', 8), parseInt('1000', 8) ); result = shell.chmod('-t', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/file1`).mode & BITMASK, parseInt('644', 8) ); - t.is(fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('1000', 8), 0); + t.is(common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('1000', 8), 0); }); }); @@ -145,7 +146,7 @@ test('Test directories', t => { let result = shell.chmod('a-w', `${TMP}/chmod/b/a/b`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/b/a/b`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/b/a/b`).mode & BITMASK, parseInt('555', 8) ); result = shell.chmod('755', `${TMP}/chmod/b/a/b`); @@ -158,13 +159,13 @@ test('Test recursion', t => { let result = shell.chmod('-R', 'a+w', `${TMP}/chmod/b`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/b/a/b`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/b/a/b`).mode & BITMASK, BITMASK ); result = shell.chmod('-R', '755', `${TMP}/chmod/b`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/b/a/b`).mode & BITMASK, + common.statFollowLinks(`${TMP}/chmod/b/a/b`).mode & BITMASK, parseInt('755', 8) ); }); @@ -176,11 +177,11 @@ test('Test symbolic links w/ recursion - WARNING: *nix only', t => { let result = shell.chmod('-R', 'u-w', `${TMP}/chmod/a/b`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/a/b/c`).mode & parseInt('700', 8), + common.statFollowLinks(`${TMP}/chmod/a/b/c`).mode & parseInt('700', 8), parseInt('500', 8) ); t.is( - fs.statSync(`${TMP}/chmod/b/a`).mode & parseInt('700', 8), + common.statFollowLinks(`${TMP}/chmod/b/a`).mode & parseInt('700', 8), parseInt('700', 8) ); result = shell.chmod('-R', 'u+w', `${TMP}/chmod/a/b`); @@ -193,7 +194,7 @@ test('Test combinations', t => { let result = shell.chmod('a-rwx', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('000', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('000', 8), parseInt('000', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -204,7 +205,7 @@ test('multiple symbolic modes', t => { let result = shell.chmod('a-rwx,u+r', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('400', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('400', 8), parseInt('400', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -215,7 +216,7 @@ test('multiple symbolic modes #2', t => { let result = shell.chmod('a-rwx,u+rw', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('600', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('600', 8), parseInt('600', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -227,7 +228,7 @@ test('multiple symbolic modes #3', t => { let result = shell.chmod('a-rwx,u+rwx', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('700', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('700', 8), parseInt('700', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -241,7 +242,7 @@ test('u+rw', t => { result = shell.chmod('u+rw', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('600', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('600', 8), parseInt('600', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -255,7 +256,7 @@ test('u+wx', t => { result = shell.chmod('u+wx', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('300', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('300', 8), parseInt('300', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -270,7 +271,7 @@ test('Multiple symbolic modes at once', t => { result = shell.chmod('u+r,g+w,o+x', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('421', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('421', 8), parseInt('421', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -285,7 +286,7 @@ test('u+rw,g+wx', t => { result = shell.chmod('u+rw,g+wx', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('630', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('630', 8), parseInt('630', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -299,7 +300,7 @@ test('u-x,g+rw', t => { result = shell.chmod('u-x,g+rw', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('660', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('660', 8), parseInt('660', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -310,13 +311,13 @@ test('a-rwx,u+rw', t => { let result = shell.chmod('a-rwx,u+rw', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('600', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('600', 8), parseInt('600', 8) ); result = shell.chmod('a-rwx,u+rw', `${TMP}/chmod/file1`); t.is(result.code, 0); t.is( - fs.statSync(`${TMP}/chmod/file1`).mode & parseInt('600', 8), + common.statFollowLinks(`${TMP}/chmod/file1`).mode & parseInt('600', 8), parseInt('600', 8) ); result = shell.chmod('644', `${TMP}/chmod/file1`); @@ -339,19 +340,19 @@ test('Numeric modes', t => { test('Make sure chmod succeeds for a variety of octal codes', t => { utils.skipOnWin(t, () => { t.is( - fs.statSync(`${TMP}/chmod/xdir`).mode & parseInt('755', 8), + common.statFollowLinks(`${TMP}/chmod/xdir`).mode & parseInt('755', 8), parseInt('755', 8) ); t.is( - fs.statSync(`${TMP}/chmod/xdir/file`).mode & parseInt('644', 8), + common.statFollowLinks(`${TMP}/chmod/xdir/file`).mode & parseInt('644', 8), parseInt('644', 8) ); t.is( - fs.statSync(`${TMP}/chmod/xdir/deep`).mode & parseInt('755', 8), + common.statFollowLinks(`${TMP}/chmod/xdir/deep`).mode & parseInt('755', 8), parseInt('755', 8) ); t.is( - fs.statSync(`${TMP}/chmod/xdir/deep/file`).mode & parseInt('644', 8), + common.statFollowLinks(`${TMP}/chmod/xdir/deep/file`).mode & parseInt('644', 8), parseInt('644', 8) ); }); diff --git a/test/cmd.js b/test/cmd.js new file mode 100644 index 000000000..f49c98873 --- /dev/null +++ b/test/cmd.js @@ -0,0 +1,279 @@ +const path = require('path'); + +const test = require('ava'); + +const shell = require('..'); + +const CWD = process.cwd(); + +test.beforeEach(() => { + process.chdir(CWD); + shell.config.resetForTesting(); +}); + +// +// Invalids +// + +test('no args', t => { + shell.cmd(); + t.truthy(shell.error()); +}); + +test('unknown command', t => { + const result = shell.cmd('asdfasdf'); // could not find command + t.truthy(result.code > 0); + t.is(result.code, 127); +}); + +test('config.fatal and unknown command', t => { + shell.config.fatal = true; + t.throws(() => { + shell.cmd('asdfasdf'); // could not find command + }, { message: /.*command not found.*/ }); +}); + +// TODO(nfischer): enable only if we implement realtime output + captured +// output. +test.skip('cmd exits gracefully if we cannot find the execPath', t => { + shell.config.execPath = null; + shell.cmd('shx', 'echo', 'foo'); + t.regex( + shell.error(), + /Unable to find a path to the node binary\. Please manually set config\.execPath/ + ); +}); + +// +// Valids +// + +// +// sync +// + +test('check if stdout goes to output', t => { + const result = shell.cmd('shx', 'echo', 'this is stdout'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'this is stdout\n'); +}); + +test('check if stderr goes to output', t => { + const result = shell.cmd(shell.config.execPath, '-e', 'console.error("1234");'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, ''); + t.is(result.stderr, '1234\n'); +}); + +test('check if stdout + stderr go to output', t => { + const result = shell.cmd(shell.config.execPath, '-e', 'console.error(1234); console.log(666);'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, '666\n'); + t.is(result.stderr, '1234\n'); +}); + +test('check exit code', t => { + const result = shell.cmd(shell.config.execPath, '-e', 'process.exit(12);'); + t.truthy(shell.error()); + t.is(result.code, 12); +}); + +test('interaction with cd', t => { + shell.cd('test/resources/external'); + const result = shell.cmd(shell.config.execPath, 'node_script.js'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'node_script_1234\n'); +}); + +test('no need to escape quotes', t => { + const result = shell.cmd(shell.config.execPath, '-e', + `console.log("'+'_'+'");`); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, "'+'_'+'\n"); +}); + +test('commands can contain newlines', t => { + // GitHub issue #175. This test uses a nodejs script rather than a shell + // command because Windows 'echo' doesn't handle \n the same way as Unix + // 'echo'. This test case proves the newline is passed correctly to the + // underlying program because otherwise node would not parse the two lines as + // separate statements and it would throw a JavaScript syntax error. + const result = shell.cmd(shell.config.execPath, '-e', ` +console.log('line1') +console.log('line2') +`); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'line1\nline2\n'); +}); + +test('does not expand shell-style variables', t => { + shell.env.FOO = 'Hello world'; + const result = shell.cmd('shx', 'echo', '$FOO'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, '$FOO\n'); +}); + +test('does not expand windows-style variables', t => { + shell.env.FOO = 'Hello world'; + let result = shell.cmd('shx', 'echo', '%FOO%'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, '%FOO%\n'); + result = shell.cmd('shx', 'echo', '!FOO!'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, '!FOO!\n'); +}); + +test('caret character is passed through to the command', t => { + // '^' is a special character on Windows, see issue #1015 + const result = shell.cmd('shx', 'echo', 'shelljs@^0.8.4'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'shelljs@^0.8.4\n'); +}); + +test('cannot inject multiple commands', t => { + const injection = '; echo semicolon && echo and || echo or'; + const result = shell.cmd('shx', 'echo', `hi${injection}`); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, `hi${injection}\n`); +}); + +test('supports globbing by default', t => { + // `echo` on windows will not glob, so it depends on shell.cmd() to expand the + // glob before spawning the subprocess. + const result = shell.cmd('shx', 'echo', 'test/resources/*.txt'); + t.falsy(shell.error()); + t.is(result.code, 0); + const expectedFiles = [ + 'test/resources/a.txt', + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.is(result.stdout, `${expectedFiles.join(' ')}\n`); +}); + +test('globbing respects config.noglob', t => { + shell.config.noglob = true; + const result = shell.cmd('shx', 'echo', 'test/resources/*.txt'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'test/resources/*.txt\n'); +}); + +test('set cwd', t => { + const result = shell.cmd('shx', 'pwd', { cwd: '..' }); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, path.resolve('..') + '\n'); +}); + +test('command fails silently with non-zero status', t => { + const result = shell.cmd('shx', 'false'); + t.truthy(shell.error()); + t.is(result.code, 1); + t.is(result.stdout, ''); + t.is(result.stderr, ''); +}); + +test('set maxBuffer (very small)', t => { + let result = shell.cmd('shx', 'echo', '1234567890'); // default maxBuffer is ok + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, '1234567890\n'); + result = shell.cmd('shx', 'echo', '1234567890', { maxBuffer: 6 }); + t.truthy(shell.error()); + t.is(result.code, 1); + t.is(result.stdout, '1234567890\n'); +}); + +test('set timeout option', t => { + let result = shell.cmd(shell.config.execPath, 'test/resources/exec/slow.js', '100'); // default timeout is ok + t.falsy(shell.error()); + t.is(result.stdout, 'fast\nslow\n'); + t.is(result.code, 0); + result = shell.cmd(shell.config.execPath, 'test/resources/exec/slow.js', '2000', { timeout: 1000 }); // times out + t.truthy(shell.error()); + t.is(result.stdout, 'fast\n'); + t.truthy(result.stderr); + t.is(result.code, 1); +}); + +test('check process.env works', t => { + shell.env.FOO = 'Hello world'; + // Launch any sub process, and process.env should be propagated through. + const result = + shell.cmd(shell.config.execPath, '-p', 'process.env.FOO'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.stdout, 'Hello world\n'); + t.is(result.stderr, ''); +}); + +test('cmd returns a ShellString', t => { + const result = shell.cmd('shx', 'echo', 'foo'); + t.is(typeof result, 'object'); + t.truthy(result instanceof String); + t.is(typeof result.stdout, 'string'); + t.is(result.toString(), result.stdout); +}); + +// +// async +// + +function cmdAsync(...commandArgs) { + return new Promise((resolve) => { + shell.cmd(...commandArgs, (code, stdout, stderr) => { + resolve({ code, stdout, stderr }); + }); + }); +} + +// TODO(nfischer): enable after we implement async. +test.skip('no callback', t => { + const c = shell.cmd(shell.config.execPath, '-e', 'console.log(1234)', { async: true }); + t.falsy(shell.error()); + t.truthy('stdout' in c, 'async exec returns child process object'); +}); + +// TODO(nfischer): enable after we implement async. +test.skip('callback as 2nd argument', async t => { + const result = await cmdAsync(shell.config.execPath, '-e', 'console.log(5678);'); + t.is(result.code, 0); + t.is(result.stdout, '5678\n'); + t.is(result.stderr, ''); +}); + +// TODO(nfischer): enable after we implement async. +test.skip('callback as end argument', async t => { + const result = await cmdAsync(shell.config.execPath, '-e', 'console.log(5566);', { async: true }); + t.is(result.code, 0); + t.is(result.stdout, '5566\n'); + t.is(result.stderr, ''); +}); + +// TODO(nfischer): enable after we implement async. +test.skip('callback as 3rd argument (silent:true)', async t => { + const result = await cmdAsync(shell.config.execPath, '-e', 'console.log(5678);', { silent: true }); + t.is(result.code, 0); + t.is(result.stdout, '5678\n'); + t.is(result.stderr, ''); +}); + +// TODO(nfischer): enable after we implement async. +test.skip('command that fails', async t => { + const result = await cmdAsync('shx', 'cp', 'onlyOneCpArgument.txt', { silent: true }); + t.is(result.code, 1); + t.is(result.stdout, ''); + t.is(result.stderr, 'cp: missing and/or \n'); +}); diff --git a/test/common.js b/test/common.js index 7f8fb1364..8fc836590 100644 --- a/test/common.js +++ b/test/common.js @@ -1,16 +1,19 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import common from '../src/common'; -import utils from './utils/utils'; - -shell.config.silent = true; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); test.beforeEach(() => { + shell.config.silent = true; common.state.error = null; common.state.errorCode = 0; }); +test.afterEach(() => { + common.config.resetForTesting(); +}); + // // Invalids // @@ -18,13 +21,13 @@ test.beforeEach(() => { test('too few args', t => { t.throws(() => { common.expand(); - }, TypeError); + }, { instanceOf: TypeError }); }); test('should be a list', t => { t.throws(() => { common.expand('test/resources'); - }, TypeError); + }, { instanceOf: TypeError }); }); test('parseOptions (invalid option in options object)', t => { @@ -34,7 +37,7 @@ test('parseOptions (invalid option in options object)', t => { f: 'force', r: 'reverse', }); - }); + }, { instanceOf: common.CommandError }); }); test('parseOptions (without a hyphen in the string)', t => { @@ -42,7 +45,7 @@ test('parseOptions (without a hyphen in the string)', t => { common.parseOptions('f', { f: 'force', }); - }); + }, { instanceOf: Error }); }); test('parseOptions (opt is not a string/object)', t => { @@ -50,13 +53,13 @@ test('parseOptions (opt is not a string/object)', t => { common.parseOptions(1, { f: 'force', }); - }); + }, { instanceOf: TypeError }); }); test('parseOptions (map is not an object)', t => { t.throws(() => { common.parseOptions('-f', 27); - }); + }, { instanceOf: TypeError }); }); test('parseOptions (errorOptions is not an object)', t => { @@ -64,7 +67,7 @@ test('parseOptions (errorOptions is not an object)', t => { common.parseOptions('-f', { f: 'force', }, 'not a valid errorOptions'); - }); + }, { instanceOf: TypeError }); }); test('parseOptions (unrecognized string option)', t => { @@ -72,7 +75,7 @@ test('parseOptions (unrecognized string option)', t => { common.parseOptions('-z', { f: 'force', }); - }); + }, { instanceOf: common.CommandError }); }); test('parseOptions (unrecognized option in Object)', t => { @@ -96,17 +99,17 @@ test('parseOptions (invalid type)', t => { test('convertErrorOutput: no args', t => { t.throws(() => { common.convertErrorOutput(); - }, TypeError); + }, { instanceOf: TypeError }); }); test('convertErrorOutput: input must be a vanilla string', t => { t.throws(() => { common.convertErrorOutput(3); - }, TypeError); + }, { instanceOf: TypeError }); t.throws(() => { common.convertErrorOutput({}); - }, TypeError); + }, { instanceOf: TypeError }); }); // @@ -139,25 +142,21 @@ test('convertErrorOutput: changes backslashes to forward slashes', t => { // test('single file, array syntax', t => { const result = common.expand(['test/resources/file1.txt']); - t.falsy(shell.error()); t.deepEqual(result, ['test/resources/file1.txt']); }); test('multiple file, glob syntax, * for file name', t => { const result = common.expand(['test/resources/file*.txt']); - t.falsy(shell.error()); t.deepEqual(result.sort(), ['test/resources/file1.txt', 'test/resources/file2.txt'].sort()); }); test('multiple file, glob syntax, * for directory name', t => { const result = common.expand(['test/r*/file*.txt']); - t.falsy(shell.error()); t.deepEqual(result.sort(), ['test/resources/file1.txt', 'test/resources/file2.txt'].sort()); }); test('multiple file, glob syntax, ** for directory name', t => { const result = common.expand(['test/resources/**/file*.js']); - t.falsy(shell.error()); t.deepEqual( result.sort(), ['test/resources/file1.js', 'test/resources/file2.js', 'test/resources/ls/file1.js', 'test/resources/ls/file2.js'].sort() @@ -166,25 +165,41 @@ test('multiple file, glob syntax, ** for directory name', t => { test('broken links still expand', t => { const result = common.expand(['test/resources/b*dlink']); - t.falsy(shell.error()); t.deepEqual(result, ['test/resources/badlink']); }); +test('glob syntax, ? for single char in directory name', t => { + const result = common.expand(['test/r?sources/file1.txt']); + t.deepEqual(result, ['test/resources/file1.txt']); +}); + +test('glob syntax, ? for single char in directory name with * in file name', t => { + const result = common.expand(['test/r?sources/file*.txt']); + t.deepEqual(result.sort(), ['test/resources/file1.txt', 'test/resources/file2.txt'].sort()); +}); + +test('glob syntax, ? in multiple path segments', t => { + const result = common.expand(['test/r?sources/file?.txt']); + t.deepEqual(result.sort(), ['test/resources/file1.txt', 'test/resources/file2.txt'].sort()); +}); + +test('glob syntax, ? in file name still works', t => { + const result = common.expand(['test/resources/file?.txt']); + t.deepEqual(result.sort(), ['test/resources/file1.txt', 'test/resources/file2.txt'].sort()); +}); + test('empty array', t => { const result = common.expand([]); - t.falsy(shell.error()); t.deepEqual(result, []); }); test('empty string', t => { const result = common.expand(['']); - t.falsy(shell.error()); t.deepEqual(result, ['']); }); test('non-string', t => { const result = common.expand([5]); - t.falsy(shell.error()); t.deepEqual(result, [5]); }); @@ -193,14 +208,12 @@ test('non-string', t => { // test('common.buffer returns buffer', t => { const buf = common.buffer(); - t.falsy(shell.error()); t.truthy(buf instanceof Buffer); t.is(buf.length, 64 * 1024); }); test('common.buffer with explicit length', t => { const buf = common.buffer(20); - t.falsy(shell.error()); t.truthy(buf instanceof Buffer); t.is(buf.length, 20); }); @@ -208,10 +221,8 @@ test('common.buffer with explicit length', t => { test('common.buffer with different config.bufLength', t => { common.config.bufLength = 20; const buf = common.buffer(); - t.falsy(shell.error()); t.truthy(buf instanceof Buffer); t.is(buf.length, 20); - common.config.reset(); }); test('common.parseOptions (normal case)', t => { @@ -272,7 +283,42 @@ test('common.parseOptions using an object to hold options', t => { test('common.parseOptions throws when passed a string not starting with "-"', t => { t.throws(() => { common.parseOptions('a', { '-a': 'throws' }); - }, Error, "Options string must start with a '-'"); + }, { instanceOf: Error }, "Options string must start with a '-'"); +}); + +test('common.parseOptions allows long options', t => { + const result = common.parseOptions({ value: true }, { + v: 'value', + }); + t.truthy(result.value); +}); + +test('common.parseOptions allows long options with values', t => { + const someObject = {}; + const result = common.parseOptions({ value: someObject }, { + v: 'value', + }); + t.is(result.value, someObject); +}); + +test('common.parseOptions throws for unknown long option', t => { + t.throws(() => { + common.parseOptions({ throws: true }, { + v: 'value', + }); + }, { instanceOf: common.CommandError }); +}); + +test('common.parseOptions with -- argument', t => { + const result = common.parseOptions('--', { + R: 'recursive', + f: 'force', + r: 'reverse', + }); + + t.falsy(result.recursive); + t.falsy(result.force); + t.falsy(result.reverse); }); test('Some basic tests on the ShellString type', t => { @@ -284,16 +330,14 @@ test('Some basic tests on the ShellString type', t => { t.truthy(result.toEnd); }); -test.cb('Commands that fail will still output error messages to stderr', t => { - const script = 'require(\'./global\'); ls(\'noexist\'); cd(\'noexist\');'; - utils.runScript(script, (err, stdout, stderr) => { - t.is(stdout, ''); - t.is( - stderr, - 'ls: no such file or directory: noexist\ncd: no such file or directory: noexist\n' - ); - t.end(); - }); +test('Commands that fail will still output error messages to stderr', async t => { + const script = "require('./global'); ls('noexist'); cd('noexist');"; + const result = await utils.runScript(script); + t.is(result.stdout, ''); + t.is( + result.stderr, + 'ls: no such file or directory: noexist\ncd: no such file or directory: noexist\n' + ); }); test('execPath value makes sense', t => { @@ -306,3 +350,10 @@ test('Changing shell.config.execPath does not modify process', t => { shell.config.execPath = 'foo'; t.not(shell.config.execPath, process.execPath); }); + +test('CommandError is a subclass of Error', t => { + const e = new common.CommandError(new common.ShellString('some value')); + t.truthy(e instanceof common.CommandError); + t.truthy(e instanceof Error); + t.is(e.constructor, common.CommandError); +}); diff --git a/test/config.js b/test/config.js index 743df3561..1e3a337ce 100644 --- a/test/config.js +++ b/test/config.js @@ -1,8 +1,10 @@ -import test from 'ava'; +const path = require('path'); -import shell from '..'; -import common from '../src/common'; -import utils from './utils/utils'; +const test = require('ava'); + +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); // // Valids @@ -30,46 +32,295 @@ test('config.silent can be set to false', t => { // config.fatal // -test.cb('config.fatal = false', t => { +test('config.fatal = false', async t => { t.falsy(shell.config.fatal); - const script = 'require(\'./global.js\'); config.silent=true; config.fatal=false; cp("this_file_doesnt_exist", "."); echo("got here");'; - utils.runScript(script, (err, stdout) => { - t.truthy(stdout.match('got here')); - t.end(); - }); + const script = `require('./global.js'); config.silent=true; config.fatal=false; cp("this_file_doesnt_exist", "."); echo("got here");`; + const result = await utils.runScript(script); + t.truthy(result.stdout.match('got here')); +}); + +test('config.fatal = true', async t => { + const script = `require('./global.js'); config.silent=true; config.fatal=true; cp("this_file_doesnt_exist", "."); echo("got here");`; + await t.throwsAsync(utils.runScript(script), + { message: /this_file_doesnt_exist/ }); }); -test.cb('config.fatal = true', t => { - const script = 'require(\'./global.js\'); config.silent=true; config.fatal=true; cp("this_file_doesnt_exist", "."); echo("got here");'; - utils.runScript(script, (err, stdout) => { - t.falsy(stdout.match('got here')); - t.end(); +test('config.fatal = false with an exec() failure returns, does not throw', t => { + const expected = { code: 2 }; + t.notThrows(() => { + const result = shell.exec('exit 2'); + t.is(result.code, expected.code); }); }); +test('config.fatal = true with an exec() failure includes a .code on the Error', t => { + shell.config.fatal = true; + try { + t.throws(() => { + shell.exec('exit 2'); + }, { code: 2 }); + } finally { + shell.config.fatal = false; + } +}); + // // config.globOptions // -test('Expands to directories by default', t => { +test('config.globOptions expands directories by default', t => { + const result = common.expand(['test/resources/*a*']); + const expected = [ + 'test/resources/a.txt', + 'test/resources/badlink', + 'test/resources/cat', + 'test/resources/external', + 'test/resources/head', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions handles non-wildcards by default', t => { + const result = common.expand(['test/resources/a.txt']); + const expected = [ + 'test/resources/a.txt', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions expands "?" symbol by default', t => { + const result = common.expand(['test/resources/file?.t*']); + const expected = [ + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions expands "*" in multiple path segments by default', t => { + const result = common.expand(['test/r*sources/file?.txt']); + const expected = [ + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); +}); + +// https://github.com/shelljs/shelljs/issues/1197 +test('config.globOptions expands "?" in folder path by default', t => { + const result = common.expand(['test/r?sources/file*.txt']); + const expected = [ + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects cwd', t => { + // Both node-glob and fast-glob call this option 'cwd'. + shell.config.globOptions = { cwd: 'test' }; + const result = common.expand(['resources/*a*']); + const expected = [ + 'resources/a.txt', + 'resources/badlink', + 'resources/cat', + 'resources/external', + 'resources/head', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects dot', t => { + // Both node-glob and fast-glob call this option 'dot'. + shell.config.globOptions = { dot: true }; + const result = common.expand(['test/resources/ls/*']); + t.is(result.length, 8); + t.truthy(result.includes('test/resources/ls/.hidden_dir')); + t.truthy(result.includes('test/resources/ls/.hidden_file')); +}); + +test('config.globOptions respects ignore', t => { + // Both node-glob and fast-glob call this option 'ignore'. + shell.config.globOptions = { ignore: ['test/resources/external'] }; + const result = common.expand(['test/resources/*a*']); + const expected = [ + 'test/resources/a.txt', + 'test/resources/badlink', + 'test/resources/cat', + 'test/resources/head', + ]; + t.deepEqual(result, expected); + // Does not include the result that we chose to ignore + t.falsy(result.includes('test/resources/external')); +}); + +test('config.globOptions respects absolute', t => { + // Both node-glob and fast-glob call this option 'absolute'. + shell.config.globOptions = { absolute: true }; const result = common.expand(['test/resources/*a*']); - t.is(result.length, 5); - t.truthy(result.indexOf('test/resources/a.txt') > -1); - t.truthy(result.indexOf('test/resources/badlink') > -1); - t.truthy(result.indexOf('test/resources/cat') > -1); - t.truthy(result.indexOf('test/resources/head') > -1); - t.truthy(result.indexOf('test/resources/external') > -1); -}); - -test( - 'Check to make sure options get passed through (nodir is an example)', - t => { - shell.config.globOptions = { nodir: true }; - const result = common.expand(['test/resources/*a*']); - t.is(result.length, 2); - t.truthy(result.indexOf('test/resources/a.txt') > -1); - t.truthy(result.indexOf('test/resources/badlink') > -1); - t.truthy(result.indexOf('test/resources/cat') < 0); - t.truthy(result.indexOf('test/resources/external') < 0); + function abs(file) { + // Normalize to posix-style path separators on all platforms. + const CWD = process.platform === 'win32' ? + process.cwd().replace(/\\/g, '/') : + process.cwd(); + return path.posix.join(CWD, file); } -); + const expected = [ + abs('test/resources/a.txt'), + abs('test/resources/badlink'), + abs('test/resources/cat'), + abs('test/resources/external'), + abs('test/resources/head'), + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects nodir', t => { + shell.config.globOptions = { nodir: true }; + const result = common.expand(['test/resources/*a*']); + // Includes files and symlinks. + const expected = [ + 'test/resources/a.txt', + 'test/resources/badlink', + ]; + t.deepEqual(result, expected); + // Does not include the directories. + t.falsy(result.includes('test/resources/cat')); + t.falsy(result.includes('test/resources/head')); + t.falsy(result.includes('test/resources/external')); +}); + +test('config.globOptions respects mark', t => { + shell.config.globOptions = { mark: true }; + const result = common.expand(['test/resources/*a*']); + // Directories get a '/' character at the end. + const expected = [ + 'test/resources/a.txt', + 'test/resources/badlink', + 'test/resources/cat/', + 'test/resources/external/', + 'test/resources/head/', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects nobrace', t => { + // Default behavior is to expand "file{1..2}.txt" to ["file1.txt", + // "file2.txt"]. + let result = common.expand(['test/resources/file{1..2}.txt']); + let expected = [ + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); + + // When 'nobrace' is true, brace expressions will expand as literals. + shell.config.globOptions = { nobrace: true }; + result = common.expand(['test/resources/file{1..2}.txt']); + expected = [ + 'test/resources/file{1..2}.txt', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects noglobstar', t => { + // Default behavior is to expand "**" to match zero or more directories. + let result = common.expand(['test/**/file1']); + let expected = [ + 'test/resources/cat/file1', + 'test/resources/chmod/file1', + 'test/resources/cp/file1', + 'test/resources/file1', + 'test/resources/ls/file1', + 'test/resources/sort/file1', + 'test/resources/uniq/file1', + ]; + t.deepEqual(result, expected); + + // When 'noglobstar' is true, "**" will behave like a regular "*" and matches + // exactly 1 directory. + shell.config.globOptions = { noglobstar: true }; + result = common.expand(['test/**/file1']); + expected = [ + 'test/resources/file1', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects noext', t => { + // Default behavior is to support fancy glob patterns (like "file1.+(js|txt)"). + let result = common.expand([ + 'test/resources/file1.+(js|txt)', + 'test/resources/file2.*', + ]); + let expected = [ + 'test/resources/file1.js', + 'test/resources/file1.txt', + 'test/resources/file2.js', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); + + // When 'noext' is true, this only matches regular globs (like "file2.*"). + shell.config.globOptions = { noext: true }; + result = common.expand([ + 'test/resources/file1.+(js|txt)', + 'test/resources/file2.*', + ]); + expected = [ + 'test/resources/file1.+(js|txt)', + 'test/resources/file2.js', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects nocase', t => { + // Default behavior will change depending on macOS, Windows, or Linux. This is + // difficult to test in a cross-platform way. + + // When 'nocase' is true, we should be able to match files even if we use the + // wrong case in the pattern. + shell.config.globOptions = { nocase: true }; + let result = common.expand(['test/resources/FILE*.TXT']); + let expected = [ + 'test/resources/file1.txt', + 'test/resources/file2.txt', + ]; + t.deepEqual(result, expected); + + // When 'nocase' is false, using the wrong case will fail to match any files. + shell.config.globOptions = { nocase: false }; + result = common.expand(['test/resources/FILE*.TXT']); + expected = [ + 'test/resources/FILE*.TXT', + ]; + t.deepEqual(result, expected); +}); + +test('config.globOptions respects matchBase', t => { + // By default, "*" expressions only match inside of the same directory. + shell.config.globOptions = { cwd: 'test/resources' }; + let result = common.expand(['*ile1']); + let expected = [ + 'file1', + ]; + t.deepEqual(result, expected); + + // When 'matchBase' is true (and the pattern contains no slashes), the + // pattern is implicitly treated like "**/*" and will expand to + // subdirectories. + shell.config.globOptions = { cwd: 'test/resources', matchBase: true }; + result = common.expand(['*ile1']); + expected = [ + 'cat/file1', + 'chmod/file1', + 'cp/file1', + 'file1', + 'head/shortfile1', + 'ls/file1', + 'sort/file1', + 'uniq/file1', + ]; + t.deepEqual(result, expected); +}); diff --git a/test/cp.js b/test/cp.js index 34b81c7e9..749884ccb 100644 --- a/test/cp.js +++ b/test/cp.js @@ -1,9 +1,10 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); const oldMaxDepth = shell.config.maxdepth; const CWD = process.cwd(); @@ -53,7 +54,7 @@ test('invalid option', t => { t.is(result.stderr, 'cp: option not recognized: @'); }); -test('invalid option', t => { +test('invalid option #2', t => { const result = shell.cp('-Z', 'asdfasdf', `${t.context.tmp}/file2`); t.truthy(shell.error()); t.is(result.code, 1); @@ -264,40 +265,34 @@ test('recursive, nothing exists', t => { t.is(shell.ls('-R', 'test/resources/cp').toString(), shell.ls('-R', `${t.context.tmp}/cp`).toString()); }); -test( - 'recursive, nothing exists, source ends in \'/\' (see Github issue #15)', - t => { - const result = shell.cp('-R', 'test/resources/cp/', `${t.context.tmp}/`); - t.falsy(shell.error()); - t.falsy(result.stderr); - t.is(result.code, 0); - t.is(shell.ls('-R', 'test/resources/cp').toString(), shell.ls('-R', `${t.context.tmp}/cp`).toString()); - } -); +test('recursive, nothing exists, source ends in "/"', t => { + // Github issue #15 + const result = shell.cp('-R', 'test/resources/cp/', `${t.context.tmp}/`); + t.falsy(shell.error()); + t.falsy(result.stderr); + t.is(result.code, 0); + t.is(shell.ls('-R', 'test/resources/cp').toString(), shell.ls('-R', `${t.context.tmp}/cp`).toString()); +}); -test( - 'recursive, globbing regular files with extension (see Github issue #376)', - t => { - const result = shell.cp('-R', 'test/resources/file*.txt', t.context.tmp); - t.falsy(shell.error()); - t.falsy(result.stderr); - t.is(result.code, 0); - t.truthy(fs.existsSync(`${t.context.tmp}/file1.txt`)); - t.truthy(fs.existsSync(`${t.context.tmp}/file2.txt`)); - } -); +test('recursive, globbing regular files with extension', t => { + // Github issue #376 + const result = shell.cp('-R', 'test/resources/file*.txt', t.context.tmp); + t.falsy(shell.error()); + t.falsy(result.stderr); + t.is(result.code, 0); + t.truthy(fs.existsSync(`${t.context.tmp}/file1.txt`)); + t.truthy(fs.existsSync(`${t.context.tmp}/file2.txt`)); +}); -test( - 'recursive, copying one regular file (also related to Github issue #376)', - t => { - const result = shell.cp('-R', 'test/resources/file1.txt', t.context.tmp); - t.falsy(shell.error()); - t.falsy(result.stderr); - t.is(result.code, 0); - t.truthy(fs.existsSync(`${t.context.tmp}/file1.txt`)); - t.falsy(fs.statSync(`${t.context.tmp}/file1.txt`).isDirectory()); // don't let it be a dir - } -); +test('recursive, copying one regular file', t => { + // Github issue #376 + const result = shell.cp('-R', 'test/resources/file1.txt', t.context.tmp); + t.falsy(shell.error()); + t.falsy(result.stderr); + t.is(result.code, 0); + t.truthy(fs.existsSync(`${t.context.tmp}/file1.txt`)); + t.falsy(common.statFollowLinks(`${t.context.tmp}/file1.txt`).isDirectory()); // don't let it be a dir +}); test('recursive, everything exists, no force flag', t => { const result = shell.cp('-R', 'test/resources/cp', t.context.tmp); @@ -309,8 +304,8 @@ test('recursive, everything exists, no force flag', t => { test('-R implies to not follow links', t => { utils.skipOnWin(t, () => { shell.cp('-R', 'test/resources/cp/*', t.context.tmp); - t.truthy(fs.lstatSync(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link - t.falsy((fs.lstatSync(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink())); // this one isn't + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link + t.falsy((common.statNoFollowLinks(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink())); // this one isn't t.not( shell.cat(`${t.context.tmp}/links/sym.lnk`).toString(), shell.cat(`${t.context.tmp}/fakeLinks/sym.lnk`).toString() @@ -319,8 +314,8 @@ test('-R implies to not follow links', t => { t.falsy(shell.error()); t.falsy(result.stderr); t.is(result.code, 0); - t.truthy(fs.lstatSync(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link - t.truthy(fs.lstatSync(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink()); // this one is now a link + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink()); // this one is now a link t.is( shell.cat(`${t.context.tmp}/links/sym.lnk`).toString(), shell.cat(`${t.context.tmp}/fakeLinks/sym.lnk`).toString() @@ -333,8 +328,8 @@ test('Missing -R implies -L', t => { // Recursive, everything exists, overwrite a real file *by following a link* // Because missing the -R implies -L. shell.cp('-R', 'test/resources/cp/*', t.context.tmp); - t.truthy(fs.lstatSync(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link - t.falsy((fs.lstatSync(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink())); // this one isn't + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link + t.falsy((common.statNoFollowLinks(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink())); // this one isn't t.not( shell.cat(`${t.context.tmp}/links/sym.lnk`).toString(), shell.cat(`${t.context.tmp}/fakeLinks/sym.lnk`).toString() @@ -343,8 +338,8 @@ test('Missing -R implies -L', t => { t.falsy(shell.error()); t.falsy(result.stderr); t.is(result.code, 0); - t.truthy(fs.lstatSync(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link - t.falsy(fs.lstatSync(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink()); // this one is still not a link + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); // this one is a link + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/fakeLinks/sym.lnk`).isSymbolicLink()); // this one is still not a link // But it still follows the link t.is( shell.cat(`${t.context.tmp}/links/sym.lnk`).toString(), @@ -364,34 +359,30 @@ test('recursive, everything exists, with force flag', t => { t.is(shell.cat('test/resources/cp/dir_a/z').toString(), shell.cat(`${t.context.tmp}/cp/dir_a/z`).toString()); // after cp }); -test( - 'recursive, creates dest dir since it\'s only one level deep (see Github issue #44)', - t => { - const result = shell.cp('-r', 'test/resources/issue44', `${t.context.tmp}/dir2`); - t.falsy(shell.error()); - t.falsy(result.stderr); - t.is(result.code, 0); - t.is(shell.ls('-R', 'test/resources/issue44').toString(), shell.ls('-R', `${t.context.tmp}/dir2`).toString()); - t.is( +test("recursive, creates dest dir since it's only one level deep", t => { + // Github issue #44 + const result = shell.cp('-r', 'test/resources/issue44', `${t.context.tmp}/dir2`); + t.falsy(shell.error()); + t.falsy(result.stderr); + t.is(result.code, 0); + t.is(shell.ls('-R', 'test/resources/issue44').toString(), shell.ls('-R', `${t.context.tmp}/dir2`).toString()); + t.is( shell.cat('test/resources/issue44/main.js').toString(), shell.cat(`${t.context.tmp}/dir2/main.js`).toString() ); - } -); +}); -test( - 'recursive, does *not* create dest dir since it\'s too deep (see Github issue #44)', - t => { - const result = shell.cp('-r', 'test/resources/issue44', `${t.context.tmp}/dir2/dir3`); - t.truthy(shell.error()); - t.is( - result.stderr, - `cp: cannot create directory '${t.context.tmp}/dir2/dir3': No such file or directory` - ); - t.is(result.code, 1); - t.falsy(fs.existsSync(`${t.context.tmp}/dir2`)); - } -); +test("recursive, does *not* create dest dir since it's too deep", t => { + // Github issue #44 + const result = shell.cp('-r', 'test/resources/issue44', `${t.context.tmp}/dir2/dir3`); + t.truthy(shell.error()); + t.is( + result.stderr, + `cp: cannot create directory '${t.context.tmp}/dir2/dir3': No such file or directory` + ); + t.is(result.code, 1); + t.falsy(fs.existsSync(`${t.context.tmp}/dir2`)); +}); test('recursive, copies entire directory', t => { const result = shell.cp('-r', 'test/resources/cp/dir_a', `${t.context.tmp}/dest`); @@ -408,21 +399,17 @@ test('recursive, with trailing slash, does the exact same', t => { t.truthy(fs.existsSync(`${t.context.tmp}/dest/z`)); }); -test( - 'On Windows, permission bits are quite different so skip those tests for now', - t => { - utils.skipOnWin(t, () => { - // preserve mode bits - const execBit = parseInt('001', 8); - t.is(fs.statSync('test/resources/cp-mode-bits/executable').mode & execBit, execBit); - shell.cp('test/resources/cp-mode-bits/executable', `${t.context.tmp}/executable`); - t.is( - fs.statSync('test/resources/cp-mode-bits/executable').mode, - fs.statSync(`${t.context.tmp}/executable`).mode - ); - }); - } -); +test('preserve mode bits by default for file', t => { + utils.skipOnWin(t, () => { + const execBit = parseInt('001', 8); + t.is(common.statFollowLinks('test/resources/cp-mode-bits/executable').mode & execBit, execBit); + shell.cp('test/resources/cp-mode-bits/executable', `${t.context.tmp}/executable`); + t.is( + common.statFollowLinks('test/resources/cp-mode-bits/executable').mode, + common.statFollowLinks(`${t.context.tmp}/executable`).mode + ); + }); +}); test('Make sure hidden files are copied recursively', t => { shell.rm('-rf', t.context.tmp); @@ -434,17 +421,10 @@ test('Make sure hidden files are copied recursively', t => { }); test('no-recursive will copy regular files only', t => { - const result = shell.cp('test/resources/file1.txt', 'test/resources/ls/', t.context.tmp); - t.is(result.code, 1); - t.truthy(shell.error()); - t.falsy(fs.existsSync(`${t.context.tmp}/.hidden_file`)); // doesn't copy dir contents - t.falsy(fs.existsSync(`${t.context.tmp}/ls`)); // doesn't copy dir itself - t.truthy(fs.existsSync(`${t.context.tmp}/file1.txt`)); -}); - -test('no-recursive will copy regular files only', t => { - const result = shell.cp('test/resources/file1.txt', 'test/resources/file2.txt', 'test/resources/cp', - 'test/resources/ls/', t.context.tmp); + const result = shell.cp( + 'test/resources/file1.txt', 'test/resources/file2.txt', 'test/resources/cp', + 'test/resources/ls/', t.context.tmp + ); t.is(result.code, 1); t.truthy(shell.error()); @@ -459,29 +439,75 @@ test('no-recursive will copy regular files only', t => { test('-R implies -P', t => { utils.skipOnWin(t, () => { shell.cp('-R', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.truthy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); }); +test("-Ru respects the -u flag recursively (don't update newer file)", t => { + // Setup code + const dir = `${t.context.tmp}/cp-Ru`; + const sourceDir = `${dir}/original`; + const sourceFile = `${sourceDir}/file`; + const destDir = `${dir}/new`; + const nestedDestDir = `${dir}/new/original`; + const destFile = `${nestedDestDir}/file`; + [sourceDir, destDir, nestedDestDir].forEach(d => shell.mkdir('-p', d)); + shell.ShellString('Source File Contents\n').to(sourceFile); + shell.ShellString('Destination File Contents\n').to(destFile); + const oldModifyTimeMs = 12345000; + const newModifyTimeMs = 67890000; + // End setup + + // Set the source file to be OLDER than the destination file + shell.touch({ '-m': true, '-d': new Date(oldModifyTimeMs) }, sourceFile); + shell.touch({ '-m': true, '-d': new Date(newModifyTimeMs) }, destFile); + shell.cp('-Ru', sourceDir, destDir); + // Check that dest has not been updated + t.is(shell.cat(destFile).stdout, 'Destination File Contents\n'); +}); + +test('-Ru respects the -u flag recursively (update older file)', t => { + // Setup code + const dir = `${t.context.tmp}/cp-Ru`; + const sourceDir = `${dir}/original`; + const sourceFile = `${sourceDir}/file`; + const destDir = `${dir}/new`; + const nestedDestDir = `${dir}/new/original`; + const destFile = `${nestedDestDir}/file`; + [sourceDir, destDir, nestedDestDir].forEach(d => shell.mkdir('-p', d)); + shell.ShellString('Source File Contents\n').to(sourceFile); + shell.ShellString('Destination File Contents\n').to(destFile); + const oldModifyTimeMs = 12345000; + const newModifyTimeMs = 67890000; + // End setup + + // Set the source file to be NEWER than the destination file + shell.touch({ '-m': true, '-d': new Date(newModifyTimeMs) }, sourceFile); + shell.touch({ '-m': true, '-d': new Date(oldModifyTimeMs) }, destFile); + shell.cp('-Ru', sourceDir, destDir); + // Check that dest has been overwritten + t.is(shell.cat(destFile).stdout, 'Source File Contents\n'); +}); + test('using -P explicitly works', t => { utils.skipOnWin(t, () => { shell.cp('-P', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.truthy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); }); test('using -PR on a link to a folder does not follow the link', t => { utils.skipOnWin(t, () => { shell.cp('-PR', 'test/resources/cp/symFolder', t.context.tmp); - t.truthy(fs.lstatSync(`${t.context.tmp}/symFolder`).isSymbolicLink()); + t.truthy(common.statNoFollowLinks(`${t.context.tmp}/symFolder`).isSymbolicLink()); }); }); test('-L overrides -P for copying directory', t => { utils.skipOnWin(t, () => { shell.cp('-LPR', 'test/resources/cp/symFolder', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/symFolder`).isSymbolicLink()); - t.falsy(fs.lstatSync(`${t.context.tmp}/symFolder/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/symFolder`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/symFolder/sym.lnk`).isSymbolicLink()); }); }); @@ -495,7 +521,7 @@ test('Recursive, copies entire directory with no symlinks and -L option does not }); }); -test('-u flag won\'t overwrite newer files', t => { +test("-u flag won't overwrite newer files", t => { shell.touch(`${t.context.tmp}/file1.js`); shell.cp('-u', 'test/resources/file1.js', t.context.tmp); t.falsy(shell.error()); @@ -509,7 +535,7 @@ test('-u flag does overwrite older files', t => { t.is(shell.cat('test/resources/file1.js').toString(), shell.cat(`${t.context.tmp}/file1.js`).toString()); }); -test('-u flag works even if it\'s not overwriting a file', t => { +test("-u flag works even if it's not overwriting a file", t => { t.falsy(fs.existsSync(`${t.context.tmp}/file1.js`)); shell.cp('-u', 'test/resources/file1.js', t.context.tmp); t.falsy(shell.error()); @@ -537,34 +563,34 @@ test('-u flag works correctly recursively', t => { test('using -R on a link to a folder *does* follow the link', t => { shell.cp('-R', 'test/resources/cp/symFolder', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/symFolder`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/symFolder`).isSymbolicLink()); }); test('Without -R, -L is implied', t => { shell.cp('test/resources/cp/links/sym.lnk', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); test('-L explicitly works', t => { shell.cp('-L', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); test('using -LR does not imply -P', t => { shell.cp('-LR', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); test('using -LR also works recursively on directories containing links', t => { shell.cp('-LR', 'test/resources/cp/links', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/links/sym.lnk`).isSymbolicLink()); }); test('-L always overrides a -P', t => { shell.cp('-LP', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); shell.cp('-LPR', 'test/resources/cp/links/sym.lnk', t.context.tmp); - t.falsy(fs.lstatSync(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); + t.falsy(common.statNoFollowLinks(`${t.context.tmp}/sym.lnk`).isSymbolicLink()); }); test('Make sure max depth does not limit shallow directory structures', t => { @@ -709,7 +735,7 @@ test('copy file to same directory', t => { t.is(result.stderr, "cp: 'test/resources/file1' and 'test/resources/file1' are the same file"); }); -test('copy mutliple files to same location', t => { +test('copy multiple files to same location', t => { const result = shell.cp('test/resources/file1', 'test/resources/file2', 'test/resources'); t.truthy(shell.error()); t.is(result.code, 1); @@ -755,3 +781,215 @@ test('should not overwrite recently created files (not give error no-force mode) // Ensure First file is copied t.is(shell.cat(`${t.context.tmp}/file1`).toString(), 'test1'); }); + +// cp -R should be able to copy a readonly src (issue #98). +// On Windows, chmod acts VERY differently so skip these tests for now +test('cp -R should be able to copy a readonly src. issue #98; (Non window platforms only)', t => { + utils.skipOnWin(t, () => { + shell.cp('-r', 'test/resources/cp', t.context.tmp); + shell.chmod('555', `${t.context.tmp}/cp/`); + shell.chmod('555', `${t.context.tmp}/cp/dir_a`); + shell.chmod('555', `${t.context.tmp}/cp/dir_b`); + shell.chmod('555', `${t.context.tmp}/cp/a`); + + const result = shell.cp('-r', `${t.context.tmp}/cp`, `${t.context.tmp}/cp_cp`); + t.falsy(shell.error()); + t.falsy(result.stderr); + t.is(result.code, 0); + + t.is(shell.ls('-R', `${t.context.tmp}/cp`) + '', shell.ls('-R', `${t.context.tmp}/cp_cp`) + ''); + t.is(fs.statSync(`${t.context.tmp}/cp_cp`).mode & parseInt('777', 8), parseInt('555', 8)); + t.is(fs.statSync(`${t.context.tmp}/cp_cp/dir_a`).mode & parseInt('777', 8), parseInt('555', 8)); + t.is(fs.statSync(`${t.context.tmp}/cp_cp/a`).mode & parseInt('777', 8), parseInt('555', 8)); + + shell.chmod('-R', '755', t.context.tmp); + }); +}); + +test('cp -p should preserve mode, ownership, and timestamp (regular file)', t => { + // Setup: copy to srcFile and modify mode and timestamp + const srcFile = `${t.context.tmp}/srcFile`; + shell.cp('test/resources/cp/file1', srcFile); + // Make this a round number of seconds, since the underlying system may not + // have millisecond precision. + const newModifyTimeMs = 12345000; + const newAccessTimeMs = 67890000; + shell.touch({ '-d': new Date(newModifyTimeMs), '-m': true }, srcFile); + shell.touch({ '-d': new Date(newAccessTimeMs), '-a': true }, srcFile); + const mode = '444'; + shell.chmod(mode, srcFile); + + // Now re-copy with '-p' and verify metadata. + const result = shell.cp('-p', srcFile, `${t.context.tmp}/preservedFile1`); + const stat = common.statFollowLinks(srcFile); + const statOfResult = common.statFollowLinks(`${t.context.tmp}/preservedFile1`); + + t.is(result.code, 0); + + // Original file should be unchanged: + t.is(stat.mtime.getTime(), newModifyTimeMs); + // cp appears to update the atime, but only of the srcFile + t.is(stat.mode.toString(8), '100' + mode); + + // New file should keep same attributes + t.is(statOfResult.mtime.getTime(), newModifyTimeMs); + t.is(statOfResult.atime.getTime(), newAccessTimeMs); + t.is(statOfResult.mode.toString(8), '100' + mode); + + t.is(stat.uid, statOfResult.uid); + t.is(stat.gid, statOfResult.gid); +}); + +test('cp -p should preserve mode, ownership, and timestamp (directory)', t => { + // Setup: copy to srcFile and modify mode and timestamp + const srcDir = `${t.context.tmp}/srcDir`; + const srcFile = `${srcDir}/srcFile`; + shell.mkdir(srcDir); + shell.cp('test/resources/cp/file1', srcFile); + // Make this a round number of seconds, since the underlying system may not + // have millisecond precision. + const newModifyTimeMs = 12345000; + const newAccessTimeMs = 67890000; + shell.touch({ '-d': new Date(newModifyTimeMs), '-m': true }, srcFile); + shell.touch({ '-d': new Date(newAccessTimeMs), '-a': true }, srcFile); + fs.utimesSync(srcDir, new Date(newAccessTimeMs), new Date(newModifyTimeMs)); + const mode = '444'; + shell.chmod(mode, srcFile); + + // Now re-copy (the whole dir) with '-p' and verify metadata of file contents. + const result = shell.cp('-pr', srcDir, `${t.context.tmp}/preservedDir`); + const stat = common.statFollowLinks(srcFile); + const statDir = common.statFollowLinks(srcDir); + const statOfResult = common.statFollowLinks(`${t.context.tmp}/preservedDir/srcFile`); + const statOfResultDir = common.statFollowLinks(`${t.context.tmp}/preservedDir`); + + t.is(result.code, 0); + + // Both original file and original dir should be unchanged: + t.is(statDir.mtime.getTime(), newModifyTimeMs); + t.is(stat.mtime.getTime(), newModifyTimeMs); + // cp appears to update the atime, but only of the srcFile & srcDir + t.is(stat.mode.toString(8), '100' + mode); + + // Both new file and new dir should keep same attributes + t.is(statOfResultDir.mtime.getTime(), newModifyTimeMs); + utils.skipOnWin(t, () => { + // The resultDir atime may be updated on Windows as a side-effect, e.g. chmod(). + t.is(statOfResultDir.atime.getTime(), newAccessTimeMs); + }); + t.is(statOfResult.mtime.getTime(), newModifyTimeMs); + t.is(statOfResult.atime.getTime(), newAccessTimeMs); + t.is(statOfResult.mode.toString(8), '100' + mode); + + t.is(stat.uid, statOfResult.uid); + t.is(stat.gid, statOfResult.gid); +}); + +test('cp -p should preserve mode, ownership, and timestamp (symlink)', t => { + // Skip in Windows because symlinks require elevated permissions. + utils.skipOnWin(t, () => { + // Setup: copy to srcFile, create srcLink, and modify mode and timestamp + shell.cp('test/resources/cp/file1', `${t.context.tmp}/srcFile`); + const srcLink = `${t.context.tmp}/srcLink`; + shell.ln('-s', 'srcFile', `${t.context.tmp}/srcLink`); + // Make this a round number of seconds, since the underlying system may not + // have millisecond precision. + const newModifyTimeMs = 12345000; + const newAccessTimeMs = 67890000; + shell.touch({ '-d': new Date(newModifyTimeMs), '-m': true }, srcLink); + shell.touch({ '-d': new Date(newAccessTimeMs), '-a': true }, srcLink); + const mode = '444'; + shell.chmod(mode, srcLink); + + // Now re-copy with '-p' and verify metadata. + const result = shell.cp('-p', srcLink, `${t.context.tmp}/preservedLink`); + const stat = common.statFollowLinks(srcLink); + const statOfResult = common.statFollowLinks(`${t.context.tmp}/preservedLink`); + + t.is(result.code, 0); + + // Original file should be unchanged: + t.is(stat.mtime.getTime(), newModifyTimeMs); + // cp appears to update the atime, but only of the srcFile + t.is(stat.mode.toString(8), '100' + mode); + + // New file should keep same attributes + t.is(statOfResult.mtime.getTime(), newModifyTimeMs); + t.is(statOfResult.atime.getTime(), newAccessTimeMs); + t.is(statOfResult.mode.toString(8), '100' + mode); + + t.is(stat.uid, statOfResult.uid); + t.is(stat.gid, statOfResult.gid); + }); +}); + +test('copy directory to its parent dir (same location) should not erase content', t => { + // Setup: create a directory with a file inside t.context.tmp + shell.mkdir(`${t.context.tmp}/srcdir`); + shell.ShellString('deadbeef').to(`${t.context.tmp}/srcdir/lol`); + + // Verify setup + t.is(shell.cat(`${t.context.tmp}/srcdir/lol`).toString(), 'deadbeef'); + + // This should error, not silently erase the file contents + const result = shell.cp('-R', `${t.context.tmp}/srcdir`, t.context.tmp); + t.truthy(shell.error()); + t.is(result.code, 1); + t.truthy(result.stderr.match(/are the same file/)); + + // Most importantly: file content must be preserved (no data loss) + t.is(shell.cat(`${t.context.tmp}/srcdir/lol`).toString(), 'deadbeef'); +}); + +test('copy directory to same location with trailing slash should not erase content', t => { + // Setup: create a directory with a file inside t.context.tmp + shell.mkdir(`${t.context.tmp}/srcdir`); + shell.ShellString('deadbeef').to(`${t.context.tmp}/srcdir/lol`); + + // Verify setup + t.is(shell.cat(`${t.context.tmp}/srcdir/lol`).toString(), 'deadbeef'); + + // cp -R /path/srcdir /path/ should detect same directory + const result = shell.cp('-R', `${t.context.tmp}/srcdir`, `${t.context.tmp}/`); + t.truthy(shell.error()); + t.is(result.code, 1); + t.truthy(result.stderr.match(/are the same file/)); + + // File content must be preserved + t.is(shell.cat(`${t.context.tmp}/srcdir/lol`).toString(), 'deadbeef'); +}); + +test('copy multiple dirs where one is to same location should error for that one and continue', t => { + // Setup: src1 in tmp root, src2 inside destination + shell.mkdir(`${t.context.tmp}/src1`); + shell.mkdir('-p', `${t.context.tmp}/destination/src2`); + shell.ShellString('content1').to(`${t.context.tmp}/src1/file`); + shell.ShellString('content2').to(`${t.context.tmp}/destination/src2/file`); + + // src1 copy should succeed, src2 copy should fail (but the original src2 is still intact) + const result = shell.cp('-R', `${t.context.tmp}/src1`, `${t.context.tmp}/destination/src2`, `${t.context.tmp}/destination`); + t.truthy(shell.error()); + t.is(result.code, 1); + + // src1 was copied successfully into destination + t.is(shell.cat(`${t.context.tmp}/destination/src1/file`).toString(), 'content1'); + // src2's original content is preserved (not lost due to same-file copy) + t.is(shell.cat(`${t.context.tmp}/destination/src2/file`).toString(), 'content2'); +}); + +test('copy single file to itself should not erase content', t => { + // Setup: create a file in t.context.tmp + shell.ShellString('important data').to(`${t.context.tmp}/myfile.txt`); + + // Verify setup + t.is(shell.cat(`${t.context.tmp}/myfile.txt`).toString(), 'important data'); + + // Copying a file to itself should error but preserve data + const result = shell.cp(`${t.context.tmp}/myfile.txt`, `${t.context.tmp}/myfile.txt`); + t.truthy(shell.error()); + t.is(result.code, 1); + t.truthy(result.stderr.match(/are the same file/)); + + // File content must be preserved (no data loss) + t.is(shell.cat(`${t.context.tmp}/myfile.txt`).toString(), 'important data'); +}); diff --git a/test/dirs.js b/test/dirs.js index 10d6e4d7f..44f82486a 100644 --- a/test/dirs.js +++ b/test/dirs.js @@ -1,8 +1,8 @@ -import path from 'path'; +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); test.beforeEach(() => { shell.config.resetForTesting(); diff --git a/test/docs.js b/test/docs.js new file mode 100644 index 000000000..67d503462 --- /dev/null +++ b/test/docs.js @@ -0,0 +1,11 @@ +const test = require('ava'); + +const gendocs = require('../scripts/generate-docs'); + +test('Documentation generation', t => { + if (gendocs.extractCurrentDocs() === gendocs.generateNewDocs()) { + t.pass(); + } else { + t.fail('README documentation is stale. Please run `npm run gendocs`.'); + } +}); diff --git a/test/echo.js b/test/echo.js index f8afbee26..c6989ac1e 100644 --- a/test/echo.js +++ b/test/echo.js @@ -1,19 +1,21 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; -import mocks from './utils/mocks'; +const shell = require('..'); +const utils = require('./utils/utils'); +const mocks = require('./utils/mocks'); shell.config.silent = true; test.beforeEach(t => { t.context.tmp = utils.getTempDir(); - mocks.init(); + mocks.stdout.init(); + mocks.stderr.init(); }); test.afterEach.always(t => { shell.rm('-rf', t.context.tmp); - mocks.restore(); + mocks.stdout.restore(); + mocks.stderr.restore(); }); // @@ -22,8 +24,8 @@ test.afterEach.always(t => { test('simple test with defaults', t => { const result = shell.echo('hello', 'world'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, 'hello world\n'); @@ -31,10 +33,10 @@ test('simple test with defaults', t => { }); test('allow arguments to begin with a hyphen', t => { - // see issue #20 + // Github issue #20 const result = shell.echo('-asdf', '111'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 1); t.is(stdout, '-asdf 111\n'); @@ -43,8 +45,8 @@ test('allow arguments to begin with a hyphen', t => { test("using null as an explicit argument doesn't crash the function", t => { const result = shell.echo(null); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, 'null\n'); @@ -53,8 +55,8 @@ test("using null as an explicit argument doesn't crash the function", t => { test('-e option', t => { const result = shell.echo('-e', '\tmessage'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, '\tmessage\n'); @@ -62,7 +64,7 @@ test('-e option', t => { }); test('piping to a file', t => { - // see issue #476 + // Github issue #476 shell.mkdir(t.context.tmp); const tmp = `${t.context.tmp}/echo.txt`; const resultA = shell.echo('A').toEnd(tmp); @@ -72,8 +74,8 @@ test('piping to a file', t => { t.falsy(shell.error()); t.is(resultB.code, 0); const result = shell.cat(tmp); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(stdout, 'A\nB\n'); t.is(stderr, ''); @@ -82,8 +84,8 @@ test('piping to a file', t => { test('-n option', t => { const result = shell.echo('-n', 'message'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, 'message'); @@ -92,8 +94,8 @@ test('-n option', t => { test('-ne option', t => { const result = shell.echo('-ne', 'message'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, 'message'); @@ -102,8 +104,8 @@ test('-ne option', t => { test('-en option', t => { const result = shell.echo('-en', 'message'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, 'message'); @@ -112,8 +114,8 @@ test('-en option', t => { test('-en option with escaped characters', t => { const result = shell.echo('-en', '\tmessage\n'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 0); t.is(stdout, '\tmessage\n'); @@ -121,7 +123,7 @@ test('-en option with escaped characters', t => { }); test('piping to a file with -n', t => { - // see issue #476 + // Github issue #476 shell.mkdir(t.context.tmp); const tmp = `${t.context.tmp}/echo.txt`; const resultA = shell.echo('-n', 'A').toEnd(tmp); @@ -131,8 +133,8 @@ test('piping to a file with -n', t => { t.falsy(shell.error()); t.is(resultB.code, 0); const result = shell.cat(tmp); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(stdout, 'AB'); t.is(stderr, ''); @@ -141,8 +143,8 @@ test('piping to a file with -n', t => { test('stderr with unrecognized options is empty', t => { const result = shell.echo('-asdf'); - const stdout = mocks.stdout(); - const stderr = mocks.stderr(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); t.falsy(shell.error()); t.is(result.code, 1); t.falsy(result.stderr); diff --git a/test/env.js b/test/env.js index 50a143545..a9f2c009e 100644 --- a/test/env.js +++ b/test/env.js @@ -1,6 +1,6 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); shell.config.silent = true; diff --git a/test/exec.js b/test/exec.js index e2e8bd1cf..79caca5da 100644 --- a/test/exec.js +++ b/test/exec.js @@ -1,19 +1,27 @@ -import os from 'os'; -import path from 'path'; -import util from 'util'; +const os = require('os'); +const path = require('path'); +const util = require('util'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); +const mocks = require('./utils/mocks'); const CWD = process.cwd(); const ORIG_EXEC_PATH = shell.config.execPath; shell.config.silent = true; +test.beforeEach(() => { + mocks.stdout.init(); + mocks.stderr.init(); +}); + test.afterEach.always(() => { process.chdir(CWD); shell.config.execPath = ORIG_EXEC_PATH; + mocks.stdout.restore(); + mocks.stderr.restore(); }); // @@ -35,10 +43,19 @@ test('config.fatal and unknown command', t => { shell.config.fatal = true; t.throws(() => { shell.exec('asdfasdf'); // could not find command - }, /exec: internal error/); + }, { message: /asdfasdf/ }); // name of command should be in error message shell.config.fatal = oldFatal; }); +test('options.fatal = true and unknown command', t => { + const oldFatal = shell.config.fatal; + shell.config.fatal = false; + t.throws(() => { + shell.exec('asdfasdf', { fatal: true }); // could not find command + }, { message: /asdfasdf/ }); // name of command should be in error message + shell.config.fatal = oldFatal; // TODO(nfischer): this setting won't get reset if the assertion above fails +}); + test('exec exits gracefully if we cannot find the execPath', t => { shell.config.execPath = null; shell.exec('echo foo'); @@ -48,6 +65,11 @@ test('exec exits gracefully if we cannot find the execPath', t => { ); }); +test('exec-child.js should not be imported', t => { + const execChild = require('../src/exec-child'); + t.deepEqual([], Object.keys(execChild)); +}); + // // Valids // @@ -79,6 +101,14 @@ test('check if stdout + stderr go to output', t => { t.is(result.stderr, '1234\n'); }); +test('check if stdout + stderr should not be printed to console if silent', t => { + shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.error(1234); console.log(666); process.exit(12);"`, { silent: true }); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); + t.is(stdout, ''); + t.is(stderr, ''); +}); + test('check exit code', t => { const result = shell.exec(`${JSON.stringify(shell.config.execPath)} -e "process.exit(12);"`); t.truthy(shell.error()); @@ -113,19 +143,23 @@ test('set maxBuffer (very small)', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.stdout, '1234567890' + os.EOL); - shell.exec('echo 1234567890', { maxBuffer: 6 }); + const result2 = shell.exec('echo 1234567890', { maxBuffer: 6 }); t.truthy(shell.error()); + t.is(result2.code, 1); + t.is(result2.stdout, '1234567890' + os.EOL); + const maxBufferErrorPattern = /.*\bmaxBuffer\b.*\bexceeded\b.*/; + t.regex(result2.stderr, maxBufferErrorPattern); }); test('set timeout option', t => { - const result = shell.exec(`${JSON.stringify(shell.config.execPath)} test/resources/exec/slow.js 100`); // default timeout is ok + let result = shell.exec(`${JSON.stringify(shell.config.execPath)} test/resources/exec/slow.js 100`); // default timeout is ok t.falsy(shell.error()); t.is(result.code, 0); - if (process.version >= 'v0.11') { - // this option doesn't work on v0.10 - shell.exec(`${JSON.stringify(shell.config.execPath)} test/resources/exec/slow.js 100`, { timeout: 10 }); // times out - } + t.is(result.stdout, 'fast\nslow\n'); + result = shell.exec(`${JSON.stringify(shell.config.execPath)} test/resources/exec/slow.js 2000`, { timeout: 1000 }); // times out t.truthy(shell.error()); + t.is(result.code, 1); + t.is(result.stdout, 'fast\n'); }); test('check process.env works', t => { @@ -145,12 +179,11 @@ test('set shell option (TODO: add tests for Windows)', t => { t.is(result.code, 0); t.is(result.stdout, '/bin/sh\n'); // sh by default const bashPath = shell.which('bash').trim(); - // this option doesn't work on v0.10 if (bashPath) { - result = shell.exec('echo $0', { shell: '/bin/bash' }); + result = shell.exec('echo $0', { shell: bashPath }); t.falsy(shell.error()); t.is(result.code, 0); - t.is(result.stdout, '/bin/bash\n'); + t.is(result.stdout, `${bashPath}\n`); } }); }); @@ -163,49 +196,76 @@ test('exec returns a ShellString', t => { t.is(result.toString(), result.stdout); }); +test('encoding option works', t => { + const result = shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.log(1234);"`, { encoding: 'buffer' }); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(Buffer.isBuffer(result.stdout)); + t.truthy(Buffer.isBuffer(result.stderr)); + t.is(result.stdout.toString(), '1234\n'); + t.is(result.stderr.toString(), ''); +}); + +test('options.fatal = false and unknown command', t => { + const oldFatal = shell.config.fatal; + shell.config.fatal = true; + const result = shell.exec('asdfasdf', { fatal: false }); // could not find command + shell.config.fatal = oldFatal; + t.truthy(shell.error()); + t.truthy(result.code); +}); + // // async // -test.cb('no callback', t => { +function execAsync(...execArgs) { + return new Promise((resolve) => { + shell.exec(...execArgs, (code, stdout, stderr) => { + resolve({ code, stdout, stderr }); + }); + }); +} + +test('no callback', t => { const c = shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.log(1234)"`, { async: true }); t.falsy(shell.error()); t.truthy('stdout' in c, 'async exec returns child process object'); - t.end(); }); -test.cb('callback as 2nd argument', t => { - shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.log(5678);"`, (code, stdout, stderr) => { - t.is(code, 0); - t.is(stdout, '5678\n'); - t.is(stderr, ''); - t.end(); - }); +test('callback as 2nd argument', async t => { + const result = await execAsync(`${JSON.stringify(shell.config.execPath)} -e "console.log(5678);"`); + t.is(result.code, 0); + t.is(result.stdout, '5678\n'); + t.is(result.stderr, ''); }); -test.cb('callback as end argument', t => { - shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.log(5566);"`, { async: true }, (code, stdout, stderr) => { - t.is(code, 0); - t.is(stdout, '5566\n'); - t.is(stderr, ''); - t.end(); - }); +test('callback as end argument', async t => { + const result = await execAsync(`${JSON.stringify(shell.config.execPath)} -e "console.log(5566);"`, { async: true }); + t.is(result.code, 0); + t.is(result.stdout, '5566\n'); + t.is(result.stderr, ''); }); -test.cb('callback as 3rd argument (silent:true)', t => { - shell.exec(`${JSON.stringify(shell.config.execPath)} -e "console.log(5678);"`, { silent: true }, (code, stdout, stderr) => { - t.is(code, 0); - t.is(stdout, '5678\n'); - t.is(stderr, ''); - t.end(); - }); +test('callback as 3rd argument (silent:true)', async t => { + const result = await execAsync(`${JSON.stringify(shell.config.execPath)} -e "console.log(5678);"`, { silent: true }); + t.is(result.code, 0); + t.is(result.stdout, '5678\n'); + t.is(result.stderr, ''); }); -test.cb('command that fails', t => { - shell.exec('shx cp onlyOneCpArgument.txt', { silent: true }, (code, stdout, stderr) => { - t.is(code, 1); - t.is(stdout, ''); - t.is(stderr, 'cp: missing and/or \n'); - t.end(); - }); +test('command that fails', async t => { + const result = await execAsync('shx cp onlyOneCpArgument.txt', { silent: true }); + t.is(result.code, 1); + t.is(result.stdout, ''); + t.is(result.stderr, 'cp: missing and/or \n'); +}); + +test('encoding option works with async', async t => { + const result = await execAsync(`${JSON.stringify(shell.config.execPath)} -e "console.log(5566);"`, { async: true, encoding: 'buffer' }); + t.is(result.code, 0); + t.truthy(Buffer.isBuffer(result.stdout)); + t.truthy(Buffer.isBuffer(result.stderr)); + t.is(result.stdout.toString(), '5566\n'); + t.is(result.stderr.toString(), ''); }); diff --git a/test/exit.js b/test/exit.js new file mode 100644 index 000000000..b0add2552 --- /dev/null +++ b/test/exit.js @@ -0,0 +1,57 @@ +const test = require('ava'); + +const shell = require('..'); + +const mocks = require('./utils/mocks'); + +// +// Valids +// + +function runExitInSubprocess(code) { + const script = code !== undefined + ? `var shell = require("."); shell.exit(${code});` + : 'var shell = require("."); shell.exit();'; + const result = shell.exec( + `${JSON.stringify(shell.config.execPath)} -e ${JSON.stringify(script)}` + ); + const actualReturnCode = result.code; + return actualReturnCode; +} + +test('exit with success status code', t => { + t.is(runExitInSubprocess(0), 0); +}); + +test('exit without explicit code should be success', t => { + t.is(runExitInSubprocess(), 0); +}); + +test('exit with failure status code', t => { + t.is(runExitInSubprocess(5), 5); + t.is(runExitInSubprocess(2), 2); + t.is(runExitInSubprocess(25), 25); +}); + +test('exit correctly sets the shell.errorCode()', t => { + try { + mocks.exit.init(); + shell.exit(5); // Safe to call shell.exit() because it's mocked. + t.is(shell.errorCode(), 5); + t.is(mocks.exit.getValue(), 5); + t.truthy(shell.error()); + + shell.exit(0); // Safe to call shell.exit() because it's mocked. + t.is(shell.errorCode(), 0); + t.falsy(mocks.exit.getValue()); + t.falsy(shell.error()); + + // Also try it without an explicit argument. + shell.exit(); // Safe to call shell.exit() because it's mocked. + t.is(shell.errorCode(), 0); + t.falsy(mocks.exit.getValue()); + t.falsy(shell.error()); + } finally { + mocks.exit.restore(); + } +}); diff --git a/test/find.js b/test/find.js index 2f9e0aa6c..f698028df 100644 --- a/test/find.js +++ b/test/find.js @@ -1,6 +1,6 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); const CWD = process.cwd(); @@ -28,9 +28,9 @@ test('current path', t => { const result = shell.find('.'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('.hidden') > -1); - t.truthy(result.indexOf('dir1/dir11/a_dir11') > -1); - t.is(result.length, 11); + t.truthy(result.includes('.hidden')); + t.truthy(result.includes('dir1/dir11/a_dir11')); + t.is(result.length, 12); shell.cd('../..'); }); @@ -38,17 +38,17 @@ test('simple path', t => { const result = shell.find('test/resources/find'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/find/.hidden') > -1); - t.truthy(result.indexOf('test/resources/find/dir1/dir11/a_dir11') > -1); - t.is(result.length, 11); + t.truthy(result.includes('test/resources/find/.hidden')); + t.truthy(result.includes('test/resources/find/dir1/dir11/a_dir11')); + t.is(result.length, 12); }); test('multiple paths - comma', t => { const result = shell.find('test/resources/find/dir1', 'test/resources/find/dir2'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/find/dir1/dir11/a_dir11') > -1); - t.truthy(result.indexOf('test/resources/find/dir2/a_dir1') > -1); + t.truthy(result.includes('test/resources/find/dir1/dir11/a_dir11')); + t.truthy(result.includes('test/resources/find/dir2/a_dir1')); t.is(result.length, 6); }); @@ -56,8 +56,8 @@ test('multiple paths - array', t => { const result = shell.find(['test/resources/find/dir1', 'test/resources/find/dir2']); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/find/dir1/dir11/a_dir11') > -1); - t.truthy(result.indexOf('test/resources/find/dir2/a_dir1') > -1); + t.truthy(result.includes('test/resources/find/dir1/dir11/a_dir11')); + t.truthy(result.includes('test/resources/find/dir2/a_dir1')); t.is(result.length, 6); }); @@ -66,3 +66,11 @@ test('nonexistent path', t => { t.is(shell.error(), 'find: no such file or directory: test/resources/find/nonexistent'); t.is(result.code, 1); }); + +test('-L flag, folder is symlinked', t => { + const result = shell.find('-L', 'test/resources/find'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(result.includes('test/resources/find/dir2_link/a_dir1')); + t.is(result.length, 13); +}); diff --git a/test/global.js b/test/global.js index b11251c3f..81227f00a 100644 --- a/test/global.js +++ b/test/global.js @@ -1,10 +1,10 @@ /* globals cat, config, cp, env, error, mkdir, rm */ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import '../global'; -import utils from './utils/utils'; +require('../global'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -47,4 +47,3 @@ test('String.prototype is modified for global require', t => { 'bar'.toEnd(`${t.context.tmp}/testfile.txt`); t.is('foobar', cat(`${t.context.tmp}/testfile.txt`).toString()); }); - diff --git a/test/grep.js b/test/grep.js index 2fc62f50e..4b64d6e76 100644 --- a/test/grep.js +++ b/test/grep.js @@ -1,9 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -24,6 +24,7 @@ test('no args', t => { const result = shell.grep(); t.truthy(shell.error()); t.is(result.code, 2); + t.is(shell.errorCode(), 2); }); test('too few args', t => { @@ -49,6 +50,34 @@ test('if at least one file is missing, this should be an error', t => { t.is(result.code, 2); }); +test("multiple files, one doesn't exist, one doesn't match", t => { + const result = shell.grep(/oogabooga/, 'test/resources/file1.txt', + 'test/resources/filedoesnotexist.txt'); + t.truthy(shell.error()); + t.is(result.code, 2); +}); + +test('-A option, negative value', t => { + const result = shell.grep('-A', -2, 'test*', 'test/resources/grep/file3'); + t.truthy(shell.error()); + t.is(result.code, 2); + t.is(result.stderr, 'grep: -2: invalid context length argument'); +}); + +test('-B option, negative value', t => { + const result = shell.grep('-B', -3, 'test*', 'test/resources/grep/file3'); + t.truthy(shell.error()); + t.is(result.code, 2); + t.is(result.stderr, 'grep: -3: invalid context length argument'); +}); + +test('-C option, negative value', t => { + const result = shell.grep('-C', -1, 'test*', 'test/resources/grep/file3'); + t.truthy(shell.error()); + t.is(result.code, 2); + t.is(result.stderr, 'grep: -1: invalid context length argument'); +}); + // // Valids // @@ -127,6 +156,17 @@ test('one file, * in string-regex, make sure * is not globbed', t => { t.is(result.toString(), 'this line ends in.js\nlllllllllllllllll.js\n'); }); +test("one file, pattern doesn't match", t => { + const result = shell.grep('notfoundstring', 'test/resources/grep/file'); + t.truthy(shell.error()); + t.is(result.toString(), ''); + t.is(result.stdout, ''); + // TODO(#900): "grep: " isn't really the correct stderr output, but we need a + // non-empty string so `shell.error()` is truthy. + t.is(result.stderr, 'grep: '); + t.is(result.code, 1); +}); + test('-l option', t => { const result = shell.grep('-l', 'test1', 'test/resources/file1', 'test/resources/file2', 'test/resources/file1.txt'); @@ -136,3 +176,241 @@ test('-l option', t => { t.falsy(result.match(/file2.txt/)); t.is(result.split('\n').length - 1, 2); }); + +test('-i option', t => { + const result = shell.grep('-i', 'test', 'test/resources/grep/case1', 'test/resources/grep/case1.txt', + 'test/resources/grep/case1.js'); + t.falsy(shell.error()); + t.is(result.split('\n').length - 1, 3); +}); + +test('-n option', t => { + const result = shell.grep('-n', /alpha*beta/, 'test/resources/grep/file'); + t.falsy(shell.error()); + t.is(result.toString(), '1:alphaaaaaaabeta\n3:alphbeta\n'); +}); + +test('the pattern looks like an option', t => { + const result = shell.grep('--', '-v', 'test/resources/grep/file2'); + t.falsy(shell.error()); + t.is(result.toString(), '-v\n-vv\n'); +}); + +// +// Before & after contexts +// +test('-B option', t => { + const result = shell.grep('-B', 3, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line1\n' + + 'line2 test line\n' + + 'line3 test line\n' + + '--\n' + + 'line7\n' + + 'line8\n' + + 'line9\n' + + 'line10 test line\n' + + '--\n' + + 'line12\n' + + 'line13\n' + + 'line14\n' + + 'line15 test line\n', + ); +}); + +test('-B option, -n option', t => { + const result = shell.grep('-nB', 3, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + '1-line1\n' + + '2:line2 test line\n' + + '3:line3 test line\n' + + '--\n' + + '7-line7\n' + + '8-line8\n' + + '9-line9\n' + + '10:line10 test line\n' + + '--\n' + + '12-line12\n' + + '13-line13\n' + + '14-line14\n' + + '15:line15 test line\n', + ); +}); + +test('-A option', t => { + const result = shell.grep('-A', 2, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line2 test line\n' + + 'line3 test line\n' + + 'line4\n' + + 'line5\n' + + '--\n' + + 'line10 test line\n' + + 'line11\n' + + 'line12\n' + + '--\n' + + 'line15 test line\n', + ); +}); + +test('-A option, -B option', t => { + const result = shell.grep( + { '-A': 2, '-B': 3 }, + 'test*', + 'test/resources/grep/file3', + ); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line1\n' + + 'line2 test line\n' + + 'line3 test line\n' + + 'line4\n' + + 'line5\n' + + '--\n' + + 'line7\n' + + 'line8\n' + + 'line9\n' + + 'line10 test line\n' + + 'line11\n' + + 'line12\n' + + 'line13\n' + + 'line14\n' + + 'line15 test line\n', + ); +}); + +test('-A option, -B option, -n option', t => { + const result = shell.grep( + { '-n': true, '-A': 2, '-B': 3 }, + 'test*', + 'test/resources/grep/file3', + ); + t.falsy(shell.error()); + t.is( + result.toString(), + '1-line1\n' + + '2:line2 test line\n' + + '3:line3 test line\n' + + '4-line4\n' + + '5-line5\n' + + '--\n' + + '7-line7\n' + + '8-line8\n' + + '9-line9\n' + + '10:line10 test line\n' + + '11-line11\n' + + '12-line12\n' + + '13-line13\n' + + '14-line14\n' + + '15:line15 test line\n', + ); +}); + +test('-C option', t => { + const result = shell.grep('-C', 3, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line1\n' + + 'line2 test line\n' + + 'line3 test line\n' + + 'line4\n' + + 'line5\n' + + 'line6\n' + + 'line7\n' + + 'line8\n' + + 'line9\n' + + 'line10 test line\n' + + 'line11\n' + + 'line12\n' + + 'line13\n' + + 'line14\n' + + 'line15 test line\n', + ); +}); + +test('-C option, small value', t => { + const result = shell.grep('-C', 1, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line1\n' + + 'line2 test line\n' + + 'line3 test line\n' + + 'line4\n' + + '--\n' + + 'line9\n' + + 'line10 test line\n' + + 'line11\n' + + '--\n' + + 'line14\n' + + 'line15 test line\n', + ); +}); + +test('-C option, large value', t => { + const result = shell.grep('-C', 100, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line1\n' + + 'line2 test line\n' + + 'line3 test line\n' + + 'line4\n' + + 'line5\n' + + 'line6\n' + + 'line7\n' + + 'line8\n' + + 'line9\n' + + 'line10 test line\n' + + 'line11\n' + + 'line12\n' + + 'line13\n' + + 'line14\n' + + 'line15 test line\n', + ); +}); + +test('-C option, add line separators', t => { + const result = shell.grep('-C', 0, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + 'line2 test line\n' + + 'line3 test line\n' + + '--\n' + + 'line10 test line\n' + + '--\n' + + 'line15 test line\n', + ); +}); + +test('-C option, -n option', t => { + const result = shell.grep('-nC', 3, 'test*', 'test/resources/grep/file3'); + t.falsy(shell.error()); + t.is( + result.toString(), + '1-line1\n' + + '2:line2 test line\n' + + '3:line3 test line\n' + + '4-line4\n' + + '5-line5\n' + + '6-line6\n' + + '7-line7\n' + + '8-line8\n' + + '9-line9\n' + + '10:line10 test line\n' + + '11-line11\n' + + '12-line12\n' + + '13-line13\n' + + '14-line14\n' + + '15:line15 test line\n', + ); +}); diff --git a/test/head.js b/test/head.js index 9d85aca6a..8f5f91574 100644 --- a/test/head.js +++ b/test/head.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const common = require('../src/common'); shell.config.silent = true; @@ -25,7 +26,7 @@ test('file does not exist', t => { }); test('directory', t => { - t.truthy(fs.statSync('test/resources/').isDirectory()); // sanity check + t.truthy(common.statFollowLinks('test/resources/').isDirectory()); // sanity check const result = shell.head('test/resources/'); t.truthy(shell.error()); t.is(result.code, 1); @@ -99,7 +100,7 @@ test('Globbed file', t => { .join('\n') + '\n'); }); -test('With `\'-n\' ` option', t => { +test("With '-n ' option", t => { const result = shell.head('-n', 4, 'test/resources/head/file2.txt', 'test/resources/head/file1.txt'); t.falsy(shell.error()); @@ -110,7 +111,7 @@ test('With `\'-n\' ` option', t => { .join('\n') + '\n'); }); -test('With `{\'-n\': }` option', t => { +test("With '{-n: }' option", t => { const result = shell.head({ '-n': 4 }, 'test/resources/head/file2.txt', 'test/resources/head/file1.txt'); t.falsy(shell.error()); diff --git a/test/ln.js b/test/ln.js index b16820584..1ef01eef4 100644 --- a/test/ln.js +++ b/test/ln.js @@ -1,10 +1,10 @@ -import fs from 'fs'; -import path from 'path'; +const fs = require('fs'); +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); const CWD = process.cwd(); diff --git a/test/ls.js b/test/ls.js index 2131aff3b..1c8ef6276 100644 --- a/test/ls.js +++ b/test/ls.js @@ -1,9 +1,10 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); const CWD = process.cwd(); @@ -35,10 +36,11 @@ test('no such file or dir', t => { // Valids // -test('it\'s ok to use no arguments', t => { +test("it's ok to use no arguments", t => { const result = shell.ls(); t.falsy(shell.error()); t.is(result.code, 0); + t.is(shell.errorCode(), 0); }); test('root directory', t => { @@ -52,12 +54,12 @@ test('no args provides the correct result', t => { const result = shell.ls(); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('file1') > -1); - t.truthy(result.indexOf('file2') > -1); - t.truthy(result.indexOf('file1.js') > -1); - t.truthy(result.indexOf('file2.js') > -1); - t.truthy(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1); - t.truthy(result.indexOf('a_dir') > -1); + t.truthy(result.includes('file1')); + t.truthy(result.includes('file2')); + t.truthy(result.includes('file1.js')); + t.truthy(result.includes('file2.js')); + t.truthy(result.includes('filename(with)[chars$]^that.must+be-escaped')); + t.truthy(result.includes('a_dir')); t.is(result.length, 6); }); @@ -65,12 +67,12 @@ test('simple arg', t => { const result = shell.ls('test/resources/ls'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('file1') > -1); - t.truthy(result.indexOf('file2') > -1); - t.truthy(result.indexOf('file1.js') > -1); - t.truthy(result.indexOf('file2.js') > -1); - t.truthy(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1); - t.truthy(result.indexOf('a_dir') > -1); + t.truthy(result.includes('file1')); + t.truthy(result.includes('file2')); + t.truthy(result.includes('file1.js')); + t.truthy(result.includes('file2.js')); + t.truthy(result.includes('filename(with)[chars$]^that.must+be-escaped')); + t.truthy(result.includes('a_dir')); t.is(result.length, 6); }); @@ -78,28 +80,36 @@ test('simple arg, with a trailing slash', t => { const result = shell.ls('test/resources/ls/'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('file1') > -1); - t.truthy(result.indexOf('file2') > -1); - t.truthy(result.indexOf('file1.js') > -1); - t.truthy(result.indexOf('file2.js') > -1); - t.truthy(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1); - t.truthy(result.indexOf('a_dir') > -1); + t.truthy(result.includes('file1')); + t.truthy(result.includes('file2')); + t.truthy(result.includes('file1.js')); + t.truthy(result.includes('file2.js')); + t.truthy(result.includes('filename(with)[chars$]^that.must+be-escaped')); + t.truthy(result.includes('a_dir')); t.is(result.length, 6); }); +test('simple arg, a file', t => { + const result = shell.ls('test/resources/ls/file1'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(result.includes('test/resources/ls/file1')); + t.is(result.length, 1); +}); + test('no args, -A option', t => { shell.cd('test/resources/ls'); const result = shell.ls('-A'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('file1') > -1); - t.truthy(result.indexOf('file2') > -1); - t.truthy(result.indexOf('file1.js') > -1); - t.truthy(result.indexOf('file2.js') > -1); - t.truthy(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('.hidden_file') > -1); - t.truthy(result.indexOf('.hidden_dir') > -1); + t.truthy(result.includes('file1')); + t.truthy(result.includes('file2')); + t.truthy(result.includes('file1.js')); + t.truthy(result.includes('file2.js')); + t.truthy(result.includes('filename(with)[chars$]^that.must+be-escaped')); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('.hidden_file')); + t.truthy(result.includes('.hidden_dir')); t.is(result.length, 8); }); @@ -108,14 +118,14 @@ test('no args, deprecated -a option', t => { const result = shell.ls('-a'); // (deprecated) backwards compatibility test t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('file1') > -1); - t.truthy(result.indexOf('file2') > -1); - t.truthy(result.indexOf('file1.js') > -1); - t.truthy(result.indexOf('file2.js') > -1); - t.truthy(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('.hidden_file') > -1); - t.truthy(result.indexOf('.hidden_dir') > -1); + t.truthy(result.includes('file1')); + t.truthy(result.includes('file2')); + t.truthy(result.includes('file1.js')); + t.truthy(result.includes('file2.js')); + t.truthy(result.includes('filename(with)[chars$]^that.must+be-escaped')); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('.hidden_file')); + t.truthy(result.includes('.hidden_dir')); t.is(result.length, 8); }); @@ -123,25 +133,28 @@ test('wildcard, very simple', t => { const result = shell.ls('test/resources/cat/*'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/cat/file1') > -1); - t.truthy(result.indexOf('test/resources/cat/file2') > -1); - t.is(result.length, 2); + t.truthy(result.includes('test/resources/cat/file1')); + t.truthy(result.includes('test/resources/cat/file2')); + t.truthy(result.includes('test/resources/cat/file3')); + t.truthy(result.includes('test/resources/cat/file4')); + t.truthy(result.includes('test/resources/cat/file5')); + t.is(result.length, 5); }); test('wildcard, simple', t => { const result = shell.ls('test/resources/ls/*'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/ls/file1') > -1); - t.truthy(result.indexOf('test/resources/ls/file2') > -1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); + t.truthy(result.includes('test/resources/ls/file1')); + t.truthy(result.includes('test/resources/ls/file2')); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); t.truthy( - result.indexOf('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1 + result.includes('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') ); - t.is(result.indexOf('test/resources/ls/a_dir'), -1); // this shouldn't be there - t.truthy(result.indexOf('nada') > -1); - t.truthy(result.indexOf('b_dir') > -1); + t.falsy(result.includes('test/resources/ls/a_dir')); // this shouldn't be there + t.truthy(result.includes('nada')); + t.truthy(result.includes('b_dir')); t.is(result.length, 7); }); @@ -149,14 +162,14 @@ test('wildcard, simple, with -d', t => { const result = shell.ls('-d', 'test/resources/ls/*'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/ls/file1') > -1); - t.truthy(result.indexOf('test/resources/ls/file2') > -1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); + t.truthy(result.includes('test/resources/ls/file1')); + t.truthy(result.includes('test/resources/ls/file2')); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); t.truthy( - result.indexOf('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1 + result.includes('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') ); - t.truthy(result.indexOf('test/resources/ls/a_dir') > -1); + t.truthy(result.includes('test/resources/ls/a_dir')); t.is(result.length, 6); }); @@ -164,8 +177,8 @@ test('wildcard, hidden only', t => { const result = shell.ls('-d', 'test/resources/ls/.*'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/ls/.hidden_file') > -1); - t.truthy(result.indexOf('test/resources/ls/.hidden_dir') > -1); + t.truthy(result.includes('test/resources/ls/.hidden_file')); + t.truthy(result.includes('test/resources/ls/.hidden_dir')); t.is(result.length, 2); }); @@ -174,12 +187,12 @@ test('wildcard, mid-file', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 5); - t.truthy(result.indexOf('test/resources/ls/file1') > -1); - t.truthy(result.indexOf('test/resources/ls/file2') > -1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); + t.truthy(result.includes('test/resources/ls/file1')); + t.truthy(result.includes('test/resources/ls/file2')); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); t.truthy( - result.indexOf('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1 + result.includes('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') ); }); @@ -188,24 +201,24 @@ test('wildcard, mid-file with dot (should escape dot for regex)', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 2); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); }); -test('one file that exists, one that doesn\'t', t => { +test("one file that exists, one that doesn't", t => { const result = shell.ls('test/resources/ls/file1.js', 'test/resources/ls/thisdoesntexist'); t.truthy(shell.error()); t.is(result.code, 2); t.is(result.length, 1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); }); -test('one file that exists, one that doesn\'t (other order)', t => { +test("one file that exists, one that doesn't (other order)", t => { const result = shell.ls('test/resources/ls/thisdoesntexist', 'test/resources/ls/file1.js'); t.truthy(shell.error()); t.is(result.code, 2); t.is(result.length, 1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); }); test('wildcard, should not do partial matches', t => { @@ -220,10 +233,10 @@ test('wildcard, all files with extension', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 3); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); t.truthy( - result.indexOf('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1 + result.includes('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') ); }); @@ -232,10 +245,10 @@ test('wildcard, with additional path', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 4); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); - t.truthy(result.indexOf('b_dir') > -1); // no wildcard == no path prefix - t.truthy(result.indexOf('nada') > -1); // no wildcard == no path prefix + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); + t.truthy(result.includes('b_dir')); // no wildcard == no path prefix + t.truthy(result.includes('nada')); // no wildcard == no path prefix }); test('wildcard for both paths', t => { @@ -243,10 +256,10 @@ test('wildcard for both paths', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 4); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); - t.truthy(result.indexOf('z') > -1); - t.truthy(result.indexOf('test/resources/ls/a_dir/nada') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); + t.truthy(result.includes('z')); + t.truthy(result.includes('test/resources/ls/a_dir/nada')); }); test('wildcard for both paths, array', t => { @@ -254,10 +267,10 @@ test('wildcard for both paths, array', t => { t.falsy(shell.error()); t.is(result.code, 0); t.is(result.length, 4); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); - t.truthy(result.indexOf('z') > -1); - t.truthy(result.indexOf('test/resources/ls/a_dir/nada') > -1); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2.js')); + t.truthy(result.includes('z')); + t.truthy(result.includes('test/resources/ls/a_dir/nada')); }); test('recursive, no path', t => { @@ -265,9 +278,9 @@ test('recursive, no path', t => { const result = shell.ls('-R'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir/z') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/b_dir')); + t.truthy(result.includes('a_dir/b_dir/z')); t.is(result.length, 9); }); @@ -275,9 +288,9 @@ test('recursive, path given', t => { const result = shell.ls('-R', 'test/resources/ls'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir/z') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/b_dir')); + t.truthy(result.includes('a_dir/b_dir/z')); t.is(result.length, 9); }); @@ -285,10 +298,10 @@ test('-RA flag, path given', t => { const result = shell.ls('-RA', 'test/resources/ls'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir/z') > -1); - t.truthy(result.indexOf('a_dir/.hidden_dir/nada') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/b_dir')); + t.truthy(result.includes('a_dir/b_dir/z')); + t.truthy(result.includes('a_dir/.hidden_dir/nada')); t.is(result.length, 14); }); @@ -296,11 +309,11 @@ test('-RA flag, symlinks are not followed', t => { const result = shell.ls('-RA', 'test/resources/rm'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/a_file') > -1); - t.truthy(result.indexOf('link_to_a_dir') > -1); - t.is(result.indexOf('link_to_a_dir/a_file'), -1); - t.truthy(result.indexOf('fake.lnk') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/a_file')); + t.truthy(result.includes('link_to_a_dir')); + t.falsy(result.includes('link_to_a_dir/a_file')); + t.truthy(result.includes('fake.lnk')); t.is(result.length, 4); }); @@ -309,11 +322,11 @@ test('-RAL flag, follows symlinks', t => { const result = shell.ls('-RAL', 'test/resources/rm'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/a_file') > -1); - t.truthy(result.indexOf('link_to_a_dir') > -1); - t.truthy(result.indexOf('link_to_a_dir/a_file') > -1); - t.truthy(result.indexOf('fake.lnk') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/a_file')); + t.truthy(result.includes('link_to_a_dir')); + t.truthy(result.includes('link_to_a_dir/a_file')); + t.truthy(result.includes('fake.lnk')); t.is(result.length, 5); }); }); @@ -323,7 +336,17 @@ test('-L flag, path is symlink', t => { const result = shell.ls('-L', 'test/resources/rm/link_to_a_dir'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_file') > -1); + t.truthy(result.includes('a_file')); + t.is(result.length, 1); + }); +}); + +test('follow links to directories by default', t => { + utils.skipOnWin(t, () => { + const result = shell.ls('test/resources/rm/link_to_a_dir'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(result.includes('a_file')); t.is(result.length, 1); }); }); @@ -341,7 +364,7 @@ test('directory option, single arg', t => { t.is(result.length, 1); }); -test('directory option, single arg with trailing \'/\'', t => { +test("directory option, single arg with trailing '/'", t => { const result = shell.ls('-d', 'test/resources/ls/'); t.falsy(shell.error()); t.is(result.code, 0); @@ -352,8 +375,8 @@ test('directory option, multiple args', t => { const result = shell.ls('-d', 'test/resources/ls/a_dir', 'test/resources/ls/file1'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/ls/a_dir') > -1); - t.truthy(result.indexOf('test/resources/ls/file1') > -1); + t.truthy(result.includes('test/resources/ls/a_dir')); + t.truthy(result.includes('test/resources/ls/file1')); t.is(result.length, 2); }); @@ -361,14 +384,14 @@ test('directory option, globbed arg', t => { const result = shell.ls('-d', 'test/resources/ls/*'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/ls/a_dir') > -1); - t.truthy(result.indexOf('test/resources/ls/file1') > -1); - t.truthy(result.indexOf('test/resources/ls/file1.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2') > -1); - t.truthy(result.indexOf('test/resources/ls/file2.js') > -1); - t.truthy(result.indexOf('test/resources/ls/file2') > -1); + t.truthy(result.includes('test/resources/ls/a_dir')); + t.truthy(result.includes('test/resources/ls/file1')); + t.truthy(result.includes('test/resources/ls/file1.js')); + t.truthy(result.includes('test/resources/ls/file2')); + t.truthy(result.includes('test/resources/ls/file2.js')); + t.truthy(result.includes('test/resources/ls/file2')); t.truthy( - result.indexOf('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1 + result.includes('test/resources/ls/filename(with)[chars$]^that.must+be-escaped') ); t.is(result.length, 6); }); @@ -442,7 +465,7 @@ test('long option, directory, recursive (and windows converts slashes)', t => { t.truthy(idx >= 0); result = result[idx]; t.is(result.name, result.name); - t.truthy(fs.statSync('test/resources/ls/a_dir/b_dir').isDirectory()); + t.truthy(common.statFollowLinks('test/resources/ls/a_dir/b_dir').isDirectory()); t.is(typeof result.nlink, 'number'); // This can vary between the local machine and travis t.is(typeof result.size, 'number'); // This can vary between different file systems t.truthy(result.mode); // check that these keys exist @@ -460,7 +483,7 @@ test('still lists broken links', t => { const result = shell.ls('test/resources/badlink'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('test/resources/badlink') > -1); + t.truthy(result.includes('test/resources/badlink')); t.is(result.length, 1); }); @@ -468,14 +491,14 @@ test('Test new ShellString-like attributes', t => { const result = shell.ls('test/resources/ls'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.stdout.indexOf('file1') > -1); - t.truthy(result.stdout.indexOf('file2') > -1); - t.truthy(result.stdout.indexOf('file1.js') > -1); - t.truthy(result.stdout.indexOf('file2.js') > -1); + t.truthy(result.stdout.includes('file1')); + t.truthy(result.stdout.includes('file2')); + t.truthy(result.stdout.includes('file1.js')); + t.truthy(result.stdout.includes('file2.js')); t.truthy( - result.stdout.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1 + result.stdout.includes('filename(with)[chars$]^that.must+be-escaped') ); - t.truthy(result.stdout.indexOf('a_dir') > -1); + t.truthy(result.stdout.includes('a_dir')); t.is(typeof result.stdout, 'string'); t.truthy(result.to); t.truthy(result.toEnd); @@ -505,8 +528,8 @@ test('non-normalized paths are still ok with -R', t => { const result = shell.ls('-R', 'test/resources/./ls/../ls'); t.falsy(shell.error()); t.is(result.code, 0); - t.truthy(result.indexOf('a_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir') > -1); - t.truthy(result.indexOf('a_dir/b_dir/z') > -1); + t.truthy(result.includes('a_dir')); + t.truthy(result.includes('a_dir/b_dir')); + t.truthy(result.includes('a_dir/b_dir/z')); t.is(result.length, 9); }); diff --git a/test/mkdir.js b/test/mkdir.js index 3827c2d38..f37b9f047 100644 --- a/test/mkdir.js +++ b/test/mkdir.js @@ -1,9 +1,10 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -28,21 +29,21 @@ test('no args', t => { }); test('dir already exists', t => { - const mtime = fs.statSync(t.context.tmp).mtime.toString(); + const mtime = common.statFollowLinks(t.context.tmp).mtime.toString(); const result = shell.mkdir(t.context.tmp); // dir already exists t.truthy(shell.error()); t.is(result.code, 1); t.is(result.stderr, `mkdir: path already exists: ${t.context.tmp}`); - t.is(fs.statSync(t.context.tmp).mtime.toString(), mtime); // didn't mess with dir + t.is(common.statFollowLinks(t.context.tmp).mtime.toString(), mtime); // didn't mess with dir }); -test('Can\'t overwrite a broken link', t => { - const mtime = fs.lstatSync('test/resources/badlink').mtime.toString(); +test("Can't overwrite a broken link", t => { + const mtime = common.statNoFollowLinks('test/resources/badlink').mtime.toString(); const result = shell.mkdir('test/resources/badlink'); t.truthy(shell.error()); t.is(result.code, 1); t.is(result.stderr, 'mkdir: path already exists: test/resources/badlink'); - t.is(fs.lstatSync('test/resources/badlink').mtime.toString(), mtime); // didn't mess with file + t.is(common.statNoFollowLinks('test/resources/badlink').mtime.toString(), mtime); // didn't mess with file }); test('root path does not exist', t => { @@ -56,37 +57,37 @@ test('root path does not exist', t => { }); test('try to overwrite file', t => { - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); const result = shell.mkdir('test/resources/file1'); t.truthy(shell.error()); t.is(result.code, 1); t.is(result.stderr, 'mkdir: path already exists: test/resources/file1'); - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); }); test('try to overwrite file, with -p', t => { - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); const result = shell.mkdir('-p', 'test/resources/file1'); t.truthy(shell.error()); t.is(result.code, 1); t.is(result.stderr, 'mkdir: cannot create directory test/resources/file1: File exists'); - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); }); test('try to make a subdirectory of a file', t => { - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); const result = shell.mkdir('test/resources/file1/subdir'); t.truthy(shell.error()); t.is(result.code, 1); t.is(result.stderr, 'mkdir: cannot create directory test/resources/file1/subdir: Not a directory'); - t.truthy(fs.statSync('test/resources/file1').isFile()); + t.truthy(common.statFollowLinks('test/resources/file1').isFile()); t.falsy(fs.existsSync('test/resources/file1/subdir')); }); test('Check for invalid permissions', t => { utils.skipOnWin(t, () => { // This test case only works on unix, but should work on Windows as well - const dirName = 'nowritedir'; + const dirName = `${t.context.tmp}/nowritedir`; shell.mkdir(dirName); t.falsy(shell.error()); shell.chmod('-w', dirName); @@ -94,11 +95,10 @@ test('Check for invalid permissions', t => { t.is(result.code, 1); t.is( result.stderr, - 'mkdir: cannot create directory nowritedir/foo: Permission denied' + `mkdir: cannot create directory ${t.context.tmp}/nowritedir/foo: Permission denied` ); t.truthy(shell.error()); t.falsy(fs.existsSync(dirName + '/foo')); - shell.rm('-rf', dirName); // clean up }); }); @@ -141,10 +141,9 @@ test('-p flag', t => { t.falsy(shell.error()); t.is(result.code, 0); t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`)); - shell.rm('-Rf', `${t.context.tmp}/a`); // revert }); -test('multiple dirs', t => { +test('-p flag: multiple dirs', t => { const result = shell.mkdir('-p', `${t.context.tmp}/zzza`, `${t.context.tmp}/zzzb`, `${t.context.tmp}/zzzc`); t.falsy(shell.error()); @@ -154,7 +153,7 @@ test('multiple dirs', t => { t.truthy(fs.existsSync(`${t.context.tmp}/zzzc`)); }); -test('multiple dirs, array syntax', t => { +test('-p flag: multiple dirs, array syntax', t => { const result = shell.mkdir('-p', [`${t.context.tmp}/yyya`, `${t.context.tmp}/yyyb`, `${t.context.tmp}/yyyc`]); t.falsy(shell.error()); @@ -164,6 +163,26 @@ test('multiple dirs, array syntax', t => { t.truthy(fs.existsSync(`${t.context.tmp}/yyyc`)); }); +test('-p flag: subdirectory already exists', t => { + t.falsy(fs.existsSync(`${t.context.tmp}/d1`)); + shell.mkdir('-p', `${t.context.tmp}/d1/d2/d3`); + t.truthy(fs.existsSync(`${t.context.tmp}/d1/d2/d3`)); + const result = shell.mkdir('-p', `${t.context.tmp}/d1/d2`); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(fs.existsSync(`${t.context.tmp}/d1/d2/d3`)); +}); + +test('-p flag: create directory in subdirectory', t => { + t.falsy(fs.existsSync(`${t.context.tmp}/d1`)); + shell.mkdir('-p', `${t.context.tmp}/d1/d2`); + t.truthy(fs.existsSync(`${t.context.tmp}/d1/d2`)); + const result = shell.mkdir('-p', `${t.context.tmp}/d1/d2/d3`); + t.falsy(shell.error()); + t.is(result.code, 0); + t.truthy(fs.existsSync(`${t.context.tmp}/d1/d2/d3`)); +}); + test('globbed dir', t => { let result = shell.mkdir('-p', `${t.context.tmp}/mydir`); t.falsy(shell.error()); diff --git a/test/mv.js b/test/mv.js index eb68bd708..f6c34e6aa 100644 --- a/test/mv.js +++ b/test/mv.js @@ -1,9 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); const CWD = process.cwd(); const numLines = utils.numLines; @@ -121,7 +121,7 @@ test('too many sources (exist, but dest is file)', t => { t.is(result.stderr, 'mv: dest is not a directory (too many sources)'); }); -test('can\'t use wildcard when dest is file', t => { +test("can't use wildcard when dest is file", t => { const result = shell.mv('file*', 'file1'); t.truthy(shell.error()); t.truthy(fs.existsSync('file1')); diff --git a/test/pipe.js b/test/pipe.js index ab75d944a..a8aabb928 100644 --- a/test/pipe.js +++ b/test/pipe.js @@ -1,6 +1,6 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); shell.config.silent = true; @@ -65,10 +65,16 @@ test('Synchronous exec', t => { t.is(result.toString(), 'alphaaaaaaabeta\nalphbeta\n'); }); -test.cb('Asynchronous exec', t => { - shell.cat('test/resources/grep/file').exec('shx grep "alpha*beta"', (code, stdout) => { - t.is(code, 0); - t.is(stdout, 'alphaaaaaaabeta\nalphbeta\n'); - t.end(); +test('Asynchronous exec', async t => { + const promise = new Promise(resolve => { + shell.cat('test/resources/grep/file') + .exec('shx grep "alpha*beta"', (code, stdout, stderr) => { + resolve({ code, stdout, stderr }); + }); }); + + const result = await promise; + t.is(result.code, 0); + t.is(result.stdout, 'alphaaaaaaabeta\nalphbeta\n'); + t.is(result.stderr, ''); }); diff --git a/test/plugin.js b/test/plugin.js index e8a2bbc4f..59626c1c0 100644 --- a/test/plugin.js +++ b/test/plugin.js @@ -1,8 +1,8 @@ -import test from 'ava'; +const test = require('ava'); // This is the supported order for importing these files -import plugin from '../plugin'; -import shell from '..'; +const plugin = require('../plugin'); +const shell = require('..'); let data = 0; let fname; @@ -39,6 +39,25 @@ test.beforeEach(() => { shell.config.resetForTesting(); }); +// +// Invalids +// + +test('Unable to register a plugin with unknown options', t => { + t.throws(() => { + plugin.register('foo', fooImplementation, { + foobar: true, + }); + }, { instanceOf: Error }); +}); + +test('Unable to register a plugin with wrong option types', t => { + t.throws(() => { + plugin.register('foo', fooImplementation, { + wrapOutput: 'true', // should be a boolean + }); + }, { instanceOf: TypeError }); +}); // // Valids @@ -51,7 +70,7 @@ test('All plugin utils exist', t => { t.is(typeof plugin.register, 'function'); }); -test('The plugin does not exist before it\'s registered', t => { +test("The plugin does not exist before it's registered", t => { t.falsy(shell.foo); }); @@ -139,10 +158,10 @@ test('Plugins can continue from errors', t => { t.is(shell.error(), 'foo: Error, but continuing'); }); -test('Cannot overwrite an existing command by default', t => { +test('Cannot overwrite an existing command', t => { const oldCat = shell.cat; t.throws(() => { plugin.register('cat', fooImplementation); - }, 'unable to overwrite `cat` command'); + }, { message: 'Command `cat` already exists' }); t.is(shell.cat, oldCat); }); diff --git a/test/popd.js b/test/popd.js index 0e83a7cfa..e02880b75 100644 --- a/test/popd.js +++ b/test/popd.js @@ -1,8 +1,9 @@ -import path from 'path'; +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const mocks = require('./utils/mocks'); const rootDir = path.resolve(); @@ -114,3 +115,45 @@ test('Test that rootDir is not stored', t => { shell.popd(); // no more in the stack t.truthy(shell.error()); }); + +test('quiet mode off', t => { + try { + shell.pushd('test/resources/pushd'); + shell.config.silent = false; + mocks.stdout.init(); + mocks.stderr.init(); + const trail = shell.popd(); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); + t.falsy(shell.error()); + t.is(stdout, ''); + t.is(stderr, `${rootDir}\n`); + t.is(process.cwd(), trail[0]); + t.deepEqual(trail, [rootDir]); + } finally { + shell.config.silent = true; + mocks.stdout.restore(); + mocks.stderr.restore(); + } +}); + +test('quiet mode on', t => { + try { + shell.pushd('test/resources/pushd'); + shell.config.silent = false; + mocks.stdout.init(); + mocks.stderr.init(); + const trail = shell.popd('-q'); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); + t.falsy(shell.error()); + t.is(stdout, ''); + t.is(stderr, ''); + t.is(process.cwd(), trail[0]); + t.deepEqual(trail, [rootDir]); + } finally { + shell.config.silent = true; + mocks.stdout.restore(); + mocks.stderr.restore(); + } +}); diff --git a/test/pushd.js b/test/pushd.js index 9ca0f76b0..987dc093d 100644 --- a/test/pushd.js +++ b/test/pushd.js @@ -1,8 +1,9 @@ -import path from 'path'; +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const mocks = require('./utils/mocks'); const rootDir = path.resolve(); @@ -328,3 +329,49 @@ test('Push without arguments invalid when stack is empty', t => { shell.pushd(); t.is(shell.error(), 'pushd: no other directory'); }); + +test('quiet mode off', t => { + try { + shell.config.silent = false; + mocks.stdout.init(); + mocks.stderr.init(); + const trail = shell.pushd('test/resources/pushd'); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); + t.falsy(shell.error()); + t.is(stdout, ''); + t.is(stderr, `${path.resolve(rootDir, 'test/resources/pushd')} ${rootDir}\n`); + t.is(process.cwd(), trail[0]); + t.deepEqual(trail, [ + path.resolve(rootDir, 'test/resources/pushd'), + rootDir, + ]); + } finally { + shell.config.silent = true; + mocks.stdout.restore(); + mocks.stderr.restore(); + } +}); + +test('quiet mode on', t => { + try { + shell.config.silent = false; + mocks.stdout.init(); + mocks.stderr.init(); + const trail = shell.pushd('-q', 'test/resources/pushd'); + const stdout = mocks.stdout.getValue(); + const stderr = mocks.stderr.getValue(); + t.falsy(shell.error()); + t.is(stdout, ''); + t.is(stderr, ''); + t.is(process.cwd(), trail[0]); + t.deepEqual(trail, [ + path.resolve(rootDir, 'test/resources/pushd'), + rootDir, + ]); + } finally { + shell.config.silent = true; + mocks.stdout.restore(); + mocks.stderr.restore(); + } +}); diff --git a/test/pwd.js b/test/pwd.js index ce55b76ce..6e6534cc5 100644 --- a/test/pwd.js +++ b/test/pwd.js @@ -1,9 +1,9 @@ -import path from 'path'; +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); const cur = process.cwd(); diff --git a/test/resources/cat/file3 b/test/resources/cat/file3 new file mode 100644 index 000000000..29f446afe --- /dev/null +++ b/test/resources/cat/file3 @@ -0,0 +1 @@ +test3 \ No newline at end of file diff --git a/test/resources/cat/file4 b/test/resources/cat/file4 new file mode 100644 index 000000000..f4538a5cc --- /dev/null +++ b/test/resources/cat/file4 @@ -0,0 +1,12 @@ +test4-01 +test4-02 +test4-03 +test4-04 +test4-05 +test4-06 +test4-07 +test4-08 +test4-09 +test4-10 +test4-11 +test4-12 diff --git a/test/resources/cat/file5 b/test/resources/cat/file5 new file mode 100644 index 000000000..e69de29bb diff --git a/test/resources/exec/slow.js b/test/resources/exec/slow.js index cffff43f5..d8f26becd 100644 --- a/test/resources/exec/slow.js +++ b/test/resources/exec/slow.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +console.log('fast'); setTimeout(function() { console.log('slow'); }, parseInt(process.argv[2], 10)); diff --git a/test/resources/find/dir2_link b/test/resources/find/dir2_link new file mode 120000 index 000000000..1e039be90 --- /dev/null +++ b/test/resources/find/dir2_link @@ -0,0 +1 @@ +dir2 \ No newline at end of file diff --git a/test/resources/grep/case1 b/test/resources/grep/case1 new file mode 100644 index 000000000..b08f7b082 --- /dev/null +++ b/test/resources/grep/case1 @@ -0,0 +1 @@ +Test3 diff --git a/test/resources/grep/case1.js b/test/resources/grep/case1.js new file mode 100644 index 000000000..df6b0d2bc --- /dev/null +++ b/test/resources/grep/case1.js @@ -0,0 +1 @@ +test3 diff --git a/test/resources/grep/case1.txt b/test/resources/grep/case1.txt new file mode 100644 index 000000000..bac87cc8e --- /dev/null +++ b/test/resources/grep/case1.txt @@ -0,0 +1 @@ +TEST3 diff --git a/test/resources/grep/file2 b/test/resources/grep/file2 new file mode 100644 index 000000000..0c37715dc --- /dev/null +++ b/test/resources/grep/file2 @@ -0,0 +1,3 @@ +-v +-vv +-a diff --git a/test/resources/grep/file3 b/test/resources/grep/file3 new file mode 100644 index 000000000..e4bee9f8c --- /dev/null +++ b/test/resources/grep/file3 @@ -0,0 +1,15 @@ +line1 +line2 test line +line3 test line +line4 +line5 +line6 +line7 +line8 +line9 +line10 test line +line11 +line12 +line13 +line14 +line15 test line diff --git a/test/resources/sed/empty.txt b/test/resources/sed/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/resources/shjs/a-file.js b/test/resources/shjs/a-file.js deleted file mode 100644 index 3050cd9a6..000000000 --- a/test/resources/shjs/a-file.js +++ /dev/null @@ -1,2 +0,0 @@ -console.log('OK!'); -process.exit(0); diff --git a/test/resources/shjs/coffeescript.coffee b/test/resources/shjs/coffeescript.coffee deleted file mode 100644 index 5b2766405..000000000 --- a/test/resources/shjs/coffeescript.coffee +++ /dev/null @@ -1,2 +0,0 @@ -msg = 'CoffeeScript: OK!' -console.log msg diff --git a/test/resources/shjs/exit-0.js b/test/resources/shjs/exit-0.js deleted file mode 100644 index dcbbff6c9..000000000 --- a/test/resources/shjs/exit-0.js +++ /dev/null @@ -1 +0,0 @@ -process.exit(0); diff --git a/test/resources/shjs/exit-codes.js b/test/resources/shjs/exit-codes.js deleted file mode 100644 index 6427ca060..000000000 --- a/test/resources/shjs/exit-codes.js +++ /dev/null @@ -1 +0,0 @@ -process.exit(42); diff --git a/test/resources/shjs/stdout-stderr.js b/test/resources/shjs/stdout-stderr.js deleted file mode 100644 index 220cb58f8..000000000 --- a/test/resources/shjs/stdout-stderr.js +++ /dev/null @@ -1,2 +0,0 @@ -console.log('stdout: OK!'); -console.error('stderr: OK!'); diff --git a/test/resources/which/node b/test/resources/which/node new file mode 100644 index 000000000..0b917ba31 --- /dev/null +++ b/test/resources/which/node @@ -0,0 +1 @@ +text file, not an executable diff --git a/test/rm.js b/test/rm.js index ccd2c6ba6..01efd7c0b 100644 --- a/test/rm.js +++ b/test/rm.js @@ -1,10 +1,10 @@ -import fs from 'fs'; -import path from 'path'; +const fs = require('fs'); +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -35,7 +35,7 @@ test('file does not exist', t => { t.is(result.stderr, 'rm: no such file or directory: asdfasdf'); }); -test('cannot delete a directoy without recursive flag', t => { +test('cannot delete a directory without recursive flag', t => { const result = shell.rm(`${t.context.tmp}/rm`); t.truthy(shell.error()); t.is(result.code, 1); diff --git a/test/sed.js b/test/sed.js index fc65c5412..fa0d41810 100644 --- a/test/sed.js +++ b/test/sed.js @@ -1,9 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -94,7 +94,7 @@ test('-i option', t => { const result = shell.sed('-i', /test1/, 'hello', `${t.context.tmp}/file1`); t.falsy(shell.error()); t.is(result.code, 0); - t.is(result.toString(), 'hello'); + t.is(result.toString(), ''); t.is(shell.cat(`${t.context.tmp}/file1`).toString(), 'hello'); }); @@ -159,7 +159,7 @@ test('multiple file names, with in-place-replacement', t => { `${t.context.tmp}/file2`]); t.falsy(shell.error()); t.is(result.code, 0); - t.is(result.toString(), 'hello1\nhello2'); + t.is(result.toString(), ''); t.is(shell.cat(`${t.context.tmp}/file1`).toString(), 'hello1'); t.is(shell.cat(`${t.context.tmp}/file2`).toString(), 'hello2'); }); @@ -170,7 +170,13 @@ test('glob file names, with in-place-replacement', t => { const result = shell.sed('-i', 'test', 'hello', `${t.context.tmp}/file*.txt`); t.falsy(shell.error()); t.is(result.code, 0); - t.is(result.toString(), 'hello1\n\nhello2\n'); // TODO: fix sed's behavior + t.is(result.toString(), ''); t.is(shell.cat(`${t.context.tmp}/file1.txt`).toString(), 'hello1\n'); t.is(shell.cat(`${t.context.tmp}/file2.txt`).toString(), 'hello2\n'); }); + +test('empty file', t => { + const result = shell.sed('widget', 'wizzle', 'test/resources/sed/empty.txt'); + t.is(result.code, 0); + t.is(result.toString(), ''); +}); diff --git a/test/set.js b/test/set.js index 55337c1e5..94c0a0bfa 100644 --- a/test/set.js +++ b/test/set.js @@ -1,7 +1,7 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); const oldConfigSilent = shell.config.silent; const uncaughtErrorExitCode = 1; @@ -28,21 +28,21 @@ test('initial values', t => { }); test('default behavior', t => { - const result = shell.exec(JSON.stringify(shell.config.execPath) + ' -e "require(\'./global\'); ls(\'file_doesnt_exist\'); echo(1234);"'); + const result = shell.exec(JSON.stringify(shell.config.execPath) + ` -e "require('./global'); ls('file_doesnt_exist'); echo(1234);"`); t.is(result.code, 0); t.is(result.stdout, '1234\n'); t.is(result.stderr, 'ls: no such file or directory: file_doesnt_exist\n'); }); test('set -e', t => { - const result = shell.exec(JSON.stringify(shell.config.execPath) + ' -e "require(\'./global\'); set(\'-e\'); ls(\'file_doesnt_exist\'); echo(1234);"'); + const result = shell.exec(JSON.stringify(shell.config.execPath) + ` -e "require('./global'); set('-e'); ls('file_doesnt_exist'); echo(1234);"`); t.is(result.code, uncaughtErrorExitCode); t.is(result.stdout, ''); - t.truthy(result.stderr.indexOf('Error: ls: no such file or directory: file_doesnt_exist') >= 0); + t.truthy(result.stderr.includes('Error: ls: no such file or directory: file_doesnt_exist')); }); test('set -v', t => { - const result = shell.exec(JSON.stringify(shell.config.execPath) + ' -e "require(\'./global\'); set(\'-v\'); ls(\'file_doesnt_exist\'); echo(1234);"'); + const result = shell.exec(JSON.stringify(shell.config.execPath) + ` -e "require('./global'); set('-v'); ls('file_doesnt_exist'); echo(1234);"`); t.is(result.code, 0); t.is(result.stdout, '1234\n'); t.is( @@ -52,16 +52,16 @@ test('set -v', t => { }); test('set -ev', t => { - const result = shell.exec(JSON.stringify(shell.config.execPath) + ' -e "require(\'./global\'); set(\'-ev\'); ls(\'file_doesnt_exist\'); echo(1234);"'); + const result = shell.exec(JSON.stringify(shell.config.execPath) + ` -e "require('./global'); set('-ev'); ls('file_doesnt_exist'); echo(1234);"`); t.is(result.code, uncaughtErrorExitCode); t.is(result.stdout, ''); - t.truthy(result.stderr.indexOf('Error: ls: no such file or directory: file_doesnt_exist') >= 0); - t.truthy(result.stderr.indexOf('ls file_doesnt_exist\n') >= 0); - t.is(result.stderr.indexOf('echo 1234\n'), -1); + t.truthy(result.stderr.includes('Error: ls: no such file or directory: file_doesnt_exist')); + t.truthy(result.stderr.includes('ls file_doesnt_exist\n')); + t.falsy(result.stderr.includes('echo 1234\n')); }); test('set -e, set +e', t => { - const result = shell.exec(JSON.stringify(shell.config.execPath) + ' -e "require(\'./global\'); set(\'-e\'); set(\'+e\'); ls(\'file_doesnt_exist\'); echo(1234);"'); + const result = shell.exec(JSON.stringify(shell.config.execPath) + ` -e "require('./global'); set('-e'); set('+e'); ls('file_doesnt_exist'); echo(1234);"`); t.is(result.code, 0); t.is(result.stdout, '1234\n'); t.is(result.stderr, 'ls: no such file or directory: file_doesnt_exist\n'); @@ -75,4 +75,3 @@ test('set -f', t => { shell.rm(`${t.context.tmp}/*.txt`); t.falsy(shell.error()); // globbing works, so rm succeeds }); - diff --git a/test/shjs.js b/test/shjs.js deleted file mode 100644 index 4df91666d..000000000 --- a/test/shjs.js +++ /dev/null @@ -1,54 +0,0 @@ -import path from 'path'; - -import test from 'ava'; - -import shell from '..'; - -function runWithShjs(name) { - // prefix with 'node ' for Windows, don't prefix for unix - const binPath = path.resolve(__dirname, '../bin/shjs'); - const execPath = process.platform === 'win32' - ? `${JSON.stringify(shell.config.execPath)} ` - : ''; - const script = path.resolve(__dirname, 'resources', 'shjs', name); - return shell.exec(`${execPath}${binPath} ${script}`, { silent: true }); -} - -// -// Valids -// - -test('Non-zero exit code', t => { - const result = runWithShjs('exit-codes.js'); - t.is(result.code, 42); - t.is(result.stdout, ''); - t.falsy(result.stderr); -}); - -test('Zero exit code', t => { - const result = runWithShjs('exit-0.js'); - t.is(result.code, 0); - t.is(result.stdout, ''); - t.falsy(result.stderr); -}); - -test('Stdout/Stderr', t => { - const result = runWithShjs('stdout-stderr.js'); - t.is(result.code, 0); - t.is(result.stdout, 'stdout: OK!\n'); - t.is(result.stderr, 'stderr: OK!\n'); -}); - -test('CoffeeScript', t => { - const result = runWithShjs('coffeescript.coffee'); - t.is(result.code, 0); - t.is(result.stdout, 'CoffeeScript: OK!\n'); - t.falsy(result.stderr); -}); - -test('Extension detection', t => { - const result = runWithShjs('a-file'); - t.is(result.code, 0); - t.is(result.stdout, 'OK!\n'); - t.falsy(result.stderr); -}); diff --git a/test/sort.js b/test/sort.js index 6d28c1e25..ac716e87c 100644 --- a/test/sort.js +++ b/test/sort.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const common = require('../src/common'); shell.config.silent = true; @@ -31,7 +32,7 @@ test('file does not exist', t => { }); test('directory', t => { - t.truthy(fs.statSync('test/resources/').isDirectory()); // sanity check + t.truthy(common.statFollowLinks('test/resources/').isDirectory()); // sanity check const result = shell.sort('test/resources/'); t.truthy(shell.error()); t.is(result.code, 1); @@ -77,14 +78,14 @@ test('Globbed file', t => { t.is(result.toString(), doubleSorted); }); -test('With \'-n\' option', t => { +test("With '-n' option", t => { const result = shell.sort('-n', 'test/resources/sort/file2'); t.falsy(shell.error()); t.is(result.code, 0); t.is(result.toString(), shell.cat('test/resources/sort/sortedDashN').toString()); }); -test('With \'-r\' option', t => { +test("With '-r' option", t => { const result = shell.sort('-r', 'test/resources/sort/file2'); t.falsy(shell.error()); t.is(result.code, 0); @@ -95,7 +96,7 @@ test('With \'-r\' option', t => { .join('\n') + '\n'); }); -test('With \'-rn\' option', t => { +test("With '-rn' option", t => { const result = shell.sort('-rn', 'test/resources/sort/file2'); t.falsy(shell.error()); t.is(result.code, 0); diff --git a/test/tail.js b/test/tail.js index 328fb22ff..158b45d8f 100644 --- a/test/tail.js +++ b/test/tail.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const common = require('../src/common'); shell.config.silent = true; @@ -24,7 +25,7 @@ test('file does not exist', t => { }); test('directory', t => { - t.truthy(fs.statSync('test/resources/').isDirectory()); // sanity check + t.truthy(common.statFollowLinks('test/resources/').isDirectory()); // sanity check const result = shell.tail('test/resources/'); t.truthy(shell.error()); t.is(result.code, 1); @@ -89,7 +90,7 @@ test('reading more lines than are in the file (with trailing newline)', t => { t.is(result.toString(), 'short2\nshort1\n'); // these files only have one line (with \n) }); -test('Globbed file', t => { +test('globbed file', t => { const result = shell.tail('test/resources/head/file?.txt'); t.falsy(shell.error()); t.is(result.code, 0); @@ -101,7 +102,7 @@ test('Globbed file', t => { .join('\n') + '\n'); }); -test('With `\'-n\' ` option', t => { +test("with '-n ' option", t => { const result = shell.tail('-n', 4, 'test/resources/head/file2.txt', 'test/resources/head/file1.txt'); t.falsy(shell.error()); @@ -114,7 +115,20 @@ test('With `\'-n\' ` option', t => { .join('\n') + '\n'); }); -test('With `{\'-n\': }` option', t => { +test("with '-n +' option", t => { + const result = shell.tail('-n', '+48', 'test/resources/head/file2.txt', + 'test/resources/head/file1.txt'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), + bottomOfFile2 + .slice(0, 3) + .reverse() + .concat(bottomOfFile1.slice(0, 3).reverse()) + .join('\n') + '\n'); +}); + +test("with '{-n: }' option", t => { const result = shell.tail({ '-n': 4 }, 'test/resources/head/file2.txt', 'test/resources/head/file1.txt'); t.falsy(shell.error()); @@ -127,6 +141,19 @@ test('With `{\'-n\': }` option', t => { .join('\n') + '\n'); }); +test("with '{-n: +}' option", t => { + const result = shell.tail({ '-n': '+48' }, 'test/resources/head/file2.txt', + 'test/resources/head/file1.txt'); + t.falsy(shell.error()); + t.is(result.code, 0); + t.is(result.toString(), + bottomOfFile2 + .slice(0, 3) + .reverse() + .concat(bottomOfFile1.slice(0, 3).reverse()) + .join('\n') + '\n'); +}); + test('negative values are the same as positive values', t => { const result = shell.tail('-n', -4, 'test/resources/head/file2.txt', 'test/resources/head/file1.txt'); diff --git a/test/tempdir.js b/test/tempdir.js index ed7ec77fd..5590667e7 100644 --- a/test/tempdir.js +++ b/test/tempdir.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const { isCached, clearCache } = require('../src/tempdir'); shell.config.silent = true; @@ -19,3 +20,12 @@ test('basic usage', t => { // It's a directory t.truthy(shell.test('-d', tmp)); }); + +test('cache', t => { + clearCache(); // In case this runs after any test which relies on tempdir(). + t.falsy(isCached()); + const tmp1 = shell.tempdir(); + t.truthy(isCached()); + const tmp2 = shell.tempdir(); + t.is(tmp1, tmp2); +}); diff --git a/test/test.js b/test/test.js index 40a1778cb..1b42057ae 100644 --- a/test/test.js +++ b/test/test.js @@ -1,7 +1,7 @@ -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); shell.config.silent = true; diff --git a/test/to.js b/test/to.js index 9774b8331..4669052eb 100644 --- a/test/to.js +++ b/test/to.js @@ -1,9 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -20,7 +20,7 @@ test.afterEach.always(t => { // Invalids // -test('Normal strings don\'t have \'.to()\' anymore', t => { +test("Normal strings don't have '.to()' anymore", t => { const str = 'hello world'; t.is(str.to, undefined); }); diff --git a/test/toEnd.js b/test/toEnd.js index cf3aec4f9..4e8417396 100644 --- a/test/toEnd.js +++ b/test/toEnd.js @@ -1,9 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -20,7 +20,7 @@ test.afterEach.always(t => { // Invalids // -test('Normal strings don\'t have \'.toEnd()\' anymore', t => { +test("Normal strings don't have '.toEnd()' anymore", t => { const str = 'hello world'; t.is(str.toEnd, undefined); }); diff --git a/test/touch.js b/test/touch.js index 0221170f5..c767eafcd 100644 --- a/test/touch.js +++ b/test/touch.js @@ -1,10 +1,11 @@ -import crypto from 'crypto'; -import fs from 'fs'; +const crypto = require('crypto'); +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const common = require('../src/common'); +const utils = require('./utils/utils'); test.beforeEach(t => { t.context.tmp = utils.getTempDir(); @@ -21,7 +22,7 @@ function resetUtimes(f) { const d = new Date(); d.setYear(2000); fs.utimesSync(f, d, d); - return fs.statSync(f); + return common.statFollowLinks(f); } function tmpFile(t, noCreate) { @@ -76,8 +77,8 @@ test('handles globs correctly', t => { const result = shell.touch(`${t.context.tmp}/file*`); t.is(result.code, 0); const files = shell.ls(`${t.context.tmp}/file*`); - t.truthy(files.indexOf(`${t.context.tmp}/file.txt`) > -1); - t.truthy(files.indexOf(`${t.context.tmp}/file.js`) > -1); + t.truthy(files.includes(`${t.context.tmp}/file.txt`)); + t.truthy(files.includes(`${t.context.tmp}/file.js`)); t.is(files.length, 2); }); @@ -98,33 +99,53 @@ test('uses a reference file for mtime', t => { t.falsy(shell.error()); t.is(result.code, 0); t.not( - fs.statSync(testFile).mtime.getTime(), - fs.statSync(testFile2).mtime.getTime() + common.statFollowLinks(testFile).mtime.getTime(), + common.statFollowLinks(testFile2).mtime.getTime() ); t.not( - fs.statSync(testFile).atime.getTime(), - fs.statSync(testFile2).atime.getTime() + common.statFollowLinks(testFile).atime.getTime(), + common.statFollowLinks(testFile2).atime.getTime() ); result = shell.touch({ '-r': testFile2 }, testFile); t.falsy(shell.error()); t.is(result.code, 0); t.is( - fs.statSync(testFile).mtime.getTime(), - fs.statSync(testFile2).mtime.getTime() + common.statFollowLinks(testFile).mtime.getTime(), + common.statFollowLinks(testFile2).mtime.getTime() ); t.is( - fs.statSync(testFile).atime.getTime(), - fs.statSync(testFile2).atime.getTime() + common.statFollowLinks(testFile).atime.getTime(), + common.statFollowLinks(testFile2).atime.getTime() ); }); +test('accepts -d flag', t => { + const testFile = tmpFile(t); + const date = new Date('December 17, 1995 03:24:00'); + const result = shell.touch({ '-d': date }, testFile); + t.is(result.code, 0); + // Compare getTime(), because Date can't be compared with triple-equals. + t.is(common.statFollowLinks(testFile).mtime.getTime(), date.getTime()); + t.is(common.statFollowLinks(testFile).atime.getTime(), date.getTime()); +}); + +test('accepts long option (--date)', t => { + const testFile = tmpFile(t); + const someDate = new Date('December 17, 1995 03:24:00'); + const result = shell.touch({ date: someDate }, testFile); + t.is(result.code, 0); + // Compare getTime(), because Date can't be compared with triple-equals. + t.is(common.statFollowLinks(testFile).mtime.getTime(), someDate.getTime()); + t.is(common.statFollowLinks(testFile).atime.getTime(), someDate.getTime()); +}); + test('sets mtime and atime by default', t => { const testFile = tmpFile(t); const oldStat = resetUtimes(testFile); const result = shell.touch(testFile); t.is(result.code, 0); - t.truthy(oldStat.mtime < fs.statSync(testFile).mtime); - t.truthy(oldStat.atime < fs.statSync(testFile).atime); + t.truthy(oldStat.mtime < common.statFollowLinks(testFile).mtime); + t.truthy(oldStat.atime < common.statFollowLinks(testFile).atime); }); test('does not set mtime if told not to', t => { @@ -132,7 +153,7 @@ test('does not set mtime if told not to', t => { const oldStat = resetUtimes(testFile); const result = shell.touch('-a', testFile); t.is(result.code, 0); - t.is(oldStat.mtime.getTime(), fs.statSync(testFile).mtime.getTime()); + t.is(oldStat.mtime.getTime(), common.statFollowLinks(testFile).mtime.getTime()); }); test('does not set atime if told not to', t => { @@ -140,7 +161,7 @@ test('does not set atime if told not to', t => { const oldStat = resetUtimes(testFile); const result = shell.touch('-m', testFile); t.is(result.code, 0); - t.is(oldStat.atime.getTime(), fs.statSync(testFile).atime.getTime()); + t.is(oldStat.atime.getTime(), common.statFollowLinks(testFile).atime.getTime()); }); test('multiple files', t => { @@ -165,10 +186,10 @@ test('file array', t => { test('touching broken link creates a new file', t => { utils.skipOnWin(t, () => { - const result = shell.touch('test/resources/badlink'); + shell.ln('-s', 'not_existed_file', `${t.context.tmp}/badlink2`); + const result = shell.touch(`${t.context.tmp}/badlink2`); t.is(result.code, 0); t.falsy(shell.error()); - t.truthy(fs.existsSync('test/resources/not_existed_file')); - shell.rm('test/resources/not_existed_file'); + t.truthy(fs.existsSync(`${t.context.tmp}/not_existed_file`)); }); }); diff --git a/test/uniq.js b/test/uniq.js index c85df4e27..72611df83 100644 --- a/test/uniq.js +++ b/test/uniq.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +const fs = require('fs'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; +const shell = require('..'); +const common = require('../src/common'); shell.config.silent = true; @@ -24,7 +25,7 @@ test('file does not exist', t => { }); test('directory', t => { - t.truthy(fs.statSync('test/resources/').isDirectory()); // sanity check + t.truthy(common.statFollowLinks('test/resources/').isDirectory()); // sanity check const result = shell.uniq('test/resources/'); t.truthy(shell.error()); t.is(result.code, 1); @@ -32,7 +33,7 @@ test('directory', t => { }); test('output directory', t => { - t.truthy(fs.statSync('test/resources/').isDirectory()); // sanity check + t.truthy(common.statFollowLinks('test/resources/').isDirectory()); // sanity check const result = shell.uniq('test/resources/file1.txt', 'test/resources/'); t.truthy(shell.error()); t.is(result.code, 1); diff --git a/test/utils/mocks.js b/test/utils/mocks.js index 79dc55087..a55e6d298 100644 --- a/test/utils/mocks.js +++ b/test/utils/mocks.js @@ -16,29 +16,53 @@ function wrapWrite(target) { }; } -const _processStdoutWrite = process.stdout.write; -const _processStderrWrite = process.stderr.write; -const _stdout = []; -const _stderr = []; -const _stdoutWrite = wrapWrite(_stdout); -const _stderrWrite = wrapWrite(_stderr); - -exports.stdout = function stdout() { - return joinData(_stdout); +const stdout = { + original: process.stdout.write, + value: [], + init: function init() { + this.value = []; + process.stdout.write = wrapWrite(this.value); + }, + restore: function restore() { + process.stdout.write = this.original; + }, + getValue: function getValue() { + return joinData(this.value); + } }; -exports.stderr = function stderr() { - return joinData(_stderr); +const stderr = { + original: process.stderr.write, + value: [], + init: function init() { + this.value = []; + process.stderr.write = wrapWrite(this.value); + }, + restore: function restore() { + process.stderr.write = this.original; + }, + getValue: function getValue() { + return joinData(this.value); + } }; -exports.init = function init() { - process.stdout.write = _stdoutWrite; - process.stderr.write = _stderrWrite; +const exit = { + original: process.exit, + value: undefined, + init: function init() { + this.value = undefined; + process.exit = (newCode) => { + this.value = newCode; + }; + }, + restore: function restore() { + process.exit = this.original; + }, + getValue: function getValue() { + return this.value; + } }; -exports.restore = function restore() { - process.stdout.write = _processStdoutWrite; - process.stderr.write = _processStderrWrite; - _stdout.splice(0); - _stderr.splice(0); -}; +exports.stdout = stdout; +exports.stderr = stderr; +exports.exit = exit; diff --git a/test/utils/utils.js b/test/utils/utils.js index aae5cdefb..9c20e5db3 100644 --- a/test/utils/utils.js +++ b/test/utils/utils.js @@ -1,10 +1,14 @@ const child = require('child_process'); const path = require('path'); +const { promisify } = require('node:util'); const chalk = require('chalk'); const common = require('../../src/common'); +// Capture process.stderr.write, otherwise we have a conflict with mocks.js +const _processStderrWrite = process.stderr.write.bind(process.stderr); + function numLines(str) { return typeof str === 'string' ? (str.match(/\n/g) || []).length + 1 : 0; } @@ -23,15 +27,16 @@ function skipOnWinForEPERM(action, testCase) { const error = ret.code; const isWindows = process.platform === 'win32'; if (isWindows && error && /EPERM:/.test(error)) { - console.warn('Got EPERM when testing symlinks on Windows. Assuming non-admin environment and skipping test.'); + _processStderrWrite('Got EPERM when testing symlinks on Windows. Assuming non-admin environment and skipping test.\n'); } else { testCase(); } } exports.skipOnWinForEPERM = skipOnWinForEPERM; -function runScript(script, cb) { - child.execFile(common.config.execPath, ['-e', script], cb); +function runScript(script) { + const promiseExec = promisify(child.execFile); + return promiseExec(common.config.execPath, ['-e', script]); } exports.runScript = runScript; @@ -56,9 +61,10 @@ exports.mkfifo = mkfifo; function skipIfTrue(booleanValue, t, closure) { if (booleanValue) { - console.warn( + _processStderrWrite( chalk.yellow('Warning: skipping platform-dependent test ') + - chalk.bold.white(`'${t._test.title}'`) + chalk.bold.white(`'${t.title}'`) + + '\n' ); t.truthy(true); // dummy assertion to satisfy ava v0.19+ } else { diff --git a/test/which.js b/test/which.js index 530fcd2ca..c49eb06d4 100644 --- a/test/which.js +++ b/test/which.js @@ -1,9 +1,10 @@ -import fs from 'fs'; +const fs = require('fs'); +const path = require('path'); -import test from 'ava'; +const test = require('ava'); -import shell from '..'; -import utils from './utils/utils'; +const shell = require('..'); +const utils = require('./utils/utils'); shell.config.silent = true; @@ -69,5 +70,27 @@ test('Searching with -a flag returns an array with first item equals to the regu t.falsy(shell.error()); t.truthy(resultForWhich); t.truthy(resultForWhichA); - t.is(resultForWhich.toString(), resultForWhichA[0].toString()); + t.is(resultForWhich.toString(), resultForWhichA[0]); +}); + +test('None executable files does not appear in the result list', t => { + const commandName = 'node'; // Should be an existing command + const extraPath = path.resolve(__dirname, 'resources', 'which'); + const matchingFile = path.resolve(extraPath, commandName); + const pathEnv = process.env.PATH; + + // make sure that file is exists (will throw error otherwise) + t.truthy(fs.existsSync(matchingFile)); + + process.env.PATH = extraPath + path.delimiter + process.env.PATH; + const resultForWhich = shell.which(commandName); + const resultForWhichA = shell.which('-a', commandName); + t.falsy(shell.error()); + t.truthy(resultForWhich); + t.truthy(resultForWhichA); + t.truthy(resultForWhichA.length); + t.not(resultForWhich.toString(), matchingFile); + t.falsy(resultForWhichA.includes(matchingFile)); + + process.env.PATH = pathEnv; });