# CBOR

The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of
extremely small code sizes, fairly small message size, and extensibility without the need for version negotiation.

!!! abstract "References"

    - [CBOR Website](http://cbor.io) - the main source on CBOR
    - [CBOR Playground](http://cbor.me) - an interactive webpage to translate between JSON and CBOR
    - [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html) - the CBOR specification

## Serialization

The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification
([RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html)):

| JSON value type | value/range                                | CBOR type                         | first byte |
|-----------------|--------------------------------------------|-----------------------------------|------------|
| null            | `null`                                     | Null                              | 0xF6       |
| boolean         | `true`                                     | True                              | 0xF5       |
| boolean         | `false`                                    | False                             | 0xF4       |
| number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow) | 0x3B       |
| number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow) | 0x3A       |
| number_integer  | -32768..-129                               | Negative integer (2 bytes follow) | 0x39       |
| number_integer  | -128..-25                                  | Negative integer (1 byte follow)  | 0x38       |
| number_integer  | -24..-1                                    | Negative integer                  | 0x20..0x37 |
| number_integer  | 0..23                                      | Integer                           | 0x00..0x17 |
| number_integer  | 24..255                                    | Unsigned integer (1 byte follow)  | 0x18       |
| number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow) | 0x19       |
| number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow) | 0x1A       |
| number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow) | 0x1B       |
| number_unsigned | 0..23                                      | Integer                           | 0x00..0x17 |
| number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)  | 0x18       |
| number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow) | 0x19       |
| number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow) | 0x1A       |
| number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow) | 0x1B       |
| number_float    | *any value representable by a float*       | Single-Precision Float            | 0xFA       |
| number_float    | *any value NOT representable by a float*   | Double-Precision Float            | 0xFB       |
| string          | *length*: 0..23                            | UTF-8 string                      | 0x60..0x77 |
| string          | *length*: 24..255                          | UTF-8 string (1 byte follow)      | 0x78       |
| string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)     | 0x79       |
| string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)     | 0x7A       |
| string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)     | 0x7B       |
| array           | *size*: 0..23                              | array                             | 0x80..0x97 |
| array           | *size*: 24..255                            | array (1 byte follow)             | 0x98       |
| array           | *size*: 256..65535                         | array (2 bytes follow)            | 0x99       |
| array           | *size*: 65536..4294967295                  | array (4 bytes follow)            | 0x9A       |
| array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)            | 0x9B       |
| object          | *size*: 0..23                              | map                               | 0xA0..0xB7 |
| object          | *size*: 24..255                            | map (1 byte follow)               | 0xB8       |
| object          | *size*: 256..65535                         | map (2 bytes follow)              | 0xB9       |
| object          | *size*: 65536..4294967295                  | map (4 bytes follow)              | 0xBA       |
| object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)              | 0xBB       |
| binary          | *size*: 0..23                              | byte string                       | 0x40..0x57 |
| binary          | *size*: 24..255                            | byte string (1 byte follow)       | 0x58       |
| binary          | *size*: 256..65535                         | byte string (2 bytes follow)      | 0x59       |
| binary          | *size*: 65536..4294967295                  | byte string (4 bytes follow)      | 0x5A       |
| binary          | *size*: 4294967296..18446744073709551615   | byte string (8 bytes follow)      | 0x5B       |

Binary values with subtype are mapped to tagged values (0xD8..0xDB) depending on the subtype, followed by a byte string,
see "binary" cells in the table above.

!!! success "Complete mapping"

    The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value.

!!! info "NaN/infinity handling"

    `NaN`, `Infinity`, and `-Infinity` are serialized as a CBOR half-precision float (type 0xF9, 3 bytes total):
    `NaN` as `0xF9 0x7E 0x00`, `Infinity` as `0xF9 0x7C 0x00`, and `-Infinity` as `0xF9 0xFC 0x00`. This behavior
    differs from the normal JSON serialization which serializes NaN or Infinity to `null`.

!!! note

    Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
    9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
    always `false` and caused the intended half-precision path to be skipped.

!!! info "Unused CBOR types"

    The following CBOR types are not used in the conversion:

      - UTF-8 strings terminated by "break" (0x7F)
      - arrays terminated by "break" (0x9F)
      - maps terminated by "break" (0xBF)
      - byte strings terminated by "break" (0x5F)
      - date/time (0xC0..0xC1)
      - bignum (0xC2..0xC3)
      - decimal fraction (0xC4)
      - bigfloat (0xC5)
      - expected conversions (0xD5..0xD7)
      - simple values (0xE0..0xF3, 0xF8)
      - undefined (0xF7)
      - half-precision floats (0xF9)
      - break (0xFF)

