# MessagePack

MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON.
But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one
extra byte in addition to the strings themselves.

!!! abstract "References"

    - [MessagePack website](https://msgpack.org)
    - [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md)

## Serialization

The library uses the following mapping from JSON values types to MessagePack types according to the MessagePack
specification:

| JSON value type | value/range                              | MessagePack type | first byte |
|-----------------|------------------------------------------|------------------|------------|
| null            | `null`                                   | nil              | 0xC0       |
| boolean         | `true`                                   | true             | 0xC3       |
| boolean         | `false`                                  | false            | 0xC2       |
| number_integer  | -9223372036854775808..-2147483649        | int64            | 0xD3       |
| number_integer  | -2147483648..-32769                      | int32            | 0xD2       |
| number_integer  | -32768..-129                             | int16            | 0xD1       |
| number_integer  | -128..-33                                | int8             | 0xD0       |
| number_integer  | -32..-1                                  | negative fixint  | 0xE0..0xFF |
| number_integer  | 0..127                                   | positive fixint  | 0x00..0x7F |
| number_integer  | 128..255                                 | uint 8           | 0xCC       |
| number_integer  | 256..65535                               | uint 16          | 0xCD       |
| number_integer  | 65536..4294967295                        | uint 32          | 0xCE       |
| number_integer  | 4294967296..18446744073709551615         | uint 64          | 0xCF       |
| number_unsigned | 0..127                                   | positive fixint  | 0x00..0x7F |
| number_unsigned | 128..255                                 | uint 8           | 0xCC       |
| number_unsigned | 256..65535                               | uint 16          | 0xCD       |
| number_unsigned | 65536..4294967295                        | uint 32          | 0xCE       |
| number_unsigned | 4294967296..18446744073709551615         | uint 64          | 0xCF       |
| number_float    | *any value representable by a float*     | float 32         | 0xCA       |
| number_float    | *any value NOT representable by a float* | float 64         | 0xCB       |
| string          | *length*: 0..31                          | fixstr           | 0xA0..0xBF |
| string          | *length*: 32..255                        | str 8            | 0xD9       |
| string          | *length*: 256..65535                     | str 16           | 0xDA       |
| string          | *length*: 65536..4294967295              | str 32           | 0xDB       |
| array           | *size*: 0..15                            | fixarray         | 0x90..0x9F |
| array           | *size*: 16..65535                        | array 16         | 0xDC       |
| array           | *size*: 65536..4294967295                | array 32         | 0xDD       |
| object          | *size*: 0..15                            | fix map          | 0x80..0x8F |
| object          | *size*: 16..65535                        | map 16           | 0xDE       |
| object          | *size*: 65536..4294967295                | map 32           | 0xDF       |
| binary          | *size*: 0..255                           | bin 8            | 0xC4       |
| binary          | *size*: 256..65535                       | bin 16           | 0xC5       |
| binary          | *size*: 65536..4294967295                | bin 32           | 0xC6       |

!!! success "Complete mapping"

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

    Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.

!!! warning "Size constraints"

    The following values can **not** be converted to a MessagePack value:

      - strings with more than 4294967295 bytes
      - byte strings with more than 4294967295 bytes
      - arrays with more than 4294967295 elements
      - objects with more than 4294967295 elements

!!! info "NaN/infinity handling"

    `NaN`, `Infinity`, and `-Infinity` are serialized as a MessagePack float 32 (type 0xCA, 5 bytes total),
    regardless of magnitude, in contrast to the [dump](../../api/basic_json/dump.md) function which serializes NaN
    or Infinity to `null`.

!!! note

    Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes
    total), because the check used to select the smaller float 32 encoding compared magnitudes with NaN, which is
    always `false` and caused the float 32 path to be skipped.

??? example

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

    Output:

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

## Deserialization

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

| MessagePack type | JSON value type | first byte |
|------------------|-----------------|------------|
| positive fixint  | number_unsigned | 0x00..0x7F |
| fixmap           | object          | 0x80..0x8F |
| fixarray         | array           | 0x90..0x9F |
| fixstr           | string          | 0xA0..0xBF |
| nil              | `null`          | 0xC0       |
| false            | `false`         | 0xC2       |
| true             | `true`          | 0xC3       |
| float 32         | number_float    | 0xCA       |
| float 64         | number_float    | 0xCB       |
| uint 8           | number_unsigned | 0xCC       |
| uint 16          | number_unsigned | 0xCD       |
| uint 32          | number_unsigned | 0xCE       |
| uint 64          | number_unsigned | 0xCF       |
| int 8            | number_integer  | 0xD0       |
| int 16           | number_integer  | 0xD1       |
| int 32           | number_integer  | 0xD2       |
| int 64           | number_integer  | 0xD3       |
| str 8            | string          | 0xD9       |
| str 16           | string          | 0xDA       |
| str 32           | string          | 0xDB       |
| array 16         | array           | 0xDC       |
| array 32         | array           | 0xDD       |
| map 16           | object          | 0xDE       |
| map 32           | object          | 0xDF       |
| bin 8            | binary          | 0xC4       |
| bin 16           | binary          | 0xC5       |
| bin 32           | binary          | 0xC6       |
| ext 8            | binary          | 0xC7       |
| ext 16           | binary          | 0xC8       |
| ext 32           | binary          | 0xC9       |
| fixext 1         | binary          | 0xD4       |
| fixext 2         | binary          | 0xD5       |
| fixext 4         | binary          | 0xD6       |
| fixext 8         | binary          | 0xD7       |
| fixext 16        | binary          | 0xD8       |
| negative fixint  | number_integer  | 0xE0-0xFF  |

!!! info

    Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.

??? example

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

    Output:

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


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

using json = nlohmann::json;

int main()
{
    json j = json::parse(R"({"uint64": 18446744073709551615})");

    std::cout << "operator[]:                " << j["uint64"] << '\n'
              << "default value (int):       " << j.value("uint64", 0) << '\n'
              << "default value (uint64_t):  " << j.value("uint64", std::uint64_t(0)) << '\n'
              << "explicit return value type: " << j.value<std::uint64_t>("uint64", 0) << '\n';
}


// self-accepting module with no asset import; the spec adds one via an HMR edit
const slot = document.querySelector('.hmr-asset')
const img = document.createElement('img')
img.id = 'hmr-asset-image'
img.alt = 'hmr-asset'
slot.replaceChildren(img)
/* @asset-src */

import.meta.hot?.accept()


import shouldTreeshake from './shared-css-empty-2.js'
document.querySelector('#app').innerHTML = `

Shared CSS, with JS

`
function shouldBeTreeshaken_0() {
  // This function should be treeshaken, even if { moduleSideEffects: 'no-treeshake' }
  // was used in the JS corresponding to the HTML entrypoint.
}


//! This crate provides Serde's two derive macros.
//!
//! ```edition2021
//! # use serde_derive::{Deserialize, Serialize};
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.229")]
#![cfg_attr(not(check_cfg), allow(unexpected_cfgs))]
// Ignored clippy lints
#![allow(
    // clippy false positive: https://github.com/rust-lang/rust-clippy/issues/7054
    clippy::branches_sharing_code,
    clippy::cognitive_complexity,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7575
    clippy::collapsible_match,
    clippy::derive_partial_eq_without_eq,
    clippy::enum_variant_names,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6797
    clippy::manual_map,
    clippy::match_like_matches_macro,
    clippy::needless_lifetimes,
    clippy::needless_pass_by_value,
    clippy::too_many_arguments,
    clippy::trivially_copy_pass_by_ref,
    clippy::used_underscore_binding,
    clippy::wildcard_in_or_patterns,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
    clippy::unnested_or_patterns,
)]
// Ignored clippy_pedantic lints
#![allow(
    clippy::cast_possible_truncation,
    clippy::checked_conversions,
    clippy::doc_markdown,
    clippy::elidable_lifetime_names,
    clippy::enum_glob_use,
    clippy::indexing_slicing,
    clippy::items_after_statements,
    clippy::let_underscore_untyped,
    clippy::manual_assert,
    clippy::map_err_ignore,
    clippy::match_same_arms,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6984
    clippy::match_wildcard_for_single_variants,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::similar_names,
    clippy::single_match_else,
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::uninlined_format_args,
    clippy::unseparated_literal_suffix,
    clippy::unused_self,
    clippy::use_self,
    clippy::wildcard_imports
)]
#![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))]
#![allow(unknown_lints, mismatched_lifetime_syntaxes)]

extern crate proc_macro2;
extern crate quote;
extern crate syn;

extern crate proc_macro;

