import type { DepOptimizationConfig } from '../optimizer'
import { mergeWithDefaults } from '../utils'

export type SSRTarget = 'node' | 'webworker'

export type SsrDepOptimizationConfig = DepOptimizationConfig

export interface SSROptions {
  noExternal?: string | RegExp | (string | RegExp)[] | true
  external?: string[] | true

  /**
   * Define the target for the ssr build. The browser field in package.json
   * is ignored for node but used if webworker is the target
   * This option will be removed in a future major version
   * @default 'node'
   */
  target?: SSRTarget

  /**
   * Control over which dependencies are optimized during SSR and esbuild options
   * During build:
   *   no external CJS dependencies are optimized by default
   * During dev:
   *   explicit no external CJS dependencies are optimized by default
   * @experimental
   */
  optimizeDeps?: SsrDepOptimizationConfig

  resolve?: {
    /**
     * Conditions that are used in the plugin pipeline. The default value is the root config's `resolve.conditions`.
     *
     * Use this to override the default ssr conditions for the ssr build.
     *
     * @default rootConfig.resolve.conditions
     */
    conditions?: string[]

    /**
     * Conditions that are used during ssr import (including `ssrLoadModule`) of externalized dependencies.
     *
     * @default ['node', 'module-sync']
     */
    externalConditions?: string[]

    mainFields?: string[]
  }
}

export interface ResolvedSSROptions extends SSROptions {
  target: SSRTarget
  optimizeDeps: SsrDepOptimizationConfig
}

const _ssrConfigDefaults = Object.freeze({
  // noExternal
  // external
  target: 'node',
  optimizeDeps: {},
  // resolve
} satisfies SSROptions)
export const ssrConfigDefaults: Readonly<Partial<SSROptions>> =
  _ssrConfigDefaults

export function resolveSSROptions(
  ssr: SSROptions | undefined,
  preserveSymlinks: boolean,
): ResolvedSSROptions {
  const defaults = mergeWithDefaults(_ssrConfigDefaults, {
    optimizeDeps: { esbuildOptions: { preserveSymlinks } },
  } satisfies SSROptions)
  return mergeWithDefaults(defaults, ssr ?? {})
}


Contributing to {fmt}
=====================

By submitting a pull request or a patch, you represent that you have the right
to license your contribution to the {fmt} project owners and the community,
agree that your contributions are licensed under the {fmt} license, and agree
to future changes to the licensing.