!!! info "Tagged items"

    Binary subtypes will be serialized as tagged items. See [binary values](../binary_values.md#cbor) for an example.

??? example

    ```cpp
    --8<-- "examples/to_cbor.cpp"
    ```

    Output:

    ```c
    --8<-- "examples/to_cbor.output"
    ```

## Deserialization

The library maps CBOR types to JSON value types as follows:

| CBOR type              | JSON value type | first byte |
|------------------------|-----------------|------------|
| Integer                | number_unsigned | 0x00..0x17 |
| Unsigned integer       | number_unsigned | 0x18       |
| Unsigned integer       | number_unsigned | 0x19       |
| Unsigned integer       | number_unsigned | 0x1A       |
| Unsigned integer       | number_unsigned | 0x1B       |
| Negative integer       | number_integer  | 0x20..0x37 |
| Negative integer       | number_integer  | 0x38       |
| Negative integer       | number_integer  | 0x39       |
| Negative integer       | number_integer  | 0x3A       |
| Negative integer       | number_integer  | 0x3B       |
| Byte string            | binary          | 0x40..0x57 |
| Byte string            | binary          | 0x58       |
| Byte string            | binary          | 0x59       |
| Byte string            | binary          | 0x5A       |
| Byte string            | binary          | 0x5B       |
| UTF-8 string           | string          | 0x60..0x77 |
| UTF-8 string           | string          | 0x78       |
| UTF-8 string           | string          | 0x79       |
| UTF-8 string           | string          | 0x7A       |
| UTF-8 string           | string          | 0x7B       |
| UTF-8 string           | string          | 0x7F       |
| array                  | array           | 0x80..0x97 |
| array                  | array           | 0x98       |
| array                  | array           | 0x99       |
| array                  | array           | 0x9A       |
| array                  | array           | 0x9B       |
| array                  | array           | 0x9F       |
| map                    | object          | 0xA0..0xB7 |
| map                    | object          | 0xB8       |
| map                    | object          | 0xB9       |
| map                    | object          | 0xBA       |
| map                    | object          | 0xBB       |
| map                    | object          | 0xBF       |
| False                  | `false`         | 0xF4       |
| True                   | `true`          | 0xF5       |
| Null                   | `null`          | 0xF6       |
| Half-Precision Float   | number_float    | 0xF9       |
| Single-Precision Float | number_float    | 0xFA       |
| Double-Precision Float | number_float    | 0xFB       |

!!! warning "Incomplete mapping"

    The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors:

     - date/time (0xC0..0xC1)
     - bignum (0xC2..0xC3)
     - decimal fraction (0xC4)
     - bigfloat (0xC5)
     - expected conversions (0xD5..0xD7)
     - simple values (0xE0..0xF3, 0xF8)
     - undefined (0xF7)

!!! warning "Negative integer overflow"

    CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
    result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
    [`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
    silently.

!!! warning "Object keys"

    CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.

!!! warning "Tagged items"

    Tagged items will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`.

??? example

    ```cpp
    --8<-- "examples/from_cbor.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/from_cbor.output"
    ```


from __future__ import annotations

import collections.abc as cabc
import typing as t

if t.TYPE_CHECKING:  # pragma: no cover
    from _typeshed.wsgi import WSGIApplication  # noqa: F401
    from werkzeug.datastructures import Headers  # noqa: F401
    from werkzeug.sansio.response import Response  # noqa: F401

# The possible types that are directly convertible or are a Response object.
ResponseValue = t.Union[
    "Response",
    str,
    bytes,
    list[t.Any],
    # Only dict is actually accepted, but Mapping allows for TypedDict.
    t.Mapping[str, t.Any],
    t.Iterator[str],
    t.Iterator[bytes],
    cabc.AsyncIterable[str],  # for Quart, until App is generic.
    cabc.AsyncIterable[bytes],
]

# the possible types for an individual HTTP header
HeaderValue = str | list[str] | tuple[str, ...]

# the possible types for HTTP headers
HeadersValue = t.Union[
    "Headers",
    t.Mapping[str, HeaderValue],
    t.Sequence[tuple[str, HeaderValue]],
]

# The possible types returned by a route function.
ResponseReturnValue = t.Union[
    ResponseValue,
    tuple[ResponseValue, HeadersValue],
    tuple[ResponseValue, int],
    tuple[ResponseValue, int, HeadersValue],
    "WSGIApplication",
]

# Allow any subclass of werkzeug.Response, such as the one from Flask,
# as a callback argument. Using werkzeug.Response directly makes a
# callback annotated with flask.Response fail type checking.
ResponseClass = t.TypeVar("ResponseClass", bound="Response")

AppOrBlueprintKey = str | None  # The App key is None, whereas blueprints are named
AfterRequestCallable = (
    t.Callable[[ResponseClass], ResponseClass]
    | t.Callable[[ResponseClass], t.Awaitable[ResponseClass]]
)
BeforeFirstRequestCallable = t.Callable[[], None] | t.Callable[[], t.Awaitable[None]]
BeforeRequestCallable = (
    t.Callable[[], ResponseReturnValue | None]
    | t.Callable[[], t.Awaitable[ResponseReturnValue | None]]
)
ShellContextProcessorCallable = t.Callable[[], dict[str, t.Any]]
TeardownCallable = (
    t.Callable[[BaseException | None], None]
    | t.Callable[[BaseException | None], t.Awaitable[None]]
)
TemplateContextProcessorCallable = (
    t.Callable[[], dict[str, t.Any]] | t.Callable[[], t.Awaitable[dict[str, t.Any]]]
)
TemplateFilterCallable = t.Callable[..., t.Any]
TemplateGlobalCallable = t.Callable[..., t.Any]
TemplateTestCallable = t.Callable[..., bool]
URLDefaultCallable = t.Callable[[str, dict[str, t.Any]], None]
URLValuePreprocessorCallable = t.Callable[[str | None, dict[str, t.Any] | None], None]

# This should take Exception, but that either breaks typing the argument
# with a specific exception, or decorating multiple times with different
# exceptions (and using a union type on the argument).
# https://github.com/pallets/flask/issues/4095
# https://github.com/pallets/flask/issues/4295
# https://github.com/pallets/flask/issues/4297
ErrorHandlerCallable = (
    t.Callable[[t.Any], ResponseReturnValue]
    | t.Callable[[t.Any], t.Awaitable[ResponseReturnValue]]
)

RouteCallable = (
    t.Callable[..., ResponseReturnValue]
    | t.Callable[..., t.Awaitable[ResponseReturnValue]]
)


# Converting values

A `basic_json` value stores JSON data, but most of the time you want to move that data into ordinary C++ types (an
`#!cpp int`, a `#!cpp std::string`, a `#!cpp std::vector`, or one of your own structs) and back. This page describes how
these conversions work.

## Getting values out

The [`get`](../api/basic_json/get.md) function template returns a copy of the stored value converted to the requested
type:

```cpp
json j = R"({"name": "Mary", "age": 42, "hobbies": ["hiking", "reading"]})"_json;

auto name = j["name"].get<std::string>();               // "Mary"
auto age  = j["age"].get<int>();                        // 42
auto hobbies = j["hobbies"].get<std::vector<std::string>>();  // {"hiking", "reading"}
```

!!! note "Getting a string without quotes"

    A frequent point of confusion: use [`get`](../api/basic_json/get.md), **not** [`dump`](serialization.md), to read a
    string value. `#!cpp j["name"].get<std::string>()` yields `#!cpp Mary`, whereas `#!cpp j["name"].dump()` yields the
    JSON text `#!cpp "Mary"` (**with** quotes), because `dump` always produces a JSON text.

Alternatively, [`get_to`](../api/basic_json/get_to.md) writes into an existing variable and deduces the target type,
which avoids repeating it:

??? example

    ```cpp
    --8<-- "examples/get_to.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/get_to.output"
    ```

The library already knows how to convert to and from the scalar types and the STL containers (such as
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
object back to a `#!cpp std::map` or a JSON array back to a `#!cpp std::vector` therefore works without any extra code:

```cpp
json j = {{"one", 1}, {"two", 2}};
auto m = j.get<std::map<std::string, int>>();  // {{"one", 1}, {"two", 2}}
```

`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:

```cpp
json j = {1.0, "hello", 42};
auto t = j.get<std::tuple<double, std::string, int>>();  // {1.0, "hello", 42}
```

!!! info "Extracting references into a tuple"

    A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
    then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
    rather than a tuple of copies:

    ```cpp
    json j = {1.0, "hello"};
    auto refs = j.get<std::tuple<double&, std::string&>>();
    std::get<1>(refs) = "world";  // modifies j[1] in place
    ```

    A referenced element must name the type the library actually *stores* — one of [`boolean_t`](../api/basic_json/boolean_t.md),
    [`number_integer_t`](../api/basic_json/number_integer_t.md), [`number_unsigned_t`](../api/basic_json/number_unsigned_t.md),
    [`number_float_t`](../api/basic_json/number_float_t.md), [`string_t`](../api/basic_json/string_t.md),
    [`binary_t`](../api/basic_json/binary_t.md), [`array_t`](../api/basic_json/array_t.md), or
    [`object_t`](../api/basic_json/object_t.md). There is nothing else to refer to, so a reference to any other type is a
    compile error even when a conversion would exist: `#!cpp std::tuple<int&>` is rejected, because the library stores a
    `#!cpp number_integer_t` (`#!cpp std::int64_t` by default) and not an `#!cpp int`. This restriction applies only to
    reference elements — a plain `#!cpp std::tuple<int>` converts by value as usual.

## Implicit conversions

By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:

```cpp
json j = "Hello";
std::string s = j;   // implicit conversion, same as j.get<std::string>()
```

Implicit conversions are convenient but can be surprising (for example, in overload resolution or with `auto`). They can
be disabled by defining [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) to `#!cpp 0`,
which forces the explicit `get` form and can catch unintended conversions at compile time.

!!! warning "Conversions do not range-check numbers"

    Just like C++ itself, the `get` family performs numeric conversions without range checks — retrieving a
    floating-point value as an integer truncates it, and narrowing conversions may overflow. See
    [number conversion](types/number_handling.md#number-conversion) for details and how to guard against it.

!!! warning "std::optional direct construction from JSON null throws"

    Constructing or assigning `std::optional<T>` directly from a JSON value does not correctly produce
    `std::nullopt` for a JSON `null`:

    ```cpp
    json j_null;
    std::optional<std::string> opt = j_null;  // ❌ throws type_error 302
    ```

    This is due to C++ language rules: `std::optional<T>` has its own converting constructor that is chosen over
    `basic_json::operator T()` when both are viable. Use `get<std::optional<T>>()` or `get_to()` instead:

    ```cpp
    auto opt = j_null.get<std::optional<std::string>>();  // ✅ std::nullopt
    j_null.get_to(opt);                                     // ✅ std::nullopt
    ```

!!! warning "`static_cast` and `get<std::optional<T>>()` are not guaranteed equivalent"

    `operator ValueType()` (used by `static_cast` and implicit conversions) intentionally excludes
    `std::optional<T>` from delegating to `get<T>()`, to avoid a constructor ambiguity with
    `std::optional<T>`'s own converting constructor from `basic_json`. As a result,
    `static_cast<std::optional<T>>(json_value)` goes through `std::optional<T>`'s own converting
    constructor rather than through `get<std::optional<T>>()`, which can behave differently -- for example,
    with a custom `adl_serializer<std::optional<T>>` specialization. Prefer `get<std::optional<T>>()`/`get_to()`
    over `static_cast` for optional types.

!!! warning "Converting to a fixed-size destination does not check the array size"

    Some destination types have a size that is fixed by their C++ type rather than by the JSON value:
    `#!cpp std::pair<A, B>`, `#!cpp std::tuple<Ts...>`, `#!cpp std::array<T, N>`, C arrays `#!cpp T[N]`, and
    `#!cpp std::map`/`#!cpp std::unordered_map` with a non-string key type (which is read from an array of
    two-element arrays). All of them read exactly as many elements as they need via
    [`at`](../api/basic_json/at.md) and **never compare the JSON array's size to that number**. The two
    mismatch directions therefore behave differently:

    - The JSON array has **too many** elements: the surplus is **silently discarded**, and no exception is
      thrown.
    - The JSON array has **too few** elements: `at` throws
      [`out_of_range.401`](../home/exceptions.md#jsonexceptionout_of_range401) for the first missing index --
      an out-of-range error, not a [`type_error`](../home/exceptions.md#type-errors), even though the cause
      is a shape mismatch.

    ```cpp
    json j = {1, 2, 3, 4, 5};

    auto a = j.get<std::array<int, 3>>();       // {1, 2, 3} -- elements 4 and 5 silently dropped
    auto p = j.get<std::pair<int, int>>();      // (1, 2)    -- elements 3, 4, and 5 silently dropped

    json k = {1};
    auto q = k.get<std::pair<int, int>>();      // ❌ throws out_of_range.401
    ```

    If a size mismatch is an error in your application, check the size yourself before converting.

## Omitting a field when serializing `std::optional`

By default, `to_json` for `std::optional<T>` writes either the value or `#!json null` -- there is no built-in way
to make a field disappear from the serialized object entirely when the `std::optional` is `std::nullopt`. Because
a specialization of `adl_serializer<std::optional<T>>` only controls how the *value* is converted (it cannot
prevent the containing object's `to_json` from inserting the key in the first place), omission has to be
implemented in the *containing* type's `to_json`:

```cpp
struct person {
    std::string name;
    std::optional<int> age;
};

void to_json(json& j, const person& p) {
    j = json{{"name", p.name}};
    if (p.age) {
        j["age"] = *p.age;  // key is only inserted when the optional has a value
    }
}
```

## Putting values in

The reverse direction works the same way: assigning or constructing a `json` from a C++ value converts it to JSON.

```cpp
std::vector<int> numbers = {1, 2, 3};
json j = numbers;   // [1,2,3]
```

!!! info "Constructing from a C++20 range view"

    A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
    of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:

    ```cpp
    std::vector<int> nums{1, 2, 37, 42, 21};
    auto filtered = nums | std::views::filter([](int i) { return i > 10; });
    json j(filtered);   // [37,42,21]
    ```

    This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
    to incomplete C++20 ranges support there.

## Your own types

The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
`to_json`/`from_json` functions (or use one of the convenience macros). This is described in detail on the
[arbitrary types conversions](arbitrary_types.md) page. Enums can be mapped to strings as described in
[specializing enum conversion](enum_conversion.md).

## See also

- [`get`](../api/basic_json/get.md) - get a copy converted to a given type
- [`get_to`](../api/basic_json/get_to.md) - convert into an existing variable
- [`get_ref`](../api/basic_json/get_ref.md) / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions


from celery import Celery
from celery import Task
from flask import Flask
from flask import render_template

def create_app() -> Flask:
    app = Flask(__name__)
    app.config.from_mapping(
        CELERY={
            "broker_url": "redis://localhost",
            "result_backend": "redis://localhost",
            "task_ignore_result": True,
        },
    )
    app.config.from_prefixed_env()
    celery_init_app(app)

    @app.route("/")
    def index() -> str:
        return render_template("index.html")

    from . import views

    app.register_blueprint(views.bp)
    return app

def celery_init_app(app: Flask) -> Celery:
    class FlaskTask(Task):
        def __call__(self, *args: object, **kwargs: object) -> object:
            with app.app_context():
                return self.run(*args, **kwargs)

    celery_app = Celery(app.name, task_cls=FlaskTask)
    celery_app.config_from_object(app.config["CELERY"])
    celery_app.set_default()
    app.extensions["celery"] = celery_app
    return celery_app


/*!
This module benchmarks the glob implementation. For benchmarks on the ripgrep
tool itself, see the benchsuite directory.
*/
#![feature(test)]

extern crate test;

use globset::{Candidate, Glob, GlobMatcher, GlobSet, GlobSetBuilder};

const EXT: &'static str = "some/a/bigger/path/to/the/crazy/needle.txt";
const EXT_PAT: &'static str = "*.txt";

const SHORT: &'static str = "some/needle.txt";
const SHORT_PAT: &'static str = "some/**/needle.txt";

const LONG: &'static str = "some/a/bigger/path/to/the/crazy/needle.txt";
const LONG_PAT: &'static str = "some/**/needle.txt";

fn new_glob(pat: &str) -> glob::Pattern {
    glob::Pattern::new(pat).unwrap()
}

fn new_reglob(pat: &str) -> GlobMatcher {
    Glob::new(pat).unwrap().compile_matcher()
}

fn new_reglob_many(pats: &[&str]) -> GlobSet {
    let mut builder = GlobSetBuilder::new();
    for pat in pats {
        builder.add(Glob::new(pat).unwrap());
    }
    builder.build().unwrap()
}

#[bench]
fn ext_glob(b: &mut test::Bencher) {
    let pat = new_glob(EXT_PAT);
    b.iter(|| assert!(pat.matches(EXT)));
}

#[bench]
fn ext_regex(b: &mut test::Bencher) {
    let set = new_reglob(EXT_PAT);
    let cand = Candidate::new(EXT);
    b.iter(|| assert!(set.is_match_candidate(&cand)));
}

#[bench]
fn short_glob(b: &mut test::Bencher) {
    let pat = new_glob(SHORT_PAT);
    b.iter(|| assert!(pat.matches(SHORT)));
}

#[bench]
fn short_regex(b: &mut test::Bencher) {
    let set = new_reglob(SHORT_PAT);
    let cand = Candidate::new(SHORT);
    b.iter(|| assert!(set.is_match_candidate(&cand)));
}

#[bench]
fn long_glob(b: &mut test::Bencher) {
    let pat = new_glob(LONG_PAT);
    b.iter(|| assert!(pat.matches(LONG)));
}

#[bench]
fn long_regex(b: &mut test::Bencher) {
    let set = new_reglob(LONG_PAT);
    let cand = Candidate::new(LONG);
    b.iter(|| assert!(set.is_match_candidate(&cand)));
}

const MANY_SHORT_GLOBS: &'static [&'static str] = &[
    // Taken from a random .gitignore on my system.
    ".*.swp",
    "tags",
    "target",
    "*.lock",
    "tmp",
    "*.csv",
    "*.fst",
    "*-got",
    "*.csv.idx",
    "words",
    "98m*",
    "dict",
    "test",
    "months",
];

const MANY_SHORT_SEARCH: &'static str = "98m-blah.csv.idx";

#[bench]
fn many_short_glob(b: &mut test::Bencher) {
    let pats: Vec<_> = MANY_SHORT_GLOBS.iter().map(|&s| new_glob(s)).collect();
    b.iter(|| {
        let mut count = 0;
        for pat in &pats {
            if pat.matches(MANY_SHORT_SEARCH) {
                count += 1;
            }
        }
        assert_eq!(2, count);
    })
}

#[bench]
fn many_short_regex_set(b: &mut test::Bencher) {
    let set = new_reglob_many(MANY_SHORT_GLOBS);
    b.iter(|| assert_eq!(2, set.matches(MANY_SHORT_SEARCH).iter().count()));
}


# <small>nlohmann::basic_json::</small>is_null

```cpp
constexpr bool is_null() const noexcept;
```

This function returns `#!cpp true` if and only if the JSON value is `#!json null`.

## Return value

`#!cpp true` if type is `#!json null`, `#!cpp false` otherwise.

## Exception safety

No-throw guarantee: this member function never throws exceptions.

## Complexity

Constant.

## Examples

??? example

    The following code exemplifies `is_null()` for all JSON types.

    ```cpp
    --8<-- "examples/is_null.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/is_null.output"
    ```

## Version history

- Added in version 1.0.0.


---
title: Vite 4.0 is out!
author:
  name: The Vite Team
date: 2022-12-09
sidebar: false
head:
  - - meta
    - property: og:type
      content: website
  - - meta
    - property: og:title
      content: Announcing Vite 4
  - - meta
    - property: og:image
      content: https://vite.dev/og-image-announcing-vite4.webp
  - - meta
    - property: og:url
      content: https://vite.dev/blog/announcing-vite4
  - - meta
    - property: og:description
      content: Vite 4 Release Announcement
  - - meta
    - name: twitter:card
      content: summary_large_image
---

# Vite 4.0 is out!

_December 9, 2022_ - Check out the [Vite 5.0 announcement](./announcing-vite5.md)

Vite 3 [was released](./announcing-vite3.md) five months ago. npm downloads per week have gone from 1 million to 2.5 million since then. The ecosystem has matured too, and continues to grow. In this year's [Jamstack Conf survey](https://twitter.com/vite_js/status/1589665610119585793), usage among the community jumped from 14% to 32% while keeping a high 9.7 satisfaction score. We saw the stable releases of [Astro 1.0](https://astro.build/), [Nuxt 3](https://v3.nuxtjs.org/), and other Vite-powered frameworks that are innovating and collaborating: [SvelteKit](https://kit.svelte.dev/), [Solid Start](https://www.solidjs.com/blog/introducing-solidstart), [Qwik City](https://qwik.builder.io/qwikcity/overview/). Storybook announced first-class support for Vite as one of its main features for [Storybook 7.0](https://storybook.js.org/blog/first-class-vite-support-in-storybook/). Deno now [supports Vite](https://www.youtube.com/watch?v=Zjojo9wdvmY). [Vitest](https://vitest.dev) adoption is exploding, it will soon represent half of Vite's npm downloads. Nx is also investing in the ecosystem, and [officially supports Vite](https://nx.dev/packages/vite).

[![Vite 4 Ecosystem](../images/ecosystem-vite4.webp)](https://viteconf.org/2022/replay)

As a showcase of the growth Vite and related projects have experienced, the Vite ecosystem gathered on October 11th at [ViteConf 2022](https://viteconf.org/2022/replay). We saw representatives from the main web framework and tools tell stories of innovation and collaboration. And in a symbolic move, the Rollup team choose that exact day to release [Rollup 3](https://rollupjs.org).

Today, the Vite [team](https://vite.dev/team) with the help of our ecosystem partners, is happy to announce the release of Vite 4, powered during build time by Rollup 3. We've worked with the ecosystem to ensure a smooth upgrade path for this new major. Vite is now using [Rollup 3](https://github.com/vitejs/vite/issues/9870), which allowed us to simplify Vite's internal asset handling and has many improvements. See the [Rollup 3 release notes here](https://github.com/rollup/rollup/releases/tag/v3.0.0).

![Vite 4 Announcement Cover Image](/og-image-announcing-vite4.webp)

Quick links:

- [Docs](/)
- [Migration Guide](https://v4.vite.dev/guide/migration.html)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#400-2022-12-09)

Docs in other languages:

- [简体中文](https://cn.vite.dev/)
- [日本語](https://ja.vite.dev/)
- [Español](https://es.vite.dev/)

If you recently started using Vite, we suggest reading the [Why Vite Guide](https://vite.dev/guide/why.html) and checking out [the Getting Started](https://vite.dev/guide/) and [Features guide](https://vite.dev/guide/features). If you want to get involved, contributions are welcome at [GitHub](https://github.com/vitejs/vite). Almost [700 collaborators](https://github.com/vitejs/vite/graphs/contributors) have contributed to Vite. Follow the updates on [Twitter](https://twitter.com/vite_js) and [Mastodon](https://webtoo.ls/@vite), or collaborate with others on our [Discord community](https://chat.vite.dev).

## Start playing with Vite 4

Use `pnpm create vite` to scaffold a Vite project with your preferred framework, or open a started template online to play with Vite 4 using [vite.new](https://vite.new).

You can also run `pnpm create vite-extra` to get access to templates from other frameworks and runtimes (Solid, Deno, SSR, and library starters). `create vite-extra` templates are also available when you run `create vite` under the `Others` option.

Note that Vite starter templates are intended to be used as a playground to test Vite with different frameworks. When building your next project, we recommend reaching out to the starters recommended by each framework. Some frameworks now redirect in `create vite` to their starters too (`create-vue` and `Nuxt 3` for Vue, and `SvelteKit` for Svelte).

## New React plugin using SWC during development

[SWC](https://swc.rs/) is now a mature replacement for [Babel](https://babeljs.io/), especially in the context of React projects. SWC's React Fast Refresh implementation is a lot faster than Babel, and for some projects, it is now a better alternative. From Vite 4, two plugins are available for React projects with different tradeoffs. We believe that both approaches are worth supporting at this point, and we'll continue to explore improvements to both plugins in the future.

### @vitejs/plugin-react

[@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react) is a plugin that uses esbuild and Babel, achieving fast HMR with a small package footprint and the flexibility of being able to use the Babel transform pipeline.

### @vitejs/plugin-react-swc (new)

[@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) is a new plugin that uses esbuild during build, but replaces Babel with SWC during development. For big projects that don't require non-standard React extensions, cold start and Hot Module Replacement (HMR) can be significantly faster.

## Browser Compatibility

The modern browser build now targets `safari14` by default for wider ES2020 compatibility. This means that modern builds can now use [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) and that the [nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing) isn't transpiled anymore. If you need to support older browsers, you can add [`@vitejs/plugin-legacy`](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) as usual.

## Importing CSS as a String

In Vite 3, importing the default export of a `.css` file could introduce a double loading of CSS.

```ts
import cssString from './global.css'
```

This double loading could occur since a `.css` file will be emitted and it's likely that the CSS string will also be used by the application code — for example, injected by the framework runtime. From Vite 4, the `.css` default export [has been deprecated](https://github.com/vitejs/vite/issues/11094). The `?inline` query suffix modifier needs to be used in this case, as that doesn't emit the imported `.css` styles.

```ts
import stuff from './global.css?inline'
```

Learn more in the [Migration Guide](https://v4.vite.dev/guide/migration.html).

## Environment Variables

Vite now uses `dotenv` 16 and `dotenv-expand` 9 (previously `dotenv` 14 and `dotenv-expand` 5). If you have a value including `#` or `` ` ``, you will need to wrap them with quotes.

```diff
-VITE_APP=ab#cd`ef
+VITE_APP="ab#cd`ef"
```

For more details, see the [`dotenv`](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) and [`dotenv-expand` changelog](https://github.com/motdotla/dotenv-expand/blob/master/CHANGELOG.md).

## Other Features

- CLI Shortcuts (press `h` during dev to see them all) ([#11228](https://github.com/vitejs/vite/pull/11228))
- Support for patch-package when pre bundling dependencies ([#10286](https://github.com/vitejs/vite/issues/10286))
- Cleaner build logs output ([#10895](https://github.com/vitejs/vite/issues/10895)) and switch to `kB` to align with browser dev tools ([#10982](https://github.com/vitejs/vite/issues/10982))
- Improved error messages during SSR ([#11156](https://github.com/vitejs/vite/issues/11156))

## Reduced Package Size

Vite cares about its footprint, to speed up installation, especially in the use case of playgrounds for documentation and reproductions. And once more, this major brings improvements in Vite's package size. Vite 4 install size is 23% smaller compared to vite 3.2.5 (14.1 MB vs 18.3 MB).

## Upgrades to Vite Core

[Vite Core](https://github.com/vitejs/vite) and [vite-ecosystem-ci](https://github.com/vitejs/vite-ecosystem-ci) continue to evolve to provide a better experience to maintainers and collaborators and to ensure that Vite development scales to cope with the growth in the ecosystem.

### Framework plugins out of core

[`@vitejs/plugin-vue`](https://github.com/vitejs/vite-plugin-vue) and [`@vitejs/plugin-react`](https://github.com/vitejs/vite-plugin-react) have been part of Vite core monorepo since the first versions of Vite. This helped us to get a close feedback loop when making changes as we were getting both Core and the plugins tested and released together. With [vite-ecosystem-ci](https://github.com/vitejs/vite-ecosystem-ci) we can get this feedback with these plugins developed on independent repositories, so from Vite 4, [they have been moved out of the Vite core monorepo](https://github.com/vitejs/vite/pull/11158). This is meaningful for Vite's framework-agnostic story and will allow us to build independent teams to maintain each of the plugins. If you have bugs to report or features to request, please create issues on the new repositories moving forward: [`vitejs/vite-plugin-vue`](https://github.com/vitejs/vite-plugin-vue) and [`vitejs/vite-plugin-react`](https://github.com/vitejs/vite-plugin-react).

### vite-ecosystem-ci improvements

[vite-ecosystem-ci](https://github.com/vitejs/vite-ecosystem-ci) extends Vite's CI by providing on-demand status reports on the state of the CIs of [most major downstream projects](https://github.com/vitejs/vite-ecosystem-ci/tree/main/tests). We run vite-ecosystem-ci three times a week against Vite's main branch and receive timely reports before introducing a regression. Vite 4 will soon be compatible with most projects using Vite, which already prepared branches with the needed changes and will be releasing them in the next few days. We are also able to run vite-ecosystem-ci on-demand on PRs using `/ecosystem-ci run` in a comment, allowing us to know [the effect of changes](https://github.com/vitejs/vite/pull/11269#issuecomment-1343365064) before they hit main.

## Acknowledgments

Vite 4 wouldn't be possible without uncountable hours of work by Vite contributors, many of them maintainers of downstream projects and plugins, and the efforts of the [Vite Team](/team). All of us have worked together to improve Vite's DX once more, for every framework and app using it. We're grateful to be able to improve a common base for such a vibrant ecosystem.

We're also thankful to individuals and companies sponsoring the Vite team, and companies investing directly in Vite's future: [@antfu7](https://twitter.com/antfu7)'s work on Vite and the ecosystem is part of his job at [Nuxt Labs](https://nuxtlabs.com/), [Astro](https://astro.build) is funding [@bluwyoo](https://twitter.com/bluwyoo)'s Vite core work, and [StackBlitz](https://stackblitz.com/) hires [@patak_dev](https://twitter.com/patak_dev) to work full time on Vite.

## Next steps

Our immediate focus would be on triaging newly opened issues to avoid disruption by possible regressions. If you would like to get involved and help us improve Vite, we suggest starting with issues triaging. Join [our Discord](https://chat.vite.dev) and reach out on the `#contributing` channel. Polish our `#docs` story, and `#help` others. We need to continue to build a helpful and welcoming community for the next wave of users, as Vite's adoption continues to grow.

There are a lot of open fronts to keep improving the DX of everyone that has chosen Vite to power their frameworks and develop their apps. Onwards!


Nested Environment Variables

import.meta.env.MODE:

  import.meta.env.VITE_EFFECTIVE_MODE_FILE_NAME:

Empty import.meta.env.VITE_PARENT_ENV:


import { readFileSync } from 'node:fs'
import { expect, test } from 'vitest'
import deepJson from 'vue/package.json'
import testJson from '../../test.json'
import hmrJson from '../../hmr.json'
import { editFile, isBundled, isBundledDev, isServe, page } from '~utils'

const stringified = JSON.stringify(testJson)
const deepStringified = JSON.stringify(deepJson)
const hmrStringified = JSON.stringify(hmrJson)

test('default import', async () => {
  expect(await page.textContent('.full')).toBe(stringified)
})

test('named import', async () => {
  expect(await page.textContent('.named')).toBe(testJson.hello)
})

test('deep import', async () => {
  expect(await page.textContent('.deep-full')).toBe(deepStringified)
})

test('named deep import', async () => {
  expect(await page.textContent('.deep-named')).toBe(deepJson.name)
})

test('dynamic import', async () => {
  expect(await page.textContent('.dynamic')).toBe(stringified)
})

test('dynamic import, named', async () => {
  expect(await page.textContent('.dynamic-named')).toBe(testJson.hello)
})

// bundled dev: fetch('/test.json') asks the server for a file in the project
// root. That file is not in the bundle, and bundled dev serves only the bundle.
test.skipIf(isBundledDev)('fetch', async () => {
  expect(await page.textContent('.fetch')).toBe(stringified)
})

test('?url', async () => {
  expect(await page.textContent('.url')).toMatch(
    isBundled ? 'data:application/json' : '/test.json',
  )
})

test('?raw', async () => {
  expect(await page.textContent('.raw')).toBe(
    readFileSync(require.resolve('../../test.json'), 'utf-8'),
  )
})

test('require(json) returns object without default export', async () => {
  expect(await page.textContent('.require')).toBe(JSON.stringify({ 200: 'ok' }))
})

test.runIf(isServe)('should full reload', async () => {
  expect(await page.textContent('.hmr')).toBe(hmrStringified)

  editFile('hmr.json', (code) =>
    code.replace('"this is hmr json"', '"this is hmr update json"'),
  )
  await expect
    .poll(() => page.textContent('.hmr'))
    .toMatch('"this is hmr update json"')
})


//     __ _____ _____ _____
//  __|  |   __|     |   | |  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("modifiers")
{
    SECTION("clear()")
    {
        SECTION("boolean")
        {
            json j = true;
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::boolean));
            CHECK(j == json(k.type()));
        }

        SECTION("string")
        {
            json j = "hello world";
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::string));
            CHECK(j == json(k.type()));
        }

        SECTION("array")
        {
            SECTION("empty array")
            {
                json j = json::array();
                json const k = j;

                j.clear();
                CHECK(j.empty());
                CHECK(j == json(json::value_t::array));
                CHECK(j == json(k.type()));
            }

            SECTION("filled array")
            {
                json j = {1, 2, 3};
                json const k = j;

                j.clear();
                CHECK(j.empty());
                CHECK(j == json(json::value_t::array));
                CHECK(j == json(k.type()));
            }
        }

        SECTION("object")
        {
            SECTION("empty object")
            {
                json j = json::object();
                json const k = j;

                j.clear();
                CHECK(j.empty());
                CHECK(j == json(json::value_t::object));
                CHECK(j == json(k.type()));
            }

            SECTION("filled object")
            {
                json j = {{"one", 1}, {"two", 2}, {"three", 3}};
                json const k = j;

                j.clear();
                CHECK(j.empty());
                CHECK(j == json(json::value_t::object));
                CHECK(j == json(k.type()));
            }
        }

        SECTION("binary")
        {
            SECTION("empty binary")
            {
                json j = json::binary({});
                json const k = j;

                j.clear();
                CHECK(!j.empty());
                CHECK(j == json(json::value_t::binary));
                CHECK(j == json(k.type()));
            }

            SECTION("filled binary")
            {
                json j = json::binary({1, 2, 3, 4, 5});
                json const k = j;

                j.clear();
                CHECK(!j.empty());
                CHECK(j == json(json::value_t::binary));
                CHECK(j == json(k.type()));
            }
        }

        SECTION("number (integer)")
        {
            json j = 23;
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::number_integer));
            CHECK(j == json(k.type()));
        }

        SECTION("number (unsigned)")
        {
            json j = 23u;
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::number_integer));
            CHECK(j == json(k.type()));
        }

        SECTION("number (float)")
        {
            json j = 23.42;
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::number_float));
            CHECK(j == json(k.type()));
        }

        SECTION("null")
        {
            json j = nullptr;
            json const k = j;

            j.clear();
            CHECK(j == json(json::value_t::null));
            CHECK(j == json(k.type()));
        }
    }

    SECTION("push_back()")
    {
        SECTION("to array")
        {
            SECTION("json&&")
            {
                SECTION("null")
                {
                    json j;
                    j.push_back(1);
                    j.push_back(2);
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2}));
                }

                SECTION("array")
                {
                    json j = {1, 2, 3};
                    j.push_back("Hello");
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2, 3, "Hello"}));
                }

                SECTION("other type")
                {
                    json j = 1;
                    CHECK_THROWS_WITH_AS(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
                }
            }

            SECTION("const json&")
            {
                SECTION("null")
                {
                    json j;
                    json const k(1);
                    j.push_back(k);
                    j.push_back(k);
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 1}));
                }

                SECTION("array")
                {
                    json j = {1, 2, 3};
                    json const k("Hello");
                    j.push_back(k);
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2, 3, "Hello"}));
                }

                SECTION("other type")
                {
                    json j = 1;
                    json const k("Hello");
                    CHECK_THROWS_WITH_AS(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
                }
            }
        }

        SECTION("to object")
        {
            SECTION("null")
            {
                json j;
                j.push_back(json::object_t::value_type({"one", 1}));
                j.push_back(json::object_t::value_type({"two", 2}));
                CHECK(j.type() == json::value_t::object);
                CHECK(j.size() == 2);
                CHECK(j["one"] == json(1));
                CHECK(j["two"] == json(2));
            }

            SECTION("object")
            {
                json j(json::value_t::object);
                j.push_back(json::object_t::value_type({"one", 1}));
                j.push_back(json::object_t::value_type({"two", 2}));
                CHECK(j.size() == 2);
                CHECK(j["one"] == json(1));
                CHECK(j["two"] == json(2));
            }

            SECTION("other type")
            {
                json j = 1;
                json const k("Hello");
                CHECK_THROWS_WITH_AS(j.push_back(json::object_t::value_type({"one", 1})), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
            }
        }

        SECTION("with initializer_list")
        {
            SECTION("null")
            {
                json j;
                j.push_back({"foo", "bar"});
                CHECK(j == json::array({{"foo", "bar"}}));

                json k;
                k.push_back({1, 2, 3});
                CHECK(k == json::array({{1, 2, 3}}));
            }

            SECTION("array")
            {
                json j = {1, 2, 3};
                j.push_back({"foo", "bar"});
                CHECK(j == json({1, 2, 3, {"foo", "bar"}}));

                json k = {1, 2, 3};
                k.push_back({1, 2, 3});
                CHECK(k == json({1, 2, 3, {1, 2, 3}}));
            }

            SECTION("object")
            {
                json j = {{"key1", 1}};
                j.push_back({"key2", "bar"});
                CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));

                // invalid values (no string/val pair)
                CHECK_THROWS_WITH_AS(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
                CHECK_THROWS_WITH_AS(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
                CHECK_THROWS_WITH_AS(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
            }
        }
    }

    SECTION("emplace_back()")
    {
        SECTION("to array")
        {
            SECTION("null")
            {
                json j;
                auto& x1 = j.emplace_back(1);
                CHECK(x1 == 1);
                auto& x2 = j.emplace_back(2);
                CHECK(x2 == 2);
                CHECK(j.type() == json::value_t::array);
                CHECK(j == json({1, 2}));
            }

            SECTION("array")
            {
                json j = {1, 2, 3};
                auto& x = j.emplace_back("Hello");
                CHECK(x == "Hello");
                CHECK(j.type() == json::value_t::array);
                CHECK(j == json({1, 2, 3, "Hello"}));
            }

            SECTION("multiple values")
            {
                json j;
                auto& x = j.emplace_back(3, "foo");
                CHECK(x == json({"foo", "foo", "foo"}));
                CHECK(j.type() == json::value_t::array);
                CHECK(j == json({{"foo", "foo", "foo"}}));
            }
        }

        SECTION("other type")
        {
            json j = 1;
            CHECK_THROWS_WITH_AS(j.emplace_back("Hello"), "[json.exception.type_error.311] cannot use emplace_back() with number", json::type_error&);
        }
    }

    SECTION("emplace()")
    {
        SECTION("to object")
        {
            SECTION("null")
            {
                // start with a null value
                json j;

                // add a new key
                auto res1 = j.emplace("foo", "bar");
                CHECK(res1.second == true);
                CHECK(*res1.first == "bar");

                // the null value is changed to an object
                CHECK(j.type() == json::value_t::object);

                // add a new key
                auto res2 = j.emplace("baz", "bam");
                CHECK(res2.second == true);
                CHECK(*res2.first == "bam");

                // we try to insert at given key - no change
                auto res3 = j.emplace("baz", "bad");
                CHECK(res3.second == false);
                CHECK(*res3.first == "bam");

                // the final object
                CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
            }

            SECTION("object")
            {
                // start with an object
                json j = {{"foo", "bar"}};

                // add a new key
                auto res1 = j.emplace("baz", "bam");
                CHECK(res1.second == true);
                CHECK(*res1.first == "bam");

                // add an existing key
                auto res2 = j.emplace("foo", "bad");
                CHECK(res2.second == false);
                CHECK(*res2.first == "bar");

                // check final object
                CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
            }
        }

        SECTION("other type")
        {
            json j = 1;
            CHECK_THROWS_WITH_AS(j.emplace("foo", "bar"), "[json.exception.type_error.311] cannot use emplace() with number", json::type_error&);
        }
    }

    SECTION("operator+=")
    {
        SECTION("to array")
        {
            SECTION("json&&")
            {
                SECTION("null")
                {
                    json j;
                    j += 1;
                    j += 2;
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2}));
                }

                SECTION("array")
                {
                    json j = {1, 2, 3};
                    j += "Hello";
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2, 3, "Hello"}));
                }

                SECTION("other type")
                {
                    json j = 1;
                    CHECK_THROWS_WITH_AS(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
                }
            }

            SECTION("const json&")
            {
                SECTION("null")
                {
                    json j;
                    json const k(1);
                    j += k;
                    j += k;
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 1}));
                }

                SECTION("array")
                {
                    json j = {1, 2, 3};
                    json const k("Hello");
                    j += k;
                    CHECK(j.type() == json::value_t::array);
                    CHECK(j == json({1, 2, 3, "Hello"}));
                }

                SECTION("other type")
                {
                    json j = 1;
                    json const k("Hello");
                    CHECK_THROWS_WITH_AS(j += k, "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
                }
            }
        }

        SECTION("to object")
        {
            SECTION("null")
            {
                json j;
                j += json::object_t::value_type({"one", 1});
                j += json::object_t::value_type({"two", 2});
                CHECK(j.type() == json::value_t::object);
                CHECK(j.size() == 2);
                CHECK(j["one"] == json(1));
                CHECK(j["two"] == json(2));
            }

            SECTION("object")
            {
                json j(json::value_t::object);
                j += json::object_t::value_type({"one", 1});
                j += json::object_t::value_type({"two", 2});
                CHECK(j.size() == 2);
                CHECK(j["one"] == json(1));
                CHECK(j["two"] == json(2));
            }

            SECTION("other type")
            {
                json j = 1;
                json const k("Hello");
                CHECK_THROWS_WITH_AS(j += json::object_t::value_type({"one", 1}), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
            }
        }

        SECTION("with initializer_list")
        {
            SECTION("null")
            {
                json j;
                j += {"foo", "bar"};
                CHECK(j == json::array({{"foo", "bar"}}));

                json k;
                k += {1, 2, 3};
                CHECK(k == json::array({{1, 2, 3}}));
            }

            SECTION("array")
            {
                json j = {1, 2, 3};
                j += {"foo", "bar"};
                CHECK(j == json({1, 2, 3, {"foo", "bar"}}));

                json k = {1, 2, 3};
                k += {1, 2, 3};
                CHECK(k == json({1, 2, 3, {1, 2, 3}}));
            }

            SECTION("object")
            {
                json j = {{"key1", 1}};
                j += {"key2", "bar"};
                CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));

                json k = {{"key1", 1}};
                CHECK_THROWS_WITH_AS((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
            }
        }
    }

    SECTION("insert()")
    {
        json j_array = {1, 2, 3, 4};
        json j_value = 5;

        SECTION("value at position")
        {
            SECTION("insert before begin()")
            {
                auto it = j_array.insert(j_array.begin(), j_value);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK(j_array.begin() == it);
                CHECK(j_array == json({5, 1, 2, 3, 4}));
            }

            SECTION("insert in the middle")
            {
                auto it = j_array.insert(j_array.begin() + 2, j_value);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK((it - j_array.begin()) == 2);
                CHECK(j_array == json({1, 2, 5, 3, 4}));
            }

            SECTION("insert before end()")
            {
                auto it = j_array.insert(j_array.end(), j_value);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK((j_array.end() - it) == 1);
                CHECK(j_array == json({1, 2, 3, 4, 5}));
            }
        }

        SECTION("rvalue at position")
        {
            SECTION("insert before begin()")
            {
                auto it = j_array.insert(j_array.begin(), 5);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK(j_array.begin() == it);
                CHECK(j_array == json({5, 1, 2, 3, 4}));
            }

            SECTION("insert in the middle")
            {
                auto it = j_array.insert(j_array.begin() + 2, 5);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK((it - j_array.begin()) == 2);
                CHECK(j_array == json({1, 2, 5, 3, 4}));
            }

            SECTION("insert before end()")
            {
                auto it = j_array.insert(j_array.end(), 5);
                CHECK(j_array.size() == 5);
                CHECK(*it == j_value);
                CHECK((j_array.end() - it) == 1);
                CHECK(j_array == json({1, 2, 3, 4, 5}));
            }
        }

        SECTION("copies at position")
        {
            SECTION("insert before begin()")
            {
                auto it = j_array.insert(j_array.begin(), 3, 5);
                CHECK(j_array.size() == 7);
                CHECK(*it == j_value);
                CHECK(j_array.begin() == it);
                CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4}));
            }

            SECTION("insert in the middle")
            {
                auto it = j_array.insert(j_array.begin() + 2, 3, 5);
                CHECK(j_array.size() == 7);
                CHECK(*it == j_value);
                CHECK((it - j_array.begin()) == 2);
                CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4}));
            }

            SECTION("insert before end()")
            {
                auto it = j_array.insert(j_array.end(), 3, 5);
                CHECK(j_array.size() == 7);
                CHECK(*it == j_value);
                CHECK((j_array.end() - it) == 3);
                CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5}));
            }

            SECTION("insert nothing (count = 0)")
            {
                auto it = j_array.insert(j_array.end(), 0, 5);
                CHECK(j_array.size() == 4);
                // the returned iterator points to the first inserted element;
                // there were 4 elements, so it should point to the 5th
                CHECK(it == j_array.begin() + 4);
                CHECK(j_array == json({1, 2, 3, 4}));
            }
        }

        SECTION("range for array")
        {
            json j_other_array = {"first", "second"};

            SECTION("proper usage")
            {
                auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
                CHECK(j_array.size() == 6);
                CHECK(*it == *j_other_array.begin());
                CHECK((j_array.end() - it) == 2);
                CHECK(j_array == json({1, 2, 3, 4, "first", "second"}));
            }

            SECTION("empty range")
            {
                auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
                CHECK(j_array.size() == 4);
                CHECK(it == j_array.end());
                CHECK(j_array == json({1, 2, 3, 4}));
            }

            SECTION("invalid iterators")
            {
                json j_other_array2 = {"first", "second"};

                CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container",
                                     json::invalid_iterator&);
                CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "[json.exception.invalid_iterator.210] iterators do not fit",
                                     json::invalid_iterator&);
            }
        }

        SECTION("range for object")
        {
            json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
            json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};

            SECTION("proper usage")
            {
                j_object1.insert(j_object2.begin(), j_object2.end());
                CHECK(j_object1.size() == 4);
            }

            SECTION("empty range")
            {
                j_object1.insert(j_object2.begin(), j_object2.begin());
                CHECK(j_object1.size() == 2);
            }

            SECTION("invalid iterators")
            {
                json const j_other_array2 = {"first", "second"};

                CHECK_THROWS_WITH_AS(j_array.insert(j_object2.begin(), j_object2.end()), "[json.exception.type_error.309] cannot use insert() with array", json::type_error&);
                CHECK_THROWS_WITH_AS(j_object1.insert(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
                CHECK_THROWS_WITH_AS(j_object1.insert(j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects", json::invalid_iterator&);
            }
        }

        SECTION("initializer list at position")
        {
            SECTION("insert before begin()")
            {
                auto it = j_array.insert(j_array.begin(), {7, 8, 9});
                CHECK(j_array.size() == 7);
                CHECK(*it == json(7));
                CHECK(j_array.begin() == it);
                CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4}));
            }

            SECTION("insert in the middle")
            {
                auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
                CHECK(j_array.size() == 7);
                CHECK(*it == json(7));
                CHECK((it - j_array.begin()) == 2);
                CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4}));
            }

            SECTION("insert before end()")
            {
                auto it = j_array.insert(j_array.end(), {7, 8, 9});
                CHECK(j_array.size() == 7);
                CHECK(*it == json(7));
                CHECK((j_array.end() - it) == 3);
                CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9}));
            }
        }

        SECTION("invalid iterator")
        {
            // pass iterator to a different array
            json j_another_array = {1, 2};
            json j_yet_another_array = {"first", "second"};
            CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
            CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_value), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
            CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10, 11), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
            CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
            CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
        }

        SECTION("non-array type")
        {
            // call insert on a non-array type
            json j_nonarray = 3;
            json j_yet_another_array = {"first", "second"};
            CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
            CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
            CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
            CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
            CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
        }
    }

    SECTION("update()")
    {
        SECTION("non-recursive (default)")
        {
            json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
            json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
            json j_array = {1, 2, 3, 4};

            SECTION("const reference")
            {
                SECTION("proper usage")
                {
                    j_object1.update(j_object2);
                    CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));

                    json j_null;
                    j_null.update(j_object2);
                    CHECK(j_null == j_object2);
                }

                SECTION("wrong types")
                {
                    CHECK_THROWS_WITH_AS(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);

                    CHECK_THROWS_WITH_AS(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
                }
            }

            SECTION("iterator range")
            {
                SECTION("proper usage")
                {
                    j_object1.update(j_object2.begin(), j_object2.end());
                    CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));

                    json j_null;
                    j_null.update(j_object2.begin(), j_object2.end());
                    CHECK(j_null == j_object2);
                }

                SECTION("empty range")
                {
                    j_object1.update(j_object2.begin(), j_object2.begin());
                    CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
                }

                SECTION("invalid iterators")
                {
                    json const j_other_array2 = {"first", "second"};

                    CHECK_THROWS_WITH_AS(j_array.update(j_object2.begin(), j_object2.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
                    CHECK_THROWS_WITH_AS(j_object1.update(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
                    CHECK_THROWS_WITH_AS(j_object1.update(j_array.begin(), j_array.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
                }
            }
        }

        SECTION("recursive")
        {
            SECTION("const reference")
            {
                SECTION("extend object")
                {
                    json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
                    json const j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}};
                    j1.update(j2, true);
                    CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}}));
                }

                SECTION("replace object")
                {
                    json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
                    json const j2 = {{"string", "t"}, {"numbers", 1}};
                    j1.update(j2, true);
                    CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
                }
            }
        }
    }

    SECTION("swap()")
    {
        SECTION("json")
        {
            SECTION("member swap")
            {
                json j("hello world");
                json k(42.23);

                j.swap(k);

                CHECK(j == json(42.23));
                CHECK(k == json("hello world"));
            }

            SECTION("nonmember swap")
            {
                json j("hello world");
                json k(42.23);

                using std::swap;
                swap(j, k);

                CHECK(j == json(42.23));
                CHECK(k == json("hello world"));
            }
        }

        SECTION("array_t")
        {
            SECTION("array_t type")
            {
                json j = {1, 2, 3, 4};
                json::array_t a = {"foo", "bar", "baz"};

                j.swap(a);

                CHECK(j == json({"foo", "bar", "baz"}));

                j.swap(a);

                CHECK(j == json({1, 2, 3, 4}));
            }

            SECTION("non-array_t type")
            {
                json j = 17;
                json::array_t a = {"foo", "bar", "baz"};

                CHECK_THROWS_WITH_AS(j.swap(a), "[json.exception.type_error.310] cannot use swap(array_t&) with number", json::type_error&);
            }
        }

        SECTION("object_t")
        {
            SECTION("object_t type")
            {
                json j = {{"one", 1}, {"two", 2}};
                json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};

                j.swap(o);

                CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));

                j.swap(o);

                CHECK(j == json({{"one", 1}, {"two", 2}}));
            }

            SECTION("non-object_t type")
            {
                json j = 17;
                json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};

                CHECK_THROWS_WITH_AS(j.swap(o), "[json.exception.type_error.310] cannot use swap(object_t&) with number", json::type_error&);
            }
        }

        SECTION("string_t")
        {
            SECTION("string_t type")
            {
                json j = "Hello world";
                json::string_t s = "Hallo Welt";

                j.swap(s);

                CHECK(j == json("Hallo Welt"));

                j.swap(s);

                CHECK(j == json("Hello world"));
            }

            SECTION("non-string_t type")
            {
                json j = 17;
                json::string_t s = "Hallo Welt";

                CHECK_THROWS_WITH_AS(j.swap(s), "[json.exception.type_error.310] cannot use swap(string_t&) with number", json::type_error&);
            }
        }

        SECTION("binary_t")
        {
            SECTION("binary_t type")
            {
                json j = json::binary({1, 2, 3, 4});
                json::binary_t s = {{5, 6, 7, 8}};

                j.swap(s);

                CHECK(j == json::binary({5, 6, 7, 8}));

                j.swap(s);

                CHECK(j == json::binary({1, 2, 3, 4}));
            }

            SECTION("binary_t::container_type type")
            {
                json j = json::binary({1, 2, 3, 4});
                std::vector<std::uint8_t> s = {{5, 6, 7, 8}};

                j.swap(s);

                CHECK(j == json::binary({5, 6, 7, 8}));

                j.swap(s);

                CHECK(j == json::binary({1, 2, 3, 4}));
            }

            SECTION("non-binary_t type")
            {
                json j = 17;
                json::binary_t s1 = {{1, 2, 3, 4}};
                std::vector<std::uint8_t> s2 = {{5, 6, 7, 8}};

                CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap(binary_t&) with number", json::type_error);
                CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap(binary_t::container_type&) with number", json::type_error);
            }
        }
    }
}