mod internals;

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{ToTokens, TokenStreamExt as _};
use syn::parse_macro_input;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod deprecated;
mod dummy;
mod pretend;
mod ser;
mod this;

#[allow(non_camel_case_types)]
struct private;

impl private {
    fn ident(&self) -> Ident {
        Ident::new(
            concat!("__private", env!("CARGO_PKG_VERSION_PATCH")),
            Span::call_site(),
        )
    }
}

impl ToTokens for private {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        tokens.append(self.ident());
    }
}

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&mut input)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&mut input)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}


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

```cpp
// (1)
iterator find(const typename object_t::key_type& key);
const_iterator find(const typename object_t::key_type& key) const;

// (2)
template<typename KeyType>
iterator find(KeyType&& key);
template<typename KeyType>
const_iterator find(KeyType&& key) const;
```

1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the
   JSON value is not an object, `end()` is returned.
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
   `#!cpp typename object_comparator_t::is_transparent` denotes a type.

## Template parameters

`KeyType`
:   A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with
    [`string_t`](string_t.md) using  [`object_comparator_t`](object_comparator_t.md).
    This can also be a string view (C++17).

## Parameters

`key` (in)
:   key value of the element to search for.

## Return value

Iterator to an element with a key equivalent to `key`. If no such element is found or the JSON value is not an object,
a past-the-end iterator (see `end()`) is returned.

## Exception safety

Strong exception safety: if an exception occurs, the original value stays intact.

## Complexity

Logarithmic in the size of the JSON object.

## Notes

This method always returns `end()` when executed on a JSON type that is not an object.

## Examples

??? example "Example: (1) find object element by key"

    The example shows how `find()` is used.

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

    Output:

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

??? example "Example: (2) find object element by key using string_view"

    The example shows how `find()` is used.

    ```cpp
    --8<-- "examples/find__keytype.c++17.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/find__keytype.c++17.output"
    ```

## See also

- [count](count.md) returns the number of occurrences of a key
- [contains](contains.md) checks whether a key exists

## Version history

1. Added in version 3.11.0.
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0.


use crate::de::field_i;
use crate::deprecated::allow_deprecated;
use crate::fragment::{Fragment, Match, Stmts};
use crate::internals::ast::{Container, Data, Field, Style, Variant};
use crate::internals::name::Name;
use crate::internals::{attr, replace_receiver, Ctxt, Derive};
use crate::{bound, dummy, pretend, private, this};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{parse_quote, Ident, Index, Member};

pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result<TokenStream> {
    replace_receiver(input);

    let ctxt = Ctxt::new();
    let Some(cont) = Container::from_ast(&ctxt, input, Derive::Serialize, &private.ident()) else {
        return Err(ctxt.check().unwrap_err());
    };
    precondition(&ctxt, &cont);
    ctxt.check()?;

    let ident = &cont.ident;
    let params = Parameters::new(&cont);
    let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
    let body = Stmts(serialize_body(&cont, &params));
    let allow_deprecated = allow_deprecated(input);

    let impl_block = if let Some(remote) = cont.attrs.remote() {
        let vis = &input.vis;
        let used = pretend::pretend_used(&cont, params.is_packed);
        quote! {
            #[automatically_derived]
            #allow_deprecated
            impl #impl_generics #ident #ty_generics #where_clause {
                #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error>
                where
                    __S: _serde::Serializer,
                {
                    #used
                    #body
                }
            }
        }
    } else {
        quote! {
            #[automatically_derived]
            #allow_deprecated
            impl #impl_generics _serde::Serialize for #ident #ty_generics #where_clause {
                fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error>
                where
                    __S: _serde::Serializer,
                {
                    #body
                }
            }
        }
    };

    Ok(dummy::wrap_in_const(
        cont.attrs.custom_serde_path(),
        impl_block,
    ))
}

fn precondition(cx: &Ctxt, cont: &Container) {
    match cont.attrs.identifier() {
        attr::Identifier::No => {}
        attr::Identifier::Field => {
            cx.error_spanned_by(cont.original, "field identifiers cannot be serialized");
        }
        attr::Identifier::Variant => {
            cx.error_spanned_by(cont.original, "variant identifiers cannot be serialized");
        }
    }
}

struct Parameters {
    /// Variable holding the value being serialized. Either `self` for local
    /// types or `__self` for remote types.
    self_var: Ident,

    /// Path to the type the impl is for. Either a single `Ident` for local
    /// types (does not include generic parameters) or `some::remote::Path` for
    /// remote types.
    this_type: syn::Path,

    /// Same as `this_type` but using `::<T>` for generic parameters for use in
    /// expression position.
    this_value: syn::Path,

    /// Generics including any explicit and inferred bounds for the impl.
    generics: syn::Generics,

    /// Type has a `serde(remote = "...")` attribute.
    is_remote: bool,

    /// Type has a repr(packed) attribute.
    is_packed: bool,
}

impl Parameters {
    fn new(cont: &Container) -> Self {
        let is_remote = cont.attrs.remote().is_some();
        let self_var = if is_remote {
            Ident::new("__self", Span::call_site())
        } else {
            Ident::new("self", Span::call_site())
        };

        let this_type = this::this_type(cont);
        let this_value = this::this_value(cont);
        let is_packed = cont.attrs.is_packed();
        let generics = build_generics(cont);

        Parameters {
            self_var,
            this_type,
            this_value,
            generics,
            is_remote,
            is_packed,
        }
    }

    /// Type name to use in error messages and `&'static str` arguments to
    /// various Serializer methods.
    fn type_name(&self) -> String {
        self.this_type.segments.last().unwrap().ident.to_string()
    }
}

// All the generics in the input, plus a bound `T: Serialize` for each generic
// field type that will be serialized by us.
fn build_generics(cont: &Container) -> syn::Generics {
    let generics = bound::without_defaults(cont.generics);

    let generics =
        bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);

    let generics =
        bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound);

    match cont.attrs.ser_bound() {
        Some(predicates) => bound::with_where_predicates(&generics, predicates),
        None => bound::with_bound(
            cont,
            &generics,
            needs_serialize_bound,
            &parse_quote!(_serde::Serialize),
        ),
    }
}

// Fields with a `skip_serializing` or `serialize_with` attribute, or which
// belong to a variant with a `skip_serializing` or `serialize_with` attribute,
// are not serialized by us so we do not generate a bound. Fields with a `bound`
// attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Serialize` bound where T is the type of the field.
fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
    !field.skip_serializing()
        && field.serialize_with().is_none()
        && field.ser_bound().is_none()
        && variant.map_or(true, |variant| {
            !variant.skip_serializing()
                && variant.serialize_with().is_none()
                && variant.ser_bound().is_none()
        })
}

fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
    if cont.attrs.transparent() {
        serialize_transparent(cont, params)
    } else if let Some(type_into) = cont.attrs.type_into() {
        serialize_into(params, type_into)
    } else {
        match &cont.data {
            Data::Enum(variants) => serialize_enum(params, variants, &cont.attrs),
            Data::Struct(Style::Struct, fields) => serialize_struct(params, fields, &cont.attrs),
            Data::Struct(Style::Tuple, fields) => {
                serialize_tuple_struct(params, fields, &cont.attrs)
            }
            Data::Struct(Style::Newtype, fields) => {
                serialize_newtype_struct(params, &fields[0], &cont.attrs)
            }
            Data::Struct(Style::Unit, _) => serialize_unit_struct(&cont.attrs),
        }
    }
}

fn serialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
    let fields = match &cont.data {
        Data::Struct(_, fields) => fields,
        Data::Enum(_) => unreachable!(),
    };

    let self_var = &params.self_var;
    let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
    let member = &transparent_field.member;

    let path = match transparent_field.attrs.serialize_with() {
        Some(path) => quote!(#path),
        None => {
            let span = transparent_field.original.span();
            quote_spanned!(span=> _serde::Serialize::serialize)
        }
    };

    quote_block! {
        #path(&#self_var.#member, __serializer)
    }
}

fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment {
    let self_var = &params.self_var;
    quote_block! {
        _serde::Serialize::serialize(
            &_serde::#private::Into::<#type_into>::into(_serde::#private::Clone::clone(#self_var)),
            __serializer)
    }
}

fn serialize_unit_struct(cattrs: &attr::Container) -> Fragment {
    let type_name = cattrs.name().serialize_name();

    quote_expr! {
        _serde::Serializer::serialize_unit_struct(__serializer, #type_name)
    }
}

fn serialize_newtype_struct(
    params: &Parameters,
    field: &Field,
    cattrs: &attr::Container,
) -> Fragment {
    let type_name = cattrs.name().serialize_name();

    let mut field_expr = get_member(
        params,
        field,
        &Member::Unnamed(Index {
            index: 0,
            span: Span::call_site(),
        }),
    );
    if let Some(path) = field.attrs.serialize_with() {
        field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
    }

    let span = field.original.span();
    let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_struct);
    quote_expr! {
        #func(__serializer, #type_name, #field_expr)
    }
}