All C++ code must adhere to [Google C++ Style Guide](
https://google.github.io/styleguide/cppguide.html) with the following
exceptions:

* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function and type
  names

All documentation must adhere to the [Google Developer Documentation Style
Guide](https://developers.google.com/style).

Thanks for contributing!


import fs from 'node:fs'
import path from 'node:path'
// eslint-disable-next-line n/no-unsupported-features/node-builtins -- our supported nodejs range supports `parseEnv` but in experimental state, which is fine
import { parseEnv } from 'node:util'
import { getEnvs } from '@voidzero-dev/vite-task-client'
import { type DotenvPopulateInput, expand } from 'dotenv-expand'
import colors from 'picocolors'
import { arraify, createDebugger, normalizePath, tryStatSync } from './utils'
import type { UserConfig } from './config'

const debug = createDebugger('vite:env')

export function getEnvFilesForMode(
  mode: string,
  envDir: string | false,
): string[] {
  if (envDir !== false) {
    return [
      /** default file */ `.env`,
      /** local file */ `.env.local`,
      /** mode file */ `.env.${mode}`,
      /** mode local file */ `.env.${mode}.local`,
    ].map((file) => normalizePath(path.join(envDir, file)))
  }

  return []
}

/**
 * Load `.env` files within the `envDir` and merge them with the matching
 * variables already present in `process.env`.
 */
export function loadEnv(
  mode: string,
  envDir: string | false,
  prefixes: string | string[] = 'VITE_',
): Record<string, string> {
  const start = performance.now()
  const getTime = () => `${(performance.now() - start).toFixed(2)}ms`

  if (mode === 'local') {
    throw new Error(
      `"local" cannot be used as a mode name because it conflicts with ` +
        `the .local postfix for .env files.`,
    )
  }
  prefixes = arraify(prefixes)
  const env: Record<string, string> = {}
  const envFiles = getEnvFilesForMode(mode, envDir)

  debug?.(`loading env files: %O`, envFiles)

  const parsed = Object.fromEntries(
    envFiles.flatMap((filePath) => {
      const stat = tryStatSync(filePath)
      // Support FIFOs (named pipes) for apps like 1Password
      if (!stat || (!stat.isFile() && !stat.isFIFO())) return []

      const parsedEnv = parseEnv(fs.readFileSync(filePath, 'utf-8'))
      return Object.entries(parsedEnv as Record<string, string>)
    }),
  )

  debug?.(`env files loaded in ${getTime()}`)

  // test NODE_ENV override before expand as otherwise process.env.NODE_ENV would override this
  if (parsed.NODE_ENV && process.env.VITE_USER_NODE_ENV === undefined) {
    process.env.VITE_USER_NODE_ENV = parsed.NODE_ENV
  }
  // support BROWSER and BROWSER_ARGS env variables
  if (parsed.BROWSER && process.env.BROWSER === undefined) {
    process.env.BROWSER = parsed.BROWSER
  }
  if (parsed.BROWSER_ARGS && process.env.BROWSER_ARGS === undefined) {
    process.env.BROWSER_ARGS = parsed.BROWSER_ARGS
  }

  // let environment variables use each other. make a copy of `process.env` so that `dotenv-expand`
  // doesn't re-assign the expanded values to the global `process.env`.
  const processEnv = { ...process.env } as DotenvPopulateInput
  expand({ parsed, processEnv })

  // only keys that start with prefix are exposed to client
  for (const [key, value] of Object.entries(parsed)) {
    if (prefixes.some((prefix) => key.startsWith(prefix))) {
      env[key] = value
    }
  }

  // Vite Task may know prefixed envs not present here; fetch them and let
  // the runner record each prefix in the build's cache key.
  for (const prefix of prefixes) {
    Object.assign(env, getEnvs({ prefix }))
  }

  // check if there are actual env variables starting with VITE_*
  // these are typically provided inline and should be prioritized
  for (const key in process.env) {
    if (prefixes.some((prefix) => key.startsWith(prefix))) {
      env[key] = process.env[key]!
    }
  }

  debug?.(`using resolved env: %O`, env)

  return env
}

export function resolveEnvPrefix({
  envPrefix = 'VITE_',
}: UserConfig): string[] {
  envPrefix = arraify(envPrefix)
  if (envPrefix.includes('')) {
    throw new Error(
      `envPrefix option contains value '', which could lead unexpected exposure of sensitive information.`,
    )
  }
  if (envPrefix.some((prefix) => /\s/.test(prefix))) {
    // eslint-disable-next-line no-console
    console.warn(
      colors.yellow(
        `[vite] Warning: envPrefix option contains values with whitespace, which does not work in practice.`,
      ),
    )
  }
  return envPrefix
}


use bstr::ByteSlice;

use grep_matcher::{LineMatchKind, Matcher};

use crate::{
    line_buffer::BinaryDetection,
    lines::{self, LineStep},
    searcher::{Config, Range, Searcher},
    sink::{
        Sink, SinkContext, SinkContextKind, SinkError, SinkFinish, SinkMatch,
    },
};

enum FastMatchResult {
    Continue,
    Stop,
    SwitchToSlow,
}

#[derive(Debug)]
pub(crate) struct Core<'s, M: 's, S> {
    config: &'s Config,
    matcher: M,
    searcher: &'s Searcher,
    sink: S,
    binary: bool,
    pos: usize,
    absolute_byte_offset: u64,
    binary_byte_offset: Option<usize>,
    line_number: Option<u64>,
    last_line_counted: usize,
    last_line_visited: usize,
    after_context_left: usize,
    has_sunk: bool,
    has_matched: bool,
    count: u64,
}

impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
    pub(crate) fn new(
        searcher: &'s Searcher,
        matcher: M,
        sink: S,
        binary: bool,
    ) -> Core<'s, M, S> {
        let line_number =
            if searcher.config.line_number { Some(1) } else { None };
        let core = Core {
            config: &searcher.config,
            matcher,
            searcher,
            sink,
            binary,
            pos: 0,
            absolute_byte_offset: 0,
            binary_byte_offset: None,
            line_number,
            last_line_counted: 0,
            last_line_visited: 0,
            after_context_left: 0,
            has_sunk: false,
            has_matched: false,
            count: 0,
        };
        if !core.searcher.multi_line_with_matcher(&core.matcher) {
            if core.is_line_by_line_fast() {
                log::trace!("searcher core: will use fast line searcher");
            } else {
                log::trace!("searcher core: will use slow line searcher");
            }
        }
        core
    }

    pub(crate) fn pos(&self) -> usize {
        self.pos
    }

    pub(crate) fn set_pos(&mut self, pos: usize) {
        self.pos = pos;
    }

    fn count(&self) -> u64 {
        self.count
    }

    fn increment_count(&mut self) {
        self.count += 1;
    }

    pub(crate) fn binary_byte_offset(&self) -> Option<u64> {
        self.binary_byte_offset.map(|offset| offset as u64)
    }

    pub(crate) fn matcher(&self) -> &M {
        &self.matcher
    }

    pub(crate) fn matched(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        self.sink_matched(buf, range)
    }

    pub(crate) fn binary_data(
        &mut self,
        binary_byte_offset: u64,
    ) -> Result<bool, S::Error> {
        self.sink.binary_data(&self.searcher, binary_byte_offset)
    }

    fn is_match(&self, line: &[u8]) -> Result<bool, S::Error> {
        // We need to strip the line terminator here to match the
        // semantics of line-by-line searching. Namely, regexes
        // like `(?m)^$` can match at the final position beyond a
        // line terminator, which is non-sensical in line oriented
        // matching.
        let line = lines::without_terminator(line, self.config.line_term);
        self.matcher.is_match(line).map_err(S::Error::error_message)
    }

    pub(crate) fn find(
        &mut self,
        slice: &[u8],
    ) -> Result<Option<Range>, S::Error> {
        if self.has_exceeded_match_limit() {
            return Ok(None);
        }
        match self.matcher().find(slice) {
            Err(err) => Err(S::Error::error_message(err)),
            Ok(None) => Ok(None),
            Ok(Some(m)) => {
                self.increment_count();
                Ok(Some(m))
            }
        }
    }

    fn shortest_match(
        &mut self,
        slice: &[u8],
    ) -> Result<Option<usize>, S::Error> {
        if self.has_exceeded_match_limit() {
            return Ok(None);
        }
        match self.matcher.shortest_match(slice) {
            Err(err) => return Err(S::Error::error_message(err)),
            Ok(None) => return Ok(None),
            Ok(Some(m)) => Ok(Some(m)),
        }
    }

    pub(crate) fn begin(&mut self) -> Result<bool, S::Error> {
        self.sink.begin(&self.searcher)
    }

    pub(crate) fn finish(
        &mut self,
        byte_count: u64,
        binary_byte_offset: Option<u64>,
    ) -> Result<(), S::Error> {
        self.sink.finish(
            &self.searcher,
            &SinkFinish { byte_count, binary_byte_offset },
        )
    }

    pub(crate) fn match_by_line(
        &mut self,
        buf: &[u8],
    ) -> Result<bool, S::Error> {
        if self.is_line_by_line_fast() {
            match self.match_by_line_fast(buf)? {
                FastMatchResult::SwitchToSlow => self.match_by_line_slow(buf),
                FastMatchResult::Continue => Ok(true),
                FastMatchResult::Stop => Ok(false),
            }
        } else {
            self.match_by_line_slow(buf)
        }
    }

    pub(crate) fn roll(&mut self, buf: &[u8]) -> usize {
        let consumed = if self.config.max_context() == 0 {
            buf.len()
        } else {
            // It might seem like all we need to care about here is just
            // the "before context," but in order to sink the context
            // separator (when before_context==0 and after_context>0), we
            // need to know something about the position of the previous
            // line visited, even if we're at the beginning of the buffer.
            //
            // ... however, we only need to find the N preceding lines based
            // on before context. We can skip this (potentially costly, for
            // large values of N) step when before_context==0.
            let context_start = lines::preceding(
                buf,
                self.config.line_term.as_byte(),
                self.config.before_context,
            );
            let consumed =
                std::cmp::max(context_start, self.last_line_visited);
            consumed
        };
        self.count_lines(buf, consumed);
        self.absolute_byte_offset += consumed as u64;
        self.last_line_counted = 0;
        self.last_line_visited = 0;
        self.set_pos(buf.len() - consumed);
        consumed
    }

    pub(crate) fn detect_binary(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        if self.binary_byte_offset.is_some() {
            return Ok(self.config.binary.quit_byte().is_some());
        }
        let binary_byte = match self.config.binary.0 {
            BinaryDetection::Quit(b) => b,
            BinaryDetection::Convert(b) => b,
            _ => return Ok(false),
        };
        if let Some(i) = buf[*range].find_byte(binary_byte) {
            let offset = range.start() + i;
            self.binary_byte_offset = Some(offset);
            if !self.binary_data(offset as u64)? {
                return Ok(true);
            }
            Ok(self.config.binary.quit_byte().is_some())
        } else {
            Ok(false)
        }
    }

    pub(crate) fn before_context_by_line(
        &mut self,
        buf: &[u8],
        upto: usize,
    ) -> Result<bool, S::Error> {
        if self.config.before_context == 0 {
            return Ok(true);
        }
        let range = Range::new(self.last_line_visited, upto);
        if range.is_empty() {
            return Ok(true);
        }
        let before_context_start = range.start()
            + lines::preceding(
                &buf[range],
                self.config.line_term.as_byte(),
                self.config.before_context - 1,
            );

        let range = Range::new(before_context_start, range.end());
        let mut stepper = LineStep::new(
            self.config.line_term.as_byte(),
            range.start(),
            range.end(),
        );
        while let Some(line) = stepper.next_match(buf) {
            if !self.sink_break_context(line.start())? {
                return Ok(false);
            }
            if !self.sink_before_context(buf, &line)? {
                return Ok(false);
            }
        }
        Ok(true)
    }

    pub(crate) fn after_context_by_line(
        &mut self,
        buf: &[u8],
        upto: usize,
    ) -> Result<bool, S::Error> {
        if self.after_context_left == 0 {
            return Ok(true);
        }
        let exceeded_match_limit = self.has_exceeded_match_limit();
        let range = Range::new(self.last_line_visited, upto);
        let mut stepper = LineStep::new(
            self.config.line_term.as_byte(),
            range.start(),
            range.end(),
        );
        while let Some(line) = stepper.next_match(buf) {
            if exceeded_match_limit
                && self.is_match(&buf[line])? != self.config.invert_match
            {
                let after_context_left = self.after_context_left;
                self.set_pos(line.end());
                if !self.sink_matched(buf, &line)? {
                    return Ok(false);
                }
                self.after_context_left = after_context_left - 1;
            } else if !self.sink_after_context(buf, &line)? {
                return Ok(false);
            }
            if self.after_context_left == 0 {
                break;
            }
        }
        Ok(true)
    }

    pub(crate) fn other_context_by_line(
        &mut self,
        buf: &[u8],
        upto: usize,
    ) -> Result<bool, S::Error> {
        let range = Range::new(self.last_line_visited, upto);
        let mut stepper = LineStep::new(
            self.config.line_term.as_byte(),
            range.start(),
            range.end(),
        );
        while let Some(line) = stepper.next_match(buf) {
            if !self.sink_other_context(buf, &line)? {
                return Ok(false);
            }
        }
        Ok(true)
    }

    fn match_by_line_slow(&mut self, buf: &[u8]) -> Result<bool, S::Error> {
        debug_assert!(!self.searcher.multi_line_with_matcher(&self.matcher));

        let range = Range::new(self.pos(), buf.len());
        let mut stepper = LineStep::new(
            self.config.line_term.as_byte(),
            range.start(),
            range.end(),
        );
        while let Some(line) = stepper.next_match(buf) {
            if self.has_exceeded_match_limit()
                && !self.config.passthru
                && self.after_context_left == 0
            {
                return Ok(false);
            }
            let matched = {
                // Stripping the line terminator is necessary to prevent some
                // classes of regexes from matching the empty position *after*
                // the end of the line. For example, `(?m)^$` will match at
                // position (2, 2) in the string `a\n`.
                let slice = lines::without_terminator(
                    &buf[line],
                    self.config.line_term,
                );
                self.shortest_match(slice)?.is_some()
            };
            self.set_pos(line.end());

            let success = matched != self.config.invert_match;
            if success {
                self.has_matched = true;
                self.increment_count();
                if !self.before_context_by_line(buf, line.start())? {
                    return Ok(false);
                }
                if !self.sink_matched(buf, &line)? {
                    return Ok(false);
                }
            } else if self.after_context_left >= 1 {
                if !self.sink_after_context(buf, &line)? {
                    return Ok(false);
                }
            } else if self.config.passthru {
                if !self.sink_other_context(buf, &line)? {
                    return Ok(false);
                }
            }
            if self.config.stop_on_nonmatch && !success && self.has_matched {
                return Ok(false);
            }
        }
        Ok(true)
    }

    fn match_by_line_fast(
        &mut self,
        buf: &[u8],
    ) -> Result<FastMatchResult, S::Error> {
        use FastMatchResult::*;

        debug_assert!(!self.config.passthru);
        while !buf[self.pos()..].is_empty() {
            if self.config.stop_on_nonmatch && self.has_matched {
                return Ok(SwitchToSlow);
            }
            if self.config.invert_match {
                if !self.match_by_line_fast_invert(buf)? {
                    break;
                }
            } else if let Some(line) = self.find_by_line_fast(buf)? {
                self.has_matched = true;
                self.increment_count();
                if self.config.max_context() > 0 {
                    if !self.after_context_by_line(buf, line.start())? {
                        return Ok(Stop);
                    }
                    if !self.before_context_by_line(buf, line.start())? {
                        return Ok(Stop);
                    }
                }
                self.set_pos(line.end());
                if !self.sink_matched(buf, &line)? {
                    return Ok(Stop);
                }
            } else {
                break;
            }
        }
        if !self.after_context_by_line(buf, buf.len())? {
            return Ok(Stop);
        }
        if self.has_exceeded_match_limit() && self.after_context_left == 0 {
            return Ok(Stop);
        }
        self.set_pos(buf.len());
        Ok(Continue)
    }

    #[inline(always)]
    fn match_by_line_fast_invert(
        &mut self,
        buf: &[u8],
    ) -> Result<bool, S::Error> {
        assert!(self.config.invert_match);

        let invert_match = match self.find_by_line_fast(buf)? {
            None => {
                let range = Range::new(self.pos(), buf.len());
                self.set_pos(range.end());
                range
            }
            Some(line) => {
                let range = Range::new(self.pos(), line.start());
                self.set_pos(line.end());
                range
            }
        };
        if invert_match.is_empty() {
            return Ok(true);
        }
        self.has_matched = true;
        if !self.after_context_by_line(buf, invert_match.start())? {
            return Ok(false);
        }
        if !self.before_context_by_line(buf, invert_match.start())? {
            return Ok(false);
        }
        let mut stepper = LineStep::new(
            self.config.line_term.as_byte(),
            invert_match.start(),
            invert_match.end(),
        );
        while let Some(line) = stepper.next_match(buf) {
            self.increment_count();
            if !self.sink_matched(buf, &line)? {
                return Ok(false);
            }
            if self.has_exceeded_match_limit() {
                return Ok(false);
            }
        }
        Ok(true)
    }

    #[inline(always)]
    fn find_by_line_fast(
        &mut self,
        buf: &[u8],
    ) -> Result<Option<Range>, S::Error> {
        debug_assert!(!self.searcher.multi_line_with_matcher(&self.matcher));
        debug_assert!(self.is_line_by_line_fast());

        let mut pos = self.pos();
        while !buf[pos..].is_empty() {
            if self.has_exceeded_match_limit() {
                return Ok(None);
            }
            match self.matcher.find_candidate_line(&buf[pos..]) {
                Err(err) => return Err(S::Error::error_message(err)),
                Ok(None) => return Ok(None),
                Ok(Some(LineMatchKind::Confirmed(i))) => {
                    let line = lines::locate(
                        buf,
                        self.config.line_term.as_byte(),
                        Range::zero(i).offset(pos),
                    );
                    // If we matched beyond the end of the buffer, then we
                    // don't report this as a match.
                    if line.start() == buf.len() {
                        pos = buf.len();
                        continue;
                    }
                    return Ok(Some(line));
                }
                Ok(Some(LineMatchKind::Candidate(i))) => {
                    let line = lines::locate(
                        buf,
                        self.config.line_term.as_byte(),
                        Range::zero(i).offset(pos),
                    );
                    if self.is_match(&buf[line])? {
                        return Ok(Some(line));
                    }
                    pos = line.end();
                }
            }
        }
        Ok(None)
    }

    #[inline(always)]
    fn sink_matched(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        if self.binary && self.detect_binary(buf, range)? {
            return Ok(false);
        }
        if !self.sink_break_context(range.start())? {
            return Ok(false);
        }
        self.count_lines(buf, range.start());
        let offset = self.absolute_byte_offset + range.start() as u64;
        let linebuf = &buf[*range];
        let keepgoing = self.sink.matched(
            &self.searcher,
            &SinkMatch {
                line_term: self.config.line_term,
                bytes: linebuf,
                absolute_byte_offset: offset,
                line_number: self.line_number,
                buffer: buf,
                bytes_range_in_buffer: range.start()..range.end(),
            },
        )?;
        if !keepgoing {
            return Ok(false);
        }
        self.last_line_visited = range.end();
        self.after_context_left = self.config.after_context;
        self.has_sunk = true;
        Ok(true)
    }

    fn sink_before_context(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        if self.binary && self.detect_binary(buf, range)? {
            return Ok(false);
        }
        self.count_lines(buf, range.start());
        let offset = self.absolute_byte_offset + range.start() as u64;
        let keepgoing = self.sink.context(
            &self.searcher,
            &SinkContext {
                #[cfg(test)]
                line_term: self.config.line_term,
                bytes: &buf[*range],
                kind: SinkContextKind::Before,
                absolute_byte_offset: offset,
                line_number: self.line_number,
            },
        )?;
        if !keepgoing {
            return Ok(false);
        }
        self.last_line_visited = range.end();
        self.has_sunk = true;
        Ok(true)
    }

    fn sink_after_context(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        assert!(self.after_context_left >= 1);

        if self.binary && self.detect_binary(buf, range)? {
            return Ok(false);
        }
        self.count_lines(buf, range.start());
        let offset = self.absolute_byte_offset + range.start() as u64;
        let keepgoing = self.sink.context(
            &self.searcher,
            &SinkContext {
                #[cfg(test)]
                line_term: self.config.line_term,
                bytes: &buf[*range],
                kind: SinkContextKind::After,
                absolute_byte_offset: offset,
                line_number: self.line_number,
            },
        )?;
        if !keepgoing {
            return Ok(false);
        }
        self.last_line_visited = range.end();
        self.after_context_left -= 1;
        self.has_sunk = true;
        Ok(true)
    }

    fn sink_other_context(
        &mut self,
        buf: &[u8],
        range: &Range,
    ) -> Result<bool, S::Error> {
        if self.binary && self.detect_binary(buf, range)? {
            return Ok(false);
        }
        self.count_lines(buf, range.start());
        let offset = self.absolute_byte_offset + range.start() as u64;
        let keepgoing = self.sink.context(
            &self.searcher,
            &SinkContext {
                #[cfg(test)]
                line_term: self.config.line_term,
                bytes: &buf[*range],
                kind: SinkContextKind::Other,
                absolute_byte_offset: offset,
                line_number: self.line_number,
            },
        )?;
        if !keepgoing {
            return Ok(false);
        }
        self.last_line_visited = range.end();
        self.has_sunk = true;
        Ok(true)
    }

    fn sink_break_context(
        &mut self,
        start_of_line: usize,
    ) -> Result<bool, S::Error> {
        let is_gap = self.last_line_visited < start_of_line;
        let any_context =
            self.config.before_context > 0 || self.config.after_context > 0;

        if !any_context || !self.has_sunk || !is_gap {
            Ok(true)
        } else {
            self.sink.context_break(&self.searcher)
        }
    }

    fn count_lines(&mut self, buf: &[u8], upto: usize) {
        if let Some(ref mut line_number) = self.line_number {
            if self.last_line_counted >= upto {
                return;
            }
            let slice = &buf[self.last_line_counted..upto];
            let count = lines::count(slice, self.config.line_term.as_byte());
            *line_number += count;
            self.last_line_counted = upto;
        }
    }

    fn is_line_by_line_fast(&self) -> bool {
        debug_assert!(!self.searcher.multi_line_with_matcher(&self.matcher));

        if self.config.passthru {
            return false;
        }
        if self.config.stop_on_nonmatch && self.has_matched {
            return false;
        }
        if let Some(line_term) = self.matcher.line_terminator() {
            // FIXME: This works around a bug in grep-regex where it does
            // not set the line terminator of the regex itself, and thus
            // line anchors like `(?m:^)` and `(?m:$)` will not match
            // anything except for `\n`. So for now, we just disable the fast
            // line-by-line searcher which requires the regex to be able to
            // deal with line terminators correctly. The slow line-by-line
            // searcher strips line terminators and thus absolves the regex
            // engine from needing to care about whether they are `\n` or NUL.
            if line_term.as_byte() == b'\x00' {
                return false;
            }
            if line_term == self.config.line_term {
                return true;
            }
        }
        if let Some(non_matching) = self.matcher.non_matching_bytes() {
            // If the line terminator is CRLF, we don't actually need to care
            // whether the regex can match `\r` or not. Namely, a `\r` is
            // neither necessary nor sufficient to terminate a line. A `\n` is
            // always required.
            if non_matching.contains(self.config.line_term.as_byte()) {
                return true;
            }
        }
        false
    }

    fn has_exceeded_match_limit(&self) -> bool {
        self.config.max_matches.map_or(false, |limit| self.count() >= limit)
    }
}


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