import fsp from 'node:fs/promises'
import path from 'node:path'
import { performance } from 'node:perf_hooks'
import getEtag from 'etag'
import MagicString from 'magic-string'
import { init, parse as parseImports } from 'es-module-lexer'
import type {
  ModuleType,
  PartialResolvedId,
  SourceDescription,
  SourceMap,
} from 'rolldown'
import colors from 'picocolors'
import type { EnvironmentModuleNode } from '../server/moduleGraph'
import {
  createDebugger,
  ensureWatchedFile,
  injectQuery,
  isObject,
  monotonicDateNow,
  prettifyUrl,
  removeImportQuery,
  removeTimestampQuery,
  stripBase,
  timeFrom,
} from '../utils'
import { ssrTransform } from '../ssr/ssrTransform'
import { checkPublicFile } from '../publicDir'
import { cleanUrl, slash, unwrapId } from '../../shared/utils'
import {
  applySourcemapIgnoreList,
  extractSourcemapFromFile,
  injectSourcesContent,
} from './sourcemap'
import { isFileLoadingAllowed } from './middlewares/static'
import { throwClosedServerError } from './pluginContainer'
import type { DevEnvironment } from './environment'
import { isServerAccessDeniedForTransform } from './middlewares/transform'

export const ERR_LOAD_URL = 'ERR_LOAD_URL'
export const ERR_LOAD_PUBLIC_URL = 'ERR_LOAD_PUBLIC_URL'
export const ERR_DENIED_ID = 'ERR_DENIED_ID'

