//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
// |  |  |__   |  |  | | | |  version 3.12.0
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#include "doctest_compatibility.h"

#include <nlohmann/json.hpp>
using nlohmann::json;

TEST_CASE("element access 1")
{
    SECTION("array")
    {
        json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
        const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization)

        SECTION("access specified element with bounds checking")
        {
            SECTION("access within bounds")
            {
                CHECK(j.at(0) == json(1));
                CHECK(j.at(1) == json(1u));
                CHECK(j.at(2) == json(true));
                CHECK(j.at(3) == json(nullptr));
                CHECK(j.at(4) == json("string"));
                CHECK(j.at(5) == json(42.23));
                CHECK(j.at(6) == json::object());
                CHECK(j.at(7) == json({1, 2, 3}));

                CHECK(j_const.at(0) == json(1));
                CHECK(j_const.at(1) == json(1u));
                CHECK(j_const.at(2) == json(true));
                CHECK(j_const.at(3) == json(nullptr));
                CHECK(j_const.at(4) == json("string"));
                CHECK(j_const.at(5) == json(42.23));
                CHECK(j_const.at(6) == json::object());
                CHECK(j_const.at(7) == json({1, 2, 3}));
            }

            SECTION("access outside bounds")
            {
                CHECK_THROWS_WITH_AS(j.at(8),
                                     "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
                CHECK_THROWS_WITH_AS(j_const.at(8),
                                     "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
            }

            SECTION("access on non-array type")
            {
                SECTION("null")
                {
                    json j_nonarray(json::value_t::null);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
                }

                SECTION("boolean")
                {
                    json j_nonarray(json::value_t::boolean);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
                }

                SECTION("string")
                {
                    json j_nonarray(json::value_t::string);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
                }

                SECTION("object")
                {
                    json j_nonarray(json::value_t::object);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
                }

                SECTION("number (integer)")
                {
                    json j_nonarray(json::value_t::number_integer);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                }

                SECTION("number (unsigned)")
                {
                    json j_nonarray(json::value_t::number_unsigned);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                }

                SECTION("number (floating-point)")
                {
                    json j_nonarray(json::value_t::number_float);
                    const json j_nonarray_const(j_nonarray);  // NOLINT(performance-unnecessary-copy-initialization)

                    CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
                }
            }
        }

        SECTION("front and back")
        {
            CHECK(j.front() == json(1));
            CHECK(j_const.front() == json(1));
            CHECK(j.back() == json({1, 2, 3}));
            CHECK(j_const.back() == json({1, 2, 3}));
        }

        SECTION("access specified element")
        {
            SECTION("access within bounds")
            {
                CHECK(j[0] == json(1));
                CHECK(j[1] == json(1u));
                CHECK(j[2] == json(true));
                CHECK(j[3] == json(nullptr));
                CHECK(j[4] == json("string"));
                CHECK(j[5] == json(42.23));
                CHECK(j[6] == json::object());
                CHECK(j[7] == json({1, 2, 3}));

                CHECK(j_const[0] == json(1));
                CHECK(j_const[1] == json(1u));
                CHECK(j_const[2] == json(true));
                CHECK(j_const[3] == json(nullptr));
                CHECK(j_const[4] == json("string"));
                CHECK(j_const[5] == json(42.23));
                CHECK(j_const[6] == json::object());
                CHECK(j_const[7] == json({1, 2, 3}));
            }

            SECTION("access on non-array type")
            {
                SECTION("null")
                {
                    SECTION("standard tests")
                    {
                        json j_nonarray(json::value_t::null);
                        const json j_nonarray_const(j_nonarray);  // NOLINT(performance-unnecessary-copy-initialization)
                        CHECK_NOTHROW(j_nonarray[0]);
                        CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null", json::type_error&);
                    }

                    SECTION("implicit transformation to properly filled array")
                    {
                        json j_nonarray;
                        j_nonarray[3] = 42;
                        CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42}));
                    }
                }

                SECTION("boolean")
                {
                    json j_nonarray(json::value_t::boolean);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
                }

                SECTION("string")
                {
                    json j_nonarray(json::value_t::string);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
                }

                SECTION("object")
                {
                    json j_nonarray(json::value_t::object);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
                }

                SECTION("number (integer)")
                {
                    json j_nonarray(json::value_t::number_integer);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                }

                SECTION("number (unsigned)")
                {
                    json j_nonarray(json::value_t::number_unsigned);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                }

                SECTION("number (floating-point)")
                {
                    json j_nonarray(json::value_t::number_float);
                    const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization)
                    CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
                }
            }
        }

        SECTION("remove specified element")
        {
            SECTION("remove element by index")
            {
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(0);
                    CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(1);
                    CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(2);
                    CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(3);
                    CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(4);
                    CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(5);
                    CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(6);
                    CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    jarray.erase(7);
                    CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()}));
                }
                {
                    json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                    CHECK_THROWS_WITH_AS(jarray.erase(8), "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
                }
            }

            SECTION("remove element by iterator")
            {
                SECTION("erase(begin())")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::iterator const it2 = jarray.erase(jarray.begin());
                        CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(1u));
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::const_iterator const it2 = jarray.erase(jarray.cbegin());
                        CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(1u));
                    }
                }

                SECTION("erase(begin(), end())")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        const json::iterator it2 = jarray.erase(jarray.begin(), jarray.end());
                        CHECK(jarray == json::array());
                        CHECK(it2 == jarray.end());
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        const json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend());
                        CHECK(jarray == json::array());
                        CHECK(it2 == jarray.cend());
                    }
                }

                SECTION("erase(begin(), begin())")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::iterator const it2 = jarray.erase(jarray.begin(), jarray.begin());
                        CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(1));
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::const_iterator const it2 = jarray.erase(jarray.cbegin(), jarray.cbegin());
                        CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(1));
                    }
                }

                SECTION("erase at offset")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::iterator const it = jarray.begin() + 4;
                        json::iterator const it2 = jarray.erase(it);
                        CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(42.23));
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::const_iterator const it = jarray.cbegin() + 4;
                        json::const_iterator const it2 = jarray.erase(it);
                        CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json(42.23));
                    }
                }

                SECTION("erase subrange")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::iterator const it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6);
                        CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json::object());
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json::const_iterator const it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6);
                        CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
                        CHECK(*it2 == json::object());
                    }
                }

                SECTION("different arrays")
                {
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json jarray2 = {"foo", "bar"};

                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin()),
                                             "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray.begin(), jarray2.end()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray.end()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray2.end()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                    }
                    {
                        json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                        json const jarray2 = {"foo", "bar"};

                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin()),
                                             "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray.cbegin(), jarray2.cend()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray.cend()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                        CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()),
                                             "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
                    }
                }
            }

            SECTION("remove element by index in non-array type")
            {
                SECTION("null")
                {
                    json j_nonobject(json::value_t::null);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
                }

                SECTION("boolean")
                {
                    json j_nonobject(json::value_t::boolean);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&);
                }

                SECTION("string")
                {
                    json j_nonobject(json::value_t::string);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&);
                }

                SECTION("object")
                {
                    json j_nonobject(json::value_t::object);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with object", json::type_error&);
                }

                SECTION("number (integer)")
                {
                    json j_nonobject(json::value_t::number_integer);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
                }

                SECTION("number (unsigned)")
                {
                    json j_nonobject(json::value_t::number_unsigned);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
                }

                SECTION("number (floating-point)")
                {
                    json j_nonobject(json::value_t::number_float);
                    CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
                }
            }
        }
    }

    SECTION("other values")
    {
        SECTION("front and back")
        {
            SECTION("null")
            {
                {
                    json j;
                    CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
                }
                {
                    const json j{};
                    CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
                }
            }

            SECTION("string")
            {
                {
                    json j = "foo";
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
                {
                    const json j = "bar";
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
            }

            SECTION("number (boolean)")
            {
                {
                    json j = false;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
                {
                    const json j = true;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
            }

            SECTION("number (integer)")
            {
                {
                    json j = 17;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
                {
                    const json j = 17;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
            }

            SECTION("number (unsigned)")
            {
                {
                    json j = 17u;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
                {
                    const json j = 17u;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
            }

            SECTION("number (floating point)")
            {
                {
                    json j = 23.42;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
                {
                    const json j = 23.42;
                    CHECK(j.front() == j);
                    CHECK(j.back() == j);
                }
            }
        }

        SECTION("erase with one valid iterator")
        {
            SECTION("null")
            {
                {
                    json j;
                    CHECK_THROWS_WITH_AS(j.erase(j.begin()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
                }
                {
                    json j;
                    CHECK_THROWS_WITH_AS(j.erase(j.begin()),
                                         "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
                }
            }

            SECTION("string")
            {
                {
                    json j = "foo";
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = "bar";
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (boolean)")
            {
                {
                    json j = false;
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = true;
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (integer)")
            {
                {
                    json j = 17;
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 17;
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (unsigned)")
            {
                {
                    json j = 17u;
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 17u;
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (floating point)")
            {
                {
                    json j = 23.42;
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 23.42;
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("binary")
            {
                {
                    json j = json::binary({1, 2, 3});
                    const json::iterator it = j.erase(j.begin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = json::binary({1, 2, 3});
                    const json::const_iterator it = j.erase(j.cbegin());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }
        }

        SECTION("erase with one invalid iterator")
        {
            SECTION("string")
            {
                {
                    json j = "foo";
                    CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
                {
                    json j = "bar";
                    CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (boolean)")
            {
                {
                    json j = false;
                    CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
                {
                    json j = true;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (integer)")
            {
                {
                    json j = 17;
                    CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
                {
                    json j = 17;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (unsigned)")
            {
                {
                    json j = 17u;
                    CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
                {
                    json j = 17u;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (floating point)")
            {
                {
                    json j = 23.42;
                    CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
                {
                    json j = 23.42;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
                }
            }
        }

        SECTION("erase with two valid iterators")
        {
            SECTION("null")
            {
                {
                    json j;
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.end()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
                }
                {
                    json j;
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cend()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
                }
            }

            SECTION("string")
            {
                {
                    json j = "foo";
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = "bar";
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (boolean)")
            {
                {
                    json j = false;
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = true;
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (integer)")
            {
                {
                    json j = 17;
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 17;
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (unsigned)")
            {
                {
                    json j = 17u;
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 17u;
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("number (floating point)")
            {
                {
                    json j = 23.42;
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = 23.42;
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }

            SECTION("binary")
            {
                {
                    json j = json::binary({1, 2, 3});
                    const json::iterator it = j.erase(j.begin(), j.end());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
                {
                    json j = json::binary({1, 2, 3});
                    const json::const_iterator it = j.erase(j.cbegin(), j.cend());
                    CHECK(j.type() == json::value_t::null);
                    CHECK(it == j.end());
                }
            }
        }

        SECTION("erase with two invalid iterators")
        {
            SECTION("string")
            {
                {
                    json j = "foo";
                    CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
                {
                    json j = "bar";
                    CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (boolean)")
            {
                {
                    json j = false;
                    CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
                {
                    json j = true;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (integer)")
            {
                {
                    json j = 17;
                    CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
                {
                    json j = 17;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (unsigned)")
            {
                {
                    json j = 17u;
                    CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
                {
                    json j = 17u;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
            }

            SECTION("number (floating point)")
            {
                {
                    json j = 23.42;
                    CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
                {
                    json j = 23.42;
                    CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
                }
            }
        }
    }
}


from __future__ import annotations

import typing as t

from werkzeug.exceptions import BadRequest
from werkzeug.exceptions import HTTPException
from werkzeug.wrappers import Request as RequestBase
from werkzeug.wrappers import Response as ResponseBase

from . import json
from .globals import current_app
from .helpers import _split_blueprint_path

if t.TYPE_CHECKING:  # pragma: no cover
    from werkzeug.routing import Rule

class Request(RequestBase):
    """The request object used by default in Flask.  Remembers the
    matched endpoint and view arguments.

    It is what ends up as :class:`~flask.request`.  If you want to replace
    the request object used you can subclass this and set
    :attr:`~flask.Flask.request_class` to your subclass.

    The request object is a :class:`~werkzeug.wrappers.Request` subclass and
    provides all of the attributes Werkzeug defines plus a few Flask
    specific ones.
    """

    json_module: t.Any = json

    #: The internal URL rule that matched the request.  This can be
    #: useful to inspect which methods are allowed for the URL from
    #: a before/after handler (``request.url_rule.methods``) etc.
    #: Though if the request's method was invalid for the URL rule,
    #: the valid list is available in ``routing_exception.valid_methods``
    #: instead (an attribute of the Werkzeug exception
    #: :exc:`~werkzeug.exceptions.MethodNotAllowed`)
    #: because the request was never internally bound.
    #:
    #: .. versionadded:: 0.6
    url_rule: Rule | None = None

    #: A dict of view arguments that matched the request.  If an exception
    #: happened when matching, this will be ``None``.
    view_args: dict[str, t.Any] | None = None

    #: If matching the URL failed, this is the exception that will be
    #: raised / was raised as part of the request handling.  This is
    #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
    #: something similar.
    routing_exception: HTTPException | None = None

    _max_content_length: int | None = None
    _max_form_memory_size: int | None = None
    _max_form_parts: int | None = None

    @property
    def max_content_length(self) -> int | None:
        """The maximum number of bytes that will be read during this request. If
        this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
        error is raised. If it is set to ``None``, no limit is enforced at the
        Flask application level. However, if it is ``None`` and the request has
        no ``Content-Length`` header and the WSGI server does not indicate that
        it terminates the stream, then no data is read to avoid an infinite
        stream.

        Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which
        defaults to ``None``. It can be set on a specific ``request`` to apply
        the limit to that specific view. This should be set appropriately based
        on an application's or view's specific needs.

        .. versionchanged:: 3.1
            This can be set per-request.

        .. versionchanged:: 0.6
            This is configurable through Flask config.
        """
        if self._max_content_length is not None:
            return self._max_content_length

        if not current_app:
            return super().max_content_length

        return current_app.config["MAX_CONTENT_LENGTH"]  # type: ignore[no-any-return]

    @max_content_length.setter
    def max_content_length(self, value: int | None) -> None:
        self._max_content_length = value

    @property
    def max_form_memory_size(self) -> int | None:
        """The maximum size in bytes any non-file form field may be in a
        ``multipart/form-data`` body. If this limit is exceeded, a 413
        :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
        is set to ``None``, no limit is enforced at the Flask application level.

        Each request defaults to the :data:`MAX_FORM_MEMORY_SIZE` config, which
        defaults to ``500_000``. It can be set on a specific ``request`` to
        apply the limit to that specific view. This should be set appropriately
        based on an application's or view's specific needs.

        .. versionchanged:: 3.1
            This is configurable through Flask config.
        """
        if self._max_form_memory_size is not None:
            return self._max_form_memory_size

        if not current_app:
            return super().max_form_memory_size

        return current_app.config["MAX_FORM_MEMORY_SIZE"]  # type: ignore[no-any-return]

    @max_form_memory_size.setter
    def max_form_memory_size(self, value: int | None) -> None:
        self._max_form_memory_size = value

    @property  # type: ignore[override]
    def max_form_parts(self) -> int | None:
        """The maximum number of fields that may be present in a
        ``multipart/form-data`` body. If this limit is exceeded, a 413
        :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
        is set to ``None``, no limit is enforced at the Flask application level.

        Each request defaults to the :data:`MAX_FORM_PARTS` config, which
        defaults to ``1_000``. It can be set on a specific ``request`` to apply
        the limit to that specific view. This should be set appropriately based
        on an application's or view's specific needs.

        .. versionchanged:: 3.1
            This is configurable through Flask config.
        """
        if self._max_form_parts is not None:
            return self._max_form_parts

        if not current_app:
            return super().max_form_parts

        return current_app.config["MAX_FORM_PARTS"]  # type: ignore[no-any-return]

    @max_form_parts.setter
    def max_form_parts(self, value: int | None) -> None:
        self._max_form_parts = value

    @property
    def endpoint(self) -> str | None:
        """The endpoint that matched the request URL.

        This will be ``None`` if matching failed or has not been
        performed yet.

        This in combination with :attr:`view_args` can be used to
        reconstruct the same URL or a modified URL.
        """
        if self.url_rule is not None:
            return self.url_rule.endpoint  # type: ignore[no-any-return]

        return None

    @property
    def blueprint(self) -> str | None:
        """The registered name of the current blueprint.

        This will be ``None`` if the endpoint is not part of a
        blueprint, or if URL matching failed or has not been performed
        yet.

        This does not necessarily match the name the blueprint was
        created with. It may have been nested, or registered with a
        different name.
        """
        endpoint = self.endpoint

        if endpoint is not None and "." in endpoint:
            return endpoint.rpartition(".")[0]

        return None

    @property
    def blueprints(self) -> list[str]:
        """The registered names of the current blueprint upwards through
        parent blueprints.

        This will be an empty list if there is no current blueprint, or
        if URL matching failed.

        .. versionadded:: 2.0.1
        """
        name = self.blueprint

        if name is None:
            return []

        return _split_blueprint_path(name)

    def _load_form_data(self) -> None:
        super()._load_form_data()

        # In debug mode we're replacing the files multidict with an ad-hoc
        # subclass that raises a different error for key errors.
        if (
            current_app
            and current_app.debug
            and self.mimetype != "multipart/form-data"
            and not self.files
        ):
            from .debughelpers import attach_enctype_error_multidict

            attach_enctype_error_multidict(self)

    def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
        try:
            return super().on_json_loading_failed(e)
        except BadRequest as ebr:
            if current_app and current_app.debug:
                raise

            raise BadRequest() from ebr

class Response(ResponseBase):
    """The response object that is used by default in Flask.  Works like the
    response object from Werkzeug but is set to have an HTML mimetype by
    default.  Quite often you don't have to create this object yourself because
    :meth:`~flask.Flask.make_response` will take care of that for you.

    If you want to replace the response object used you can subclass this and
    set :attr:`~flask.Flask.response_class` to your subclass.

    .. versionchanged:: 1.0
        JSON support is added to the response, like the request. This is useful
        when testing to get the test client response data as JSON.

    .. versionchanged:: 1.0

        Added :attr:`max_cookie_size`.
    """

    default_mimetype: str | None = "text/html"

    json_module = json

    autocorrect_location_header = False

    @property
    def max_cookie_size(self) -> int:  # type: ignore
        """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.

        See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
        Werkzeug's docs.
        """
        if current_app:
            return current_app.config["MAX_COOKIE_SIZE"]  # type: ignore[no-any-return]

        # return Werkzeug's default when not in an app context
        return super().max_cookie_size


import { type Ref, onBeforeUnmount, onMounted, ref } from 'vue'

type YoutubeState = 'play' | 'pause' | 'end' | 'buffer' | 'unstarted' | 'cued'

interface YoutubePlayerState {
  state: YoutubeState
}

const STATE_BY_CODE: Record<number, YoutubeState> = {
  [-1]: 'unstarted',
  [0]: 'end',
  [1]: 'play',
  [2]: 'pause',
  [3]: 'buffer',
  [5]: 'cued',
}

export const useYoutubePlayer = (iframeEl: Ref<HTMLIFrameElement | null>) => {
  const player = ref<YoutubePlayerState>({ state: 'pause' })

  const postPlayerCommand = (func: string, args: unknown[] = []) => {
    const frameWindow = iframeEl.value?.contentWindow
    if (!frameWindow) return

    frameWindow.postMessage(
      JSON.stringify({
        event: 'command',
        func,
        args,
      }),
      '*',
    )
  }

  const setStateByCode = (code: number) => {
    player.value = { state: STATE_BY_CODE[code] ?? 'pause' }
  }

  const onMessage = (event: MessageEvent) => {
    const frameWindow = iframeEl.value?.contentWindow
    if (!frameWindow || event.source !== frameWindow) return

    let payload: unknown = event.data
    if (typeof payload === 'string') {
      try {
        payload = JSON.parse(payload)
      } catch {
        return
      }
    }

    if (!payload || typeof payload !== 'object') return

    const data = payload as {
      event?: string
      info?: number | { playerState?: number }
    }

    if (data.event === 'onStateChange' && typeof data.info === 'number') {
      setStateByCode(data.info)
      return
    }

    if (
      data.event === 'infoDelivery' &&
      data.info &&
      typeof data.info === 'object' &&
      typeof data.info.playerState === 'number'
    ) {
      setStateByCode(data.info.playerState)
    }
  }

  const initPlayer = () => {
    const frameWindow = iframeEl.value?.contentWindow
    if (!frameWindow) return

    frameWindow.postMessage(JSON.stringify({ event: 'listening' }), '*')
    postPlayerCommand('addEventListener', ['onStateChange'])
    postPlayerCommand('getPlayerState')
  }

  const play = () => {
    player.value = { state: 'play' }
    postPlayerCommand('playVideo')
  }

  const pause = () => {
    player.value = { state: 'pause' }
    postPlayerCommand('pauseVideo')
  }

  const seekTo = (seconds: number, allowSeekAhead = true) => {
    postPlayerCommand('seekTo', [seconds, allowSeekAhead])
  }

  const togglePlayback = () => {
    if (player.value.state === 'play') {
      pause()
      return
    }

    play()
  }

  onMounted(() => {
    window.addEventListener('message', onMessage)
  })

  onBeforeUnmount(() => {
    window.removeEventListener('message', onMessage)
  })

  return {
    player,
    initPlayer,
    play,
    pause,
    seekTo,
    togglePlayback,
  }
}


#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}
};

template<typename BasicJsonType>
void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t)
{
    nlohmann_json_j["name"] = nlohmann_json_t.name;
    nlohmann_json_j["address"] = nlohmann_json_t.address;
    nlohmann_json_j["age"] = nlohmann_json_t.age;
}

template<typename BasicJsonType>
void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t)
{
    person nlohmann_json_default_obj;
    nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name);
    nlohmann_json_t.address = nlohmann_json_j.value("address", nlohmann_json_default_obj.address);
    nlohmann_json_t.age = nlohmann_json_j.value("age", nlohmann_json_default_obj.age);
}
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    auto p3 = j3.get<ns::person>();
    std::cout << "roundtrip: " << json(p3) << std::endl;
}


#include <exception>
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    // different JSON pointers
    json::json_pointer ptr0;
    json::json_pointer ptr1("");
    json::json_pointer ptr2("/foo");

    // different strings
    std::string str0("");
    std::string str1("/foo");
    std::string str2("bar");

    // compare JSON pointers and strings
    std::cout << std::boolalpha
              << "\"" << ptr0 << "\" == \"" << str0 << "\": " << (ptr0 == str0) << '\n'
              << "\"" << str0 << "\" == \"" << ptr1 << "\": " << (str0 == ptr1) << '\n'
              << "\"" << ptr2 << "\" == \"" << str1 << "\": " << (ptr2 == str1) << std::endl;

    try
    {
        std::cout << "\"" << str2 << "\" == \"" << ptr2 << "\": " << (str2 == ptr2) << std::endl;
    }
    catch (const json::parse_error& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}


// test utils used in e2e tests for playgrounds.
// `import { getColor } from '~utils'`

import fs from 'node:fs'
import path from 'node:path'
import colors from 'css-color-names'
import type {
  ConsoleMessage,
  ElementHandle,
  Locator,
} from 'playwright-chromium'
import type { DepOptimizationMetadata, Manifest } from 'vite'
import { normalizePath } from 'vite'
import {
  fromComment,
  fromMapFileComment,
  removeComments,
} from 'convert-source-map'
import { expect } from 'vitest'
import type { ResultPromise as ExecaResultPromise } from 'execa'
import { isWindows, page, sourcemapSnapshot, testDir } from './vitestSetup'

export * from './vitestSetup'

// make sure these ports are unique
export const ports = {
  cli: 9510,
  'cli-module': 9511,
  json: 9512,
  'legacy/ssr': 9520,
  lib: 9521,
  'optimize-missing-deps': 9522,
  'legacy/client-and-ssr': 9523,
  'assets/encoded-base': 9554, // not imported but used in `assets/vite.config-encoded-base.js`
  'assets/url-base': 9525, // not imported but used in `assets/vite.config-url-base.js`
  ssr: 9600,
  'ssr-deps': 9601,
  'ssr-html': 9602,
  'ssr-noexternal': 9603,
  'ssr-pug': 9604,
  'ssr-wasm': 9608,
  'ssr-webworker': 9605,
  'proxy-bypass': 9606, // not imported but used in `proxy-hmr/vite.config.js`
  'proxy-bypass/non-existent-app': 9607, // not imported but used in `proxy-hmr/other-app/vite.config.js`
  'ssr-hmr': 9609, // not imported but used in `hmr-ssr/__tests__/hmr.spec.ts`
  'proxy-hmr': 9616, // not imported but used in `proxy-hmr/vite.config.js`
  'proxy-hmr/other-app': 9617, // not imported but used in `proxy-hmr/other-app/vite.config.js`
  'ssr-conditions': 9620,
  'css/postcss-caching': 5005,
  'css/postcss-plugins-different-dir': 5006,
  'css/dynamic-import': 5007,
  'css/lightningcss-proxy': 5008,
  'backend-integration': 5009,
  'client-reload': 5010,
  'client-reload/hmr-port': 5011,
  'client-reload/cross-origin': 5012,
}
export const hmrPorts = {
  'optimize-missing-deps': 24680,
  ssr: 24681,
  'ssr-deps': 24682,
  'ssr-html': 24683,
  'ssr-noexternal': 24684,
  'ssr-pug': 24685,
  'ssr-wasm': 24691,
  'css/lightningcss-proxy': 24686,
  json: 24687,
  'ssr-conditions': 24688,
  'client-reload/hmr-port': 24689,
  'client-reload/cross-origin': 24690,
}

const hexToNameMap: Record<string, string> = {}
Object.keys(colors).forEach((color) => {
  hexToNameMap[colors[color]] = color
})

function componentToHex(c: number): string {
  const hex = c.toString(16)
  return hex.length === 1 ? '0' + hex : hex
}

function rgbToHex(rgb: string): string | undefined {
  const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/)
  if (match) {
    const [_, rs, gs, bs] = match
    return (
      '#' +
      componentToHex(parseInt(rs, 10)) +
      componentToHex(parseInt(gs, 10)) +
      componentToHex(parseInt(bs, 10))
    )
  }
  return undefined
}

async function toEl(
  el: string | ElementHandle | Locator,
): Promise<ElementHandle> {
  if (typeof el === 'string') {
    const realEl = await page.$(el)
    if (realEl == null) {
      throw new Error(`Cannot find element: "${el}"`)
    }
    return realEl
  }
  if ('elementHandle' in el) {
    return el.elementHandle()
  }
  return el
}

export async function getColor(
  el: string | ElementHandle | Locator,
): Promise<string> {
  el = await toEl(el)
  const rgb = await el.evaluate((el) => getComputedStyle(el as Element).color)
  return hexToNameMap[rgbToHex(rgb)] ?? rgb
}

export async function getBg(
  el: string | ElementHandle | Locator,
): Promise<string> {
  el = await toEl(el)
  return el.evaluate((el) => getComputedStyle(el as Element).backgroundImage)
}

/**
 * Unlike `getBg`, this function returns the raw value of the `background-image` CSS property.
 *
 * `getBg` returns the resolved value, which has the hostname and port prepended due to `computedStyle` call.
 */
export async function getCssRuleBg(selector: string): Promise<string> {
  return page.evaluate((sel) => {
    for (const sheet of document.styleSheets) {
      try {
        for (const rule of sheet.cssRules) {
          if (rule instanceof CSSStyleRule && rule.selectorText === sel) {
            return rule.style.backgroundImage
          }
        }
      } catch (_e) {}
    }
  }, selector)
}

export async function getBgColor(
  el: string | ElementHandle | Locator,
): Promise<string> {
  el = await toEl(el)
  const rgb = await el.evaluate(
    (el) => getComputedStyle(el as Element).backgroundColor,
  )
  return hexToNameMap[rgbToHex(rgb)] ?? rgb
}

export function readFile(filename: string, encoding?: BufferEncoding): string
export function readFile(filename: string, encoding: null): Buffer
export function readFile(
  filename: string,
  encoding?: BufferEncoding | null,
): Buffer | string {
  if (encoding === undefined) encoding = 'utf-8'
  return fs.readFileSync(path.resolve(testDir, filename), encoding)
}

export function editFile(
  filename: string,
  replacer: (content: string) => string,
): void
export function editFile(
  filename: string,
  encoding: null,
  replacer: (content: Buffer) => Buffer,
): void
export function editFile(
  filename: string,
  encoding: BufferEncoding | null,
  replacer: ((content: Buffer) => Buffer) | ((content: string) => string),
): void
export function editFile(
  filename: string,
  encodingOrReplacer: BufferEncoding | null | ((content: string) => string),
  maybeReplacer?: ((content: Buffer) => Buffer) | ((content: string) => string),
): void {
  filename = path.resolve(testDir, filename)
  const [encoding, replacer] = maybeReplacer
    ? [encodingOrReplacer as BufferEncoding | null, maybeReplacer]
    : ['utf-8' as const, encodingOrReplacer as (content: string) => string]
  const content: string | Buffer = fs.readFileSync(filename, encoding)
  const modified = (replacer as (content: string | Buffer) => string | Buffer)(
    content,
  )
  if (Buffer.byteLength(modified) === Buffer.byteLength(content)) {
    const e = new Error(
      `editFile("${filename}") did not change the file size. The polling ` +
        `watcher used in tests may miss same-length edits and cause flaky ` +
        `failures; change the edit so the file's byte length changes. See ` +
        `https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md#test-env-and-helpers`,
    )
    Error.captureStackTrace(e, editFile)
    throw e
  }
  fs.writeFileSync(filename, modified)
}

export function addFile(filename: string, content: string): void {
  const resolvedFilename = path.resolve(testDir, filename)
  fs.mkdirSync(path.dirname(resolvedFilename), { recursive: true })
  fs.writeFileSync(resolvedFilename, content)
}

export function removeFile(filename: string): void {
  fs.unlinkSync(path.resolve(testDir, filename))
}

export function listAssets(base = ''): string[] {
  const assetsDir = path.join(testDir, 'dist', base, 'assets')
  return fs.readdirSync(assetsDir)
}

export function findAssetFile(
  match: string | RegExp,
  base = '',
  assets = 'assets',
  matchAll = false,
): string | undefined {
  const assetsDir = path.join(testDir, 'dist', base, assets)
  let files: string[]
  try {
    files = fs.readdirSync(assetsDir)
  } catch (e) {
    if (e.code === 'ENOENT') {
      return undefined
    }
    throw e
  }
  if (matchAll) {
    const matchedFiles = files.filter((file) => file.match(match))
    return matchedFiles.length
      ? matchedFiles
          .map((file) =>
            fs.readFileSync(path.resolve(assetsDir, file), 'utf-8'),
          )
          .join('')
      : undefined
  } else {
    const matchedFile = files.find((file) => file.match(match))
    return matchedFile
      ? fs.readFileSync(path.resolve(assetsDir, matchedFile), 'utf-8')
      : undefined
  }
}

export function readManifest(base = ''): Manifest {
  return JSON.parse(
    fs.readFileSync(
      path.join(testDir, 'dist', base, '.vite/manifest.json'),
      'utf-8',
    ),
  )
}

export function readDepOptimizationMetadata(
  environmentName = 'client',
): DepOptimizationMetadata {
  const suffix = environmentName === 'client' ? '' : `_${environmentName}`
  return JSON.parse(
    fs.readFileSync(
      path.join(testDir, `node_modules/.vite/deps${suffix}/_metadata.json`),
      'utf-8',
    ),
  )
}

type UntilBrowserLogAfterCallback = (logs: string[]) => PromiseLike<void> | void

export async function untilBrowserLogAfter(
  operation: () => any,
  target: string | RegExp | Array<string | RegExp>,
  expectOrder?: boolean,
  callback?: UntilBrowserLogAfterCallback,
): Promise<string[]>
export async function untilBrowserLogAfter(
  operation: () => any,
  target: string | RegExp | Array<string | RegExp>,
  callback?: UntilBrowserLogAfterCallback,
): Promise<string[]>
export async function untilBrowserLogAfter(
  operation: () => any,
  target: string | RegExp | Array<string | RegExp>,
  arg3?: boolean | UntilBrowserLogAfterCallback,
  arg4?: UntilBrowserLogAfterCallback,
): Promise<string[]> {
  const expectOrder = typeof arg3 === 'boolean' ? arg3 : false
  const callback = typeof arg3 === 'boolean' ? arg4 : arg3

  const promise = untilBrowserLog(target, expectOrder)
  await operation()
  const logs = await promise
  if (callback) {
    await callback(logs)
  }
  return logs
}

async function untilBrowserLog(
  target?: string | RegExp | Array<string | RegExp>,
  expectOrder = true,
): Promise<string[]> {
  const { promise, resolve, reject } = promiseWithResolvers<void>()
  let timeoutId: ReturnType<typeof setTimeout>

  const logs = []

  try {
    const isMatch = (matcher: string | RegExp) => (text: string) =>
      typeof matcher === 'string' ? text === matcher : matcher.test(text)

    let processMsg: (text: string) => boolean

    if (!target) {
      processMsg = () => true
    } else if (Array.isArray(target)) {
      if (expectOrder) {
        const remainingTargets = [...target]
        processMsg = (text: string) => {
          const nextTarget = remainingTargets.shift()
          expect(text).toMatch(nextTarget)
          return remainingTargets.length === 0
        }
      } else {
        const remainingMatchers = target.map(isMatch)
        processMsg = (text: string) => {
          const nextIndex = remainingMatchers.findIndex((matcher) =>
            matcher(text),
          )
          if (nextIndex >= 0) {
            remainingMatchers.splice(nextIndex, 1)
          }
          return remainingMatchers.length === 0
        }
      }
    } else {
      processMsg = isMatch(target)
    }

    const handleMsg = (msg: ConsoleMessage) => {
      try {
        const text = msg.text()
        logs.push(text)
        const done = processMsg(text)
        if (done) {
          resolve()
        }
      } catch (err) {
        reject(err)
      }
    }

    timeoutId = setTimeout(() => {
      const nextTarget = Array.isArray(target)
        ? expectOrder
          ? target[0]
          : target.join(', ')
        : target
      reject(
        new Error(
          `Timeout waiting for browser logs. Waiting for: ${nextTarget}`,
        ),
      )
      page.off('console', handleMsg)
    }, 5000)

    page.on('console', handleMsg)
  } catch (err) {
    reject(err)
  }

  await promise
  clearTimeout(timeoutId)

  return logs
}

export function extractSourcemap(content: string): any
export function extractSourcemap(
  content: string,
  read: (filename: string) => Promise<string>,
): Promise<any>
export function extractSourcemap(
  content: string,
  read?: (filename: string) => Promise<string>,
): any {
  const lines = content.trim().split('\n')
  const lastLine = lines[lines.length - 1]
  if (read) {
    const result = fromMapFileComment(lastLine, async (url) => {
      if (url.startsWith('data:')) {
        throw new Error(`Omit read argument when sourcemap is inline`)
      }
      const content = await read(url)
      return content
    })
    return result.then((r) => r.toObject())
  }
  return fromComment(lastLine).toObject()
}

export const formatSourcemapForSnapshot = (
  map: any,
  code: string,
  withoutContent = false,
): any => {
  const root = normalizePath(testDir)
  const m = { ...map }
  delete m.file
  if (m.names && m.names.length === 0) {
    delete m.names
  }
  if (m.ignoreList && m.ignoreList.length === 0) {
    delete m.ignoreList
  }
  if (m.debugId) {
    m.debugId = '00000000-0000-0000-0000-000000000000'
  }
  m.sources = m.sources.map((source) => source.replace(root, '/root'))
  if (m.sourceRoot) {
    m.sourceRoot = m.sourceRoot.replace(root, '/root')
  }
  const normalized = Object.fromEntries(
    Object.keys(m)
      .filter((key) => m[key] !== null)
      .sort()
      .map((key) => [key, m[key]]),
  )
  const c = removeComments(code.replace(/\?v=[\da-f]{8}/g, '?v=00000000'))
  return { map: normalized, code: c, [sourcemapSnapshot]: { withoutContent } }
}

// helper function to kill process, uses taskkill on windows to ensure child process is killed too
export async function killProcess(
  serverProcess: ExecaResultPromise,
): Promise<void> {
  if (isWindows) {
    try {
      const { execaCommandSync } = await import('execa')
      execaCommandSync(`taskkill /pid ${serverProcess.pid} /T /F`)
    } catch (e) {
      console.error('failed to taskkill:', e)
    }
  } else {
    serverProcess.kill('SIGTERM')
  }
}

export interface PromiseWithResolvers<T> {
  promise: Promise<T>
  resolve: (value: T | PromiseLike<T>) => void
  reject: (reason?: any) => void
}
export function promiseWithResolvers<T>(): PromiseWithResolvers<T> {
  let resolve: any
  let reject: any
  const promise = new Promise<T>((_resolve, _reject) => {
    resolve = _resolve
    reject = _reject
  })
  return { promise, resolve, reject }
}


import path from 'node:path'
import MagicString from 'magic-string'
import type { ImportSpecifier } from 'es-module-lexer'
import { init, parse as parseImports } from 'es-module-lexer'
import type { SourceMap } from 'rolldown'
import { viteBuildImportAnalysisPlugin as nativeBuildImportAnalysisPlugin } from 'rolldown/experimental'
import type { RawSourceMap } from '@jridgewell/remapping'
import convertSourceMap from 'convert-source-map'
import {
  combineSourcemaps,
  generateCodeFrame,
  getFileStartIndex,
  numberToPos,
} from '../utils'
import { type Plugin, perEnvironmentPlugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { toOutputFilePathInJS } from '../build'
import { genSourceMapUrl } from '../server/sourcemap'
import type { PartialEnvironment } from '../baseEnvironment'
import { removedPureCssFilesCache } from './css'
import { getImportMap, getImportMapFilename } from './html'

type FileDep = {
  url: string
  runtime: boolean
}

type VitePreloadErrorEvent = Event & { payload: Error }

/**
 * A flag for injected helpers. This flag will be set to `false` if the output
 * target is not native es - so that injected helper logic can be conditionally
 * dropped.
 */
export const isModernFlag = `__VITE_IS_MODERN__`
export const preloadMethod = `__vitePreload`
export const preloadMarker = `__VITE_PRELOAD__`

export const preloadHelperId = '\0vite/preload-helper.js'
const preloadMarkerRE = new RegExp(preloadMarker, 'g')

function toRelativePath(filename: string, importer: string) {
  const relPath = path.posix.relative(path.posix.dirname(importer), filename)
  return relPath[0] === '.' ? relPath : `./${relPath}`
}

function findPreloadMarker(str: string, pos: number = 0): number {
  preloadMarkerRE.lastIndex = pos
  const result = preloadMarkerRE.exec(str)
  return result?.index ?? -1
}

/**
 * Pairs each dynamic `import()` with the `__VITE_PRELOAD__` marker that belongs to it.
 *
 * Each import is wrapped as `__vitePreload(factory, __VITE_PRELOAD__)`, so its marker
 * comes right after it. Nested imports nest the wrappers, so the imports and markers
 * form balanced brackets. For example, in `import('a').then(() => import('b'))`, b's
 * marker ends up before a's. Pairing each import with the next marker would give b's
 * marker to both imports and drop a's CSS to `void 0` (#22700). Instead the markers are
 * matched with a stack in one pass, and each marker closes the innermost import that is
 * still open.
 *
 * The returned array is parallel to `imports`. Entry `i` holds the start position of
 * import `i`'s marker, or -1 when the import has no marker.
 */
export function matchImportsToPreloadMarkers(
  code: string,
  imports: readonly ImportSpecifier[],
): number[] {
  const importMarkerPos = new Array<number>(imports.length).fill(-1)
  if (imports.length === 0) return importMarkerPos

  const openImports: number[] = []
  let nextImport = 0
  for (
    let markerStartPos = findPreloadMarker(code, imports[0].e);
    markerStartPos !== -1;
    markerStartPos = findPreloadMarker(
      code,
      markerStartPos + preloadMarker.length,
    )
  ) {
    while (
      nextImport < imports.length &&
      imports[nextImport].e <= markerStartPos
    ) {
      openImports.push(nextImport++)
    }
    if (openImports.length) {
      importMarkerPos[openImports.pop()!] = markerStartPos
    }
  }
  // #3051: a lone import whose marker isn't placed after it pairs with the only marker
  if (imports.length === 1 && importMarkerPos[0] === -1) {
    importMarkerPos[0] = findPreloadMarker(code)
  }

  return importMarkerPos
}

/**
 * Helper for preloading CSS and direct imports of async chunks in parallel to
 * the async chunk itself.
 */

function detectScriptRel() {
  const relList =
    typeof document !== 'undefined' && document.createElement('link').relList
  return relList && relList.supports && relList.supports('modulepreload')
    ? 'modulepreload'
    : 'preload'
}

declare const scriptRel: string
declare const seen: Record<string, boolean>
function preload(
  baseModule: () => Promise<unknown>,
  deps?: string[],
  importerUrl?: string,
) {
  let promise: Promise<PromiseSettledResult<unknown>[] | void> =
    Promise.resolve()
  // @ts-expect-error __VITE_IS_MODERN__ will be replaced with boolean later
  if (__VITE_IS_MODERN__ && deps && deps.length > 0) {
    const links = document.getElementsByTagName('link')
    const cspNonceMeta = document.querySelector<HTMLMetaElement>(
      'meta[property=csp-nonce]',
    )
    // `.nonce` should be used to get along with nonce hiding (https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce#accessing_nonces_and_nonce_hiding)
    // Firefox 67-74 uses modern chunks and supports CSP nonce, but does not support `.nonce`
    // in that case fallback to getAttribute
    const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute('nonce')

    // Promise.allSettled is not supported by Chrome 64-75, Firefox 67-70, Safari 11.1-12.1
    function allSettled<T>(
      promises: Array<T | PromiseLike<T>>,
    ): Promise<PromiseSettledResult<T>[]> {
      return Promise.all(
        promises.map((p) =>
          Promise.resolve(p).then(
            (value: T) => ({ status: 'fulfilled' as const, value }),
            (reason: unknown) => ({ status: 'rejected' as const, reason }),
          ),
        ),
      )
    }

    function importMetaResolve(specifier: string): string {
      // @ts-expect-error import.meta.resolve is not supported by all browsers we support
      // But `import.meta.resolve` is only needed when build.chunkImportMap is enabled,
      // and that option requires `import.meta.resolve` support.
      if (import.meta.resolve) {
        return import.meta.resolve(specifier)
      }
      return new URL(specifier, /** #__KEEP__ */ import.meta.url).href
    }

    promise = allSettled(
      deps.map((dep) => {
        // @ts-expect-error assetsURL is declared before preload.toString()
        dep = assetsURL(dep, importerUrl)
        dep = importMetaResolve(dep)
        if (dep in seen) return
        seen[dep] = true
        const isCss = dep.endsWith('.css')

        // check if the file is already preloaded by SSR markup
        // `dep` is already converted to an absolute URL by the `assetsURL` function
        for (let i = links.length - 1; i >= 0; i--) {
          const link = links[i]
          // The `links[i].href` is an absolute URL thanks to browser doing the work
          // for us. See https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:idl-domstring-5
          if (link.href === dep && (!isCss || link.rel === 'stylesheet')) {
            return
          }
        }

        const link = document.createElement('link')
        link.rel = isCss ? 'stylesheet' : scriptRel
        if (!isCss) {
          link.as = 'script'
        }
        link.crossOrigin = ''
        link.href = dep
        if (cspNonce) {
          link.setAttribute('nonce', cspNonce)
        }
        document.head.appendChild(link)
        if (isCss) {
          return new Promise((res, rej) => {
            link.addEventListener('load', res)
            link.addEventListener('error', () =>
              rej(new Error(`Unable to preload CSS for ${dep}`)),
            )
          })
        }
      }),
    )
  }

  function handlePreloadError(err: Error) {
    const e = new Event('vite:preloadError', {
      cancelable: true,
    }) as VitePreloadErrorEvent
    e.payload = err
    window.dispatchEvent(e)
    if (!e.defaultPrevented) {
      throw err
    }
  }

  return promise.then((res) => {
    for (const item of res || []) {
      if (item.status !== 'rejected') continue
      handlePreloadError(item.reason)
    }
    return baseModule().catch(handlePreloadError)
  })
}

function getPreloadCode(
  environment: PartialEnvironment,
  renderBuiltUrlBoolean: boolean,
  isRelativeBase: boolean,
) {
  const { modulePreload } = environment.config.build

  const scriptRel =
    modulePreload && modulePreload.polyfill
      ? `'modulepreload'`
      : `/* @__PURE__ */ (${detectScriptRel.toString()})()`

  // There are two different cases for the preload list format in __vitePreload
  //
  // __vitePreload(() => import(asyncChunk), [ ...deps... ])
  //
  // This is maintained to keep backwards compatibility as some users developed plugins
  // using regex over this list to workaround the fact that module preload wasn't
  // configurable.
  const assetsURL =
    renderBuiltUrlBoolean || isRelativeBase
      ? // If `experimental.renderBuiltUrl` is used, the dependencies might be relative to the current chunk.
        // If relative base is used, the dependencies are relative to the current chunk.
        // The importerUrl is passed as third parameter to __vitePreload in this case
        `function(dep, importerUrl) { return new URL(dep, importerUrl).href }`
      : // If the base isn't relative, then the deps are relative to the projects `outDir` and the base
        // is appended inside __vitePreload too.
        `function(dep) { return ${JSON.stringify(environment.config.base)}+dep }`
  // replace `import` as a workaround for stackblitz: https://stackblitz.com/edit/node-vqfvv8dy?file=index.js
  const preloadMethodCode = preload.toString().replaceAll('𝐢𝐦𝐩𝐨𝐫𝐭', 'import')
  const preloadCode = `const scriptRel = ${scriptRel};const assetsURL = ${assetsURL};const seen = {};export const ${preloadMethod} = ${preloadMethodCode}`
  return preloadCode
}

/**
 * Build only. During serve this is performed as part of ./importAnalysis.
 */
export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] {
  const getInsertPreload = (environment: PartialEnvironment) =>
    environment.config.consumer === 'client' &&
    !config.isWorker &&
    !config.build.lib

  const renderBuiltUrl = config.experimental.renderBuiltUrl
  const isRelativeBase = config.base === './' || config.base === ''

  const plugin: Plugin = {
    name: 'vite:build-import-analysis',

    renderChunk(code, _, { format }) {
      // make sure we only perform the preload logic in modern builds.
      if (code.includes(isModernFlag)) {
        const re = new RegExp(isModernFlag, 'g')
        const isModern = String(format === 'es')
        const isModernWithPadding =
          isModern + ' '.repeat(isModernFlag.length - isModern.length)
        return {
          code: code.replace(re, isModernWithPadding),
          map: null,
        }
      }
      return null
    },

    async generateBundle(opts, bundle) {
      const { format } = opts
      if (format !== 'es') {
        return
      }

      await init

      // If preload is not enabled, we parse through each imports and remove any imports to pure CSS chunks
      // as they are removed from the bundle
      if (!getInsertPreload(this.environment)) {
        const removedPureCssFiles = removedPureCssFilesCache.get(config)
        if (removedPureCssFiles && removedPureCssFiles.size > 0) {
          for (const file in bundle) {
            const chunk = bundle[file]
            if (chunk.type === 'chunk' && chunk.code.includes('import')) {
              const code = chunk.code
              let imports!: ImportSpecifier[]
              try {
                imports = parseImports(code)[0].filter((i) => i.d > -1)
              } catch (e: any) {
                const loc = numberToPos(code, e.idx)
                this.error({
                  name: e.name,
                  message: e.message,
                  stack: e.stack,
                  cause: e.cause,
                  pos: e.idx,
                  loc: { ...loc, file: chunk.fileName },
                  frame: generateCodeFrame(code, loc),
                })
              }

              for (const imp of imports) {
                const {
                  n: name,
                  s: start,
                  e: end,
                  ss: expStart,
                  se: expEnd,
                } = imp
                let url = name
                if (!url) {
                  const rawUrl = code.slice(start, end)
                  if (
                    (rawUrl[0] === `"` && rawUrl[rawUrl.length - 1] === `"`) ||
                    (rawUrl[0] === '`' && rawUrl[rawUrl.length - 1] === '`')
                  )
                    url = rawUrl.slice(1, -1)
                }
                if (!url) continue

                const normalizedFile = path.posix.join(
                  path.posix.dirname(chunk.fileName),
                  url,
                )
                if (removedPureCssFiles.has(normalizedFile)) {
                  // remove with Promise.resolve({}) while preserving source map location
                  chunk.code =
                    chunk.code.slice(0, expStart) +
                    `Promise.resolve({${''.padEnd(expEnd - expStart - 19, ' ')}})` +
                    chunk.code.slice(expEnd)
                }
              }
            }
          }
        }
        return
      }
      const buildSourcemap = this.environment.config.build.sourcemap
      const { modulePreload } = this.environment.config.build

      let importMapMapping: Record<string, string> | undefined
      let importMapReverseMapping: Record<string, string> | undefined
      if (this.environment.config.build.chunkImportMap) {
        const importMap = getImportMap(bundle, this.environment.config)!
        importMapMapping = importMap.mapping
        importMapReverseMapping = Object.fromEntries(
          Object.entries(importMapMapping).map(([k, v]) => [v, k]),
        )

        if (config.isOutputOptionsForLegacyChunks?.(opts)) {
          this.emitFile({
            type: 'asset',
            fileName: 'importmap.legacy.json',
            source: importMap.asset.source,
          })
          delete bundle[getImportMapFilename(this.environment.config)]
        }
      }

      for (const file in bundle) {
        const chunk = bundle[file]
        // can't use chunk.dynamicImports.length here since some modules e.g.
        // dynamic import to constant json may get inlined.
        if (chunk.type === 'chunk' && chunk.code.includes(preloadMarker)) {
          const code = chunk.code
          let imports!: ImportSpecifier[]
          try {
            imports = parseImports(code)[0].filter((i) => i.d > -1)
          } catch (e: any) {
            const loc = numberToPos(code, e.idx)
            this.error({
              name: e.name,
              message: e.message,
              stack: e.stack,
              cause: e.cause,
              pos: e.idx,
              loc: { ...loc, file: chunk.fileName },
              frame: generateCodeFrame(code, loc),
            })
          }

          const s = new MagicString(code)
          const rewroteMarkerStartPos = new Set() // position of the leading double quote

          const fileDeps: FileDep[] = []
          const addFileDep = (
            url: string,
            runtime: boolean = false,
          ): number => {
            const index = fileDeps.findIndex((dep) => dep.url === url)
            if (index === -1) {
              return fileDeps.push({ url, runtime }) - 1
            } else {
              return index
            }
          }

          if (imports.length) {
            const importMarkerPos = matchImportsToPreloadMarkers(code, imports)

            for (let index = 0; index < imports.length; index++) {
              // To handle escape sequences in specifier strings, the .n field will be provided where possible.
              const {
                n: name,
                s: start,
                e: end,
                ss: expStart,
                se: expEnd,
              } = imports[index]
              // check the chunk being imported
              let url = name
              if (!url) {
                const rawUrl = code.slice(start, end)
                if (
                  (rawUrl[0] === `"` && rawUrl[rawUrl.length - 1] === `"`) ||
                  (rawUrl[0] === '`' && rawUrl[rawUrl.length - 1] === '`')
                )
                  url = rawUrl.slice(1, -1)
              }
              const deps = new Set<string>()
              let hasRemovedPureCssChunk = false

              let normalizedFile: string | undefined = undefined

              if (url) {
                normalizedFile = path.posix.join(
                  path.posix.dirname(chunk.fileName),
                  url,
                )

                const ownerFilename = chunk.fileName
                // literal import - trace direct imports and add to deps
                const analyzed: Set<string> = new Set<string>()
                const addDeps = (rawFilename: string) => {
                  const filename =
                    importMapMapping?.[rawFilename] ?? rawFilename
                  if (filename === ownerFilename) return
                  if (analyzed.has(filename)) return
                  analyzed.add(filename)
                  const chunk = bundle[filename]
                  if (chunk) {
                    deps.add(chunk.fileName)
                    if (chunk.type === 'chunk') {
                      chunk.imports.forEach(addDeps)
                      // Ensure that the css imported by current chunk is loaded after the dependencies.
                      // So the style of current chunk won't be overwritten unexpectedly.
                      chunk.viteMetadata!.importedCss.forEach((file) => {
                        deps.add(file)
                      })
                    }
                  } else {
                    const removedPureCssFiles =
                      removedPureCssFilesCache.get(config)!
                    const chunk = removedPureCssFiles.get(filename)
                    if (chunk) {
                      if (chunk.viteMetadata!.importedCss.size) {
                        chunk.viteMetadata!.importedCss.forEach((file) => {
                          deps.add(file)
                        })
                        hasRemovedPureCssChunk = true
                      }

                      s.update(expStart, expEnd, 'Promise.resolve({})')
                    }
                  }
                }
                addDeps(normalizedFile)
              }

              const markerStartPos = importMarkerPos[index]

              if (markerStartPos > 0) {
                // the dep list includes the main chunk, so only need to reload when there are actual other deps.
                let depsArray =
                  deps.size > 1 ||
                  // main chunk is removed
                  (hasRemovedPureCssChunk && deps.size > 0)
                    ? modulePreload === false
                      ? // CSS deps use the same mechanism as module preloads, so even if disabled,
                        // we still need to pass these deps to the preload helper in dynamic imports.
                        [...deps].filter((d) => d.endsWith('.css'))
                      : [...deps]
                    : []

                const resolveDependencies = modulePreload
                  ? modulePreload.resolveDependencies
                  : undefined
                if (resolveDependencies && normalizedFile) {
                  // We can't let the user remove css deps as these aren't really preloads, they are just using
                  // the same mechanism as module preloads for this chunk
                  const cssDeps: string[] = []
                  const otherDeps: string[] = []
                  for (const dep of depsArray) {
                    ;(dep.endsWith('.css') ? cssDeps : otherDeps).push(dep)
                  }
                  depsArray = [
                    // NOTE: deps are URLs, not specifiers using the import map mapping
                    ...resolveDependencies(normalizedFile, otherDeps, {
                      hostId: file,
                      hostType: 'js',
                    }),
                    ...cssDeps,
                  ]
                }

                depsArray = depsArray.map(
                  (dep) => importMapReverseMapping?.[dep] ?? dep,
                )

                let renderedDeps: number[]
                if (renderBuiltUrl) {
                  renderedDeps = depsArray.map((dep) => {
                    const replacement = toOutputFilePathInJS(
                      this.environment,
                      dep,
                      'asset',
                      chunk.fileName,
                      'js',
                      toRelativePath,
                    )

                    if (typeof replacement === 'string') {
                      return addFileDep(replacement)
                    }

                    return addFileDep(replacement.runtime, true)
                  })
                } else {
                  renderedDeps = depsArray.map((d) =>
                    // Don't include the assets dir if the default asset file names
                    // are used, the path will be reconstructed by the import preload helper
                    isRelativeBase
                      ? addFileDep(toRelativePath(d, file))
                      : addFileDep(d),
                  )
                }

                s.update(
                  markerStartPos,
                  markerStartPos + preloadMarker.length,
                  renderedDeps.length > 0
                    ? `__vite__mapDeps([${renderedDeps.join(',')}])`
                    : `[]`,
                )
                rewroteMarkerStartPos.add(markerStartPos)
              }
            }
          }

          if (fileDeps.length > 0) {
            const fileDepsCode = `[${fileDeps
              .map((fileDep) =>
                fileDep.runtime ? fileDep.url : JSON.stringify(fileDep.url),
              )
              .join(',')}]`

            const mapDepsCode = `const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=${fileDepsCode})))=>i.map(i=>d[i]);\n`

            // inject extra code at the top or next line of hashbang
            if (code.startsWith('#!')) {
              s.prependLeft(getFileStartIndex(code), mapDepsCode)
            } else {
              s.prepend(mapDepsCode)
            }
          }

          // there may still be markers due to inlined dynamic imports, remove
          // all the markers regardless
          let markerStartPos = findPreloadMarker(code)
          while (markerStartPos >= 0) {
            if (!rewroteMarkerStartPos.has(markerStartPos)) {
              s.update(
                markerStartPos,
                markerStartPos + preloadMarker.length,
                'void 0',
              )
            }
            markerStartPos = findPreloadMarker(
              code,
              markerStartPos + preloadMarker.length,
            )
          }

          if (s.hasChanged()) {
            chunk.code = s.toString()
            if (buildSourcemap && chunk.map) {
              const nextMap = s.generateMap({
                source: chunk.fileName,
                hires: 'boundary',
              })
              const originalFile = chunk.map.file
              const map = combineSourcemaps(chunk.fileName, [
                nextMap as RawSourceMap,
                chunk.map as RawSourceMap,
              ]) as SourceMap
              map.toUrl = () => genSourceMapUrl(map)
              if (originalFile) {
                map.file = originalFile
              }

              const originalDebugId = chunk.map.debugId
              chunk.map = map

              if (buildSourcemap === 'inline') {
                chunk.code = chunk.code.replace(
                  convertSourceMap.mapFileCommentRegex,
                  '',
                )
                chunk.code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}`
              } else {
                if (originalDebugId) {
                  map.debugId = originalDebugId
                }
                const mapAsset = bundle[chunk.fileName + '.map']
                if (mapAsset && mapAsset.type === 'asset') {
                  mapAsset.source = map.toString()
                }
              }
            }
          }
        }
      }
    },
  }

  return [
    plugin,
    perEnvironmentPlugin('native:import-analysis-build', (environment) => {
      const preloadCode = getPreloadCode(
        environment,
        !!renderBuiltUrl,
        isRelativeBase,
      )
      return nativeBuildImportAnalysisPlugin({
        preloadCode,
        insertPreload: getInsertPreload(environment),
        // this field looks redundant, put a dummy value for now
        optimizeModulePreloadRelativePaths: false,
        renderBuiltUrl: !!renderBuiltUrl,
        isRelativeBase,
      })
    }),
  ]
}


# Debugging

This page collects the library's built-in debugger integrations and other debugging-related features. They are
not linked from a single place elsewhere in the docs, so are collected here.

## Visual Studio (natvis)

The repository ships [`nlohmann_json.natvis`](https://github.com/nlohmann/json/blob/develop/nlohmann_json.natvis)
at its root, a [Natvis](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects)
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.

Debug engines that wrap LLDB instead of the MSVC debug engine (for example, `codelldb` in VS Code) only have
partial/experimental Natvis support, and commonly fall back to showing raw internal fields even with the
`.natvis` file present. Switching to `cppvsdbg` where available, or checking your debug extension's own Natvis
support/version, are the next things to try if this happens. There is currently no bundled LLDB-native
pretty-printer script in this repository.

## GDB

The repository ships a [GDB Python pretty printer](https://github.com/nlohmann/json/tree/develop/tools/gdb_pretty_printer)
under `tools/gdb_pretty_printer`, with its own usage instructions in that directory's `README.md`.

## Extended exception diagnostics

Defining [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) before including the library augments
`type_error`/`out_of_range`-style exceptions with a JSON Pointer to the offending value, which can help pinpoint
where in a large document a runtime error occurred. This only applies to exceptions thrown *after* a value
exists (e.g. during element access); parse errors, which happen before any value exists to point at, are not
covered by this mechanism -- see [Parsing and exceptions](../features/parsing/parse_exceptions.md) for how parse
errors report their own location instead.