```cpp
template<typename ReferenceType>
ReferenceType get_ref();

template<typename ReferenceType>
const ReferenceType get_ref() const;
```

Implicit reference access to the internally stored JSON value. No copies are made.

## Template parameters

`ReferenceType`
:   reference type; must be a reference to [`array_t`](array_t.md), [`object_t`](object_t.md),
    [`string_t`](string_t.md), [`boolean_t`](boolean_t.md), [`number_integer_t`](number_integer_t.md), or
    [`number_unsigned_t`](number_unsigned_t.md), [`number_float_t`](number_float_t.md), or [`binary_t`](binary_t.md).
    Enforced by a static assertion.

## Return value

reference to the internally stored JSON value if the requested reference type fits to the JSON value; throws
[`type_error.303`](../../home/exceptions.md#jsonexceptiontype_error303) otherwise

## Exception safety

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

## Exceptions

Throws [`type_error.303`](../../home/exceptions.md#jsonexceptiontype_error303) if the requested reference type does not
match the stored JSON value type; example: `"incompatible ReferenceType for get_ref, actual type is binary"`.

## Complexity

Constant.

## Notes

!!! danger "Undefined behavior"

    The reference becomes invalid if the underlying JSON object changes.

## Examples

??? example

    The example shows several calls to `get_ref()`.

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

    Output:

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

## See also

- [get_ptr()](get_ptr.md) get a pointer value

## Version history

- Added in version 1.1.0.
- Extended to binary types in version 3.8.0.


import path from 'node:path'
import { type ServerOptions, type ViteDevServer, createServer } from 'vite'
import { afterEach, describe, expect, test } from 'vitest'
import { hmrPorts, isServe, page, ports } from '~utils'

let server: ViteDevServer

afterEach(async () => {
  await server?.close()
})

async function testClientReload(serverOptions: ServerOptions) {
  // start server
  server = await createServer({
    root: path.resolve(import.meta.dirname, '..'),
    logLevel: 'silent',
    server: {
      strictPort: true,
      ...serverOptions,
    },
  })

  await server.listen()
  const serverUrl = server.resolvedUrls.local[0]

  // open page and wait for connection
  const connectedPromise = page.waitForEvent('console', {
    predicate: (message) => message.text().includes('[vite] connected.'),
    timeout: 5000,
  })
  await page.goto(serverUrl)
  await connectedPromise

  // input state
  await page.locator('input').fill('hello')

  // restart and wait for reconnection after reload
  const reConnectedPromise = page.waitForEvent('console', {
    predicate: (message) => message.text().includes('[vite] connected.'),
    timeout: 5000,
  })
  await server.restart()
  await reConnectedPromise
  expect(await page.textContent('input')).toBe('')
}

describe.runIf(isServe)('client-reload', () => {
  test('default', async () => {
    await testClientReload({
      port: ports['client-reload'],
    })
  })

  test('custom hmr port', async () => {
    await testClientReload({
      port: ports['client-reload/hmr-port'],
      ws: {
        port: hmrPorts['client-reload/hmr-port'],
      },
    })
  })

  test('custom hmr port and cross origin isolation', async () => {
    await testClientReload({
      port: ports['client-reload/cross-origin'],
      ws: {
        port: hmrPorts['client-reload/cross-origin'],
      },
      headers: {
        'Cross-Origin-Embedder-Policy': 'require-corp',
        'Cross-Origin-Opener-Policy': 'same-origin',
      },
    })
  })
})


import path from 'node:path'
import { setTimeout } from 'node:timers/promises'
import { afterEach, expect, test } from 'vitest'
import type { ViteDevServer } from '../..'
import { createLogger, createServer } from '../..'
import { promiseWithResolvers } from '../../../shared/utils'

let server: ViteDevServer | undefined

afterEach(async () => {
  await server?.close()
  server = undefined
})

// regression test for https://github.com/vitejs/vite/issues/22847
// When a module importing a bare specifier is transformed between `createServer()`
// and `server.listen()`, the dep is discovered and a debounced optimizer run starts
// before `depsOptimizer.init()` runs. `init()` then resets the optimizer metadata
// while that run is in flight, so `commitProcessing()` reads a dep from the run
// result that no longer exists in the (freshly reset) metadata and crashes with
// `Cannot read properties of undefined (reading 'browserHash')`.
test('does not crash when a dep is discovered before the server starts listening', async () => {
  const errors: string[] = []
  const logger = createLogger('error')
  logger.error = (msg) => {
    errors.push(typeof msg === 'string' ? msg : String(msg))
  }

  const bundleStarted = promiseWithResolvers<void>()

  server = await createServer({
    configFile: false,
    customLogger: logger,
    root: path.join(
      import.meta.dirname,
      '../fixtures/optimizer-discover-before-listen',
    ),
    // the fixture has no package.json, so the default cacheDir would land in
    // the shared __tests__/node_modules/.vite and this test's optimizer
    // (force: true) would delete the deps of other specs running in parallel
    // (e.g. scan.spec.ts reading deps_ssr while its module runner is active)
    cacheDir: 'node_modules/.vite',
    environments: {
      ssr: {
        resolve: {
          noExternal: true,
        },
        optimizeDeps: {
          // ensure a cold cache so the optimizer takes the crawl-end path in init()
          force: true,
          noDiscovery: false,
          rolldownOptions: {
            plugins: [
              {
                name: 'test:signal-pre-bundle',
                buildStart() {
                  bundleStarted.resolve()
                },
              },
            ],
          },
        },
      },
    },
  })

  const ssr = server.environments.ssr

  // Transform a module importing a bare specifier before the server is listening.
  // This discovers the dep and, on the buggy code, schedules a debounced optimizer
  // run, all before `depsOptimizer.init()` has run (init is deferred to `listen()`).
  await ssr.transformRequest('/entry.js')

  // The bug is that discovering a dep here schedules a debounced optimizer run
  // that races with `depsOptimizer.init()` (deferred to `listen()`) resetting the
  // metadata, crashing in `commitProcessing`. The fix's guarantee is that no run
  // starts before `init()`, so assert exactly that: fail if one starts.
  const preInitRunStarted = await Promise.race([
    bundleStarted.promise.then(() => true),
    setTimeout(300, false),
  ])
  expect(preInitRunStarted).toBe(false)

  // Starting the server runs `init()`, which scans and optimizes in the
  // background. Wait for that startup work to settle so any optimizer error has
  // surfaced (this also backstops the probe above: a run that somehow starts just
  // after it would still crash here), then assert none was logged.
  await server.listen()
  await ssr.waitForRequestsIdle()
  await ssr.depsOptimizer?.scanProcessing
  expect(errors).toStrictEqual([])
})


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

#pragma once

// This file contains all macro definitions affecting or depending on the ABI

#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
            #warning "Already included a different version of the library!"
        #endif
    #endif
#endif

#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 12  // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 0   // NOLINT(modernize-macro-to-enum)

#ifndef JSON_DIAGNOSTICS
    #define JSON_DIAGNOSTICS 0
#endif

#ifndef JSON_DIAGNOSTIC_POSITIONS
    #define JSON_DIAGNOSTIC_POSITIONS 0
#endif

#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif

#if JSON_DIAGNOSTICS
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
#endif

#if JSON_DIAGNOSTIC_POSITIONS
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
#else
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
#endif

#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
#else
    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
#endif

#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
#endif

// Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)

#define NLOHMANN_JSON_ABI_TAGS                                       \
    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
            NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)

// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
    _v ## major ## _ ## minor ## _ ## patch
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)

