Initial commit

This commit is contained in:
mats committed 2026-10-19 16:04:17 +02:00
commit afed023152
8152 files changed
+2086461

No files matched your search

+965
View File
@@ -0,0 +1,965 @@
# Vitest core license
Vitest is released under the MIT license:
MIT License
Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# Licenses of bundled dependencies
The published Vitest artifact additionally contains code with the following licenses:
BSD-3-Clause, ISC, MIT
# Bundled dependencies:
## @antfu/install-pkg
License: MIT
By: Anthony Fu
Repository: git+https://github.com/antfu-collective/install-pkg.git
> MIT License
>
> Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## @bomb.sh/tab
License: MIT
By: Bombshell Authors
Repository: git+https://github.com/bombshell-dev/tab.git
---------------------------------------
## @jridgewell/resolve-uri
License: MIT
By: Justin Ridgewell
Repository: https://github.com/jridgewell/resolve-uri
> Copyright 2019 Justin Ridgewell <jridgewell@google.com>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## @jridgewell/sourcemap-codec
License: MIT
By: Justin Ridgewell
Repository: git+https://github.com/jridgewell/sourcemaps.git
> Copyright 2024 Justin Ridgewell <justin@ridgewell.name>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## @jridgewell/trace-mapping
License: MIT
By: Justin Ridgewell
Repository: git+https://github.com/jridgewell/sourcemaps.git
> Copyright 2024 Justin Ridgewell <justin@ridgewell.name>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## @sinonjs/commons
License: BSD-3-Clause
Repository: git+https://github.com/sinonjs/commons.git
> BSD 3-Clause License
>
> Copyright (c) 2018, Sinon.JS
> All rights reserved.
>
> 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 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.
---------------------------------------
## @sinonjs/fake-timers
License: BSD-3-Clause
By: Christian Johansen
Repository: git+https://github.com/sinonjs/fake-timers.git
> Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no. All rights reserved.
>
> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
>
> 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
>
> 2. 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.
>
> 3. 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.
---------------------------------------
## acorn
License: MIT
By: Marijn Haverbeke, Ingvar Stepanyan, Adrian Heine
Repository: https://github.com/acornjs/acorn.git
> MIT License
>
> Copyright (C) 2012-2022 by various contributors (see AUTHORS)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## acorn-walk
License: MIT
By: Marijn Haverbeke, Ingvar Stepanyan, Adrian Heine
Repository: https://github.com/acornjs/acorn.git
> MIT License
>
> Copyright (C) 2012-2020 by various contributors (see AUTHORS)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## birpc
License: MIT
By: Anthony Fu
Repository: git+https://github.com/antfu-collective/birpc.git
> MIT License
>
> Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## cac
License: MIT
By: egoist
Repository: egoist/cac
> The MIT License (MIT)
>
> Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## convert-source-map
License: MIT
By: Thorsten Lorenz
Repository: git://github.com/thlorenz/convert-source-map.git
> Copyright 2013 Thorsten Lorenz.
> All rights reserved.
>
> Permission is hereby granted, free of charge, to any person
> obtaining a copy of this software and associated documentation
> files (the "Software"), to deal in the Software without
> restriction, including without limitation the rights to use,
> copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the
> Software is furnished to do so, subject to the following
> conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> OTHER DEALINGS IN THE SOFTWARE.
---------------------------------------
## diff-sequences
License: MIT
Repository: https://github.com/jestjs/jest.git
> MIT License
>
> Copyright (c) Meta Platforms, Inc. and affiliates.
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## flatted
License: ISC
By: Andrea Giammarchi
Repository: git+https://github.com/WebReflection/flatted.git
> ISC License
>
> Copyright (c) 2018-2020, Andrea Giammarchi, @WebReflection
>
> Permission to use, copy, modify, and/or distribute this software for any
> purpose with or without fee is hereby granted, provided that the above
> copyright notice and this permission notice appear in all copies.
>
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
> AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
> OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
> PERFORMANCE OF THIS SOFTWARE.
---------------------------------------
## js-tokens
License: MIT
By: Simon Lydell
Repository: lydell/js-tokens
> The MIT License (MIT)
>
> Copyright (c) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Simon Lydell
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## kleur
License: MIT
By: Luke Edwards
Repository: lukeed/kleur
> The MIT License (MIT)
>
> Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## local-pkg
License: MIT
By: Anthony Fu
Repository: git+https://github.com/antfu-collective/local-pkg.git
> MIT License
>
> Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## mime
License: MIT
By: Robert Kieffer
Repository: https://github.com/broofa/mime
> MIT License
>
> Copyright (c) 2023 Robert Kieffer
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## mlly
License: MIT
Repository: unjs/mlly
> MIT License
>
> Copyright (c) Pooya Parsa <pooya@pi0.io>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## natural-compare
License: MIT
By: Lauri Rooden
Repository: git://github.com/litejs/natural-compare-lite.git
---------------------------------------
## package-manager-detector
License: MIT
By: Anthony Fu
Repository: git+https://github.com/antfu-collective/package-manager-detector.git
> MIT License
>
> Copyright (c) 2020-PRESENT Anthony Fu <https://github.com/antfu>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## pathe
License: MIT
Repository: unjs/pathe
> MIT License
>
> Copyright (c) Pooya Parsa <pooya@pi0.io> - Daniel Roe <daniel@roe.dev>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
>
> ---
>
> Copyright Joyent, Inc. and other Node contributors.
>
> Permission is hereby granted, free of charge, to any person obtaining a
> copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to permit
> persons to whom the Software is furnished to do so, subject to the
> following conditions:
>
> The above copyright notice and this permission notice shall be included
> in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
> NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
> DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
> OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
> USE OR OTHER DEALINGS IN THE SOFTWARE.
>
> ---
>
> Bundled zeptomatch (https://github.com/fabiospampinato/zeptomatch)
>
> The MIT License (MIT)
>
> Copyright (c) 2023-present Fabio Spampinato
>
> Permission is hereby granted, free of charge, to any person obtaining a
> copy of this software and associated documentation files (the "Software"),
> to deal in the Software without restriction, including without limitation
> the rights to use, copy, modify, merge, publish, distribute, sublicense,
> and/or sell copies of the Software, and to permit persons to whom the
> Software is furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS IN THE SOFTWARE.
---------------------------------------
## prompts
License: MIT
By: Terkel Gjervig
Repository: terkelg/prompts
> MIT License
>
> Copyright (c) 2018 Terkel Gjervig Nielsen
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## quansync
License: MIT
By: Anthony Fu, 三咲智子 Kevin Deng
Repository: git+https://github.com/quansync-dev/quansync.git
> MIT License
>
> Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu> and Kevin Deng <https://github.com/sxzz>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## sisteransi
License: MIT
By: Terkel Gjervig
Repository: https://github.com/terkelg/sisteransi
> MIT License
>
> Copyright (c) 2018 Terkel Gjervig Nielsen
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## strip-literal
License: MIT
By: Anthony Fu
Repository: git+https://github.com/antfu/strip-literal.git
> MIT License
>
> Copyright (c) 2022 Anthony Fu <https://github.com/antfu>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## tinyhighlight
License: MIT
Repository: git+https://github.com/tinylibs/tinyhighlight.git
> # Tinyhighlight core license
>
> MIT License
>
> Copyright (c) 2023 Tinylibs
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
>
> # Additionaly Tinyhighlight modifies code with the following licenses:
>
> MIT
>
> ## @babel/highlight
>
> MIT License
>
> Copyright (c) 2014-present Sebastian McKenzie and other contributors
>
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to
> permit persons to whom the Software is furnished to do so, subject to
> the following conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>
> ## @babel/helper-validator-identifier
>
> MIT License
>
> Copyright (c) 2014-present Sebastian McKenzie and other contributors
>
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to
> permit persons to whom the Software is furnished to do so, subject to
> the following conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---------------------------------------
## tinyrainbow
License: MIT
Repository: git+https://github.com/tinylibs/tinyrainbow.git
> MIT License
>
> Copyright (c) 2022 Tinylibs
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## type-detect
License: MIT
By: Jake Luer, Keith Cirkel, David Losert, Aleksey Shvayka, Lucas Fernandes da Costa, Grant Snodgrass, Jeremy Tice, Edward Betts, dvlsg, Amila Welihinda, Jake Champion, Miroslav Bajtoš
Repository: git+ssh://git@github.com/chaijs/type-detect.git
> Copyright (c) 2013 Jake Luer <jake@alogicalparadox.com> (http://alogicalparadox.com)
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---------------------------------------
## ufo
License: MIT
Repository: unjs/ufo
> MIT License
>
> Copyright (c) Pooya Parsa <pooya@pi0.io>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
---------------------------------------
## ws
License: MIT
By: Einar Otto Stangvik
Repository: git+https://github.com/websockets/ws.git
> Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
> Copyright (c) 2013 Arnout Kazemier and contributors
> Copyright (c) 2016 Luigi Pinca and contributors
>
> Permission is hereby granted, free of charge, to any person obtaining a copy of
> this software and associated documentation files (the "Software"), to deal in
> the Software without restriction, including without limitation the rights to
> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
> the Software, and to permit persons to whom the Software is furnished to do so,
> subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
# vitest
[![NPM version](https://img.shields.io/npm/v/vitest?color=a1b858&label=)](https://npmx.dev/package/vitest)
Next generation testing framework powered by Vite.
[GitHub](https://github.com/vitest-dev/vitest) | [Documentation](https://vitest.dev/)
+7
View File
@@ -0,0 +1,7 @@
// @ts-ignore -- @vitest/browser-playwright might not be installed
export * from '@vitest/browser-playwright/context'
// @ts-ignore -- @vitest/browser-webdriverio might not be installed
export * from '@vitest/browser-webdriverio/context'
// @ts-ignore -- @vitest/browser-preview might not be installed
export * from '@vitest/browser-preview/context'
export { BrowserCommands, CDPSession, FsOptions } from 'vitest/internal/browser'
+20
View File
@@ -0,0 +1,20 @@
// Vitest resolves "vitest/browser" as a virtual module instead
// fake exports for static analysis
export const page = null
export const server = null
export const userEvent = null
export const cdp = null
export const commands = null
export const locators = null
export const utils = null
const pool = globalThis.__vitest_worker__?.ctx?.pool
throw new Error(
// eslint-disable-next-line prefer-template
'vitest/browser can be imported only inside the Browser Mode. '
+ (pool
? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.`
: 'Instead, it was imported outside of Vitest.'),
)
+3
View File
@@ -0,0 +1,3 @@
// ensure `@vitest/expect` provides `chai` types
import type {} from '@vitest/expect'
export * from './dist/config.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/coverage.js'
+64
View File
@@ -0,0 +1,64 @@
import { F as FileSpecification, c as SerializedDiffOptions } from './chunks/config.d.CU_b-wJj.js';
import { SerializedCoverageConfig, RunnerTestFile as File, SerializedConfig, DiffOptions, TestError } from './index.js';
import { VitestRunner } from './runtime.js';
export { P as ParsedStack, _ as SpyModule, d as StringifyOptions, f as format, i as inspect, s as stringify } from './chunks/config.d.CU_b-wJj.js';
import { R as RuntimeCoverageModuleLoader } from './chunks/source-map.d.YqWNcp4e.js';
export { D as DecodedMap, g as getOriginalPosition } from './chunks/source-map.d.YqWNcp4e.js';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
declare function getType(value: unknown): string;
interface SafeTimers {
nextTick?: (cb: () => void) => void;
setImmediate?: {
<TArgs extends any[]>(callback: (...args: TArgs) => void, ...args: TArgs): any;
__promisify__: <T = void>(value?: T, options?: any) => Promise<T>;
};
clearImmediate?: (immediateId: any) => void;
setTimeout: typeof setTimeout;
setInterval: typeof setInterval;
clearInterval: typeof clearInterval;
clearTimeout: typeof clearTimeout;
queueMicrotask: typeof queueMicrotask;
}
declare function getSafeTimers(): SafeTimers;
declare function setSafeTimers(): void;
declare function startCoverageInsideWorker(options: SerializedCoverageConfig, loader: RuntimeCoverageModuleLoader, runtimeOptions: {
isolate: boolean;
}): Promise<unknown>;
declare function takeCoverageInsideWorker(options: SerializedCoverageConfig, loader: RuntimeCoverageModuleLoader): Promise<unknown>;
declare function stopCoverageInsideWorker(options: SerializedCoverageConfig, loader: RuntimeCoverageModuleLoader, runtimeOptions: {
isolate: boolean;
}): Promise<unknown>;
declare function startTests(specs: string[] | FileSpecification[], runner: VitestRunner): Promise<File[]>;
declare function publicCollect(specs: string[] | FileSpecification[], runner: VitestRunner): Promise<File[]>;
interface PublicModuleRunner {
import: (id: string) => Promise<any>;
}
declare function setupCommonEnv(config: SerializedConfig): Promise<void>;
declare function setupEnv(env: Record<string, any>, metaEnv: Record<string, any>): void;
declare function loadDiffConfig(config: SerializedConfig, moduleRunner: PublicModuleRunner): Promise<SerializedDiffOptions | undefined>;
declare function loadSnapshotSerializers(config: SerializedConfig, moduleRunner: PublicModuleRunner): Promise<void>;
declare function processError(_err: any, diffOptions?: DiffOptions, seen?: WeakSet<WeakKey>): TestError;
interface FsOptions {
encoding?: BufferEncoding;
flag?: string | number;
}
interface BrowserCommands {
readFile: (path: string, options?: BufferEncoding | FsOptions) => Promise<string>;
writeFile: (path: string, content: string, options?: BufferEncoding | (FsOptions & {
mode?: number | string;
})) => Promise<void>;
removeFile: (path: string) => Promise<void>;
}
interface CDPSession {}
export { FileSpecification, publicCollect as collectTests, getSafeTimers, getType, loadDiffConfig, loadSnapshotSerializers, processError, setSafeTimers, setupCommonEnv, setupEnv, startCoverageInsideWorker, startTests, stopCoverageInsideWorker, takeCoverageInsideWorker };
export type { BrowserCommands, CDPSession, FsOptions };
+19
View File
@@ -0,0 +1,19 @@
export { s as startCoverageInsideWorker, a as stopCoverageInsideWorker, t as takeCoverageInsideWorker } from './chunks/coverage.BtDxDqSU.js';
export { p as collectTests, a as processError, s as startTests } from './chunks/run.CQOUYP-x.js';
export { l as loadDiffConfig, a as loadSnapshotSerializers, s as setupCommonEnv, b as setupEnv } from './chunks/setup-common.D5-2VgqG.js';
export { s as SpyModule } from './chunks/spy.DPLvp2J7.js';
export { f as format, i as inspect, s as stringify } from './chunks/display.pkpxlVcY.js';
export { g as getType } from './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
export { D as DecodedMap, g as getOriginalPosition, a as getSafeTimers, s as setSafeTimers } from './chunks/source-map.Dx3YPjIe.js';
import './chunks/coverage.BcXK1DsQ.js';
import './chunks/index.M2dsQ_UQ.js';
import './chunks/tinyrainbow.Ht9iggcq.js';
import './task-utils.js';
import './chunks/plugins.Cigb0uSy.js';
/**
* @internal
*/
const __INTERNAL = { _extendedMethods: /* @__PURE__ */ new Set() };
export { __INTERNAL };
File diff suppressed because it is too large. Load diff
+211
View File
@@ -0,0 +1,211 @@
import { runInThisContext } from 'node:vm';
import { s as spyModule } from './spy.DPLvp2J7.js';
import { r as resolveTestRunner, a as resolveSnapshotEnvironment, d as detectAsyncLeaks, s as setupChaiConfig } from './index.5J3Pr46F.js';
import { l as loadEnvironment, e as emitModuleRunner, a as listenForErrors } from './init.BS957yFf.js';
import { Traces } from '../traces.js';
import { V as VitestEvaluatedModules } from './rpc.DWKX1ZbI.js';
import { s as startVitestModuleRunner, c as createNodeImportMeta } from './index.1_nbEjJY.js';
import { performance as performance$1 } from 'node:perf_hooks';
import { s as startCoverageInsideWorker, a as stopCoverageInsideWorker } from './coverage.BtDxDqSU.js';
import { i as index, g as globalExpect, v as vi } from './index.OVGXnVRj.js';
import { c as closeInspector } from './inspector.CvyFGlXm.js';
import { s as startTests, p as publicCollect } from './run.CQOUYP-x.js';
import { createRequire } from 'node:module';
import timers from 'node:timers';
import timersPromises from 'node:timers/promises';
import util from 'node:util';
import { K as KNOWN_ASSET_TYPES } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { s as setupCommonEnv, b as setupEnv } from './setup-common.D5-2VgqG.js';
import { g as getWorkerState, r as resetModules, p as provideWorkerState, a as getSafeWorkerState } from './utils.DYj33du9.js';
// this should only be used in Node
let globalSetup = false;
async function setupGlobalEnv(config, environment) {
await setupCommonEnv(config);
Object.defineProperty(globalThis, "__vitest_index__", {
value: index,
enumerable: false
});
globalExpect.setState({ environment: environment.name });
if (globalSetup) return;
globalSetup = true;
if ((environment.viteEnvironment || environment.name) === "client") {
const _require = createRequire(import.meta.url);
// always mock "required" `css` files, because we cannot process them
_require.extensions[".css"] = resolveCss;
_require.extensions[".scss"] = resolveCss;
_require.extensions[".sass"] = resolveCss;
_require.extensions[".less"] = resolveCss;
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset;
});
process.env.SSR = "";
} else process.env.SSR = "1";
// @ts-expect-error not typed global for patched timers
globalThis.__vitest_required__ = {
util,
timers,
timersPromises
};
if (!config.disableConsoleIntercept) await setupConsoleLogSpy();
}
function resolveCss(mod) {
mod.exports = "";
}
function resolveAsset(mod, url) {
mod.exports = url;
}
async function setupConsoleLogSpy() {
const { createCustomConsole } = await import('./console.CHi0lDgQ.js');
globalThis.console = createCustomConsole();
}
// browser shouldn't call this!
async function run(method, files, config, moduleRunner, environment, traces) {
const workerState = getWorkerState();
const [testRunner] = await Promise.all([
traces.$("vitest.runtime.runner", () => resolveTestRunner(config, moduleRunner, traces)),
traces.$("vitest.runtime.global_env", () => setupGlobalEnv(config, environment)),
traces.$("vitest.runtime.coverage.start", () => startCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate })),
traces.$("vitest.runtime.snapshot.environment", async () => {
if (!workerState.config.snapshotOptions.snapshotEnvironment) workerState.config.snapshotOptions.snapshotEnvironment = await resolveSnapshotEnvironment(config, moduleRunner);
})
]);
const offCancel = workerState.onCancel((reason) => {
closeInspector(config);
testRunner.cancel?.(reason);
});
workerState.durations.prepare = performance$1.now() - workerState.durations.prepare;
try {
await traces.$(`vitest.test.runner.${method}`, async () => {
for (const file of files) {
if (config.isolate) {
moduleRunner.mocker?.reset();
resetModules(workerState.evaluatedModules, true);
}
workerState.filepath = file.filepath;
if (method === "run") {
const collectAsyncLeaks = config.detectAsyncLeaks ? detectAsyncLeaks(file.filepath, workerState.ctx.projectName) : void 0;
await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => startTests([file], testRunner));
const leaks = await collectAsyncLeaks?.();
if (leaks?.length) workerState.rpc.onAsyncLeaks(leaks);
} else await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => publicCollect([file], testRunner));
// reset after tests, because user might call `vi.setConfig` in setupFile
vi.resetConfig();
// mocks should not affect different files
vi.restoreAllMocks();
}
});
} finally {
offCancel();
}
await traces.$("vitest.runtime.coverage.stop", () => stopCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate }));
}
let _moduleRunner;
const evaluatedModules = new VitestEvaluatedModules();
const moduleExecutionInfo = /* @__PURE__ */ new Map();
async function startModuleRunner(options) {
if (_moduleRunner) return _moduleRunner;
const state = () => getSafeWorkerState() || options.state;
process.exit = (code = process.exitCode || 0) => {
const filepath = state().filepath;
throw new Error(`process.exit unexpectedly called with "${code}"${filepath ? ` (test file: ${filepath})` : ""}`);
};
listenForErrors(state);
if (options.state.config.experimental.viteModuleRunner === false) {
const root = options.state.config.root;
let mocker;
if (options.state.config.experimental.nodeLoader !== false) {
// this additionally imports acorn/magic-string
const { NativeModuleMocker } = await import('./nativeModuleMocker.E7jPi2fl.js');
mocker = new NativeModuleMocker({
async resolveId(id, importer) {
// TODO: use import.meta.resolve instead
return state().rpc.resolve(id, importer, "__vitest__");
},
root,
moduleDirectories: state().config.deps.moduleDirectories || ["/node_modules/"],
traces: options.traces || new Traces({ enabled: false }),
getCurrentTestFilepath() {
return state().filepath;
},
spyModule
});
}
// imported lazily (pulls in `local-pkg`) so it stays out of the default
// worker startup graph; only the `viteModuleRunner: false` path needs it
const { NativeModuleRunner } = await import('./nativeModuleRunner.BOvBCgI_.js').then(function (n) { return n.n; });
_moduleRunner = new NativeModuleRunner(root, mocker);
return _moduleRunner;
}
_moduleRunner = startVitestModuleRunner(options);
return _moduleRunner;
}
let _currentEnvironment;
let _environmentTime;
/** @experimental */
async function setupBaseEnvironment(context) {
if (context.config.experimental.viteModuleRunner === false) {
const { setupNodeLoaderHooks } = await import('./native.cLbCmZDO.js');
await setupNodeLoaderHooks(context);
}
const startTime = performance.now();
const { environment: { name: environmentName, options: environmentOptions }, rpc, config } = context;
setupEnv(config.env, context.metaEnv);
const otel = context.traces;
const { environment, loader } = await loadEnvironment(environmentName, config.root, rpc, otel, context.config.experimental.viteModuleRunner);
_currentEnvironment = environment;
const env = await otel.$("vitest.runtime.environment.setup", { attributes: {
"vitest.environment": environment.name,
"vitest.environment.vite_environment": environment.viteEnvironment || environment.name
} }, () => environment.setup(globalThis, environmentOptions || config.environmentOptions || {}));
_environmentTime = performance.now() - startTime;
if (config.chaiConfig) setupChaiConfig(config.chaiConfig);
return async () => {
await otel.$("vitest.runtime.environment.teardown", () => env.teardown(globalThis));
await loader?.close();
};
}
/** @experimental */
async function runBaseTests(method, state, traces) {
const { ctx } = state;
state.environment = _currentEnvironment;
state.durations.environment = _environmentTime;
// state has new context, but we want to reuse existing ones
state.evaluatedModules = evaluatedModules;
state.moduleExecutionInfo = moduleExecutionInfo;
provideWorkerState(globalThis, state);
// we could load @vite/env, but it would take ~8ms, while this takes ~0,02ms
if (state.config.serializedDefines) try {
runInThisContext(`(() =>{\n${state.config.serializedDefines}})()`, {
lineOffset: 1,
filename: "virtual:load-defines.js"
});
} catch (error) {
throw new Error(`Failed to load custom "defines": ${error.message}`);
}
if (ctx.invalidates) ctx.invalidates.forEach((filepath) => {
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
ctx.files.forEach((i) => {
const filepath = i.filepath;
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
const moduleRunner = await startModuleRunner({
state,
evaluatedModules: state.evaluatedModules,
spyModule,
createImportMeta: createNodeImportMeta,
traces
});
emitModuleRunner(moduleRunner);
await run(method, ctx.files, ctx.config, moduleRunner, _currentEnvironment, traces);
}
export { runBaseTests as r, setupBaseEnvironment as s };
+64
View File
@@ -0,0 +1,64 @@
import { F as FileSpecification } from './config.d.CU_b-wJj.js';
import { O as OTELCarrier } from './rpc.d.DA9Utv4e.js';
import { T as TestExecutionMethod } from './worker.d.MLmnzOJE.js';
interface ModuleDefinitionLocation {
line: number;
column: number;
}
interface SourceModuleLocations {
modules: ModuleDefinitionDiagnostic[];
untracked: ModuleDefinitionDiagnostic[];
}
interface ModuleDefinitionDiagnostic {
start: ModuleDefinitionLocation;
end: ModuleDefinitionLocation;
startIndex: number;
endIndex: number;
rawUrl: string;
resolvedUrl: string;
resolvedId: string;
}
interface ModuleDefinitionDurationsDiagnostic extends ModuleDefinitionDiagnostic {
selfTime: number;
totalTime: number;
transformTime?: number;
external?: boolean;
importer?: string;
}
interface UntrackedModuleDefinitionDiagnostic {
url: string;
resolvedId: string;
resolvedUrl: string;
selfTime: number;
totalTime: number;
transformTime?: number;
external?: boolean;
importer?: string;
}
interface SourceModuleDiagnostic {
modules: ModuleDefinitionDurationsDiagnostic[];
untrackedModules: UntrackedModuleDefinitionDiagnostic[];
}
type SerializedTestSpecification = [project: {
name: string | undefined;
root: string;
}, file: string, options: {
pool: string;
testLines?: number[] | undefined;
testIds?: string[] | undefined;
testNamePattern?: RegExp | undefined;
testTagsFilter?: string[] | undefined;
}];
interface BrowserTesterOptions {
method: TestExecutionMethod;
files: FileSpecification[];
providedContext: string;
otelCarrier?: OTELCarrier;
concurrencyId: number;
workerId: number;
}
export type { BrowserTesterOptions as B, ModuleDefinitionDurationsDiagnostic as M, SerializedTestSpecification as S, UntrackedModuleDefinitionDiagnostic as U, ModuleDefinitionDiagnostic as a, ModuleDefinitionLocation as b, SourceModuleDiagnostic as c, SourceModuleLocations as d };
File diff suppressed because it is too large. Load diff
+528
View File
@@ -0,0 +1,528 @@
import { mkdirSync, writeFileSync } from 'node:fs';
import { C as CoverageProviderMap } from './coverage.BcXK1DsQ.js';
import { L as Logger, k as VitestPackageInstaller, P as PluginHarness, w as resolveConfig, V as Vitest, A as stdout, y as prompt, g as FilesNotFoundError, G as GitNotFoundError, I as IncludeTaskLocationDisabledError, C as RangeLocationFilterProvidedError, E as LocationFilterFileNotFoundError } from './index.B89dZ0-N.js';
import readline from 'node:readline';
import { y } from './tinyrainbow.Ht9iggcq.js';
import { i as isWindows } from './env.DzFJjrmK.js';
import { stripVTControlCharacters } from 'node:util';
import { e as createDefer, a as relative, r as resolve, d as dirname, i as isAbsolute } from './pathe.M-eThtNZ.Clq7AXhv.js';
async function createVitest(modeOrOptions, optionsOrViteOverrides = {}, viteOverridesOrVitestOptions = {}, maybeVitestOptions = {}) {
let options;
let viteOverrides;
let vitestOptions;
if (typeof modeOrOptions === "string") {
options = optionsOrViteOverrides;
viteOverrides = viteOverridesOrVitestOptions;
vitestOptions = maybeVitestOptions;
} else {
options = modeOrOptions;
viteOverrides = optionsOrViteOverrides;
vitestOptions = viteOverridesOrVitestOptions;
}
const logger = new Logger(vitestOptions.stdout, vitestOptions.stderr);
const packageInstaller = vitestOptions.packageInstaller ?? new VitestPackageInstaller();
const pluginHarness = new PluginHarness(logger, packageInstaller);
const config = await resolveConfig(options, viteOverrides, pluginHarness);
const vitest = new Vitest(pluginHarness, config);
try {
await vitest._start(config);
if (vitest.config.api.port && vitest.config.ui && vitest.config.open) vitest.vite.openBrowser();
return vitest;
}
// Vitest can fail at any point during setup or inside a custom plugin.
// Make sure everything is properly closed (like the logger).
catch (error) {
await vitest.close();
throw error;
}
}
const MAX_RESULT_COUNT = 10;
const SELECTION_MAX_INDEX = 7;
const ESC = "\x1B[";
class WatchFilter {
filterRL;
currentKeyword = void 0;
message;
results = [];
selectionIndex = -1;
onKeyPress;
stdin;
stdout;
constructor(message, stdin = process.stdin, stdout$1 = stdout()) {
this.message = message;
this.stdin = stdin;
this.stdout = stdout$1;
this.filterRL = readline.createInterface({
input: this.stdin,
escapeCodeTimeout: 50
});
readline.emitKeypressEvents(this.stdin, this.filterRL);
if (this.stdin.isTTY) this.stdin.setRawMode(true);
}
async filter(filterFunc) {
this.write(this.promptLine());
const resultPromise = createDefer();
this.onKeyPress = this.filterHandler(filterFunc, (result) => {
resultPromise.resolve(result);
});
this.stdin.on("keypress", this.onKeyPress);
try {
return await resultPromise;
} finally {
this.close();
}
}
filterHandler(filterFunc, onSubmit) {
return async (str, key) => {
switch (true) {
case key.sequence === "":
if (this.currentKeyword && this.currentKeyword?.length > 1) this.currentKeyword = this.currentKeyword?.slice(0, -1);
else this.currentKeyword = void 0;
break;
case key?.ctrl && key?.name === "c":
case key?.name === "escape":
this.write(`${ESC}1G${ESC}0J`);
onSubmit(void 0);
return;
case key?.name === "enter":
case key?.name === "return": {
const selection = this.results[this.selectionIndex];
onSubmit((typeof selection === "string" ? selection : selection?.key) || this.currentKeyword || "");
this.currentKeyword = void 0;
break;
}
case key?.name === "up":
if (this.selectionIndex && this.selectionIndex > 0) this.selectionIndex--;
else this.selectionIndex = -1;
break;
case key?.name === "down":
if (this.selectionIndex < this.results.length - 1) this.selectionIndex++;
else if (this.selectionIndex >= this.results.length - 1) this.selectionIndex = this.results.length - 1;
break;
case !key?.ctrl && !key?.meta: if (this.currentKeyword === void 0) this.currentKeyword = str;
else this.currentKeyword += str || "";
}
if (this.currentKeyword) this.results = await filterFunc(this.currentKeyword);
this.render();
};
}
render() {
let printStr = this.promptLine();
if (!this.currentKeyword) printStr += "\nPlease input filter pattern";
else if (this.currentKeyword && this.results.length === 0) printStr += "\nPattern matches no results";
else {
const resultCountLine = this.results.length === 1 ? `Pattern matches ${this.results.length} result` : `Pattern matches ${this.results.length} results`;
let resultBody = "";
if (this.results.length > MAX_RESULT_COUNT) {
const offset = this.selectionIndex > SELECTION_MAX_INDEX ? this.selectionIndex - SELECTION_MAX_INDEX : 0;
const displayResults = this.results.slice(offset, MAX_RESULT_COUNT + offset);
const remainingResultCount = this.results.length - offset - displayResults.length;
resultBody = `${displayResults.map((result, index) => index + offset === this.selectionIndex ? y.green(` › ${result}`) : y.dim(` › ${result}`)).join("\n")}`;
if (remainingResultCount > 0) resultBody += `
${y.dim(` ...and ${remainingResultCount} more ${remainingResultCount === 1 ? "result" : "results"}`)}`;
} else resultBody = this.results.map((result, index) => index === this.selectionIndex ? y.green(` › ${result}`) : y.dim(` › ${result}`)).join("\n");
printStr += `\n${resultCountLine}\n${resultBody}`;
}
this.eraseAndPrint(printStr);
this.restoreCursor();
}
keywordOffset() {
return `? ${this.message} › `.length + 1;
}
promptLine() {
return `${y.cyan("?")} ${y.bold(this.message)} › ${this.currentKeyword || ""}`;
}
eraseAndPrint(str) {
let rows = 0;
const lines = str.split(/\r?\n/);
for (const line of lines) {
const columns = "columns" in this.stdout ? this.stdout.columns : 80;
// We have to take care of screen width in case of long lines
rows += 1 + Math.floor(Math.max(stripVTControlCharacters(line).length - 1, 0) / columns);
}
this.write(`${ESC}1G`);
this.write(`${ESC}J`);
this.write(str);
this.write(`${ESC}${rows - 1}A`);
}
close() {
this.filterRL.close();
if (this.onKeyPress) this.stdin.removeListener("keypress", this.onKeyPress);
if (this.stdin.isTTY) this.stdin.setRawMode(false);
}
restoreCursor() {
const cursorPos = this.keywordOffset() + (this.currentKeyword?.length || 0);
this.write(`${ESC}${cursorPos}G`);
}
write(data) {
this.stdout.write(data);
}
getLastResults() {
return this.results.map((r) => typeof r === "string" ? r : r.toString());
}
}
const keys = [
[["a", "return"], "rerun all tests"],
["r", "rerun current pattern tests"],
["f", "rerun only failed tests"],
["u", "update snapshot"],
["p", "filter by a filename"],
["t", "filter by a test name regex pattern"],
["w", "filter by a project name"],
["q", "quit"]
];
const cancelKeys = [
"space",
"c",
"h",
...keys.map((key) => key[0]).flat()
];
function printShortcutsHelp() {
stdout().write(`
${y.bold(" Watch Usage")}
${keys.map((i) => y.dim(" press ") + y.reset([i[0]].flat().map(y.bold).join(", ")) + y.dim(` to ${i[1]}`)).join("\n")}
`);
}
function* traverseFilteredTestNames(parentName, filter, t) {
if (t.type === "test") {
if (t.name.match(filter)) {
const displayName = `${parentName} > ${t.name}`;
yield {
key: t.name,
toString: () => displayName
};
}
} else {
parentName = parentName.length ? `${parentName} > ${t.name}` : t.name;
for (const task of t.tasks) yield* traverseFilteredTestNames(parentName, filter, task);
}
}
function* getFilteredTestNames(pattern, suite) {
try {
const reg = new RegExp(pattern);
// TODO: we cannot run tests per workspace yet: filtering files
const files = /* @__PURE__ */ new Set();
for (const file of suite) if (!files.has(file.name)) {
files.add(file.name);
yield* traverseFilteredTestNames("", reg, file);
}
} catch {}
}
function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
let latestFilename = "";
async function _keypressHandler(str, key) {
// Cancel run and exit when ctrl-c or esc is pressed.
// If cancelling takes long and key is pressed multiple times, exit forcefully.
if (str === "" || str === "\x1B" || key && key.ctrl && key.name === "c") {
if (!ctx.isCancelling) {
ctx.logger.log(y.red("Cancelling test run. Press CTRL+c again to exit forcefully.\n"));
process.exitCode = 130;
// Unregister raw mode so that second CTRL+c is handled by Node.js as SIGINT
off();
await ctx.cancelCurrentRun("keyboard-input");
}
return ctx.exit(true);
}
// window not support suspend
if (!isWindows && key && key.ctrl && key.name === "z") {
process.kill(process.ppid, "SIGTSTP");
process.kill(process.pid, "SIGTSTP");
return;
}
const name = key?.name;
if (ctx.runningPromise) {
if (cancelKeys.includes(name)) await ctx.cancelCurrentRun("keyboard-input");
return;
}
// quit
if (name === "q") return ctx.exit(true);
// help
if (name === "h") return printShortcutsHelp();
// update snapshot
if (name === "u") return ctx.updateSnapshot();
// rerun all tests
if (name === "a" || name === "return") {
const files = await ctx._globTestFilepaths();
return ctx.changeNamePattern("", files, "rerun all tests");
}
// rerun current pattern tests
if (name === "r") return ctx.rerunFiles();
// rerun only failed tests
if (name === "f") return ctx.rerunFailed();
// change project filter
if (name === "w") return inputProjectName();
// change testNamePattern
if (name === "t") return inputNamePattern();
// change fileNamePattern
if (name === "p") return inputFilePattern();
}
async function keypressHandler(str, key) {
await _keypressHandler(str, key);
}
async function inputNamePattern() {
off();
const filter = await new WatchFilter("Input test name pattern (RegExp)", stdin, stdout).filter((str) => {
return [...getFilteredTestNames(str, ctx.state.getFiles())];
});
on();
if (typeof filter === "undefined") return;
const files = ctx.state.getFilepaths();
// if running in standalone mode, Vitest instance doesn't know about any test file
const cliFiles = ctx.config.standalone && !files.length ? await ctx._globTestFilepaths() : void 0;
await ctx.changeNamePattern(filter?.trim() || "", cliFiles, "change pattern");
}
async function inputProjectName() {
off();
const { filter = "" } = await prompt([{
name: "filter",
type: "text",
message: "Input a single project name",
initial: ctx.config.project[0] || ""
}]);
on();
await ctx.changeProjectName(filter.trim());
}
async function inputFilePattern() {
off();
const watchFilter = new WatchFilter("Input filename pattern", stdin, stdout);
const filter = await watchFilter.filter(async (str) => {
return (await ctx.globTestSpecifications([str])).map((specification) => relative(ctx.config.root, specification.moduleId)).filter((file, index, all) => all.indexOf(file) === index);
});
on();
if (typeof filter === "undefined") return;
latestFilename = filter?.trim() || "";
const lastResults = watchFilter.getLastResults();
await ctx.changeFilenamePattern(latestFilename, filter && lastResults.length ? lastResults.map((i) => resolve(ctx.config.root, i)) : void 0);
}
let rl;
function on() {
off();
rl = readline.createInterface({
input: stdin,
escapeCodeTimeout: 50
});
readline.emitKeypressEvents(stdin, rl);
if (stdin.isTTY) stdin.setRawMode(true);
stdin.on("keypress", keypressHandler);
}
function off() {
rl?.close();
rl = void 0;
stdin.removeListener("keypress", keypressHandler);
if (stdin.isTTY) stdin.setRawMode(false);
}
on();
return function cleanup() {
off();
};
}
async function startVitest(modeOrCliFilters, cliFiltersOrOptions, optionsOrViteOverrides, viteOverridesOrVitestOptions, maybeVitestOptions) {
let cliFilters;
let options;
let viteOverrides;
let vitestOptions;
if (typeof modeOrCliFilters === "string") {
cliFilters = cliFiltersOrOptions ?? [];
options = optionsOrViteOverrides ?? {};
viteOverrides = viteOverridesOrVitestOptions;
vitestOptions = maybeVitestOptions;
} else {
cliFilters = modeOrCliFilters ?? [];
options = cliFiltersOrOptions ?? {};
viteOverrides = optionsOrViteOverrides;
vitestOptions = viteOverridesOrVitestOptions;
}
const root = resolve(options.root || process.cwd());
const ctx = await prepareVitest(options, viteOverrides, vitestOptions, cliFilters);
if (ctx._coverageOptions.enabled) {
const provider = ctx._coverageOptions.provider || "v8";
const requiredPackages = CoverageProviderMap[provider];
if (requiredPackages) {
if (!await ctx.packageInstaller.ensureInstalled(requiredPackages, root, ctx.version)) {
process.exitCode = 1;
return ctx;
}
}
}
const stdin = vitestOptions?.stdin || process.stdin;
const stdout = vitestOptions?.stdout || process.stdout;
let stdinCleanup;
if (stdin.isTTY && ctx.config.watch) stdinCleanup = registerConsoleShortcuts(ctx, stdin, stdout);
ctx.onAfterSetServer(async () => {
if (ctx.closingPromise) return;
try {
if (ctx.config.standalone) await ctx.standalone();
else await ctx.start(cliFilters);
} catch (error) {
reportStartError(ctx, error);
}
});
try {
if (ctx.config.listTags) await ctx.listTags();
else if (ctx.config.clearCache) await ctx.clearCache();
else if (ctx.config.mergeReports) await ctx.mergeReports();
else if (ctx.config.standalone) await ctx.standalone();
else await ctx.start(cliFilters);
return ctx;
} catch (e) {
reportStartError(ctx, e);
return ctx;
} finally {
if (!ctx?.shouldKeepServer()) {
stdinCleanup?.();
await ctx.close();
}
}
}
function reportStartError(ctx, error) {
if (error instanceof FilesNotFoundError) return;
if (error instanceof GitNotFoundError) {
ctx.logger.error(error.message);
return;
}
if (error instanceof IncludeTaskLocationDisabledError || error instanceof RangeLocationFilterProvidedError || error instanceof LocationFilterFileNotFoundError) {
ctx.logger.printError(error, { verbose: false });
return;
}
process.exitCode = 1;
ctx.logger.printError(error, {
fullStack: true,
type: "Unhandled Error"
});
ctx.logger.error("\n\n");
}
async function prepareVitest(modeOrOptions, optionsOrViteOverrides, viteOverridesOrVitestOptions, vitestOptionsOrCliFilters, maybeCliFilters) {
let options;
let viteOverrides;
let vitestOptions;
let cliFilters;
if (typeof modeOrOptions === "string") {
options = optionsOrViteOverrides ?? {};
viteOverrides = viteOverridesOrVitestOptions;
vitestOptions = vitestOptionsOrCliFilters;
cliFilters = maybeCliFilters;
} else {
options = modeOrOptions ?? {};
viteOverrides = optionsOrViteOverrides;
vitestOptions = viteOverridesOrVitestOptions;
cliFilters = vitestOptionsOrCliFilters;
}
process.env.TEST = "true";
process.env.VITEST = "true";
process.env.NODE_ENV ??= "test";
if (options.run) options.watch = false;
if (options.standalone && (cliFilters?.length || 0) > 0) options.standalone = false;
// this shouldn't affect _application root_ that can be changed inside config
const root = resolve(options.root || process.cwd());
const ctx = await createVitest(options, viteOverrides, vitestOptions);
const environmentPackage = getEnvPackageName(ctx.config.environment);
if (environmentPackage && !await ctx.packageInstaller.ensureInstalled(environmentPackage, root)) {
process.exitCode = 1;
return ctx;
}
return ctx;
}
function processCollected(ctx, files, options) {
let errorsPrinted = false;
forEachSuite(files, (suite) => {
suite.errors().forEach((error) => {
errorsPrinted = true;
ctx.logger.printError(error, { project: suite.project });
});
});
if (errorsPrinted) return;
if (typeof options.json !== "undefined") return processJsonOutput(files, options);
return formatCollectedAsString(files).forEach((test) => console.log(test));
}
function outputFileList(files, options) {
if (typeof options.json !== "undefined") return outputJsonFileList(files, options);
formatFilesAsString(files, options).map((file) => console.log(file));
}
function outputJsonFileList(files, options) {
if (typeof options.json === "boolean") return console.log(JSON.stringify(formatFilesAsJSON(files), null, 2));
if (typeof options.json === "string") {
const jsonPath = resolve(options.root || process.cwd(), options.json);
mkdirSync(dirname(jsonPath), { recursive: true });
writeFileSync(jsonPath, JSON.stringify(formatFilesAsJSON(files), null, 2));
}
}
function formatFilesAsJSON(files) {
return files.map((file) => {
const result = { file: file.moduleId };
if (file.project.name) result.projectName = file.project.name;
return result;
});
}
function formatFilesAsString(files, options) {
return files.map((file) => {
let name = relative(options.root || process.cwd(), file.moduleId);
if (file.project.name) name = `[${file.project.name}] ${name}`;
return name;
});
}
function processJsonOutput(files, options) {
if (typeof options.json === "boolean") return console.log(JSON.stringify(formatCollectedAsJSON(files), null, 2));
if (typeof options.json === "string") {
const jsonPath = resolve(options.root || process.cwd(), options.json);
mkdirSync(dirname(jsonPath), { recursive: true });
writeFileSync(jsonPath, JSON.stringify(formatCollectedAsJSON(files), null, 2));
}
}
function forEachSuite(modules, callback) {
modules.forEach((testModule) => {
callback(testModule);
for (const suite of testModule.children.allSuites()) callback(suite);
});
}
function formatCollectedAsJSON(files) {
const results = [];
files.forEach((file) => {
for (const test of file.children.allTests()) {
if (test.result().state === "skipped") continue;
const result = {
name: test.fullName,
file: test.module.moduleId
};
if (test.project.name) result.projectName = test.project.name;
if (test.location) result.location = test.location;
results.push(result);
}
});
return results;
}
function formatCollectedAsString(testModules) {
const results = [];
testModules.forEach((testModule) => {
for (const test of testModule.children.allTests()) {
if (test.result().state === "skipped") continue;
const fullName = `${test.module.task.name} > ${test.fullName}`;
results.push((test.project.name ? `[${test.project.name}] ` : "") + fullName);
}
});
return results;
}
const envPackageNames = {
"jsdom": "jsdom",
"happy-dom": "happy-dom",
"edge-runtime": "@edge-runtime/vm"
};
function getEnvPackageName(env) {
if (env === "node") return null;
if (env in envPackageNames) return envPackageNames[env];
if (env[0] === "." || isAbsolute(env)) return null;
return `vitest-environment-${env}`;
}
var cliApi = /*#__PURE__*/Object.freeze({
__proto__: null,
outputFileList: outputFileList,
prepareVitest: prepareVitest,
processCollected: processCollected,
startVitest: startVitest
});
export { cliApi as a, createVitest as c, registerConsoleShortcuts as r, startVitest as s };
File diff suppressed because it is too large. Load diff
+149
View File
@@ -0,0 +1,149 @@
import { Console } from 'node:console';
import { relative } from 'node:path';
import { Writable } from 'node:stream';
import { a as getSafeTimers } from './source-map.Dx3YPjIe.js';
import { y } from './tinyrainbow.Ht9iggcq.js';
import { g as getWorkerState } from './utils.DYj33du9.js';
import './pathe.M-eThtNZ.Clq7AXhv.js';
const RealDate = globalThis.Date;
const UNKNOWN_TEST_ID = "__vitest__unknown_test__";
function getTaskIdByStack(root) {
const stack = (/* @__PURE__ */ new Error("STACK_TRACE_ERROR")).stack?.split("\n");
if (!stack) return UNKNOWN_TEST_ID;
const index = stack.findIndex((line) => line.includes("at Console.value"));
const line = index === -1 ? null : stack[index + 2];
if (!line) return UNKNOWN_TEST_ID;
const filepath = line.match(/at\s(.*)\s?/)?.[1];
if (filepath) return relative(root, filepath);
return UNKNOWN_TEST_ID;
}
function createCustomConsole(defaultState) {
const stdoutBuffer = /* @__PURE__ */ new Map();
const stderrBuffer = /* @__PURE__ */ new Map();
const timers = /* @__PURE__ */ new Map();
const { queueMicrotask } = getSafeTimers();
function queueCancelableMicrotask(callback) {
let canceled = false;
queueMicrotask(() => {
if (!canceled) callback();
});
return () => {
canceled = true;
};
}
const state = () => defaultState || getWorkerState();
// group sync console.log calls with micro task
function schedule(taskId) {
const timer = timers.get(taskId);
const { stdoutTime, stderrTime } = timer;
timer.cancel?.();
timer.cancel = queueCancelableMicrotask(() => {
if (stderrTime < stdoutTime) {
sendStderr(taskId);
sendStdout(taskId);
} else {
sendStdout(taskId);
sendStderr(taskId);
}
});
}
function sendStdout(taskId) {
sendBuffer("stdout", taskId);
}
function sendStderr(taskId) {
sendBuffer("stderr", taskId);
}
function sendBuffer(type, taskId) {
const buffers = type === "stdout" ? stdoutBuffer : stderrBuffer;
const buffer = buffers.get(taskId);
if (!buffer) return;
if (state().config.printConsoleTrace) buffer.forEach(([buffer, origin]) => {
sendLog(type, taskId, String(buffer), buffer.length, origin);
});
else sendLog(type, taskId, buffer.map((i) => String(i[0])).join(""), buffer.length);
const timer = timers.get(taskId);
buffers.delete(taskId);
if (type === "stderr") timer.stderrTime = 0;
else timer.stdoutTime = 0;
}
function sendLog(type, taskId, content, size, origin) {
const timer = timers.get(taskId);
const time = type === "stderr" ? timer.stderrTime : timer.stdoutTime;
state().rpc.onUserConsoleLog({
type,
content: content || "<empty line>",
taskId,
time: time || RealDate.now(),
size,
origin
});
}
const stdout = new Writable({ write(data, encoding, callback) {
const s = state();
const id = s?.current?.id || s?.current?.suite?.id || s.current?.file.id || getTaskIdByStack(s.config.root);
let timer = timers.get(id);
if (timer) timer.stdoutTime = timer.stdoutTime || RealDate.now();
else {
timer = {
stdoutTime: RealDate.now(),
stderrTime: 0
};
timers.set(id, timer);
}
let buffer = stdoutBuffer.get(id);
if (!buffer) {
buffer = [];
stdoutBuffer.set(id, buffer);
}
if (state().config.printConsoleTrace) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = limit + 6;
const trace = (/* @__PURE__ */ new Error("STACK_TRACE")).stack?.split("\n").slice(7).join("\n");
Error.stackTraceLimit = limit;
buffer.push([data, trace]);
} else buffer.push([data, void 0]);
schedule(id);
callback();
} });
const stderr = new Writable({ write(data, encoding, callback) {
const s = state();
const id = s?.current?.id || s?.current?.suite?.id || s.current?.file.id || getTaskIdByStack(s.config.root);
let timer = timers.get(id);
if (timer) timer.stderrTime = timer.stderrTime || RealDate.now();
else {
timer = {
stderrTime: RealDate.now(),
stdoutTime: 0
};
timers.set(id, timer);
}
let buffer = stderrBuffer.get(id);
if (!buffer) {
buffer = [];
stderrBuffer.set(id, buffer);
}
if (state().config.printConsoleTrace) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = limit + 6;
const stack = (/* @__PURE__ */ new Error("STACK_TRACE")).stack?.split("\n");
Error.stackTraceLimit = limit;
if (stack?.some((line) => line.includes("at Console.trace"))) buffer.push([data, void 0]);
else {
const trace = stack?.slice(7).join("\n");
Error.stackTraceLimit = limit;
buffer.push([data, trace]);
}
} else buffer.push([data, void 0]);
schedule(id);
callback();
} });
return new Console({
stdout,
stderr,
colorMode: y.isColorSupported,
groupIndentation: 2
});
}
export { createCustomConsole };
+38
View File
@@ -0,0 +1,38 @@
// if changed, update also jsdocs and docs
const defaultPort = 51204;
const defaultBrowserPort = 63315;
const defaultInspectPort = 9229;
const API_PATH = "/__vitest_api__";
const CONFIG_NAMES = ["vitest.config", "vite.config"];
const CONFIG_EXTENSIONS = [
".ts",
".mts",
".cts",
".js",
".mjs",
".cjs"
];
const configFiles = CONFIG_NAMES.flatMap((name) => CONFIG_EXTENSIONS.map((ext) => name + ext));
const globalApis = [
"suite",
"test",
"describe",
"it",
"chai",
"expect",
"assert",
"expectTypeOf",
"assertType",
"vitest",
"vi",
"beforeAll",
"afterAll",
"beforeEach",
"afterEach",
"onTestFinished",
"onTestFailed",
"aroundEach",
"aroundAll"
];
export { API_PATH as A, defaultPort as a, defaultInspectPort as b, configFiles as c, defaultBrowserPort as d, globalApis as g };
+33
View File
@@ -0,0 +1,33 @@
import { r as resolve } from './pathe.M-eThtNZ.Clq7AXhv.js';
function getCoverageFilesDirectory(reportsDirectory, shard) {
return resolve(reportsDirectory, `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`);
}
const CoverageProviderMap = {
v8: "@vitest/coverage-v8",
istanbul: "@vitest/coverage-istanbul"
};
async function resolveCoverageProviderModule(options, loader) {
if (!options?.enabled || !options.provider) return null;
const provider = options.provider;
if (provider === "v8" || provider === "istanbul") {
let builtInModule = CoverageProviderMap[provider];
if (loader.isBrowser) builtInModule += "/browser";
const { default: coverageModule } = loader.isBrowser ? await loader.import(builtInModule) : await import(
/* @vite-ignore */
builtInModule
);
if (!coverageModule) throw new Error(`Failed to load ${CoverageProviderMap[provider]}. Default export is missing.`);
return coverageModule;
}
let customProviderModule;
try {
customProviderModule = await loader.import(options.customProviderModule);
} catch (error) {
throw new Error(`Failed to load custom CoverageProviderModule from ${options.customProviderModule}`, { cause: error });
}
if (customProviderModule.default == null) throw new Error(`Custom CoverageProviderModule loaded from ${options.customProviderModule} was not the default export`);
return customProviderModule.default;
}
export { CoverageProviderMap as C, getCoverageFilesDirectory as g, resolveCoverageProviderModule as r };
+26
View File
@@ -0,0 +1,26 @@
import { r as resolveCoverageProviderModule } from './coverage.BcXK1DsQ.js';
async function startCoverageInsideWorker(options, loader, runtimeOptions) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.startCoverage?.({
...runtimeOptions,
autoAttachSubprocess: options.autoAttachSubprocess,
reportsDirectory: options.reportsDirectory
});
return null;
}
async function takeCoverageInsideWorker(options, loader) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.takeCoverage?.({
moduleExecutionInfo: loader.moduleExecutionInfo,
coverageFilesDirectory: options.coverageFilesDirectory
});
return null;
}
async function stopCoverageInsideWorker(options, loader, runtimeOptions) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.stopCoverage?.(runtimeOptions);
return null;
}
export { stopCoverageInsideWorker as a, startCoverageInsideWorker as s, takeCoverageInsideWorker as t };
+751
View File
@@ -0,0 +1,751 @@
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
import { mkdir, writeFile } from 'node:fs/promises';
import { resolve, dirname, relative } from 'node:path';
import { detectPackageManager, installPackage } from './index.DXQx-kDM.js';
import { y as prompt, z as findConfigFile } from './index.B89dZ0-N.js';
import { x } from 'tinyexec';
import { y } from './tinyrainbow.Ht9iggcq.js';
import 'node:process';
import 'node:os';
import './pathe.M-eThtNZ.Clq7AXhv.js';
import './source-map.Dx3YPjIe.js';
import './cac.D805sv8h.js';
import 'events';
import './env.DzFJjrmK.js';
import 'std-env';
import './constants.-juJ8b_4.js';
import 'node:assert';
import 'node:perf_hooks';
import './index.DmDMHCg8.js';
import 'https';
import 'http';
import 'net';
import 'tls';
import 'crypto';
import 'stream';
import 'url';
import 'zlib';
import 'buffer';
import 'util';
import 'vite';
import 'node:crypto';
import '../path.js';
import 'node:url';
import './run.CQOUYP-x.js';
import './index.M2dsQ_UQ.js';
import './display.pkpxlVcY.js';
import '../task-utils.js';
import './nativeModuleRunner.BOvBCgI_.js';
import 'node:module';
import 'node:v8';
import 'node:util';
import 'vite/module-runner';
import '../traces.js';
import 'obug';
import 'tinyglobby';
import './defaults.D2ip7f-X.js';
import 'magic-string';
import '@vitest/mocker/node';
import 'node:console';
import 'node:stream';
import './offset.Dy-5Fdfn.js';
import './utils.CtqmNjXD.js';
import '#module-evaluator';
import 'picomatch';
import './coverage.BcXK1DsQ.js';
import './resolver.CBCHRZ7j.js';
import 'es-module-lexer';
import 'node:tty';
import 'node:events';
import './modules.BJuCwlRJ.js';
import 'node:child_process';
import 'node:worker_threads';
import 'readline';
const jsxExample = {
name: "HelloWorld.jsx",
js: `
export default function HelloWorld({ name }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from '@testing-library/jsx'
import HelloWorld from './HelloWorld.<EXT>x'
test('renders name', async () => {
const { getByText } = await render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const vueExample = {
name: "HelloWorld.vue",
js: `
<script setup>
defineProps({
name: String
})
<\/script>
<template>
<div>
<h1>Hello {{ name }}!</h1>
</div>
</template>
`,
ts: `
<script setup lang="ts">
defineProps<{
name: string
}>()
<\/script>
<template>
<div>
<h1>Hello {{ name }}!</h1>
</div>
</template>
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-vue'
import HelloWorld from './HelloWorld.vue'
test('renders name', async () => {
const { getByText } = await render(HelloWorld, {
props: { name: 'Vitest' },
})
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const svelteExample = {
name: "HelloWorld.svelte",
js: `
<script>
export let name
<\/script>
<h1>Hello {name}!</h1>
`,
ts: `
<script lang="ts">
export let name: string
<\/script>
<h1>Hello {name}!</h1>
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-svelte'
import HelloWorld from './HelloWorld.svelte'
test('renders name', async () => {
const { getByText } = await render(HelloWorld, { name: 'Vitest' })
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const markoExample = {
name: "HelloWorld.marko",
js: `
class {
onCreate() {
this.state = { name: null }
}
}
<h1>Hello \${state.name}!</h1>
`,
ts: `
export interface Input {
name: string
}
<h1>Hello \${input.name}!</h1>
`,
test: `
import { expect, test } from 'vitest'
import { render } from '@marko/testing-library'
import HelloWorld from './HelloWorld.svelte'
test('renders name', async () => {
const { getByText } = await render(HelloWorld, { name: 'Vitest' })
const element = getByText('Hello Vitest!')
expect(element).toBeInTheDocument()
})
`
};
const litExample = {
name: "HelloWorld.js",
js: `
import { html, LitElement } from 'lit'
export class HelloWorld extends LitElement {
static properties = {
name: { type: String },
}
constructor() {
super()
this.name = 'World'
}
render() {
return html\`<h1>Hello \${this.name}!</h1>\`
}
}
customElements.define('hello-world', HelloWorld)
`,
ts: `
import { html, LitElement } from 'lit'
import { customElement, property } from 'lit/decorators.js'
@customElement('hello-world')
export class HelloWorld extends LitElement {
@property({ type: String })
name = 'World'
render() {
return html\`<h1>Hello \${this.name}!</h1>\`
}
}
declare global {
interface HTMLElementTagNameMap {
'hello-world': HelloWorld
}
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-lit'
import { html } from 'lit'
import './HelloWorld.js'
test('renders name', async () => {
const screen = render(html\`<hello-world name="Vitest"></hello-world>\`)
const element = screen.getByText('Hello Vitest!')
await expect.element(element).toBeInTheDocument()
})
`
};
const qwikExample = {
name: "HelloWorld.jsx",
js: `
import { component$ } from '@builder.io/qwik'
export default component$(({ name }) => {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
})
`,
ts: `
import { component$ } from '@builder.io/qwik'
export default component$(({ name }: { name: string }) => {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
})
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-qwik'
import HelloWorld from './HelloWorld.tsx'
test('renders name', async () => {
const { getByText } = render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const preactExample = {
name: "HelloWorld.jsx",
js: `
export default function HelloWorld({ name }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-preact'
import HelloWorld from './HelloWorld.<EXT>x'
test('renders name', async () => {
const { getByText } = render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const vanillaExample = {
name: "HelloWorld.js",
js: `
export default function HelloWorld({ name }) {
const parent = document.createElement('div')
const h1 = document.createElement('h1')
h1.textContent = 'Hello ' + name + '!'
parent.appendChild(h1)
return parent
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }): HTMLDivElement {
const parent = document.createElement('div')
const h1 = document.createElement('h1')
h1.textContent = 'Hello ' + name + '!'
parent.appendChild(h1)
return parent
}
`,
test: `
import { expect, test } from 'vitest'
import { getByText } from '@testing-library/dom'
import HelloWorld from './HelloWorld.js'
test('renders name', () => {
const parent = HelloWorld({ name: 'Vitest' })
document.body.appendChild(parent)
const element = getByText(parent, 'Hello Vitest!')
expect(element).toBeInTheDocument()
})
`
};
function getExampleTest(framework) {
switch (framework) {
case "solid": return {
...jsxExample,
test: jsxExample.test.replace("@testing-library/jsx", `@testing-library/${framework}`)
};
case "preact": return preactExample;
case "react": return {
...jsxExample,
test: jsxExample.test.replace("@testing-library/jsx", `vitest-browser-${framework}`)
};
case "vue": return vueExample;
case "svelte": return svelteExample;
case "lit": return litExample;
case "marko": return markoExample;
case "qwik": return qwikExample;
default: return vanillaExample;
}
}
async function generateExampleFiles(framework, lang) {
const example = getExampleTest(framework);
let fileName = example.name;
const folder = resolve(process.cwd(), "vitest-example");
const fileContent = example[lang];
if (!existsSync(folder)) await mkdir(folder, { recursive: true });
const isJSX = fileName.endsWith(".jsx");
if (isJSX && lang === "ts") fileName = fileName.replace(".jsx", ".tsx");
else if (fileName.endsWith(".js") && lang === "ts") fileName = fileName.replace(".js", ".ts");
example.test = example.test.replace("<EXT>", lang);
const filePath = resolve(folder, fileName);
const testPath = resolve(folder, `HelloWorld.test.${isJSX ? `${lang}x` : lang}`);
writeFileSync(filePath, fileContent.trimStart(), "utf-8");
writeFileSync(testPath, example.test.trimStart(), "utf-8");
return testPath;
}
// eslint-disable-next-line no-console
const log = console.log;
function getProviderOptions() {
return Object.entries({
playwright: "Playwright relies on Chrome DevTools protocol. Read more: https://playwright.dev",
webdriverio: "WebdriverIO uses WebDriver protocol. Read more: https://webdriver.io",
preview: "Preview is useful to quickly run your tests in the browser, but not suitable for CI."
}).map(([provider, description]) => {
return {
title: provider,
description,
value: provider
};
});
}
function getBrowserNames(provider) {
switch (provider) {
case "webdriverio": return [
"chrome",
"firefox",
"edge",
"safari"
];
case "playwright": return [
"chromium",
"firefox",
"webkit"
];
case "preview": return [
"chrome",
"firefox",
"safari"
];
}
}
function getFramework() {
return [
{
title: "vanilla",
value: "vanilla",
description: "No framework, just plain JavaScript or TypeScript."
},
{
title: "vue",
value: "vue",
description: "\"The Progressive JavaScript Framework\""
},
{
title: "svelte",
value: "svelte",
description: "\"Svelte: cybernetically enhanced web apps\""
},
{
title: "react",
value: "react",
description: "\"The library for web and native user interfaces\""
},
{
title: "lit",
value: "lit",
description: "\"A simple library for building fast, lightweight web components.\""
},
{
title: "preact",
value: "preact",
description: "\"Fast 3kB alternative to React with the same modern API\""
},
{
title: "solid",
value: "solid",
description: "\"Simple and performant reactivity for building user interfaces\""
},
{
title: "marko",
value: "marko",
description: "\"A declarative, HTML-based language that makes building web apps fun\""
},
{
title: "qwik",
value: "qwik",
description: "\"Instantly interactive web apps at scale\""
}
];
}
function getFrameworkTestPackage(framework) {
switch (framework) {
case "vanilla": return null;
case "vue": return "vitest-browser-vue";
case "svelte": return "vitest-browser-svelte";
case "react": return "vitest-browser-react";
case "lit": return "vitest-browser-lit";
case "preact": return "vitest-browser-preact";
case "solid": return "@solidjs/testing-library";
case "marko": return "@marko/testing-library";
case "qwik": return "vitest-browser-qwik";
}
throw new Error(`Unsupported framework: ${framework}`);
}
function getFrameworkPluginPackage(framework) {
switch (framework) {
case "vue": return "@vitejs/plugin-vue";
case "svelte": return "@sveltejs/vite-plugin-svelte";
case "react": return "@vitejs/plugin-react";
case "preact": return "@preact/preset-vite";
case "solid": return "vite-plugin-solid";
case "marko": return "@marko/vite";
case "qwik": return "@builder.io/qwik/optimizer";
}
return null;
}
function getLanguageOptions() {
return [{
title: "TypeScript",
description: "Use TypeScript.",
value: "ts"
}, {
title: "JavaScript",
description: "Use plain JavaScript.",
value: "js"
}];
}
async function installPackages(pkgManager, packages) {
if (!packages.length) {
log(y.green("✔"), y.bold("All packages are already installed."));
return;
}
log(y.cyan("◼"), y.bold("Installing packages..."));
log(y.cyan("◼"), packages.join(", "));
log();
await installPackage(packages, {
dev: true,
packageManager: pkgManager ?? void 0
});
}
function readPkgJson(path) {
if (!existsSync(path)) return null;
const content = readFileSync(path, "utf-8");
return JSON.parse(content);
}
function getPossibleDefaults(dependencies) {
return {
lang: "ts",
provider: getPossibleProvider(dependencies),
framework: getPossibleFramework(dependencies)
};
}
function getPossibleFramework(dependencies) {
if (dependencies.vue || dependencies["vue-tsc"] || dependencies["@vue/reactivity"]) return "vue";
if (dependencies.react || dependencies["react-dom"]) return "react";
if (dependencies.svelte || dependencies["@sveltejs/kit"]) return "svelte";
if (dependencies.lit || dependencies["lit-html"]) return "lit";
if (dependencies.preact) return "preact";
if (dependencies["solid-js"] || dependencies["@solidjs/start"]) return "solid";
if (dependencies.marko) return "marko";
if (dependencies["@builder.io/qwik"] || dependencies["@qwik.dev/core"]) return "qwik";
return "vanilla";
}
function getPossibleProvider(dependencies) {
if (dependencies.webdriverio || dependencies["@wdio/cli"] || dependencies["@wdio/config"]) return "webdriverio";
// playwright is the default recommendation
return "playwright";
}
function getProviderDocsLink(provider) {
switch (provider) {
case "playwright": return "https://vitest.dev/config/browser/playwright";
case "webdriverio": return "https://vitest.dev/config/browser/webdriverio";
}
}
function sort(choices, value) {
const index = choices.findIndex((i) => i.value === value);
if (index === -1) return choices;
return [choices.splice(index, 1)[0], ...choices];
}
function fail() {
process.exitCode = 1;
}
function getFrameworkImportInfo(framework) {
switch (framework) {
case "svelte": return {
importName: "svelte",
isNamedExport: true
};
case "qwik": return {
importName: "qwikVite",
isNamedExport: true
};
default: return {
importName: framework,
isNamedExport: false
};
}
}
async function generateFrameworkConfigFile(options) {
const { importName, isNamedExport } = getFrameworkImportInfo(options.framework);
const frameworkImport = isNamedExport ? `import { ${importName} } from '${options.frameworkPlugin}'` : `import ${importName} from '${options.frameworkPlugin}'`;
const configContent = [
`import { defineConfig } from 'vitest/config'`,
`import { ${options.provider} } from '@vitest/browser-${options.provider}'`,
options.frameworkPlugin ? frameworkImport : null,
``,
"export default defineConfig({",
options.frameworkPlugin ? ` plugins: [${importName}()],` : null,
` test: {`,
` browser: {`,
` enabled: true,`,
` provider: ${options.provider}(),`,
options.provider !== "preview" && ` // ${getProviderDocsLink(options.provider)}`,
` instances: [`,
...options.browsers.map((browser) => ` { browser: '${browser}' },`),
` ],`,
` },`,
` },`,
`})`,
""
].filter((t) => typeof t === "string").join("\n");
await writeFile(options.configPath, configContent);
}
async function updatePkgJsonScripts(pkgJsonPath, vitestScript) {
if (!existsSync(pkgJsonPath)) await writeFile(pkgJsonPath, `${JSON.stringify({ scripts: { "test:browser": vitestScript } }, null, 2)}\n`, "utf-8");
else {
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
pkg.scripts = pkg.scripts || {};
pkg.scripts["test:browser"] = vitestScript;
await writeFile(pkgJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
}
log(y.green("✔"), "Added \"test:browser\" script to your package.json.");
}
function getRunScript(pkgManager) {
switch (pkgManager) {
case "yarn@berry":
case "yarn": return "yarn test:browser";
case "pnpm@6":
case "pnpm": return "pnpm test:browser";
case "bun": return "bun test:browser";
default: return "npm run test:browser";
}
}
function getPlaywrightRunArgs(pkgManager) {
switch (pkgManager) {
case "yarn@berry":
case "yarn": return ["yarn", "exec"];
case "pnpm@6":
case "pnpm": return ["pnpx"];
case "bun": return ["bunx"];
default: return ["npx"];
}
}
async function create() {
log(y.cyan("◼"), "This utility will help you set up a browser testing environment.\n");
const pkgJsonPath = resolve(process.cwd(), "package.json");
const pkg = readPkgJson(pkgJsonPath) || {};
const dependencies = {
...pkg.dependencies,
...pkg.devDependencies
};
const defaults = getPossibleDefaults(dependencies);
const { lang } = await prompt({
type: "select",
name: "lang",
message: "Choose a language for your tests",
choices: sort(getLanguageOptions(), defaults?.lang)
});
if (!lang) return fail();
const { provider } = await prompt({
type: "select",
name: "provider",
message: "Choose a browser provider. Vitest will use its API to control the testing environment",
choices: sort(getProviderOptions(), defaults?.provider)
});
if (!provider) return fail();
const { browsers } = await prompt({
type: "multiselect",
name: "browsers",
message: "Choose a browser",
choices: getBrowserNames(provider).map((browser) => ({
title: browser,
value: browser
}))
});
if (!provider) return fail();
const { framework } = await prompt({
type: "select",
name: "framework",
message: "Choose your framework",
choices: sort(getFramework(), defaults?.framework)
});
if (!framework) return fail();
let installPlaywright = false;
if (provider === "playwright") ({installPlaywright} = await prompt({
type: "confirm",
name: "installPlaywright",
message: `Install Playwright browsers (can be done manually via 'pnpm exec playwright install')?`
}));
if (installPlaywright == null) return fail();
const dependenciesToInstall = [`@vitest/browser-${provider}`];
const frameworkPackage = getFrameworkTestPackage(framework);
if (frameworkPackage) dependenciesToInstall.push(frameworkPackage);
const frameworkPlugin = getFrameworkPluginPackage(framework);
if (frameworkPlugin) dependenciesToInstall.push(frameworkPlugin);
const pkgManager = await detectPackageManager();
log();
await installPackages(pkgManager, dependenciesToInstall.filter((pkg) => !dependencies[pkg]));
const rootConfig = findConfigFile(process.cwd());
let scriptCommand = "vitest";
log();
if (rootConfig) {
const configPath = resolve(dirname(rootConfig), `vitest.browser.config.${lang}`);
scriptCommand = `vitest --config=${relative(process.cwd(), configPath)}`;
await generateFrameworkConfigFile({
configPath,
framework,
frameworkPlugin,
provider,
browsers
});
log(
y.green("✔"),
"Created a new config file for browser tests:",
y.bold(relative(process.cwd(), configPath)),
// TODO: Can we modify the config ourselves?
"\nSince you already have a Vitest config file, it is recommended to copy the contents of the new file ",
"into your existing config located at ",
y.bold(relative(process.cwd(), rootConfig))
);
} else {
const configPath = resolve(process.cwd(), `vitest.config.${lang}`);
await generateFrameworkConfigFile({
configPath,
framework,
frameworkPlugin,
provider,
browsers
});
log(y.green("✔"), "Created a config file for browser tests:", y.bold(relative(process.cwd(), configPath)));
}
log();
await updatePkgJsonScripts(pkgJsonPath, scriptCommand);
if (installPlaywright) {
log();
const [command, ...args] = getPlaywrightRunArgs(pkgManager);
const allArgs = [
...args,
"playwright",
"install",
"--with-deps"
];
log(y.cyan("◼"), `Installing Playwright dependencies with \`${y.bold(command)} ${y.bold(allArgs.join(" "))}\`...`);
log();
await x(command, allArgs, { nodeOptions: { stdio: [
"pipe",
"inherit",
"inherit"
] } });
}
log();
const exampleTestFile = await generateExampleFiles(framework, lang);
log(y.green("✔"), "Created example test file in", y.bold(relative(process.cwd(), exampleTestFile)));
log(y.dim(" You can safely delete this file once you have written your own tests."));
log();
log(y.cyan("◼"), "All done! Run your tests with", y.bold(getRunScript(pkgManager)));
}
export { create };
+90
View File
@@ -0,0 +1,90 @@
import os__default from 'node:os';
import './env.DzFJjrmK.js';
import { isCI, isAgent } from 'std-env';
const defaultInclude = ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
const defaultExclude = ["**/node_modules/**", "**/.git/**"];
const benchmarkConfigDefaults = {
enabled: false,
include: ["**/*.{bench,benchmark}.?(c|m)[jt]s?(x)"],
exclude: defaultExclude,
includeSource: [],
retainSamples: false,
suppressExportGetterWarnings: false,
// Populated automatically when Vitest clones the parent project; the default
// here applies to the (unused) raw config that's never run as a benchmark.
projectName: ""
};
// These are the generic defaults for coverage. Providers may also set some provider specific defaults.
const coverageConfigDefaults = {
provider: "v8",
enabled: false,
clean: true,
cleanOnRerun: true,
reportsDirectory: "./coverage",
exclude: [],
reportOnFailure: false,
reporter: [
"text",
"html",
"clover",
"json"
],
allowExternal: false,
excludeAfterRemap: false,
processingConcurrency: Math.min(20, os__default.availableParallelism?.() ?? os__default.cpus().length),
ignoreClassMethods: [],
skipFull: false,
watermarks: {
statements: [50, 80],
functions: [50, 80],
branches: [50, 80],
lines: [50, 80]
},
autoAttachSubprocess: false
};
const fakeTimersDefaults = {
loopLimit: 1e4,
shouldClearNativeTimers: true
};
const configDefaults = Object.freeze({
allowOnly: !isCI,
isolate: true,
watch: !isCI && process.stdin.isTTY && !isAgent,
globals: false,
injectCjsGlobals: true,
environment: "node",
clearMocks: true,
restoreMocks: false,
mockReset: false,
unstubGlobals: false,
unstubEnvs: false,
include: defaultInclude,
exclude: defaultExclude,
teardownTimeout: 1e4,
forceRerunTriggers: ["**/package.json", "**/{vitest,vite}.config.*"],
update: false,
reporters: [isAgent ? "minimal" : "default", ...process.env.GITHUB_ACTIONS === "true" ? ["github-actions"] : []],
silent: false,
hideSkippedTests: false,
api: false,
ui: false,
uiBase: "/__vitest__/",
open: !isCI,
css: { include: [] },
coverage: coverageConfigDefaults,
fakeTimers: fakeTimersDefaults,
maxConcurrency: 5,
dangerouslyIgnoreUnhandledErrors: false,
typecheck: {
checker: "tsc",
include: ["**/*.{test,spec}-d.?(c|m)[jt]s?(x)"],
exclude: defaultExclude
},
slowTestThreshold: 300,
taskTitleValueFormatTruncate: 40,
disableConsoleIntercept: false,
detectAsyncLeaks: false
});
export { coverageConfigDefaults as a, defaultInclude as b, configDefaults as c, defaultExclude as d, benchmarkConfigDefaults as e };
+194
View File
@@ -0,0 +1,194 @@
import { f as format$1, p as plugins, c as createDOMElementFilter } from './index.M2dsQ_UQ.js';
const { AsymmetricMatcher, DOMCollection, DOMElement, Immutable, ReactElement, ReactTestComponent } = plugins;
const PLUGINS = [
ReactTestComponent,
ReactElement,
DOMElement,
DOMCollection,
Immutable,
AsymmetricMatcher
];
function stringify(object, maxDepth = 10, { maxLength, filterNode, ...options } = {}) {
const MAX_LENGTH = maxLength ?? 1e4;
let result;
// Convert string selector to filter function
const filterFn = typeof filterNode === "string" ? createNodeFilterFromSelector(filterNode) : filterNode;
const plugins = filterFn ? [
ReactTestComponent,
ReactElement,
createDOMElementFilter(filterFn),
DOMCollection,
Immutable,
AsymmetricMatcher
] : PLUGINS;
try {
result = format$1(object, {
maxDepth,
escapeString: false,
// min: true,
plugins,
...options
});
} catch {
result = format$1(object, {
callToJSON: false,
maxDepth,
escapeString: false,
// min: true,
plugins,
...options
});
}
// Prevents infinite loop https://github.com/vitest-dev/vitest/issues/7249
return result.length >= MAX_LENGTH && maxDepth > 1 ? stringify(object, Math.floor(Math.min(maxDepth, Number.MAX_SAFE_INTEGER) / 2), {
maxLength,
filterNode,
...options
}) : result;
}
function createNodeFilterFromSelector(selector) {
const ELEMENT_NODE = 1;
const COMMENT_NODE = 8;
return (node) => {
// Filter out comments
if (node.nodeType === COMMENT_NODE) return false;
// Filter out elements matching the selector
if (node.nodeType === ELEMENT_NODE && node.matches) try {
return !node.matches(selector);
} catch {
return true;
}
return true;
};
}
const formatRegExp = /%[sdjifoOc%]/g;
function format(args, options = {}) {
const formatArg = (item) => inspect(item, options);
if (typeof args[0] !== "string") {
const objects = [];
for (let i = 0; i < args.length; i++) objects.push(formatArg(args[i]));
return objects.join(" ");
}
const len = args.length;
let i = 1;
const template = args[0];
let str = String(template).replace(formatRegExp, (x) => {
if (x === "%%") return "%";
if (i >= len) return x;
switch (x) {
case "%s": {
const value = args[i++];
if (typeof value === "bigint") return `${value.toString()}n`;
if (typeof value === "number" && value === 0 && 1 / value < 0) return "-0";
if (typeof value === "object" && value !== null) {
if (typeof value.toString === "function" && value.toString !== Object.prototype.toString) return value.toString();
return formatArg(value);
}
return String(value);
}
case "%d": {
const value = args[i++];
if (typeof value === "bigint") return `${value.toString()}n`;
if (typeof value === "symbol") return "NaN";
return Number(value).toString();
}
case "%i": {
const value = args[i++];
if (typeof value === "bigint") return `${value.toString()}n`;
return Number.parseInt(String(value)).toString();
}
case "%f": return Number.parseFloat(String(args[i++])).toString();
case "%o":
case "%O": return formatArg(args[i++]);
case "%c":
i++;
return "";
case "%j": try {
return JSON.stringify(args[i++]);
} catch (err) {
const m = err.message;
if (m.includes("circular structure") || m.includes("cyclic structures") || m.includes("cyclic object")) return "[Circular]";
throw err;
}
default: return x;
}
});
for (let x = args[i]; i < len; x = args[++i]) if (x === null || typeof x !== "object") str += ` ${typeof x === "symbol" ? x.toString() : x}`;
else str += ` ${formatArg(x)}`;
return str;
}
function inspect(obj, options) {
const { truncate, multiline, ...stringifyOptions } = options ?? {};
const prettyFormatOptions = {
singleQuote: true,
quoteKeys: false,
min: true,
spacingInner: " ",
spacingOuter: " ",
printBasicPrototype: false,
compareKeys: null,
...multiline ? {
min: false,
spacingInner: void 0,
spacingOuter: void 0
} : {}
};
const threshold = truncate ?? 0;
const formatted = stringify(obj, void 0, {
...prettyFormatOptions,
...stringifyOptions,
maxLength: threshold || void 0
});
if (threshold === 0 || formatted.length <= threshold) return formatted;
// if stringify's adaptive maxDepth (down to 1) fails to truncate enough,
// - for known types (e.g. string, object, array, etc), apply best effort truncation.
// - for other values, fallback to maxDepth = 0 which should can show minimal output.
const type = Object.prototype.toString.call(obj);
if (typeof obj === "string") {
let end = threshold - 1;
if (end > 0 && isHighSurrogate(formatted[end - 1])) end = end - 1;
return `'${formatted.slice(1, end)}…'`;
}
if (type === "[object Array]" || type === "[object Object]" || type === "[object Set]" || type === "[object Map]") return stringifyByMaxWidth(obj, threshold, {
...prettyFormatOptions,
...stringifyOptions,
maxDepth: 1
});
return stringify(obj, void 0, {
...prettyFormatOptions,
...stringifyOptions,
maxDepth: 0
});
}
function truncateString(string, maxLength) {
if (string.length <= maxLength) return string;
let end = maxLength - 1;
if (isHighSurrogate(string[end - 1])) end = end - 1;
return `${string.slice(0, end)}…`;
}
function stringifyByMaxWidth(object, threshold, options) {
function evaluate(x) {
return stringify(object, void 0, {
...options,
maxWidth: x
});
}
return evaluate(binarySearch(0, threshold, (x) => evaluate(x).length <= threshold));
}
// find max(x \in [x, y) | f(x) = true)
// if f(x0) is false, then returns x0.
function binarySearch(x0, x1, f) {
while (x0 + 1 < x1) {
const x = Math.floor((x0 + x1) / 2);
if (f(x)) x0 = x;
else x1 = x;
}
return x0;
}
// https://github.com/chaijs/loupe/pull/79
function isHighSurrogate(char) {
return char >= "\ud800" && char <= "\udbff";
}
export { formatRegExp as a, format as f, inspect as i, stringify as s, truncateString as t };
+465
View File
@@ -0,0 +1,465 @@
import { spawn } from 'node:child_process';
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
import { availableParallelism, tmpdir } from 'node:os';
import { join } from 'node:path';
import { performance } from 'node:perf_hooks';
import { pathToFileURL } from 'node:url';
import { t as toArray, r as resolve } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { y } from './tinyrainbow.Ht9iggcq.js';
import { distDir } from '../path.js';
const DOM_ENVIRONMENTS = /* @__PURE__ */ new Set(["jsdom", "happy-dom"]);
/**
* Builds the list of configurations worth measuring for this config. The
* candidates are pruned by what the config already uses - there is no point
* in measuring `threads` when the config already runs in `threads`.
*/
function resolveDoctorCandidates(projects, options = {}) {
const candidates = [];
// `pool`, `environment` and the fs module cache don't reach the browser
// runtime, so those candidates are driven by the node-side projects only;
// isolation applies to browser projects as well
const testProjects = projects.filter((project) => !project.browser);
const pools = new Set(testProjects.map((project) => project.pool));
const usesVmPool = pools.has("vmThreads") || pools.has("vmForks");
const runsDom = testProjects.some((project) => DOM_ENVIRONMENTS.has(project.environment));
const isolates = projects.some((project) => project.isolate && (project.browser || project.pool !== "vmThreads" && project.pool !== "vmForks"));
if (pools.has("forks")) candidates.push({
id: "threads",
title: `pool: 'threads'`,
overrides: { pool: "threads" },
configLines: [`pool: 'threads'`],
preservesIsolation: true
});
if (runsDom && !pools.has("vmThreads")) candidates.push({
id: "vmThreads",
title: `pool: 'vmThreads'`,
overrides: { pool: "vmThreads" },
configLines: [`pool: 'vmThreads'`],
preservesIsolation: true
});
// vmForks trades vmThreads' worker threads for child processes: each child
// gets its own heap and GC, which can beat vmThreads on GC-heavy suites, and
// it is the vm option for suites that cannot run in worker threads
if (runsDom && !pools.has("vmForks")) candidates.push({
id: "vmForks",
title: `pool: 'vmForks'`,
overrides: { pool: "vmForks" },
configLines: [`pool: 'vmForks'`],
preservesIsolation: true
});
// jsdom -> happy-dom is the only swap with a speed upside; it is applied per
// project, so projects running other environments keep them
if (options.happyDomAvailable && testProjects.some((project) => project.environment === "jsdom")) candidates.push({
id: "happy-dom",
title: `environment: 'happy-dom'`,
overrides: {},
envSwap: {
from: "jsdom",
to: "happy-dom"
},
configLines: [`environment: 'happy-dom'`],
preservesIsolation: true
});
if (isolates) candidates.push({
id: "no-isolate",
title: "isolate: false",
overrides: { isolate: false },
configLines: ["isolate: false"],
preservesIsolation: false,
validateIsolation: true
});
if (usesVmPool)
// the honest competitor of a vm pool: reused workers with shared state
candidates.push({
id: "threads-no-isolate",
title: `pool: 'threads' + isolate: false`,
overrides: {
pool: "threads",
isolate: false
},
configLines: [`pool: 'threads'`, "isolate: false"],
preservesIsolation: false,
validateIsolation: true
});
if (testProjects.length > 0 && testProjects.every((project) => !project.fsModuleCache))
// an untimed priming run populates the cache first: the candidate measures
// what repeated runs pay, which is what doctor compares everywhere else
candidates.push({
id: "fs-cache",
title: "fsModuleCache: true",
overrides: { fsModuleCache: true },
configLines: ["fsModuleCache: true"],
preservesIsolation: true,
primeRuns: 1
});
return candidates;
}
function log(...args) {
console.log(...args);
}
async function doctor(cliFilters, options) {
const { prepareVitest } = await import('./cli-api.LUtK11-x.js').then(function (n) { return n.a; });
log();
log(y.inverse(y.bold(y.blue(" DOCTOR "))), "resolving the current configuration...");
const ctx = await prepareVitest({
...options,
watch: false,
run: true
}, void 0, void 0, cliFilters);
const projects = ctx.projects.map((project) => ({
name: project.name,
pool: project.config.pool,
environment: project.config.environment,
isolate: project.config.isolate,
browser: project.config.browser.enabled,
fsModuleCache: project.config.fsModuleCache === true
}));
const fileCount = (await ctx.getRelevantTestSpecifications(cliFilters)).length;
const configuredMaxWorkers = ctx.config.maxWorkers;
const effectiveMaxWorkers = typeof configuredMaxWorkers === "number" && configuredMaxWorkers > 0 ? configuredMaxWorkers : Math.max(1, availableParallelism() - 1);
await ctx.close();
// the environments import 'happy-dom' relative to the vitest package (it is
// a peer dependency), so resolving from here mirrors what a run would do
let happyDomAvailable = false;
try {
import.meta.resolve("happy-dom");
happyDomAvailable = true;
} catch {}
const candidates = resolveDoctorCandidates(projects, { happyDomAvailable });
const baselineTitle = `baseline (${describeProjects(projects)})`;
// every measured run goes through one generated runner script: candidates
// may need per-project overrides (the happy-dom swap), which the CLI cannot
// express, and routing the baseline through the same entry keeps the
// process cost identical across measurements
const runnerDir = mkdtempSync(join(tmpdir(), "vitest-doctor-"));
const runnerPath = join(runnerDir, "runner.mjs");
writeFileSync(runnerPath, createRunnerScript());
process.once("exit", () => rmSync(runnerDir, {
recursive: true,
force: true
}));
const childProjects = toArray(options.project).map(String);
const childOptions = {
watch: false,
...options.root ? { root: String(options.root) } : {},
...options.config ? { config: String(options.config) } : {},
...childProjects.length ? { project: childProjects } : {}
};
// make sure an interrupted doctor doesn't leave a test suite running
let activeChild;
const killActiveChild = () => {
activeChild?.kill("SIGKILL");
process.exit(130);
};
process.once("SIGINT", killActiveChild);
process.once("SIGTERM", killActiveChild);
const runVitest = (run, timeoutMs) => {
return new Promise((resolve) => {
const start = performance.now();
const payload = JSON.stringify({
filters: cliFilters,
options: {
...childOptions,
...run.overrides
},
envSwap: run.envSwap
});
const child = spawn(process.execPath, [runnerPath, payload], {
env: {
...process.env,
NO_COLOR: "1"
},
stdio: [
"ignore",
"ignore",
"pipe"
]
});
activeChild = child;
let timedOut = false;
const timer = timeoutMs ? setTimeout(() => {
timedOut = true;
child.kill("SIGKILL");
}, timeoutMs) : void 0;
let stderr = "";
child.stderr.on("data", (chunk) => {
stderr = (stderr + String(chunk)).slice(-4e3);
});
child.on("close", (code) => {
if (timer) clearTimeout(timer);
activeChild = void 0;
if (timedOut) stderr += `\n(the run was killed after exceeding ${Math.round((timeoutMs || 0) / 1e3)}s - several times the baseline duration)`;
resolve({
wall: performance.now() - start,
ok: code === 0 && !timedOut,
stderr
});
});
});
};
log();
if (candidates.length === 0) log("The configuration already uses the fastest setup Vitest knows how to compare - measuring it for reference.");
else {
log("Measuring alternative configurations by running the test suite under each of them.");
log(y.dim("Close other heavy programs - the comparison is only as good as the machine is quiet."));
}
log();
// The first baseline run also warms up caches for every following run, so
// candidates are compared in a warm steady state.
process.stdout.write(y.dim(` measuring ${baselineTitle}...`));
const firstRun = await runVitest({});
if (!firstRun.ok) {
process.stdout.write("\n");
log(y.red("The test suite fails with the current configuration. Fix the failures first - doctor needs a green suite to compare configurations."));
if (firstRun.stderr.trim()) log(y.dim(firstRun.stderr.trim()));
process.exitCode = 1;
return;
}
// scale repetitions to the suite duration: short suites are noisy and can
// afford more runs, long suites cannot
const reps = firstRun.wall < 1e4 ? 3 : firstRun.wall < 6e4 ? 2 : 1;
// the first run is cold and mostly warms up caches: always take at least one
// more baseline run so the reported baseline is a warm one, like the
// candidates that run after it
const baselineRuns = Math.max(reps, 2);
const baselineWalls = [firstRun.wall];
for (let i = 1; i < baselineRuns; i++) {
const run = await runVitest({});
if (!run.ok) {
process.stdout.write("\n");
log(y.red(`The test suite passed once but failed on repetition ${i + 1} with the same configuration - doctor needs a stable green suite to compare configurations.`));
if (run.stderr.trim()) log(y.dim(run.stderr.trim()));
process.exitCode = 1;
return;
}
baselineWalls.push(run.wall);
}
const baselineWall = Math.min(...baselineWalls);
process.stdout.write(` ${formatSeconds(baselineWall)}\n`);
// a candidate that takes several times the baseline is a regression, not a
// recommendation - don't let it stall doctor indefinitely
const candidateTimeout = Math.max(6e4, baselineWall * 4);
const measured = [];
for (const candidate of candidates) {
process.stdout.write(y.dim(` measuring ${candidate.title}...`));
let wall = Number.POSITIVE_INFINITY;
let ok = true;
let stderr = "";
const candidateRun = {
overrides: candidate.overrides,
envSwap: candidate.envSwap
};
for (let i = 0; i < (candidate.primeRuns ?? 0) && ok; i++) {
const run = await runVitest(candidateRun, candidateTimeout);
ok = run.ok;
stderr = run.stderr;
}
for (let i = 0; i < reps && ok; i++) {
const run = await runVitest(candidateRun, candidateTimeout);
wall = Math.min(wall, run.wall);
ok = run.ok;
stderr = run.stderr;
}
let isolationVerdict;
if (ok && candidate.validateIsolation) {
// tests can pass under shared state by accident of ordering: shuffle the
// file order twice to catch the common cross-file couplings; the explicit
// distinct seeds guarantee two different orders even when the user pinned
// `sequence.seed` in their config
isolationVerdict = "passed";
for (let i = 0; i < 2; i++) {
const run = await runVitest({
overrides: {
...candidate.overrides,
sequence: {
shuffle: { files: true },
seed: 271828 + i
}
},
envSwap: candidate.envSwap
}, candidateTimeout);
if (!run.ok) {
isolationVerdict = "failed";
stderr = run.stderr;
break;
}
}
}
measured.push({
candidate,
wall,
ok,
stderr,
isolationVerdict
});
process.stdout.write(ok ? ` ${formatSeconds(wall)}\n` : ` ${y.red("failed")}\n`);
}
const viable = measured.filter((result) => result.ok && result.isolationVerdict !== "failed" && result.wall < baselineWall * .9);
// among candidates close to the fastest, prefer the one that keeps per-file
// isolation - equal speed with stronger guarantees wins
const fastest = viable.length > 0 ? viable.reduce((a, b) => b.wall < a.wall ? b : a) : void 0;
const preferred = fastest ? viable.filter((result) => result.wall <= fastest.wall * 1.05).sort((a, b) => Number(b.candidate.preservesIsolation) - Number(a.candidate.preservesIsolation))[0] : void 0;
// Past a certain worker count the single main-thread Vite server becomes the
// bottleneck, so FEWER workers can be faster. Greedily descend by halving on
// top of the winning configuration, keeping a step only when it is a real
// (>= 5%) improvement.
const workerStack = preferred ? {
overrides: preferred.candidate.overrides,
envSwap: preferred.candidate.envSwap
} : {};
const workerSuffix = preferred ? ` (with ${preferred.candidate.title})` : "";
const workerProbes = [];
let bestWorkers;
{
let currentBest = preferred ? preferred.wall : baselineWall;
let probe = Math.floor(effectiveMaxWorkers / 2);
while (probe >= 2 && probe < fileCount) {
process.stdout.write(y.dim(` measuring maxWorkers: ${probe}${workerSuffix}...`));
let wall = Number.POSITIVE_INFINITY;
let ok = true;
for (let i = 0; i < reps && ok; i++) {
const run = await runVitest({
overrides: {
...workerStack.overrides,
maxWorkers: probe
},
envSwap: workerStack.envSwap
}, candidateTimeout);
wall = Math.min(wall, run.wall);
ok = run.ok;
}
process.stdout.write(ok ? ` ${formatSeconds(wall)}\n` : ` ${y.red("failed")}\n`);
if (!ok) break;
workerProbes.push({
workers: probe,
wall
});
if (wall >= currentBest * .95) break;
currentBest = wall;
bestWorkers = {
workers: probe,
wall
};
probe = Math.floor(probe / 2);
}
}
log();
log(y.bold("Results") + y.dim(` (min of ${reps} run${reps === 1 ? "" : "s"} each)`));
log();
const rowTitles = [...measured.map((m) => m.candidate.title), ...workerProbes.map((p) => `maxWorkers: ${p.workers}${workerSuffix}`)];
const width = Math.max(baselineTitle.length, ...rowTitles.map((title) => title.length)) + 2;
log(` ${baselineTitle.padEnd(width)}${formatSeconds(baselineWall)}`);
for (const result of measured) {
const title = result.candidate.title.padEnd(width);
if (!result.ok) log(` ${title}${y.red("failed")}`);
else if (result.isolationVerdict === "failed") log(` ${title}${formatSeconds(result.wall)} ${y.red("(fails with a shuffled file order - tests depend on isolation)")}`);
else log(` ${title}${formatSeconds(result.wall)} ${formatDelta(result.wall, baselineWall)}`);
}
for (const probeResult of workerProbes) log(` ${`maxWorkers: ${probeResult.workers}${workerSuffix}`.padEnd(width)}${formatSeconds(probeResult.wall)} ${formatDelta(probeResult.wall, baselineWall)}`);
// failing candidates are as informative as fast ones: show what broke,
// so "vmThreads is not an option for this suite" comes with the reason
for (const result of measured) if (!result.ok) logFailureExcerpt(`${result.candidate.title} failed with:`, result.stderr);
else if (result.isolationVerdict === "failed") logFailureExcerpt(`${result.candidate.title} failed with a shuffled file order:`, result.stderr);
log();
if (!preferred && !bestWorkers) {
if (candidates.length === 0) log(`${y.bold("Recommendation:")} keep the current configuration (${describeProjects(projects)}) - it measured ${y.yellow(formatSeconds(baselineWall))} and Vitest has no faster candidate to suggest for it.`);
else log(`${y.bold("Recommendation:")} keep the current configuration (${describeProjects(projects)}) - no measured candidate was more than 10% faster.`);
return;
}
const finalWall = bestWorkers ? bestWorkers.wall : preferred.wall;
const recommendationTitle = [preferred?.candidate.title, bestWorkers ? `maxWorkers: ${bestWorkers.workers}` : void 0].filter(Boolean).join(" + ");
const configLines = [...preferred ? preferred.candidate.configLines : [], ...bestWorkers ? [`maxWorkers: ${bestWorkers.workers}`] : []];
log(`${y.bold("Recommendation:")} ${y.yellow(recommendationTitle)} ${formatDelta(finalWall, baselineWall)}`);
log();
log(y.dim(" // vitest.config.ts"));
log(y.dim(` import { defineConfig } from 'vitest/config'`));
log();
log(y.dim(" export default defineConfig({"));
log(y.dim(" test: {"));
for (const [index, line] of configLines.entries()) log(` ${line},${index === configLines.length - 1 ? y.dim(` // measured ${Math.round((finalWall - baselineWall) / baselineWall * 100)}% on this suite`) : ""}`);
log(y.dim(" },"));
log(y.dim(" })"));
log();
if (preferred) for (const note of candidateNotes(preferred)) log(y.dim(` ${note}`));
if (bestWorkers) {
log(y.dim(` Fewer workers can be faster because every worker funnels transforms through the`));
log(y.dim(` single main-thread Vite server; a lower count also reduces memory pressure.`));
}
if (projects.length > 1) {
const note = preferred?.candidate.envSwap ? `Doctor swapped the environment of every project running ${preferred.candidate.envSwap.from}; other projects were left unchanged.` : "Doctor overrides options for all projects at once; apply the change per project if they need different settings.";
log(y.dim(` ${note}`));
}
log(y.dim(" Trade-offs of every option: https://vitest.dev/guide/improving-performance"));
}
/**
* The measured runs execute this script instead of the CLI binary: it accepts
* the overrides as JSON and can apply per-project changes (the environment
* swap) that CLI flags cannot express. `createVitest` attaches the projects,
* so the swap mutates each resolved project config before the run starts.
*/
function createRunnerScript() {
const nodeEntry = pathToFileURL(resolve(distDir, "node.js")).href;
return `import { createVitest } from ${JSON.stringify(nodeEntry)}
const { filters, options, envSwap } = JSON.parse(process.argv[2])
const ctx = await createVitest(options)
if (envSwap) {
for (const project of ctx.projects) {
if (!project.config.browser.enabled && project.config.environment === envSwap.from) {
project.config.environment = envSwap.to
}
}
}
try {
await ctx.start(filters)
}
catch (error) {
console.error(error?.stack || String(error))
process.exitCode = 1
}
await ctx.close()
process.exit(process.exitCode || 0)
`;
}
function logFailureExcerpt(title, stderr) {
log();
log(` ${y.red(title)}`);
if (!stderr.trim()) {
log(y.dim(" (the run produced no error output - rerun with the same options to inspect it)"));
return;
}
const lines = stderr.trim().split("\n");
const excerpt = lines.slice(-15);
if (lines.length > excerpt.length) log(y.dim(` … (last ${excerpt.length} lines)`));
for (const line of excerpt) log(y.dim(` ${line}`));
}
function candidateNotes(result) {
switch (result.candidate.id) {
case "vmThreads":
case "vmForks": return [`vm pools keep per-file isolation but run test code in a VM context: cross-realm`, `instanceof edge cases and higher memory usage are possible (see vmMemoryLimit).`];
case "no-isolate":
case "threads-no-isolate": return [`The suite passed twice with a shuffled file order under shared state, so it is`, `likely - but not guaranteed - that no test depends on isolation.`];
case "happy-dom": return [
`The swap was applied only to projects running jsdom; other projects kept their`,
`environment. happy-dom implements the DOM differently than jsdom: the suite`,
`passed under it, but double-check tests that depend on layout, navigation or`,
`other DOM edge cases.`
];
case "fs-cache": return [`The fs module cache persists transformed modules on disk: repeated runs skip`, `the transforms, the first run after a file change still pays them.`];
default: return [];
}
}
function describeProjects(projects) {
return `pool: ${[...new Set(projects.map((project) => project.browser ? "browser" : project.pool))].join(", ")} · isolate: ${projects.some((project) => project.isolate)}`;
}
function formatSeconds(time) {
return `${(time / 1e3).toFixed(2)}s`;
}
function formatDelta(wall, baseline) {
const delta = Math.round((wall - baseline) / baseline * 100);
if (delta === 0) return y.dim("(±0%)");
return delta < 0 ? y.green(`(${delta}%)`) : y.yellow(`(+${delta}%)`);
}
export { doctor, resolveDoctorCandidates };
+9
View File
@@ -0,0 +1,9 @@
import { isCI } from 'std-env';
const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
const isWindows = (isNode || isDeno) && process.platform === "win32";
const isTTY = (isNode || isDeno) && process.stdout?.isTTY && !isCI;
const isForceColor = () => "FORCE_COLOR" in process.env;
export { isForceColor as a, isTTY as b, isWindows as i };
+37
View File
@@ -0,0 +1,37 @@
import { A as Awaitable } from './config.d.CU_b-wJj.js';
interface EnvironmentReturn {
teardown: (global: any) => Awaitable<void>;
}
interface VmEnvironmentReturn {
getVmContext: () => {
[key: string]: any;
};
teardown: () => Awaitable<void>;
}
interface Environment {
name: string;
/**
* @deprecated use `viteEnvironment` instead. Uses `name` by default
*/
transformMode?: "web" | "ssr";
/**
* Environment initiated by the Vite server. It is usually available
* as `vite.server.environments.${name}`.
*
* By default, fallbacks to `name`.
*/
viteEnvironment?: "client" | "ssr" | ({} & string);
/**
* Let the server transform the test file's import graph while `setupVM`
* runs. Only worth it when `setupVM` is slow (jsdom, happy-dom): otherwise
* the transforms compete with the worker's own module requests.
*
* @default `true`
*/
prewarmModules?: boolean;
setupVM?: (options: Record<string, any>) => Awaitable<VmEnvironmentReturn>;
setup: (global: any, options: Record<string, any>) => Awaitable<EnvironmentReturn>;
}
export type { Environment as E, VmEnvironmentReturn as V, EnvironmentReturn as a };
@@ -0,0 +1,7 @@
import { EvaluatedModules } from 'vite/module-runner';
declare class VitestEvaluatedModules extends EvaluatedModules {
getModuleSourceMapById(id: string): any;
}
export { VitestEvaluatedModules as V };
+28
View File
@@ -0,0 +1,28 @@
import { g as globalApis } from './constants.-juJ8b_4.js';
import { i as index } from './index.OVGXnVRj.js';
import './run.CQOUYP-x.js';
import './index.M2dsQ_UQ.js';
import './tinyrainbow.Ht9iggcq.js';
import './display.pkpxlVcY.js';
import './pathe.M-eThtNZ.Clq7AXhv.js';
import './source-map.Dx3YPjIe.js';
import '../task-utils.js';
import './utils.DYj33du9.js';
import './spy.DPLvp2J7.js';
import 'chai';
import './plugins.Cigb0uSy.js';
import './offset.Dy-5Fdfn.js';
import './rpc.DWKX1ZbI.js';
import 'vite/module-runner';
import './index.DmDMHCg8.js';
import 'tinybench';
import 'expect-type';
function registerApiGlobally() {
globalApis.forEach((api) => {
// @ts-expect-error I know what I am doing :P
globalThis[api] = index[api];
});
}
export { registerApiGlobally };
File diff suppressed because it is too large. Load diff
+196
View File
@@ -0,0 +1,196 @@
import * as chai from 'chai';
import { createHook } from 'node:async_hooks';
import { t as takeCoverageInsideWorker } from './coverage.BtDxDqSU.js';
import { r as rpc } from './rpc.DWKX1ZbI.js';
import { l as loadDiffConfig, a as loadSnapshotSerializers } from './setup-common.D5-2VgqG.js';
import { g as getWorkerState } from './utils.DYj33du9.js';
import { T as TestRunner } from './index.OVGXnVRj.js';
function setupChaiConfig(config) {
Object.assign(chai.config, config);
}
async function resolveSnapshotEnvironment(config, moduleRunner) {
if (!config.snapshotEnvironment) {
const { VitestNodeSnapshotEnvironment } = await import('./node.Es9ukMGX.js');
return new VitestNodeSnapshotEnvironment();
}
const mod = await moduleRunner.import(config.snapshotEnvironment);
if (typeof mod.default !== "object" || !mod.default) throw new Error("Snapshot environment module must have a default export object with a shape of `SnapshotEnvironment`");
return mod.default;
}
const IGNORED_TYPES = /* @__PURE__ */ new Set([
"DNSCHANNEL",
"ELDHISTOGRAM",
"PerformanceObserver",
"RANDOMBYTESREQUEST",
"SIGNREQUEST",
"STREAM_END_OF_STREAM",
"TCPWRAP",
"TIMERWRAP",
"TLSWRAP",
"ZLIB"
]);
function detectAsyncLeaks(testFile, projectName) {
const resources = /* @__PURE__ */ new Map();
const hook = createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (IGNORED_TYPES.has(type)) return;
let stack = "";
const limit = Error.stackTraceLimit;
// VitestModuleEvaluator's async wrapper of node:vm causes out-of-bound stack traces, simply skip it.
// Crash fixed in https://github.com/vitejs/vite/pull/21585
try {
Error.stackTraceLimit = 100;
stack = (/* @__PURE__ */ new Error("VITEST_DETECT_ASYNC_LEAKS")).stack || "";
} catch {
return;
} finally {
Error.stackTraceLimit = limit;
}
if (!stack.includes(testFile)) {
const trigger = resources.get(triggerAsyncId);
if (!trigger) return;
stack = trigger.stack;
}
let isActive = isActiveDefault;
if ("hasRef" in resource) {
const ref = new WeakRef(resource);
isActive = () => ref.deref()?.hasRef() ?? false;
}
resources.set(asyncId, {
type,
stack,
projectName,
filename: testFile,
isActive
});
},
destroy(asyncId) {
if (resources.get(asyncId)?.type !== "PROMISE") resources.delete(asyncId);
},
promiseResolve(asyncId) {
resources.delete(asyncId);
}
});
hook.enable();
return async function collect() {
await new Promise((resolve) => setImmediate(resolve));
hook.disable();
const leaks = [];
for (const resource of resources.values()) if (resource.isActive()) leaks.push({
stack: resource.stack,
type: resource.type,
filename: resource.filename,
projectName: resource.projectName
});
resources.clear();
return leaks;
};
}
function isActiveDefault() {
return true;
}
async function getTestRunnerConstructor(config, moduleRunner) {
if (!config.runner) return TestRunner;
const mod = await moduleRunner.import(config.runner);
if (!mod.default && typeof mod.default !== "function") throw new Error(`Runner must export a default function, but got ${typeof mod.default} imported from ${config.runner}`);
return mod.default;
}
async function resolveTestRunner(config, moduleRunner, traces) {
const testRunner = new (await (getTestRunnerConstructor(config, moduleRunner)))(config);
// inject private executor to every runner
Object.defineProperty(testRunner, "moduleRunner", {
value: moduleRunner,
enumerable: false,
configurable: false
});
if (!testRunner.config) testRunner.config = config;
if (!testRunner.importFile) throw new Error("Runner must implement \"importFile\" method.");
if ("__setTraces" in testRunner) testRunner.__setTraces(traces);
const [diffOptions] = await Promise.all([loadDiffConfig(config, moduleRunner), loadSnapshotSerializers(config, moduleRunner)]);
testRunner.config._diffOptions = diffOptions;
// patch some methods, so custom runners don't need to call RPC
const originalOnTaskUpdate = testRunner.onTaskUpdate;
testRunner.onTaskUpdate = async (task, events) => {
const p = rpc().onTaskUpdate(task, events);
await originalOnTaskUpdate?.call(testRunner, task, events);
return p;
};
// patch some methods, so custom runners don't need to call RPC
const originalOnTestAnnotate = testRunner.onTestAnnotate;
testRunner.onTestAnnotate = async (test, annotation) => {
const p = rpc().onTaskArtifactRecord(test.id, {
type: "internal:annotation",
location: annotation.location,
annotation
});
const overriddenResult = await originalOnTestAnnotate?.call(testRunner, test, annotation);
const vitestResult = await p;
return overriddenResult || vitestResult.annotation;
};
const originalOnTestArtifactRecord = testRunner.onTestArtifactRecord;
testRunner.onTestArtifactRecord = async (test, artifact) => {
const p = rpc().onTaskArtifactRecord(test.id, artifact);
const overriddenResult = await originalOnTestArtifactRecord?.call(testRunner, test, artifact);
const vitestResult = await p;
return overriddenResult || vitestResult;
};
const originalOnCollectStart = testRunner.onCollectStart;
testRunner.onCollectStart = async (file) => {
await rpc().onQueued(file);
await originalOnCollectStart?.call(testRunner, file);
};
const originalOnCollected = testRunner.onCollected;
testRunner.onCollected = async (files) => {
const state = getWorkerState();
files.forEach((file) => {
file.prepareDuration = state.durations.prepare;
file.environmentLoad = state.durations.environment;
// should be collected only for a single test file in a batch
state.durations.prepare = 0;
state.durations.environment = 0;
});
// Strip function conditions from retry config before sending via RPC
// Functions cannot be cloned by structured clone algorithm
const sanitizeRetryConditions = (task) => {
if (task.retry && typeof task.retry === "object" && typeof task.retry.condition === "function")
// Remove function condition - it can't be serialized
task.retry = {
...task.retry,
condition: void 0
};
if (task.tasks) task.tasks.forEach(sanitizeRetryConditions);
};
files.forEach(sanitizeRetryConditions);
rpc().onCollected(files);
await originalOnCollected?.call(testRunner, files);
};
const originalOnAfterRun = testRunner.onAfterRunFiles;
testRunner.onAfterRunFiles = async (files) => {
const state = getWorkerState();
const coverage = await takeCoverageInsideWorker(config.coverage, moduleRunner);
if (coverage) rpc().onAfterSuiteRun({
coverage,
testFiles: files.map((file) => file.name).sort(),
environment: state.environment.viteEnvironment || state.environment.name,
projectName: state.ctx.projectName
});
await originalOnAfterRun?.call(testRunner, files);
};
const originalOnAfterRunTask = testRunner.onAfterRunTask;
testRunner.onAfterRunTask = async (test) => {
if (config.bail && test.result?.state === "fail") {
if (1 + await rpc().getCountOfFailedTests() >= config.bail) {
rpc().onCancel("test-failure");
testRunner.cancel?.("test-failure");
}
}
await originalOnAfterRunTask?.call(testRunner, test);
};
return testRunner;
}
export { resolveSnapshotEnvironment as a, detectAsyncLeaks as d, resolveTestRunner as r, setupChaiConfig as s };
File diff suppressed because it is too large. Load diff
+234
View File
@@ -0,0 +1,234 @@
import process from 'node:process';
import fs from 'node:fs/promises';
import path, { resolve } from 'node:path';
import { existsSync } from 'node:fs';
import { x } from 'tinyexec';
const AGENTS = [
"npm",
"yarn",
"yarn@berry",
"pnpm",
"pnpm@6",
"pnpm-rush",
"bun",
"deno",
"nub",
"aube"
];
const LOCKS = {
"aube-lock.yaml": "aube",
"aube-workspace.yaml": "aube",
"bun.lock": "bun",
"bun.lockb": "bun",
"deno.lock": "deno",
"nub.lock": "nub",
"pnpm-lock.yaml": "pnpm",
"pnpm-workspace.yaml": "pnpm",
"yarn.lock": "yarn",
"package-lock.json": "npm",
"npm-shrinkwrap.json": "npm"
};
const INSTALL_METADATA = {
"node_modules/.aube/": "aube",
"node_modules/.deno/": "deno",
"node_modules/.pnpm/": "pnpm",
"node_modules/.yarn-state.yml": "yarn",
// yarn v2+ (node-modules)
"node_modules/.yarn_integrity": "yarn",
// yarn v1
"node_modules/.package-lock.json": "npm",
".pnp.cjs": "yarn",
// yarn v3+ (pnp)
".pnp.js": "yarn",
// yarn v2 (pnp)
"bun.lock": "bun",
"bun.lockb": "bun"
};
async function pathExists(path2, type) {
try {
const stat = await fs.stat(path2);
return type === "file" ? stat.isFile() : stat.isDirectory();
} catch {
return false;
}
}
function* lookup(cwd = process.cwd()) {
let directory = path.resolve(cwd);
const { root } = path.parse(directory);
while (directory && directory !== root) {
yield directory;
directory = path.dirname(directory);
}
}
async function parsePackageJson(filepath, options) {
if (!filepath || !await pathExists(filepath, "file"))
return null;
return await handlePackageManager(filepath, options);
}
async function detect(options = {}) {
const {
cwd,
strategies = ["lockfile", "packageManager-field", "devEngines-field"]
} = options;
let stopDir;
if (typeof options.stopDir === "string") {
const resolved = path.resolve(options.stopDir);
stopDir = (dir) => dir === resolved;
} else {
stopDir = options.stopDir;
}
for (const directory of lookup(cwd)) {
for (const strategy of strategies) {
switch (strategy) {
case "lockfile": {
if (await pathExists(path.join(directory, "rush.json"), "file")) {
return { name: "pnpm", agent: "pnpm-rush" };
}
for (const lock of Object.keys(LOCKS)) {
if (await pathExists(path.join(directory, lock), "file")) {
const name = LOCKS[lock];
const result = await parsePackageJson(path.join(directory, "package.json"), options);
if (result)
return result;
else
return { name, agent: name };
}
}
break;
}
case "packageManager-field":
case "devEngines-field": {
const result = await parsePackageJson(path.join(directory, "package.json"), options);
if (result)
return result;
break;
}
case "install-metadata": {
for (const metadata of Object.keys(INSTALL_METADATA)) {
const fileOrDir = metadata.endsWith("/") ? "dir" : "file";
if (await pathExists(path.join(directory, metadata), fileOrDir)) {
const name = INSTALL_METADATA[metadata];
const agent = name === "yarn" ? isMetadataYarnClassic(metadata) ? "yarn" : "yarn@berry" : name;
return { name, agent };
}
}
break;
}
}
}
if (stopDir?.(directory))
break;
}
return null;
}
function getNameAndVer(pkg) {
const handelVer = (version) => version?.match(/\d+(\.\d+){0,2}/)?.[0] ?? version;
if (typeof pkg.packageManager === "string") {
const [name, ver] = pkg.packageManager.replace(/^\^/, "").split("@");
return { name, ver: handelVer(ver) };
}
if (typeof pkg.devEngines?.packageManager?.name === "string") {
return {
name: pkg.devEngines.packageManager.name,
ver: handelVer(pkg.devEngines.packageManager.version)
};
}
return void 0;
}
async function handlePackageManager(filepath, options) {
try {
const content = await fs.readFile(filepath, "utf8");
const pkg = options.packageJsonParser ? await options.packageJsonParser(content, filepath) : JSON.parse(content);
let agent;
const nameAndVer = getNameAndVer(pkg);
if (nameAndVer) {
const name = nameAndVer.name;
const ver = nameAndVer.ver;
let version = ver;
if (name === "yarn" && ver && Number.parseInt(ver) > 1) {
agent = "yarn@berry";
version = "berry";
return { name, agent, version };
} else if (name === "pnpm" && ver && Number.parseInt(ver) < 7) {
agent = "pnpm@6";
return { name, agent, version };
} else if (AGENTS.includes(name)) {
agent = name;
return { name, agent, version };
} else {
return options.onUnknown?.(pkg.packageManager) ?? null;
}
}
} catch {
}
return null;
}
function isMetadataYarnClassic(metadataPath) {
return metadataPath.endsWith(".yarn_integrity");
}
//#region src/detect.ts
async function detectPackageManager(cwd = process.cwd()) {
return (await detect({
cwd,
onUnknown(packageManager) {
console.warn("[@antfu/install-pkg] Unknown packageManager:", packageManager);
}
}))?.agent || null;
}
//#endregion
//#region src/install.ts
async function installPackage(names, options = {}) {
const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || "npm";
const [agent] = detectedAgent.split("@");
if (!Array.isArray(names)) names = [names];
const args = (typeof options.additionalArgs === "function" ? options.additionalArgs(agent, detectedAgent) : options.additionalArgs) || [];
if (options.preferOffline) if (detectedAgent === "yarn@berry") args.unshift("--cached");
else args.unshift("--prefer-offline");
if (agent === "pnpm") {
args.unshift(
/**
* Prevent pnpm from removing installed devDeps while `NODE_ENV` is `production`
* @see https://pnpm.io/cli/install#--prod--p
*/
"--prod=false"
);
if (existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.unshift("-w");
}
return x(agent, [
agent === "yarn" ? "add" : "install",
options.dev ? "-D" : "",
...args,
...names
].filter(Boolean), {
nodeOptions: {
stdio: options.silent ? "ignore" : "inherit",
cwd: options.cwd
},
throwOnError: true
});
}
//#endregion
//#region src/uninstall.ts
async function uninstallPackage(names, options = {}) {
const [agent] = (options.packageManager || await detectPackageManager(options.cwd) || "npm").split("@");
if (!Array.isArray(names)) names = [names];
const args = options.additionalArgs || [];
if (agent === "pnpm" && existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.unshift("-w");
return x(agent, [
agent === "yarn" ? "remove" : "uninstall",
options.dev ? "-D" : "",
...args,
...names
].filter(Boolean), {
nodeOptions: {
stdio: options.silent ? "ignore" : "inherit",
cwd: options.cwd
},
throwOnError: true
});
}
export { detectPackageManager, installPackage, uninstallPackage };
+200
View File
@@ -0,0 +1,200 @@
//#region src/utils.ts
function createPromiseWithResolvers() {
let resolve;
let reject;
return {
promise: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
resolve,
reject
};
}
const random = Math.random.bind(Math);
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
function nanoid(size = 21) {
let id = "";
let i = size;
while (i--) id += urlAlphabet[random() * 64 | 0];
return id;
}
//#endregion
//#region src/main.ts
const DEFAULT_TIMEOUT = 6e4;
const defaultSerialize = (i) => i;
const defaultDeserialize = defaultSerialize;
const { clearTimeout, setTimeout } = globalThis;
function createBirpc($functions, options) {
const { post, on, off = () => {}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT, proxify = true } = options;
let $closed = false;
const _rpcPromiseMap = /* @__PURE__ */ new Map();
let _promiseInit;
let rpc;
async function _call(method, args, event, optional) {
if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
const req = {
m: method,
a: args,
t: "q"
};
if (optional) req.o = true;
const send = async (_req) => post(serialize(_req));
if (event) {
await send(req);
return;
}
if (_promiseInit) try {
await _promiseInit;
} finally {
_promiseInit = void 0;
}
let { promise, resolve, reject } = createPromiseWithResolvers();
const id = nanoid();
req.i = id;
let timeoutId;
async function handler(newReq = req) {
if (timeout >= 0) {
timeoutId = setTimeout(() => {
try {
if (options.onTimeoutError?.call(rpc, method, args) !== true) throw new Error(`[birpc] timeout on calling "${method}"`);
} catch (e) {
reject(e);
}
_rpcPromiseMap.delete(id);
}, timeout);
if (typeof timeoutId === "object") timeoutId = timeoutId.unref?.();
}
_rpcPromiseMap.set(id, {
resolve,
reject,
timeoutId,
method
});
await send(newReq);
return promise;
}
try {
if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
else await handler();
} catch (e) {
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
return;
} finally {
clearTimeout(timeoutId);
_rpcPromiseMap.delete(id);
}
return promise;
}
const builtinMethods = {
$call: (method, ...args) => _call(method, args, false),
$callOptional: (method, ...args) => _call(method, args, false, true),
$callEvent: (method, ...args) => _call(method, args, true),
$callRaw: (options) => _call(options.method, options.args, options.event, options.optional),
$rejectPendingCalls,
get $closed() {
return $closed;
},
get $meta() {
return options.meta;
},
$close,
$functions
};
if (proxify) rpc = new Proxy({}, { get(_, method) {
if (Object.hasOwn(builtinMethods, method)) return builtinMethods[method];
if (method === "then" && !eventNames.includes("then") && !("then" in $functions)) return void 0;
const sendEvent = (...args) => _call(method, args, true);
if (eventNames.includes(method)) {
sendEvent.asEvent = sendEvent;
return sendEvent;
}
const sendCall = (...args) => _call(method, args, false);
sendCall.asEvent = sendEvent;
return sendCall;
} });
else rpc = builtinMethods;
function $close(customError) {
$closed = true;
_rpcPromiseMap.forEach(({ reject, method }) => {
const error = /* @__PURE__ */ new Error(`[birpc] rpc is closed, cannot call "${method}"`);
if (customError) {
customError.cause ??= error;
return reject(customError);
}
reject(error);
});
_rpcPromiseMap.clear();
off(onMessage);
}
function $rejectPendingCalls(handler) {
const handlerResults = Array.from(_rpcPromiseMap.values()).map(({ method, reject }) => {
if (!handler) return reject(/* @__PURE__ */ new Error(`[birpc]: rejected pending call "${method}".`));
return handler({
method,
reject
});
});
_rpcPromiseMap.clear();
return handlerResults;
}
async function onMessage(data, ...extra) {
let msg;
try {
msg = deserialize(data);
} catch (e) {
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
return;
}
if (msg.t === "q") {
const { m: method, a: args, o: optional } = msg;
let result, error;
let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
if (optional) fn ||= () => void 0;
if (!fn) error = /* @__PURE__ */ new Error(`[birpc] function "${method}" not found`);
else try {
result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
} catch (e) {
error = e;
}
if (msg.i) {
if (error && options.onFunctionError) {
if (options.onFunctionError.call(rpc, error, method, args) === true) return;
}
if (!error) try {
await post(serialize({
t: "s",
i: msg.i,
r: result
}), ...extra);
return;
} catch (e) {
error = e;
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
}
try {
await post(serialize({
t: "s",
i: msg.i,
e: error
}), ...extra);
} catch (e) {
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
}
}
} else {
const { i: ack, r: result, e: error } = msg;
const promise = _rpcPromiseMap.get(ack);
if (promise) {
clearTimeout(promise.timeoutId);
if (error) promise.reject(error);
else promise.resolve(result);
}
_rpcPromiseMap.delete(ack);
}
}
_promiseInit = on(onMessage);
return rpc;
}
export { createBirpc as c };
+944
View File
@@ -0,0 +1,944 @@
import { y } from './tinyrainbow.Ht9iggcq.js';
function _mergeNamespaces$1(n, m) {
m.forEach(function(e) {
e && typeof e !== "string" && !Array.isArray(e) && Object.keys(e).forEach(function(k) {
if (k !== "default" && !(k in n)) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function() {
return e[k];
}
});
}
});
});
return Object.freeze(n);
}
function getKeysOfEnumerableProperties(object, compareKeys) {
const rawKeys = Object.keys(object);
const keys = compareKeys === null ? rawKeys : rawKeys.sort(compareKeys);
if (Object.getOwnPropertySymbols) {
for (const symbol of Object.getOwnPropertySymbols(object)) if (Object.getOwnPropertyDescriptor(object, symbol).enumerable) keys.push(symbol);
}
return keys;
}
/**
* Return entries (for example, of a map)
* with spacing, indentation, and comma
* without surrounding punctuation (for example, braces)
*/
function printIteratorEntries(iterator, config, indentation, depth, refs, printer, separator = ": ", length) {
let result = "";
let width = 0;
let current = iterator.next();
if (!current.done) {
result += config.spacingOuter;
const indentationNext = indentation + config.indent;
while (!current.done) {
result += indentationNext;
if (width++ === config.maxWidth) {
result += typeof length === "number" ? `…(${length - width + 1})` : "…";
break;
}
const name = printer(current.value[0], config, indentationNext, depth, refs);
const value = printer(current.value[1], config, indentationNext, depth, refs);
result += name + separator + value;
current = iterator.next();
if (!current.done) result += `,${config.spacingInner}`;
else if (!config.min) result += ",";
}
result += config.spacingOuter + indentation;
}
return result;
}
/**
* Return values (for example, of a set)
* with spacing, indentation, and comma
* without surrounding punctuation (braces or brackets)
*/
function printIteratorValues(iterator, config, indentation, depth, refs, printer, length) {
let result = "";
let width = 0;
let current = iterator.next();
if (!current.done) {
result += config.spacingOuter;
const indentationNext = indentation + config.indent;
while (!current.done) {
result += indentationNext;
if (width++ === config.maxWidth) {
result += typeof length === "number" ? `…(${length - width + 1})` : "…";
break;
}
result += printer(current.value, config, indentationNext, depth, refs);
current = iterator.next();
if (!current.done) result += `,${config.spacingInner}`;
else if (!config.min) result += ",";
}
result += config.spacingOuter + indentation;
}
return result;
}
/**
* Return items (for example, of an array)
* with spacing, indentation, and comma
* without surrounding punctuation (for example, brackets)
*/
function printListItems(list, config, indentation, depth, refs, printer) {
let result = "";
list = list instanceof ArrayBuffer ? new DataView(list) : list;
const isDataView = (l) => l instanceof DataView;
const length = isDataView(list) ? list.byteLength : list.length;
if (length > 0) {
result += config.spacingOuter;
const indentationNext = indentation + config.indent;
for (let i = 0; i < length; i++) {
result += indentationNext;
if (i === config.maxWidth) {
result += `…(${length - i})`;
break;
}
if (isDataView(list) || i in list) result += printer(isDataView(list) ? list.getInt8(i) : list[i], config, indentationNext, depth, refs);
if (i < length - 1) result += `,${config.spacingInner}`;
else if (!config.min) result += ",";
}
result += config.spacingOuter + indentation;
}
return result;
}
/**
* Return properties of an object
* with spacing, indentation, and comma
* without surrounding punctuation (for example, braces)
*/
function printObjectProperties(val, config, indentation, depth, refs, printer, compareKeysOverride = config.compareKeys) {
let result = "";
const keys = getKeysOfEnumerableProperties(val, compareKeysOverride);
if (keys.length > 0) {
result += config.spacingOuter;
const indentationNext = indentation + config.indent;
for (let i = 0; i < keys.length; i++) {
result += indentationNext;
if (i === config.maxWidth) {
result += `…(${keys.length - i})`;
break;
}
const key = keys[i];
const name = !config.quoteKeys && isUnquotableKey(key) ? key : printer(key, config, indentationNext, depth, refs);
const value = printer(val[key], config, indentationNext, depth, refs);
result += `${name}: ${value}`;
if (i < keys.length - 1) result += `,${config.spacingInner}`;
else if (!config.min) result += ",";
}
result += config.spacingOuter + indentation;
}
return result;
}
// https://github.com/nodejs/node/blob/61102cdbb3d59155ad5bb4fc9419627a31e63f7a/lib/internal/util/inspect.js#L249
// /^[a-zA-Z_][a-zA-Z_0-9]*$/
const keyStrRegExp = /^[a-z_]\w*$/i;
function isUnquotableKey(key) {
return typeof key === "string" && key !== "__proto__" && keyStrRegExp.test(key);
}
const asymmetricMatcher = typeof Symbol === "function" && Symbol.for ? Symbol.for("jest.asymmetricMatcher") : 1267621;
const SPACE$2 = " ";
const serialize$5 = (val, config, indentation, depth, refs, printer) => {
const stringedValue = val.toString();
if (stringedValue === "ArrayContaining" || stringedValue === "ArrayNotContaining") {
if (++depth > config.maxDepth) return `[${stringedValue}]`;
return `${stringedValue + SPACE$2}[${printListItems(val.sample, config, indentation, depth, refs, printer)}]`;
}
if (stringedValue === "ObjectContaining" || stringedValue === "ObjectNotContaining") {
if (++depth > config.maxDepth) return `[${stringedValue}]`;
return `${stringedValue + SPACE$2}{${printObjectProperties(val.sample, config, indentation, depth, refs, printer)}}`;
}
if (stringedValue === "StringMatching" || stringedValue === "StringNotMatching") return stringedValue + SPACE$2 + printer(val.sample, config, indentation, depth, refs);
if (stringedValue === "StringContaining" || stringedValue === "StringNotContaining") return stringedValue + SPACE$2 + printer(val.sample, config, indentation, depth, refs);
if (typeof val.toAsymmetricMatcher !== "function") throw new TypeError(`Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()`);
return val.toAsymmetricMatcher();
};
const test$5 = (val) => val && val.$$typeof === asymmetricMatcher;
const plugin$5 = {
serialize: serialize$5,
test: test$5
};
const SPACE$1 = " ";
const OBJECT_NAMES = /* @__PURE__ */ new Set(["DOMStringMap", "NamedNodeMap"]);
const ARRAY_REGEXP = /^(?:HTML\w*Collection|NodeList)$/;
function testName(name) {
return OBJECT_NAMES.has(name) || ARRAY_REGEXP.test(name);
}
const test$4 = (val) => val && val.constructor && !!val.constructor.name && testName(val.constructor.name);
function isNamedNodeMap(collection) {
return collection.constructor.name === "NamedNodeMap";
}
const serialize$4 = (collection, config, indentation, depth, refs, printer) => {
const name = collection.constructor.name;
if (++depth > config.maxDepth) return `[${name}]`;
return (config.min ? "" : name + SPACE$1) + (OBJECT_NAMES.has(name) ? `{${printObjectProperties(isNamedNodeMap(collection) ? [...collection].reduce((props, attribute) => {
props[attribute.name] = attribute.value;
return props;
}, {}) : { ...collection }, config, indentation, depth, refs, printer)}}` : `[${printListItems([...collection], config, indentation, depth, refs, printer)}]`);
};
const plugin$4 = {
serialize: serialize$4,
test: test$4
};
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
function escapeHTML(str) {
return str.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
}
// Return empty string if keys is empty.
function printProps(keys, props, config, indentation, depth, refs, printer) {
const indentationNext = indentation + config.indent;
const colors = config.colors;
return keys.map((key) => {
const value = props[key];
// hidden injected value that should not be printed
if (typeof value === "string" && value[0] === "_" && value.startsWith("__vitest_") && /__vitest_\d+__/.test(value)) return "";
let printed = printer(value, config, indentationNext, depth, refs);
if (typeof value !== "string") {
if (printed.includes("\n")) printed = config.spacingOuter + indentationNext + printed + config.spacingOuter + indentation;
printed = `{${printed}}`;
}
return `${config.spacingInner + indentation + colors.prop.open + key + colors.prop.close}=${colors.value.open}${printed}${colors.value.close}`;
}).join("");
}
// Return empty string if children is empty.
function printChildren(children, config, indentation, depth, refs, printer) {
return children.map((child) => config.spacingOuter + indentation + (typeof child === "string" ? printText(child, config) : printer(child, config, indentation, depth, refs))).join("");
}
function printShadowRoot(children, config, indentation, depth, refs, printer) {
if (config.printShadowRoot === false) return "";
return [`${config.spacingOuter + indentation}#shadow-root`, printChildren(children, config, indentation + config.indent, depth, refs, printer)].join("");
}
function printText(text, config) {
const contentColor = config.colors.content;
return contentColor.open + escapeHTML(text) + contentColor.close;
}
function printComment(comment, config) {
const commentColor = config.colors.comment;
return `${commentColor.open}<!--${escapeHTML(comment)}-->${commentColor.close}`;
}
// Separate the functions to format props, children, and element,
// so a plugin could override a particular function, if needed.
// Too bad, so sad: the traditional (but unnecessary) space
// in a self-closing tagColor requires a second test of printedProps.
function printElement(type, printedProps, printedChildren, config, indentation) {
const tagColor = config.colors.tag;
return `${tagColor.open}<${type}${printedProps && tagColor.close + printedProps + config.spacingOuter + indentation + tagColor.open}${printedChildren ? `>${tagColor.close}${printedChildren}${config.spacingOuter}${indentation}${tagColor.open}</${type}` : `${printedProps && !config.min ? "" : " "}/`}>${tagColor.close}`;
}
function printElementAsLeaf(type, config) {
const tagColor = config.colors.tag;
return `${tagColor.open}<${type}${tagColor.close} …${tagColor.open} />${tagColor.close}`;
}
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;
const FRAGMENT_NODE = 11;
const ELEMENT_REGEXP = /^(?:(?:HTML|SVG)\w*)?Element$/;
function testHasAttribute(val) {
try {
return typeof val.hasAttribute === "function" && val.hasAttribute("is");
} catch {
return false;
}
}
function testNode(val) {
const constructorName = val.constructor.name;
const { nodeType, tagName } = val;
const isCustomElement = typeof tagName === "string" && tagName.includes("-") || testHasAttribute(val);
return nodeType === ELEMENT_NODE && (ELEMENT_REGEXP.test(constructorName) || isCustomElement) || nodeType === TEXT_NODE && constructorName === "Text" || nodeType === COMMENT_NODE && constructorName === "Comment" || nodeType === FRAGMENT_NODE && constructorName === "DocumentFragment";
}
const test$3 = (val) => val?.constructor?.name && testNode(val);
function nodeIsText(node) {
return node.nodeType === TEXT_NODE;
}
function nodeIsComment(node) {
return node.nodeType === COMMENT_NODE;
}
function nodeIsFragment(node) {
return node.nodeType === FRAGMENT_NODE;
}
function filterChildren(children, filterNode) {
// Filter out text nodes that only contain whitespace to prevent empty lines
// This is done regardless of whether a filterNode is provided
let filtered = children.filter((node) => {
// Filter out text nodes that are only whitespace
if (node.nodeType === TEXT_NODE)
// Keep text nodes that have non-whitespace content
return (node.data || "").trim().length > 0;
return true;
});
// Apply additional user-provided filter if specified
if (filterNode) filtered = filtered.filter(filterNode);
return filtered;
}
function serializeDOM(node, config, indentation, depth, refs, printer, filterNode) {
if (nodeIsText(node)) return printText(node.data, config);
if (nodeIsComment(node)) return printComment(node.data, config);
const type = nodeIsFragment(node) ? "DocumentFragment" : node.tagName.toLowerCase();
if (++depth > config.maxDepth) return printElementAsLeaf(type, config);
const children = Array.prototype.slice.call(node.childNodes || node.children);
const shadowChildren = nodeIsFragment(node) || !node.shadowRoot ? [] : Array.prototype.slice.call(node.shadowRoot.children);
const resolvedChildren = filterNode ? filterChildren(children, filterNode) : children;
const resolvedShadowChildren = filterNode ? filterChildren(shadowChildren, filterNode) : shadowChildren;
return printElement(type, printProps(nodeIsFragment(node) ? [] : Array.from(node.attributes, (attr) => attr.name).sort(), nodeIsFragment(node) ? {} : [...node.attributes].reduce((props, attribute) => {
props[attribute.name] = attribute.value;
return props;
}, {}), config, indentation + config.indent, depth, refs, printer), (resolvedShadowChildren.length > 0 ? printShadowRoot(resolvedShadowChildren, config, indentation + config.indent, depth, refs, printer) : "") + printChildren(resolvedChildren, config, indentation + config.indent, depth, refs, printer), config, indentation);
}
const serialize$3 = (node, config, indentation, depth, refs, printer) => serializeDOM(node, config, indentation, depth, refs, printer);
function createDOMElementFilter(filterNode) {
return {
test: test$3,
serialize: (node, config, indentation, depth, refs, printer) => serializeDOM(node, config, indentation, depth, refs, printer, filterNode)
};
}
const plugin$3 = {
serialize: serialize$3,
test: test$3
};
// SENTINEL constants are from https://github.com/immutable-js/immutable-js
const IS_ITERABLE_SENTINEL = "@@__IMMUTABLE_ITERABLE__@@";
const IS_LIST_SENTINEL = "@@__IMMUTABLE_LIST__@@";
const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@";
const IS_MAP_SENTINEL = "@@__IMMUTABLE_MAP__@@";
const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@";
const IS_RECORD_SENTINEL = "@@__IMMUTABLE_RECORD__@@";
const IS_SEQ_SENTINEL = "@@__IMMUTABLE_SEQ__@@";
const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@";
const IS_STACK_SENTINEL = "@@__IMMUTABLE_STACK__@@";
const getImmutableName = (name) => `Immutable.${name}`;
const printAsLeaf = (name) => `[${name}]`;
const SPACE = " ";
const LAZY = "…";
function printImmutableEntries(val, config, indentation, depth, refs, printer, type) {
return ++depth > config.maxDepth ? printAsLeaf(getImmutableName(type)) : `${getImmutableName(type) + SPACE}{${printIteratorEntries(val.entries(), config, indentation, depth, refs, printer)}}`;
}
// Record has an entries method because it is a collection in immutable v3.
// Return an iterator for Immutable Record from version v3 or v4.
function getRecordEntries(val) {
let i = 0;
return { next() {
if (i < val._keys.length) {
const key = val._keys[i++];
return {
done: false,
value: [key, val.get(key)]
};
}
return {
done: true,
value: void 0
};
} };
}
function printImmutableRecord(val, config, indentation, depth, refs, printer) {
// _name property is defined only for an Immutable Record instance
// which was constructed with a second optional descriptive name arg
const name = getImmutableName(val._name || "Record");
return ++depth > config.maxDepth ? printAsLeaf(name) : `${name + SPACE}{${printIteratorEntries(getRecordEntries(val), config, indentation, depth, refs, printer)}}`;
}
function printImmutableSeq(val, config, indentation, depth, refs, printer) {
const name = getImmutableName("Seq");
if (++depth > config.maxDepth) return printAsLeaf(name);
if (val[IS_KEYED_SENTINEL]) return `${name + SPACE}{${
// from Immutable collection of entries or from ECMAScript object
val._iter || val._object ? printIteratorEntries(val.entries(), config, indentation, depth, refs, printer) : LAZY}}`;
return `${name + SPACE}[${val._iter || val._array || val._collection || val._iterable ? printIteratorValues(val.values(), config, indentation, depth, refs, printer) : LAZY}]`;
}
function printImmutableValues(val, config, indentation, depth, refs, printer, type) {
return ++depth > config.maxDepth ? printAsLeaf(getImmutableName(type)) : `${getImmutableName(type) + SPACE}[${printIteratorValues(val.values(), config, indentation, depth, refs, printer)}]`;
}
const serialize$2 = (val, config, indentation, depth, refs, printer) => {
if (val[IS_MAP_SENTINEL]) return printImmutableEntries(val, config, indentation, depth, refs, printer, val[IS_ORDERED_SENTINEL] ? "OrderedMap" : "Map");
if (val[IS_LIST_SENTINEL]) return printImmutableValues(val, config, indentation, depth, refs, printer, "List");
if (val[IS_SET_SENTINEL]) return printImmutableValues(val, config, indentation, depth, refs, printer, val[IS_ORDERED_SENTINEL] ? "OrderedSet" : "Set");
if (val[IS_STACK_SENTINEL]) return printImmutableValues(val, config, indentation, depth, refs, printer, "Stack");
if (val[IS_SEQ_SENTINEL]) return printImmutableSeq(val, config, indentation, depth, refs, printer);
// For compatibility with immutable v3 and v4, let record be the default.
return printImmutableRecord(val, config, indentation, depth, refs, printer);
};
// Explicitly comparing sentinel properties to true avoids false positive
// when mock identity-obj-proxy returns the key as the value for any key.
const test$2 = (val) => val && (val[IS_ITERABLE_SENTINEL] === true || val[IS_RECORD_SENTINEL] === true);
const plugin$2 = {
serialize: serialize$2,
test: test$2
};
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
var reactIs$1 = { exports: {} };
var reactIs_production = {};
/**
* @license React
* react-is.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactIs_production;
function requireReactIs_production() {
if (hasRequiredReactIs_production) return reactIs_production;
hasRequiredReactIs_production = 1;
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_VIEW_TRANSITION_TYPE = Symbol.for("react.view_transition"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference");
function typeOf(object) {
if ("object" === typeof object && null !== object) {
var $$typeof = object.$$typeof;
switch ($$typeof) {
case REACT_ELEMENT_TYPE: switch (object = object.type, object) {
case REACT_FRAGMENT_TYPE:
case REACT_PROFILER_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_SUSPENSE_TYPE:
case REACT_SUSPENSE_LIST_TYPE:
case REACT_VIEW_TRANSITION_TYPE: return object;
default: switch (object = object && object.$$typeof, object) {
case REACT_CONTEXT_TYPE:
case REACT_FORWARD_REF_TYPE:
case REACT_LAZY_TYPE:
case REACT_MEMO_TYPE: return object;
case REACT_CONSUMER_TYPE: return object;
default: return $$typeof;
}
}
case REACT_PORTAL_TYPE: return $$typeof;
}
}
}
reactIs_production.ContextConsumer = REACT_CONSUMER_TYPE;
reactIs_production.ContextProvider = REACT_CONTEXT_TYPE;
reactIs_production.Element = REACT_ELEMENT_TYPE;
reactIs_production.ForwardRef = REACT_FORWARD_REF_TYPE;
reactIs_production.Fragment = REACT_FRAGMENT_TYPE;
reactIs_production.Lazy = REACT_LAZY_TYPE;
reactIs_production.Memo = REACT_MEMO_TYPE;
reactIs_production.Portal = REACT_PORTAL_TYPE;
reactIs_production.Profiler = REACT_PROFILER_TYPE;
reactIs_production.StrictMode = REACT_STRICT_MODE_TYPE;
reactIs_production.Suspense = REACT_SUSPENSE_TYPE;
reactIs_production.SuspenseList = REACT_SUSPENSE_LIST_TYPE;
reactIs_production.isContextConsumer = function(object) {
return typeOf(object) === REACT_CONSUMER_TYPE;
};
reactIs_production.isContextProvider = function(object) {
return typeOf(object) === REACT_CONTEXT_TYPE;
};
reactIs_production.isElement = function(object) {
return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
};
reactIs_production.isForwardRef = function(object) {
return typeOf(object) === REACT_FORWARD_REF_TYPE;
};
reactIs_production.isFragment = function(object) {
return typeOf(object) === REACT_FRAGMENT_TYPE;
};
reactIs_production.isLazy = function(object) {
return typeOf(object) === REACT_LAZY_TYPE;
};
reactIs_production.isMemo = function(object) {
return typeOf(object) === REACT_MEMO_TYPE;
};
reactIs_production.isPortal = function(object) {
return typeOf(object) === REACT_PORTAL_TYPE;
};
reactIs_production.isProfiler = function(object) {
return typeOf(object) === REACT_PROFILER_TYPE;
};
reactIs_production.isStrictMode = function(object) {
return typeOf(object) === REACT_STRICT_MODE_TYPE;
};
reactIs_production.isSuspense = function(object) {
return typeOf(object) === REACT_SUSPENSE_TYPE;
};
reactIs_production.isSuspenseList = function(object) {
return typeOf(object) === REACT_SUSPENSE_LIST_TYPE;
};
reactIs_production.isValidElementType = function(type) {
return "string" === typeof type || "function" === typeof type || type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || "object" === typeof type && null !== type && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_CONSUMER_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_CLIENT_REFERENCE || void 0 !== type.getModuleId) ? true : false;
};
reactIs_production.typeOf = typeOf;
return reactIs_production;
}
var hasRequiredReactIs$1;
function requireReactIs$1() {
if (hasRequiredReactIs$1) return reactIs$1.exports;
hasRequiredReactIs$1 = 1;
reactIs$1.exports = requireReactIs_production();
return reactIs$1.exports;
}
var reactIsExports$1 = requireReactIs$1();
var ReactIs19 = /*#__PURE__*/ _mergeNamespaces$1({
__proto__: null,
default: /* @__PURE__ */ getDefaultExportFromCjs(reactIsExports$1)
}, [reactIsExports$1]);
var reactIs = { exports: {} };
var reactIs_production_min = {};
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactIs_production_min;
function requireReactIs_production_min() {
if (hasRequiredReactIs_production_min) return reactIs_production_min;
hasRequiredReactIs_production_min = 1;
var b = Symbol.for("react.element"), c = Symbol.for("react.portal"), d = Symbol.for("react.fragment"), e = Symbol.for("react.strict_mode"), f = Symbol.for("react.profiler"), g = Symbol.for("react.provider"), h = Symbol.for("react.context"), k = Symbol.for("react.server_context"), l = Symbol.for("react.forward_ref"), m = Symbol.for("react.suspense"), n = Symbol.for("react.suspense_list"), p = Symbol.for("react.memo"), q = Symbol.for("react.lazy"), t = Symbol.for("react.offscreen"), u = Symbol.for("react.module.reference");
function v(a) {
if ("object" === typeof a && null !== a) {
var r = a.$$typeof;
switch (r) {
case b: switch (a = a.type, a) {
case d:
case f:
case e:
case m:
case n: return a;
default: switch (a = a && a.$$typeof, a) {
case k:
case h:
case l:
case q:
case p:
case g: return a;
default: return r;
}
}
case c: return r;
}
}
}
reactIs_production_min.ContextConsumer = h;
reactIs_production_min.ContextProvider = g;
reactIs_production_min.Element = b;
reactIs_production_min.ForwardRef = l;
reactIs_production_min.Fragment = d;
reactIs_production_min.Lazy = q;
reactIs_production_min.Memo = p;
reactIs_production_min.Portal = c;
reactIs_production_min.Profiler = f;
reactIs_production_min.StrictMode = e;
reactIs_production_min.Suspense = m;
reactIs_production_min.SuspenseList = n;
reactIs_production_min.isAsyncMode = function() {
return false;
};
reactIs_production_min.isConcurrentMode = function() {
return false;
};
reactIs_production_min.isContextConsumer = function(a) {
return v(a) === h;
};
reactIs_production_min.isContextProvider = function(a) {
return v(a) === g;
};
reactIs_production_min.isElement = function(a) {
return "object" === typeof a && null !== a && a.$$typeof === b;
};
reactIs_production_min.isForwardRef = function(a) {
return v(a) === l;
};
reactIs_production_min.isFragment = function(a) {
return v(a) === d;
};
reactIs_production_min.isLazy = function(a) {
return v(a) === q;
};
reactIs_production_min.isMemo = function(a) {
return v(a) === p;
};
reactIs_production_min.isPortal = function(a) {
return v(a) === c;
};
reactIs_production_min.isProfiler = function(a) {
return v(a) === f;
};
reactIs_production_min.isStrictMode = function(a) {
return v(a) === e;
};
reactIs_production_min.isSuspense = function(a) {
return v(a) === m;
};
reactIs_production_min.isSuspenseList = function(a) {
return v(a) === n;
};
reactIs_production_min.isValidElementType = function(a) {
return "string" === typeof a || "function" === typeof a || a === d || a === f || a === e || a === m || a === n || a === t || "object" === typeof a && null !== a && (a.$$typeof === q || a.$$typeof === p || a.$$typeof === g || a.$$typeof === h || a.$$typeof === l || a.$$typeof === u || void 0 !== a.getModuleId) ? true : false;
};
reactIs_production_min.typeOf = v;
return reactIs_production_min;
}
var hasRequiredReactIs;
function requireReactIs() {
if (hasRequiredReactIs) return reactIs.exports;
hasRequiredReactIs = 1;
reactIs.exports = requireReactIs_production_min();
return reactIs.exports;
}
var reactIsExports = requireReactIs();
var ReactIs18 = /*#__PURE__*/ _mergeNamespaces$1({
__proto__: null,
default: /* @__PURE__ */ getDefaultExportFromCjs(reactIsExports)
}, [reactIsExports]);
const ReactIs = Object.fromEntries([
"isAsyncMode",
"isConcurrentMode",
"isContextConsumer",
"isContextProvider",
"isElement",
"isForwardRef",
"isFragment",
"isLazy",
"isMemo",
"isPortal",
"isProfiler",
"isStrictMode",
"isSuspense",
"isSuspenseList",
"isValidElementType"
].map((m) => [m, (v) => ReactIs18[m](v) || ReactIs19[m](v)]));
// Given element.props.children, or subtree during recursive traversal,
// return flattened array of children.
function getChildren(arg, children = []) {
if (Array.isArray(arg)) for (const item of arg) getChildren(item, children);
else if (arg != null && arg !== false && arg !== "") children.push(arg);
return children;
}
function getType(element) {
const type = element.type;
if (typeof type === "string") return type;
if (typeof type === "function") return type.displayName || type.name || "Unknown";
if (ReactIs.isFragment(element)) return "React.Fragment";
if (ReactIs.isSuspense(element)) return "React.Suspense";
if (typeof type === "object" && type !== null) {
if (ReactIs.isContextProvider(element)) return "Context.Provider";
if (ReactIs.isContextConsumer(element)) return "Context.Consumer";
if (ReactIs.isForwardRef(element)) {
if (type.displayName) return type.displayName;
const functionName = type.render.displayName || type.render.name || "";
return functionName === "" ? "ForwardRef" : `ForwardRef(${functionName})`;
}
if (ReactIs.isMemo(element)) {
const functionName = type.displayName || type.type.displayName || type.type.name || "";
return functionName === "" ? "Memo" : `Memo(${functionName})`;
}
}
return "UNDEFINED";
}
function getPropKeys$1(element) {
const { props } = element;
return Object.keys(props).filter((key) => key !== "children" && props[key] !== void 0).sort();
}
const serialize$1 = (element, config, indentation, depth, refs, printer) => ++depth > config.maxDepth ? printElementAsLeaf(getType(element), config) : printElement(getType(element), printProps(getPropKeys$1(element), element.props, config, indentation + config.indent, depth, refs, printer), printChildren(getChildren(element.props.children), config, indentation + config.indent, depth, refs, printer), config, indentation);
const test$1 = (val) => val != null && ReactIs.isElement(val);
const plugin$1 = {
serialize: serialize$1,
test: test$1
};
const testSymbol = typeof Symbol === "function" && Symbol.for ? Symbol.for("react.test.json") : 245830487;
function getPropKeys(object) {
const { props } = object;
return props ? Object.keys(props).filter((key) => props[key] !== void 0).sort() : [];
}
const serialize = (object, config, indentation, depth, refs, printer) => ++depth > config.maxDepth ? printElementAsLeaf(object.type, config) : printElement(object.type, object.props ? printProps(getPropKeys(object), object.props, config, indentation + config.indent, depth, refs, printer) : "", object.children ? printChildren(object.children, config, indentation + config.indent, depth, refs, printer) : "", config, indentation);
const test = (val) => val && val.$$typeof === testSymbol;
const plugin = {
serialize,
test
};
const toString = Object.prototype.toString;
const toISOString = Date.prototype.toISOString;
const errorToString = Error.prototype.toString;
const regExpToString = RegExp.prototype.toString;
/**
* Explicitly comparing typeof constructor to function avoids undefined as name
* when mock identity-obj-proxy returns the key as the value for any key.
*/
function getConstructorName(val) {
return typeof val.constructor === "function" && val.constructor.name || "Object";
}
/** Is val is equal to global window object? Works even if it does not exist :) */
function isWindow(val) {
return typeof window !== "undefined" && val === window;
}
// eslint-disable-next-line regexp/no-super-linear-backtracking
const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
const NEWLINE_REGEXP = /\n/g;
class PrettyFormatPluginError extends Error {
constructor(message, stack) {
super(message);
this.stack = stack;
this.name = this.constructor.name;
}
}
function isToStringedArrayType(toStringed) {
return toStringed === "[object Array]" || toStringed === "[object ArrayBuffer]" || toStringed === "[object DataView]" || toStringed === "[object Float32Array]" || toStringed === "[object Float64Array]" || toStringed === "[object Int8Array]" || toStringed === "[object Int16Array]" || toStringed === "[object Int32Array]" || toStringed === "[object Uint8Array]" || toStringed === "[object Uint8ClampedArray]" || toStringed === "[object Uint16Array]" || toStringed === "[object Uint32Array]";
}
function printNumber(val) {
return Object.is(val, -0) ? "-0" : String(val);
}
function printBigInt(val) {
return String(`${val}n`);
}
function printFunction(val, printFunctionName) {
if (!printFunctionName) return "[Function]";
return `[Function ${val.name || "anonymous"}]`;
}
function printSymbol(val) {
return String(val).replace(SYMBOL_REGEXP, "Symbol($1)");
}
function printError(val) {
return `[${errorToString.call(val)}]`;
}
/**
* The first port of call for printing an object, handles most of the
* data-types in JS.
*/
function printBasicValue(val, printFunctionName, escapeRegex, escapeString, singleQuote) {
if (val === true || val === false) return `${val}`;
if (val === void 0) return "undefined";
if (val === null) return "null";
const typeOf = typeof val;
if (typeOf === "number") return printNumber(val);
if (typeOf === "bigint") return printBigInt(val);
if (typeOf === "string") {
const q = singleQuote ? "'" : "\"";
if (escapeString) {
// escape quote in each case, e.g.
// it's me -> 'it\'s me'
// say "hi" -> "say \"hi\""
const escapePattern = singleQuote ? /['\\]/g : /["\\]/g;
return `${q}${val.replaceAll(escapePattern, "\\$&")}${q}`;
}
return `${q}${val}${q}`;
}
if (typeOf === "function") return printFunction(val, printFunctionName);
if (typeOf === "symbol") return printSymbol(val);
const toStringed = toString.call(val);
if (toStringed === "[object WeakMap]") return "WeakMap {}";
if (toStringed === "[object WeakSet]") return "WeakSet {}";
if (toStringed === "[object Function]" || toStringed === "[object GeneratorFunction]") return printFunction(val, printFunctionName);
if (toStringed === "[object Symbol]") return printSymbol(val);
if (toStringed === "[object Date]") return Number.isNaN(+val) ? "Date { NaN }" : toISOString.call(val);
if (toStringed === "[object Error]") return printError(val);
if (toStringed === "[object RegExp]") {
if (escapeRegex)
// https://github.com/benjamingr/RegExp.escape/blob/main/polyfill.js
return regExpToString.call(val).replaceAll(/[$()*+.?[\\\]^{|}]/g, "\\$&");
return regExpToString.call(val);
}
if (val instanceof Error) return printError(val);
return null;
}
/**
* Handles more complex objects ( such as objects with circular references.
* maps and sets etc )
*/
function printComplexValue(val, config, indentation, depth, refs, hasCalledToJSON) {
if (refs.includes(val)) return "[Circular]";
refs = [...refs];
refs.push(val);
const hitMaxDepth = ++depth > config.maxDepth;
const min = config.min;
if (config.callToJSON && !hitMaxDepth && val.toJSON && typeof val.toJSON === "function" && !hasCalledToJSON) return printer(val.toJSON(), config, indentation, depth, refs, true);
const toStringed = toString.call(val);
if (toStringed === "[object Arguments]") return hitMaxDepth ? "[Arguments]" : `${min ? "" : "Arguments "}[${printListItems(val, config, indentation, depth, refs, printer)}]`;
if (isToStringedArrayType(toStringed)) return hitMaxDepth ? `[${val.constructor.name}]` : `${!config.printBasicPrototype && val.constructor.name === "Array" ? "" : `${val.constructor.name} `}[${printListItems(val, config, indentation, depth, refs, printer)}]`;
if (toStringed === "[object Map]") return hitMaxDepth ? "[Map]" : `Map {${printIteratorEntries(val.entries(), config, indentation, depth, refs, printer, " => ", val.size)}}`;
if (toStringed === "[object Set]") return hitMaxDepth ? "[Set]" : `Set {${printIteratorValues(val.values(), config, indentation, depth, refs, printer, val.size)}}`;
// Avoid failure to serialize global window object in jsdom test environment.
// For example, not even relevant if window is prop of React element.
return hitMaxDepth || isWindow(val) ? `[${getConstructorName(val)}]` : `${!config.printBasicPrototype && getConstructorName(val) === "Object" ? "" : `${getConstructorName(val)} `}{${printObjectProperties(val, config, indentation, depth, refs, printer)}}`;
}
const ErrorPlugin = {
test: (val) => val && val instanceof Error,
serialize(val, config, indentation, depth, refs, printer) {
if (refs.includes(val)) return "[Circular]";
refs = [...refs, val];
const hitMaxDepth = ++depth > config.maxDepth;
const { message, cause, ...rest } = val;
const entries = {
message,
...typeof cause !== "undefined" ? { cause } : {},
...val instanceof AggregateError ? { errors: val.errors } : {},
...rest
};
const name = val.name !== "Error" ? val.name : getConstructorName(val);
return hitMaxDepth ? `[${name}]` : `${name} {${printObjectProperties(entries, config, indentation, depth, refs, printer, null)}}`;
}
};
function isNewPlugin(plugin) {
return plugin.serialize != null;
}
function printPlugin(plugin, val, config, indentation, depth, refs) {
let printed;
try {
printed = isNewPlugin(plugin) ? plugin.serialize(val, config, indentation, depth, refs, printer) : plugin.print(val, (valChild) => printer(valChild, config, indentation, depth, refs), (str) => {
const indentationNext = indentation + config.indent;
return indentationNext + str.replaceAll(NEWLINE_REGEXP, `\n${indentationNext}`);
}, {
edgeSpacing: config.spacingOuter,
min: config.min,
spacing: config.spacingInner
}, config.colors);
} catch (error) {
throw new PrettyFormatPluginError(error.message, error.stack);
}
if (typeof printed !== "string") throw new TypeError(`pretty-format: Plugin must return type "string" but instead returned "${typeof printed}".`);
return printed;
}
function findPlugin(plugins, val) {
for (const plugin of plugins) try {
if (plugin.test(val)) return plugin;
} catch (error) {
throw new PrettyFormatPluginError(error.message, error.stack);
}
return null;
}
function printer(val, config, indentation, depth, refs, hasCalledToJSON) {
let result;
const plugin = findPlugin(config.plugins, val);
if (plugin !== null) result = printPlugin(plugin, val, config, indentation, depth, refs);
else {
const basicResult = printBasicValue(val, config.printFunctionName, config.escapeRegex, config.escapeString, config.singleQuote);
if (basicResult !== null) result = basicResult;
else result = printComplexValue(val, config, indentation, depth, refs, hasCalledToJSON);
}
// Per-depth output budget (inspired by Node's util.inspect).
// Each depth level tracks output independently, so nested results
// don't inflate a single counter (which would undercount by ~Nx for
// N levels of nesting). Nodes at the same depth produce disjoint spans
// in the output string, so each bucket accurately reflects output at
// that level. Total output is bounded by maxDepth × maxOutputLength.
config._outputLengthPerDepth[depth] ??= 0;
config._outputLengthPerDepth[depth] += result.length;
if (config._outputLengthPerDepth[depth] > config.maxOutputLength) config.maxDepth = 0;
return result;
}
const DEFAULT_THEME = {
comment: "gray",
content: "reset",
prop: "yellow",
tag: "cyan",
value: "green"
};
const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME);
const DEFAULT_OPTIONS = {
callToJSON: true,
compareKeys: void 0,
escapeRegex: false,
escapeString: true,
highlight: false,
indent: 2,
maxDepth: Number.POSITIVE_INFINITY,
// Practical default hard-limit to avoid too long string being generated
// (Node's limit is buffer.constants.MAX_STRING_LENGTH ~ 512MB)
maxOutputLength: 1e6,
maxWidth: Number.POSITIVE_INFINITY,
min: false,
plugins: [],
printBasicPrototype: true,
printFunctionName: true,
printShadowRoot: true,
theme: DEFAULT_THEME,
singleQuote: false,
quoteKeys: true,
spacingInner: "\n",
spacingOuter: "\n"
};
function validateOptions(options) {
for (const key of Object.keys(options)) if (!Object.hasOwn(DEFAULT_OPTIONS, key)) throw new Error(`pretty-format: Unknown option "${key}".`);
if (options.min && options.indent !== void 0 && options.indent !== 0) throw new Error("pretty-format: Options \"min\" and \"indent\" cannot be used together.");
}
function getColorsHighlight() {
return DEFAULT_THEME_KEYS.reduce((colors, key) => {
const value = DEFAULT_THEME[key];
const color = value && y[value];
if (color && typeof color.close === "string" && typeof color.open === "string") colors[key] = color;
else throw new Error(`pretty-format: Option "theme" has a key "${key}" whose value "${value}" is undefined in ansi-styles.`);
return colors;
}, Object.create(null));
}
function getColorsEmpty() {
return DEFAULT_THEME_KEYS.reduce((colors, key) => {
colors[key] = {
close: "",
open: ""
};
return colors;
}, Object.create(null));
}
function getPrintFunctionName(options) {
return options?.printFunctionName ?? DEFAULT_OPTIONS.printFunctionName;
}
function getEscapeRegex(options) {
return options?.escapeRegex ?? DEFAULT_OPTIONS.escapeRegex;
}
function getEscapeString(options) {
return options?.escapeString ?? DEFAULT_OPTIONS.escapeString;
}
function getConfig(options) {
return {
callToJSON: options?.callToJSON ?? DEFAULT_OPTIONS.callToJSON,
colors: options?.highlight ? getColorsHighlight() : getColorsEmpty(),
compareKeys: typeof options?.compareKeys === "function" || options?.compareKeys === null ? options.compareKeys : DEFAULT_OPTIONS.compareKeys,
escapeRegex: getEscapeRegex(options),
escapeString: getEscapeString(options),
indent: options?.min ? "" : createIndent(options?.indent ?? DEFAULT_OPTIONS.indent),
maxDepth: options?.maxDepth ?? DEFAULT_OPTIONS.maxDepth,
maxWidth: options?.maxWidth ?? DEFAULT_OPTIONS.maxWidth,
min: options?.min ?? DEFAULT_OPTIONS.min,
plugins: options?.plugins ?? DEFAULT_OPTIONS.plugins,
printBasicPrototype: options?.printBasicPrototype ?? !options?.min,
printFunctionName: getPrintFunctionName(options),
printShadowRoot: options?.printShadowRoot ?? true,
spacingInner: options?.spacingInner ?? (options?.min ? " " : "\n"),
spacingOuter: options?.spacingOuter ?? (options?.min ? "" : "\n"),
singleQuote: options?.singleQuote ?? DEFAULT_OPTIONS.singleQuote,
quoteKeys: options?.quoteKeys ?? DEFAULT_OPTIONS.quoteKeys,
maxOutputLength: options?.maxOutputLength ?? DEFAULT_OPTIONS.maxOutputLength,
_outputLengthPerDepth: []
};
}
function createIndent(indent) {
return Array.from({ length: indent + 1 }).join(" ");
}
/**
* Returns a presentation string of your `val` object
* @param val any potential JavaScript object
* @param options Custom settings
*/
function format(val, options) {
if (options) validateOptions(options);
const config = getConfig(options);
const plugin = findPlugin(config.plugins, val);
if (plugin !== null) return printPlugin(plugin, val, config, "", 0, []);
const basicResult = printBasicValue(val, config.printFunctionName, config.escapeRegex, config.escapeString, config.singleQuote);
if (basicResult !== null) return basicResult;
return printComplexValue(val, config, "", 0, []);
}
const plugins = {
AsymmetricMatcher: plugin$5,
DOMCollection: plugin$4,
DOMElement: plugin$3,
Immutable: plugin$2,
ReactElement: plugin$1,
ReactTestComponent: plugin,
Error: ErrorPlugin
};
export { createDOMElementFilter as c, format as f, plugins as p };
File diff suppressed because it is too large. Load diff
+38
View File
@@ -0,0 +1,38 @@
import { i as init } from './init.BS957yFf.js';
if (!process.send) throw new Error("Expected worker to be run in node:child_process");
// Store globals in case tests overwrite them
const processExit = process.exit.bind(process);
const processSend = process.send.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const processRemoveAllListeners = process.removeAllListeners.bind(process);
// Work-around for nodejs/node#55094
if (process.execArgv.some((execArg) => execArg.startsWith("--prof") || execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"))) processOn("SIGTERM", () => processExit());
processOn("error", onError);
function workerInit(options) {
const { runTests } = options;
init({
post: (v) => processSend(v),
on: (cb) => processOn("message", cb),
off: (cb) => processOff("message", cb),
teardown: () => {
processRemoveAllListeners("message");
processOff("error", onError);
// the guard installed by the test runner stays active between test
// files: with `isolate: false` a late process.exit would kill the
// other files sharing this process
process.exit = processExit;
},
runTests: (state, traces) => runTests("run", state, traces),
collectTests: (state, traces) => runTests("collect", state, traces),
setup: options.setup
});
}
// Prevent leaving worker in loops where it tries to send message to closed main
// thread, errors, and tries to send the error.
function onError(error) {
if (error?.code === "ERR_IPC_CHANNEL_CLOSED" || error?.code === "EPIPE") processExit(1);
}
export { workerInit as w };
+18
View File
@@ -0,0 +1,18 @@
import { isMainThread, parentPort } from 'node:worker_threads';
import { i as init } from './init.BS957yFf.js';
if (isMainThread || !parentPort) throw new Error("Expected worker to be run in node:worker_threads");
function workerInit(options) {
const { runTests } = options;
init({
post: (response) => parentPort.postMessage(response),
on: (callback) => parentPort.on("message", callback),
off: (callback) => parentPort.off("message", callback),
teardown: () => parentPort.removeAllListeners("message"),
runTests: async (state, traces) => runTests("run", state, traces),
collectTests: async (state, traces) => runTests("collect", state, traces),
setup: options.setup
});
}
export { workerInit as w };
+450
View File
@@ -0,0 +1,450 @@
import { readFileSync } from 'node:fs';
import module$1, { isBuiltin } from 'node:module';
import { pathToFileURL } from 'node:url';
import { EvaluatedModules, ModuleRunner } from 'vite/module-runner';
import { e as environments, b as VitestTransport } from './index.1_nbEjJY.js';
import { r as resolve } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { b as serializeValue, c as createStackString, p as parseStacktrace } from './source-map.Dx3YPjIe.js';
import './index.M2dsQ_UQ.js';
import { m } from './tinyrainbow.Ht9iggcq.js';
import { Traces } from '../traces.js';
import { o as onCancel, V as VitestEvaluatedModules, a as rpcDone, c as createRuntimeRpc } from './rpc.DWKX1ZbI.js';
import { s as setupInspect } from './inspector.CvyFGlXm.js';
import { E as EnvironmentTeardownError } from './utils.DYj33du9.js';
function isBuiltinEnvironment(env) {
return env in environments;
}
const isWindows = process.platform === "win32";
const _loaders = /* @__PURE__ */ new Map();
function createEnvironmentLoader(root, rpc) {
const cachedLoader = _loaders.get(root);
if (!cachedLoader || cachedLoader.isClosed()) {
_loaders.delete(root);
const evaluatedModules = new EvaluatedModules();
const moduleRunner = new ModuleRunner({
hmr: false,
sourcemapInterceptor: "prepareStackTrace",
transport: new VitestTransport({
async fetchModule(id, importer, options) {
const result = await rpc.fetch(id, importer, "__vitest__", options);
if ("cached" in result) return {
code: readFileSync(result.tmp, "utf-8"),
...result
};
if (isWindows && "externalize" in result)
// TODO: vitest returns paths for external modules, but Vite returns file://
// https://github.com/vitejs/vite/pull/20449
result.externalize = isBuiltin(id) || /^(?:node:|data:|http:|https:|file:)/.test(id) ? result.externalize : pathToFileURL(result.externalize).toString();
return result;
},
async resolveId(id, importer) {
return rpc.resolve(id, importer, "__vitest__");
}
}, evaluatedModules, /* @__PURE__ */ new WeakMap())
});
_loaders.set(root, moduleRunner);
}
return _loaders.get(root);
}
async function loadNativeEnvironment(name, root, traces) {
const packageId = name[0] === "." || name[0] === "/" ? pathToFileURL(resolve(root, name)).toString() : import.meta.resolve(`vitest-environment-${name}`, pathToFileURL(root).toString());
return resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => import(packageId)));
}
function resolveEnvironmentFromModule(name, packageId, pkg) {
if (!pkg || !pkg.default || typeof pkg.default !== "object") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "setup" or/and "setupVM" method.`);
const environment = pkg.default;
if (environment.transformMode != null && environment.transformMode !== "web" && environment.transformMode !== "ssr") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "transformMode" method equal to "ssr" or "web", received "${environment.transformMode}".`);
if (environment.transformMode) {
console.warn(`The Vitest environment ${environment.name} defines the "transformMode". This options was deprecated in Vitest 4 and will be removed in the next major version. Please, use "viteEnvironment" instead.`);
// keep for backwards compat
environment.viteEnvironment ??= environment.transformMode === "ssr" ? "ssr" : "client";
}
return environment;
}
async function loadEnvironment(name, root, rpc, traces, viteModuleRunner) {
if (isBuiltinEnvironment(name)) return { environment: environments[name] };
if (!viteModuleRunner) return { environment: await loadNativeEnvironment(name, root, traces) };
const loader = createEnvironmentLoader(root, rpc);
const packageId = name[0] === "." || name[0] === "/" ? resolve(root, name) : (await traces.$("vitest.runtime.environment.resolve", () => rpc.resolve(`vitest-environment-${name}`, void 0, "__vitest__")))?.id ?? resolve(root, name);
return {
environment: resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => loader.import(packageId))),
loader
};
}
const cleanupListeners = /* @__PURE__ */ new Set();
const moduleRunnerListeners = /* @__PURE__ */ new Set();
function onCleanup(cb) {
cleanupListeners.add(cb);
}
async function cleanup() {
await Promise.all(Array.from(cleanupListeners, (l) => l()));
}
function onModuleRunner(cb) {
moduleRunnerListeners.add(cb);
}
function emitModuleRunner(moduleRunner) {
moduleRunnerListeners.forEach((l) => l(moduleRunner));
}
// Store globals in case tests overwrite them
const processListeners = process.listeners.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const dispose = [];
function listenForErrors(state) {
dispose.forEach((fn) => fn());
dispose.length = 0;
function catchError(err, type, event) {
const worker = state();
// if there is another listener, assume that it's handled by user code
// one is Vitest's own listener
if (processListeners(event).length > 1) return;
const error = serializeValue(err);
if (typeof error === "object" && error != null) {
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
}
state().rpc.onUnhandledError(error, type);
}
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException");
const unhandledRejection = (e) => catchError(e, "Unhandled Rejection", "unhandledRejection");
processOn("uncaughtException", uncaughtException);
processOn("unhandledRejection", unhandledRejection);
dispose.push(() => {
processOff("uncaughtException", uncaughtException);
processOff("unhandledRejection", unhandledRejection);
});
}
class GetterTracker {
static EXPORTS_MAX_INVOCATIONS = 1e6;
invocations = /* @__PURE__ */ new Map();
excessiveInvocations = /* @__PURE__ */ new Map();
createTracker(moduleId, defineExport) {
return (name, getter) => {
const key = `${moduleId}:${name}`;
defineExport(name, () => {
const count = (this.invocations.get(key) || 0) + 1;
this.invocations.set(key, count);
if (count > GetterTracker.EXPORTS_MAX_INVOCATIONS && !this.excessiveInvocations.has(key)) this.excessiveInvocations.set(key, {
moduleId,
exportName: name
});
return getter();
});
};
}
resetInvocations() {
this.invocations.clear();
this.excessiveInvocations.clear();
}
getExcessiveInvocations() {
return [...this.excessiveInvocations.values()];
}
}
const resolvingModules = /* @__PURE__ */ new Set();
async function execute(method, ctx, worker, traces) {
const prepareStart = performance.now();
const cleanups = [setupInspect(ctx)];
// RPC is used to communicate between worker (be it a thread worker or child process or a custom implementation) and the main thread
const rpc = ctx.rpc;
try {
// do not close the RPC channel so that we can get the error messages sent to the main thread
cleanups.push(async () => {
await Promise.all(rpc.$rejectPendingCalls(({ method, reject }) => {
reject(new EnvironmentTeardownError(`[vitest-worker]: Closing rpc while "${method}" was pending`));
}));
});
const state = {
ctx,
// here we create a new one, workers can reassign this if they need to keep it non-isolated
evaluatedModules: new VitestEvaluatedModules(),
resolvingModules,
moduleExecutionInfo: /* @__PURE__ */ new Map(),
config: ctx.config,
// this is set later by vm or base
environment: null,
durations: {
environment: 0,
prepare: prepareStart,
fetch: 0
},
rpc,
onCancel,
onCleanup: onCleanup,
providedContext: ctx.providedContext,
onFilterStackTrace(stack) {
return createStackString(parseStacktrace(stack));
},
metaEnv: ctx.metaEnv,
getterTracker: ctx.config.benchmark.enabled && !ctx.config.benchmark.suppressExportGetterWarnings ? new GetterTracker() : void 0
};
const methodName = method === "collect" ? "collectTests" : "runTests";
if (!worker[methodName] || typeof worker[methodName] !== "function") throw new TypeError(`Test worker should expose "runTests" method. Received "${typeof worker.runTests}".`);
await worker[methodName](state, traces);
} finally {
await rpcDone().catch(() => {});
await Promise.all(cleanups.map((fn) => fn())).catch(() => {});
}
}
function run(ctx, worker, traces) {
return execute("run", ctx, worker, traces);
}
function collect(ctx, worker, traces) {
return execute("collect", ctx, worker, traces);
}
async function teardown() {
await cleanup();
}
// default import: `flushCompileCache` only exists since Node 22.10, a named
// import would fail to link on older versions
function createImportMetaEnvProxy() {
const booleanKeys = [
"DEV",
"PROD",
"SSR"
];
return new Proxy(process.env, {
get(_, key) {
if (typeof key !== "string") return;
if (booleanKeys.includes(key)) return !!process.env[key];
return process.env[key];
},
set(_, key, value) {
if (typeof key !== "string") return true;
if (booleanKeys.includes(key)) process.env[key] = value ? "1" : "";
else process.env[key] = value;
return true;
}
});
}
const importMetaEnvProxy = createImportMetaEnvProxy();
const __vitest_worker_response__ = true;
const memoryUsage = process.memoryUsage.bind(process);
let reportMemory = false;
const streams = [{ write: process.stdout.write.bind(process.stdout) }, { write: process.stderr.write.bind(process.stderr) }];
// In worker threads stdio is proxied to the parent over a MessagePort with a
// backpressure protocol: a chunk stays buffered inside the worker until the
// parent acks the previous one. The pool starts `runner.stop()` as soon as it
// receives `testfileFinished`, and `thread.terminate()` halts the worker before
// buffered chunks are ever posted, losing output. An empty write's callback
// only fires after every previously buffered chunk has been acked, so awaiting
// it before signaling completion guarantees the output reached the parent.
// A cheap no-op for forks, where stdio goes through OS pipes.
function flushStdio() {
const flush = (stream) => new Promise((resolve) => {
try {
stream.write("", () => resolve(void 0));
} catch {
resolve(void 0);
}
});
return Promise.all(streams.map((stream) => flush(stream)));
}
let traces;
/** @experimental */
function init(worker) {
let runPromise;
let isRunning = false;
let workerTeardown;
let setupContext;
let poolId;
worker.on(onMessage);
if (worker.onModuleRunner) onModuleRunner(worker.onModuleRunner);
function send(response) {
worker.post(worker.serialize ? worker.serialize(response) : response);
}
async function onMessage(rawMessage) {
const message = worker.deserialize ? worker.deserialize(rawMessage) : rawMessage;
if (message?.__vitest_worker_request__ !== true) return;
switch (message.type) {
case "start": {
process.env.VITEST_POOL_ID = String(message.poolId);
process.env.VITEST_WORKER_ID = String(message.workerId);
reportMemory = message.options.reportMemory;
poolId = message.poolId;
if (message.context.config.disableColors) m();
traces ??= await new Traces({
enabled: message.traces.enabled,
sdkPath: message.traces.sdkPath
}).waitInit();
const { environment, config, pool } = message.context;
const context = traces.getContextFromCarrier(message.traces.otelCarrier);
// record telemetry as part of "start"
traces.recordInitSpan(context);
try {
setupContext = {
environment,
config,
pool,
rpc: createRuntimeRpc(worker),
metaEnv: importMetaEnvProxy,
projectName: config.name || "",
traces
};
workerTeardown = await traces.$("vitest.runtime.setup", { context }, () => worker.setup?.(setupContext));
send({
type: "started",
__vitest_worker_response__
});
} catch (error) {
send({
type: "started",
__vitest_worker_response__,
error: serializeValue(error)
});
}
break;
}
case "run":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeValue(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeValue(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.run", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => run({
...setupContext,
...message.context,
concurrencyId: poolId
}, worker, traces).catch((error) => serializeValue(error)));
const error = await runPromise;
await flushStdio();
send({
type: "testfileFinished",
__vitest_worker_response__,
error,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "collect":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeValue(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeValue(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.collect", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => collect({
...setupContext,
...message.context,
concurrencyId: poolId
}, worker, traces).catch((error) => serializeValue(error)));
const error = await runPromise;
await flushStdio();
send({
type: "testfileFinished",
__vitest_worker_response__,
error,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "stop": {
await runPromise;
// Persist this worker's compile cache before the parent tears the
// worker down — forks are SIGTERM'd and never reach Node's exit-time
// flush, so without this the cache stays write-only for them. Runs
// even when teardown throws (the compiled modules are still worth
// persisting). A no-op when the cache is disabled or was fully loaded
// from disk, and cheap (~tens of ms) otherwise, so every worker can
// afford it.
const persistCompileCache = () => {
try {
module$1.flushCompileCache?.();
} catch {}
};
try {
const context = traces.getContextFromCarrier(message.otelCarrier);
const error = await traces.$("vitest.runtime.teardown", { context }, async () => {
const error = await teardown().catch((error) => serializeValue(error));
await workerTeardown?.();
return error;
});
await traces.finish();
persistCompileCache();
await flushStdio();
send({
type: "stopped",
error,
__vitest_worker_response__
});
} catch (error) {
persistCompileCache();
await flushStdio();
send({
type: "stopped",
error: serializeValue(error),
__vitest_worker_response__
});
}
worker.teardown?.();
break;
}
}
}
}
function getFilesWithLocations(files) {
return files.flatMap((file) => {
if (!file.testLocations) return file.filepath;
return file.testLocations.map((location) => {
return `${file}:${location}`;
});
});
}
export { listenForErrors as a, emitModuleRunner as e, init as i, loadEnvironment as l };
+53
View File
@@ -0,0 +1,53 @@
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
const __require = createRequire(import.meta.url);
let inspector;
let session;
/**
* Enables debugging inside `worker_threads` and `child_process`.
* Should be called as early as possible when worker/process has been set up.
*/
function setupInspect(ctx) {
const config = ctx.config;
const isEnabled = config.inspector.enabled;
if (isEnabled) {
inspector = __require("node:inspector");
if (!(inspector.url() !== void 0)) {
inspector.open(config.inspector.port, config.inspector.host, config.inspector.waitForDebugger);
if (config.inspectBrk) {
const firstTestFile = typeof ctx.files[0] === "string" ? ctx.files[0] : ctx.files[0].filepath;
// Stop at first test file
if (firstTestFile) {
session = new inspector.Session();
session.connect();
session.post("Debugger.enable");
session.post("Debugger.setBreakpointByUrl", {
lineNumber: 0,
url: pathToFileURL(firstTestFile)
});
}
}
}
}
const keepOpen = shouldKeepOpen(config);
return function cleanup() {
if (isEnabled && !keepOpen && inspector) {
inspector.close();
session?.disconnect();
}
};
}
function closeInspector(config) {
const keepOpen = shouldKeepOpen(config);
if (inspector && !keepOpen) {
inspector.close();
session?.disconnect();
}
}
function shouldKeepOpen(config) {
// In watch mode the inspector can persist re-runs if isolation is disabled and a single worker is used
return config.watch && config.isolate === false && config.maxWorkers === 1;
}
export { closeInspector as c, setupInspect as s };
+36
View File
@@ -0,0 +1,36 @@
import { builtinModules } from 'node:module';
// copied from vite
// https://github.com/vitejs/vite/blob/814120f2ad387ca3d1e16c7dd403b04ca4b97f75/packages/vite/src/node/utils.ts#L106
// Supported by Node, Deno, Bun
const NODE_BUILTIN_NAMESPACE = "node:";
// Supported by Deno
const NPM_BUILTIN_NAMESPACE = "npm:";
// Supported by Bun
const BUN_BUILTIN_NAMESPACE = "bun:";
// Some runtimes like Bun injects namespaced modules here, which is not a node builtin
const nodeBuiltins = builtinModules.filter((id) => !id.includes(":"));
const { bun: isBun, deno: isDeno } = process.versions;
// TODO: Use `isBuiltin` from `node:module`, but Deno doesn't support it
function isBuiltin(id) {
if (isDeno && id.startsWith(NPM_BUILTIN_NAMESPACE)) return true;
if (isBun && id.startsWith(BUN_BUILTIN_NAMESPACE)) return true;
return isNodeBuiltin(id);
}
function isNodeBuiltin(id) {
if (id.startsWith(NODE_BUILTIN_NAMESPACE)) return true;
return nodeBuiltins.includes(id);
}
const browserExternalId = "__vite-browser-external";
const browserExternalLength = 24;
function isBrowserExternal(id) {
return id.startsWith(browserExternalId);
}
function toBuiltin(id) {
if (id.startsWith(browserExternalId)) id = id.slice(browserExternalLength);
if (id.startsWith(NPM_BUILTIN_NAMESPACE) || id.startsWith(BUN_BUILTIN_NAMESPACE) || id.startsWith(NODE_BUILTIN_NAMESPACE)) return id;
if (isDeno || isBun) return id;
return `node:${id}`;
}
export { isBrowserExternal as a, isBuiltin as i, toBuiltin as t };
+147
View File
@@ -0,0 +1,147 @@
import module$1, { isBuiltin } from 'node:module';
import { fileURLToPath } from 'node:url';
import { MessageChannel } from 'node:worker_threads';
import { initSyntaxLexers, hoistMocks } from '@vitest/mocker/transforms';
import { c as cleanUrl, r as resolve } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { p as parse } from './acorn.C1kjbUFw.js';
import MagicString from 'magic-string';
import { y } from './tinyrainbow.Ht9iggcq.js';
import { distDir } from '../path.js';
import { t as toBuiltin } from './modules.BJuCwlRJ.js';
import 'node:path';
const NOW_LENGTH = Date.now().toString().length;
const REGEXP_VITEST = new RegExp(`%3Fvitest=\\d{${NOW_LENGTH}}`);
const REGEXP_MOCK_ACTUAL = /\?mock=actual/;
async function setupNodeLoaderHooks(worker) {
if (module$1.setSourceMapsSupport) module$1.setSourceMapsSupport(true);
else if (process.setSourceMapsEnabled) process.setSourceMapsEnabled(true);
if (worker.config.experimental.nodeLoader !== false) await initSyntaxLexers();
if (typeof module$1.registerHooks === "function") module$1.registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier.includes("mock=actual")) {
// url is already resolved by `importActual`
const moduleId = specifier.replace(REGEXP_MOCK_ACTUAL, "");
const builtin = isBuiltin(moduleId);
return {
url: builtin ? toBuiltin(moduleId) : moduleId,
format: builtin ? "builtin" : void 0,
shortCircuit: true
};
}
const isVitest = specifier.includes("%3Fvitest=");
const result = nextResolve(isVitest ? specifier.replace(REGEXP_VITEST, "") : specifier, context);
// avoid tracking /node_modules/ module graph for performance reasons
if (context.parentURL && result.url && !result.url.includes("/node_modules/")) worker.rpc.ensureModuleGraphEntry(result.url, context.parentURL).catch(() => {
// ignore errors
});
// this is require for in-source tests to be invalidated if
// one of the files already imported it in --maxWorkers=1 --no-isolate
if (isVitest) result.url = `${result.url}?vitest=${Date.now()}`;
if (worker.config.experimental.nodeLoader === false || !context.parentURL || result.url.includes(distDir) || context.parentURL?.toString().includes(distDir)) return result;
const mockedResult = getNativeMocker()?.resolveMockedModule(result.url, context.parentURL);
if (mockedResult != null) return mockedResult;
return result;
},
load: worker.config.experimental.nodeLoader === false ? void 0 : createLoadHook()
});
else if (module$1.register) {
if (worker.config.experimental.nodeLoader !== false) console.warn(`${y.bgYellow(" WARNING ")} "module.registerHooks" is not supported in Node.js ${process.version}. This means that some features like module mocking or in-source testing are not supported. Upgrade your Node.js version to at least 22.15 or disable "experimental.nodeLoader" flag manually.\n`);
const { port1, port2 } = new MessageChannel();
port1.unref();
port2.unref();
port1.on("message", (data) => {
if (!data || typeof data !== "object") return;
switch (data.event) {
case "register-module-graph-entry": {
const { url, parentURL } = data;
worker.rpc.ensureModuleGraphEntry(url, parentURL);
return;
}
default: console.error("Unknown message event:", data.event);
}
});
/** Registers {@link file://./../nodejsWorkerLoader.ts} */
module$1.register("#nodejs-worker-loader", {
parentURL: import.meta.url,
data: { port: port2 },
transferList: [port2]
});
} else if (!process.versions.deno && !process.versions.bun) console.warn("\"module.registerHooks\" and \"module.register\" are not supported. Some Vitest features may not work. Please, use Node.js 18.19.0 or higher.");
}
function replaceInSourceMarker(url, source, ms) {
const re = /import\.meta\.vitest/g;
let match;
let overridden = false;
// eslint-disable-next-line no-cond-assign
while (match = re.exec(source)) {
const { index, "0": code } = match;
overridden = true;
// should it support process.vitest for CJS modules?
ms().overwrite(index, index + code.length, "IMPORT_META_TEST()");
}
if (overridden) {
const filename = resolve(fileURLToPath(url));
// appending instead of prepending because functions are hoisted and we don't change the offset
ms().append(`;\nfunction IMPORT_META_TEST() { return typeof __vitest_worker__ !== 'undefined' && __vitest_worker__.filepath === "${filename.replace(/"/g, "\\\"")}" ? __vitest_index__ : undefined; }`);
}
}
const ignoreFormats = /* @__PURE__ */ new Set([
"addon",
"builtin",
"wasm"
]);
function createLoadHook(_worker) {
return (url, context, nextLoad) => {
const result = url.includes("mock=") && isBuiltin(cleanUrl(url)) ? { format: "commonjs" } : nextLoad(url, context);
if (result.format && ignoreFormats.has(result.format) || url.includes(distDir)) return result;
const mocker = getNativeMocker();
mocker?.checkCircularManualMock(url);
if (url.includes("mock=automock") || url.includes("mock=autospy")) {
const automockedResult = mocker?.loadAutomock(url, result);
if (automockedResult != null) return automockedResult;
return result;
}
if (url.includes("mock=manual")) {
const mockedResult = mocker?.loadManualMock(url, result);
if (mockedResult != null) return mockedResult;
return result;
}
// ignore non-vitest modules for performance reasons,
// vi.hoisted and vi.mock won't work outside of test files or setup files
if (!result.source || !url.includes("vitest=")) return result;
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const source = result.source.toString();
const transformedCode = result.format?.includes("typescript") ? module$1.stripTypeScriptTypes(source) : source;
let _ms;
const ms = () => _ms || (_ms = new MagicString(source));
if (source.includes("import.meta.vitest")) replaceInSourceMarker(url, source, ms);
hoistMocks(transformedCode, filename, (code) => parse(code, {
ecmaVersion: "latest",
sourceType: result.format === "module" || result.format === "module-typescript" || result.format === "typescript" ? "module" : "script"
}), {
magicString: ms,
globalThisAccessor: "\"__vitest_mocker__\""
});
let code;
if (_ms) code = `${_ms.toString()}\n//# sourceMappingURL=${genSourceMapUrl(_ms.generateMap({
hires: "boundary",
source: filename
}))}`;
else code = source;
return {
format: result.format,
shortCircuit: true,
source: code
};
};
}
function genSourceMapUrl(map) {
if (typeof map !== "string") map = JSON.stringify(map);
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
}
function getNativeMocker() {
return typeof __vitest_mocker__ !== "undefined" ? __vitest_mocker__ : void 0;
}
export { setupNodeLoaderHooks };
+213
View File
@@ -0,0 +1,213 @@
import module$1, { isBuiltin } from 'node:module';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { automockModule, createManualModuleSource, collectModuleExports } from '@vitest/mocker/transforms';
import { c as cleanUrl, e as createDefer, i as isAbsolute } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { p as parse } from './acorn.C1kjbUFw.js';
import { t as toBuiltin } from './modules.BJuCwlRJ.js';
import { B as BareModuleMocker, n as normalizeModuleId } from './index.1_nbEjJY.js';
import 'node:fs';
import './utils.DYj33du9.js';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import 'node:vm';
import 'vite/module-runner';
import '../traces.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
class NativeModuleMocker extends BareModuleMocker {
wrapDynamicImport(moduleFactory) {
if (typeof moduleFactory === "function") return new Promise((resolve, reject) => {
this.resolveMocks().finally(() => {
moduleFactory().then(resolve, reject);
});
});
return moduleFactory;
}
resolveMockedModule(url, parentURL) {
// don't mock modules inside of packages because there is
// a high chance that it uses `require` which is not mockable
// because we use top-level await in "manual" mocks.
// for the sake of consistency we don't support mocking anything at all
if (parentURL.includes("/node_modules/")) return;
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const moduleId = normalizeModuleId(filename);
const mockedModule = this.getDependencyMock(moduleId);
if (!mockedModule) return;
if (mockedModule.type === "redirect") return {
url: pathToFileURL(mockedModule.redirect).toString(),
shortCircuit: true
};
if (mockedModule.type === "automock" || mockedModule.type === "autospy") return {
url: injectQuery(url, parentURL, `mock=${mockedModule.type}`),
shortCircuit: true
};
if (mockedModule.type === "manual") return {
url: injectQuery(url, parentURL, "mock=manual"),
shortCircuit: true
};
}
loadAutomock(url, result) {
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const moduleId = cleanUrl(normalizeModuleId(filename));
let source;
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(moduleId);
const exports = Object.keys(builtinModule);
source = `
import * as builtinModule from '${toBuiltin(moduleId)}?mock=actual'
${exports.map((key, index) => {
return `
const __${index} = builtinModule["${key}"]
export { __${index} as "${key}" }
`;
}).join("")}`;
} else source = result.source?.toString();
if (source == null) return;
const mockType = url.includes("mock=automock") ? "automock" : "autospy";
const transformedCode = transformCode(source, result.format || "module", moduleId);
try {
const ms = automockModule(transformedCode, mockType, (code) => parse(code, {
sourceType: "module",
ecmaVersion: "latest"
}), { id: moduleId });
return {
format: "module",
source: `${ms.toString()}\n//# sourceMappingURL=${genSourceMapUrl(ms.generateMap({
hires: "boundary",
source: moduleId
}))}`,
shortCircuit: true
};
} catch (cause) {
throw new Error(`Cannot automock '${url}' because it failed to parse.`, { cause });
}
}
loadManualMock(url, result) {
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const moduleId = cleanUrl(normalizeModuleId(filename));
// should not be possible
if (this.getDependencyMock(moduleId)?.type !== "manual") {
console.warn(`Vitest detected unregistered manual mock ${moduleId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
return;
}
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(toBuiltin(moduleId));
const exports = Object.keys(builtinModule);
return {
format: "module",
source: createManualModuleSource(moduleId, exports),
shortCircuit: true
};
}
if (!result.source) return;
const transformedCode = transformCode(result.source.toString(), result.format || "module", moduleId);
if (transformedCode == null) return;
const format = result.format?.startsWith("module") ? "module" : "commonjs";
try {
// we parse the module with es/cjs-module-lexer to find the original exports -- we assume the same ones are returned from the factory
// injecting new keys is not supported (and should not be advised anyway)
const exports = collectModuleExports(moduleId, transformedCode, format);
return {
format: "module",
source: createManualModuleSource(moduleId, exports),
shortCircuit: true
};
} catch (cause) {
throw new Error(`Failed to mock '${url}'. See the cause for more information.`, { cause });
}
}
processedModules = /* @__PURE__ */ new Map();
checkCircularManualMock(url) {
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const id = cleanUrl(normalizeModuleId(filename));
this.processedModules.set(id, (this.processedModules.get(id) ?? 0) + 1);
// the module is mocked and requested a second time, let's resolve
// the factory function that will redefine the exports later
if (this.originalModulePromises.has(id)) {
const factoryPromise = this.factoryPromises.get(id);
this.originalModulePromises.get(id)?.resolve({ __factoryPromise: factoryPromise });
}
}
originalModulePromises = /* @__PURE__ */ new Map();
factoryPromises = /* @__PURE__ */ new Map();
// potential performance improvement:
// store by URL, not ids, no need to call url.*to* methods and normalizeModuleId
getFactoryModule(id) {
const mock = this.getMockerRegistry().getById(id);
if (!mock || mock.type !== "manual") throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
const mockResult = mock.resolve();
if (mockResult instanceof Promise) {
// to avoid circular dependency, we resolve this function as {__factoryPromise} in `checkCircularManualMock`
// when it's requested the second time. then the exports are exposed as `undefined`,
// but later redefined when the promise is actually resolved
const promise = createDefer();
promise.finally(() => {
this.originalModulePromises.delete(id);
});
mockResult.then(promise.resolve, promise.reject).finally(() => {
this.factoryPromises.delete(id);
});
this.factoryPromises.set(id, mockResult);
this.originalModulePromises.set(id, promise);
// Node.js on windows processes all the files first, and then runs them
// unlike Node.js logic on Mac and Unix where it also runs the code while evaluating
// So on Linux/Mac this `if` won't be hit because `checkCircularManualMock` will resolve it
// And on Windows, the `checkCircularManualMock` will never have `originalModulePromises`
// because `getFactoryModule` is not called until the evaluation phase
// But if we track how many times the module was transformed,
// we can deduce when to return `__factoryPromise` to support circular modules
if ((this.processedModules.get(id) ?? 0) > 1) {
this.processedModules.set(id, (this.processedModules.get(id) ?? 1) - 1);
promise.resolve({ __factoryPromise: mockResult });
}
return promise;
}
return mockResult;
}
importActual(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "actual");
return import(url.toString());
}
importMock(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
// file is already mocked
if (resolvedId.includes("mock=")) return import(resolvedId);
const filename = fileURLToPath(resolvedId);
const external = !isAbsolute(filename) || this.isModuleDirectory(resolvedId) ? normalizeModuleId(rawId) : null;
// file is not mocked, automock or redirect it
const redirect = this.findMockRedirect(filename, external);
if (redirect) return import(pathToFileURL(redirect).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "automock");
return import(url.toString());
}
}
const replacePercentageRE = /%/g;
function injectQuery(url, importer, queryToInject) {
const { search, hash } = new URL(url.replace(replacePercentageRE, "%25"), importer);
return `${cleanUrl(url)}?${queryToInject}${search ? `&${search.slice(1)}` : ""}${hash ?? ""}`;
}
let __require;
function getBuiltinModule(moduleId) {
__require ??= module$1.createRequire(import.meta.url);
return __require(`${moduleId}?mock=actual`);
}
function genSourceMapUrl(map) {
if (typeof map !== "string") map = JSON.stringify(map);
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
}
function transformCode(code, format, filename) {
if (format.includes("typescript")) {
if (!module$1.stripTypeScriptTypes) throw new Error(`Cannot parse '${filename}' because "module.stripTypeScriptTypes" is not supported. Module mocking requires Node.js 22.15 or higher. This is NOT a bug of Vitest.`);
return module$1.stripTypeScriptTypes(code);
}
return code;
}
export { NativeModuleMocker };
File diff suppressed because it is too large. Load diff
+47
View File
@@ -0,0 +1,47 @@
import { g as getWorkerState } from './utils.DYj33du9.js';
import { promises, existsSync } from 'node:fs';
import { i as isAbsolute, r as resolve, d as dirname, j as join, b as basename } from './pathe.M-eThtNZ.Clq7AXhv.js';
class NodeSnapshotEnvironment {
options;
constructor(options = {}) {
this.options = options;
}
getVersion() {
return "1";
}
getHeader() {
return `// Snapshot v${this.getVersion()}`;
}
async resolveRawPath(testPath, rawPath) {
return isAbsolute(rawPath) ? rawPath : resolve(dirname(testPath), rawPath);
}
async resolvePath(filepath) {
return join(join(dirname(filepath), this.options.snapshotsDirName ?? "__snapshots__"), `${basename(filepath)}.snap`);
}
async prepareDirectory(dirPath) {
await promises.mkdir(dirPath, { recursive: true });
}
async saveSnapshotFile(filepath, snapshot) {
await promises.mkdir(dirname(filepath), { recursive: true });
await promises.writeFile(filepath, snapshot, "utf-8");
}
async readSnapshotFile(filepath) {
if (!existsSync(filepath)) return null;
return promises.readFile(filepath, "utf-8");
}
async removeSnapshotFile(filepath) {
if (existsSync(filepath)) await promises.unlink(filepath);
}
}
class VitestNodeSnapshotEnvironment extends NodeSnapshotEnvironment {
getHeader() {
return `// Vitest Snapshot v${this.getVersion()}, https://vitest.dev/guide/snapshot.html`;
}
resolvePath(filepath) {
return getWorkerState().rpc.resolveSnapshotPath(filepath);
}
}
export { VitestNodeSnapshotEnvironment };
+24
View File
@@ -0,0 +1,24 @@
const lineSplitRE = /\r?\n/;
function positionToOffset(source, lineNumber, columnNumber) {
const lines = source.split(lineSplitRE);
const nl = /\r\n/.test(source) ? 2 : 1;
let start = 0;
if (lineNumber > lines.length) return source.length;
for (let i = 0; i < lineNumber - 1; i++) start += lines[i].length + nl;
return start + columnNumber;
}
function offsetToLineNumber(source, offset) {
if (offset > source.length) throw new Error(`offset is longer than source length! offset ${offset} > length ${source.length}`);
const lines = source.split(lineSplitRE);
const nl = /\r\n/.test(source) ? 2 : 1;
let counted = 0;
let line = 0;
for (; line < lines.length; line++) {
const lineLength = lines[line].length + nl;
if (counted + lineLength >= offset) break;
counted += lineLength;
}
return line + 1;
}
export { lineSplitRE as l, offsetToLineNumber as o, positionToOffset as p };
+489
View File
@@ -0,0 +1,489 @@
// TODO: this is all copy pasted from Vite - can they expose a module that exports only constants?
const KNOWN_ASSET_TYPES = [
"apng",
"bmp",
"png",
"jpe?g",
"jfif",
"pjpeg",
"pjp",
"gif",
"svg",
"ico",
"webp",
"avif",
"mp4",
"webm",
"ogg",
"mp3",
"wav",
"flac",
"aac",
"woff2?",
"eot",
"ttf",
"otf",
"webmanifest",
"pdf",
"txt"
];
const KNOWN_ASSET_RE = new RegExp(`\\.(${KNOWN_ASSET_TYPES.join("|")})$`);
const CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
/**
* Prefix for resolved Ids that are not valid browser import specifiers
*/
const VALID_ID_PREFIX = `/@id/`;
/**
* Plugins that use 'virtual modules' (e.g. for helper functions), prefix the
* module ID with `\0`, a convention from the rollup ecosystem.
* This prevents other plugins from trying to process the id (like node resolution),
* and core features like sourcemaps can use this info to differentiate between
* virtual modules and regular files.
* `\0` is not a permitted char in import URLs so we have to replace them during
* import analysis. The id will be decoded back before entering the plugins pipeline.
* These encoded virtual ids are also prefixed by the VALID_ID_PREFIX, so virtual
* modules in the browser end up encoded as `/@id/__x00__{id}`
*/
const NULL_BYTE_PLACEHOLDER = `__x00__`;
/**
* Get original stacktrace without source map support the most performant way.
* - Create only 1 stack frame.
* - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms).
*/
function createSimpleStackTrace(options) {
const { message = "$$stack trace error", stackTraceLimit = 1 } = options || {};
const limit = Error.stackTraceLimit;
const prepareStackTrace = Error.prepareStackTrace;
Error.stackTraceLimit = stackTraceLimit;
Error.prepareStackTrace = (e) => e.stack;
const stackTrace = new Error(message).stack || "";
Error.prepareStackTrace = prepareStackTrace;
Error.stackTraceLimit = limit;
return stackTrace;
}
function notNullish(v) {
return v != null;
}
function assertTypes(value, name, types) {
const receivedType = typeof value;
if (!types.includes(receivedType)) throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`);
}
function isPrimitive(value) {
return value === null || typeof value !== "function" && typeof value !== "object";
}
function slash(path) {
return path.replace(/\\/g, "/");
}
const postfixRE = /[?#].*$/;
function cleanUrl(url) {
return url.replace(postfixRE, "");
}
function splitFileAndPostfix(path) {
const file = cleanUrl(path);
return {
file,
postfix: path.slice(file.length)
};
}
const externalRE = /^(?:[a-z]+:)?\/\//;
const isExternalUrl = (url) => externalRE.test(url);
/**
* Prepend `/@id/` and replace null byte so the id is URL-safe.
* This is prepended to resolved ids that are not valid browser
* import specifiers by the importAnalysis plugin.
*/
function wrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id : VALID_ID_PREFIX + id.replace("\0", NULL_BYTE_PLACEHOLDER);
}
/**
* Undo {@link wrapId}'s `/@id/` and null byte replacements.
*/
function unwrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length).replace(NULL_BYTE_PLACEHOLDER, "\0") : id;
}
function withTrailingSlash(path) {
if (path.at(-1) !== "/") return `${path}/`;
return path;
}
function filterOutComments(s) {
const result = [];
let commentState = "none";
for (let i = 0; i < s.length; ++i) if (commentState === "singleline") {
if (s[i] === "\n") commentState = "none";
} else if (commentState === "multiline") {
if (s[i - 1] === "*" && s[i] === "/") commentState = "none";
} else if (commentState === "none") if (s[i] === "/" && s[i + 1] === "/") commentState = "singleline";
else if (s[i] === "/" && s[i + 1] === "*") {
commentState = "multiline";
i += 2;
} else result.push(s[i]);
return result.join("");
}
const bareImportRE = /^(?![a-z]:)[\w@](?!.*:\/\/)/i;
function isBareImport(id) {
return bareImportRE.test(id);
}
function toArray(array) {
array ??= [];
if (Array.isArray(array)) return array;
return [array];
}
function isObject(item) {
return item != null && typeof item === "object" && !Array.isArray(item);
}
function isFinalObj(obj) {
return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype;
}
function getType(value) {
return Object.prototype.toString.apply(value).slice(8, -1);
}
function collectOwnProperties(obj, collector) {
const collect = typeof collector === "function" ? collector : (key) => collector.add(key);
Object.getOwnPropertyNames(obj).forEach(collect);
Object.getOwnPropertySymbols(obj).forEach(collect);
}
function getOwnProperties(obj) {
const ownProps = /* @__PURE__ */ new Set();
if (isFinalObj(obj)) return [];
collectOwnProperties(obj, ownProps);
return Array.from(ownProps);
}
const defaultCloneOptions = { forceWritable: false };
function deepClone(val, options = defaultCloneOptions) {
return clone(val, /* @__PURE__ */ new WeakMap(), options);
}
function clone(val, seen, options = defaultCloneOptions) {
let k, out;
if (seen.has(val)) return seen.get(val);
if (Array.isArray(val)) {
out = Array.from({ length: k = val.length });
seen.set(val, out);
while (k--) out[k] = clone(val[k], seen, options);
return out;
}
if (Object.prototype.toString.call(val) === "[object Object]") {
out = Object.create(Object.getPrototypeOf(val));
seen.set(val, out);
// we don't need properties from prototype
const props = getOwnProperties(val);
for (const k of props) {
const descriptor = Object.getOwnPropertyDescriptor(val, k);
if (!descriptor) continue;
const cloned = clone(val[k], seen, options);
if (options.forceWritable) Object.defineProperty(out, k, {
enumerable: descriptor.enumerable,
configurable: true,
writable: true,
value: cloned
});
else if ("get" in descriptor) Object.defineProperty(out, k, {
...descriptor,
get() {
return cloned;
}
});
else Object.defineProperty(out, k, {
...descriptor,
value: cloned
});
}
return out;
}
return val;
}
function noop() {}
function objectAttr(source, path, defaultValue = void 0) {
// a[3].b -> a.3.b
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = source;
for (const p of paths) {
result = new Object(result)[p];
if (result === void 0) return defaultValue;
}
return result;
}
function createDefer() {
let resolve = null;
let reject = null;
const p = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
p.resolve = resolve;
p.reject = reject;
return p;
}
/**
* If code starts with a function call, will return its last index, respecting arguments.
* This will return 25 - last ending character of toMatch ")"
* Also works with callbacks
* ```
* toMatch({ test: '123' });
* toBeAliased('123')
* ```
*/
function getCallLastIndex(code) {
let charIndex = -1;
let inString = null;
let startedBracers = 0;
let endedBracers = 0;
let beforeChar = null;
while (charIndex <= code.length) {
beforeChar = code[charIndex];
charIndex++;
const char = code[charIndex];
if ((char === "\"" || char === "'" || char === "`") && beforeChar !== "\\") {
if (inString === char) inString = null;
else if (!inString) inString = char;
}
if (!inString) {
if (char === "(") startedBracers++;
if (char === ")") endedBracers++;
}
if (startedBracers && endedBracers && startedBracers === endedBracers) return charIndex;
}
return null;
}
function isNegativeNaN(val) {
if (!Number.isNaN(val)) return false;
const f64 = /* @__PURE__ */ new Float64Array(1);
f64[0] = val;
return new Uint32Array(f64.buffer)[1] >>> 31 === 1;
}
function toString(v) {
return Object.prototype.toString.call(v);
}
function isPlainObject(val) {
return toString(val) === "[object Object]" && (!val.constructor || val.constructor.name === "Object");
}
function isMergeableObject(item) {
return isPlainObject(item) && !Array.isArray(item);
}
function ordinal(i) {
const j = i % 10;
const k = i % 100;
if (j === 1 && k !== 11) return `${i}st`;
if (j === 2 && k !== 12) return `${i}nd`;
if (j === 3 && k !== 13) return `${i}rd`;
return `${i}th`;
}
/**
* Deep merge :P
*
* Will merge objects only if they are plain
*
* Do not merge types - it is very expensive and usually it's better to case a type here
*/
function deepMerge(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (source === void 0) return target;
if (isMergeableObject(target) && isMergeableObject(source)) Object.keys(source).forEach((key) => {
const _source = source;
if (isMergeableObject(_source[key])) {
if (!target[key]) target[key] = {};
deepMerge(target[key], _source[key]);
} else target[key] = _source[key];
});
return deepMerge(target, ...sources);
}
function unique(array) {
return Array.from(new Set(array));
}
function sanitizeFilePath(s) {
// eslint-disable-next-line no-control-regex
return s.replace(/[\x00-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-");
}
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
function normalizeWindowsPath(input = "") {
if (!input) {
return input;
}
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
}
const _UNC_REGEX = /^[/\\]{2}/;
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
const normalize = function(path) {
if (path.length === 0) {
return ".";
}
path = normalizeWindowsPath(path);
const isUNCPath = path.match(_UNC_REGEX);
const isPathAbsolute = isAbsolute(path);
const trailingSeparator = path[path.length - 1] === "/";
path = normalizeString(path, !isPathAbsolute);
if (path.length === 0) {
if (isPathAbsolute) {
return "/";
}
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator) {
path += "/";
}
if (_DRIVE_LETTER_RE.test(path)) {
path += "/";
}
if (isUNCPath) {
if (!isPathAbsolute) {
return `//./${path}`;
}
return `//${path}`;
}
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
};
const join = function(...segments) {
let path = "";
for (const seg of segments) {
if (!seg) {
continue;
}
if (path.length > 0) {
const pathTrailing = path[path.length - 1] === "/";
const segLeading = seg[0] === "/";
const both = pathTrailing && segLeading;
if (both) {
path += seg.slice(1);
} else {
path += pathTrailing || segLeading ? seg : `/${seg}`;
}
} else {
path += seg;
}
}
return normalize(path);
};
function cwd() {
if (typeof process !== "undefined" && typeof process.cwd === "function") {
return process.cwd().replace(/\\/g, "/");
}
return "/";
}
const resolve = function(...arguments_) {
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
let resolvedPath = "";
let resolvedAbsolute = false;
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
const path = index >= 0 ? arguments_[index] : cwd();
if (!path || path.length === 0) {
continue;
}
resolvedPath = `${path}/${resolvedPath}`;
resolvedAbsolute = isAbsolute(path);
}
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
return `/${resolvedPath}`;
}
return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) {
char = path[index];
} else if (char === "/") {
break;
} else {
char = "/";
}
if (char === "/") {
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) {
res += `/${path.slice(lastSlash + 1, index)}`;
} else {
res = path.slice(lastSlash + 1, index);
}
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
const isAbsolute = function(p) {
return _IS_ABSOLUTE_RE.test(p);
};
const extname = function(p) {
if (p === "..") return "";
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
return match && match[1] || "";
};
const relative = function(from, to) {
const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
return _to.join("/");
}
const _fromCopy = [..._from];
for (const segment of _fromCopy) {
if (_to[0] !== segment) {
break;
}
_from.shift();
_to.shift();
}
return [..._from.map(() => ".."), ..._to].join("/");
};
const dirname = function(p) {
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
segments[0] += "/";
}
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
};
const basename = function(p, extension) {
const segments = normalizeWindowsPath(p).split("/");
let lastSegment = "";
for (let i = segments.length - 1; i >= 0; i--) {
const val = segments[i];
if (val) {
lastSegment = val;
break;
}
}
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
};
export { objectAttr as A, getCallLastIndex as B, CSS_LANGS_RE as C, noop as D, createSimpleStackTrace as E, withTrailingSlash as F, wrapId as G, deepMerge as H, isExternalUrl as I, unwrapId as J, KNOWN_ASSET_TYPES as K, sanitizeFilePath as L, relative as a, basename as b, cleanUrl as c, dirname as d, createDefer as e, extname as f, getType as g, KNOWN_ASSET_RE as h, isAbsolute as i, join as j, splitFileAndPostfix as k, isBareImport as l, isPrimitive as m, normalize as n, notNullish as o, deepClone as p, getOwnProperties as q, resolve as r, slash as s, toArray as t, isObject as u, filterOutComments as v, ordinal as w, assertTypes as x, isNegativeNaN as y, unique as z };
File diff suppressed because it is too large. Load diff
+37
View File
@@ -0,0 +1,37 @@
import { p as plugins } from './index.M2dsQ_UQ.js';
const serialize = (val, config, indentation, depth, refs, printer) => {
// Serialize a non-default name, even if config.printFunctionName is false.
const name = val.getMockName();
const nameString = name === "vi.fn()" ? "" : ` ${name}`;
let callsString = "";
if (val.mock.calls.length !== 0) {
const indentationNext = indentation + config.indent;
callsString = ` {${config.spacingOuter}${indentationNext}"calls": ${printer(val.mock.calls, config, indentationNext, depth, refs)}${config.min ? ", " : ","}${config.spacingOuter}${indentationNext}"results": ${printer(val.mock.results, config, indentationNext, depth, refs)}${config.min ? "" : ","}${config.spacingOuter}${indentation}}`;
}
return `[MockFunction${nameString}]${callsString}`;
};
const test = (val) => val && !!val._isMockFunction;
const plugin = {
serialize,
test
};
const { DOMCollection, DOMElement, Immutable, ReactElement, ReactTestComponent, AsymmetricMatcher } = plugins;
let PLUGINS = [
ReactTestComponent,
ReactElement,
DOMElement,
DOMCollection,
Immutable,
AsymmetricMatcher,
plugin
];
function addSerializer(plugin) {
PLUGINS = [plugin].concat(PLUGINS);
}
function getSerializers() {
return PLUGINS;
}
export { addSerializer as a, getSerializers as g };
+48
View File
@@ -0,0 +1,48 @@
import fs__default from 'node:fs';
import { b as basename, j as join, d as dirname } from './pathe.M-eThtNZ.Clq7AXhv.js';
const packageScopeTypeCache = /* @__PURE__ */ new Map();
// mirrors LOOKUP_PACKAGE_SCOPE from the ESM resolution algorithm:
// the lookup stops at the first package.json and never crosses
// the "node_modules" boundary, so typeless dependencies don't
// inherit the `type` field of the user's project
function lookupPackageScopeType(directory) {
const visited = [];
let result = "none";
let current = directory;
while (current) {
const cached = packageScopeTypeCache.get(current);
if (cached) {
result = cached;
break;
}
if (basename(current) === "node_modules") break;
visited.push(current);
const packageJsonPath = join(current, "package.json");
if (tryStatSync(packageJsonPath)?.isFile()) {
try {
const packageJson = JSON.parse(stripBomTag(fs__default.readFileSync(packageJsonPath, "utf8")));
if (packageJson.type === "module") result = "esm";
else if (packageJson.type === "commonjs") result = "cjs";
} catch {}
break;
}
const parent = dirname(current);
if (parent === current) break;
current = parent;
}
visited.forEach((dir) => packageScopeTypeCache.set(dir, result));
return result;
}
function stripBomTag(content) {
if (content.charCodeAt(0) === 65279) return content.slice(1);
return content;
}
function tryStatSync(file) {
try {
// The "throwIfNoEntry" is a performance optimization for cases where the file does not exist
return fs__default.statSync(file, { throwIfNoEntry: false });
} catch {}
}
export { lookupPackageScopeType as l };
+91
View File
@@ -0,0 +1,91 @@
import { EvaluatedModules } from 'vite/module-runner';
import { d as dirname, r as resolve } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { a as getSafeTimers } from './source-map.Dx3YPjIe.js';
import { c as createBirpc } from './index.DmDMHCg8.js';
import { g as getWorkerState } from './utils.DYj33du9.js';
// TODO: this is not needed in Vite 7.2+
class VitestEvaluatedModules extends EvaluatedModules {
getModuleSourceMapById(id) {
const map = super.getModuleSourceMapById(id);
if (map != null && !("_patched" in map)) {
map._patched = true;
const dir = dirname(map.url);
map.resolvedSources = (map.map.sources || []).map((s) => resolve(dir, s || ""));
}
return map;
}
}
const { get } = Reflect;
function withSafeTimers(fn) {
const { setTimeout, clearTimeout, nextTick, setImmediate, clearImmediate } = getSafeTimers();
const currentSetTimeout = globalThis.setTimeout;
const currentClearTimeout = globalThis.clearTimeout;
const currentSetImmediate = globalThis.setImmediate;
const currentClearImmediate = globalThis.clearImmediate;
const currentNextTick = globalThis.process?.nextTick;
try {
globalThis.setTimeout = setTimeout;
globalThis.clearTimeout = clearTimeout;
if (setImmediate) globalThis.setImmediate = setImmediate;
if (clearImmediate) globalThis.clearImmediate = clearImmediate;
if (globalThis.process && nextTick) globalThis.process.nextTick = nextTick;
return fn();
} finally {
globalThis.setTimeout = currentSetTimeout;
globalThis.clearTimeout = currentClearTimeout;
globalThis.setImmediate = currentSetImmediate;
globalThis.clearImmediate = currentClearImmediate;
if (globalThis.process && nextTick) nextTick(() => {
globalThis.process.nextTick = currentNextTick;
});
}
}
const promises = /* @__PURE__ */ new Set();
async function rpcDone() {
if (!promises.size) return;
const awaitable = Array.from(promises);
return Promise.all(awaitable);
}
const onCancelCallbacks = [];
function onCancel(callback) {
onCancelCallbacks.push(callback);
return () => {
const index = onCancelCallbacks.indexOf(callback);
if (index !== -1) onCancelCallbacks.splice(index, 1);
};
}
function createRuntimeRpc(options) {
return createSafeRpc(createBirpc({ async onCancel(reason) {
await Promise.all(onCancelCallbacks.map((fn) => fn(reason)));
} }, {
eventNames: ["onCancel"],
timeout: -1,
...options
}));
}
function createSafeRpc(rpc) {
return new Proxy(rpc, { get(target, p, handler) {
// keep $rejectPendingCalls as sync function
if (p === "$rejectPendingCalls") return rpc.$rejectPendingCalls;
const sendCall = get(target, p, handler);
const safeSendCall = (...args) => withSafeTimers(async () => {
const result = sendCall(...args);
promises.add(result);
try {
return await result;
} finally {
promises.delete(result);
}
});
safeSendCall.asEvent = sendCall.asEvent;
return safeSendCall;
} });
}
function rpc() {
const { rpc } = getWorkerState();
return rpc;
}
export { VitestEvaluatedModules as V, rpcDone as a, createRuntimeRpc as c, onCancel as o, rpc as r };
+74
View File
@@ -0,0 +1,74 @@
import { bt as FetchCachedFileSystemResult, bu as ResolveFunctionResult, U as UserConsoleLog, bl as AsyncLeak, a as File, a2 as AfterSuiteRunMeta, b0 as TestBenchmark, h as TestArtifact, j as TaskResultPack, k as TaskEventPack, Y as CancelReason, aM as SnapshotResult, a5 as BaselineData } from './config.d.CU_b-wJj.js';
import { FetchFunctionOptions, FetchResult } from 'vite/module-runner';
interface OTELCarrier {
traceparent?: string;
tracestate?: string;
}
interface TracesOptions {
enabled: boolean;
watchMode?: boolean;
sdkPath?: string;
tracerName?: string;
}
declare class Traces {
#private;
constructor(options: TracesOptions);
isEnabled(): boolean;
}
declare class GetterTracker {
static EXPORTS_MAX_INVOCATIONS: number;
private invocations;
private excessiveInvocations;
createTracker(moduleId: string, defineExport: (name: string, getter: () => unknown) => void): (name: string, getter: () => unknown) => void;
resetInvocations(): void;
getExcessiveInvocations(): GetterTrackerExport[];
}
interface GetterTrackerExport {
moduleId: string;
exportName: string;
}
interface RuntimeRPC {
fetch: (id: string, importer: string | undefined, environment: string, options?: FetchFunctionOptions, otelCarrier?: OTELCarrier) => Promise<FetchResult | FetchCachedFileSystemResult>;
resolve: (id: string, importer: string | undefined, environment: string) => Promise<ResolveFunctionResult | null>;
/**
* Returns the modules of the given test files' import graphs that the server
* has already processed, so a fresh worker can load them from disk without
* paying a `fetch` round-trip per module.
*/
fetchWarmModules: (environment: string, files: string[]) => Promise<Record<string, FetchResult | FetchCachedFileSystemResult>>;
/**
* Transforms the import graphs of the given test files ahead of the
* worker's own fetches. Fired by vm pool workers before their environment
* setup, so the server transforms modules while the worker is busy
* importing its environment package.
*/
prewarmModuleGraph: (environment: string, files: string[]) => Promise<void>;
transform: (id: string) => Promise<{
code?: string;
}>;
onUserConsoleLog: (log: UserConsoleLog) => void;
onUnhandledError: (err: unknown, type: string) => void;
onAsyncLeaks: (leak: AsyncLeak[]) => void;
onQueued: (file: File) => void;
onCollected: (files: File[]) => Promise<void>;
onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void;
onTestBenchmark: (testId: string, bench: TestBenchmark) => void;
onTaskArtifactRecord: <Artifact extends TestArtifact>(testId: string, artifact: Artifact) => Promise<Artifact>;
onTaskUpdate: (pack: TaskResultPack[], events: TaskEventPack[]) => Promise<void>;
onCancel: (reason: CancelReason) => void;
getCountOfFailedTests: () => number;
snapshotSaved: (snapshot: SnapshotResult) => void;
resolveSnapshotPath: (testPath: string) => string;
readBenchmarkResult: (relativePath: string) => Promise<BaselineData | null>;
writeBenchmarkResult: (relativePath: string, data: BaselineData) => Promise<void>;
ensureModuleGraphEntry: (id: string, importer: string) => void;
}
interface RunnerRPC {
onCancel: (reason: CancelReason) => void;
}
export { GetterTracker as G, Traces as T };
export type { OTELCarrier as O, RunnerRPC as R, RuntimeRPC as a };
File diff suppressed because it is too large. Load diff
+36
View File
@@ -0,0 +1,36 @@
import { s as setSafeTimers } from './source-map.Dx3YPjIe.js';
import { a as addSerializer } from './plugins.Cigb0uSy.js';
let globalSetup = false;
async function setupCommonEnv(config) {
setupDefines(config);
if (globalSetup) return;
globalSetup = true;
setSafeTimers();
if (config.globals) (await import('./globals.DQ4rmEUL.js')).registerApiGlobally();
}
function setupDefines(config) {
for (const key in config.defines) globalThis[key] = config.defines[key];
}
function setupEnv(env, metaEnv) {
for (const key in env) metaEnv[key] = env[key];
}
async function loadDiffConfig(config, moduleRunner) {
if (typeof config.diff === "object") return config.diff;
if (typeof config.diff !== "string") return;
const diffModule = await moduleRunner.import(config.diff);
if (diffModule && typeof diffModule.default === "object" && diffModule.default != null) return diffModule.default;
else throw new Error(`invalid diff config file ${config.diff}. Must have a default export with config object`);
}
async function loadSnapshotSerializers(config, moduleRunner) {
const files = config.snapshotSerializers;
(await Promise.all(files.map(async (file) => {
const mo = await moduleRunner.import(file);
if (!mo || typeof mo.default !== "object" || mo.default === null) throw new Error(`invalid snapshot serializer file ${file}. Must export a default object`);
const config = mo.default;
if (typeof config.test !== "function" || typeof config.serialize !== "function" && typeof config.print !== "function") throw new TypeError(`invalid snapshot serializer in ${file}. Must have a 'test' method along with either a 'serialize' or 'print' method.`);
return config;
}))).forEach((serializer) => addSerializer(serializer));
}
export { loadSnapshotSerializers as a, setupEnv as b, loadDiffConfig as l, setupCommonEnv as s };
File diff suppressed because it is too large. Load diff
+88
View File
@@ -0,0 +1,88 @@
import { T as TestError, P as ParsedStack } from './config.d.CU_b-wJj.js';
interface RuntimeCoverageModuleLoader {
import: (id: string) => Promise<{
default: RuntimeCoverageProviderModule;
}>;
isBrowser?: boolean;
moduleExecutionInfo?: Map<string, {
startOffset: number;
}>;
}
interface RuntimeCoverageProviderModule {
/**
* Factory for creating a new coverage provider
*/
getProvider: () => any;
/**
* Executed before tests are run in the worker thread.
*/
startCoverage?: (runtimeOptions: {
isolate: boolean;
/** @internal */
autoAttachSubprocess: boolean;
/** @internal */
reportsDirectory: string;
}) => unknown | Promise<unknown>;
/**
* Executed on after each run in the worker thread. Possible to return a payload passed to the provider
*/
takeCoverage?: (runtimeOptions?: {
moduleExecutionInfo?: Map<string, {
startOffset: number;
}>;
coverageFilesDirectory: string;
}) => unknown | Promise<unknown>;
/**
* Executed after all tests have been run in the worker thread.
*/
stopCoverage?: (runtimeOptions: {
isolate: boolean;
}) => unknown | Promise<unknown>;
}
type OriginalMapping = {
source: string | null;
line: number;
column: number;
name: string | null;
};
interface StackTraceParserOptions {
ignoreStackEntries?: (RegExp | string)[];
getSourceMap?: (file: string) => unknown;
getUrlId?: (id: string) => string;
frameFilter?: (error: TestError, frame: ParsedStack) => boolean | void;
}
interface SourceMapLike {
version: number;
mappings?: string;
names?: string[];
sources?: string[];
sourcesContent?: string[];
sourceRoot?: string;
}
interface Needle {
line: number;
column: number;
}
declare class DecodedMap {
map: SourceMapLike;
_encoded: string;
_decoded: undefined | number[][][];
_decodedMemo: Stats;
url: string;
version: number;
names: string[];
resolvedSources: string[];
constructor(map: SourceMapLike, from: string);
}
interface Stats {
lastKey: number;
lastNeedle: number;
lastIndex: number;
}
declare function getOriginalPosition(map: DecodedMap, needle: Needle): OriginalMapping | null;
export { DecodedMap as D, getOriginalPosition as g };
export type { RuntimeCoverageModuleLoader as R, StackTraceParserOptions as S, RuntimeCoverageProviderModule as a };
+477
View File
@@ -0,0 +1,477 @@
function isMockFunction(fn) {
return typeof fn === "function" && "_isMockFunction" in fn && fn._isMockFunction === true;
}
const MOCK_RESTORE = /* @__PURE__ */ new Set();
// Jest keeps the state in a separate WeakMap which is good for memory,
// but it makes the state slower to access and return different values
// if you stored it before calling `mockClear` where it will be recreated
const DIRTY_MOCK_STATES = /* @__PURE__ */ new Set();
const REGISTERED_MOCKS = /* @__PURE__ */ new Set();
const MOCK_FINALIZER = new FinalizationRegistry((ref) => {
REGISTERED_MOCKS.delete(ref);
});
const MOCK_CONFIGS = /* @__PURE__ */ new WeakMap();
const MOCKS_BY_STATE = /* @__PURE__ */ new WeakMap();
function createMockInstance(options = {}) {
const { originalImplementation, restore, mockImplementation, resetToMockImplementation, resetToMockName } = options;
if (restore) MOCK_RESTORE.add(restore);
const config = getDefaultConfig(originalImplementation);
const state = getDefaultState();
const mock = createMock({
config,
state,
...options
});
const mockLength = (mockImplementation || originalImplementation)?.length ?? 0;
Object.defineProperty(mock, "length", {
writable: true,
enumerable: false,
value: mockLength,
configurable: true
});
// inherit the default name so it appears in snapshots and logs
// this is used by `vi.spyOn()` for better debugging.
// when `vi.fn()` is called, we just use the default string
if (resetToMockName) config.mockName = mock.name || "vi.fn()";
MOCK_CONFIGS.set(mock, config);
MOCKS_BY_STATE.set(state, mock);
const ref = new WeakRef(mock);
REGISTERED_MOCKS.add(ref);
MOCK_FINALIZER.register(mock, ref);
mock._isMockFunction = true;
mock.getMockImplementation = () => {
// Jest only returns `config.mockImplementation` here,
// but we think it makes sense to return what the next function will be called
return config.onceMockImplementations[0] || config.mockImplementation;
};
// keep the prototype chain in sync with the implementation the next
// construction will use, so it is correct before any `new` call.
// automocked classes are skipped: their methods are pre-mocked
// on `mock.prototype`
const updateMockPrototype = () => {
if (!options.prototypeMembers?.length) reparentMockPrototype(mock, config.onceMockImplementations[0] || config.mockImplementation || originalImplementation);
};
Object.defineProperty(mock, "mock", {
configurable: false,
enumerable: true,
writable: false,
value: state
});
mock.mockImplementation = function mockImplementation(implementation) {
config.mockImplementation = implementation;
updateMockPrototype();
return mock;
};
mock.mockImplementationOnce = function mockImplementationOnce(implementation) {
config.onceMockImplementations.push(implementation);
updateMockPrototype();
return mock;
};
mock.withImplementation = function withImplementation(implementation, callback) {
const previousImplementation = config.mockImplementation;
const previousOnceImplementations = config.onceMockImplementations;
const reset = () => {
config.mockImplementation = previousImplementation;
config.onceMockImplementations = previousOnceImplementations;
updateMockPrototype();
};
config.mockImplementation = implementation;
config.onceMockImplementations = [];
updateMockPrototype();
const returnValue = callback();
if (typeof returnValue === "object" && typeof returnValue?.then === "function") return returnValue.then(() => {
reset();
return mock;
});
else reset();
return mock;
};
mock.mockReturnThis = function mockReturnThis() {
return mock.mockImplementation(function() {
return this;
});
};
mock.mockReturnValue = function mockReturnValue(value) {
return mock.mockImplementation(function() {
if (new.target) throwConstructorError("mockReturnValue");
return value;
});
};
mock.mockReturnValueOnce = function mockReturnValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) throwConstructorError("mockReturnValueOnce");
return value;
});
};
mock.mockThrow = function mockThrow(value) {
// eslint-disable-next-line prefer-arrow-callback
return mock.mockImplementation(function() {
throw value;
});
};
mock.mockThrowOnce = function mockThrowOnce(value) {
// eslint-disable-next-line prefer-arrow-callback
return mock.mockImplementationOnce(function() {
throw value;
});
};
mock.mockResolvedValue = function mockResolvedValue(value) {
return mock.mockImplementation(function() {
if (new.target) throwConstructorError("mockResolvedValue");
return Promise.resolve(value);
});
};
mock.mockResolvedValueOnce = function mockResolvedValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) throwConstructorError("mockResolvedValueOnce");
return Promise.resolve(value);
});
};
mock.mockRejectedValue = function mockRejectedValue(value) {
return mock.mockImplementation(function() {
if (new.target) throwConstructorError("mockRejectedValue");
return Promise.reject(value);
});
};
mock.mockRejectedValueOnce = function mockRejectedValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) throwConstructorError("mockRejectedValueOnce");
return Promise.reject(value);
});
};
mock.mockClear = function mockClear() {
state.calls = [];
state.contexts = [];
state.instances = [];
state.invocationCallOrder = [];
state.results = [];
state.settledResults = [];
DIRTY_MOCK_STATES.delete(mock);
return mock;
};
mock.mockReset = function mockReset() {
mock.mockClear();
config.mockImplementation = resetToMockImplementation ? mockImplementation : void 0;
config.mockName = resetToMockName ? mock.name || "vi.fn()" : "vi.fn()";
config.onceMockImplementations = [];
updateMockPrototype();
return mock;
};
mock.mockRestore = function mockRestore() {
mock.mockReset();
return restore?.();
};
mock.mockName = function mockName(name) {
if (typeof name === "string") config.mockName = name;
return mock;
};
mock.getMockName = function getMockName() {
return config.mockName || "vi.fn()";
};
if (Symbol.dispose) mock[Symbol.dispose] = () => mock.mockRestore();
if (mockImplementation) mock.mockImplementation(mockImplementation);
else
// vi.spyOn() has no mock implementation, chain the original one
updateMockPrototype();
return mock;
}
function fn(originalImplementation) {
// if the function is already a mock, just return the same function,
// similarly to how vi.spyOn() works
if (originalImplementation != null && isMockFunction(originalImplementation)) return originalImplementation;
return createMockInstance({
// we pass this down so getMockImplementation() always returns the value
mockImplementation: originalImplementation,
// special case so that .mockReset() resets the value to
// the the originalImplementation instead of () => undefined
resetToMockImplementation: true
});
}
function spyOn(object, key, accessor) {
assert(object != null, "The vi.spyOn() function could not find an object to spy upon. The first argument must be defined.");
assert(typeof object === "object" || typeof object === "function", "Vitest cannot spy on a primitive value.");
const [originalDescriptorObject, originalDescriptor] = getDescriptor(object, key) || [];
assert(originalDescriptor || key in object, `The property "${String(key)}" is not defined on the ${typeof object}.`);
let accessType = accessor || "value";
let ssr = false;
// vite ssr support - actual function is stored inside a getter
if (accessType === "value" && originalDescriptor && originalDescriptor.value == null && originalDescriptor.get) {
accessType = "get";
ssr = true;
}
let original;
if (originalDescriptor) {
original = originalDescriptor[accessType];
// weird Proxy edge case where descriptor's value is undefined,
// but there's still a value on the object when called
// https://github.com/vitest-dev/vitest/issues/9439
if (original == null && accessType === "value") original = object[key];
} else if (accessType !== "value") original = () => object[key];
else original = object[key];
const originalImplementation = ssr && original ? original() : original;
const originalType = typeof originalImplementation;
assert(
// allow only functions
originalType === "function" || accessType !== "value" && original == null,
`vi.spyOn() can only spy on a function. Received ${originalType}.`
);
if (isMockFunction(originalImplementation)) return originalImplementation;
const reassign = (cb) => {
const { value, ...desc } = originalDescriptor || {
configurable: true,
writable: true
};
if (accessType !== "value") delete desc.writable;
desc[accessType] = cb;
Object.defineProperty(object, key, desc);
};
const restore = () => {
// if method is defined on the prototype, we can just remove it from
// the current object instead of redefining a copy of it
if (originalDescriptorObject !== object) Reflect.deleteProperty(object, key);
else if (originalDescriptor && !original) Object.defineProperty(object, key, originalDescriptor);
else reassign(original);
};
const mock = createMockInstance({
restore,
originalImplementation,
resetToMockName: true
});
try {
reassign(ssr ? () => mock : mock);
} catch (error) {
if (error instanceof TypeError && Symbol.toStringTag && object[Symbol.toStringTag] === "Module" && (error.message.includes("Cannot redefine property") || error.message.includes("Cannot replace module namespace") || error.message.includes("can't redefine non-configurable property"))) throw new TypeError(`Cannot spy on export "${String(key)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/mocking/modules#mocking-a-module`, { cause: error });
throw error;
}
return mock;
}
function getDescriptor(obj, method) {
const objDescriptor = Object.getOwnPropertyDescriptor(obj, method);
if (objDescriptor) return [obj, objDescriptor];
let currentProto = Object.getPrototypeOf(obj);
while (currentProto !== null) {
const descriptor = Object.getOwnPropertyDescriptor(currentProto, method);
if (descriptor) return [currentProto, descriptor];
currentProto = Object.getPrototypeOf(currentProto);
}
}
function assert(condition, message) {
if (!condition) throw new Error(message);
}
let invocationCallCounter = 1;
function createMock({ state, config, name: mockName, prototypeState, prototypeConfig, keepMembersImplementation, mockImplementation, prototypeMembers = [] }) {
const original = config.mockOriginal;
const pseudoOriginal = mockImplementation;
const name = mockName || original?.name || "Mock";
const noopImplementation = function() {};
const prototypeMock = prototypeState && MOCKS_BY_STATE.get(prototypeState);
const namedObject = {
// to keep the name of the function intact
[name]: (function(...args) {
DIRTY_MOCK_STATES.add(namedObject[name]);
if (prototypeMock) DIRTY_MOCK_STATES.add(prototypeMock);
registerCalls(args, state, prototypeState);
registerInvocationOrder(invocationCallCounter++, state, prototypeState);
const result = {
type: "incomplete",
value: void 0
};
const settledResult = {
type: "incomplete",
value: void 0
};
registerResult(result, state, prototypeState);
registerSettledResult(settledResult, state, prototypeState);
const context = new.target ? void 0 : this;
const [instanceIndex, instancePrototypeIndex] = registerInstance(context, state, prototypeState);
const [contextIndex, contextPrototypeIndex] = registerContext(context, state, prototypeState);
const implementation = config.onceMockImplementations.shift() || config.mockImplementation || prototypeConfig?.onceMockImplementations.shift() || prototypeConfig?.mockImplementation || original || noopImplementation;
let returnValue;
let thrownValue;
let didThrow = false;
try {
if (new.target) {
// the prototype chain is already prepared when the implementation
// is registered, but a consumed `mockImplementationOnce` can change
// which implementation this construction uses
if (prototypeMembers.length === 0)
// eslint-disable-next-line ts/no-use-before-define
reparentMockPrototype(mock, implementation === noopImplementation ? void 0 : implementation);
returnValue = Reflect.construct(implementation, args, new.target);
// jest calls this before the implementation, but we have to resolve this _after_
// because we cannot do it before the `Reflect.construct` called the custom implementation.
// fortunately, the constructor is always an empty function because `prototypeMethods`
// are only used by the automocker, so this doesn't matter
for (const prop of prototypeMembers) {
const prototypeMock = returnValue[prop];
// the method was overridden because of inheritance, ignore it
// eslint-disable-next-line ts/no-use-before-define
if (prototypeMock !== mock.prototype[prop]) continue;
const isMock = isMockFunction(prototypeMock);
const prototypeState = isMock ? prototypeMock.mock : void 0;
const prototypeConfig = isMock ? MOCK_CONFIGS.get(prototypeMock) : void 0;
returnValue[prop] = createMockInstance({
originalImplementation: keepMembersImplementation ? prototypeConfig?.mockOriginal : void 0,
prototypeState,
prototypeConfig,
keepMembersImplementation
});
}
} else returnValue = implementation.apply(this, args);
} catch (error) {
thrownValue = error;
didThrow = true;
if (error instanceof TypeError && error.message.includes("is not a constructor")) console.warn(`[vitest] The ${namedObject[name].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);
throw error;
} finally {
if (didThrow) {
result.type = "throw";
result.value = thrownValue;
settledResult.type = "rejected";
settledResult.value = thrownValue;
} else {
result.type = "return";
result.value = returnValue;
if (new.target) {
state.contexts[contextIndex - 1] = returnValue;
state.instances[instanceIndex - 1] = returnValue;
if (contextPrototypeIndex != null && prototypeState) prototypeState.contexts[contextPrototypeIndex - 1] = returnValue;
if (instancePrototypeIndex != null && prototypeState) prototypeState.instances[instancePrototypeIndex - 1] = returnValue;
}
if (returnValue instanceof Promise) returnValue.then((settledValue) => {
settledResult.type = "fulfilled";
settledResult.value = settledValue;
}, (rejectedValue) => {
settledResult.type = "rejected";
settledResult.value = rejectedValue;
});
else {
settledResult.type = "fulfilled";
settledResult.value = returnValue;
}
}
}
return returnValue;
}) };
const mock = namedObject[name];
const copyPropertiesFrom = original || pseudoOriginal;
if (copyPropertiesFrom) copyOriginalStaticProperties(mock, copyPropertiesFrom);
return mock;
}
// puts the implementation's prototype behind `mock.prototype` so instances
// see prototype methods both during and after construction, while properties
// assigned on `mock.prototype` still shadow them
function reparentMockPrototype(mock, implementation) {
const mockPrototype = mock.prototype;
if (mockPrototype == null) return;
// an implementation without a usable prototype (reset mock, arrow or bound
// function) reverts the chain to `Object.prototype`, the parent every mock
// is created with
const parent = implementation?.prototype ?? Object.prototype;
if (Object.getPrototypeOf(mockPrototype) !== parent) Object.setPrototypeOf(mockPrototype, parent);
}
function registerCalls(args, state, prototypeState) {
state.calls.push(args);
prototypeState?.calls.push(args);
}
function registerInvocationOrder(order, state, prototypeState) {
state.invocationCallOrder.push(order);
prototypeState?.invocationCallOrder.push(order);
}
function registerResult(result, state, prototypeState) {
state.results.push(result);
prototypeState?.results.push(result);
}
function registerSettledResult(result, state, prototypeState) {
state.settledResults.push(result);
prototypeState?.settledResults.push(result);
}
function registerInstance(instance, state, prototypeState) {
return [state.instances.push(instance), prototypeState?.instances.push(instance)];
}
function registerContext(context, state, prototypeState) {
return [state.contexts.push(context), prototypeState?.contexts.push(context)];
}
function copyOriginalStaticProperties(mock, original) {
const { properties, descriptors } = getAllProperties(original);
for (const key of properties) {
const descriptor = descriptors[key];
if (getDescriptor(mock, key)) continue;
Object.defineProperty(mock, key, descriptor);
}
}
const ignoreProperties = /* @__PURE__ */ new Set([
"length",
"name",
"prototype",
Symbol.for("nodejs.util.promisify.custom")
]);
function getAllProperties(original) {
const properties = /* @__PURE__ */ new Set();
const descriptors = {};
while (original && original !== Object.prototype && original !== Function.prototype) {
const ownProperties = [...Object.getOwnPropertyNames(original), ...Object.getOwnPropertySymbols(original)];
for (const prop of ownProperties) {
if (descriptors[prop] || ignoreProperties.has(prop)) continue;
properties.add(prop);
descriptors[prop] = Object.getOwnPropertyDescriptor(original, prop);
}
original = Object.getPrototypeOf(original);
}
return {
properties,
descriptors
};
}
function getDefaultConfig(original) {
return {
mockImplementation: void 0,
mockOriginal: original,
mockName: "vi.fn()",
onceMockImplementations: []
};
}
function getDefaultState() {
const state = {
calls: [],
contexts: [],
instances: [],
invocationCallOrder: [],
settledResults: [],
results: [],
get lastCall() {
return state.calls.at(-1);
}
};
return state;
}
function restoreAllMocks() {
for (const restore of MOCK_RESTORE) restore();
MOCK_RESTORE.clear();
}
function clearAllMocks() {
for (const mock of DIRTY_MOCK_STATES) mock.mockClear();
}
function resetAllMocks() {
for (const ref of REGISTERED_MOCKS) {
const mock = ref.deref();
if (mock) mock.mockReset();
else REGISTERED_MOCKS.delete(ref);
}
}
function throwConstructorError(shorthand) {
throw new TypeError(`Cannot use \`${shorthand}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`);
}
var spyModule = /*#__PURE__*/Object.freeze({
__proto__: null,
clearAllMocks: clearAllMocks,
createMockInstance: createMockInstance,
fn: fn,
isMockFunction: isMockFunction,
resetAllMocks: resetAllMocks,
restoreAllMocks: restoreAllMocks,
spyOn: spyOn
});
export { spyOn as a, resetAllMocks as b, clearAllMocks as c, createMockInstance as d, fn as f, isMockFunction as i, restoreAllMocks as r, spyModule as s };
+143
View File
@@ -0,0 +1,143 @@
import { bn as SnapshotState, m as Test, bo as PromisifyAssertion, bp as Tester, bq as Plugin, ad as BenchResult, a as File } from './config.d.CU_b-wJj.js';
interface SnapshotMatcher<
R extends void | Promise<void>,
T = unknown
> {
<U extends { [P in keyof T]: any }>(snapshot: Partial<U>, hint?: string): R;
(hint?: string): R;
}
interface InlineSnapshotMatcher<
R extends void | Promise<void>,
T = unknown
> {
<U extends { [P in keyof T]: any }>(properties: Partial<U>, snapshot?: string, hint?: string): R;
(hint?: string): R;
}
declare module "vitest" {
interface MatcherState {
environment: string;
snapshotState: SnapshotState;
task?: Readonly<Test>;
}
interface ExpectPollOptions {
interval?: number;
timeout?: number;
message?: string;
}
interface ExpectStatic {
assert: Chai.AssertStatic;
unreachable: (message?: string) => never;
soft: <T>(actual: T, message?: string) => Assertion<void, T>;
poll: <T>(actual: (options: {
signal: AbortSignal;
}) => T, options?: ExpectPollOptions) => PromisifyAssertion<Awaited<T>>;
addEqualityTesters: (testers: Array<Tester>) => void;
assertions: (expected: number) => void;
hasAssertions: () => void;
addSnapshotSerializer: (plugin: Plugin) => void;
}
interface Assertion<
R,
T
> {
matchSnapshot: SnapshotMatcher<R, T>;
toMatchSnapshot: SnapshotMatcher<R, T>;
toMatchInlineSnapshot: InlineSnapshotMatcher<R, T>;
/**
* Checks that an error thrown by a function matches a previously recorded snapshot.
*
* @param hint - Optional custom error message.
*
* @example
* expect(functionWithError).toThrowErrorMatchingSnapshot();
*/
toThrowErrorMatchingSnapshot: (hint?: string) => R;
/**
* Checks that an error thrown by a function matches an inline snapshot within the test file.
* Useful for keeping snapshots close to the test code.
*
* @param snapshot - Optional inline snapshot string to match.
* @param hint - Optional custom error message.
*
* @example
* const throwError = () => { throw new Error('Error occurred') };
* expect(throwError).toThrowErrorMatchingInlineSnapshot(`"Error occurred"`);
*/
toThrowErrorMatchingInlineSnapshot: (snapshot?: string, hint?: string) => R;
/**
* Compares the received value to a snapshot saved in a specified file.
* Useful for cases where snapshot content is large or needs to be shared across tests.
*
* @param filepath - Path to the snapshot file.
* @param hint - Optional custom error message.
*
* @example
* await expect(largeData).toMatchFileSnapshot('path/to/snapshot.json');
*/
toMatchFileSnapshot: (filepath: string, hint?: string) => Promise<void>;
/**
* Asserts that a benchmark result is faster than another benchmark result.
* Compares mean latency — lower is faster.
*
* @example
* const result = await bench.compare(
* bench('lib1', () => { lib1() }),
* bench('lib2', () => { lib2() }),
* )
* expect(result.get('lib1')).toBeFasterThan(result.get('lib2'))
* expect(result.get('lib1')).toBeFasterThan(result.get('lib2'), { delta: 0.1 })
*/
toBeFasterThan: (expected: BenchResult, options?: {
delta?: number;
}) => R;
/**
* Asserts that a benchmark result is slower than another benchmark result.
* Compares mean latency — higher is slower.
*
* @example
* const result = await bench.compare(
* bench('lib1', () => { lib1() }),
* bench('lib2', () => { lib2() }),
* )
* expect(result.get('lib2')).toBeSlowerThan(result.get('lib1'))
* expect(result.get('lib2')).toBeSlowerThan(result.get('lib1'), { delta: 0.2 })
*/
toBeSlowerThan: (expected: BenchResult, options?: {
delta?: number;
}) => R;
/**
* Ensures a `vi.when` chain has been exhausted.
*
* A chain is exhausted when at least one `calledWith` with an associated action (`then*`) has been registered
* and every registered behavior has been fully consumed. A chain with no registered
* behaviors, or with `calledWith` entries that have no associated `then*` actions, is never considered exhausted.
*
* @see {@link https://vitest.dev/api/expect#tohavebeenexhausted}
*
* @example
* const w = vi.when(spy).calledWith('hello').thenReturnOnce('HELLO')
*
* expect(w).not.toHaveBeenExhausted()
*
* expect(spy('hello')).toBe('HELLO')
*
* expect(w).toHaveBeenExhausted()
*/
toHaveBeenExhausted: () => R;
}
}
interface HashMeta {
typecheck?: boolean;
__vitest_label__?: string;
}
declare function createFileTask(filepath: string, root: string, projectName: string | undefined, pool?: string, viteEnvironment?: string, meta?: HashMeta): File;
/**
* Generate a unique ID for a file based on its path and project name
* @param file File relative to the root of the project to keep ID the same between different machines
* @param projectName The name of the test project
*/
declare function generateFileHash(file: string, projectName: string | undefined, meta?: HashMeta): string;
export { createFileTask as c, generateFileHash as g };
+95
View File
@@ -0,0 +1,95 @@
// src/index.ts
var b = {
reset: [0, 0],
bold: [1, 22, "\x1B[22m\x1B[1m"],
dim: [2, 22, "\x1B[22m\x1B[2m"],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29],
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
gray: [90, 39],
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49],
blackBright: [90, 39],
redBright: [91, 39],
greenBright: [92, 39],
yellowBright: [93, 39],
blueBright: [94, 39],
magentaBright: [95, 39],
cyanBright: [96, 39],
whiteBright: [97, 39],
bgBlackBright: [100, 49],
bgRedBright: [101, 49],
bgGreenBright: [102, 49],
bgYellowBright: [103, 49],
bgBlueBright: [104, 49],
bgMagentaBright: [105, 49],
bgCyanBright: [106, 49],
bgWhiteBright: [107, 49]
};
function i(e) {
return String(e);
}
i.open = "";
i.close = "";
function p() {
let e = {
isColorSupported: false,
reset: i
};
for (let r in b)
e[r] = i;
return e;
}
function B() {
let e = typeof process != "undefined" ? process : void 0, r = (e == null ? void 0 : e.env) || {}, a = r.FORCE_TTY !== "false", l = (e == null ? void 0 : e.argv) || [];
return !("NO_COLOR" in r || l.includes("--no-color")) && ("FORCE_COLOR" in r || l.includes("--color") || (e == null ? void 0 : e.platform) === "win32" || a && r.TERM !== "dumb" || "CI" in r) || typeof window != "undefined" && !!window.chrome;
}
function C({ force: e } = {}) {
let r = e || B(), a = (t, o, u, n) => {
let g = "", s = 0;
do
g += t.substring(s, n) + u, s = n + o.length, n = t.indexOf(o, s);
while (~n);
return g + t.substring(s);
}, l = (t, o, u = t) => {
let n = (g) => {
let s = String(g), h = s.indexOf(o, t.length);
return ~h ? t + a(s, o, u, h) + o : t + s + o;
};
return n.open = t, n.close = o, n;
}, c = {
isColorSupported: r
}, f = (t) => `\x1B[${t}m`;
for (let t in b) {
let o = b[t];
c[t] = r ? l(
f(o[0]),
f(o[1]),
o[2]
) : i;
}
return c;
}
var d = C();
function m() {
Object.assign(d, p());
}
var y = d;
export { m, y };
+182
View File
@@ -0,0 +1,182 @@
import { stripVTControlCharacters } from 'node:util';
import { t as truncateString$1 } from './display.pkpxlVcY.js';
import { i as isAbsolute, a as relative, d as dirname, b as basename, s as slash } from './pathe.M-eThtNZ.Clq7AXhv.js';
import { y } from './tinyrainbow.Ht9iggcq.js';
const F_RIGHT = "→";
const F_DOWN = "↓";
const F_DOWN_RIGHT = "↳";
const F_POINTER = "❯";
const F_DOT = "·";
const F_CHECK = "✓";
const F_CROSS = "×";
const F_LONG_DASH = "⎯";
const F_TODO = "□";
const F_TREE_NODE_MIDDLE = "├──";
const F_TREE_NODE_END = "└──";
const pointer = y.yellow(F_POINTER);
const skipped = y.dim(y.gray(F_DOWN));
const todo = y.dim(y.gray(F_TODO));
const benchmarkPass = y.green(F_DOT);
const testPass = y.green(F_CHECK);
const taskFail = y.red(F_CROSS);
const suiteFail = y.red(F_POINTER);
const pending = y.gray("·");
const separator = y.dim(" > ");
const labelDefaultColors = [
y.bgYellow,
y.bgCyan,
y.bgGreen,
y.bgMagenta
];
function getCols(delta = 0) {
let length = process.stdout?.columns;
if (!length || Number.isNaN(length)) length = 30;
return Math.max(length + delta, 0);
}
function errorBanner(message) {
return divider(y.bold(y.bgRed(` ${message} `)), null, null, y.red);
}
function divider(text, left, right, color) {
const cols = getCols();
const c = color || ((text) => text);
if (text) {
const textLength = stripVTControlCharacters(text).length;
if (left == null && right != null) left = cols - textLength - right;
else {
left = left ?? Math.floor((cols - textLength) / 2);
right = cols - textLength - left;
}
left = Math.max(0, left);
right = Math.max(0, right);
return `${c(F_LONG_DASH.repeat(left))}${text}${c(F_LONG_DASH.repeat(right))}`;
}
return F_LONG_DASH.repeat(cols);
}
function formatTestPath(root, path) {
if (isAbsolute(path)) path = relative(root, path);
const dir = dirname(path);
const ext = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)?.[0] || "";
const base = basename(path, ext);
return slash(y.dim(`${dir}/`) + y.bold(base)) + y.dim(ext);
}
function renderSnapshotSummary(rootDir, snapshots) {
const summary = [];
if (snapshots.added) summary.push(y.bold(y.green(`${snapshots.added} written`)));
if (snapshots.unmatched) summary.push(y.bold(y.red(`${snapshots.unmatched} failed`)));
if (snapshots.updated) summary.push(y.bold(y.green(`${snapshots.updated} updated `)));
if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(y.bold(y.green(`${snapshots.filesRemoved} files removed `)));
else summary.push(y.bold(y.yellow(`${snapshots.filesRemoved} files obsolete `)));
if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
const [head, ...tail] = snapshots.filesRemovedList;
summary.push(`${y.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
tail.forEach((key) => {
summary.push(` ${y.gray(F_DOT)} ${formatTestPath(rootDir, key)}`);
});
}
if (snapshots.unchecked) {
if (snapshots.didUpdate) summary.push(y.bold(y.green(`${snapshots.unchecked} removed`)));
else summary.push(y.bold(y.yellow(`${snapshots.unchecked} obsolete`)));
snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
summary.push(`${y.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
uncheckedFile.keys.forEach((key) => summary.push(` ${y.gray(F_DOT)} ${key}`));
});
}
return summary;
}
function countTestErrors(tasks) {
return tasks.reduce((c, i) => c + (i.result?.errors?.length || 0), 0);
}
function getStateString(tasks, name = "tests", showTotal = true) {
if (tasks.length === 0) return y.dim(`no ${name}`);
const passed = tasks.reduce((acc, i) => {
// Exclude expected failures from passed count
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc;
return i.result?.state === "pass" ? acc + 1 : acc;
}, 0);
const failed = tasks.reduce((acc, i) => i.result?.state === "fail" ? acc + 1 : acc, 0);
const skipped = tasks.reduce((acc, i) => i.mode === "skip" ? acc + 1 : acc, 0);
const todo = tasks.reduce((acc, i) => i.mode === "todo" ? acc + 1 : acc, 0);
const expectedFail = tasks.reduce((acc, i) => {
// Count tests that are marked as .fails and passed (which means they failed as expected)
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc + 1;
return acc;
}, 0);
return [
failed ? y.bold(y.red(`${failed} failed`)) : null,
passed ? y.bold(y.green(`${passed} passed`)) : null,
expectedFail ? y.cyan(`${expectedFail} expected fail`) : null,
skipped ? y.yellow(`${skipped} skipped`) : null,
todo ? y.gray(`${todo} todo`) : null
].filter(Boolean).join(y.dim(" | ")) + (showTotal ? y.gray(` (${tasks.length})`) : "");
}
function getStateSymbol(task) {
if (task.mode === "todo") return todo;
if (task.mode === "skip") return skipped;
if (!task.result) return pending;
if (task.result.state === "run" || task.result.state === "queued") {
if (task.type === "suite") return pointer;
}
if (task.result.state === "pass") return task.meta?.benchmark ? benchmarkPass : testPass;
if (task.result.state === "fail") return task.type === "suite" ? suiteFail : taskFail;
return " ";
}
function formatTimeString(date) {
return date.toTimeString().split(" ")[0];
}
function formatTime(time) {
if (time > 1e3) return `${(time / 1e3).toFixed(2)}s`;
return `${Math.round(time)}ms`;
}
function formatProjectName(project, suffix = " ") {
if (!project?.name) return "";
if (!y.isColorSupported) return `|${project.name}|${suffix}`;
let background = project.color && y[`bg${capitalize(project.color)}`];
if (!background) {
const index = project.name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0);
background = labelDefaultColors[index % labelDefaultColors.length];
}
return y.black(background(` ${project.name} `)) + suffix;
}
function withLabel(color, label, message) {
const bgColor = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;
return `${y.bold(y.black(y[bgColor](` ${label} `)))} ${message ? y[color](message) : ""}`;
}
function padSummaryTitle(str) {
return y.dim(`${str.padStart(11)} `);
}
function truncateString(text, maxLength) {
return truncateString$1(stripVTControlCharacters(text), maxLength);
}
function capitalize(text) {
return `${text[0].toUpperCase()}${text.slice(1)}`;
}
/**
* Returns the singular or plural form of a word based on the count.
*/
function noun(count, singular, plural) {
if (count === 1) return singular;
return plural;
}
var utils = /*#__PURE__*/Object.freeze({
__proto__: null,
countTestErrors: countTestErrors,
divider: divider,
errorBanner: errorBanner,
formatProjectName: formatProjectName,
formatTime: formatTime,
formatTimeString: formatTimeString,
getStateString: getStateString,
getStateSymbol: getStateSymbol,
noun: noun,
padSummaryTitle: padSummaryTitle,
renderSnapshotSummary: renderSnapshotSummary,
separator: separator,
taskFail: taskFail,
truncateString: truncateString,
withLabel: withLabel
});
export { F_POINTER as F, formatTimeString as a, taskFail as b, F_CHECK as c, divider as d, errorBanner as e, formatProjectName as f, F_DOWN_RIGHT as g, getStateSymbol as h, getStateString as i, formatTime as j, countTestErrors as k, F_TREE_NODE_END as l, F_TREE_NODE_MIDDLE as m, noun as n, F_RIGHT as o, padSummaryTitle as p, renderSnapshotSummary as r, separator as s, truncateString as t, utils as u, withLabel as w };
+43
View File
@@ -0,0 +1,43 @@
const NAME_WORKER_STATE = "__vitest_worker__";
class EnvironmentTeardownError extends Error {
name = "EnvironmentTeardownError";
}
function getWorkerState() {
// @ts-expect-error untyped global
const workerState = globalThis[NAME_WORKER_STATE];
if (!workerState) throw new Error("Vitest failed to access its internal state.\n\nOne of the following is possible:\n- \"vitest\" is imported directly without running \"vitest\" command\n- \"vitest\" is imported inside \"globalSetup\" (to fix this, use \"setupFiles\" instead, because \"globalSetup\" runs in a different context)\n- \"vitest\" is imported inside Vite / Vitest config file\n- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues\n");
return workerState;
}
function getSafeWorkerState() {
// @ts-expect-error untyped global
return globalThis[NAME_WORKER_STATE];
}
function provideWorkerState(context, state) {
Object.defineProperty(context, NAME_WORKER_STATE, {
value: state,
configurable: true,
writable: true,
enumerable: false
});
return state;
}
function isChildProcess() {
return typeof process !== "undefined" && !!process.send;
}
function resetModules(modules, resetMocks = false) {
const skipPaths = [
/\/vitest\/dist\//,
/vitest-virtual-\w+\/dist/,
/@vitest\/dist/,
...!resetMocks ? [/^mock:/] : []
];
modules.idToModuleMap.forEach((node, path) => {
if (skipPaths.some((re) => re.test(path))) return;
node.promise = void 0;
node.exports = void 0;
node.evaluated = false;
node.importers.clear();
});
}
export { EnvironmentTeardownError as E, getSafeWorkerState as a, getWorkerState as g, isChildProcess as i, provideWorkerState as p, resetModules as r };
File diff suppressed because it is too large. Load diff
+265
View File
@@ -0,0 +1,265 @@
import { EvaluatedModules } from 'vite/module-runner';
import { F as FileSpecification, b as SerializedConfig, X as Task, Y as CancelReason } from './config.d.CU_b-wJj.js';
import { a as RuntimeRPC, R as RunnerRPC, G as GetterTracker } from './rpc.d.DA9Utv4e.js';
import { E as Environment } from './environment.d.C6xYahWA.js';
//#region src/messages.d.ts
declare const TYPE_REQUEST: "q";
interface RpcRequest {
/**
* Type
*/
t: typeof TYPE_REQUEST;
/**
* ID
*/
i?: string;
/**
* Method
*/
m: string;
/**
* Arguments
*/
a: any[];
/**
* Optional
*/
o?: boolean;
}
//#endregion
//#region src/utils.d.ts
type ArgumentsType<T> = T extends ((...args: infer A) => any) ? A : never;
type ReturnType<T> = T extends ((...args: any) => infer R) ? R : never;
type Thenable<T> = T | PromiseLike<T>;
//#endregion
//#region src/main.d.ts
type PromisifyFn<T> = ReturnType<T> extends Promise<any> ? T : (...args: ArgumentsType<T>) => Promise<Awaited<ReturnType<T>>>;
type BirpcResolver<This> = (this: This, name: string, resolved: (...args: unknown[]) => unknown) => Thenable<((...args: any[]) => any) | undefined>;
interface ChannelOptions {
/**
* Function to post raw message
*/
post: (data: any, ...extras: any[]) => Thenable<any>;
/**
* Listener to receive raw message
*/
on: (fn: (data: any, ...extras: any[]) => void) => Thenable<any>;
/**
* Clear the listener when `$close` is called
*/
off?: (fn: (data: any, ...extras: any[]) => void) => Thenable<any>;
/**
* Custom function to serialize data
*
* by default it passes the data as-is
*/
serialize?: (data: any) => any;
/**
* Custom function to deserialize data
*
* by default it passes the data as-is
*/
deserialize?: (data: any) => any;
/**
* Call the methods with the RPC context or the original functions object
*/
bind?: 'rpc' | 'functions';
/**
* Custom meta data to attached to the RPC instance's `$meta` property
*/
meta?: any;
}
interface EventOptions<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> {
/**
* Names of remote functions that do not need response.
*/
eventNames?: (keyof RemoteFunctions)[];
/**
* Maximum timeout for waiting for response, in milliseconds.
*
* @default 60_000
*/
timeout?: number;
/**
* Whether to proxy the remote functions.
*
* When `proxify` is false, calling the remote function
* with `rpc.$call('method', ...args)` instead of `rpc.method(...args)`
* explicitly is required.
*
* @default true
*/
proxify?: Proxify;
/**
* Custom resolver to resolve function to be called
*
* For advanced use cases only
*/
resolver?: BirpcResolver<BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>>;
/**
* Hook triggered before an event is sent to the remote
*
* @param req - Request parameters
* @param next - Function to continue the request
* @param resolve - Function to resolve the response directly
*/
onRequest?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, req: RpcRequest, next: (req?: RpcRequest) => Promise<any>, resolve: (res: any) => void) => void | Promise<void>;
/**
* Custom error handler for errors occurred in local functions being called
*
* @returns `true` to prevent the error from being thrown
*/
onFunctionError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, error: Error, functionName: string, args: any[]) => boolean | void;
/**
* Custom error handler for errors occurred during serialization or messsaging
*
* @returns `true` to prevent the error from being thrown
*/
onGeneralError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, error: Error, functionName?: string, args?: any[]) => boolean | void;
/**
* Custom error handler for timeouts
*
* @returns `true` to prevent the error from being thrown
*/
onTimeoutError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, functionName: string, args: any[]) => boolean | void;
}
type BirpcOptions<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> = EventOptions<RemoteFunctions, LocalFunctions, Proxify> & ChannelOptions;
type BirpcFn<T> = PromisifyFn<T> & {
/**
* Send event without asking for response
*/
asEvent: (...args: ArgumentsType<T>) => Promise<void>;
};
interface BirpcReturnBuiltin<RemoteFunctions, LocalFunctions = Record<string, unknown>> {
/**
* Raw functions object
*/
$functions: LocalFunctions;
/**
* Whether the RPC is closed
*/
readonly $closed: boolean;
/**
* Custom meta data attached to the RPC instance
*/
readonly $meta: any;
/**
* Close the RPC connection
*/
$close: (error?: Error) => void;
/**
* Reject pending calls
*/
$rejectPendingCalls: (handler?: PendingCallHandler) => Promise<void>[];
/**
* Call the remote function and wait for the result.
* An alternative to directly calling the function
*/
$call: <K extends keyof RemoteFunctions>(method: K, ...args: ArgumentsType<RemoteFunctions[K]>) => Promise<Awaited<ReturnType<RemoteFunctions[K]>>>;
/**
* Same as `$call`, but returns `undefined` if the function is not defined on the remote side.
*/
$callOptional: <K extends keyof RemoteFunctions>(method: K, ...args: ArgumentsType<RemoteFunctions[K]>) => Promise<Awaited<ReturnType<RemoteFunctions[K]> | undefined>>;
/**
* Send event without asking for response
*/
$callEvent: <K extends keyof RemoteFunctions>(method: K, ...args: ArgumentsType<RemoteFunctions[K]>) => Promise<void>;
/**
* Call the remote function with the raw options.
*/
$callRaw: (options: {
method: string;
args: unknown[];
event?: boolean;
optional?: boolean;
}) => Promise<Awaited<ReturnType<any>>[]>;
}
type ProxifiedRemoteFunctions<RemoteFunctions extends object = Record<string, unknown>> = { [K in keyof RemoteFunctions]: BirpcFn<RemoteFunctions[K]>; };
type BirpcReturn<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> = Proxify extends true ? ProxifiedRemoteFunctions<RemoteFunctions> & BirpcReturnBuiltin<RemoteFunctions, LocalFunctions> : BirpcReturnBuiltin<RemoteFunctions, LocalFunctions>;
type PendingCallHandler = (options: Pick<PromiseEntry, 'method' | 'reject'>) => void | Promise<void>;
interface PromiseEntry {
resolve: (arg: any) => void;
reject: (error: any) => void;
method: string;
timeoutId?: ReturnType<typeof setTimeout>;
}
declare const setTimeout: any;
type WorkerRPC = BirpcReturn<RuntimeRPC, RunnerRPC>;
interface ContextTestEnvironment {
name: string;
options: Record<string, any> | null;
}
interface WorkerTestEnvironment {
name: string;
options: Record<string, any> | null;
}
type TestExecutionMethod = "run" | "collect";
interface WorkerExecuteContext {
files: FileSpecification[];
providedContext: Record<string, any>;
invalidates?: string[];
environment: ContextTestEnvironment;
/** Exposed to test runner as `VITEST_WORKER_ID`. Value is unique per each isolated worker. */
workerId: number;
}
interface MetaEnv {
[key: string]: any;
BASE_URL: string;
MODE: string;
DEV: boolean;
PROD: boolean;
SSR: boolean;
}
interface ContextRPC {
pool: string;
config: SerializedConfig;
projectName: string;
environment: WorkerTestEnvironment;
rpc: WorkerRPC;
metaEnv: MetaEnv;
files: FileSpecification[];
providedContext: Record<string, any>;
invalidates?: string[];
/** Exposed to test runner as `VITEST_WORKER_ID`. Value is unique per each isolated worker. */
workerId: number;
concurrencyId: number;
}
interface WorkerSetupContext {
environment: WorkerTestEnvironment;
pool: string;
config: SerializedConfig;
projectName: string;
rpc: WorkerRPC;
metaEnv: MetaEnv;
}
interface WorkerGlobalState {
ctx: ContextRPC;
config: SerializedConfig;
rpc: WorkerRPC;
current?: Task;
filepath?: string;
metaEnv: MetaEnv;
metaDefines?: Record<string, any>;
environment: Environment;
evaluatedModules: EvaluatedModules;
resolvingModules: Set<string>;
moduleExecutionInfo: Map<string, any>;
getterTracker?: GetterTracker;
onCancel: (listener: (reason: CancelReason) => unknown) => () => void;
onCleanup: (listener: () => unknown) => void;
providedContext: Record<string, any>;
durations: {
environment: number;
prepare: number;
/**
* Wall time the worker spent blocked on module fetch requests to the
* server, measured as the union of in-flight intervals.
*/
fetch: number;
};
onFilterStackTrace?: (trace: string) => string;
}
export type { BirpcReturn as B, ContextRPC as C, TestExecutionMethod as T, WorkerGlobalState as W, ContextTestEnvironment as a, WorkerExecuteContext as b, WorkerTestEnvironment as c, WorkerSetupContext as d, BirpcOptions as e };
+10
View File
@@ -0,0 +1,10 @@
import { c as createCLI } from './chunks/cac.D805sv8h.js';
import './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import 'events';
import './chunks/tinyrainbow.Ht9iggcq.js';
import './chunks/env.DzFJjrmK.js';
import 'std-env';
import './chunks/constants.-juJ8b_4.js';
import 'node:assert';
createCLI().parse();
+106
View File
@@ -0,0 +1,106 @@
'use strict';
var os = require('node:os');
var stdEnv = require('std-env');
var vite = require('vite');
const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
(isNode || isDeno) && process.platform === "win32";
(isNode || isDeno) && process.stdout?.isTTY && !stdEnv.isCI;
// if changed, update also jsdocs and docs
const defaultBrowserPort = 63315;
const defaultInclude = ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
const defaultExclude = ["**/node_modules/**", "**/.git/**"];
// These are the generic defaults for coverage. Providers may also set some provider specific defaults.
const coverageConfigDefaults = {
provider: "v8",
enabled: false,
clean: true,
cleanOnRerun: true,
reportsDirectory: "./coverage",
exclude: [],
reportOnFailure: false,
reporter: [
"text",
"html",
"clover",
"json"
],
allowExternal: false,
excludeAfterRemap: false,
processingConcurrency: Math.min(20, os.availableParallelism?.() ?? os.cpus().length),
ignoreClassMethods: [],
skipFull: false,
watermarks: {
statements: [50, 80],
functions: [50, 80],
branches: [50, 80],
lines: [50, 80]
},
autoAttachSubprocess: false
};
const fakeTimersDefaults = {
loopLimit: 1e4,
shouldClearNativeTimers: true
};
const configDefaults = Object.freeze({
allowOnly: !stdEnv.isCI,
isolate: true,
watch: !stdEnv.isCI && process.stdin.isTTY && !stdEnv.isAgent,
globals: false,
injectCjsGlobals: true,
environment: "node",
clearMocks: true,
restoreMocks: false,
mockReset: false,
unstubGlobals: false,
unstubEnvs: false,
include: defaultInclude,
exclude: defaultExclude,
teardownTimeout: 1e4,
forceRerunTriggers: ["**/package.json", "**/{vitest,vite}.config.*"],
update: false,
reporters: [stdEnv.isAgent ? "minimal" : "default", ...process.env.GITHUB_ACTIONS === "true" ? ["github-actions"] : []],
silent: false,
hideSkippedTests: false,
api: false,
ui: false,
uiBase: "/__vitest__/",
open: !stdEnv.isCI,
css: { include: [] },
coverage: coverageConfigDefaults,
fakeTimers: fakeTimersDefaults,
maxConcurrency: 5,
dangerouslyIgnoreUnhandledErrors: false,
typecheck: {
checker: "tsc",
include: ["**/*.{test,spec}-d.?(c|m)[jt]s?(x)"],
exclude: defaultExclude
},
slowTestThreshold: 300,
taskTitleValueFormatTruncate: 40,
disableConsoleIntercept: false,
detectAsyncLeaks: false
});
function defineConfig(config) {
return config;
}
function defineProject(config) {
return config;
}
Object.defineProperty(exports, "mergeConfig", {
enumerable: true,
get: function () { return vite.mergeConfig; }
});
exports.configDefaults = configDefaults;
exports.coverageConfigDefaults = coverageConfigDefaults;
exports.defaultBrowserPort = defaultBrowserPort;
exports.defaultExclude = defaultExclude;
exports.defaultInclude = defaultInclude;
exports.defineConfig = defineConfig;
exports.defineProject = defineProject;
+108
View File
@@ -0,0 +1,108 @@
import { HookHandler, UserConfig, ConfigEnv } from 'vite';
export { ConfigEnv, Plugin, UserConfig as ViteUserConfig, mergeConfig } from 'vite';
import { F as FieldsWithDefaultValues, U as UserWorkspaceConfig, a as UserProjectConfigFn, b as UserProjectConfigExport } from './chunks/plugin.d.BbcoZhuj.js';
import { InlineConfig, ResolvedConfig, VitestPluginContext, CoverageOptions } from './node.js';
export { T as TestProjectConfiguration, c as TestProjectInlineConfiguration, d as TestUserConfig, W as WatcherTriggerPattern } from './chunks/plugin.d.BbcoZhuj.js';
import { C as Config } from './chunks/config.d.CU_b-wJj.js';
export { e as TestTagDefinition } from './chunks/config.d.CU_b-wJj.js';
import 'node:stream';
import './chunks/browser.d.g5Thl309.js';
import './chunks/rpc.d.DA9Utv4e.js';
import 'vite/module-runner';
import './chunks/worker.d.MLmnzOJE.js';
import './chunks/environment.d.C6xYahWA.js';
import 'chai';
import 'vitest/optional-types.js';
import '@vitest/mocker';
import './chunks/source-map.d.YqWNcp4e.js';
import 'vitest/browser';
import 'node:path';
import 'node:console';
import 'node:fs/promises';
import 'node:fs';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
type VitestInlineConfig = InlineConfig;
type VitestResolvedConfig = ResolvedConfig;
declare module "vite" {
interface UserConfig {
/**
* Options for Vitest
*/
test?: VitestInlineConfig;
}
interface ResolvedConfig {
/**
* Options for Vitest
*/
test: VitestResolvedConfig;
}
interface Plugin<A = any> {
configureVitest?: HookHandler<(context: VitestPluginContext) => void>;
}
}
declare const defaultBrowserPort = 63315;
declare const defaultInclude: string[];
declare const defaultExclude: string[];
declare const coverageConfigDefaults: Required<Pick<CoverageOptions, FieldsWithDefaultValues>>;
declare const configDefaults: Readonly<{
allowOnly: boolean;
isolate: boolean;
watch: boolean;
globals: boolean;
injectCjsGlobals: boolean;
environment: "node";
clearMocks: boolean;
restoreMocks: boolean;
mockReset: boolean;
unstubGlobals: boolean;
unstubEnvs: boolean;
include: string[];
exclude: string[];
teardownTimeout: number;
forceRerunTriggers: string[];
update: boolean;
reporters: string[];
silent: boolean;
hideSkippedTests: boolean;
api: boolean;
ui: boolean;
uiBase: string;
open: boolean;
css: {
include: never[];
};
coverage: CoverageOptions;
fakeTimers: Config;
maxConcurrency: number;
dangerouslyIgnoreUnhandledErrors: boolean;
typecheck: {
checker: "tsc";
include: string[];
exclude: string[];
};
slowTestThreshold: number;
taskTitleValueFormatTruncate: number;
disableConsoleIntercept: boolean;
detectAsyncLeaks: boolean;
}>;
type ViteUserConfigFnObject = (env: ConfigEnv) => UserConfig;
type ViteUserConfigFnPromise = (env: ConfigEnv) => Promise<UserConfig>;
type ViteUserConfigFn = (env: ConfigEnv) => UserConfig | Promise<UserConfig>;
type ViteUserConfigExport = UserConfig | Promise<UserConfig> | ViteUserConfigFnObject | ViteUserConfigFnPromise | ViteUserConfigFn;
declare function defineConfig(config: UserConfig): UserConfig;
declare function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>;
declare function defineConfig(config: ViteUserConfigFnObject): ViteUserConfigFnObject;
declare function defineConfig(config: ViteUserConfigFnPromise): ViteUserConfigFnPromise;
declare function defineConfig(config: ViteUserConfigExport): ViteUserConfigExport;
declare function defineProject(config: UserWorkspaceConfig): UserWorkspaceConfig;
declare function defineProject(config: Promise<UserWorkspaceConfig>): Promise<UserWorkspaceConfig>;
declare function defineProject(config: UserProjectConfigFn): UserProjectConfigFn;
declare function defineProject(config: UserProjectConfigExport): UserProjectConfigExport;
export { UserProjectConfigExport, UserProjectConfigFn, UserWorkspaceConfig, configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude, defineConfig, defineProject };
export type { ViteUserConfigExport, ViteUserConfigFn, ViteUserConfigFnObject, ViteUserConfigFnPromise };
+15
View File
@@ -0,0 +1,15 @@
export { c as configDefaults, a as coverageConfigDefaults, d as defaultExclude, b as defaultInclude } from './chunks/defaults.D2ip7f-X.js';
export { mergeConfig } from 'vite';
export { d as defaultBrowserPort } from './chunks/constants.-juJ8b_4.js';
import 'node:os';
import './chunks/env.DzFJjrmK.js';
import 'std-env';
function defineConfig(config) {
return config;
}
function defineProject(config) {
return config;
}
export { defineConfig, defineProject };
+1069
View File
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
export { S as Snapshots, T as TestRunner, a as assert, b as assertType, c as createExpect, g as expect, d as inject, s as should, v as vi, e as vitest } from './chunks/index.OVGXnVRj.js';
export { V as EvaluatedModules } from './chunks/rpc.DWKX1ZbI.js';
export { j as afterAll, k as afterEach, l as aroundAll, n as aroundEach, o as beforeAll, q as beforeEach, r as describe, t as it, u as onTestFailed, v as onTestFinished, w as recordArtifact, x as suite, y as test } from './chunks/run.CQOUYP-x.js';
export { expectTypeOf } from 'expect-type';
import * as chai from 'chai';
export { chai };
import './chunks/utils.DYj33du9.js';
import './chunks/source-map.Dx3YPjIe.js';
import './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import './chunks/spy.DPLvp2J7.js';
import './chunks/display.pkpxlVcY.js';
import './chunks/index.M2dsQ_UQ.js';
import './chunks/tinyrainbow.Ht9iggcq.js';
import './task-utils.js';
import './chunks/plugins.Cigb0uSy.js';
import './chunks/offset.Dy-5Fdfn.js';
import 'tinybench';
import 'vite/module-runner';
import './chunks/index.DmDMHCg8.js';
+191
View File
@@ -0,0 +1,191 @@
import { ModuleEvaluator, ModuleRunnerImportMeta, ModuleRunnerContext, EvaluatedModuleNode } from 'vite/module-runner';
import { G as GetterTracker } from './chunks/rpc.d.DA9Utv4e.js';
import { RuntimeRPC } from './index.js';
import { EvaluatedModules as VitestEvaluatedModules } from './index.js';
import vm from 'node:vm';
import './chunks/config.d.CU_b-wJj.js';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
type ModuleExecutionInfo = Map<string, ModuleExecutionInfoEntry>;
interface ModuleExecutionInfoEntry {
startOffset: number;
/** The duration that was spent executing the module. */
duration: number;
/** The time that was spent executing the module itself and externalized imports. */
selfTime: number;
external?: boolean;
importer?: string;
}
/**
* Worker-wide cache of V8 code cache buffers for externalized modules.
*
* vm pools create a fresh executor per test file, so every externalized
* module is compiled and evaluated again in each fresh context. The compiled
* code has no per-context state — reusing its V8 code cache skips the
* re-parse/re-compile while the evaluation still happens per context.
*
* Entries are keyed by the module identifier and guarded by the exact source
* text, so an invalidated module that produces different code simply replaces
* its entry.
*/
declare class CodeCache {
private entries;
get(identifier: string, source: string): Buffer | undefined;
/**
* Stores the code cache produced by `produce` unless an entry for the same
* source already exists. A `produce` failure is recorded as an empty entry,
* so it is not retried on every fresh context.
*/
store(identifier: string, source: string, produce: () => Buffer): void;
delete(identifier: string): void;
clear(): void;
}
declare class FileMap {
private fsCache;
private fsBufferCache;
readFileAsync(path: string): Promise<string>;
readFile(path: string): string;
readBuffer(path: string): Buffer<ArrayBuffer>;
}
interface ModuleEvaluateOptions {
timeout?: vm.RunningScriptOptions["timeout"] | undefined;
breakOnSigint?: vm.RunningScriptOptions["breakOnSigint"] | undefined;
}
type ModuleLinker = (specifier: string, referencingModule: VMModule, extra: {
assert: object;
}) => VMModule | Promise<VMModule>;
type ModuleStatus = "unlinked" | "linking" | "linked" | "evaluating" | "evaluated" | "errored";
declare class VMModule {
dependencySpecifiers: readonly string[];
error: any;
identifier: string;
context: vm.Context;
namespace: object;
status: ModuleStatus;
evaluate(options?: ModuleEvaluateOptions): Promise<void>;
link(linker: ModuleLinker): Promise<void>;
}
interface ExternalModulesExecutorOptions {
context: vm.Context;
fileMap: FileMap;
codeCache?: CodeCache;
resolveCache?: Map<string, string>;
moduleInfoCache?: Map<string, ModuleInformation>;
packageCache: Map<string, any>;
transform: RuntimeRPC["transform"];
interopDefault?: boolean;
viteClientModule: Record<string, unknown>;
}
interface ModuleInformation {
type: "data" | "builtin" | "vite" | "wasm" | "module" | "commonjs" | "network";
url: string;
path: string;
exists?: boolean;
}
type SyncModuleDisposition = {
kind: "ready";
module: VMModule;
} | {
kind: "json";
code: string;
} | {
kind: "source";
code: string;
};
declare class ExternalModulesExecutor {
#private;
private options;
private cjs;
private esm;
private vite;
private context;
private fs;
readonly codeCache: CodeCache | undefined;
private resolvers;
constructor(options: ExternalModulesExecutorOptions);
import(identifier: string): Promise<object>;
require(identifier: string): any;
createRequire(identifier: string): NodeJS.Require;
private shouldRequireAsEsm;
private requireEsm;
resolveSyncSpecifier: (specifier: string, referencer: string) => string;
materializeSyncModule: (identifier: string, forceEsmSource: boolean) => SyncModuleDisposition;
importModuleDynamically: (specifier: string, referencer: VMModule) => Promise<VMModule>;
resolveModule: (specifier: string, referencer: string) => Promise<VMModule>;
resolve(specifier: string, parent: string): string;
private getModuleInformation;
private resolveModuleInformation;
private assertModuleExists;
private createModule;
private get isNetworkSupported();
}
declare module "vite/module-runner" {
interface EvaluatedModuleNode {
/**
* @internal
*/
mockedExports?: Record<string, any>;
}
}
declare module "vite/module-runner" {
interface EvaluatedModuleNode {
/**
* @internal
*/
mockedExports?: Record<string, any>;
}
}
interface VitestVmOptions {
context: vm.Context;
externalModulesExecutor: ExternalModulesExecutor;
}
interface VitestModuleEvaluatorOptions {
evaluatedModules?: VitestEvaluatedModules;
metaEnv?: ModuleRunnerImportMeta["env"];
interopDefault?: boolean | undefined;
injectCjsGlobals?: boolean | undefined;
moduleExecutionInfo?: ModuleExecutionInfo;
getCurrentTestFilepath?: () => string | undefined;
compiledFunctionArgumentsNames?: string[];
compiledFunctionArgumentsValues?: unknown[];
getterTracker?: GetterTracker;
}
declare class VitestModuleEvaluator implements ModuleEvaluator {
private options;
stubs: Record<string, any>;
env: ModuleRunnerImportMeta["env"];
private vm;
private compiledFunctionArgumentsNames?;
private compiledFunctionArgumentsValues;
private getterTracker;
static EXPORTS_MAX_INVOCATIONS: number;
private primitives;
private debug;
private _otel;
private _evaluatedModules?;
constructor(vmOptions?: VitestVmOptions | undefined, options?: VitestModuleEvaluatorOptions);
private convertIdToImportUrl;
runExternalModule(id: string): Promise<any>;
runInlinedModule(context: ModuleRunnerContext, code: string, module: Readonly<EvaluatedModuleNode>): Promise<any>;
private _createCJSGlobals;
private _runInlinedModule;
private createRequire;
private shouldInterop;
}
declare function createImportMetaEnvProxy(): ModuleRunnerImportMeta["env"];
declare function getDefaultRequestStubs(context?: vm.Context): Record<string, any>;
declare function isPrimitive(v: any): boolean;
declare function wrapId(id: string): string;
declare function unwrapId(id: string): string;
export { VitestModuleEvaluator, createImportMetaEnvProxy, getDefaultRequestStubs, isPrimitive, unwrapId, wrapId };
export type { VitestModuleEvaluatorOptions };
+413
View File
@@ -0,0 +1,413 @@
import { isBuiltin, createRequire } from 'node:module';
import { pathToFileURL, fileURLToPath } from 'node:url';
import vm from 'node:vm';
import { ssrModuleExportsKey, ssrImportMetaKey, ssrImportKey, ssrDynamicImportKey, ssrExportAllKey } from 'vite/module-runner';
import { Traces } from './traces.js';
const performanceNow = performance.now.bind(performance);
class ModuleDebug {
executionStack = [];
startCalculateModuleExecutionInfo(filename, options) {
const startTime = performanceNow();
this.executionStack.push({
filename,
startTime,
subImportTime: 0
});
return () => {
const duration = performanceNow() - startTime;
const currentExecution = this.executionStack.pop();
if (currentExecution == null) throw new Error("Execution stack is empty, this should never happen");
const selfTime = duration - currentExecution.subImportTime;
if (this.executionStack.length > 0) this.executionStack.at(-1).subImportTime += duration;
return {
startOffset: options.startOffset,
external: options.external,
importer: options.importer,
duration,
selfTime
};
};
}
}
const isWindows = process.platform === "win32";
// Compiled scripts of inlined modules, shared across vm contexts: vm pools
// evaluate every module again in each fresh context, but the compiled script
// holds no per-context state (Vite rewrites dynamic imports to
// `__vite_ssr_dynamic_import__`, so no per-context import callback is baked
// in) — only its evaluation has to happen per context. Keyed by module id
// (`mock:` ids stay distinct from their originals).
const vmInlineScriptCache = /* @__PURE__ */ new Map();
function getVmInlineScript(id, wrappedCode, options) {
let script = vmInlineScriptCache.get(id);
if (!script) {
script = new vm.Script(wrappedCode, options);
vmInlineScriptCache.set(id, script);
}
return script;
}
class VitestModuleEvaluator {
options;
stubs = {};
env;
vm;
compiledFunctionArgumentsNames;
compiledFunctionArgumentsValues = [];
getterTracker;
static EXPORTS_MAX_INVOCATIONS = 1e6;
primitives;
debug = new ModuleDebug();
_otel;
_evaluatedModules;
constructor(vmOptions, options = {}) {
this.options = options;
this._otel = options.traces || new Traces({ enabled: false });
this.env = options.metaEnv ?? createImportMetaEnvProxy();
this.vm = vmOptions;
this.stubs = getDefaultRequestStubs(vmOptions?.context);
this._evaluatedModules = options.evaluatedModules;
if (options.compiledFunctionArgumentsNames) this.compiledFunctionArgumentsNames = options.compiledFunctionArgumentsNames;
if (options.compiledFunctionArgumentsValues) this.compiledFunctionArgumentsValues = options.compiledFunctionArgumentsValues;
if (vmOptions) this.primitives = vm.runInContext("({ Object, Proxy, Reflect })", vmOptions.context);
else this.primitives = {
Object,
Proxy,
Reflect
};
this.getterTracker = options.getterTracker;
}
convertIdToImportUrl(id) {
// TODO: vitest returns paths for external modules, but Vite returns file://
// REMOVE WHEN VITE 6 SUPPORT IS OVER
// unfortunately, there is a bug in Vite where ID is resolved incorrectly, so we can't return files until the fix is merged
// https://github.com/vitejs/vite/pull/20449
if (!isWindows || isBuiltin(id) || /^(?:node:|data:|http:|https:|file:)/.test(id)) return id;
const [filepath, query] = id.split("?");
if (query) return `${pathToFileURL(filepath).toString()}?${query}`;
return pathToFileURL(filepath).toString();
}
async runExternalModule(id) {
if (id in this.stubs) return this.stubs[id];
const file = this.convertIdToImportUrl(id);
const importer = (this._evaluatedModules?.getModuleById(id)?.importers)?.values().next().value;
const filename = id.startsWith("file://") ? fileURLToPath(id) : id;
const finishModuleExecutionInfo = this.debug.startCalculateModuleExecutionInfo(filename, {
startOffset: 0,
external: true,
importer
});
const namespace = await this._otel.$("vitest.module.external", { attributes: { "code.file.path": file } }, () => this.vm ? this.vm.externalModulesExecutor.import(file) : import(file)).finally(() => {
this.options.moduleExecutionInfo?.set(filename, finishModuleExecutionInfo());
});
if (!this.shouldInterop(file, namespace)) return namespace;
const { mod, defaultExport } = interopModule(namespace);
const { Proxy, Reflect } = this.primitives;
return new Proxy(mod, {
get(mod, prop) {
if (prop === "default") return defaultExport;
return mod[prop] ?? defaultExport?.[prop];
},
has(mod, prop) {
if (prop === "default") return defaultExport !== void 0;
return prop in mod || defaultExport && prop in defaultExport;
},
getOwnPropertyDescriptor(mod, prop) {
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop);
if (descriptor) return descriptor;
if (prop === "default" && defaultExport !== void 0) return {
value: defaultExport,
enumerable: true,
configurable: true
};
}
});
}
async runInlinedModule(context, code, module) {
return this._otel.$("vitest.module.inline", (span) => this._runInlinedModule(context, code, module, span));
}
_createCJSGlobals(context, module, span) {
const { Reflect, Proxy, Object } = this.primitives;
const exportsObject = context[ssrModuleExportsKey];
const SYMBOL_NOT_DEFINED = Symbol("not defined");
let moduleExports = SYMBOL_NOT_DEFINED;
// this proxy is triggered only on exports.{name} and module.exports access
// inside the module itself. imported module is always "exports"
const cjsExports = new Proxy(exportsObject, {
get: (target, p, receiver) => {
if (Reflect.has(target, p)) return Reflect.get(target, p, receiver);
return Reflect.get(Object.prototype, p, receiver);
},
getPrototypeOf: () => Object.prototype,
set: (_, p, value) => {
span.addEvent(`cjs export proxy is triggered for ${String(p)}`);
// treat "module.exports =" the same as "exports.default =" to not have nested "default.default",
// so "exports.default" becomes the actual module
if (p === "default" && this.shouldInterop(module.file, { default: value }) && cjsExports !== value) {
span.addEvent("`exports.default` is assigned, copying values");
exportAll(cjsExports, value);
exportsObject.default = value;
return true;
}
if (!Reflect.has(exportsObject, "default")) exportsObject.default = {};
// returns undefined, when accessing named exports, if default is not an object
// but is still present inside hasOwnKeys, this is Node behaviour for CJS
if (moduleExports !== SYMBOL_NOT_DEFINED && isPrimitive(moduleExports)) {
span.addEvent(`\`exports.${String(p)}\` is assigned, but module.exports is a primitive. assigning "undefined" values instead to comply with ESM`);
defineExport(exportsObject, p, () => void 0);
return true;
}
if (!isPrimitive(exportsObject.default)) exportsObject.default[p] = value;
if (p !== "default") defineExport(exportsObject, p, () => value);
return true;
}
});
return {
exports: cjsExports,
module: {
set exports(value) {
span.addEvent("`module.exports` is assigned directly, copying all properties to `exports`");
exportAll(cjsExports, value);
exportsObject.default = value;
moduleExports = value;
},
get exports() {
return cjsExports;
}
},
require: this.createRequire(context[ssrImportMetaKey].url),
__filename: context[ssrImportMetaKey].filename,
__dirname: context[ssrImportMetaKey].dirname
};
}
async _runInlinedModule(context, code, module, span) {
const meta = context[ssrImportMetaKey];
meta.env = this.env;
const globalNamespace = this.vm?.context || globalThis;
// `import.meta` defines evaluated by the runtime defines script;
// `import.meta.env.*` entries are applied through `process.env` instead
const metaDefines = globalNamespace.__vitest_worker__?.metaDefines;
if (metaDefines) for (const key in metaDefines) {
const segments = key.split(".");
let target = meta;
for (let i = 0; i < segments.length - 1; i++) target = target[segments[i]] || (target[segments[i]] = {});
target[segments[segments.length - 1]] = metaDefines[key];
}
if (this.options.getCurrentTestFilepath?.() === module.file) Object.defineProperty(meta, "vitest", {
// @ts-expect-error injected untyped global
get: () => globalNamespace.__vitest_index__ });
span.setAttribute("code.file.path", meta.filename);
const __vite_ssr_exportName__ = context.__vite_ssr_exportName__ || ((name, getter) => Object.defineProperty(context[ssrModuleExportsKey], name, {
enumerable: true,
configurable: true,
get: getter
}));
let __vite_track_exportName__;
const getterTracker = this.getterTracker;
if (getterTracker) __vite_track_exportName__ = getterTracker.createTracker(module.id, __vite_ssr_exportName__);
const argumentsList = [
ssrModuleExportsKey,
ssrImportMetaKey,
ssrImportKey,
ssrDynamicImportKey,
ssrExportAllKey,
"__vite_ssr_exportName__"
];
const argumentsValues = [
context[ssrModuleExportsKey],
context[ssrImportMetaKey],
context[ssrImportKey],
context[ssrDynamicImportKey],
context[ssrExportAllKey],
__vite_track_exportName__ || __vite_ssr_exportName__
];
// TODO@discuss switch the default in Vitest 6(?)
// backwards compat for vite-node
const injectCjsGlobals = this.options.injectCjsGlobals !== false || module.meta?.moduleType === "cjs";
if (injectCjsGlobals) {
const cjsGlobals = this._createCJSGlobals(context, module, span);
argumentsList.push("__filename", "__dirname", "module", "exports", "require");
argumentsValues.push(cjsGlobals.__filename, cjsGlobals.__dirname, cjsGlobals.module, cjsGlobals.exports, cjsGlobals.require);
}
if (this.compiledFunctionArgumentsNames) argumentsList.push(...this.compiledFunctionArgumentsNames);
if (this.compiledFunctionArgumentsValues) argumentsValues.push(...this.compiledFunctionArgumentsValues);
span.setAttribute("vitest.module.arguments", argumentsList);
// add 'use strict' since ESM enables it by default
const codeDefinition = `'use strict';async (${argumentsList.join(",")})=>{{`;
const wrappedCode = `${codeDefinition}${code}\n}}`;
const options = {
// use original id for auto spy module (vi.mock(..., { spy: true }))
filename: module.id.startsWith("mock:") ? module.id.slice(5) : module.id,
lineOffset: 0,
columnOffset: -codeDefinition.length
};
// this will always be 1 element because it's cached after load
const importer = module.importers.values().next().value;
// Initialize execution info in case worker exited before module evaluation finished
this.options.moduleExecutionInfo?.set(options.filename, {
duration: 0,
selfTime: 0,
startOffset: codeDefinition.length,
importer
});
const finishModuleExecutionInfo = this.debug.startCalculateModuleExecutionInfo(options.filename, {
startOffset: codeDefinition.length,
importer
});
try {
await (this.vm ? getVmInlineScript(module.id, wrappedCode, options).runInContext(this.vm.context) : vm.runInThisContext(wrappedCode, options))(...argumentsValues);
} catch (error) {
if (!injectCjsGlobals) throw enhanceMissingCjsGlobalsError(error);
throw error;
} finally {
// moduleExecutionInfo needs to use Node filename instead of the normalized one
// because we rely on this behaviour in coverage-v8, for example
this.options.moduleExecutionInfo?.set(options.filename, finishModuleExecutionInfo());
}
}
createRequire(url) {
if (url.startsWith("data:")) {
const _require = (id) => {
throw new SyntaxError(`require() is not supported in virtual modules. Trying to call require("${id}") in ${url}`);
};
_require.resolve = _require;
return _require;
}
return this.vm ? this.vm.externalModulesExecutor.createRequire(url) : createRequire(url);
}
shouldInterop(path, mod) {
if (this.options.interopDefault === false) return false;
// never interop ESM modules
// TODO: should also skip for `.js` with `type="module"`
return !path.endsWith(".mjs") && "default" in mod;
}
}
function createImportMetaEnvProxy() {
// packages/vitest/src/node/plugins/index.ts:146
const booleanKeys = [
"DEV",
"PROD",
"SSR"
];
return new Proxy(process.env, {
get(_, key) {
if (typeof key !== "string") return;
if (booleanKeys.includes(key)) return !!process.env[key];
return process.env[key];
},
set(_, key, value) {
if (typeof key !== "string") return true;
if (booleanKeys.includes(key)) process.env[key] = value ? "1" : "";
else process.env[key] = value;
return true;
}
});
}
function updateStyle(id, css) {
if (typeof document === "undefined") return;
const element = document.querySelector(`[data-vite-dev-id="${id}"]`);
if (element) {
element.textContent = css;
return;
}
const head = document.querySelector("head");
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.setAttribute("data-vite-dev-id", id);
style.textContent = css;
head?.appendChild(style);
}
function removeStyle(id) {
if (typeof document === "undefined") return;
const sheet = document.querySelector(`[data-vite-dev-id="${id}"]`);
if (sheet) document.head.removeChild(sheet);
}
const defaultClientStub = {
injectQuery: (id) => id,
createHotContext: () => {
return {
accept: () => {},
prune: () => {},
dispose: () => {},
decline: () => {},
invalidate: () => {},
on: () => {},
send: () => {}
};
},
updateStyle: () => {},
removeStyle: () => {}
};
function getDefaultRequestStubs(context) {
if (!context) return { "/@vite/client": {
...defaultClientStub,
updateStyle,
removeStyle
} };
return { "/@vite/client": vm.runInContext(`(defaultClient) => ({ ...defaultClient, updateStyle: ${updateStyle.toString()}, removeStyle: ${removeStyle.toString()} })`, context)(defaultClientStub) };
}
function exportAll(exports, sourceModule) {
// #1120 when a module exports itself it causes
// call stack error
if (exports === sourceModule) return;
if (isPrimitive(sourceModule) || Array.isArray(sourceModule) || sourceModule instanceof Promise) return;
for (const key in sourceModule) if (key !== "default" && !(key in exports)) try {
defineExport(exports, key, () => sourceModule[key]);
} catch {}
}
// keep consistency with Vite on how exports are defined
function defineExport(exports, key, value) {
Object.defineProperty(exports, key, {
enumerable: true,
configurable: true,
get: value
});
}
function isPrimitive(v) {
return !(typeof v === "object" || typeof v === "function") || v == null;
}
function interopModule(mod) {
if (isPrimitive(mod)) return {
mod: { default: mod },
defaultExport: mod
};
let defaultExport = "default" in mod ? mod.default : mod;
if (!isPrimitive(defaultExport) && defaultExport.__esModule) {
mod = defaultExport;
if ("default" in defaultExport) defaultExport = defaultExport.default;
}
return {
mod,
defaultExport
};
}
const CJS_GLOBALS_REFERENCE_ERROR_RE = /^(module|exports|require|__filename|__dirname) is not defined$/;
const ESM_HINTS = {
module: "use \"export\" declarations instead of \"module.exports\"",
exports: "use \"export\" declarations instead of \"exports\"",
require: "use \"import\" declarations or \"createRequire(import.meta.url)\" instead of \"require\"",
__filename: "use \"import.meta.filename\" instead of \"__filename\"",
__dirname: "use \"import.meta.dirname\" instead of \"__dirname\""
};
function enhanceMissingCjsGlobalsError(error) {
if (error == null || typeof error !== "object") return error;
const referenceError = error;
if (referenceError.name !== "ReferenceError" || typeof referenceError.message !== "string") return error;
// the message is anchored, so already enhanced errors are not enhanced twice
const name = referenceError.message.match(CJS_GLOBALS_REFERENCE_ERROR_RE)?.[1];
if (!name) return error;
const message = `${referenceError.message}\n\n"${name}" is a CommonJS variable that is not available in ES modules, and "injectCjsGlobals" is disabled. If this module is meant to be an ES module, ${ESM_HINTS[name]}. If it is meant to be a CommonJS module, use the ".cjs" file extension, set "type": "commonjs" in the nearest package.json, or externalize it with "server.deps.external".`;
if (typeof referenceError.stack === "string") referenceError.stack = referenceError.stack.replace(referenceError.message, message);
referenceError.message = message;
return error;
}
const VALID_ID_PREFIX = `/@id/`;
const NULL_BYTE_PLACEHOLDER = `__x00__`;
function wrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id : VALID_ID_PREFIX + id.replace("\0", NULL_BYTE_PLACEHOLDER);
}
function unwrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length).replace(NULL_BYTE_PLACEHOLDER, "\0") : id;
}
export { VitestModuleEvaluator, createImportMetaEnvProxy, getDefaultRequestStubs, isPrimitive, unwrapId, wrapId };
+516
View File
@@ -0,0 +1,516 @@
import * as vite from 'vite';
import { UserConfig as UserConfig$1, ResolvedConfig as ResolvedConfig$1, TransformResult, ViteDevServer, LogLevel, LoggerOptions, Logger as Logger$1 } from 'vite';
export { vite as Vite };
export { esbuildVersion, isCSSRequest, isFileLoadingAllowed, parseAst, parseAstAsync, rollupVersion, version as viteVersion } from 'vite';
import { IncomingMessage } from 'node:http';
import { R as ResolvedConfig, e as CliOptions, d as UserConfig, L as Logger, A as ApiConfig, P as PluginHarness, f as Vitest, g as ResolvedCoverageOptions, h as CoverageMap, i as ReportContext, j as TestProject, k as VitestOptions, l as VitestRunMode, m as TestSpecification, n as PoolWorker, o as PoolOptions, p as WorkerRequest, q as TestSequencer } from './chunks/plugin.d.BbcoZhuj.js';
export { M as AgentReporter, B as BaseCoverageOptions, r as BaseReporter, s as BenchmarkUserOptions, t as BrowserBuiltinProvider, u as BrowserCommand, v as BrowserCommandContext, w as BrowserConfigOptions, x as BrowserInstanceOption, y as BrowserModuleMocker, z as BrowserOrchestrator, D as BrowserProvider, E as BrowserProviderOption, G as BrowserScript, H as BrowserServerContribution, J as BrowserServerFactory, K as BrowserServerState, N as BrowserServerStateSession, O as BrowserTraceViewOptions, Q as BuiltinEnvironment, S as BuiltinReporterOptions, X as BuiltinReporters, Y as CSSModuleScopeStrategy, Z as CacheKeyIdGenerator, _ as CacheKeyIdGeneratorContext, $ as CoverageInstrumenter, a0 as CoverageIstanbulOptions, C as CoverageOptions, a1 as CoverageProvider, a2 as CoverageProviderModule, a3 as CoverageReporter, a4 as CoverageV8Options, a5 as CustomProviderOptions, a6 as DefaultReporter, a7 as DepsOptimizationOptions, a8 as DotReporter, a9 as EnvironmentOptions, aa as GithubActionsReporter, ab as HTMLOptions, ac as HangingProcessReporter, I as InlineConfig, ad as InstrumenterOptions, ae as JUnitOptions, af as JUnitReporter, ag as JsonAssertionResult, ah as JsonOptions, ai as JsonReporter, aj as JsonTestResult, ak as JsonTestResults, M as MinimalReporter, al as ModuleDiagnostic, am as OnServerRestartHandler, an as OnTestsRerunHandler, ao as ParentProjectBrowser, ap as Pool, aq as PoolRunnerInitializer, ar as PoolTask, as as ProjectBrowser, at as ProjectConfig, au as Report, av as ReportedHookContext, aw as Reporter, ax as ReportersMap, ay as ResolveSnapshotPathHandler, az as ResolveSnapshotPathHandlerContext, aA as ResolvedBrowserOptions, aB as ResolvedProjectConfig, aC as SerializedTestProject, aD as SuiteNameTemplateVariables, aE as TapFlatReporter, aF as TapReporter, aG as TaskOptions, aH as TestCase, aI as TestCollection, aJ as TestDiagnostic, aK as TestModule, aL as TestModuleState, aM as TestResult, aN as TestResultFailed, aO as TestResultPassed, aP as TestResultPending, aQ as TestResultSkipped, aR as TestRunEndReason, aS as TestRunResult, aT as TestSequencerConstructor, aU as TestSpecificationOptions, aV as TestState, aW as TestSuite, aX as TestSuiteState, aY as ToMatchScreenshotComparators, aZ as ToMatchScreenshotOptions, a_ as TypecheckConfig, U as UserWorkspaceConfig, a$ as VerboseReporter, b0 as VitestEnvironment, b1 as VitestPackageInstaller, V as VitestPluginContext, W as WatcherTriggerPattern, b2 as WorkerResponse, b3 as _BrowserNames, b4 as experimental_getRunnerTask, b5 as startVitest } from './chunks/plugin.d.BbcoZhuj.js';
import { EventEmitter } from 'events';
import { A as Awaitable } from './chunks/config.d.CU_b-wJj.js';
import { AfterSuiteRunMeta } from './index.js';
export { X as RunnerTask, aH as RunnerTaskResult, j as RunnerTaskResultPack, m as RunnerTestCase, a as RunnerTestFile, O as RunnerTestSuite, aI as RuntimeConfig, bf as SequenceHooks, bg as SequenceSetupFiles, aJ as SerializedError, bh as disableDefaultColors } from './chunks/config.d.CU_b-wJj.js';
import { RuntimeRPC } from './index.js';
import { Writable } from 'node:stream';
import { ContextRPC } from './index.js';
export { T as TestExecutionType } from './chunks/worker.d.MLmnzOJE.js';
import { Debugger } from 'obug';
export { g as generateFileHash } from './chunks/task-utils.d.BZm4GSQD.js';
export { CDPSession } from 'vitest/browser';
import './chunks/browser.d.g5Thl309.js';
import 'chai';
import 'vitest/optional-types.js';
import '@vitest/mocker';
import './chunks/source-map.d.YqWNcp4e.js';
import 'node:path';
import 'node:console';
import 'node:fs/promises';
import 'node:fs';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
import 'vite/module-runner';
import './chunks/environment.d.C6xYahWA.js';
declare function escapeTestName(label: string, dynamic: boolean): string;
type RawErrsMap = Map<string, TscErrorInfo[]>;
interface TscErrorInfo {
filePath: string;
errCode: number;
errMsg: string;
line: number;
column: number;
}
interface CollectLineNumbers {
target: number;
next: number;
prev?: number;
}
type CollectLines = { [key in keyof CollectLineNumbers]: string };
interface RootAndTarget {
root: string;
targetAbsPath: string;
}
type Context = RootAndTarget & {
rawErrsMap: RawErrsMap;
openedDirs: Set<string>;
lastActivePath?: string;
};
declare function isValidApiRequest(config: ResolvedConfig, req: IncomingMessage): boolean;
interface OptionConfig {
default?: any;
type?: any;
}
declare class Option {
rawName: string;
description: string;
/** Option name */
name: string;
/** Option name and aliases */
names: string[];
isBoolean?: boolean;
required?: boolean;
config: OptionConfig;
negated: boolean;
constructor(rawName: string, description: string, config?: OptionConfig);
}
interface CommandArg {
required: boolean;
value: string;
variadic: boolean;
}
interface HelpSection {
title?: string;
body: string;
}
interface CommandConfig {
allowUnknownOptions?: boolean;
ignoreOptionDefaultValue?: boolean;
}
declare type HelpCallback = (sections: HelpSection[]) => void | HelpSection[];
declare type CommandExample = ((bin: string) => string) | string;
declare class Command {
rawName: string;
description: string;
config: CommandConfig;
cli: CAC;
options: Option[];
aliasNames: string[];
name: string;
args: CommandArg[];
commandAction?: (...args: any[]) => any;
usageText?: string;
versionNumber?: string;
examples: CommandExample[];
helpCallback?: HelpCallback;
globalCommand?: GlobalCommand;
constructor(rawName: string, description: string, config: CommandConfig, cli: CAC);
usage(text: string): this;
allowUnknownOptions(): this;
ignoreOptionDefaultValue(): this;
version(version: string, customFlags?: string): this;
example(example: CommandExample): this;
/**
* Add a option for this command
* @param rawName Raw option name(s)
* @param description Option description
* @param config Option config
*/
option(rawName: string, description: string, config?: OptionConfig): this;
alias(name: string): this;
action(callback: (...args: any[]) => any): this;
/**
* Check if a command name is matched by this command
* @param name Command name
*/
isMatched(name: string): boolean;
get isDefaultCommand(): boolean;
get isGlobalCommand(): boolean;
/**
* Check if an option is registered in this command
* @param name Option name
*/
hasOption(name: string): Option | undefined;
outputHelp(): void;
outputVersion(): void;
checkRequiredArgs(): void;
/**
* Check if the parsed options contain any unknown options
*
* Exit and output error when true
*/
checkUnknownOptions(): void;
/**
* Check if the required string-type options exist
*/
checkOptionValue(): void;
}
declare class GlobalCommand extends Command {
constructor(cli: CAC);
}
interface ParsedArgv {
args: ReadonlyArray<string>;
options: {
[k: string]: any;
};
}
declare class CAC extends EventEmitter {
/** The program name to display in help and version message */
name: string;
commands: Command[];
globalCommand: GlobalCommand;
matchedCommand?: Command;
matchedCommandName?: string;
/**
* Raw CLI arguments
*/
rawArgs: string[];
/**
* Parsed CLI arguments
*/
args: ParsedArgv['args'];
/**
* Parsed CLI options, camelCased
*/
options: ParsedArgv['options'];
showHelpOnExit?: boolean;
showVersionOnExit?: boolean;
/**
* @param name The program name to display in help and version message
*/
constructor(name?: string);
/**
* Add a global usage text.
*
* This is not used by sub-commands.
*/
usage(text: string): this;
/**
* Add a sub-command
*/
command(rawName: string, description?: string, config?: CommandConfig): Command;
/**
* Add a global CLI option.
*
* Which is also applied to sub-commands.
*/
option(rawName: string, description: string, config?: OptionConfig): this;
/**
* Show help message when `-h, --help` flags appear.
*
*/
help(callback?: HelpCallback): this;
/**
* Show version number when `-v, --version` flags appear.
*
*/
version(version: string, customFlags?: string): this;
/**
* Add a global example.
*
* This example added here will not be used by sub-commands.
*/
example(example: CommandExample): this;
/**
* Output the corresponding help message
* When a sub-command is matched, output the help message for the command
* Otherwise output the global one.
*
*/
outputHelp(): void;
/**
* Output the version number.
*
*/
outputVersion(): void;
private setParsedInfo;
unsetMatchedCommand(): void;
/**
* Parse argv
*/
parse(argv?: string[], {
/** Whether to run the action for matched command */
run, }?: {
run?: boolean | undefined;
}): ParsedArgv;
private mri;
runMatchedCommand(): any;
}
interface CliParseOptions {
allowUnknownOptions?: boolean;
}
declare function createCLI(options?: CliParseOptions): CAC;
declare function parseCLI(argv: string | string[], config?: CliParseOptions): {
filter: string[];
options: CliOptions;
};
declare function resolveApiServerConfig(config: UserConfig, defaultPort: number, logger: Logger): ApiConfig;
declare function resolveConfig(options?: UserConfig, viteOverrides?: UserConfig$1, pluginsHarness?: PluginHarness): Promise<ResolvedConfig$1>;
type Threshold = "lines" | "functions" | "statements" | "branches";
interface ResolvedThreshold {
coverageMap: CoverageMap;
name: string;
thresholds: Partial<Record<Threshold, number | undefined>>;
/** When `true`, check `thresholds` against each file instead of the aggregate. */
perFile: boolean;
/** Additional per-file-only minimums (object form of `perFile`), or `null`. */
perFileThresholds: Partial<Record<Threshold, number | undefined>> | null;
}
/**
* Holds info about raw coverage results that are stored on file system:
*
* ```json
* "project-a": {
* "web": {
* "tests/math.test.ts": "coverage-1.json",
* "tests/utils.test.ts": "coverage-2.json",
* // ^^^^^^^^^^^^^^^ Raw coverage on file system
* },
* "ssr": { ... },
* "browser": { ... },
* },
* "project-b": ...
* ```
*/
type CoverageFiles = Map<NonNullable<AfterSuiteRunMeta["projectName"]> | symbol, Record<AfterSuiteRunMeta["environment"], {
[TestFilenames: string]: string;
}>>;
declare class BaseCoverageProvider {
ctx: Vitest;
readonly name: "v8" | "istanbul";
version: string;
options: ResolvedCoverageOptions;
globCache: Map<string, boolean>;
autoUpdateMarker: string;
globMatchers?: {
matchExclude: (file: string) => boolean;
matchInclude: (file: string) => boolean;
};
coverageFiles: CoverageFiles;
coverageFilesDirectory: string;
reportsDirectoryLock: ReportsDirectoryLock;
roots: string[];
changedFiles?: string[];
_initialize(ctx: Vitest): void;
/**
* Check if file matches `coverage.include` but not `coverage.exclude`
*/
isIncluded(_filename: string, root?: string): boolean;
/**
* Compile `coverage.include`/`coverage.exclude` into reusable matchers once.
* `picomatch.isMatch(file, patterns, options)` recompiles the patterns on
* every call, which dominates the filtering step on large test suites.
*/
private getGlobMatchers;
private getUntestedFilesByRoot;
getUntestedFiles(testedFiles: string[]): Promise<string[]>;
createCoverageMap(): CoverageMap;
generateReports(_: CoverageMap, __: boolean | undefined): Promise<void>;
parseConfigModule(_: string): Promise<{
generate: () => {
code: string;
};
}>;
resolveOptions(): ResolvedCoverageOptions;
clean(clean?: boolean): Promise<void>;
onAfterSuiteRun({ coverage, environment, projectName, testFiles }: AfterSuiteRunMeta): void;
readCoverageFiles<CoverageType>({ onFileRead, onFinished, onDebug }: {
/** Callback invoked with a single coverage result */
onFileRead: (data: CoverageType) => void;
/** Callback invoked once all results of a project for specific transform mode are read */
onFinished: (project: Vitest["projects"][number], environment: string) => Promise<void>;
onDebug: ((...logs: any[]) => void) & {
enabled: boolean;
};
}): Promise<void>;
cleanAfterRun(): Promise<void>;
onTestRunStart(): Promise<void>;
onTestFailure(): Promise<void>;
reportCoverage(coverageMap: unknown, { allTestsRun }: ReportContext): Promise<void>;
reportThresholds(coverageMap: CoverageMap, allTestsRun: boolean | undefined): Promise<void>;
/**
* Constructs collected coverage and users' threshold options into separate sets
* where each threshold set holds their own coverage maps. Threshold set is either
* for specific files defined by glob pattern or global for all other files.
*/
private resolveThresholds;
/**
* Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
*/
private checkThresholds;
private reportThresholdViolations;
/**
* Check if current coverage is above configured thresholds and bump the thresholds if needed
*/
updateThresholds({ thresholds: allThresholds, onUpdate, configurationFile }: {
thresholds: ResolvedThreshold[];
configurationFile: unknown;
onUpdate: () => void;
}): Promise<void>;
mergeReports(coverageMaps: unknown[]): Promise<void>;
hasTerminalReporter(reporters: ResolvedCoverageOptions["reporter"]): boolean;
toSlices<T>(array: T[], size: number): T[][];
transformFile(url: string, project: TestProject, viteEnvironment: string, isTransformedByVite?: boolean): Promise<TransformResult | null | undefined>;
createUncoveredFileTransformer(ctx: Vitest): (filename: string) => Promise<TransformResult | null | undefined>;
}
declare class ReportsDirectoryLock {
private readonly reportsDirectory;
readonly lockFile: string;
constructor(reportsDirectory: string);
acquire(): Promise<void>;
release(): Promise<void>;
private tryWrite;
private readOwner;
private inUseError;
}
declare function createVitest(options: CliOptions, viteOverrides?: UserConfig$1, vitestOptions?: VitestOptions): Promise<Vitest>;
/**
* @deprecated The `mode` argument is no longer used. Use `createVitest(options, viteOverrides?, vitestOptions?)` instead.
*/
declare function createVitest(mode: VitestRunMode, options: CliOptions, viteOverrides?: UserConfig$1, vitestOptions?: VitestOptions): Promise<Vitest>;
declare class FilesNotFoundError extends Error {
code: string;
constructor();
}
declare class GitNotFoundError extends Error {
code: string;
constructor();
}
declare class BrowserConnectionError extends Error {
code: string;
}
declare function resolveFsAllow(projectRoot: string, rootConfigFile: string | false | undefined): string[];
type RunWithFiles = (files: TestSpecification[], invalidates?: string[]) => Promise<void>;
interface ProcessPool {
name: string;
runTests: RunWithFiles;
collectTests: RunWithFiles;
close?: () => Awaitable<void>;
}
declare function getFilePoolName(project: TestProject): ResolvedConfig["pool"];
interface MethodsOptions {
cacheFs?: boolean;
collect?: boolean;
}
declare function createMethodsRPC(project: TestProject, methodsOptions?: MethodsOptions): RuntimeRPC;
/** @experimental */
declare class ForksPoolWorker implements PoolWorker {
readonly name: string;
readonly cacheFs: boolean;
protected readonly entrypoint: string;
protected execArgv: string[];
protected env: Partial<NodeJS.ProcessEnv>;
private _fork?;
private stdout;
private stderr;
private _errorEmitter;
private _pipeErrorTimer;
constructor(options: PoolOptions);
on(event: string, callback: (...args: any[]) => void): void;
off(event: string, callback: (...args: any[]) => void): void;
send(message: WorkerRequest): void;
start(): Promise<void>;
stop(): Promise<void>;
deserialize(data: unknown): unknown;
private emitError;
private get fork();
}
/** @experimental */
declare class ThreadsPoolWorker implements PoolWorker {
readonly name: string;
protected readonly entrypoint: string;
protected execArgv: string[];
protected env: Partial<NodeJS.ProcessEnv>;
private _thread?;
private stdout;
private stderr;
constructor(options: PoolOptions);
on(event: string, callback: (...args: any[]) => void): void;
off(event: string, callback: (...args: any[]) => void): void;
send(message: WorkerRequest): void;
start(): Promise<void>;
stop(): Promise<void>;
deserialize(data: unknown): unknown;
private get thread();
}
/** @experimental */
declare class TypecheckPoolWorker implements PoolWorker {
readonly name: string;
private readonly project;
private _eventEmitter;
constructor(options: PoolOptions);
start(): Promise<void>;
stop(): Promise<void>;
canReuse(): boolean;
send(message: WorkerRequest): void;
on(event: string, callback: (arg: any) => any): void;
off(event: string, callback: (arg: any) => any): void;
deserialize(data: unknown): unknown;
}
/** @experimental */
declare class VmForksPoolWorker extends ForksPoolWorker {
readonly name = "vmForks";
readonly reportMemory = true;
protected readonly entrypoint: string;
constructor(options: PoolOptions);
canReuse(): boolean;
}
/** @experimental */
declare class VmThreadsPoolWorker extends ThreadsPoolWorker {
readonly name = "vmThreads";
readonly reportMemory = true;
protected readonly entrypoint: string;
constructor(options: PoolOptions);
canReuse(): boolean;
}
declare class BaseSequencer implements TestSequencer {
protected ctx: Vitest;
constructor(ctx: Vitest);
shard(files: TestSpecification[]): Promise<TestSpecification[]>;
sort(files: TestSpecification[]): Promise<TestSpecification[]>;
private calculateShardRange;
}
declare function registerConsoleShortcuts(ctx: Vitest, stdin: NodeJS.ReadStream | undefined, stdout: NodeJS.WriteStream | Writable): () => void;
interface WorkerContext extends ContextRPC {}
/**
* Check if the url is allowed to be served, via the `server.fs` config.
* @deprecated Use the `isFileLoadingAllowed` function instead.
*/
declare function isFileServingAllowed(config: ResolvedConfig$1, url: string): boolean;
declare function isFileServingAllowed(url: string, server: ViteDevServer): boolean;
declare function createViteLogger(console: Logger, level?: LogLevel, options?: LoggerOptions): Logger$1;
declare const rootDir: string;
declare const distDir: string;
declare function createDebugger(namespace: `vitest:${string}`): Debugger | undefined;
declare const version: string;
declare const createViteServer: typeof vite.createServer;
declare const rolldownVersion: string | undefined;
export { ApiConfig, BaseCoverageProvider, BaseSequencer, BrowserConnectionError, CliOptions, ForksPoolWorker, GitNotFoundError, Logger, PluginHarness, PoolOptions, PoolWorker, ReportContext, ResolvedConfig, ResolvedCoverageOptions, TestProject, TestSequencer, TestSpecification, UserConfig as TestUserConfig, FilesNotFoundError as TestsNotFoundError, ThreadsPoolWorker, TypecheckPoolWorker, Vitest, VitestOptions, VitestRunMode, VmForksPoolWorker, VmThreadsPoolWorker, WorkerRequest, createCLI, createDebugger, createMethodsRPC, createViteLogger, createViteServer, createVitest, distDir, escapeTestName, getFilePoolName, isFileServingAllowed, isValidApiRequest, parseCLI, registerConsoleShortcuts, resolveApiServerConfig, resolveConfig, resolveFsAllow, rolldownVersion, rootDir, version };
export type { CliParseOptions, ProcessPool, CollectLineNumbers as TypeCheckCollectLineNumbers, CollectLines as TypeCheckCollectLines, Context as TypeCheckContext, TscErrorInfo as TypeCheckErrorInfo, RawErrsMap as TypeCheckRawErrorsMap, RootAndTarget as TypeCheckRootAndTarget, WorkerContext };
+73
View File
@@ -0,0 +1,73 @@
import * as vite from 'vite';
export { esbuildVersion, isCSSRequest, isFileLoadingAllowed, parseAst, parseAstAsync, rollupVersion, version as viteVersion } from 'vite';
import { V as Vitest } from './chunks/index.B89dZ0-N.js';
export { M as AgentReporter, B as BaseCoverageProvider, a as BaseSequencer, b as BrowserConnectionError, D as DefaultReporter, c as DotReporter, F as ForksPoolWorker, G as GitNotFoundError, d as GithubActionsReporter, H as HangingProcessReporter, J as JUnitReporter, e as JsonReporter, L as Logger, M as MinimalReporter, P as PluginHarness, R as ReportersMap, T as TapFlatReporter, f as TapReporter, g as TestsNotFoundError, h as ThreadsPoolWorker, i as TypecheckPoolWorker, j as VerboseReporter, k as VitestPackageInstaller, l as VmForksPoolWorker, m as VmThreadsPoolWorker, n as createDebugger, o as createMethodsRPC, p as createViteLogger, q as escapeTestName, r as experimental_getRunnerTask, s as getFilePoolName, t as isFileServingAllowed, u as isValidApiRequest, v as resolveApiServerConfig, w as resolveConfig, x as resolveFsAllow } from './chunks/index.B89dZ0-N.js';
export { c as createCLI, p as parseCLI } from './chunks/cac.D805sv8h.js';
export { c as createVitest, r as registerConsoleShortcuts, s as startVitest } from './chunks/cli-api.LUtK11-x.js';
export { distDir, rootDir } from './path.js';
export { generateFileHash } from './task-utils.js';
export { m as disableDefaultColors } from './chunks/tinyrainbow.Ht9iggcq.js';
import 'node:os';
import './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import './chunks/source-map.Dx3YPjIe.js';
import 'node:fs';
import 'node:perf_hooks';
import './chunks/index.DmDMHCg8.js';
import 'events';
import 'https';
import 'http';
import 'net';
import 'tls';
import 'crypto';
import 'stream';
import 'url';
import 'zlib';
import 'buffer';
import 'util';
import './chunks/constants.-juJ8b_4.js';
import 'node:crypto';
import './chunks/run.CQOUYP-x.js';
import './chunks/index.M2dsQ_UQ.js';
import './chunks/display.pkpxlVcY.js';
import './chunks/nativeModuleRunner.BOvBCgI_.js';
import 'node:url';
import 'node:module';
import 'node:path';
import 'node:process';
import 'node:fs/promises';
import 'node:assert';
import 'node:v8';
import 'node:util';
import 'vite/module-runner';
import './traces.js';
import 'obug';
import 'tinyglobby';
import './chunks/defaults.D2ip7f-X.js';
import './chunks/env.DzFJjrmK.js';
import 'std-env';
import 'magic-string';
import '@vitest/mocker/node';
import 'node:console';
import 'node:stream';
import 'tinyexec';
import './chunks/offset.Dy-5Fdfn.js';
import './chunks/utils.CtqmNjXD.js';
import '#module-evaluator';
import 'picomatch';
import './chunks/coverage.BcXK1DsQ.js';
import './chunks/resolver.CBCHRZ7j.js';
import 'es-module-lexer';
import 'node:tty';
import 'node:events';
import './chunks/modules.BJuCwlRJ.js';
import 'node:child_process';
import 'node:worker_threads';
import 'readline';
import 'node:readline';
const version = Vitest.version;
const createViteServer = vite.createServer;
// rolldownVersion is exported only by rolldown-vite
const rolldownVersion = vite.rolldownVersion;
export { createViteServer, rolldownVersion, version };
+41
View File
@@ -0,0 +1,41 @@
import { isBuiltin } from 'node:module';
let port;
const initialize = async ({ port: _port, time: _time }) => {
port = _port;
};
const NOW_LENGTH = Date.now().toString().length;
const REGEXP_VITEST = new RegExp(`%3Fvitest=\\d{${NOW_LENGTH}}`);
const REGEXP_MOCK_ACTUAL = /\?mock=actual/;
const resolve = (specifier, context, defaultResolve) => {
if (specifier.includes("mock=actual")) {
// url is already resolved by `importActual`
const moduleId = specifier.replace(REGEXP_MOCK_ACTUAL, "");
return {
url: moduleId,
format: isBuiltin(moduleId) ? "builtin" : void 0,
shortCircuit: true
};
}
const isVitest = specifier.includes("%3Fvitest=");
const result = defaultResolve(isVitest ? specifier.replace(REGEXP_VITEST, "") : specifier, context);
if (!port || !context?.parentURL) return result;
if (typeof result === "object" && "then" in result) return result.then((resolved) => {
ensureModuleGraphEntry(resolved.url, context.parentURL);
if (isVitest) resolved.url = `${resolved.url}?vitest=${Date.now()}`;
return resolved;
});
if (isVitest) result.url = `${result.url}?vitest=${Date.now()}`;
ensureModuleGraphEntry(result.url, context.parentURL);
return result;
};
function ensureModuleGraphEntry(url, parentURL) {
if (url.includes("/node_modules/")) return;
port.postMessage({
event: "register-module-graph-entry",
url,
parentURL
});
}
export { initialize, resolve };
+7
View File
@@ -0,0 +1,7 @@
import { resolve } from 'node:path';
import url from 'node:url';
const rootDir = resolve(url.fileURLToPath(import.meta.url), "../../");
const distDir = resolve(url.fileURLToPath(import.meta.url), "../../dist");
export { distDir, rootDir };
+43
View File
@@ -0,0 +1,43 @@
import { E as Environment } from './chunks/environment.d.C6xYahWA.js';
export { a as EnvironmentReturn, V as VmEnvironmentReturn } from './chunks/environment.d.C6xYahWA.js';
import { br as SnapshotEnvironment, bs as SnapshotEnvironmentOptions } from './chunks/config.d.CU_b-wJj.js';
export { b as SerializedConfig, V as VitestRunner } from './chunks/config.d.CU_b-wJj.js';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
declare const environments: {
"node": Environment;
"jsdom": Environment;
"happy-dom": Environment;
"edge-runtime": Environment;
};
interface PopulateOptions {
bindFunctions?: boolean;
additionalKeys?: string[];
}
declare function populateGlobal(global: any, win: any, options?: PopulateOptions): {
keys: Set<string>;
skipKeys: string[];
originals: Map<string | symbol, PropertyDescriptor>;
};
declare class NodeSnapshotEnvironment implements SnapshotEnvironment {
private options;
constructor(options?: SnapshotEnvironmentOptions);
getVersion(): string;
getHeader(): string;
resolveRawPath(testPath: string, rawPath: string): Promise<string>;
resolvePath(filepath: string): Promise<string>;
prepareDirectory(dirPath: string): Promise<void>;
saveSnapshotFile(filepath: string, snapshot: string): Promise<void>;
readSnapshotFile(filepath: string): Promise<string | null>;
removeSnapshotFile(filepath: string): Promise<void>;
}
declare class VitestNodeSnapshotEnvironment extends NodeSnapshotEnvironment {
getHeader(): string;
resolvePath(filepath: string): Promise<string>;
}
export { Environment, SnapshotEnvironment, VitestNodeSnapshotEnvironment as VitestSnapshotEnvironment, environments as builtinEnvironments, populateGlobal };
+32
View File
@@ -0,0 +1,32 @@
import { VitestModuleEvaluator } from './module-evaluator.js';
import { V as VITEST_VM_CONTEXT_SYMBOL, s as startVitestModuleRunner, a as VitestModuleRunner } from './chunks/index.1_nbEjJY.js';
export { e as builtinEnvironments, p as populateGlobal } from './chunks/index.1_nbEjJY.js';
import { g as getWorkerState } from './chunks/utils.DYj33du9.js';
export { VitestNodeSnapshotEnvironment as VitestSnapshotEnvironment } from './chunks/node.Es9ukMGX.js';
import 'node:module';
import 'node:url';
import 'node:vm';
import 'vite/module-runner';
import './traces.js';
import 'node:fs';
import './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import './chunks/modules.BJuCwlRJ.js';
import './path.js';
import 'node:path';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
/**
* @internal
*/
const __INTERNAL = {
VitestModuleEvaluator,
VitestModuleRunner,
startVitestModuleRunner,
VITEST_VM_CONTEXT_SYMBOL,
getWorkerState
};
// #endregion
export { __INTERNAL };
+1
View File
@@ -0,0 +1 @@
export { c as clearAllMocks, d as createMockInstance, f as fn, i as isMockFunction, b as resetAllMocks, r as restoreAllMocks, a as spyOn } from './chunks/spy.DPLvp2J7.js';
+281
View File
@@ -0,0 +1,281 @@
import { t as toArray, a as relative } from './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
/* @__NO_SIDE_EFFECTS__ */
function isTestCase(s) {
return s.type === "test";
}
/* @__NO_SIDE_EFFECTS__ */
function getTests(suite) {
const tests = [];
const arraySuites = toArray(suite);
for (const s of arraySuites) if (/* @__PURE__ */ isTestCase(s)) tests.push(s);
else for (const task of s.tasks) if (/* @__PURE__ */ isTestCase(task)) tests.push(task);
else {
const taskTests = /* @__PURE__ */ getTests(task);
for (const test of taskTests) tests.push(test);
}
return tests;
}
/* @__NO_SIDE_EFFECTS__ */
function getTasks(tasks = []) {
return toArray(tasks).flatMap((s) => /* @__PURE__ */ isTestCase(s) ? [s] : [s, .../* @__PURE__ */ getTasks(s.tasks)]);
}
/* @__NO_SIDE_EFFECTS__ */
function getSuites(suite) {
return toArray(suite).flatMap((s) => s.type === "suite" ? [s, .../* @__PURE__ */ getSuites(s.tasks)] : []);
}
/* @__NO_SIDE_EFFECTS__ */
function hasFailed(suite) {
return toArray(suite).some((s) => s.result?.state === "fail" || s.type === "suite" && /* @__PURE__ */ hasFailed(s.tasks));
}
/* @__NO_SIDE_EFFECTS__ */
function getNames(task) {
const names = [task.name];
let current = task;
while (current?.suite) {
current = current.suite;
if (current?.name) names.unshift(current.name);
}
if (current !== task.file) names.unshift(task.file.name);
return names;
}
/* @__NO_SIDE_EFFECTS__ */
function getFullName(task, separator = " > ") {
return (/* @__PURE__ */ getNames(task)).join(separator);
}
/* @__NO_SIDE_EFFECTS__ */
function getTestName(task, separator = " > ") {
return (/* @__PURE__ */ getNames(task)).slice(1).join(separator);
}
/* @__NO_SIDE_EFFECTS__ */
function createTaskName(names, separator = " > ") {
return names.filter((name) => name !== void 0).join(separator);
}
/* @__NO_SIDE_EFFECTS__ */
function hasFailedSnapshot(suite) {
return (/* @__PURE__ */ getTests(suite)).some((s) => {
return s.result?.errors?.some((e) => typeof e?.message === "string" && e.message.match(/Snapshot .* mismatched/));
});
}
/* @__NO_SIDE_EFFECTS__ */
function convertTasksToEvents(file, onTask) {
const packs = [];
const events = [];
function visit(suite) {
onTask?.(suite);
packs.push([
suite.id,
suite.result,
suite.meta
]);
events.push([
suite.id,
"suite-prepare",
void 0
]);
suite.tasks.forEach((task) => {
if (task.type === "suite") visit(task);
else {
onTask?.(task);
if (suite.mode !== "skip" && suite.mode !== "todo") {
packs.push([
task.id,
task.result,
task.meta
]);
events.push([
task.id,
"test-prepare",
void 0
]);
task.annotations.forEach((annotation) => {
events.push([
task.id,
"test-annotation",
{ annotation }
]);
});
task.artifacts.forEach((artifact) => {
events.push([
task.id,
"test-artifact",
{ artifact }
]);
});
events.push([
task.id,
"test-finished",
void 0
]);
}
}
});
events.push([
suite.id,
"suite-finished",
void 0
]);
}
visit(file);
return {
packs,
events
};
}
/* @__NO_SIDE_EFFECTS__ */
function createFileTask(filepath, root, projectName, pool, viteEnvironment, meta) {
const path = relative(root, filepath);
// this can be called outside of the test run, so worker might not be there
// @ts-expect-error injected global
const workerState = globalThis.__vitest_worker__;
const file = {
id: /* @__PURE__ */ generateFileHash(path, projectName, meta),
name: path,
fullName: path,
type: "suite",
mode: "queued",
filepath,
tasks: [],
meta: Object.assign(Object.create(null), meta),
projectName,
file: void 0,
pool,
viteEnvironment,
concurrencyId: workerState?.ctx.concurrencyId ?? 0,
workerId: workerState?.ctx.workerId ?? 0
};
file.file = file;
return file;
}
/**
* Generate a unique ID for a file based on its path and project name
* @param file File relative to the root of the project to keep ID the same between different machines
* @param projectName The name of the test project
*/
/* @__NO_SIDE_EFFECTS__ */
function generateFileHash(file, projectName, meta) {
return /* @__PURE__ */ generateHash([
file,
projectName || "",
meta?.typecheck ? "__typecheck__" : "",
meta?.__vitest_label__ || ""
].join("\0"));
}
/* @__NO_SIDE_EFFECTS__ */
function generateHash(str) {
let hash = 0;
if (str.length === 0) return `${hash}`;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return `${hash}`;
}
function calculateSuiteHash(parent) {
parent.tasks.forEach((t, idx) => {
t.id = `${parent.id}_${idx}`;
if (t.type === "suite") calculateSuiteHash(t);
});
}
/**
* If any tasks been marked as `only`, mark all other tasks as `skip`.
*/
function interpretTaskModes(file, namePattern, testLocations, testIds, testTagsFilter, onlyMode, parentIsOnly, allowOnly) {
const matchedLocations = [];
const testLocationsSet = testLocations !== void 0 && testLocations.length !== 0 ? new Set(testLocations) : void 0;
const testIdsSet = testIds ? new Set(testIds) : void 0;
const traverseSuite = (suite, parentIsOnly, parentMatchedWithLocation) => {
const suiteIsOnly = parentIsOnly || suite.mode === "only";
// Check if any tasks in this suite have `.only` - if so, only those should run.
// `containsOnly` is computed during collection (in the runtime/AST collectors).
const hasSomeTasksOnly = !!(onlyMode && suite.containsOnly);
suite.tasks.forEach((t) => {
// Check if either the parent suite or the task itself are marked as included
// If there are tasks with `.only` in this suite, only include those (not all tasks from describe.only)
const includeTask = hasSomeTasksOnly ? t.mode === "only" || t.type === "suite" && !!t.containsOnly : suiteIsOnly || t.mode === "only";
if (onlyMode) {
if (t.type === "suite" && (includeTask || t.containsOnly)) {
// Don't skip this suite
if (t.mode === "only") {
checkAllowOnly(t, allowOnly);
t.mode = "run";
}
} else if (t.mode === "run" && !includeTask) t.mode = "skip";
else if (t.mode === "only") {
checkAllowOnly(t, allowOnly);
t.mode = "run";
}
}
let hasLocationMatch = parentMatchedWithLocation;
// Match test location against provided locations, only run if present
// in `testLocations`. Note: if `includeTaskLocation` is not enabled,
// all test will be skipped.
if (testLocationsSet !== void 0) {
if (t.location && testLocationsSet.has(t.location.line)) {
t.mode = "run";
matchedLocations.push(t.location.line);
hasLocationMatch = true;
} else if (parentMatchedWithLocation) t.mode = "run";
else if (t.type === "test") t.mode = "skip";
}
if (t.type === "test") {
if (namePattern && !t.fullTestName.match(namePattern)) t.mode = "skip";
if (testIdsSet && !testIdsSet.has(t.id)) t.mode = "skip";
if (testTagsFilter && !testTagsFilter(t.tags || [])) t.mode = "skip";
} else if (t.type === "suite") if (t.mode === "skip") skipAllTasks(t);
else if (t.mode === "todo") todoAllTasks(t);
else traverseSuite(t, includeTask, hasLocationMatch);
});
// if all subtasks are skipped, mark as skip
if (suite.mode === "run" || suite.mode === "queued") {
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run" && i.mode !== "queued")) suite.mode = "skip";
}
};
traverseSuite(file, parentIsOnly, false);
const nonMatching = testLocations?.filter((loc) => !matchedLocations.includes(loc));
if (nonMatching && nonMatching.length !== 0) {
const message = nonMatching.length === 1 ? `line ${nonMatching[0]}` : `lines ${nonMatching.join(", ")}`;
if (file.result === void 0) file.result = {
state: "fail",
errors: []
};
if (file.result.errors === void 0) file.result.errors = [];
const error = /* @__PURE__ */ new Error(`No test found in ${file.name} in ${message}`);
file.result.errors.push({
name: error.name,
message: error.message,
stack: error.stack
});
}
}
function skipAllTasks(suite) {
suite.tasks.forEach((t) => {
if (t.mode === "run" || t.mode === "queued") {
t.mode = "skip";
if (t.type === "suite") skipAllTasks(t);
}
});
}
function todoAllTasks(suite) {
suite.tasks.forEach((t) => {
if (t.mode === "run" || t.mode === "queued") {
t.mode = "todo";
if (t.type === "suite") todoAllTasks(t);
}
});
}
function checkAllowOnly(task, allowOnly) {
if (allowOnly) return;
const error = /* @__PURE__ */ new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error");
task.result = {
state: "fail",
errors: [{
name: error.name,
message: error.message,
stack: error.stack
}]
};
}
export { calculateSuiteHash, convertTasksToEvents, createFileTask, createTaskName, generateFileHash, generateHash, getFullName, getNames, getSuites, getTasks, getTestName, getTests, hasFailed, hasFailedSnapshot, interpretTaskModes, isTestCase };
+218
View File
@@ -0,0 +1,218 @@
class Traces {
/**
* otel stands for OpenTelemetry
*/
#otel = null;
#sdk = null;
#init = null;
#noopSpan = createNoopSpan();
#noopContext = createNoopContext();
#initStartTime = performance.now();
#initEndTime = 0;
#initRecorded = false;
constructor(options) {
if (options.enabled) {
const apiInit = import('@opentelemetry/api').then((api) => {
const otel = {
tracer: api.trace.getTracer(options.tracerName || "vitest"),
context: api.context,
propagation: api.propagation,
trace: api.trace,
SpanKind: api.SpanKind,
SpanStatusCode: api.SpanStatusCode
};
this.#otel = otel;
}).catch(() => {
throw new Error(`"@opentelemetry/api" is not installed locally. Make sure you have setup OpenTelemetry instrumentation: https://vitest.dev/guide/open-telemetry`);
});
const sdkInit = (options.sdkPath ? import(
/* @vite-ignore */
options.sdkPath
) : Promise.resolve()).catch((cause) => {
throw new Error(`Failed to import custom OpenTelemetry SDK script (${options.sdkPath}): ${cause.message}`);
});
this.#init = Promise.all([sdkInit, apiInit]).then(([sdk]) => {
if (sdk != null) {
if (sdk.default != null && typeof sdk.default === "object" && typeof sdk.default.shutdown === "function") this.#sdk = sdk.default;
else if (options.watchMode !== true && process.env.VITEST_MODE !== "watch") console.warn(`OpenTelemetry instrumentation module (${options.sdkPath}) does not have a default export with a "shutdown" method. Vitest won't be able to ensure that all traces are processed in time. Try running Vitest in watch mode instead.`);
}
}).finally(() => {
this.#initEndTime = performance.now();
this.#init = null;
});
}
}
isEnabled() {
return !!this.#otel;
}
/**
* @internal
*/
async waitInit() {
if (this.#init) await this.#init;
return this;
}
/**
* @internal
*/
recordInitSpan(context) {
if (this.#initRecorded) return;
this.#initRecorded = true;
this.startSpan("vitest.runtime.traces", { startTime: this.#initStartTime }, context).end(this.#initEndTime);
}
/**
* @internal
*/
startContextSpan(name, currentContext) {
if (!this.#otel) return {
span: this.#noopSpan,
context: this.#noopContext
};
const activeContext = currentContext || this.#otel.context.active();
const span = this.#otel.tracer.startSpan(name, {}, activeContext);
return {
span,
context: this.#otel.trace.setSpan(activeContext, span)
};
}
/**
* @internal
*/
getContextFromCarrier(carrier) {
if (!this.#otel) return this.#noopContext;
const activeContext = this.#otel.context.active();
if (!carrier) return activeContext;
return this.#otel.propagation.extract(activeContext, carrier);
}
/**
* @internal
*/
getContextFromEnv(env) {
if (!this.#otel) return this.#noopContext;
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/env-carriers.md
// some tools sets only `TRACEPARENT` but not `TRACESTATE`
const carrier = {};
if (typeof env.TRACEPARENT === "string") carrier.traceparent = env.TRACEPARENT;
if (typeof env.TRACESTATE === "string") carrier.tracestate = env.TRACESTATE;
return this.getContextFromCarrier(carrier);
}
/**
* @internal
*/
getContextCarrier(context) {
if (!this.#otel) return;
const carrier = {};
this.#otel.propagation.inject(context || this.#otel.context.active(), carrier);
return carrier;
}
#callActiveSpan(span, callback) {
const otel = this.#otel;
let result;
try {
result = callback(span);
if (result instanceof Promise) return result.catch((error) => {
span.recordException({
name: error.name,
message: error.message,
stack: error.stack
});
span.setStatus({ code: otel.SpanStatusCode.ERROR });
throw error;
}).finally(() => span.end());
return result;
} catch (error) {
if (error instanceof Error) {
span.recordException({
name: error.name,
message: error.message,
stack: error.stack
});
span.setStatus({ code: otel.SpanStatusCode.ERROR });
}
throw error;
} finally {
// end sync callback
if (!(result instanceof Promise)) span.end();
}
}
/**
* @internal
*/
$(name, optionsOrFn, fn) {
const callback = typeof optionsOrFn === "function" ? optionsOrFn : fn;
if (!this.#otel) return callback(this.#noopSpan);
const otel = this.#otel;
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
const context = options.context;
if (context) return otel.tracer.startActiveSpan(name, options, context, (span) => this.#callActiveSpan(span, callback));
return otel.tracer.startActiveSpan(name, options, (span) => this.#callActiveSpan(span, callback));
}
/**
* @internal
*/
startSpan(name, options, context) {
if (!this.#otel) return this.#noopSpan;
const { tracer } = this.#otel;
return tracer.startSpan(name, options, context);
}
// On browser mode, async context is not automatically propagated,
// so we manually bind the `$` calls to the provided context.
// TODO: this doesn't bind to user land's `@optelemetry/api` calls
/**
* @internal
*/
bind(context) {
if (!this.#otel) return;
const original = this.$.__original ?? this.$;
this.$ = this.#otel.context.bind(context, original);
this.$.__original = original;
}
/**
* @internal
*/
async finish() {
await this.#sdk?.shutdown();
}
/**
* @internal
*/
async flush() {
await this.#sdk?.forceFlush?.();
}
}
function noopSpan() {
return this;
}
function createNoopSpan() {
return {
setAttribute: noopSpan,
setStatus: noopSpan,
addEvent: noopSpan,
addLink: noopSpan,
addLinks: noopSpan,
setAttributes: noopSpan,
updateName: noopSpan,
end: () => {},
isRecording: () => false,
recordException: noopSpan,
spanContext() {
return {
spanId: "",
traceFlags: 0,
traceId: ""
};
}
};
}
function noopContext() {
return this;
}
function createNoopContext() {
return {
getValue: noopContext,
setValue: noopContext,
deleteValue: noopContext
};
}
export { Traces };
+30
View File
@@ -0,0 +1,30 @@
import { d as WorkerSetupContext, e as BirpcOptions } from './chunks/worker.d.MLmnzOJE.js';
import { WorkerGlobalState } from './index.js';
import { T as Traces } from './chunks/rpc.d.DA9Utv4e.js';
import { RuntimeRPC } from './index.js';
import { A as Awaitable } from './chunks/config.d.CU_b-wJj.js';
import { ModuleRunner } from 'vite/module-runner';
import './chunks/environment.d.C6xYahWA.js';
import 'vitest/optional-runtime-types.js';
import 'tinybench';
/** @experimental */
declare function setupBaseEnvironment(context: WorkerSetupContext): Promise<() => Promise<void>>;
/** @experimental */
declare function runBaseTests(method: "run" | "collect", state: WorkerGlobalState, traces: Traces): Promise<void>;
type WorkerRpcOptions = Pick<BirpcOptions<RuntimeRPC>, "on" | "off" | "post" | "serialize" | "deserialize">;
interface VitestWorker extends WorkerRpcOptions {
runTests: (state: WorkerGlobalState, traces: Traces) => Awaitable<unknown>;
collectTests: (state: WorkerGlobalState, traces: Traces) => Awaitable<unknown>;
onModuleRunner?: (moduleRunner: ModuleRunner) => Awaitable<unknown>;
setup?: (context: WorkerSetupContext) => void | Promise<() => Promise<unknown>>;
}
interface Options extends VitestWorker {
teardown?: () => void;
}
/** @experimental */
declare function init(worker: Options): void;
export { init, runBaseTests, setupBaseEnvironment as setupEnvironment };
+43
View File
@@ -0,0 +1,43 @@
export { r as runBaseTests, s as setupEnvironment } from './chunks/base.BTS5G301.js';
export { i as init } from './chunks/init.BS957yFf.js';
import 'node:vm';
import './chunks/spy.DPLvp2J7.js';
import './chunks/index.5J3Pr46F.js';
import 'chai';
import 'node:async_hooks';
import './chunks/coverage.BtDxDqSU.js';
import './chunks/coverage.BcXK1DsQ.js';
import './chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import './chunks/rpc.DWKX1ZbI.js';
import 'vite/module-runner';
import './chunks/source-map.Dx3YPjIe.js';
import './chunks/index.DmDMHCg8.js';
import './chunks/utils.DYj33du9.js';
import './chunks/setup-common.D5-2VgqG.js';
import './chunks/plugins.Cigb0uSy.js';
import './chunks/index.M2dsQ_UQ.js';
import './chunks/tinyrainbow.Ht9iggcq.js';
import './chunks/index.OVGXnVRj.js';
import './chunks/run.CQOUYP-x.js';
import './chunks/display.pkpxlVcY.js';
import './task-utils.js';
import './chunks/offset.Dy-5Fdfn.js';
import 'tinybench';
import 'expect-type';
import './traces.js';
import './chunks/index.1_nbEjJY.js';
import 'node:fs';
import './chunks/modules.BJuCwlRJ.js';
import 'node:module';
import 'node:url';
import './path.js';
import 'node:path';
import './module-evaluator.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
import 'node:perf_hooks';
import './chunks/inspector.CvyFGlXm.js';
import 'node:timers';
import 'node:timers/promises';
import 'node:util';
+49
View File
@@ -0,0 +1,49 @@
import { r as runBaseTests, s as setupBaseEnvironment } from '../chunks/base.BTS5G301.js';
import { w as workerInit } from '../chunks/init-forks.CiCtIMPj.js';
import 'node:vm';
import '../chunks/spy.DPLvp2J7.js';
import '../chunks/index.5J3Pr46F.js';
import 'chai';
import 'node:async_hooks';
import '../chunks/coverage.BtDxDqSU.js';
import '../chunks/coverage.BcXK1DsQ.js';
import '../chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import '../chunks/rpc.DWKX1ZbI.js';
import 'vite/module-runner';
import '../chunks/source-map.Dx3YPjIe.js';
import '../chunks/index.DmDMHCg8.js';
import '../chunks/utils.DYj33du9.js';
import '../chunks/setup-common.D5-2VgqG.js';
import '../chunks/plugins.Cigb0uSy.js';
import '../chunks/index.M2dsQ_UQ.js';
import '../chunks/tinyrainbow.Ht9iggcq.js';
import '../chunks/index.OVGXnVRj.js';
import '../chunks/run.CQOUYP-x.js';
import '../chunks/display.pkpxlVcY.js';
import '../task-utils.js';
import '../chunks/offset.Dy-5Fdfn.js';
import 'tinybench';
import 'expect-type';
import '../chunks/init.BS957yFf.js';
import 'node:fs';
import 'node:module';
import 'node:url';
import '../chunks/index.1_nbEjJY.js';
import '../chunks/modules.BJuCwlRJ.js';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import '../traces.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
import '../chunks/inspector.CvyFGlXm.js';
import 'node:perf_hooks';
import 'node:timers';
import 'node:timers/promises';
import 'node:util';
workerInit({
runTests: runBaseTests,
setup: setupBaseEnvironment
});
+106
View File
@@ -0,0 +1,106 @@
import { createRequire } from 'node:module';
import { performance } from 'node:perf_hooks';
import timers from 'node:timers';
import timersPromises from 'node:timers/promises';
import util from 'node:util';
import { K as KNOWN_ASSET_TYPES } from '../chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import { s as setupChaiConfig, r as resolveTestRunner, a as resolveSnapshotEnvironment, d as detectAsyncLeaks } from '../chunks/index.5J3Pr46F.js';
import { s as startCoverageInsideWorker, a as stopCoverageInsideWorker } from '../chunks/coverage.BtDxDqSU.js';
import { i as index, g as globalExpect } from '../chunks/index.OVGXnVRj.js';
import { c as closeInspector } from '../chunks/inspector.CvyFGlXm.js';
import { s as startTests, p as publicCollect } from '../chunks/run.CQOUYP-x.js';
import { s as setupCommonEnv } from '../chunks/setup-common.D5-2VgqG.js';
import { g as getWorkerState } from '../chunks/utils.DYj33du9.js';
import 'chai';
import 'node:async_hooks';
import '../chunks/rpc.DWKX1ZbI.js';
import 'vite/module-runner';
import '../chunks/source-map.Dx3YPjIe.js';
import '../chunks/index.DmDMHCg8.js';
import '../chunks/coverage.BcXK1DsQ.js';
import '../chunks/spy.DPLvp2J7.js';
import '../chunks/display.pkpxlVcY.js';
import '../chunks/index.M2dsQ_UQ.js';
import '../chunks/tinyrainbow.Ht9iggcq.js';
import '../task-utils.js';
import '../chunks/plugins.Cigb0uSy.js';
import '../chunks/offset.Dy-5Fdfn.js';
import 'tinybench';
import 'expect-type';
import 'node:url';
async function run(method, files, config, moduleRunner, traces) {
const workerState = getWorkerState();
await traces.$("vitest.runtime.global_env", () => setupCommonEnv(config));
Object.defineProperty(globalThis, "__vitest_index__", {
value: index,
enumerable: false,
configurable: true,
writable: true
});
const viteEnvironment = workerState.environment.viteEnvironment || workerState.environment.name;
globalExpect.setState({ environment: workerState.environment.name });
if (viteEnvironment === "client") {
const _require = createRequire(import.meta.url);
// always mock "required" `css` files, because we cannot process them
_require.extensions[".css"] = resolveCss;
_require.extensions[".scss"] = resolveCss;
_require.extensions[".sass"] = resolveCss;
_require.extensions[".less"] = resolveCss;
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset;
});
process.env.SSR = "";
} else process.env.SSR = "1";
// @ts-expect-error not typed global for patched timers
globalThis.__vitest_required__ = {
util,
timers,
timersPromises
};
await traces.$("vitest.runtime.coverage.start", () => startCoverageInsideWorker(config.coverage, moduleRunner, { isolate: false }));
if (config.chaiConfig) setupChaiConfig(config.chaiConfig);
const [testRunner, snapshotEnvironment] = await Promise.all([traces.$("vitest.runtime.runner", () => resolveTestRunner(config, moduleRunner, traces)), traces.$("vitest.runtime.snapshot.environment", () => resolveSnapshotEnvironment(config, moduleRunner))]);
config.snapshotOptions.snapshotEnvironment = snapshotEnvironment;
// the callback captures this file's runner: unsubscribe once the run is
// over, or every finished file's world stays reachable from the worker's
// cancel listeners for the lifetime of the worker
const offCancel = workerState.onCancel((reason) => {
closeInspector(config);
testRunner.cancel?.(reason);
});
// unlike other pools, the vm pool creates the environment inside the
// prepare window; subtract it so `prepare` excludes the environment
// load time in every pool
workerState.durations.prepare = performance.now() - workerState.durations.prepare - workerState.durations.environment;
const { vi } = index;
try {
await traces.$(`vitest.test.runner.${method}`, async () => {
for (const file of files) {
workerState.filepath = file.filepath;
if (method === "run") {
const collectAsyncLeaks = config.detectAsyncLeaks ? detectAsyncLeaks(file.filepath, workerState.ctx.projectName) : void 0;
await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => startTests([file], testRunner));
const leaks = await collectAsyncLeaks?.();
if (leaks?.length) workerState.rpc.onAsyncLeaks(leaks);
} else await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => publicCollect([file], testRunner));
// reset after tests, because user might call `vi.setConfig` in setupFile
vi.resetConfig();
// mocks should not affect different files
vi.restoreAllMocks();
}
});
} finally {
offCancel();
}
await traces.$("vitest.runtime.coverage.stop", () => stopCoverageInsideWorker(config.coverage, moduleRunner, { isolate: false }));
}
function resolveCss(mod) {
mod.exports = "";
}
function resolveAsset(mod, url) {
mod.exports = url;
}
export { run };
+50
View File
@@ -0,0 +1,50 @@
import { s as setupBaseEnvironment, r as runBaseTests } from '../chunks/base.BTS5G301.js';
import { w as workerInit } from '../chunks/init-threads.DbGO6vUd.js';
import 'node:vm';
import '../chunks/spy.DPLvp2J7.js';
import '../chunks/index.5J3Pr46F.js';
import 'chai';
import 'node:async_hooks';
import '../chunks/coverage.BtDxDqSU.js';
import '../chunks/coverage.BcXK1DsQ.js';
import '../chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import '../chunks/rpc.DWKX1ZbI.js';
import 'vite/module-runner';
import '../chunks/source-map.Dx3YPjIe.js';
import '../chunks/index.DmDMHCg8.js';
import '../chunks/utils.DYj33du9.js';
import '../chunks/setup-common.D5-2VgqG.js';
import '../chunks/plugins.Cigb0uSy.js';
import '../chunks/index.M2dsQ_UQ.js';
import '../chunks/tinyrainbow.Ht9iggcq.js';
import '../chunks/index.OVGXnVRj.js';
import '../chunks/run.CQOUYP-x.js';
import '../chunks/display.pkpxlVcY.js';
import '../task-utils.js';
import '../chunks/offset.Dy-5Fdfn.js';
import 'tinybench';
import 'expect-type';
import '../chunks/init.BS957yFf.js';
import 'node:fs';
import 'node:module';
import 'node:url';
import '../chunks/index.1_nbEjJY.js';
import '../chunks/modules.BJuCwlRJ.js';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import '../traces.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
import '../chunks/inspector.CvyFGlXm.js';
import 'node:perf_hooks';
import 'node:timers';
import 'node:timers/promises';
import 'node:util';
import 'node:worker_threads';
workerInit({
runTests: runBaseTests,
setup: setupBaseEnvironment
});
+37
View File
@@ -0,0 +1,37 @@
import { w as workerInit } from '../chunks/init-forks.CiCtIMPj.js';
import { r as runVmTests, s as setupVmWorker } from '../chunks/vm.BQTVRi0W.js';
import '../chunks/init.BS957yFf.js';
import 'node:fs';
import 'node:module';
import 'node:url';
import 'vite/module-runner';
import '../chunks/index.1_nbEjJY.js';
import '../chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import '../chunks/modules.BJuCwlRJ.js';
import '../chunks/utils.DYj33du9.js';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import 'node:vm';
import '../traces.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
import '../chunks/source-map.Dx3YPjIe.js';
import '../chunks/index.M2dsQ_UQ.js';
import '../chunks/tinyrainbow.Ht9iggcq.js';
import '../chunks/rpc.DWKX1ZbI.js';
import '../chunks/index.DmDMHCg8.js';
import '../chunks/inspector.CvyFGlXm.js';
import 'node:v8';
import '../chunks/console.CHi0lDgQ.js';
import 'node:stream';
import '../chunks/resolver.CBCHRZ7j.js';
import 'es-module-lexer';
import '../chunks/setup-common.D5-2VgqG.js';
import '../chunks/plugins.Cigb0uSy.js';
workerInit({
runTests: runVmTests,
setup: setupVmWorker
});
+38
View File
@@ -0,0 +1,38 @@
import { w as workerInit } from '../chunks/init-threads.DbGO6vUd.js';
import { s as setupVmWorker, r as runVmTests } from '../chunks/vm.BQTVRi0W.js';
import 'node:worker_threads';
import '../chunks/init.BS957yFf.js';
import 'node:fs';
import 'node:module';
import 'node:url';
import 'vite/module-runner';
import '../chunks/index.1_nbEjJY.js';
import '../chunks/pathe.M-eThtNZ.Clq7AXhv.js';
import '../chunks/modules.BJuCwlRJ.js';
import '../chunks/utils.DYj33du9.js';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import 'node:vm';
import '../traces.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
import 'node:console';
import '../chunks/source-map.Dx3YPjIe.js';
import '../chunks/index.M2dsQ_UQ.js';
import '../chunks/tinyrainbow.Ht9iggcq.js';
import '../chunks/rpc.DWKX1ZbI.js';
import '../chunks/index.DmDMHCg8.js';
import '../chunks/inspector.CvyFGlXm.js';
import 'node:v8';
import '../chunks/console.CHi0lDgQ.js';
import 'node:stream';
import '../chunks/resolver.CBCHRZ7j.js';
import 'es-module-lexer';
import '../chunks/setup-common.D5-2VgqG.js';
import '../chunks/plugins.Cigb0uSy.js';
workerInit({
runTests: runVmTests,
setup: setupVmWorker
});
+1
View File
@@ -0,0 +1 @@
export * from './dist/environments'
+22
View File
@@ -0,0 +1,22 @@
declare global {
let suite: typeof import('vitest')['suite']
let test: typeof import('vitest')['test']
let chai: typeof import("vitest")["chai"]
let describe: typeof import('vitest')['describe']
let it: typeof import('vitest')['it']
let expectTypeOf: typeof import('vitest')['expectTypeOf']
let assertType: typeof import('vitest')['assertType']
let expect: typeof import('vitest')['expect']
let assert: typeof import('vitest')['assert']
let vitest: typeof import('vitest')['vitest']
let vi: typeof import('vitest')['vitest']
let beforeAll: typeof import('vitest')['beforeAll']
let afterAll: typeof import('vitest')['afterAll']
let beforeEach: typeof import('vitest')['beforeEach']
let afterEach: typeof import('vitest')['afterEach']
let aroundEach: typeof import('vitest')['aroundEach']
let aroundAll: typeof import('vitest')['aroundAll']
let onTestFailed: typeof import('vitest')['onTestFailed']
let onTestFinished: typeof import('vitest')['onTestFinished']
}
export {}
+5
View File
@@ -0,0 +1,5 @@
/// <reference path="./importMeta.d.ts" />
// https://github.com/microsoft/TypeScript/issues/45096
// TypeScript has a bug that makes <reference types="vite/types/importMeta" />
// not possible in userland. This file provides a workaround for now.
+4
View File
@@ -0,0 +1,4 @@
interface ImportMeta {
url: string
readonly vitest?: typeof import('vitest')
}
+5
View File
@@ -0,0 +1,5 @@
throw new Error(
'Vitest cannot be imported in a CommonJS module using require(). Please use "import" instead.'
+ '\n\nIf you are using "import" in your source code, then it\'s possible it was bundled into require() automatically by your bundler. '
+ 'In that case, do not bundle CommonJS output since it will never work with Vitest, or use dynamic import() which is available in all CommonJS modules.',
)
+1
View File
@@ -0,0 +1 @@
export * from './dist/index.js'
+6
View File
@@ -0,0 +1,6 @@
import type { JSDOM } from 'jsdom'
declare global {
const jsdom: JSDOM
}
export {}
+1
View File
@@ -0,0 +1 @@
export * from './dist/mocker.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/node.js'
+6
View File
@@ -0,0 +1,6 @@
/* eslint-disable ts/ban-ts-comment */
export interface Disposable {
// @ts-ignore -- Symbol.dispose might not be in user types
[Symbol.dispose]: () => void
}
+7
View File
@@ -0,0 +1,7 @@
/* eslint-disable ts/ban-ts-comment */
// @ts-ignore optional peer dep
export type * as jsdomTypes from 'jsdom'
// @ts-ignore optional peer dep
export type * as happyDomTypes from 'happy-dom'
+218
View File
@@ -0,0 +1,218 @@
{
"name": "vitest",
"type": "module",
"version": "5.0.0",
"description": "Next generation testing framework powered by Vite",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://vitest.dev",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/vitest"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vite",
"vitest",
"test",
"jest"
],
"sideEffects": false,
"imports": {
"#module-evaluator": {
"__vitest_source__": "./src/runtime/moduleRunner/moduleEvaluator.ts",
"types": "./dist/module-evaluator.d.ts",
"default": "./dist/module-evaluator.js"
},
"#nodejs-worker-loader": "./dist/nodejs-worker-loader.js"
},
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./index.d.cts",
"default": "./index.cjs"
}
},
"./browser": {
"types": "./browser/context.d.ts",
"default": "./browser/context.js"
},
"./package.json": "./package.json",
"./optional-types.js": {
"types": "./optional-types.d.ts"
},
"./optional-runtime-types.js": {
"types": "./optional-runtime-types.d.ts"
},
"./globals": {
"types": "./globals.d.ts"
},
"./jsdom": {
"types": "./jsdom.d.ts"
},
"./importMeta": {
"types": "./importMeta.d.ts"
},
"./import-meta": {
"types": "./import-meta.d.ts"
},
"./node": {
"types": "./dist/node.d.ts",
"default": "./dist/node.js"
},
"./internal/browser": {
"__vitest_source__": "./src/public/browser.ts",
"types": "./dist/browser.d.ts",
"default": "./dist/browser.js"
},
"./internal/traces": {
"__vitest_source__": "./src/utils/traces.ts",
"default": "./dist/traces.js"
},
"./config": {
"types": "./config.d.ts",
"require": "./dist/config.cjs",
"default": "./dist/config.js"
},
"./runtime": {
"types": "./dist/runtime.d.ts",
"default": "./dist/runtime.js"
},
"./worker": {
"types": "./worker.d.ts",
"default": "./dist/worker.js"
}
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"vitest": "./vitest.mjs"
},
"files": [
"*.cjs",
"*.d.cts",
"*.d.ts",
"*.mjs",
"bin",
"browser",
"dist"
],
"engines": {
"node": "^22.12.0 || ^24.0.0 || >=26.0.0"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "5.0.0",
"@vitest/browser-preview": "5.0.0",
"@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0",
"@vitest/coverage-istanbul": "5.0.0",
"@vitest/coverage-v8": "5.0.0",
"@vitest/ui": "5.0.0",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.4.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
"@opentelemetry/api": {
"optional": true
},
"@types/node": {
"optional": true
},
"@vitest/browser-playwright": {
"optional": true
},
"@vitest/browser-preview": {
"optional": true
},
"@vitest/browser-webdriverio": {
"optional": true
},
"@vitest/coverage-istanbul": {
"optional": true
},
"@vitest/coverage-v8": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
},
"vite": {
"optional": false
}
},
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/mocker": "5.0.0",
"chai": "^6.2.2",
"es-module-lexer": "^2.3.2",
"expect-type": "^1.4.0",
"magic-string": "^1.2.3",
"obug": "^2.1.4",
"picomatch": "^4.0.7",
"std-env": "^4.2.0",
"tinybench": "6.1.4",
"tinyexec": "1.3.0",
"tinyglobby": "^0.2.17",
"why-is-node-running": "^2.3.0"
},
"devDependencies": {
"@antfu/install-pkg": "^2.0.1",
"@bomb.sh/tab": "^0.0.22",
"@edge-runtime/vm": "^5.0.0",
"@jridgewell/trace-mapping": "0.3.31",
"@opentelemetry/api": "^1.9.1",
"@sinonjs/fake-timers": "15.4.0",
"@types/jsdom": "^28.0.3",
"@types/node": "^24.13.3",
"@types/picomatch": "^4.0.3",
"@types/prompts": "^2.4.9",
"@vitest/expect": "5.0.0",
"@vitest/istanbul-lib-coverage": "^1.0.0",
"@vitest/istanbul-lib-report": "^1.0.0",
"@vitest/pretty-format": "5.0.0",
"@vitest/snapshot": "5.0.0",
"@vitest/spy": "5.0.0",
"@vitest/utils": "5.0.0",
"acorn": "8.11.3",
"acorn-walk": "^8.3.5",
"birpc": "^4.2.0",
"cac": "^6.7.14",
"flatted": "^3.4.4",
"happy-dom": "^20.12.0",
"jsdom": "^29.1.1",
"local-pkg": "^1.2.1",
"mime": "^4.1.0",
"pathe": "^2.0.3",
"prompts": "^2.4.2",
"strip-literal": "^3.1.0",
"tinyhighlight": "^0.3.2",
"tinyrainbow": "^3.1.1",
"vite": "^6.4.0 || ^7.0.0 || ^8.0.0",
"ws": "^8.21.3"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "NODE_OPTIONS=\"--max-old-space-size=8192\" rollup -c --watch -m inline"
}
}
+1
View File
@@ -0,0 +1 @@
export * from './dist/reporters.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/runners.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/snapshot.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/suite.js'
Loaded 100 of 8152 files, more files were not shown because too many files have changed in this diff. Show more