const debugLoad = createDebugger('vite:load')
const debugTransform = createDebugger('vite:transform')
const debugCache = createDebugger('vite:cache')

export interface TransformResult {
  code: string
  map: SourceMap | { mappings: '' } | null
  ssr?: boolean
  etag?: string
  deps?: string[]
  dynamicDeps?: string[]
}

export interface TransformOptions {
  /**
   * @deprecated inferred from environment
   */
  ssr?: boolean
}

interface TransformOptionsInternal {
  /**
   * Whether to skip the `server.fs` check.
   */
  skipFsCheck: boolean
}

// TODO: This function could be moved to the DevEnvironment class.
// It was already using private fields from the server before, and it now does
// the same with environment._closing, environment._pendingRequests and
// environment._registerRequestProcessing. Maybe it makes sense to keep it in
// separate file to preserve the history or keep the DevEnvironment class cleaner,
// but conceptually this is: `environment.transformRequest(url, options)`

export function transformRequest(
  environment: DevEnvironment,
  url: string,
  options: TransformOptionsInternal,
): Promise<TransformResult | null> {
  if (environment._closing && environment.config.dev.recoverable)
    throwClosedServerError()

  // This module may get invalidated while we are processing it. For example
  // when a full page reload is needed after the re-processing of pre-bundled
  // dependencies when a missing dep is discovered. We save the current time
  // to compare it to the last invalidation performed to know if we should
  // cache the result of the transformation or we should discard it as stale.
  //
  // A module can be invalidated due to:
  // 1. A full reload because of pre-bundling newly discovered deps
  // 2. A full reload after a config change
  // 3. The file that generated the module changed
  // 4. Invalidation for a virtual module
  //
  // For 1 and 2, a new request for this module will be issued after
  // the invalidation as part of the browser reloading the page. For 3 and 4
  // there may not be a new request right away because of HMR handling.
  // In all cases, the next time this module is requested, it should be
  // re-processed.
  //
  // We save the timestamp when we start processing and compare it with the
  // last time this module is invalidated
  const timestamp = monotonicDateNow()

  url = removeTimestampQuery(url)

  const pending = environment._pendingRequests.get(url)
  if (pending) {
    return environment.moduleGraph.getModuleByUrl(url).then((module) => {
      if (!module || pending.timestamp > module.lastInvalidationTimestamp) {
        // The pending request is still valid, we can safely reuse its result
        return pending.request
      } else {
        // Request 1 for module A     (pending.timestamp)
        // Invalidate module A        (module.lastInvalidationTimestamp)
        // Request 2 for module A     (timestamp)

        // First request has been invalidated, abort it to clear the cache,
        // then perform a new doTransform.
        pending.abort()
        return transformRequest(environment, url, options)
      }
    })
  }

  const request = doTransform(environment, url, options, timestamp)

  // Avoid clearing the cache of future requests if aborted
  let cleared = false
  const clearCache = () => {
    if (!cleared) {
      environment._pendingRequests.delete(url)
      cleared = true
    }
  }

  // Cache the request and clear it once processing is done
  environment._pendingRequests.set(url, {
    request,
    timestamp,
    abort: clearCache,
  })

  return request.finally(clearCache)
}