#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_VERSION
#else
#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
                                           NLOHMANN_JSON_VERSION_MINOR, \
                                           NLOHMANN_JSON_VERSION_PATCH)
#endif

// Combine namespace components
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)

#ifndef NLOHMANN_JSON_NAMESPACE
#define NLOHMANN_JSON_NAMESPACE               \
    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
            NLOHMANN_JSON_ABI_TAGS,           \
            NLOHMANN_JSON_NAMESPACE_VERSION)
#endif

#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
    namespace nlohmann                               \
    {                                                \
    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
                NLOHMANN_JSON_ABI_TAGS,              \
                NLOHMANN_JSON_NAMESPACE_VERSION)     \
    {
#endif

#ifndef NLOHMANN_JSON_NAMESPACE_END
#define NLOHMANN_JSON_NAMESPACE_END                                     \
    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
    }  // namespace nlohmann
#endif


import aliasPlugin, { type ResolverFunction } from '@rollup/plugin-alias'
import colors from 'picocolors'
import type { ObjectHook } from 'rolldown'
import {
  viteAliasPlugin as nativeAliasPlugin,
  viteJsonPlugin as nativeJsonPlugin,
  oxcRuntimePlugin,
} from 'rolldown/experimental'
import type { PluginHookUtils, ResolvedConfig } from '../config'
import {
  type HookHandler,
  type Plugin,
  type PluginWithRequiredHook,
} from '../plugin'
import { watchPackageDataPlugin } from '../packages'
import { resolveBuildPlugins } from '../build'
import { oxcResolvePlugin } from './resolve'
import { optimizedDepsPlugin } from './optimizedDeps'
import { importAnalysisPlugin } from './importAnalysis'
import { cssAnalysisPlugin, cssPlugin, cssPostPlugin } from './css'
import { assetPlugin } from './asset'
import { clientInjectionsPlugin } from './clientInjections'
import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html'
import { wasmHelperPlugin } from './wasm'
import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill'
import { webWorkerPlugin } from './worker'
import { preAliasPlugin } from './preAlias'
import { definePlugin } from './define'
import { workerImportMetaUrlPlugin } from './workerImportMetaUrl'
import { assetImportMetaUrlPlugin } from './assetImportMetaUrl'
import { dynamicImportVarsPlugin } from './dynamicImportVars'
import { importGlobPlugin } from './importMetaGlob'
import {
  type PluginFilter,
  type TransformHookFilter,
  createFilterForTransform,
  createIdFilter,
} from './pluginFilter'
import { forwardConsolePlugin } from './forwardConsole'
import { oxcPlugin } from './oxc'
import { esbuildBannerFooterCompatPlugin } from './esbuildBannerFooterCompatPlugin'

export async function resolvePlugins(
  config: ResolvedConfig,
  prePlugins: Plugin[],
  normalPlugins: Plugin[],
  postPlugins: Plugin[],
): Promise<Plugin[]> {
  const isBuild = config.command === 'build'
  const isWorker = config.isWorker
  const anyEnvBundled =
    isBuild || Object.values(config.environments).some((env) => env.isBundled)
  const buildPlugins = anyEnvBundled
    ? resolveBuildPlugins(config)
    : { pre: [], post: [] }
  const devtoolsIntegrationPlugin =
    config.devtools.enabled && !isWorker
      ? await loadDevToolsIntegrationPlugin(config)
      : null
  const { modulePreload } = config.build

  return [
    optimizedDepsPlugin(),
    !isWorker ? watchPackageDataPlugin(config.packageCache) : null,
    preAliasPlugin(config),
    {
      ...aliasPlugin({
        // @ts-expect-error aliasPlugin receives rollup types
        entries: config.resolve.alias,
        customResolver: viteAliasCustomResolver,
      }),
      applyToEnvironment(environment) {
        if (
          environment.config.isBundled &&
          !environment.config.resolve.alias.some((v) => v.customResolver)
        ) {
          return nativeAliasPlugin({
            entries: config.resolve.alias.map((item) => {
              return {
                find: item.find,
                replacement: item.replacement,
              }
            }),
          })
        }
        return true
      },
    } as Plugin,

    ...prePlugins,

    modulePreload !== false && modulePreload.polyfill
      ? modulePreloadPolyfillPlugin()
      : null,
    ...oxcResolvePlugin(
      {
        root: config.root,
        isProduction: config.isProduction,
        isBuild,
        packageCache: config.packageCache,
        asSrc: true,
        optimizeDeps: true,
        externalize: true,
        legacyInconsistentCjsInterop: config.legacy?.inconsistentCjsInterop,
      },
      isWorker
        ? {
            ...config,
            consumer: 'client',
            isBundled: true,
            optimizeDepsPluginNames: [],
          }
        : undefined,
    ),
    htmlInlineProxyPlugin(config),
    cssPlugin(config),
    esbuildBannerFooterCompatPlugin(config),
    // @oxc-project/runtime resolution is handled by rolldown in build
    config.oxc !== false
      ? ({
          ...oxcRuntimePlugin(),
          applyToEnvironment(environment) {
            return !environment.config.isBundled
          },
        } satisfies Plugin)
      : null,
    config.oxc !== false ? oxcPlugin(config) : null,
    nativeJsonPlugin({ ...config.json, minify: isBuild }),
    wasmHelperPlugin(),
    webWorkerPlugin(config),
    assetPlugin(config),
    // for now client only
    config.server.forwardConsole.enabled &&
      forwardConsolePlugin({ environments: ['client'] }),

    ...normalPlugins,

    definePlugin(config),
    cssPostPlugin(config),
    buildHtmlPlugin(config),
    workerImportMetaUrlPlugin(config),
    assetImportMetaUrlPlugin(config),
    ...buildPlugins.pre,
    dynamicImportVarsPlugin(config),
    importGlobPlugin(config),

    ...postPlugins,

    ...buildPlugins.post,
    devtoolsIntegrationPlugin,

    // internal server-only plugins are always applied after everything else
    clientInjectionsPlugin(config),
    cssAnalysisPlugin(config),
    importAnalysisPlugin(config),
  ].filter(Boolean) as Plugin[]
}