fn serialize_tuple_struct(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
) -> Fragment {
    let serialize_stmts =
        serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);

    let type_name = cattrs.name().serialize_name();

    let mut serialized_fields = fields
        .iter()
        .enumerate()
        .filter(|(_, field)| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some());

    let len = serialized_fields
        .map(|(i, field)| match field.attrs.skip_serializing_if() {
            None => quote!(1),
            Some(path) => {
                let index = syn::Index {
                    index: i as u32,
                    span: Span::call_site(),
                };
                let field_expr = get_member(params, field, &Member::Unnamed(index));
                quote!(if #path(#field_expr) { 0 } else { 1 })
            }
        })
        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));

    quote_block! {
        let #let_mut __serde_state = _serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len)?;
        #(#serialize_stmts)*
        _serde::ser::SerializeTupleStruct::end(__serde_state)
    }
}

fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
    assert!(
        fields.len() as u64 <= u64::from(u32::MAX),
        "too many fields in {}: {}, maximum supported count is {}",
        cattrs.name().serialize_name(),
        fields.len(),
        u32::MAX,
    );

    let has_non_skipped_flatten = fields
        .iter()
        .any(|field| field.attrs.flatten() && !field.attrs.skip_serializing());
    if has_non_skipped_flatten {
        serialize_struct_as_map(params, fields, cattrs)
    } else {
        serialize_struct_as_struct(params, fields, cattrs)
    }
}

fn serialize_struct_tag_field(cattrs: &attr::Container, struct_trait: &StructTrait) -> TokenStream {
    match cattrs.tag() {
        attr::TagType::Internal { tag } => {
            let type_name = cattrs.name().serialize_name();
            let func = struct_trait.serialize_field(Span::call_site());
            quote! {
                #func(&mut __serde_state, #tag, #type_name)?;
            }
        }
        _ => quote! {},
    }
}

fn serialize_struct_as_struct(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
) -> Fragment {
    let serialize_fields =
        serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);

    let type_name = cattrs.name().serialize_name();

    let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeStruct);
    let tag_field_exists = !tag_field.is_empty();

    let mut serialized_fields = fields
        .iter()
        .filter(|&field| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);

    let len = serialized_fields
        .map(|field| match field.attrs.skip_serializing_if() {
            None => quote!(1),
            Some(path) => {
                let field_expr = get_member(params, field, &field.member);
                quote!(if #path(#field_expr) { 0 } else { 1 })
            }
        })
        .fold(
            quote!(#tag_field_exists as usize),
            |sum, expr| quote!(#sum + #expr),
        );

    quote_block! {
        let #let_mut __serde_state = _serde::Serializer::serialize_struct(__serializer, #type_name, #len)?;
        #tag_field
        #(#serialize_fields)*
        _serde::ser::SerializeStruct::end(__serde_state)
    }
}

fn serialize_struct_as_map(
    params: &Parameters,
    fields: &[Field],
    cattrs: &attr::Container,
) -> Fragment {
    let serialize_fields =
        serialize_struct_visitor(fields, params, false, &StructTrait::SerializeMap);

    let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeMap);
    let tag_field_exists = !tag_field.is_empty();

    let mut serialized_fields = fields
        .iter()
        .filter(|&field| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);

    quote_block! {
        let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, _serde::#private::None)?;
        #tag_field
        #(#serialize_fields)*
        _serde::ser::SerializeMap::end(__serde_state)
    }
}

fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment {
    assert!(variants.len() as u64 <= u64::from(u32::MAX));

    let self_var = &params.self_var;

    let mut arms: Vec<_> = variants
        .iter()
        .enumerate()
        .map(|(variant_index, variant)| {
            serialize_variant(params, variant, variant_index as u32, cattrs)
        })
        .collect();

    if cattrs.remote().is_some() && cattrs.non_exhaustive() {
        arms.push(quote! {
            ref unrecognized => _serde::#private::Err(_serde::ser::Error::custom(_serde::#private::ser::CannotSerializeVariant(unrecognized))),
        });
    }

    quote_expr! {
        match *#self_var {
            #(#arms)*
        }
    }
}

fn serialize_variant(
    params: &Parameters,
    variant: &Variant,
    variant_index: u32,
    cattrs: &attr::Container,
) -> TokenStream {
    let this_value = &params.this_value;
    let variant_ident = &variant.ident;

    if variant.attrs.skip_serializing() {
        let skipped_msg = format!(
            "the enum variant {}::{} cannot be serialized",
            params.type_name(),
            variant_ident
        );
        let skipped_err = quote! {
            _serde::#private::Err(_serde::ser::Error::custom(#skipped_msg))
        };
        let fields_pat = match variant.style {
            Style::Unit => quote!(),
            Style::Newtype | Style::Tuple => quote!((..)),
            Style::Struct => quote!({ .. }),
        };
        quote! {
            #this_value::#variant_ident #fields_pat => #skipped_err,
        }
    } else {
        // variant wasn't skipped
        let case = match variant.style {
            Style::Unit => {
                quote! {
                    #this_value::#variant_ident
                }
            }
            Style::Newtype => {
                quote! {
                    #this_value::#variant_ident(ref __field0)
                }
            }
            Style::Tuple => {
                let field_names = (0..variant.fields.len()).map(field_i);
                quote! {
                    #this_value::#variant_ident(#(ref #field_names),*)
                }
            }
            Style::Struct => {
                let members = variant.fields.iter().map(|f| &f.member);
                quote! {
                    #this_value::#variant_ident { #(ref #members),* }
                }
            }
        };

        let body = Match(match (cattrs.tag(), variant.attrs.untagged()) {
            (attr::TagType::External, false) => {
                serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
            }
            (attr::TagType::Internal { tag }, false) => {
                serialize_internally_tagged_variant(params, variant, cattrs, tag)
            }
            (attr::TagType::Adjacent { tag, content }, false) => {
                serialize_adjacently_tagged_variant(
                    params,
                    variant,
                    cattrs,
                    variant_index,
                    tag,
                    content,
                )
            }
            (attr::TagType::None, _) | (_, true) => {
                serialize_untagged_variant(params, variant, cattrs)
            }
        });

        quote! {
            #case => #body
        }
    }
}

fn serialize_externally_tagged_variant(
    params: &Parameters,
    variant: &Variant,
    variant_index: u32,
    cattrs: &attr::Container,
) -> Fragment {
    let type_name = cattrs.name().serialize_name();
    let variant_name = variant.attrs.name().serialize_name();

    if let Some(path) = variant.attrs.serialize_with() {
        let ser = wrap_serialize_variant_with(params, path, variant);
        return quote_expr! {
            _serde::Serializer::serialize_newtype_variant(
                __serializer,
                #type_name,
                #variant_index,
                #variant_name,
                #ser,
            )
        };
    }

    match effective_style(variant) {
        Style::Unit => {
            quote_expr! {
                _serde::Serializer::serialize_unit_variant(
                    __serializer,
                    #type_name,
                    #variant_index,
                    #variant_name,
                )
            }
        }
        Style::Newtype => {
            let field = &variant.fields[0];
            let mut field_expr = quote!(__field0);
            if let Some(path) = field.attrs.serialize_with() {
                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
            }

            let span = field.original.span();
            let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_variant);
            quote_expr! {
                #func(
                    __serializer,
                    #type_name,
                    #variant_index,
                    #variant_name,
                    #field_expr,
                )
            }
        }
        Style::Tuple => serialize_tuple_variant(
            TupleVariant::ExternallyTagged {
                type_name,
                variant_index,
                variant_name,
            },
            params,
            &variant.fields,
        ),
        Style::Struct => serialize_struct_variant(
            StructVariant::ExternallyTagged {
                variant_index,
                variant_name,
            },
            params,
            &variant.fields,
            type_name,
        ),
    }
}