async function doTransform(
  environment: DevEnvironment,
  url: string,
  options: TransformOptionsInternal,
  timestamp: number,
) {
  const { pluginContainer } = environment

  let module = await environment.moduleGraph.getModuleByUrl(url)
  if (module) {
    // try use cache from url
    const cached = await getCachedTransformResult(
      environment,
      url,
      module,
      timestamp,
    )
    if (cached) return cached
  }

  const resolved = module
    ? undefined
    : ((await pluginContainer.resolveId(url, undefined)) ?? undefined)

  // resolve
  const id = module?.id ?? resolved?.id ?? url

  module ??= environment.moduleGraph.getModuleById(id)
  if (module) {
    // if a different url maps to an existing loaded id,  make sure we relate this url to the id
    await environment.moduleGraph._ensureEntryFromUrl(url, undefined, resolved)
    // try use cache from id
    const cached = await getCachedTransformResult(
      environment,
      url,
      module,
      timestamp,
    )
    if (cached) return cached
  }

  const result = loadAndTransform(
    environment,
    id,
    url,
    options,
    timestamp,
    module,
    resolved,
  )

  const { depsOptimizer } = environment
  if (!depsOptimizer?.isOptimizedDepFile(id)) {
    environment._registerRequestProcessing(id, () => result)
  }

  return result
}

