import { Manifest } from 'vite';
import { ModuleMap } from 'react-server-dom-webpack/server.edge';
import { RenderToReadableStreamOptions } from 'react-dom/server';

/**
 * A route that was created using `defineRoutes` or created conventionally from
 * looking at the files on the filesystem.
 */
interface ConfigRoute {
    /**
     * The path this route uses to match on the URL pathname.
     */
    path?: string;
    /**
     * Should be `true` if it is an index route. This disallows child routes.
     */
    index?: boolean;
    /**
     * Should be `true` if the `path` is case-sensitive. Defaults to `false`.
     */
    caseSensitive?: boolean;
    /**
     * The unique id for this route, named like its `file` but without the
     * extension. So `app/routes/gists/$username.jsx` will have an `id` of
     * `routes/gists/$username`.
     */
    id: string;
    /**
     * The unique `id` for this route's parent route, if there is one.
     */
    parentId: string;
    /**
     * The path to the entry point for this route, relative to
     * `config.appDirectory`.
     */
    file: string;
    routeHandler?: boolean;
}
interface RouteManifest {
    [routeId: string]: ConfigRoute;
}

interface DefineRouteOptions {
    /**
     * Should be `true` if the route `path` is case-sensitive. Defaults to
     * `false`.
     */
    caseSensitive?: boolean;
    /**
     * Should be `true` if this is an index route that does not allow child routes.
     */
    index?: boolean;
    /**
     * An optional unique id string for this route. Use this if you need to aggregate
     * two or more routes with the same route file.
     */
    id?: string;
    routeHandler?: boolean;
}
interface DefineRouteChildren {
    (): void;
}
/**
 * A function for defining a route that is passed as the argument to the
 * `defineRoutes` callback.
 *
 * Calls to this function are designed to be nested, using the `children`
 * callback argument.
 *
 *   defineRoutes(route => {
 *     route('/', 'pages/layout', () => {
 *       route('react-router', 'pages/react-router');
 *       route('reach-ui', 'pages/reach-ui');
 *     });
 *   });
 */
interface DefineRouteFunction {
    (
    /**
     * The path this route uses to match the URL pathname.
     */
    path: string | undefined, 
    /**
     * The path to the file that exports the React component rendered by this
     * route as its default export, relative to the `app` directory.
     */
    file: string, id: string, 
    /**
     * Options for defining routes, or a function for defining child routes.
     */
    optionsOrChildren?: DefineRouteOptions | DefineRouteChildren, 
    /**
     * A function for defining child routes.
     */
    children?: DefineRouteChildren): void;
}
type DefineRoutesFunction = typeof defineRoutes;
/**
 * A function for defining routes programmatically, instead of using the
 * filesystem convention.
 */
declare function defineRoutes(callback: (defineRoute: DefineRouteFunction) => void): RouteManifest;
declare function createRouteId(file: string): string;
declare function normalizeSlashes(file: string): string;
declare function stripFileExtension(file: string): string;
declare const routeModuleExts: string[];
declare function isRouteModuleFile(filename: string): boolean;
declare function toPath(id: string, removePathlessLayouts?: boolean): string;
/**
 * Defines routes using the filesystem convention in `app/routes`. The rules are:
 *
 * - Route paths are derived from the file path. A `.` in the filename indicates
 *   a `/` in the URL (a "nested" URL, but no route nesting). A `$` in the
 *   filename indicates a dynamic URL segment.
 * - Subdirectories are used for nested routes.
 *
 * For example, a file named `app/routes/gists/$username.tsx` creates a route
 * with a path of `gists/:username`.
 */
declare function defineFileSystemRoutes(appDir: string, ignoredFilePatterns?: string[]): RouteManifest;
declare const paramPrefixChar: "$";
declare const escapeStart: "[";
declare const escapeEnd: "]";
declare const optionalStart: "(";
declare const optionalEnd: ")";
declare function createRemixRoutePath(partialRouteId: string): string | undefined;
declare function createRoutePath(partialRouteId: string): string | undefined;
declare function isSegmentSeparator(checkChar: string | undefined): boolean;