fn serialize_internally_tagged_variant(
    params: &Parameters,
    variant: &Variant,
    cattrs: &attr::Container,
    tag: &str,
) -> Fragment {
    let type_name = cattrs.name().serialize_name();
    let variant_name = variant.attrs.name().serialize_name();

    let enum_ident_str = params.type_name();
    let variant_ident_str = variant.ident.to_string();

    if let Some(path) = variant.attrs.serialize_with() {
        let ser = wrap_serialize_variant_with(params, path, variant);
        return quote_expr! {
            _serde::#private::ser::serialize_tagged_newtype(
                __serializer,
                #enum_ident_str,
                #variant_ident_str,
                #tag,
                #variant_name,
                #ser,
            )
        };
    }

    match effective_style(variant) {
        Style::Unit => {
            quote_block! {
                let mut __struct = _serde::Serializer::serialize_struct(
                    __serializer, #type_name, 1)?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __struct, #tag, #variant_name)?;
                _serde::ser::SerializeStruct::end(__struct)
            }
        }
        Style::Newtype => {
            let field = &variant.fields[0];
            let mut field_expr = quote!(__field0);
            if let Some(path) = field.attrs.serialize_with() {
                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
            }

            let span = field.original.span();
            let func = quote_spanned!(span=> _serde::#private::ser::serialize_tagged_newtype);
            quote_expr! {
                #func(
                    __serializer,
                    #enum_ident_str,
                    #variant_ident_str,
                    #tag,
                    #variant_name,
                    #field_expr,
                )
            }
        }
        Style::Struct => serialize_struct_variant(
            StructVariant::InternallyTagged { tag, variant_name },
            params,
            &variant.fields,
            type_name,
        ),
        Style::Tuple => unreachable!("checked in serde_derive_internals"),
    }
}

fn serialize_adjacently_tagged_variant(
    params: &Parameters,
    variant: &Variant,
    cattrs: &attr::Container,
    variant_index: u32,
    tag: &str,
    content: &str,
) -> Fragment {
    let this_type = &params.this_type;
    let type_name = cattrs.name().serialize_name();
    let variant_name = variant.attrs.name().serialize_name();
    let serialize_variant = quote! {
        &_serde::#private::ser::AdjacentlyTaggedEnumVariant {
            enum_name: #type_name,
            variant_index: #variant_index,
            variant_name: #variant_name,
        }
    };

    let inner = Stmts(if let Some(path) = variant.attrs.serialize_with() {
        let ser = wrap_serialize_variant_with(params, path, variant);
        quote_expr! {
            _serde::Serialize::serialize(#ser, __serializer)
        }
    } else {
        match effective_style(variant) {
            Style::Unit => {
                return quote_block! {
                    let mut __struct = _serde::Serializer::serialize_struct(
                        __serializer, #type_name, 1)?;
                    _serde::ser::SerializeStruct::serialize_field(
                        &mut __struct, #tag, #serialize_variant)?;
                    _serde::ser::SerializeStruct::end(__struct)
                };
            }
            Style::Newtype => {
                let field = &variant.fields[0];
                let mut field_expr = quote!(__field0);
                if let Some(path) = field.attrs.serialize_with() {
                    field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
                }

                let span = field.original.span();
                let func = quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field);
                return quote_block! {
                    let mut __struct = _serde::Serializer::serialize_struct(
                        __serializer, #type_name, 2)?;
                    _serde::ser::SerializeStruct::serialize_field(
                        &mut __struct, #tag, #serialize_variant)?;
                    #func(
                        &mut __struct, #content, #field_expr)?;
                    _serde::ser::SerializeStruct::end(__struct)
                };
            }
            Style::Tuple => {
                serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
            }
            Style::Struct => serialize_struct_variant(
                StructVariant::Untagged,
                params,
                &variant.fields,
                variant_name,
            ),
        }
    });

    let fields_ty = variant.fields.iter().map(|f| &f.ty);
    let fields_ident: &[_] = &match variant.style {
        Style::Unit => {
            if variant.attrs.serialize_with().is_some() {
                vec![]
            } else {
                unreachable!()
            }
        }
        Style::Newtype => vec![Member::Named(field_i(0))],
        Style::Tuple => (0..variant.fields.len())
            .map(|i| Member::Named(field_i(i)))
            .collect(),
        Style::Struct => variant.fields.iter().map(|f| f.member.clone()).collect(),
    };

    let (_, ty_generics, where_clause) = params.generics.split_for_impl();

    let wrapper_generics = if fields_ident.is_empty() {
        params.generics.clone()
    } else {
        bound::with_lifetime_bound(&params.generics, "'__a")
    };
    let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();

    quote_block! {
        #[doc(hidden)]
        struct __AdjacentlyTagged #wrapper_generics #where_clause {
            data: (#(&'__a #fields_ty,)*),
            phantom: _serde::#private::PhantomData<#this_type #ty_generics>,
        }

        #[automatically_derived]
        impl #wrapper_impl_generics _serde::Serialize for __AdjacentlyTagged #wrapper_ty_generics #where_clause {
            fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error>
            where
                __S: _serde::Serializer,
            {
                // Elements that have skip_serializing will be unused.
                #[allow(unused_variables)]
                let (#(#fields_ident,)*) = self.data;
                #inner
            }
        }

        let mut __struct = _serde::Serializer::serialize_struct(
            __serializer, #type_name, 2)?;
        _serde::ser::SerializeStruct::serialize_field(
            &mut __struct, #tag, #serialize_variant)?;
        _serde::ser::SerializeStruct::serialize_field(
            &mut __struct, #content, &__AdjacentlyTagged {
                data: (#(#fields_ident,)*),
                phantom: _serde::#private::PhantomData::<#this_type #ty_generics>,
            })?;
        _serde::ser::SerializeStruct::end(__struct)
    }
}

fn serialize_untagged_variant(
    params: &Parameters,
    variant: &Variant,
    cattrs: &attr::Container,
) -> Fragment {
    if let Some(path) = variant.attrs.serialize_with() {
        let ser = wrap_serialize_variant_with(params, path, variant);
        return quote_expr! {
            _serde::Serialize::serialize(#ser, __serializer)
        };
    }

    match effective_style(variant) {
        Style::Unit => {
            quote_expr! {
                _serde::Serializer::serialize_unit(__serializer)
            }
        }
        Style::Newtype => {
            let field = &variant.fields[0];
            let mut field_expr = quote!(__field0);
            if let Some(path) = field.attrs.serialize_with() {
                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
            }

            let span = field.original.span();
            let func = quote_spanned!(span=> _serde::Serialize::serialize);
            quote_expr! {
                #func(#field_expr, __serializer)
            }
        }
        Style::Tuple => serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields),
        Style::Struct => {
            let type_name = cattrs.name().serialize_name();
            serialize_struct_variant(StructVariant::Untagged, params, &variant.fields, type_name)
        }
    }
}

enum TupleVariant<'a> {
    ExternallyTagged {
        type_name: &'a Name,
        variant_index: u32,
        variant_name: &'a Name,
    },
    Untagged,
}

fn serialize_tuple_variant(
    context: TupleVariant,
    params: &Parameters,
    fields: &[Field],
) -> Fragment {
    let tuple_trait = match context {
        TupleVariant::ExternallyTagged { .. } => TupleTrait::SerializeTupleVariant,
        TupleVariant::Untagged => TupleTrait::SerializeTuple,
    };

    let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);

    let mut serialized_fields = fields
        .iter()
        .enumerate()
        .filter(|(_, field)| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some());

    let len = serialized_fields
        .map(|(i, field)| match field.attrs.skip_serializing_if() {
            None => quote!(1),
            Some(path) => {
                let field_expr = field_i(i);
                quote!(if #path(#field_expr) { 0 } else { 1 })
            }
        })
        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));

    match context {
        TupleVariant::ExternallyTagged {
            type_name,
            variant_index,
            variant_name,
        } => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_tuple_variant(
                    __serializer,
                    #type_name,
                    #variant_index,
                    #variant_name,
                    #len)?;
                #(#serialize_stmts)*
                _serde::ser::SerializeTupleVariant::end(__serde_state)
            }
        }
        TupleVariant::Untagged => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_tuple(
                    __serializer,
                    #len)?;
                #(#serialize_stmts)*
                _serde::ser::SerializeTuple::end(__serde_state)
            }
        }
    }
}

enum StructVariant<'a> {
    ExternallyTagged {
        variant_index: u32,
        variant_name: &'a Name,
    },
    InternallyTagged {
        tag: &'a str,
        variant_name: &'a Name,
    },
    Untagged,
}