async function getCachedTransformResult(
  environment: DevEnvironment,
  url: string,
  module: EnvironmentModuleNode,
  timestamp: number,
) {
  const prettyUrl = debugCache ? prettifyUrl(url, environment.config.root) : ''

  // tries to handle soft invalidation of the module if available,
  // returns a boolean true is successful, or false if no handling is needed
  const softInvalidatedTransformResult = await handleModuleSoftInvalidation(
    environment,
    module,
    timestamp,
  )
  if (softInvalidatedTransformResult) {
    debugCache?.(`[memory-hmr] ${prettyUrl}`)
    return softInvalidatedTransformResult
  }

  // check if we have a fresh cache
  const cached = module.transformResult
  if (cached) {
    debugCache?.(`[memory] ${prettyUrl}`)
    return cached
  }
}

async function loadAndTransform(
  environment: DevEnvironment,
  id: string,
  url: string,
  options: TransformOptionsInternal,
  timestamp: number,
  mod?: EnvironmentModuleNode,
  resolved?: PartialResolvedId,
) {
  const { config, pluginContainer, logger } = environment
  const prettyUrl =
    debugLoad || debugTransform ? prettifyUrl(url, config.root) : ''

  const moduleGraph = environment.moduleGraph

  if (
    !options.skipFsCheck &&
    id[0] !== '\0' &&
    isServerAccessDeniedForTransform(config, id)
  ) {
    const err: any = new Error(`Denied ID ${id}`)
    err.code = ERR_DENIED_ID
    err.id = id
    throw err
  }

  let code: string | null = null
  let map: SourceDescription['map'] = null
  let moduleType: ModuleType | undefined

  // load
  const loadStart = debugLoad ? performance.now() : 0
  const loadResult = await pluginContainer.load(id)

  if (loadResult == null) {
    const file = cleanUrl(id)

    // try fallback loading it from fs as string
    // if the file is a binary, there should be a plugin that already loaded it
    // as string
    // only try the fallback if access is allowed, skip for out of root url
    // like /service-worker.js or /api/users
    if (
      options.skipFsCheck ||
      isFileLoadingAllowed(environment.getTopLevelConfig(), slash(file))
    ) {
      try {
        code = await fsp.readFile(file, 'utf-8')
        debugLoad?.(`${timeFrom(loadStart)} [fs] ${prettyUrl}`)
      } catch (e) {
        if (e.code !== 'ENOENT' && e.code !== 'EISDIR') {
          throw e
        }
      }
      if (code != null && environment.pluginContainer.watcher) {
        ensureWatchedFile(
          environment.pluginContainer.watcher,
          file,
          config.root,
        )
      }
    }
    if (code) {
      try {
        const extracted = extractSourcemapFromFile(code, file, logger)
        if (extracted) {
          code = extracted.code
          map = extracted.map
        }
      } catch (e) {
        logger.warn(`Failed to load source map for ${file}.\n${e}`, {
          timestamp: true,
        })
      }
    }
  } else {
    debugLoad?.(`${timeFrom(loadStart)} [plugin] ${prettyUrl}`)
    if (isObject(loadResult)) {
      code = loadResult.code
      map = loadResult.map
      moduleType = loadResult.moduleType
    } else {
      code = loadResult
    }
  }
  if (code == null) {
    const isPublicFile = checkPublicFile(url, environment.getTopLevelConfig())
    let publicDirName = path.relative(config.root, config.publicDir)
    if (publicDirName[0] !== '.') publicDirName = '/' + publicDirName
    const msg = isPublicFile
      ? `This file is in ${publicDirName} and will be copied as-is during ` +
        `build without going through the plugin transforms, and therefore ` +
        `should not be imported from source code. It can only be referenced ` +
        `via HTML tags.`
      : `Does the file exist?`
    const importerMod: EnvironmentModuleNode | undefined =
      moduleGraph.idToModuleMap.get(id)?.importers.values().next().value
    const importer = importerMod?.file || importerMod?.url
    const err: any = new Error(
      `Failed to load url ${url} (resolved id: ${id})${
        importer ? ` in ${importer}` : ''
      }. ${msg}`,
    )
    err.code = isPublicFile ? ERR_LOAD_PUBLIC_URL : ERR_LOAD_URL
    throw err
  }
  if (moduleType === undefined) {
    const guessedModuleType = getModuleTypeFromId(id)
    if (guessedModuleType && guessedModuleType !== 'js') {
      moduleType = guessedModuleType
    }
  }

  if (environment._closing && environment.config.dev.recoverable)
    throwClosedServerError()

  // ensure module in graph after successful load
  mod ??= await moduleGraph._ensureEntryFromUrl(url, undefined, resolved)

  // transform
  const transformStart = debugTransform ? performance.now() : 0
  const transformResult = await pluginContainer.transform(code, id, {
    inMap: map,
    moduleType,
  })
  const originalCode = code
  if (transformResult.code === originalCode) {
    // no transform applied, keep code as-is
    debugTransform?.(
      timeFrom(transformStart) + colors.dim(` [skipped] ${prettyUrl}`),
    )
  } else {
    debugTransform?.(`${timeFrom(transformStart)} ${prettyUrl}`)
    code = transformResult.code!
    map = transformResult.map
  }

  let normalizedMap: SourceMap | { mappings: '' } | null
  if (typeof map === 'string') {
    normalizedMap = JSON.parse(map)
  } else if (map) {
    normalizedMap = map as SourceMap | { mappings: '' }
  } else {
    normalizedMap = null
  }

  if (normalizedMap && 'version' in normalizedMap && mod.file) {
    if (normalizedMap.mappings) {
      await injectSourcesContent(normalizedMap, mod.file, logger)
    }

    const sourcemapPath = `${mod.file}.map`
    applySourcemapIgnoreList(
      normalizedMap,
      sourcemapPath,
      config.server.sourcemapIgnoreList,
      logger,
    )

    if (path.isAbsolute(mod.file)) {
      let modDirname
      for (
        let sourcesIndex = 0;
        sourcesIndex < normalizedMap.sources.length;
        ++sourcesIndex
      ) {
        const sourcePath = normalizedMap.sources[sourcesIndex]
        if (sourcePath) {
          // Rewrite sources to relative paths to give debuggers the chance
          // to resolve and display them in a meaningful way (rather than
          // with absolute paths).
          if (path.isAbsolute(sourcePath)) {
            modDirname ??= path.dirname(mod.file)
            normalizedMap.sources[sourcesIndex] = path.relative(
              modDirname,
              sourcePath,
            )
          }
        }
      }
    }
  }

  if (environment._closing && environment.config.dev.recoverable)
    throwClosedServerError()

  const topLevelConfig = environment.getTopLevelConfig()
  const result = environment.config.dev.moduleRunnerTransform
    ? await ssrTransform(code, normalizedMap, url, originalCode, {
        json: {
          stringify:
            topLevelConfig.json.stringify === true &&
            topLevelConfig.json.namedExports !== true,
        },
      })
    : ({
        code,
        map: normalizedMap,
        etag: getEtag(code, { weak: true }),
      } satisfies TransformResult)

  // Only cache the result if the module wasn't invalidated while it was
  // being processed, so it is re-processed next time if it is stale
  if (timestamp > mod.lastInvalidationTimestamp)
    moduleGraph.updateModuleTransformResult(mod, result)

  return result
}