async function loadDevToolsIntegrationPlugin(
  config: ResolvedConfig,
): Promise<Plugin | null> {
  try {
    const { DevToolsIntegration } = await import('@vitejs/devtools/integration')
    return DevToolsIntegration({ config })
  } catch (error: any) {
    config.logger.error(
      colors.red(
        `Failed to load Vite DevTools integration: ${error?.message || error?.stack}`,
      ),
      { error },
    )
    return null
  }
}

export function createPluginHookUtils(
  plugins: readonly Plugin[],
): PluginHookUtils {
  // sort plugins per hook
  const sortedPluginsCache = new Map<keyof Plugin, Plugin[]>()
  function getSortedPlugins<K extends keyof Plugin>(
    hookName: K,
  ): PluginWithRequiredHook<K>[] {
    if (sortedPluginsCache.has(hookName))
      return sortedPluginsCache.get(hookName) as PluginWithRequiredHook<K>[]
    const sorted = getSortedPluginsByHook(hookName, plugins)
    sortedPluginsCache.set(hookName, sorted)
    return sorted
  }
  function getSortedPluginHooks<K extends keyof Plugin>(
    hookName: K,
  ): NonNullable<HookHandler<Plugin[K]>>[] {
    const plugins = getSortedPlugins(hookName)
    return plugins.map((p) => getHookHandler(p[hookName])).filter(Boolean)
  }

  return {
    getSortedPlugins,
    getSortedPluginHooks,
  }
}

export function getSortedPluginsByHook<K extends keyof Plugin>(
  hookName: K,
  plugins: readonly Plugin[],
): PluginWithRequiredHook<K>[] {
  const sortedPlugins: Plugin[] = []
  // Use indexes to track and insert the ordered plugins directly in the
  // resulting array to avoid creating 3 extra temporary arrays per hook
  let pre = 0,
    normal = 0,
    post = 0
  for (const plugin of plugins) {
    const hook = plugin[hookName]
    if (hook) {
      if (typeof hook === 'object') {
        if (hook.order === 'pre') {
          sortedPlugins.splice(pre++, 0, plugin)
          continue
        }
        if (hook.order === 'post') {
          sortedPlugins.splice(pre + normal + post++, 0, plugin)
          continue
        }
      }
      sortedPlugins.splice(pre + normal++, 0, plugin)
    }
  }

  return sortedPlugins as PluginWithRequiredHook<K>[]
}

export function getHookHandler<T extends ObjectHook<Function>>(
  hook: T,
): HookHandler<T> {
  return (typeof hook === 'object' ? hook.handler : hook) as HookHandler<T>
}

type FilterForPluginValue = {
  resolveId?: PluginFilter | undefined
  load?: PluginFilter | undefined
  transform?: TransformHookFilter | undefined
}
const filterForPlugin = new WeakMap<Plugin, FilterForPluginValue>()

export function getCachedFilterForPlugin<
  H extends 'resolveId' | 'load' | 'transform',
>(plugin: Plugin, hookName: H): FilterForPluginValue[H] | undefined {
  let filters = filterForPlugin.get(plugin)
  if (filters && hookName in filters) {
    return filters[hookName]
  }

  if (!filters) {
    filters = {}
    filterForPlugin.set(plugin, filters)
  }

  let filter: PluginFilter | TransformHookFilter | undefined
  switch (hookName) {
    case 'resolveId': {
      const rawFilter = extractFilter(plugin.resolveId)?.id
      filters.resolveId = createIdFilter(rawFilter)
      filter = filters.resolveId
      break
    }
    case 'load': {
      const rawFilter = extractFilter(plugin.load)?.id
      filters.load = createIdFilter(rawFilter)
      filter = filters.load
      break
    }
    case 'transform': {
      const rawFilters = extractFilter(plugin.transform)
      filters.transform = createFilterForTransform(
        rawFilters?.id,
        rawFilters?.code,
        rawFilters?.moduleType,
      )
      filter = filters.transform
      break
    }
  }
  return filter as FilterForPluginValue[H] | undefined
}

function extractFilter<T extends Function, F>(
  hook: ObjectHook<T, { filter?: F }> | undefined,
) {
  return hook && 'filter' in hook && hook.filter ? hook.filter : undefined
}

// Same as `@rollup/plugin-alias` default resolver, but we attach additional meta
// if we can't resolve to something, which will error in `importAnalysis`
export const viteAliasCustomResolver: ResolverFunction = async function (
  id,
  importer,
  options,
) {
  const resolved = await this.resolve(id, importer, options)
  return resolved || { id, meta: { 'vite:alias': { noResolved: true } } }
}


// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_

#ifndef FMT_MODULE
#  include <fstream>  // std::filebuf
#endif

#ifdef _WIN32
#  ifdef __GLIBCXX__
#    include <ext/stdio_filebuf.h>
#    include <ext/stdio_sync_filebuf.h>
#  endif
#  include <io.h>
#endif

#include "chrono.h"  // formatbuf

#ifdef _MSVC_STL_UPDATE
#  define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912  // VS 15.5
#  define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
#  define FMT_MSVC_STL_UPDATE 0
#endif

FMT_BEGIN_NAMESPACE
namespace detail {

// Generate a unique explicit instantiation in every translation unit using a
// tag type in an anonymous namespace.
namespace {
struct file_access_tag {};
}  // namespace
template <typename Tag, typename BufType, FILE* BufType::* FileMemberPtr>
class file_access {
  friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};

#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
                           &std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif

// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
  const Char* buf_data = buf.data();
  using unsigned_streamsize = make_unsigned_t<std::streamsize>;
  unsigned_streamsize size = buf.size();
  unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
  do {
    unsigned_streamsize n = size <= max_size ? size : max_size;
    os.write(buf_data, static_cast<std::streamsize>(n));
    buf_data += n;
    size -= n;
  } while (size != 0);
}

template <typename T> struct streamed_view {
  const T& value;
};
}  // namespace detail

FMT_BEGIN_EXPORT

// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
  void set_debug_format() = delete;

  template <typename T, typename Context>
  auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
    auto buffer = basic_memory_buffer<Char>();
    auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
    auto&& output = std::basic_ostream<Char>(&formatbuf);
    output.imbue(std::locale::classic());  // The default is always unlocalized.
    output << value;
    output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
    return formatter<basic_string_view<Char>, Char>::format(
        {buffer.data(), buffer.size()}, ctx);
  }
};

using ostream_formatter = basic_ostream_formatter<char>;

template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
    : basic_ostream_formatter<Char> {
  template <typename Context>
  auto format(detail::streamed_view<T> view, Context& ctx) const
      -> decltype(ctx.out()) {
    return basic_ostream_formatter<Char>::format(view.value, ctx);
  }
};

/**
 * Returns a view that formats `value` via an ostream `operator<<`.
 *
 * **Example**:
 *
 *     fmt::print("Current thread id: {}\n",
 *                fmt::streamed(std::this_thread::get_id()));
 */
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
  return {value};
}

inline void vprint(std::ostream& os, string_view fmt, format_args args) {
  auto buffer = memory_buffer();
  detail::vformat_to(buffer, fmt, args);
  FILE* f = nullptr;
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
  if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
    f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
  auto* rdbuf = os.rdbuf();
  if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
    f = sfbuf->file();
  else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
    f = fbuf->file();
#endif
#ifdef _WIN32
  if (f) {
    int fd = _fileno(f);
    if (_isatty(fd)) {
      os.flush();
      if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
    }
  }
#endif
  detail::ignore_unused(f);
  detail::write_buffer(os, buffer);
}

/**
 * Prints formatted data to the stream `os`.
 *
 * **Example**:
 *
 *     fmt::print(cerr, "Don't {}!", "panic");
 */
template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
  fmt::vargs<T...> vargs = {{args...}};
  if FMT_CONSTEXPR20 (detail::use_utf8) return vprint(os, fmt.str, vargs);
  auto buffer = memory_buffer();
  detail::vformat_to(buffer, fmt.str, vargs);
  detail::write_buffer(os, buffer);
}

template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
  fmt::print(os, FMT_STRING("{}\n"),
             fmt::format(fmt, std::forward<T>(args)...));
}

FMT_END_EXPORT
FMT_END_NAMESPACE

#endif  // FMT_OSTREAM_H_


import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    outDir: 'dist/preload-disabled',
    minify: 'terser',
    terserOptions: {
      format: {
        beautify: true,
      },
      compress: {
        passes: 3,
      },
    },
    rolldownOptions: {
      output: {
        // manualChunks(id) {
        //   if (id.includes('chunk.js')) {
        //     return 'chunk'
        //   }
        // },
      },
    },
    modulePreload: false,
  },
  cacheDir: 'node_modules/.vite-preload-disabled',
})


import { describe, expect, test } from 'vitest'
import { isFileInTargetPath } from '../static'

describe('isFileInTargetPath', () => {
  const cases = {
    '/parent': {
      '/parent': true,
      '/parenta': false,
      '/parent/': true,
      '/parent/child': true,
      '/parent/child/child2': true,
    },
    '/parent/': {
      '/parent': false,
      '/parenta': false,
      '/parent/': true,
      '/parent/child': true,
      '/parent/child/child2': true,
    },
  }

  for (const [parent, children] of Object.entries(cases)) {
    for (const [child, expected] of Object.entries(children)) {
      test(`isFileInTargetPath("${parent}", "${child}")`, () => {
        expect(isFileInTargetPath(parent, child)).toBe(expected)
      })
    }
  }
})


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

```cpp
// (1)
template<class ValueType>
ValueType value(const typename object_t::key_type& key,
                ValueType&& default_value) const;

// (2)
template<class ValueType, class KeyType>
ValueType value(KeyType&& key,
                ValueType&& default_value) const;

// (3)
template<class ValueType>
ValueType value(const json_pointer& ptr,
                const ValueType& default_value) const;
```

This is equivalent to Python's `dict.get(key, default)`.

1. Returns either a copy of an object's element at the specified key `key` or a given default value if no element with
   key `key` exists.

    The function is basically equivalent to executing
    ```cpp
    try {
       return at(key);
    } catch(out_of_range) {
       return default_value;
    }
    ```

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.

3. Returns either a copy of an object's element at the specified JSON pointer `ptr` or a given default value if no value
   at `ptr` exists.

    The function is basically equivalent to executing
    ```cpp
    try {
       return at(ptr);
    } catch(out_of_range) {
       return default_value;
    }
    ```

!!! note "Differences to `at` and `operator[]`"

    - Unlike [`at`](at.md), this function does not throw if the given `key`/`ptr` was not found.
    - Unlike [`operator[]`](operator[].md), this function does not implicitly add an element to the position defined by
     `key`/`ptr` key. This function is furthermore also applicable to const objects.

## 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).
`ValueType`
:   type compatible to JSON values, for instance `#!cpp int` for JSON integer numbers, `#!cpp bool` for JSON booleans,
    or `#!cpp std::vector` types for JSON arrays. Note the type of the expected value at `key`/`ptr` and the default
    value `default_value` must be compatible.

## Parameters

`key` (in)
:   key of the element to access

`default_value` (in)
:   the value to return if `key`/`ptr` found no value

`ptr` (in)
:   a JSON pointer to the element to access

## Return value

1. copy of the element at key `key` or `default_value` if `key` is not found
2. copy of the element at key `key` or `default_value` if `key` is not found
3. copy of the element at JSON Pointer `ptr` or `default_value` if no value for `ptr` is found

## Exception safety

Strong guarantee: if an exception is thrown, there are no
changes to any JSON value.

## Exceptions

1. The function can throw the following exceptions:
    - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match
      the type of the value at `key`
    - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object;
      in that case, using `value()` with a key makes no sense.
2. See 1.
3. The function can throw the following exceptions:
    - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match
      the type of the value at `ptr`
    - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an array
      or object; in that case, using `value()` with a JSON pointer makes no sense.
    - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed
      JSON pointer `ptr` begins with '0'.
    - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed
      JSON pointer `ptr` is not a number.

## Complexity

1. Logarithmic in the size of the container.
2. Logarithmic in the size of the container.
3. Logarithmic in the size of the container.

## Notes

!!! warning "Return type"

    The value function is a template, and the return type of the function is determined by the type of the provided
    default value unless otherwise specified. This can have unexpected effects. In the example below, we store a 64-bit
    unsigned integer. We get exactly that value when using [`operator[]`](operator[].md). However, when we call `value`
    and provide `#!c 0` as default value, then `#!c -1` is returned. This occurs, because `#!c 0` has type `#!c int`
    which overflows when handling the value `#!c 18446744073709551615`.

    To address this issue, either provide a correctly typed default value or use the template parameter to specify the
    desired return type. Note that this issue occurs even when a value is stored at the provided key, and the default
    value is not used as the return value.

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

    Output:

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

## Examples

??? example "Example: (1) access specified object element with default value"

    The example below shows how object elements can be queried with a default value.

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

    Output:

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

??? example "Example: (2) access specified object element using string_view with default value"

    The example below shows how object elements can be queried with a default value.

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

    Output:

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

??? example "Example: (3) access specified object element via JSON Pointer with default value"

    The example below shows how object elements can be queried with a default value.

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

    Output:

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

## See also

- see [`at`](at.md) for access by reference with range checking
- see [`operator[]`](operator%5B%5D.md) for unchecked access by reference

## Version history