fn serialize_struct_variant(
    context: StructVariant,
    params: &Parameters,
    fields: &[Field],
    name: &Name,
) -> Fragment {
    if fields.iter().any(|field| field.attrs.flatten()) {
        return serialize_struct_variant_with_flatten(context, params, fields, name);
    }

    let struct_trait = match context {
        StructVariant::ExternallyTagged { .. } => StructTrait::SerializeStructVariant,
        StructVariant::InternallyTagged { .. } | StructVariant::Untagged => {
            StructTrait::SerializeStruct
        }
    };

    let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);

    let mut serialized_fields = fields
        .iter()
        .filter(|&field| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some());

    let len = serialized_fields
        .map(|field| {
            let member = &field.member;

            match field.attrs.skip_serializing_if() {
                Some(path) => quote!(if #path(#member) { 0 } else { 1 }),
                None => quote!(1),
            }
        })
        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));

    match context {
        StructVariant::ExternallyTagged {
            variant_index,
            variant_name,
        } => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_struct_variant(
                    __serializer,
                    #name,
                    #variant_index,
                    #variant_name,
                    #len,
                )?;
                #(#serialize_fields)*
                _serde::ser::SerializeStructVariant::end(__serde_state)
            }
        }
        StructVariant::InternallyTagged { tag, variant_name } => {
            quote_block! {
                let mut __serde_state = _serde::Serializer::serialize_struct(
                    __serializer,
                    #name,
                    #len + 1,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    #tag,
                    #variant_name,
                )?;
                #(#serialize_fields)*
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
        StructVariant::Untagged => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_struct(
                    __serializer,
                    #name,
                    #len,
                )?;
                #(#serialize_fields)*
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
    }
}

fn serialize_struct_variant_with_flatten(
    context: StructVariant,
    params: &Parameters,
    fields: &[Field],
    name: &Name,
) -> Fragment {
    let struct_trait = StructTrait::SerializeMap;
    let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);

    let mut serialized_fields = fields
        .iter()
        .filter(|&field| !field.attrs.skip_serializing())
        .peekable();

    let let_mut = mut_if(serialized_fields.peek().is_some());

    match context {
        StructVariant::ExternallyTagged {
            variant_index,
            variant_name,
        } => {
            let this_type = &params.this_type;
            let fields_ty = fields.iter().map(|f| &f.ty);
            let members = &fields.iter().map(|f| &f.member).collect::<Vec<_>>();

            let (_, ty_generics, where_clause) = params.generics.split_for_impl();
            let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
            let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();

            quote_block! {
                #[doc(hidden)]
                struct __EnumFlatten #wrapper_generics #where_clause {
                    data: (#(&'__a #fields_ty,)*),
                    phantom: _serde::#private::PhantomData<#this_type #ty_generics>,
                }

                #[automatically_derived]
                impl #wrapper_impl_generics _serde::Serialize for __EnumFlatten #wrapper_ty_generics #where_clause {
                    fn serialize<__S>(&self, __serializer: __S) -> _serde::#private::Result<__S::Ok, __S::Error>
                    where
                        __S: _serde::Serializer,
                    {
                        let (#(#members,)*) = self.data;
                        let #let_mut __serde_state = _serde::Serializer::serialize_map(
                            __serializer,
                            _serde::#private::None)?;
                        #(#serialize_fields)*
                        _serde::ser::SerializeMap::end(__serde_state)
                    }
                }

                _serde::Serializer::serialize_newtype_variant(
                    __serializer,
                    #name,
                    #variant_index,
                    #variant_name,
                    &__EnumFlatten {
                        data: (#(#members,)*),
                        phantom: _serde::#private::PhantomData::<#this_type #ty_generics>,
                    })
            }
        }
        StructVariant::InternallyTagged { tag, variant_name } => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_map(
                    __serializer,
                    _serde::#private::None)?;
                _serde::ser::SerializeMap::serialize_entry(
                    &mut __serde_state,
                    #tag,
                    #variant_name,
                )?;
                #(#serialize_fields)*
                _serde::ser::SerializeMap::end(__serde_state)
            }
        }
        StructVariant::Untagged => {
            quote_block! {
                let #let_mut __serde_state = _serde::Serializer::serialize_map(
                    __serializer,
                    _serde::#private::None)?;
                #(#serialize_fields)*
                _serde::ser::SerializeMap::end(__serde_state)
            }
        }
    }
}