/**
 * When a module is soft-invalidated, we can preserve its previous `transformResult` and
 * return similar code to before:
 *
 * - Client: We need to transform the import specifiers with new timestamps
 * - SSR: We don't need to change anything as `ssrLoadModule` controls it
 */
async function handleModuleSoftInvalidation(
  environment: DevEnvironment,
  mod: EnvironmentModuleNode,
  timestamp: number,
) {
  const transformResult = mod.invalidationState

  // Reset invalidation state
  mod.invalidationState = undefined

  // Skip if not soft-invalidated
  if (!transformResult || transformResult === 'HARD_INVALIDATED') return

  if (mod.transformResult) {
    throw new Error(
      `Internal server error: Soft-invalidated module "${mod.url}" should not have existing transform result`,
    )
  }

  let result: TransformResult
  // For SSR soft-invalidation, no transformation is needed
  if (transformResult.ssr) {
    result = transformResult
  }
  // We need to transform each imports with new timestamps if available
  else {
    await init
    const source = transformResult.code
    const s = new MagicString(source)
    const [imports] = parseImports(source, mod.id || undefined)

    for (const imp of imports) {
      let rawUrl = source.slice(imp.s, imp.e)
      if (rawUrl === 'import.meta') continue

      const hasQuotes = rawUrl[0] === '"' || rawUrl[0] === "'"
      if (hasQuotes) {
        rawUrl = rawUrl.slice(1, -1)
      }

      const urlWithoutTimestamp = removeTimestampQuery(rawUrl)
      // hmrUrl must be derived the same way as importAnalysis
      const hmrUrl = unwrapId(
        stripBase(
          removeImportQuery(urlWithoutTimestamp),
          environment.config.base,
        ),
      )
      for (const importedMod of mod.importedModules) {
        if (importedMod.url !== hmrUrl) continue
        if (importedMod.lastHMRTimestamp > 0) {
          const replacedUrl = injectQuery(
            urlWithoutTimestamp,
            `t=${importedMod.lastHMRTimestamp}`,
          )
          const start = hasQuotes ? imp.s + 1 : imp.s
          const end = hasQuotes ? imp.e - 1 : imp.e
          s.overwrite(start, end, replacedUrl)
        }

        if (imp.d === -1 && environment.config.dev.preTransformRequests) {
          // pre-transform known direct imports
          environment.warmupRequest(hmrUrl)
        }

        break
      }
    }

    // Update `transformResult` with new code. We don't have to update the sourcemap
    // as the timestamp changes doesn't affect the code lines (stable).
    const code = s.toString()
    result = {
      ...transformResult,
      code,
      etag: getEtag(code, { weak: true }),
    }
  }

  // Only cache the result if the module wasn't invalidated while it was
  // being processed, so it is re-processed next time if it is stale
  if (timestamp > mod.lastInvalidationTimestamp)
    environment.moduleGraph.updateModuleTransformResult(mod, result)

  return result
}

// https://github.com/rolldown/rolldown/blob/cc66f4b7189dfb3a248608d02f5962edb09b11f8/crates/rolldown/src/utils/normalize_options.rs#L95-L111
const defaultModuleTypes: Record<string, ModuleType | undefined> = {
  js: 'js',
  mjs: 'js',
  cjs: 'js',
  jsx: 'jsx',
  ts: 'ts',
  mts: 'ts',
  cts: 'ts',
  tsx: 'tsx',
  json: 'json',
  txt: 'text',
  css: 'css',
}

// https://github.com/rolldown/rolldown/blob/bf53a100edf1780d5a5aa41f0bc0459c5696543e/crates/rolldown/src/utils/load_source.rs#L53-L89
export function getModuleTypeFromId(id: string): ModuleType | undefined {
  let pos = -1
  while ((pos = id.indexOf('.', pos + 1)) >= 0) {
    const ext = id.slice(pos + 1)
    const moduleType = defaultModuleTypes[ext]
    if (moduleType) {
      return moduleType
    }
  }
}