type AssetDesc = string | {
    type: "style";
    style: string;
    src?: string;
};
type Env = {
    clientModuleMap: ModuleMap;
    components: {
        [key: string]: any;
    };
    findAssets: () => Promise<Array<AssetDesc>>;
    routesConfig: {
        [key: string]: any;
    };
    manifests?: {
        buildAppRoot: string;
        srcAppRoot: string;
        clientManifest: Manifest;
        serverManifest: Manifest;
        reactServerManifest: Manifest;
        routesConfig: RouteManifest;
        findInServerManifest(chunk: string): string;
    };
    loadModule(id: string): Promise<any>;
    lazyComponent(id: string): React.FC<any>;
} & RenderToReadableStreamOptions;
declare global {
    var env: Env;
}

/**
 * MIT License
 *
 * Copyright (c) React Training 2015-2019
 * Copyright (c) Remix Software 2020-2022
 */

/**
 * @private
 * Arguments passed to route loader/action functions.  Same for now but we keep
 * this as a private implementation detail in case they diverge in the future.
 */
interface DataFunctionArgs {
    request: Request;
    params: Params;
    context?: any;
}
/**
 * Arguments passed to loader functions
 */
type LoaderFunctionArgs = DataFunctionArgs;
/**
 * Arguments passed to action functions
 */
type ActionFunctionArgs = DataFunctionArgs;
/**
 * Route loader function signature
 */
interface LoaderFunction {
    (args: LoaderFunctionArgs): Promise<Response> | Response | Promise<any> | any;
}
/**
 * Route action function signature
 */
interface ActionFunction {
    (args: ActionFunctionArgs): Promise<Response> | Response | Promise<any> | any;
}
/**
 * Keys we cannot change from within a lazy() function. We spread all other keys
 * onto the route. Either they're meaningful to the router, or they'll get
 * ignored.
 */
type ImmutableRouteKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
/**
 * lazy() function to load a route definition, which can add non-matching
 * related properties to a route
 */
interface LazyRouteFunction<R extends RouteObject> {
    (): Promise<Omit<R, ImmutableRouteKey>>;
}
/**
 * Base RouteObject with common props shared by all types of routes
 */
type AgnosticBaseRouteObject = {
    caseSensitive?: boolean;
    path?: string;
    id?: string;
    loader?: LoaderFunction;
    action?: ActionFunction;
    hasErrorBoundary?: boolean;
    component?: any;
    lazy?: LazyRouteFunction<AgnosticBaseRouteObject>;
};
/**
 * Index routes must not have children
 */
type IndexRouteObject = AgnosticBaseRouteObject & {
    children?: undefined;
    index: true;
};
/**
 * Non-index routes may have children, but cannot have index
 */
type NonIndexRouteObject = AgnosticBaseRouteObject & {
    children?: RouteObject[];
    index?: false;
};
/**
 * A route object represents a logical route, with (optionally) its child
 * routes organized in a tree-like structure.
 */
type RouteObject = IndexRouteObject | NonIndexRouteObject;
/**
 * The parameters that were parsed from the URL path.
 */
type Params<Key extends string = string> = {
    readonly [key in Key]: string | undefined;
};

declare function groupRoutesByParentId(manifest: RouteManifest): Record<string, Omit<any, "children">[]>;
declare function createServerRoutes(env: Env, parentId?: string, routesByParentId?: Record<string, Omit<any, "children">[]>): RouteObject[];

export { DefineRouteFunction, DefineRouteOptions, DefineRoutesFunction, createRemixRoutePath, createRouteId, createRoutePath, createServerRoutes, defineFileSystemRoutes, defineRoutes, escapeEnd, escapeStart, groupRoutesByParentId, isRouteModuleFile, isSegmentSeparator, normalizeSlashes, optionalEnd, optionalStart, paramPrefixChar, routeModuleExts, stripFileExtension, toPath };