fn serialize_tuple_struct_visitor(
    fields: &[Field],
    params: &Parameters,
    is_enum: bool,
    tuple_trait: &TupleTrait,
) -> Vec<TokenStream> {
    let mut dst_fields = Vec::new();

    for (i, field) in fields.iter().enumerate() {
        if field.attrs.skip_serializing() {
            continue;
        }
        let mut field_expr = if is_enum {
            let id = field_i(i);
            quote!(#id)
        } else {
            get_member(
                params,
                field,
                &Member::Unnamed(Index {
                    index: i as u32,
                    span: Span::call_site(),
                }),
            )
        };

        let skip = field
            .attrs
            .skip_serializing_if()
            .map(|path| quote!(#path(#field_expr)));

        if let Some(path) = field.attrs.serialize_with() {
            field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
        }

        let span = field.original.span();
        let func = tuple_trait.serialize_element(span);
        let ser = quote! {
            #func(&mut __serde_state, #field_expr)?;
        };

        dst_fields.push(match skip {
            None => ser,
            Some(skip) => quote!(if !#skip { #ser }),
        });
    }
    dst_fields
}

fn serialize_struct_visitor(
    fields: &[Field],
    params: &Parameters,
    is_enum: bool,
    struct_trait: &StructTrait,
) -> Vec<TokenStream> {
    let mut dst_fields = Vec::new();

    for field in fields {
        if field.attrs.skip_serializing() {
            continue;
        }
        let member = &field.member;

        let mut field_expr = if is_enum {
            quote!(#member)
        } else {
            get_member(params, field, member)
        };

        let key_expr = field.attrs.name().serialize_name();

        let skip = field
            .attrs
            .skip_serializing_if()
            .map(|path| quote!(#path(#field_expr)));

        if let Some(path) = field.attrs.serialize_with() {
            field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
        }

        let span = field.original.span();
        let ser = if field.attrs.flatten() {
            let func = quote_spanned!(span=> _serde::Serialize::serialize);
            quote! {
                #func(&#field_expr, _serde::#private::ser::FlatMapSerializer(&mut __serde_state))?;
            }
        } else {
            let func = struct_trait.serialize_field(span);
            quote! {
                #func(&mut __serde_state, #key_expr, #field_expr)?;
            }
        };

        dst_fields.push(match skip {
            None => ser,
            Some(skip) => {
                if let Some(skip_func) = struct_trait.skip_field(span) {
                    quote! {
                        if !#skip {
                            #ser
                        } else {
                            #skip_func(&mut __serde_state, #key_expr)?;
                        }
                    }
                } else {
                    quote! {
                        if !#skip {
                            #ser
                        }
                    }
                }
            }
        });
    }
    dst_fields
}

fn wrap_serialize_field_with(
    params: &Parameters,
    field_ty: &syn::Type,
    serialize_with: &syn::ExprPath,
    field_expr: &TokenStream,
) -> TokenStream {
    wrap_serialize_with(params, serialize_with, &[field_ty], &[quote!(#field_expr)])
}

fn wrap_serialize_variant_with(
    params: &Parameters,
    serialize_with: &syn::ExprPath,
    variant: &Variant,
) -> TokenStream {
    let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
    let field_exprs: Vec<_> = variant
        .fields
        .iter()
        .map(|field| {
            let id = match &field.member {
                Member::Named(ident) => ident.clone(),
                Member::Unnamed(member) => field_i(member.index as usize),
            };
            quote!(#id)
        })
        .collect();
    wrap_serialize_with(
        params,
        serialize_with,
        field_tys.as_slice(),
        field_exprs.as_slice(),
    )
}

fn wrap_serialize_with(
    params: &Parameters,
    serialize_with: &syn::ExprPath,
    field_tys: &[&syn::Type],
    field_exprs: &[TokenStream],
) -> TokenStream {
    let this_type = &params.this_type;
    let (_, ty_generics, where_clause) = params.generics.split_for_impl();

    let wrapper_generics = if field_exprs.is_empty() {
        params.generics.clone()
    } else {
        bound::with_lifetime_bound(&params.generics, "'__a")
    };
    let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();

    let field_access = (0..field_exprs.len()).map(|n| {
        Member::Unnamed(Index {
            index: n as u32,
            span: Span::call_site(),
        })
    });

    let self_var = quote!(self);
    let serializer_var = quote!(__s);

    // If #serialize_with returns wrong type, error will be reported on here.
    // We attach span of the path to this piece so error will be reported
    // on the #[serde(with = "...")]
    //                       ^^^^^
    let wrapper_serialize = quote_spanned! {serialize_with.span()=>
        #serialize_with(#(#self_var.values.#field_access, )* #serializer_var)
    };

    quote!(&{
        #[doc(hidden)]
        struct __SerializeWith #wrapper_impl_generics #where_clause {
            values: (#(&'__a #field_tys, )*),
            phantom: _serde::#private::PhantomData<#this_type #ty_generics>,
        }

        #[automatically_derived]
        impl #wrapper_impl_generics _serde::Serialize for __SerializeWith #wrapper_ty_generics #where_clause {
            fn serialize<__S>(&#self_var, #serializer_var: __S) -> _serde::#private::Result<__S::Ok, __S::Error>
            where
                __S: _serde::Serializer,
            {
                #wrapper_serialize
            }
        }

        __SerializeWith {
            values: (#(#field_exprs, )*),
            phantom: _serde::#private::PhantomData::<#this_type #ty_generics>,
        }
    })
}

// Serialization of an empty struct results in code like:
//
//     let mut __serde_state = serializer.serialize_struct("S", 0)?;
//     _serde::ser::SerializeStruct::end(__serde_state)
//
// where we want to omit the `mut` to avoid a warning.
fn mut_if(is_mut: bool) -> Option<TokenStream> {
    if is_mut {
        Some(quote!(mut))
    } else {
        None
    }
}

fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream {
    let self_var = &params.self_var;
    match (params.is_remote, field.attrs.getter()) {
        (false, None) => {
            if params.is_packed {
                quote!(&{#self_var.#member})
            } else {
                quote!(&#self_var.#member)
            }
        }
        (true, None) => {
            let inner = if params.is_packed {
                quote!(&{#self_var.#member})
            } else {
                quote!(&#self_var.#member)
            };
            let ty = field.ty;
            quote!(_serde::#private::ser::constrain::<#ty>(#inner))
        }
        (true, Some(getter)) => {
            let ty = field.ty;
            quote!(_serde::#private::ser::constrain::<#ty>(&#getter(#self_var)))
        }
        (false, Some(_)) => {
            unreachable!("getter is only allowed for remote impls");
        }
    }
}

fn effective_style(variant: &Variant) -> Style {
    match variant.style {
        Style::Newtype if variant.fields[0].attrs.skip_serializing() => Style::Unit,
        other => other,
    }
}

enum StructTrait {
    SerializeMap,
    SerializeStruct,
    SerializeStructVariant,
}

impl StructTrait {
    fn serialize_field(&self, span: Span) -> TokenStream {
        match *self {
            StructTrait::SerializeMap => {
                quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
            }
            StructTrait::SerializeStruct => {
                quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field)
            }
            StructTrait::SerializeStructVariant => {
                quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field)
            }
        }
    }

    fn skip_field(&self, span: Span) -> Option<TokenStream> {
        match *self {
            StructTrait::SerializeMap => None,
            StructTrait::SerializeStruct => {
                Some(quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field))
            }
            StructTrait::SerializeStructVariant => {
                Some(quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field))
            }
        }
    }
}

enum TupleTrait {
    SerializeTuple,
    SerializeTupleStruct,
    SerializeTupleVariant,
}

impl TupleTrait {
    fn serialize_element(&self, span: Span) -> TokenStream {
        match *self {
            TupleTrait::SerializeTuple => {
                quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element)
            }
            TupleTrait::SerializeTupleStruct => {
                quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field)
            }
            TupleTrait::SerializeTupleVariant => {
                quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field)
            }
        }
    }
}


# Breaking Changes

List of breaking changes in Vite including API deprecations, removals, and changes. Most of the changes below can be opt-in using the [`future` option](/config/shared-options.html#future) in your Vite config.

## Planned

These changes are planned for the next major version of Vite. The deprecation or usage warnings will guide you where possible, and we're reaching out to framework, plugin authors, and users to apply these changes.

- [`this.environment` in Hooks](/changes/this-environment-in-hooks)
- [HMR `hotUpdate` Plugin Hook](/changes/hotupdate-hook)
- [SSR Using `ModuleRunner` API](/changes/ssr-using-modulerunner)

## Considering

These changes are being considered and are often experimental APIs that intend to improve upon current usage patterns. As not all changes are listed here, please check out the [Experimental Label in Vite GitHub Discussions](https://github.com/vitejs/vite/discussions/categories/feedback?discussions_q=label%3Aexperimental+category%3AFeedback) for the full list.

We don't recommend switching to these APIs yet. They are included in Vite to help us gather feedback. Please check these proposals and let us know how they work in your use case in each's linked GitHub Discussions.

- [Move to Per-environment APIs](/changes/per-environment-apis)
- [Shared Plugins During Build](/changes/shared-plugins-during-build)

## Past

The changes below have been done or reverted. They are no longer relevant in the current major version.

- _No past changes yet_


import type { Mock } from 'vitest'
import { describe, expect, test, vi } from 'vitest'
import { createServer } from '../server'
import { preview } from '../preview'
import { bindCLIShortcuts } from '../shortcuts'

describe('bindCLIShortcuts', () => {
  test.each([
    ['dev server', () => createServer()],
    ['preview server', () => preview()],
  ])('binding custom shortcuts with the %s', async (_, startServer) => {
    const server = await startServer()

    try {
      const xAction = vi.fn()
      const yAction = vi.fn()

      bindCLIShortcuts(
        server,
        {
          customShortcuts: [
            { key: 'x', description: 'test x', action: xAction },
            { key: 'y', description: 'test y', action: yAction },
          ],
        },
        true,
      )

      expect.assert(
        server._shortcutsState?.rl,
        'The readline interface should be defined after binding shortcuts.',
      )
      expect(xAction).not.toHaveBeenCalled()

      server._shortcutsState.rl.emit('line', 'x')
      await vi.waitFor(() => expect(xAction).toHaveBeenCalledOnce())

      const xUpdatedAction = vi.fn()
      const zAction = vi.fn()

      bindCLIShortcuts(
        server,
        {
          customShortcuts: [
            { key: 'x', description: 'test x updated', action: xUpdatedAction },
            { key: 'z', description: 'test z', action: zAction },
          ],
        },
        true,
      )

      expect(xUpdatedAction).not.toHaveBeenCalled()
      server._shortcutsState.rl.emit('line', 'x')
      await vi.waitFor(() => expect(xUpdatedAction).toHaveBeenCalledOnce())

      // Ensure original xAction is not called again
      expect(xAction).toHaveBeenCalledOnce()

      expect(yAction).not.toHaveBeenCalled()
      server._shortcutsState.rl.emit('line', 'y')
      await vi.waitFor(() => expect(yAction).toHaveBeenCalledOnce())

      expect(zAction).not.toHaveBeenCalled()
      server._shortcutsState.rl.emit('line', 'z')
      await vi.waitFor(() => expect(zAction).toHaveBeenCalledOnce())
    } finally {
      await server.close()
    }
  })

  test('rebinds shortcuts after server restart', async () => {
    const manualShortcutAction = vi.fn()
    const pluginShortcutActions: Array<Mock<any>> = []

    const server = await createServer({
      plugins: [
        {
          name: 'custom-shortcut-plugin',
          configureServer(viteDevServer) {
            const action = vi.fn()

            // Keep track of actions created by the plugin
            // To verify if they are overwritten on server restart
            pluginShortcutActions.push(action)

            // Bind custom shortcut from plugin
            bindCLIShortcuts(
              viteDevServer,
              {
                customShortcuts: [
                  {
                    key: 'y',
                    description: 'plugin shortcut',
                    action,
                  },
                ],
              },
              true,
            )
          },
        },
      ],
    })

    try {
      const readline = server._shortcutsState?.rl

      expect.assert(
        readline,
        'The readline interface should be defined after binding shortcuts.',
      )

      readline.emit('line', 'y')
      await vi.waitFor(() => {
        expect(pluginShortcutActions).toHaveLength(1)
        expect(pluginShortcutActions[0]).toHaveBeenCalledOnce()
      })

      // Manually bind another custom shortcut
      bindCLIShortcuts(
        server,
        {
          customShortcuts: [
            {
              key: 'x',
              description: 'manual shortcut',
              action: manualShortcutAction,
            },
          ],
        },
        true,
      )

      readline.emit('line', 'x')
      await vi.waitFor(() =>
        expect(manualShortcutAction).toHaveBeenCalledOnce(),
      )

      // Check the order of shortcuts before restart
      expect(
        server._shortcutsState?.options.customShortcuts?.map((s) => s.key),
      ).toEqual(['x', 'y'])

      // Restart the server
      await server.restart()

      // Shortcut orders should be preserved after restart
      expect(
        server._shortcutsState?.options.customShortcuts?.map((s) => s.key),
      ).toEqual(['x', 'y'])

      expect.assert(
        server._shortcutsState?.rl === readline,
        'The readline interface should be preserved.',
      )

      // Shortcuts should still work after restart
      readline.emit('line', 'x')
      await vi.waitFor(() =>
        expect(manualShortcutAction).toHaveBeenCalledTimes(2),
      )

      readline.emit('line', 'y')
      await vi.waitFor(() => {
        expect(pluginShortcutActions).toHaveLength(2)
        expect(pluginShortcutActions[1]).toHaveBeenCalledOnce()
        expect(pluginShortcutActions[0]).toHaveBeenCalledOnce()
      })
    } finally {
      await server.close()
    }
  })
})


# JSON Lines

The [JSON Lines](https://jsonlines.org) format is a text format of newline-delimited JSON. In particular:

1. The input must be UTF-8 encoded.
2. Every line must be a valid JSON value.
3. The line separator must be `\n`. As `\r` is silently ignored, `\r\n` is also supported.
4. The final character may be `\n`, but is not required to be one.

!!! example "JSON Text example"

    ```json
    {"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
    {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
    {"name": "May", "wins": []}
    {"name": "Deloise", "wins": [["three of a kind", "5♣"]]}
    ```

JSON Lines input with more than one value is treated as invalid JSON by the [`parse`](../../api/basic_json/parse.md) or
[`accept`](../../api/basic_json/accept.md) functions. To process it line by line, functions like
[`std::getline`](https://en.cppreference.com/w/cpp/string/basic_string/getline) can be used:

!!! example "Example: Parse JSON Text input line by line"

    The example below demonstrates how JSON Lines can be processed.

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

    Output:

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

!!! warning "Note"

    Using [`operator>>`](../../api/operator_gtgt.md) like

    ```cpp
    json j;
    while (input >> j)
    {
        std::cout << j << std::endl;
    }
    ```

    with a JSON Lines input does not work, because the parser will try to parse one value after the last one.

    This is different from parsing a stream of *concatenated* (non-newline-delimited) JSON values, for which
    `operator>>` does work, provided that a value that is a number is followed by whitespace -- see its
    [notes](../../api/operator_gtgt.md#notes) for details.


//     __ _____ _____ _____
//  __|  |   __|     |   | |  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"

#ifdef JSON_DIAGNOSTICS
    #undef JSON_DIAGNOSTICS
#endif

#define JSON_DIAGNOSTICS 1

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

TEST_CASE("Better diagnostics")
{
    SECTION("empty JSON Pointer")
    {
        json const j = 1;
        std::string s;
        CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
    }

    SECTION("invalid type")
    {
        json j;
        j["a"]["b"]["c"] = 1;
        std::string s;
        CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
    }

    SECTION("missing key")
    {
        json j;
        j["object"]["object"] = true;
        CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
    }

    SECTION("array index out of range")
    {
        json j;
        j["array"][4] = true;
        CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
    }

    SECTION("array index at wrong type")
    {
        json j;
        j["array"][4] = true;
        CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
    }

    SECTION("wrong iterator")
    {
        json j;
        j["array"] = json::array();
        CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
    }

    SECTION("JSON Pointer escaping")
    {
        json j;
        j["a/b"]["m~n"] = 1;
        std::string s;
        CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
    }

    SECTION("Parse error")
    {
        json _;
        CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error);
    }

    SECTION("Wrong type in update()")
    {
        json j = {{"foo", "bar"}};
        json k = {{"bla", 1}};

        CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
        CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
    }
}

TEST_CASE("Regression tests for extended diagnostics")
{
    SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
    {
        CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
        CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
    }

    SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
    {
        json j;
        j["/foo"] = {1, 2, 3};
        CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
    }

    SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
    {
        // void push_back(basic_json&& val)
        {
            json j_arr = json::array();
            j_arr.push_back(json::object());
            j_arr.push_back(json::object());
            j_arr.push_back(json::object());
            j_arr.push_back(json::object());
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }

        // void push_back(const basic_json& val)
        {
            json j_arr = json::array();
            auto object = json::object();
            j_arr.push_back(object);
            j_arr.push_back(object);
            j_arr.push_back(object);
            j_arr.push_back(object);
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }

        // reference emplace_back(Args&& ... args)
        {
            json j_arr = json::array();
            j_arr.emplace_back(json::object());
            j_arr.emplace_back(json::object());
            j_arr.emplace_back(json::object());
            j_arr.emplace_back(json::object());
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }

        // iterator insert(const_iterator pos, const basic_json& val)
        {
            json j_arr = json::array();
            j_arr.insert(j_arr.begin(), json::object());
            j_arr.insert(j_arr.begin(), json::object());
            j_arr.insert(j_arr.begin(), json::object());
            j_arr.insert(j_arr.begin(), json::object());
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }

        // iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
        {
            json j_arr = json::array();
            j_arr.insert(j_arr.begin(), 2, json::object());
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }

        // iterator insert(const_iterator pos, const_iterator first, const_iterator last)
        {
            json j_arr = json::array();
            json j_objects = {json::object(), json::object()};
            j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
            json j_obj = json::object();
            j_obj["key"] = j_arr;
        }
    }

    SECTION("Regression test for issue #2962 - JSON_DIAGNOSTICS assertion for ordered_json")
    {
        nlohmann::ordered_json j;
        nlohmann::ordered_json j2;
        const std::string value;
        j["first"] = value;
        j["second"] = value;
        j2["something"] = j;
    }

    SECTION("Regression test for issue #3007 - Parent pointers properly set when using update()")
    {
        // void update(const_reference j)
        {
            json j = json::object();

            {
                json j2 = json::object();
                j2["one"] = 1;

                j.update(j2);
            }

            // Must call operator[] on const element, otherwise m_parent gets updated.
            auto const& constJ = j;
            CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
        }

        // void update(const_iterator first, const_iterator last)
        {
            json j = json::object();

            {
                json j2 = json::object();
                j2["one"] = 1;

                j.update(j2.begin(), j2.end());
            }

            // Must call operator[] on const element, otherwise m_parent gets updated.
            auto const& constJ = j;
            CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
        }

        // Code from #3007 triggering unwanted assertion without fix to update().
        {
            json root = json::array();
            json lower = json::object();

            {
                json lowest = json::object();
                lowest["one"] = 1;

                lower.update(lowest);
            }

            root.push_back(lower);
        }
    }

    SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
    {
        // reference operator[](size_type idx)
        {
            json j_arr = json::array();
            j_arr[0] = 0;
            j_arr[1] = 1;
            j_arr[2] = 2;
            j_arr[3] = 3;
            j_arr[4] = 4;
            j_arr[5] = 5;
            j_arr[6] = 6;
            j_arr[7] = 7;
            json const j_arr_copy = j_arr;
        }
    }

    SECTION("Regression test for issue #3915 - JSON_DIAGNOSTICS trigger assertion")
    {
        json j = json::object();
        j["root"] = "root_str";

        json jj = json::object();
        jj["child"] = json::object();

        // If do not push anything in object, then no assert will be produced
        jj["child"]["prop1"] = "prop1_value";

        // Push all properties of child in parent
        j.insert(jj.at("child").begin(), jj.at("child").end());

        // Here assert is generated when construct new json
        const json k(j);

        CHECK(k.dump() == "{\"prop1\":\"prop1_value\",\"root\":\"root_str\"}");
    }

    SECTION("Regression test for issue #4813 - update() with merge_objects=true triggers JSON_ASSERT with JSON_DIAGNOSTICS")
    {
        // https://github.com/nlohmann/json/issues/4813
        nlohmann::ordered_json j1 = {{"numbers", {{"one", 1}}}};
        nlohmann::ordered_json const j2 = {{"numbers", {{"two", 2}}}, {"string", "t"}};
        CHECK_NOTHROW(j1.update(j2, true));
        CHECK(j1["numbers"]["one"] == 1);
        CHECK(j1["numbers"]["two"] == 2);
        CHECK(j1["string"] == "t");
    }
}


Optimize Deps

Optimized Dep

Vue & Vuex


# nlohmann::operator<<(basic_json), nlohmann::operator<<(json_pointer)

```cpp
std::ostream& operator<<(std::ostream& o, const basic_json& j);      // (1)

std::ostream& operator<<(std::ostream& o, const json_pointer& ptr);  // (2)
```

1. Serialize the given JSON value `j` to the output stream `o`. The JSON value will be serialized using the
   [`dump`](basic_json/dump.md) member function.
    - The indentation of the output can be controlled with the member variable `width` of the output stream `o`. For
      instance, using the manipulator `std::setw(4)` on `o` sets the indentation level to `4` and the serialization
      result is the same as calling `dump(4)`.
    - The indentation character can be controlled with the member variable `fill` of the output stream `o`.
      For instance, the manipulator `std::setfill('\\t')` sets indentation to use a tab character rather than the
      default space character.
2. Write a string representation of the given JSON pointer `ptr` to the output stream `o`. The string representation is
   obtained using the [`to_string`](json_pointer/to_string.md) member function.

## Parameters

`o` (in, out)
:   stream to write to

`j` (in)
:   JSON value to serialize

`ptr` (in)
:   JSON pointer to write

## Return value

the stream `o`

## Exceptions

1. Throws [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON
   value is not UTF-8 encoded. Note that unlike the [`dump`](basic_json/dump.md) member functions, no `error_handler`
   can be set.
2. None.

## Complexity

Linear.

## Notes

!!! warning "Deprecation"

    Function  `#!cpp std::ostream& operator<<(std::ostream& o, const basic_json& j)` replaces function
    `#!cpp std::ostream& operator>>(const basic_json& j, std::ostream& o)` which has been deprecated in version 3.0.0.
    It will be removed in version 4.0.0. Please replace calls like `#!cpp j >> o;` with `#!cpp o << j;`.

## Examples

??? example "Example: (1) serialize JSON value to stream"

    The example below shows the serialization with different parameters to `width` to adjust the indentation level.

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

    Output:

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

??? example "Example: (2) write JSON pointer to stream"

    The example below shows how to write a JSON pointer to a stream.

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

    Output:

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

## See also

- [dump](basic_json/dump.md) - serialize to a JSON-formatted string
- [Serialization](../features/serialization.md) - the serialization article

## Version history

1. Added in version 1.0.0. Added support for indentation character and deprecated
   `#!cpp std::ostream& operator>>(const basic_json& j, std::ostream& o)` in version 3.0.0.
2. Added in version 3.11.0.


import path, { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { readFileSync } from 'node:fs'
import type { RollupPluginHooks } from './typeUtils'

const { version } = JSON.parse(
  readFileSync(new URL('../../package.json', import.meta.url)).toString(),
)

export const ROLLUP_HOOKS: RollupPluginHooks[] = [
  'options',
  'buildStart',
  'buildEnd',
  'renderStart',
  'renderError',
  'renderChunk',
  'writeBundle',
  'generateBundle',
  'banner',
  'footer',
  'augmentChunkHash',
  'outputOptions',
  // 'renderDynamicImport',
  // 'resolveFileUrl',
  // 'resolveImportMeta',
  'intro',
  'outro',
  'closeBundle',
  'closeWatcher',
  'load',
  'moduleParsed',
  'watchChange',
  'resolveDynamicImport',
  'resolveId',
  // 'shouldTransformCachedModule',
  'transform',
  'onLog',
]

export const VERSION = version as string

const DEFAULT_MAIN_FIELDS = [
  'browser',
  'module',
  'jsnext:main', // moment still uses this...
  'jsnext',
]
export const DEFAULT_CLIENT_MAIN_FIELDS: readonly string[] =
  Object.freeze(DEFAULT_MAIN_FIELDS)
export const DEFAULT_SERVER_MAIN_FIELDS: readonly string[] = Object.freeze(
  DEFAULT_MAIN_FIELDS.filter((f) => f !== 'browser'),
)

/**
 * A special condition that would be replaced with production or development
 * depending on NODE_ENV env variable
 */
export const DEV_PROD_CONDITION = `development|production` as const

const DEFAULT_CONDITIONS = ['module', 'browser', 'node', DEV_PROD_CONDITION]
export const DEFAULT_CLIENT_CONDITIONS: readonly string[] = Object.freeze(
  DEFAULT_CONDITIONS.filter((c) => c !== 'node'),
)
export const DEFAULT_SERVER_CONDITIONS: readonly string[] = Object.freeze(
  DEFAULT_CONDITIONS.filter((c) => c !== 'browser'),
)

export const DEFAULT_EXTERNAL_CONDITIONS: readonly string[] = Object.freeze([
  'node',
  'module-sync',
])

export const DEFAULT_EXTENSIONS: string[] = [
  '.mjs',
  '.js',
  '.mts',
  '.ts',
  '.jsx',
  '.tsx',
  '.json',
]

/**
 * The browser versions that are included in the Baseline Widely Available on 2025-05-01.
 *
 * This value would be bumped on each major release of Vite.
 *
 * The value is generated by `pnpm generate-target` script.
 */
export const ESBUILD_BASELINE_WIDELY_AVAILABLE_TARGET: string[] = [
  'chrome111',
  'edge111',
  'firefox114',
  'safari16.4',
  'ios16.4',
]

export const DEFAULT_CONFIG_FILES: string[] = [
  'vite.config.js',
  'vite.config.mjs',
  'vite.config.ts',
  'vite.config.cjs',
  'vite.config.mts',
  'vite.config.cts',
]

export const JS_TYPES_RE: RegExp = /\.(?:j|t)sx?$|\.mjs$/

export const CSS_LANGS_RE: RegExp =
  /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/

export const OPTIMIZABLE_ENTRY_RE: RegExp = /\.[cm]?[jt]s$/

export const SPECIAL_QUERY_RE: RegExp = /[?&](?:worker|sharedworker|raw|url)\b/

/**
 * Prefix for resolved fs paths, since windows paths may not be valid as URLs.
 */
export const FS_PREFIX = `/@fs/`

export const CLIENT_PUBLIC_PATH = `/@vite/client`
export const ENV_PUBLIC_PATH = `/@vite/env`
export const VITE_PACKAGE_DIR: string = resolve(
  fileURLToPath(import.meta.url),
  '../../..',
)

export const CLIENT_ENTRY: string = resolve(
  VITE_PACKAGE_DIR,
  'dist/client/client.mjs',
)
export const BUNDLED_DEV_CLIENT_ENTRY: string = resolve(
  VITE_PACKAGE_DIR,
  'dist/client/bundledDevClient.mjs',
)
/** URL filename the bundled-dev server serves the vite client under */
export const BUNDLED_DEV_CLIENT_FILENAME: string = 'bundledDevClient.mjs'
export const ENV_ENTRY: string = resolve(
  VITE_PACKAGE_DIR,
  'dist/client/env.mjs',
)
export const CLIENT_DIR: string = path.dirname(CLIENT_ENTRY)

// ** READ THIS ** before editing `KNOWN_ASSET_TYPES`.
//   If you add an asset to `KNOWN_ASSET_TYPES`, make sure to also add it
//   to the TypeScript declaration file `packages/vite/client.d.ts` and
//   add a mime type to the `registerCustomMime` in
//   `packages/vite/src/node/plugin/assets.ts` if mime type cannot be
//   looked up by mrmime.
//   You can check if the mime type can be looked up by mrmime by running
//   `node --print "require('mrmime').lookup('foo.png')"`
export const KNOWN_ASSET_TYPES: string[] = [
  // images
  'apng',
  'bmp',
  'png',
  'jpe?g',
  'jfif',
  'pjpeg',
  'pjp',
  'gif',
  'svg',
  'ico',
  'webp',
  'avif',
  'cur',
  'jxl',

  // media
  'mp4',
  'webm',
  'ogg',
  'mp3',
  'wav',
  'flac',
  'aac',
  'opus',
  'mov',
  'm4a',
  'vtt',

  // fonts
  'woff2?',
  'eot',
  'ttf',
  'otf',

  // other
  'webmanifest',
  'pdf',
  'txt',
]

export const DEFAULT_ASSETS_RE: RegExp = new RegExp(
  `\\.(` + KNOWN_ASSET_TYPES.join('|') + `)(\\?.*)?$`,
  'i',
)

export const DEP_VERSION_RE: RegExp = /[?&](v=[\w.-]+)\b/

export const loopbackHosts: Set<string> = new Set([
  'localhost',
  '127.0.0.1',
  '::1',
  '0000:0000:0000:0000:0000:0000:0000:0001',
])
export const wildcardHosts: Set<string> = new Set([
  '0.0.0.0',
  '::',
  '0000:0000:0000:0000:0000:0000:0000:0000',
])

export const DEFAULT_DEV_PORT = 5173

export const DEFAULT_PREVIEW_PORT = 4173

export const DEFAULT_ASSETS_INLINE_LIMIT = 4096

// the regex to allow loopback address origins:
// - localhost domains (which will always resolve to the loopback address by RFC 6761 section 6.3)
// - 127.0.0.1
// - ::1
export const defaultAllowedOrigins: RegExp =
  /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/

export const METADATA_FILENAME = '_metadata.json'

export const ERR_OPTIMIZE_DEPS_PROCESSING_ERROR =
  'ERR_OPTIMIZE_DEPS_PROCESSING_ERROR'
export const ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR =
  'ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR'