UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

709 lines 28.8 kB
import type { GetTransform } from '@vxrn/compiler'; import type { metroPlugin } from '@vxrn/vite-plugin-metro'; import type { AutoDepOptimizationOptions, DepPatch, AfterBuildProps as VXRNAfterBuildProps, VXRNBuildOptions, VXRNOptions } from 'vxrn'; import type { One as OneShared } from '../interfaces/router'; import type { RouteNode } from '../router/Route'; import type { EnvironmentGuardOptions } from './plugins/environmentGuardPlugin'; type MetroPluginOptions = Parameters<typeof metroPlugin>[0]; export type DeployTarget = 'node' | 'vercel' | 'cloudflare'; export type DeployConfig = { target: DeployTarget; /** production URL, auto-detected from package name for cloudflare if not set */ url?: string; }; export type RouteInfo<TRegex = string> = { file: string; page: string; namedRegex: TRegex; loaderPath?: string; loaderServerPath?: string; urlPath: string; urlCleanPath: string; routeKeys: Record<string, string>; layouts?: RouteNode[]; middlewares?: RouteNode[]; type: One.RouteType; isNotFound?: boolean; hasLoader?: boolean; }; export declare namespace One { export type Options = Omit<VXRNOptions, keyof PluginOptions> & PluginOptions; export type RouteRenderMode = 'ssg' | 'spa' | 'ssr'; export type RouteType = RouteRenderMode | 'api' | 'layout'; export type ClientData = Record<string, any>; export type RouteOptions = { routeModes?: Record<string, RouteRenderMode>; }; export type FixDependencies = { [key: string]: DepPatch['patchFiles']; }; type PluginPlatformTarget = 'native' | 'web'; type CompilerEnvironment = 'ios' | 'android' | 'client' | 'ssr'; type ReactCompilerFilter = boolean | RegExp | ((id: string, environment: CompilerEnvironment) => boolean); type ReactCompilerConfig = boolean | PluginPlatformTarget | RegExp | ((id: string, environment: CompilerEnvironment) => boolean) | { web?: ReactCompilerFilter; native?: ReactCompilerFilter; }; export type PluginOptions = { /** * Per-environment module aliases applied via resolveId so they work * during both vite transforms AND rolldown dep pre-bundling (where * resolve.alias is not applied). * * @example * ```ts * one({ * alias: { * web: { * 'react-native-web': '@tamagui/react-native-web-lite', * }, * }, * }) * ``` */ alias?: { /** applies to client + ssr */ web?: Record<string, string>; /** applies to ios + android */ native?: Record<string, string>; /** overrides web */ client?: Record<string, string>; /** overrides web */ ssr?: Record<string, string>; /** overrides native */ ios?: Record<string, string>; /** overrides native */ android?: Record<string, string>; }; /** * Enabling zero does a couple very simple things: * * - It makes zero hand of seamelessly from server to client without flicker * */ /** * Per-file control over how code transforms. * Defaults to SWC, runs babel before SWC if: * * - options.react.compiler is `true`, on tsx files in your app * - `react-native-reanimated` is in your dependencies and a file contains a reanimated keyword * * Otherwise One defaults to using `@swc/core`. * * Accepts a function: * * (props: { * id: string * code: string * development: boolean * environment: Environment * reactForRNVersion: '18' | '19' * }) => * | true // default transforms * | false // no transforms * | 'babel' // force babel default transform * | 'swc' // force swc default transform * * // force babel, custom transform * * | { * transform: 'babel' * excludeDefaultPlugins?: boolean * } & babel.TransformOptions * * // force swc, custom transform * * | { * transform: 'swc' * } & SWCOptions * * Babel defaults to preset `@babel/preset-typescript` with plugins: * * - @babel/plugin-transform-destructuring * - @babel/plugin-transform-runtime * - @babel/plugin-transform-react-jsx * - @babel/plugin-transform-async-generator-functions * - @babel/plugin-transform-async-to-generator * * * SWC defaults to target es5 for native, es2020 for web. * */ transform?: GetTransform; router?: { /** * An array of globs that One uses to ignore files in your routes directory. If a file matches any pattern, it will not be treated as a route. * * This is useful for ignoring test or utility files you wish to colocate. * * Currently, we only support patterns starting with <code>&#42;&#42;/&#42;</code>. * * Example: * * <code>&#42;&#42;/&#42;.test.*</code> */ ignoredRouteFiles?: Array<`**/*${string}`>; /** * Dangerously customize the router root directory. This may lead to unexpected behavior. */ root?: string; experimental?: { /** * Auto-generate route type helpers in route files. * * Route types are always generated in routes.d.ts. This option controls whether * One automatically inserts type helpers into your route files. * * Options: * - `false` (default): No auto-generation, manually add types yourself * - `'type'`: Auto-inserts type-only helpers: * ```typescript * import type { RouteType } from 'one' * type Route = RouteType<'/your/[route]'> * ``` * - `'runtime'`: Auto-inserts runtime helpers: * ```typescript * import { createRoute } from 'one' * const route = createRoute<'/your/[route]'>() * ``` * * The insertion happens automatically when route files are created or modified, * and respects your existing code (won't modify loaders, etc). * * @default false */ typedRoutesGeneration?: false | 'type' | 'runtime'; }; }; react?: { /** * Enable the React Compiler, for all or specific platforms * * - `true` - enable for all platforms * - `'native'` - enable for iOS/Android only * - `'web'` - enable for web only * - `RegExp` - enable for files matching the pattern * - `(id: string) => boolean` - custom function to determine per-file * - `{ web, native }` - configure each platform separately * * @example * // enable for all * compiler: true * * @example * // different config per platform * compiler: { * web: true, * native: /\/components\//, * } * * @default false */ compiler?: ReactCompilerConfig; }; optimization?: { /** * By default One scans your fs routes and adds them as Vite `entries`, this prevents some hard * reloads on web as you navigate to new pages, but can slow things startup. * * The 'flat' option is default and will automatically add just the routes at the root of your * app but nothing nested in non-group folders below that. * * @default 'flat' */ autoEntriesScanning?: boolean | 'flat'; }; /** * Path to a js or ts file to import before the rest of your app runs * One controls your root, but you may want to runs some JS before anything else * Use this to give One the entrypoint to run * * Can be a string to use the same file for all environments, or an object to specify * different files per environment: * * @example * setupFile: './setup.ts' * * @example * setupFile: { * client: './setup.client.ts', * server: './setup.server.ts', * native: './setup.native.ts' * } * * @example * setupFile: { * client: './setup.client.ts', * server: './setup.server.ts', * ios: './setup.ios.ts', * android: './setup.android.ts' * } */ setupFile?: string | { client?: string; server?: string; native?: string; } | { client?: string; server?: string; ios?: string; android?: string; }; config?: { ensureTSConfig?: false; /** * One enables Vite's native resolve.tsconfigPaths by default. * Set this to false to disable tsconfig path resolution. * * Pass an object for fine-grained control (options are accepted for * backwards-compatibility; the built-in resolver always ignores config errors). * * @default true */ tsConfigPaths?: boolean | { ignoreConfigErrors?: boolean; }; }; /** * Set to `false` to completely disable native (iOS/Android) support. * * When disabled, One will not register the `ios`/`android` Vite environments, * skip the Metro / React Native dev server plugins, and skip all native-only * globals. Use this when you want to use One purely as a web framework. */ native?: false | ({ /** * The uid of your native app, this will be used internally in one to call * `AppRegistry.registerComponent(key)` */ key?: string; /** * Wrap each route screen in a React Suspense boundary on native. * * On native the JS bundle already contains all route modules, so the * Suspense boundary only catches the async `import()` wrapper which * resolves on the next microtick. Setting this to `false` removes * the `<Suspense fallback={null}>` wrapper, which prevents a blank * flash when JS-driven navigation animations (e.g. SOI) slide a * screen in before the Suspense fallback is replaced. * * @default true */ suspendRoutes?: boolean; /** * Turns on react-native-css-interop support when importing CSS on native */ css?: boolean; /** * Specifies the bundler to use for native builds. Defaults to 'vite'. * * - 'metro' is recommended for production stability. Note that this option comes with some limitations, see https://onestack.dev/docs/metro-mode#limitations for more info. * - 'vite' is experimental but offers faster builds with SWC. * * Note that the ONE_METRO_MODE environment variable can override this setting to 'metro'. */ bundler?: 'metro' | 'vite'; } & ({ bundler: 'metro'; /** Options merging for Metro is not fully implemented in the One plugin, changing this may not work properly. Search for "METRO-OPTIONS-MERGING" in the codebase for details. */ bundlerOptions?: MetroPluginOptions; } | { bundler?: 'vite'; /** No configurable options with the default vite bundler. */ bundlerOptions?: { currentlyHaveNoOptions?: null; }; })); web?: { /** * Choose the default strategy for pages to be rendered on the web. * * For sites that are mostly static, choose "ssg": * SSG stands for "server side generated", in this mode when you run `build` * your pages will all be fully rendered on the server once during the build, * outputting a static HTML page with the rendered page and loader data inlined. * This gives better performance for initial render, and better SEO. * * * For apps that are mostly dynamic, choose "spa": * SPA stands for "single page app", in this mode when you run `build` your * pages will only render out an empty shell of HTML and will not attempt to * server render at all. Loaders will be run on the server side and the data will * be available to your app on initial render. * * @default 'ssg' */ defaultRenderMode?: RouteRenderMode; /** * An array of redirect objects, works in development and production: * * @example * * [ * { * source: '/vite', * destination: 'https://vxrn.dev', * permanent: true, * }, * { * source: '/docs/components/:slug/:version', * destination: '/ui/:slug/:version', * permanent: true, * }, * ] * */ redirects?: Redirect[]; /** * Deployment target for production builds. * * String shorthand: * - 'node': Default Node.js server using Hono * - 'vercel': Vercel serverless functions * - 'cloudflare': Cloudflare Workers * * Object form allows additional configuration: * ```ts * deploy: { * target: 'cloudflare', * url: 'https://my-app.example.com', * } * ``` * * @default 'node' */ deploy?: DeployTarget | DeployConfig; /** * @experimental * When true, inlines the CSS content directly into the HTML instead of using <link> tags. * This can improve performance by eliminating an extra network request for CSS. * * @default false */ inlineLayoutCSS?: boolean; /** * @experimental * Controls how scripts are loaded for improved performance. * * - `'defer-non-critical'`: Critical scripts (framework entry, page, layouts) load immediately. * Non-critical scripts (component imports, utilities) become modulepreload hints only, * reducing network/CPU contention. * * - `'after-lcp'`: Scripts download immediately via modulepreload but execution is deferred * until after first paint using double requestAnimationFrame. This allows the browser to * paint the SSR content before executing JavaScript. Only applies to SSG pages. * * - `'after-lcp-aggressive'`: Only modulepreloads critical scripts (entry, layouts). * Non-critical scripts have no modulepreload hints, reducing network saturation. * Best for pages with many chunks or slow networks. * * @default undefined (all scripts load with async) */ experimental_scriptLoading?: 'defer-non-critical' | 'after-lcp' | 'after-lcp-aggressive'; /** * Generate a sitemap.xml file during build. * * Set to `true` for default behavior, or pass an object to configure: * * @example * sitemap: true * * @example * sitemap: { * baseUrl: 'https://example.com', // defaults to ONE_SERVER_URL env var * priority: 0.7, // default priority for all routes * changefreq: 'weekly', // default changefreq for all routes * exclude: ['/admin/*', '/api/*'], // glob patterns to exclude * } * * Routes can also export sitemap metadata: * ```ts * export const sitemap = { priority: 0.9, changefreq: 'daily' } * // or exclude: export const sitemap = { exclude: true } * ``` */ sitemap?: boolean | SitemapOptions; /** * Controls link prefetching strategy for improved navigation performance. * * - `'intent'`: Smart prefetching using mouse trajectory prediction (default) * - `'viewport'`: Prefetch links when they enter the viewport * - `'hover'`: Prefetch on mouseover only * - `false`: Disable prefetching entirely * * @default 'intent' */ linkPrefetch?: false | 'hover' | 'viewport' | 'intent'; /** * Skew protection detects when deployed assets have changed and handles * stale client bundles gracefully instead of showing broken pages. * * - `true` (default): Detects chunk load failures and auto-reloads * - `'proactive'`: Also polls for new versions and forces full-page * navigation when a new deployment is detected * - `false`: Disable skew protection entirely * * @default true */ skewProtection?: boolean | 'proactive'; /** * Wrap each route screen in a React Suspense boundary on web. * * By default web does not wrap routes in Suspense (to avoid flickers * during navigation). Enable this if your routes use React `use()` * or lazy loading and you want Suspense boundaries per-route. * * @default false */ suspendRoutes?: boolean; }; /** * Development tools configuration. Set to `true` to enable all devtools, * `false` to disable, or an object to configure individual tools. * * @default true */ devtools?: boolean | { /** * Enable the source inspector. * Hold Shift+Ctrl (or Shift+Cmd on Mac) and hover over elements * to see their source file location. Click to open in your editor. * * @default true (when devtools is enabled) */ inspector?: boolean; /** * Editor CLI command for the source inspector to open files with. * Examples: 'cursor', 'code', 'zed', 'subl', 'webstorm' * * Can also be set via the LAUNCH_EDITOR or EDITOR env var. */ editor?: string; /** * Enable the SEO preview panel. * Press Alt+S to toggle a panel showing how the page appears in * Google search results, Twitter cards, and Open Graph shares. * * @default true (when devtools is enabled) */ seoPreview?: boolean; }; server?: VXRNOptions['server'] & { /** * Log HTTP requests to the console * @default true */ loggingEnabled?: boolean; }; /** * Skip loading .env files during build */ skipEnv?: boolean; build?: { server?: VXRNBuildOptions | false; api?: VXRNBuildOptions; /** * Use worker threads for parallel static page generation during build. * This utilizes multiple CPU cores for faster builds. * * @default true */ workers?: boolean; /** * Scan client bundles for leaked secrets (API keys, tokens, passwords) * after building. Catches accidental exposure of server-side secrets * in client-facing JavaScript. * * - `'warn'` (default): Log warnings but don't fail the build * - `'error'`: Fail the build if secrets are found * - `false`: Disable scanning entirely * * Pass an object for fine-grained control: * * @example * securityScan: { * level: 'error', * safePatterns: [ * 'sk_live_test_placeholder', * /my-custom-uuid-[a-f0-9-]+/, * ] * } * * For production deployments, we recommend setting this to `'error'` * to prevent secrets from being shipped to clients. * * @default 'warn' */ securityScan?: boolean | 'warn' | 'error' | { /** * @default 'warn' */ level?: 'warn' | 'error'; /** * Strings or regex patterns that should be considered safe * and ignored by the scanner. Useful for test tokens, known * UUIDs, or domain-specific patterns. */ safePatterns?: (string | RegExp)[]; }; }; patches?: FixDependencies; ssr?: { /** * One scans dependencies on startup and decides which ones to optimize based on known broken * dependencies. These include react-native and react, which need to be optimized to work. * It finds all parent dependencies of the know bad deps and adds them to ssr.optimizeDeps.include. * * You can disable with false, or configure the include/exclude with options. * * Note: the **full path** (e.g. `<your_project_path>/node_modules/<some_package>`) will be used to match dependencies, if you are using a string to match a package name you may want to add `*` + `/` at the start and `/*` the end. * * @default { include: /node_modules/ } */ autoDepsOptimization?: boolean | AutoDepOptimizationOptions; /** * In monorepos with symlinked workspace packages, Vite's SSR optimizer registers * pre-bundled deps by their node_modules path, but the resolver follows symlinks * to the real (source) path. This causes duplicate module instances — the optimizer * serves one copy while source imports create another. * * Enable this to redirect symlink-resolved paths back to node_modules paths so * the optimizer can recognize and deduplicate them. * * @default false */ dedupeSymlinkedModules?: boolean; }; /** * Configure environment guard behavior for `server-only`, `client-only`, * `native-only`, and `web-only` imports. * * Some libraries like `floating-ui` import `client-only` even though the * imported code works fine on the server. Use this to disable guards. * * @example * // disable the client-only guard (allows importing on server) * environmentGuards: { disableGuards: ['client-only'] } * * @example * // disable all environment guards * environmentGuards: { disabled: true } */ environmentGuards?: EnvironmentGuardOptions; }; export interface RouteContext { keys(): string[]; (id: string): any; <T>(id: string): T; resolve(id: string): string; id: string; } export type Redirect = { source: string; destination: string; permanent: boolean; }; export type BuildInfo = { outDir?: string; constants: { CACHE_KEY: string; }; oneOptions?: PluginOptions; routeToBuildInfo: Record<string, Omit<One.RouteBuildInfo, 'loaderData'>>; /** A mapping to lookup the full route name from a path */ pathToRoute: Record<string, string>; routeMap: Record<string, string>; manifest: { pageRoutes: RouteInfo[]; apiRoutes: RouteInfo[]; allRoutes: RouteInfo[]; }; preloads: Record<string, boolean>; cssPreloads: Record<string, boolean>; loaders: Record<string, boolean>; useRolldown?: boolean; }; export type AfterBuildProps = VXRNAfterBuildProps & BuildInfo; export type RouteBuildInfo = { type: One.RouteType; path: string; routeFile: string; middlewares: string[]; preloadPath: string; cssPreloadPath: string; loaderPath: string; cleanPath: string; htmlPath: string; clientJsPath: string; serverJsPath: string; params: object; loaderData: any; /** All preloads (for backwards compatibility) */ preloads: string[]; /** Critical preloads that load immediately with async */ criticalPreloads?: string[]; /** Non-critical preloads that are modulepreload hints only */ deferredPreloads?: string[]; css: string[]; /** CSS from layout entries only - loaded before scripts to prevent FOUC */ layoutCSS: string[]; /** When inlineLayoutCSS is enabled, contains the actual CSS content */ cssContents?: string[]; }; /** * Represents a matched route in the route hierarchy. * Used by useMatches() to expose loader data from all matched routes. */ export type RouteMatch = { /** Route identifier (e.g., "docs/_layout" or "docs/intro") */ routeId: string; /** The pathname segment this route matched */ pathname: string; /** URL params extracted for this route */ params: Record<string, string | string[]>; /** Data returned from this route's loader */ loaderData: unknown; }; export type ServerContext = { css?: string[]; /** When inlineLayoutCSS is enabled, this contains the actual CSS content to inline */ cssContents?: string[]; /** Number of inline CSS entries - used for hydration matching when cssContents is stripped */ cssInlineCount?: number; postRenderData?: any; loaderData?: any; loaderProps?: any; mode?: 'spa' | 'ssg' | 'ssr' | 'spa-shell'; routePreloads?: Record<string, string>; /** * All matched routes with their loader data. * Ordered from root layout to leaf page (parent → child). */ matches?: RouteMatch[]; }; export type Flags = {}; export type SitemapChangefreq = OneShared.SitemapChangefreq; export type RouteSitemap = OneShared.RouteSitemap; export type SitemapOptions = { /** * Base URL for the sitemap. Defaults to ONE_SERVER_URL environment variable. */ baseUrl?: string; /** * Default priority for all routes (0.0 to 1.0). * @default 0.5 */ priority?: number; /** * Default change frequency for all routes. */ changefreq?: SitemapChangefreq; /** * Glob patterns for routes to exclude from the sitemap. * API routes and not-found routes are always excluded. */ exclude?: string[]; }; export {}; } export {}; //# sourceMappingURL=types.d.ts.map