Skip to content

Commit

Permalink
feat(vxrn): rename ssr.disableAutoDepsPreBundling to autoDepsOptimiza…
Browse files Browse the repository at this point in the history
…tion and add options for both include and exclude filters
  • Loading branch information
natew committed Feb 6, 2025
1 parent 63c396e commit cef9dff
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 45 deletions.
34 changes: 24 additions & 10 deletions packages/one/src/router/useScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -238,6 +238,18 @@ export function getQualifiedRouteComponent(value: RouteNode) {
return <Component {...props} ref={ref} />
})

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 <Suspense fallback={null}>{children}</Suspense>
}
return children
}

const QualifiedRoute = React.forwardRef(
(
{
Expand All @@ -254,15 +266,17 @@ export function getQualifiedRouteComponent(value: RouteNode) {
return (
<Route route={route} node={value}>
<RootErrorBoundary>
<ScreenComponent
{...{
...props,
ref,
// Expose the template segment path, e.g. `(home)`, `[foo]`, `index`
// the intention is to make it possible to deduce shared routes.
segment: value.route,
}}
/>
{wrapSuspense(
<ScreenComponent
{...{
...props,
ref,
// Expose the template segment path, e.g. `(home)`, `[foo]`, `index`
// the intention is to make it possible to deduce shared routes.
segment: value.route,
}}
/>
)}
</RootErrorBoundary>
</Route>
)
Expand Down
9 changes: 5 additions & 4 deletions packages/one/src/vite/one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export function one(options: One.PluginOptions = {}): PluginOption {
})
}

const autoDepsOptions = options.ssr?.autoDepsOptimization

const devAndProdPlugins: Plugin[] = [
{
name: 'one:config',
Expand All @@ -97,7 +99,7 @@ export function one(options: One.PluginOptions = {}): PluginOption {
},
},

...(options.ssr?.disableAutoDepsPreBundling === true
...(autoDepsOptions === false
? []
: [
autoDepOptimizePlugin({
Expand All @@ -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),
}),
]),

Expand Down
11 changes: 9 additions & 2 deletions packages/one/src/vite/types.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
}
}

Expand Down
12 changes: 9 additions & 3 deletions packages/one/types/vite/types.d.ts
Original file line number Diff line number Diff line change
@@ -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<TRegex = string> = {
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 11 additions & 12 deletions packages/vxrn/src/plugins/autoDepOptimizePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
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'

const name = 'vxrn:auto-dep-optimize'

const { debug, debugDetails } = createDebugger(name)

type FindDepsOptions = {
type FindDepsOptions = AutoDepOptimizationOptions & {
root: string
exclude?: string[]
onScannedDeps?: (result: ScanDepsResult) => void
}

Expand All @@ -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
Expand All @@ -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,
},
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
7 changes: 6 additions & 1 deletion packages/vxrn/src/types.ts
Original file line number Diff line number Diff line change
@@ -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)[]]

Expand All @@ -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
Expand Down
10 changes: 2 additions & 8 deletions packages/vxrn/src/utils/scanDepsToOptimize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const rootOptions = {}
export async function scanDepsToOptimize(
packageJsonPath: string,
options: {
filter?: (id: string | unknown) => boolean
parentDepNames?: string[]
proceededDeps?: Map<string, string[]>
/** If the content of the package.json is already read before calling this function, pass it here to avoid reading it again */
Expand Down Expand Up @@ -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 []
}

Expand Down
8 changes: 4 additions & 4 deletions packages/vxrn/types/plugins/autoDepOptimizePlugin.d.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,6 +19,6 @@ export declare function getScannedOptimizeDepsConfig(props: FindDepsOptionsByMod
noExternal: string[];
};
}>;
export declare function findDepsToOptimize({ root, mode, exclude }: FindDepsOptionsByMode): Promise<ScanDepsResult>;
export declare function findDepsToOptimize({ root, mode, exclude, include }: FindDepsOptionsByMode): Promise<ScanDepsResult>;
export {};
//# sourceMappingURL=autoDepOptimizePlugin.d.ts.map
6 changes: 5 additions & 1 deletion packages/vxrn/types/types.d.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -20,6 +20,10 @@ export type AfterBuildProps = {
[key: string]: ClientManifestEntry;
};
};
export type AutoDepOptimizationOptions = {
exclude?: FilterPattern;
include?: FilterPattern;
};
export type ClientManifestEntry = {
file: string;
src?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/vxrn/types/utils/scanDepsToOptimize.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export declare const EXCLUDE_LIST_SET: Set<string>;
export declare const INCLUDE_LIST: string[];
export declare const INCLUDE_LIST_SET: Set<string>;
export declare function scanDepsToOptimize(packageJsonPath: string, options?: {
filter?: (id: string | unknown) => boolean;
parentDepNames?: string[];
proceededDeps?: Map<string, string[]>;
/** If the content of the package.json is already read before calling this function, pass it here to avoid reading it again */
Expand Down

0 comments on commit cef9dff

Please sign in to comment.