From cef9dff1bbf64809be20a559ec28a1e6910e67c3 Mon Sep 17 00:00:00 2001 From: natew Date: Thu, 6 Feb 2025 11:29:47 -1000 Subject: [PATCH] feat(vxrn): rename ssr.disableAutoDepsPreBundling to autoDepsOptimization and add options for both include and exclude filters --- packages/one/src/router/useScreens.tsx | 34 +++++++++++++------ packages/one/src/vite/one.ts | 9 ++--- packages/one/src/vite/types.ts | 11 ++++-- packages/one/types/vite/types.d.ts | 12 +++++-- .../vxrn/src/plugins/autoDepOptimizePlugin.ts | 23 ++++++------- packages/vxrn/src/types.ts | 7 +++- packages/vxrn/src/utils/scanDepsToOptimize.ts | 10 ++---- .../types/plugins/autoDepOptimizePlugin.d.ts | 8 ++--- packages/vxrn/types/types.d.ts | 6 +++- .../vxrn/types/utils/scanDepsToOptimize.d.ts | 1 + 10 files changed, 76 insertions(+), 45 deletions(-) diff --git a/packages/one/src/router/useScreens.tsx b/packages/one/src/router/useScreens.tsx index b1fbdc591..2a1b95c66 100644 --- a/packages/one/src/router/useScreens.tsx +++ b/packages/one/src/router/useScreens.tsx @@ -7,7 +7,7 @@ import type { RouteProp, ScreenListeners, } from '@react-navigation/native' -import React, { forwardRef, memo, Suspense, useId } from 'react' +import React, { forwardRef, memo, Suspense } from 'react' import { ServerContextScript } from '../server/ServerContextScript' import { getPageExport } from '../utils/getPageExport' import { EmptyRoute } from '../views/EmptyRoute' @@ -238,6 +238,18 @@ export function getQualifiedRouteComponent(value: RouteNode) { return }) + const wrapSuspense = (children: any) => { + // so as far as i understand, adding suspense causes flickers on web during nav because + // we can't seem to get react navigation to properly respect startTransition(() => {}) + // i tried a lot of things, but didn't find the root cause, but native needs suspense or + // else it hits an error about no suspense boundary being set + + if (process.env.TAMAGUI_TARGET === 'native') { + return {children} + } + return children + } + const QualifiedRoute = React.forwardRef( ( { @@ -254,15 +266,17 @@ export function getQualifiedRouteComponent(value: RouteNode) { return ( - + {wrapSuspense( + + )} ) diff --git a/packages/one/src/vite/one.ts b/packages/one/src/vite/one.ts index 1cca91518..36da351d3 100644 --- a/packages/one/src/vite/one.ts +++ b/packages/one/src/vite/one.ts @@ -74,6 +74,8 @@ export function one(options: One.PluginOptions = {}): PluginOption { }) } + const autoDepsOptions = options.ssr?.autoDepsOptimization + const devAndProdPlugins: Plugin[] = [ { name: 'one:config', @@ -97,7 +99,7 @@ export function one(options: One.PluginOptions = {}): PluginOption { }, }, - ...(options.ssr?.disableAutoDepsPreBundling === true + ...(autoDepsOptions === false ? [] : [ autoDepOptimizePlugin({ @@ -109,9 +111,8 @@ export function one(options: One.PluginOptions = {}): PluginOption { }) }, root, - exclude: Array.isArray(options.ssr?.disableAutoDepsPreBundling) - ? options.ssr?.disableAutoDepsPreBundling - : undefined, + include: /node_modules/, + ...(autoDepsOptions === true ? {} : autoDepsOptions), }), ]), diff --git a/packages/one/src/vite/types.ts b/packages/one/src/vite/types.ts index 73e93425c..e610047e6 100644 --- a/packages/one/src/vite/types.ts +++ b/packages/one/src/vite/types.ts @@ -1,6 +1,7 @@ import type { GetTransform } from '@vxrn/compiler' import type { PluginOptions as TSConfigPluginOptions } from 'vite-tsconfig-paths' import type { + AutoDepOptimizationOptions, DepOptimize, DepPatch, AfterBuildProps as VXRNAfterBuildProps, @@ -230,9 +231,15 @@ export namespace One { ssr?: { /** - * Do not pre-bundle specific dependencies for SSR, or disable the automatic scan for dependencies to pre-bundle entirely. + * 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. + * + * @default { include: /node_modules/ } */ - disableAutoDepsPreBundling?: boolean | string[] + autoDepsOptimization?: boolean | AutoDepOptimizationOptions } } diff --git a/packages/one/types/vite/types.d.ts b/packages/one/types/vite/types.d.ts index dd713f0a0..055da8f81 100644 --- a/packages/one/types/vite/types.d.ts +++ b/packages/one/types/vite/types.d.ts @@ -1,6 +1,6 @@ import type { GetTransform } from '@vxrn/compiler'; import type { PluginOptions as TSConfigPluginOptions } from 'vite-tsconfig-paths'; -import type { DepOptimize, DepPatch, AfterBuildProps as VXRNAfterBuildProps, VXRNBuildOptions, VXRNOptions, VXRNServePlatform } from 'vxrn'; +import type { AutoDepOptimizationOptions, DepOptimize, DepPatch, AfterBuildProps as VXRNAfterBuildProps, VXRNBuildOptions, VXRNOptions, VXRNServePlatform } from 'vxrn'; import type { RouteNode } from '../router/Route'; import type { Options as ReactScanOptions } from 'react-scan'; export type RouteInfo = { @@ -193,9 +193,15 @@ export declare namespace One { deps?: FixDependencies; ssr?: { /** - * Do not pre-bundle specific dependencies for SSR, or disable the automatic scan for dependencies to pre-bundle entirely. + * 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. + * + * @default { include: /node_modules/ } */ - disableAutoDepsPreBundling?: boolean | string[]; + autoDepsOptimization?: boolean | AutoDepOptimizationOptions; }; }; export interface RouteContext { diff --git a/packages/vxrn/src/plugins/autoDepOptimizePlugin.ts b/packages/vxrn/src/plugins/autoDepOptimizePlugin.ts index e30788670..d7a27fc7e 100644 --- a/packages/vxrn/src/plugins/autoDepOptimizePlugin.ts +++ b/packages/vxrn/src/plugins/autoDepOptimizePlugin.ts @@ -1,7 +1,8 @@ import { createDebugger } from '@vxrn/debug' import FSExtra from 'fs-extra' import path from 'node:path' -import type { ConfigEnv, Plugin } from 'vite' +import { createFilter, type Plugin } from 'vite' +import type { AutoDepOptimizationOptions } from '../types' import { EXCLUDE_LIST, type ScanDepsResult, scanDepsToOptimize } from '../utils/scanDepsToOptimize' import { getFileHash, lookupFile } from '../utils/utils' @@ -9,9 +10,8 @@ const name = 'vxrn:auto-dep-optimize' const { debug, debugDetails } = createDebugger(name) -type FindDepsOptions = { +type FindDepsOptions = AutoDepOptimizationOptions & { root: string - exclude?: string[] onScannedDeps?: (result: ScanDepsResult) => void } @@ -32,10 +32,12 @@ export function autoDepOptimizePlugin(props: FindDepsOptions): Plugin { .map(([k]) => k) : [] + const userExcludes = Array.isArray(props.exclude) ? props.exclude : [props.exclude] + return getScannedOptimizeDepsConfig({ ...props, mode: env.mode, - exclude: [...exclude, ...(props.exclude || [])], + exclude: [...exclude, ...userExcludes].filter(Boolean), }) }, } satisfies Plugin @@ -56,7 +58,7 @@ export async function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMode) ssr: { optimizeDeps: { include: result.prebundleDeps, - exclude: props.exclude ? [...props.exclude, ...EXCLUDE_LIST] : EXCLUDE_LIST, + exclude: EXCLUDE_LIST, }, noExternal: result.prebundleDeps, }, @@ -65,7 +67,7 @@ export async function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMode) let sessionCacheVal: ScanDepsResult | null = null -export async function findDepsToOptimize({ root, mode, exclude }: FindDepsOptionsByMode) { +export async function findDepsToOptimize({ root, mode, exclude, include }: FindDepsOptionsByMode) { const cacheFilePath = getSSRExternalsCachePath(root) const startedAt = debug ? Date.now() : 0 @@ -107,8 +109,10 @@ export async function findDepsToOptimize({ root, mode, exclude }: FindDepsOption } } + const filter = createFilter(include, exclude) + if (!value) { - value = await scanDepsToOptimize(`${root}/package.json`) + value = await scanDepsToOptimize(`${root}/package.json`, { filter }) if (sessionCache) { sessionCacheVal = value @@ -130,11 +134,6 @@ export async function findDepsToOptimize({ root, mode, exclude }: FindDepsOption : ` (Focus on this debug scope, "DEBUG=${debug.namespace}", to see more details.)`) ) - if (exclude) { - debug?.(`Excluding user specified deps ${JSON.stringify(exclude)} from pre-bundling for SSR.`) - value.prebundleDeps = value.prebundleDeps.filter((dep) => !exclude.includes(dep)) - } - debugDetails?.(`Deps discovered to be pre-bundled for SSR: ${value.prebundleDeps.join(', ')}`) return value diff --git a/packages/vxrn/src/types.ts b/packages/vxrn/src/types.ts index 1c17e520e..248a2ae9b 100644 --- a/packages/vxrn/src/types.ts +++ b/packages/vxrn/src/types.ts @@ -1,6 +1,6 @@ import type { Hono } from 'hono' import type { OutputAsset, OutputChunk, TreeshakingOptions, TreeshakingPreset } from 'rollup' -import type { InlineConfig, UserConfig } from 'vite' +import type { FilterPattern, InlineConfig, UserConfig } from 'vite' type RollupOutputList = [OutputChunk, ...(OutputChunk | OutputAsset)[]] @@ -26,6 +26,11 @@ export type AfterBuildProps = { } } +export type AutoDepOptimizationOptions = { + exclude?: FilterPattern + include?: FilterPattern +} + export type ClientManifestEntry = { file: string // assets/_user_-Bg0DW2rm.js src?: string // app/[user].tsx diff --git a/packages/vxrn/src/utils/scanDepsToOptimize.ts b/packages/vxrn/src/utils/scanDepsToOptimize.ts index 413069f66..50c10d9e6 100644 --- a/packages/vxrn/src/utils/scanDepsToOptimize.ts +++ b/packages/vxrn/src/utils/scanDepsToOptimize.ts @@ -103,6 +103,7 @@ const rootOptions = {} export async function scanDepsToOptimize( packageJsonPath: string, options: { + filter?: (id: string | unknown) => boolean parentDepNames?: string[] proceededDeps?: Map /** If the content of the package.json is already read before calling this function, pass it here to avoid reading it again */ @@ -136,20 +137,13 @@ export async function scanDepsToOptimize( if (EXCLUDE_LIST_SET.has(dep)) { return [] } - - // lets be a bit conservative and assume react-native starting packages are - // if (dep.startsWith('react-native-') || dep.startsWith(`@react-native-`)) { - // return [] - // } - const localPath = await findDepPkgJsonPath(dep, currentRoot) if (!localPath) return [] const depPkgJsonPath = await FSExtra.realpath(localPath) - if (!depPkgJsonPath.includes('node_modules')) { - // if not in node_modules (in monorepo) dont auto optimize + if (options.filter && !options.filter(depPkgJsonPath)) { return [] } diff --git a/packages/vxrn/types/plugins/autoDepOptimizePlugin.d.ts b/packages/vxrn/types/plugins/autoDepOptimizePlugin.d.ts index db3d4ba96..86ff33f8b 100644 --- a/packages/vxrn/types/plugins/autoDepOptimizePlugin.d.ts +++ b/packages/vxrn/types/plugins/autoDepOptimizePlugin.d.ts @@ -1,8 +1,8 @@ -import type { Plugin } from 'vite'; +import { type Plugin } from 'vite'; +import type { AutoDepOptimizationOptions } from '../types'; import { type ScanDepsResult } from '../utils/scanDepsToOptimize'; -type FindDepsOptions = { +type FindDepsOptions = AutoDepOptimizationOptions & { root: string; - exclude?: string[]; onScannedDeps?: (result: ScanDepsResult) => void; }; export declare function autoDepOptimizePlugin(props: FindDepsOptions): Plugin; @@ -19,6 +19,6 @@ export declare function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMod noExternal: string[]; }; }>; -export declare function findDepsToOptimize({ root, mode, exclude }: FindDepsOptionsByMode): Promise; +export declare function findDepsToOptimize({ root, mode, exclude, include }: FindDepsOptionsByMode): Promise; export {}; //# sourceMappingURL=autoDepOptimizePlugin.d.ts.map \ No newline at end of file diff --git a/packages/vxrn/types/types.d.ts b/packages/vxrn/types/types.d.ts index 93365286d..b2697c62c 100644 --- a/packages/vxrn/types/types.d.ts +++ b/packages/vxrn/types/types.d.ts @@ -1,6 +1,6 @@ import type { Hono } from 'hono'; import type { OutputAsset, OutputChunk, TreeshakingOptions, TreeshakingPreset } from 'rollup'; -import type { InlineConfig, UserConfig } from 'vite'; +import type { FilterPattern, InlineConfig, UserConfig } from 'vite'; type RollupOutputList = [OutputChunk, ...(OutputChunk | OutputAsset)[]]; export type Mode = 'dev' | 'prod'; export type BuildArgs = { @@ -20,6 +20,10 @@ export type AfterBuildProps = { [key: string]: ClientManifestEntry; }; }; +export type AutoDepOptimizationOptions = { + exclude?: FilterPattern; + include?: FilterPattern; +}; export type ClientManifestEntry = { file: string; src?: string; diff --git a/packages/vxrn/types/utils/scanDepsToOptimize.d.ts b/packages/vxrn/types/utils/scanDepsToOptimize.d.ts index f8f4053d1..3f678a885 100644 --- a/packages/vxrn/types/utils/scanDepsToOptimize.d.ts +++ b/packages/vxrn/types/utils/scanDepsToOptimize.d.ts @@ -9,6 +9,7 @@ export declare const EXCLUDE_LIST_SET: Set; export declare const INCLUDE_LIST: string[]; export declare const INCLUDE_LIST_SET: Set; export declare function scanDepsToOptimize(packageJsonPath: string, options?: { + filter?: (id: string | unknown) => boolean; parentDepNames?: string[]; proceededDeps?: Map; /** If the content of the package.json is already read before calling this function, pass it here to avoid reading it again */