1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
   `ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
   `default_value`, as documented).


/*!
Defines a very high level "search worker" abstraction.

A search worker manages the high level interaction points between the matcher
(i.e., which regex engine is used), the searcher (i.e., how data is actually
read and matched using the regex engine) and the printer. For example, the
search worker is where things like preprocessors or decompression happens.
*/

use std::{io, path::Path};

use {grep::matcher::Matcher, termcolor::WriteColor};

/// The configuration for the search worker.
///
/// Among a few other things, the configuration primarily controls the way we
/// show search results to users at a very high level.
#[derive(Clone, Debug)]
struct Config {
    preprocessor: Option<std::path::PathBuf>,
    preprocessor_globs: ignore::overrides::Override,
    search_zip: bool,
    binary_implicit: grep::searcher::BinaryDetection,
    binary_explicit: grep::searcher::BinaryDetection,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            preprocessor: None,
            preprocessor_globs: ignore::overrides::Override::empty(),
            search_zip: false,
            binary_implicit: grep::searcher::BinaryDetection::none(),
            binary_explicit: grep::searcher::BinaryDetection::none(),
        }
    }
}

/// A builder for configuring and constructing a search worker.
#[derive(Clone, Debug)]
pub(crate) struct SearchWorkerBuilder {
    config: Config,
    command_builder: grep::cli::CommandReaderBuilder,
}

impl Default for SearchWorkerBuilder {
    fn default() -> SearchWorkerBuilder {
        SearchWorkerBuilder::new()
    }
}

impl SearchWorkerBuilder {
    /// Create a new builder for configuring and constructing a search worker.
    pub(crate) fn new() -> SearchWorkerBuilder {
        let mut command_builder = grep::cli::CommandReaderBuilder::new();
        command_builder.async_stderr(true);

        SearchWorkerBuilder { config: Config::default(), command_builder }
    }

    /// Create a new search worker using the given searcher, matcher and
    /// printer.
    pub(crate) fn build<W: WriteColor>(
        &self,
        matcher: PatternMatcher,
        searcher: grep::searcher::Searcher,
        printer: Printer<W>,
    ) -> SearchWorker<W> {
        let config = self.config.clone();
        let command_builder = self.command_builder.clone();
        let decomp_builder = config.search_zip.then(|| {
            let mut decomp_builder =
                grep::cli::DecompressionReaderBuilder::new();
            decomp_builder.async_stderr(true);
            decomp_builder
        });
        SearchWorker {
            config,
            command_builder,
            decomp_builder,
            matcher,
            searcher,
            printer,
        }
    }

    /// Set the path to a preprocessor command.
    ///
    /// When this is set, instead of searching files directly, the given
    /// command will be run with the file path as the first argument, and the
    /// output of that command will be searched instead.
    pub(crate) fn preprocessor(
        &mut self,
        cmd: Option<std::path::PathBuf>,
    ) -> anyhow::Result<&mut SearchWorkerBuilder> {
        if let Some(ref prog) = cmd {
            let bin = grep::cli::resolve_binary(prog)?;
            self.config.preprocessor = Some(bin);
        } else {
            self.config.preprocessor = None;
        }
        Ok(self)
    }

    /// Set the globs for determining which files should be run through the
    /// preprocessor. By default, with no globs and a preprocessor specified,
    /// every file is run through the preprocessor.
    pub(crate) fn preprocessor_globs(
        &mut self,
        globs: ignore::overrides::Override,
    ) -> &mut SearchWorkerBuilder {
        self.config.preprocessor_globs = globs;
        self
    }

    /// Enable the decompression and searching of common compressed files.
    ///
    /// When enabled, if a particular file path is recognized as a compressed
    /// file, then it is decompressed before searching.
    ///
    /// Note that if a preprocessor command is set, then it overrides this
    /// setting.
    pub(crate) fn search_zip(
        &mut self,
        yes: bool,
    ) -> &mut SearchWorkerBuilder {
        self.config.search_zip = yes;
        self
    }

    /// Set the binary detection that should be used when searching files
    /// found via a recursive directory search.
    ///
    /// Generally, this binary detection may be
    /// `grep::searcher::BinaryDetection::quit` if we want to skip binary files
    /// completely.
    ///
    /// By default, no binary detection is performed.
    pub(crate) fn binary_detection_implicit(
        &mut self,
        detection: grep::searcher::BinaryDetection,
    ) -> &mut SearchWorkerBuilder {
        self.config.binary_implicit = detection;
        self
    }

    /// Set the binary detection that should be used when searching files
    /// explicitly supplied by an end user.
    ///
    /// Generally, this binary detection should NOT be
    /// `grep::searcher::BinaryDetection::quit`, since we never want to
    /// automatically filter files supplied by the end user.
    ///
    /// By default, no binary detection is performed.
    pub(crate) fn binary_detection_explicit(
        &mut self,
        detection: grep::searcher::BinaryDetection,
    ) -> &mut SearchWorkerBuilder {
        self.config.binary_explicit = detection;
        self
    }
}

/// The result of executing a search.
///
/// Generally speaking, the "result" of a search is sent to a printer, which
/// writes results to an underlying writer such as stdout or a file. However,
/// every search also has some aggregate statistics or meta data that may be
/// useful to higher level routines.
#[derive(Clone, Debug, Default)]
pub(crate) struct SearchResult {
    has_match: bool,
    stats: Option<grep::printer::Stats>,
}

impl SearchResult {
    /// Whether the search found a match or not.
    pub(crate) fn has_match(&self) -> bool {
        self.has_match
    }

    /// Return aggregate search statistics for a single search, if available.
    ///
    /// It can be expensive to compute statistics, so these are only present
    /// if explicitly enabled in the printer provided by the caller.
    pub(crate) fn stats(&self) -> Option<&grep::printer::Stats> {
        self.stats.as_ref()
    }
}

/// The pattern matcher used by a search worker.
#[derive(Clone, Debug)]
pub(crate) enum PatternMatcher {
    RustRegex(grep::regex::RegexMatcher),
    #[cfg(feature = "pcre2")]
    PCRE2(grep::pcre2::RegexMatcher),
}

/// The printer used by a search worker.
///
/// The `W` type parameter refers to the type of the underlying writer.
#[derive(Clone, Debug)]
pub(crate) enum Printer<W> {
    /// Use the standard printer, which supports the classic grep-like format.
    Standard(grep::printer::Standard<W>),
    /// Use the summary printer, which supports aggregate displays of search
    /// results.
    Summary(grep::printer::Summary<W>),
    /// A JSON printer, which emits results in the JSON Lines format.
    JSON(grep::printer::JSON<W>),
}

impl<W: WriteColor> Printer<W> {
    /// Return a mutable reference to the underlying printer's writer.
    pub(crate) fn get_mut(&mut self) -> &mut W {
        match *self {
            Printer::Standard(ref mut p) => p.get_mut(),
            Printer::Summary(ref mut p) => p.get_mut(),
            Printer::JSON(ref mut p) => p.get_mut(),
        }
    }
}

/// A worker for executing searches.
///
/// It is intended for a single worker to execute many searches, and is
/// generally intended to be used from a single thread. When searching using
/// multiple threads, it is better to create a new worker for each thread.
#[derive(Clone, Debug)]
pub(crate) struct SearchWorker<W> {
    config: Config,
    command_builder: grep::cli::CommandReaderBuilder,
    /// This is `None` when `search_zip` is not enabled, since in this case it
    /// can never be used. We do this because building the reader can sometimes
    /// do non-trivial work (like resolving the paths of decompression binaries
    /// on Windows).
    decomp_builder: Option<grep::cli::DecompressionReaderBuilder>,
    matcher: PatternMatcher,
    searcher: grep::searcher::Searcher,
    printer: Printer<W>,
}

impl<W: WriteColor> SearchWorker<W> {
    /// Execute a search over the given haystack.
    pub(crate) fn search(
        &mut self,
        haystack: &crate::haystack::Haystack,
    ) -> io::Result<SearchResult> {
        let bin = if haystack.is_explicit() {
            self.config.binary_explicit.clone()
        } else {
            self.config.binary_implicit.clone()
        };
        let path = haystack.path();
        log::trace!("{}: binary detection: {:?}", path.display(), bin);

        self.searcher.set_binary_detection(bin);
        if haystack.is_stdin() {
            self.search_reader(path, &mut io::stdin().lock())
        } else if self.should_preprocess(path) {
            self.search_preprocessor(path)
        } else if self.should_decompress(path) {
            self.search_decompress(path)
        } else {
            self.search_path(path)
        }
    }

    /// Return a mutable reference to the underlying printer.
    pub(crate) fn printer(&mut self) -> &mut Printer<W> {
        &mut self.printer
    }

    /// Returns true if and only if the given file path should be
    /// decompressed before searching.
    fn should_decompress(&self, path: &Path) -> bool {
        self.decomp_builder.as_ref().is_some_and(|decomp_builder| {
            decomp_builder.get_matcher().has_command(path)
        })
    }

    /// Returns true if and only if the given file path should be run through
    /// the preprocessor.
    fn should_preprocess(&self, path: &Path) -> bool {
        if !self.config.preprocessor.is_some() {
            return false;
        }
        if self.config.preprocessor_globs.is_empty() {
            return true;
        }
        !self.config.preprocessor_globs.matched(path, false).is_ignore()
    }

    /// Search the given file path by first asking the preprocessor for the
    /// data to search instead of opening the path directly.
    fn search_preprocessor(
        &mut self,
        path: &Path,
    ) -> io::Result<SearchResult> {
        use std::{fs::File, process::Stdio};

        let bin = self.config.preprocessor.as_ref().unwrap();
        let mut cmd = std::process::Command::new(bin);
        cmd.arg(path).stdin(Stdio::from(File::open(path)?));

        let mut rdr = self.command_builder.build(&mut cmd).map_err(|err| {
            io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "preprocessor command could not start: '{cmd:?}': {err}",
                ),
            )
        })?;
        let result = self.search_reader(path, &mut rdr).map_err(|err| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("preprocessor command failed: '{cmd:?}': {err}"),
            )
        });
        let close_result = rdr.close();
        let search_result = result?;
        close_result?;
        Ok(search_result)
    }

    /// Attempt to decompress the data at the given file path and search the
    /// result. If the given file path isn't recognized as a compressed file,
    /// then search it without doing any decompression.
    fn search_decompress(&mut self, path: &Path) -> io::Result<SearchResult> {
        let Some(ref decomp_builder) = self.decomp_builder else {
            return self.search_path(path);
        };
        let mut rdr = decomp_builder.build(path)?;
        let result = self.search_reader(path, &mut rdr);
        let close_result = rdr.close();
        let search_result = result?;
        close_result?;
        Ok(search_result)
    }

    /// Search the contents of the given file path.
    fn search_path(&mut self, path: &Path) -> io::Result<SearchResult> {
        use self::PatternMatcher::*;

        let (searcher, printer) = (&mut self.searcher, &mut self.printer);
        match self.matcher {
            RustRegex(ref m) => search_path(m, searcher, printer, path),
            #[cfg(feature = "pcre2")]
            PCRE2(ref m) => search_path(m, searcher, printer, path),
        }
    }

    /// Executes a search on the given reader, which may or may not correspond
    /// directly to the contents of the given file path. Instead, the reader
    /// may actually cause something else to be searched (for example, when
    /// a preprocessor is set or when decompression is enabled). In those
    /// cases, the file path is used for visual purposes only.
    ///
    /// Generally speaking, this method should only be used when there is no
    /// other choice. Searching via `search_path` provides more opportunities
    /// for optimizations (such as memory maps).
    fn search_reader<R: io::Read>(
        &mut self,
        path: &Path,
        rdr: &mut R,
    ) -> io::Result<SearchResult> {
        use self::PatternMatcher::*;

        let (searcher, printer) = (&mut self.searcher, &mut self.printer);
        match self.matcher {
            RustRegex(ref m) => search_reader(m, searcher, printer, path, rdr),
            #[cfg(feature = "pcre2")]
            PCRE2(ref m) => search_reader(m, searcher, printer, path, rdr),
        }
    }
}

/// Search the contents of the given file path using the given matcher,
/// searcher and printer.
fn search_path<M: Matcher, W: WriteColor>(
    matcher: M,
    searcher: &mut grep::searcher::Searcher,
    printer: &mut Printer<W>,
    path: &Path,
) -> io::Result<SearchResult> {
    match *printer {
        Printer::Standard(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_path(&matcher, path, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: sink.stats().cloned(),
            })
        }
        Printer::Summary(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_path(&matcher, path, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: sink.stats().cloned(),
            })
        }
        Printer::JSON(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_path(&matcher, path, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: Some(sink.stats().clone()),
            })
        }
    }
}

/// Search the contents of the given reader using the given matcher, searcher
/// and printer.
fn search_reader<M: Matcher, R: io::Read, W: WriteColor>(
    matcher: M,
    searcher: &mut grep::searcher::Searcher,
    printer: &mut Printer<W>,
    path: &Path,
    mut rdr: R,
) -> io::Result<SearchResult> {
    match *printer {
        Printer::Standard(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: sink.stats().cloned(),
            })
        }
        Printer::Summary(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: sink.stats().cloned(),
            })
        }
        Printer::JSON(ref mut p) => {
            let mut sink = p.sink_with_path(&matcher, path);
            searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
            Ok(SearchResult {
                has_match: sink.has_match(),
                stats: Some(sink.stats().clone()),
            })
        }
    }
}


<!doctype html>

    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>


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

using json = nlohmann::json;

int main()
{
    // create a JSON array
    json j_array = {0, 1, 2, 3, 4, 5};

    // call erase()
    j_array.erase(2);

    // print values
    std::cout << j_array << '\n';
}


/*!
This crate provides common routines used in command line applications, with a
focus on routines useful for search oriented applications. As a utility
library, there is no central type or function. However, a key focus of this
crate is to improve failure modes and provide user friendly error messages
when things go wrong.

To the best extent possible, everything in this crate works on Windows, macOS
and Linux.

# Standard I/O

[`is_readable_stdin`] determines whether stdin can be usefully read from. It
is useful when writing an application that changes behavior based on whether
the application was invoked with data on stdin. For example, `rg foo` might
recursively search the current working directory for occurrences of `foo`, but
`rg foo < file` might only search the contents of `file`.

# Coloring and buffering

The [`stdout`], [`stdout_buffered_block`] and [`stdout_buffered_line`] routines
are alternative constructors for [`StandardStream`]. A `StandardStream`
implements `termcolor::WriteColor`, which provides a way to emit colors to
terminals. Its key use is the encapsulation of buffering style. Namely,
`stdout` will return a line buffered `StandardStream` if and only if
stdout is connected to a tty, and will otherwise return a block buffered
`StandardStream`. Line buffering is important for use with a tty because it
typically decreases the latency at which the end user sees output. Block
buffering is used otherwise because it is faster, and redirecting stdout to a
file typically doesn't benefit from the decreased latency that line buffering
provides.

The `stdout_buffered_block` and `stdout_buffered_line` can be used to
explicitly set the buffering strategy regardless of whether stdout is connected
to a tty or not.

# Escaping

The [`escape`](crate::escape()), [`escape_os`], [`unescape`] and
[`unescape_os`] routines provide a user friendly way of dealing with UTF-8
encoded strings that can express arbitrary bytes. For example, you might want
to accept a string containing arbitrary bytes as a command line argument, but
most interactive shells make such strings difficult to type. Instead, we can
ask users to use escape sequences.

For example, `a\xFFz` is itself a valid UTF-8 string corresponding to the
following bytes:

```ignore
[b'a', b'\\', b'x', b'F', b'F', b'z']
```

However, we can
interpret `\xFF` as an escape sequence with the `unescape`/`unescape_os`
routines, which will yield

```ignore
[b'a', b'\xFF', b'z']
```

instead. For example:

```
use grep_cli::unescape;

// Note the use of a raw string!
assert_eq!(vec![b'a', b'\xFF', b'z'], unescape(r"a\xFFz"));
```

The `escape`/`escape_os` routines provide the reverse transformation, which
makes it easy to show user friendly error messages involving arbitrary bytes.

# Building patterns

Typically, regular expression patterns must be valid UTF-8. However, command
line arguments aren't guaranteed to be valid UTF-8. Unfortunately, the standard
library's UTF-8 conversion functions from `OsStr`s do not provide good error
messages. However, the [`pattern_from_bytes`] and [`pattern_from_os`] do,
including reporting exactly where the first invalid UTF-8 byte is seen.

Additionally, it can be useful to read patterns from a file while reporting
good error messages that include line numbers. The [`patterns_from_path`],
[`patterns_from_reader`] and [`patterns_from_stdin`] routines do just that. If
any pattern is found that is invalid UTF-8, then the error includes the file
path (if available) along with the line number and the byte offset at which the
first invalid UTF-8 byte was observed.

# Read process output

Sometimes a command line application needs to execute other processes and
read its stdout in a streaming fashion. The [`CommandReader`] provides this
functionality with an explicit goal of improving failure modes. In particular,
if the process exits with an error code, then stderr is read and converted into
a normal Rust error to show to end users. This makes the underlying failure
modes explicit and gives more information to end users for debugging the
problem.

As a special case, [`DecompressionReader`] provides a way to decompress
arbitrary files by matching their file extensions up with corresponding
decompression programs (such as `gzip` and `xz`). This is useful as a means of
performing simplistic decompression in a portable manner without binding to
specific compression libraries. This does come with some overhead though, so
if you need to decompress lots of small files, this may not be an appropriate
convenience to use.

Each reader has a corresponding builder for additional configuration, such as
whether to read stderr asynchronously in order to avoid deadlock (which is
enabled by default).

# Miscellaneous parsing

The [`parse_human_readable_size`] routine parses strings like `2M` and converts
them to the corresponding number of bytes (`2 * 1<<20` in this case). If an
invalid size is found, then a good error message is crafted that typically
tells the user how to fix the problem.
*/

#![deny(missing_docs)]

mod decompress;
mod escape;
mod hostname;
mod human;
mod pattern;
mod process;
mod wtr;

pub use crate::{
    decompress::{
        DecompressionMatcher, DecompressionMatcherBuilder,
        DecompressionReader, DecompressionReaderBuilder, resolve_binary,
    },
    escape::{escape, escape_os, unescape, unescape_os},
    hostname::hostname,
    human::{ParseSizeError, parse_human_readable_size},
    pattern::{
        InvalidPatternError, pattern_from_bytes, pattern_from_os,
        patterns_from_path, patterns_from_reader, patterns_from_stdin,
    },
    process::{CommandError, CommandReader, CommandReaderBuilder},
    wtr::{
        StandardStream, stdout, stdout_buffered_block, stdout_buffered_line,
    },
};

/// Returns true if and only if stdin is believed to be readable.
///
/// When stdin is readable, command line programs may choose to behave
/// differently than when stdin is not readable. For example, `command foo`
/// might search the current directory for occurrences of `foo` where as
/// `command foo < some-file` or `cat some-file | command foo` might instead
/// only search stdin for occurrences of `foo`.
///
/// Note that this isn't perfect and essentially corresponds to a heuristic.
/// When things are unclear (such as if an error occurs during introspection to
/// determine whether stdin is readable), this prefers to return `false`. That
/// means it's possible for an end user to pipe something into your program and
/// have this return `false` and thus potentially lead to ignoring the user's
/// stdin data. While not ideal, this is perhaps better than falsely assuming
/// stdin is readable, which would result in blocking forever on reading stdin.
/// Regardless, commands should always provide explicit fallbacks to override
/// behavior. For example, `rg foo -` will explicitly search stdin and `rg foo
/// ./` will explicitly search the current working directory.
pub fn is_readable_stdin() -> bool {
    use std::io::IsTerminal;

    #[cfg(unix)]
    fn imp() -> bool {
        use std::{
            fs::File,
            os::{fd::AsFd, unix::fs::FileTypeExt},
        };

        let stdin = std::io::stdin();
        let fd = match stdin.as_fd().try_clone_to_owned() {
            Ok(fd) => fd,
            Err(err) => {
                log::debug!(
                    "for heuristic stdin detection on Unix, \
                     could not clone stdin file descriptor \
                     (thus assuming stdin is not readable): {err}",
                );
                return false;
            }
        };
        let file = File::from(fd);
        let md = match file.metadata() {
            Ok(md) => md,
            Err(err) => {
                log::debug!(
                    "for heuristic stdin detection on Unix, \
                     could not get file metadata for stdin \
                     (thus assuming stdin is not readable): {err}",
                );
                return false;
            }
        };
        let ft = md.file_type();
        let is_file = ft.is_file();
        let is_fifo = ft.is_fifo();
        let is_socket = ft.is_socket();
        let is_readable = is_file || is_fifo || is_socket;
        log::debug!(
            "for heuristic stdin detection on Unix, \
             found that \
             is_file={is_file}, is_fifo={is_fifo} and is_socket={is_socket}, \
             and thus concluded that is_stdin_readable={is_readable}",
        );
        is_readable
    }

    #[cfg(windows)]
    fn imp() -> bool {
        let stdin = winapi_util::HandleRef::stdin();
        let typ = match winapi_util::file::typ(stdin) {
            Ok(typ) => typ,
            Err(err) => {
                log::debug!(
                    "for heuristic stdin detection on Windows, \
                     could not get file type of stdin \
                     (thus assuming stdin is not readable): {err}",
                );
                return false;
            }
        };
        let is_disk = typ.is_disk();
        let is_pipe = typ.is_pipe();
        let is_readable = is_disk || is_pipe;
        log::debug!(
            "for heuristic stdin detection on Windows, \
             found that is_disk={is_disk} and is_pipe={is_pipe}, \
             and thus concluded that is_stdin_readable={is_readable}",
        );
        is_readable
    }

    #[cfg(not(any(unix, windows)))]
    fn imp() -> bool {
        log::debug!("on non-{{Unix,Windows}}, assuming stdin is not readable");
        false
    }

    !std::io::stdin().is_terminal() && imp()
}

/// Returns true if and only if stdin is believed to be connected to a tty
/// or a console.
///
/// Note that this is now just a wrapper around
/// [`std::io::IsTerminal`](https://doc.rust-lang.org/std/io/trait.IsTerminal.html).
/// Callers should prefer using the `IsTerminal` trait directly. This routine
/// is deprecated and will be removed in the next semver incompatible release.
#[deprecated(since = "0.1.10", note = "use std::io::IsTerminal instead")]
pub fn is_tty_stdin() -> bool {
    use std::io::IsTerminal;
    std::io::stdin().is_terminal()
}

/// Returns true if and only if stdout is believed to be connected to a tty
/// or a console.
///
/// This is useful for when you want your command line program to produce
/// different output depending on whether it's printing directly to a user's
/// terminal or whether it's being redirected somewhere else. For example,
/// implementations of `ls` will often show one item per line when stdout is
/// redirected, but will condensed output when printing to a tty.
///
/// Note that this is now just a wrapper around
/// [`std::io::IsTerminal`](https://doc.rust-lang.org/std/io/trait.IsTerminal.html).
/// Callers should prefer using the `IsTerminal` trait directly. This routine
/// is deprecated and will be removed in the next semver incompatible release.
#[deprecated(since = "0.1.10", note = "use std::io::IsTerminal instead")]
pub fn is_tty_stdout() -> bool {
    use std::io::IsTerminal;
    std::io::stdout().is_terminal()
}

/// Returns true if and only if stderr is believed to be connected to a tty
/// or a console.
///
/// Note that this is now just a wrapper around
/// [`std::io::IsTerminal`](https://doc.rust-lang.org/std/io/trait.IsTerminal.html).
/// Callers should prefer using the `IsTerminal` trait directly. This routine
/// is deprecated and will be removed in the next semver incompatible release.
#[deprecated(since = "0.1.10", note = "use std::io::IsTerminal instead")]
pub fn is_tty_stderr() -> bool {
    use std::io::IsTerminal;
    std::io::stderr().is_terminal()
}


# Vue 3 + Vite

This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.

Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).


import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    cssCodeSplit: true,
    lib: {
      entry: [
        fileURLToPath(new URL('src/css-entry-1.js', import.meta.url)),
        fileURLToPath(new URL('src/css-entry-2.js', import.meta.url)),
      ],
      name: 'css-code-split',
    },
    outDir: 'dist/css-code-split',
  },
})