UNPKG

one

Version:

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

323 lines (322 loc) 12.5 kB
import { clearCompilerCache, configureVXRNCompilerPlugin } from "@vxrn/compiler"; import { resolvePath } from "@vxrn/resolve"; import events from "node:events"; import path from "node:path"; import { barrel } from "vite-plugin-barrel"; import tsconfigPaths from "vite-tsconfig-paths"; import { autoDepOptimizePlugin, getOptimizeDeps, getOptionsFilled, loadEnv } from "vxrn"; import { CACHE_KEY } from "../constants.mjs"; import "../polyfills-server.mjs"; import { getRouterRootFromOneOptions } from "../utils/getRouterRootFromOneOptions.mjs"; import { ensureTSConfig } from "./ensureTsConfig.mjs"; import { setOneOptions } from "./loadConfig.mjs"; import { clientTreeShakePlugin } from "./plugins/clientTreeShakePlugin.mjs"; import { createFileSystemRouterPlugin } from "./plugins/fileSystemRouterPlugin.mjs"; import { fixDependenciesPlugin } from "./plugins/fixDependenciesPlugin.mjs"; import { generateFileSystemRouteTypesPlugin } from "./plugins/generateFileSystemRouteTypesPlugin.mjs"; import { SSRCSSPlugin } from "./plugins/SSRCSSPlugin.mjs"; import { virtualEntryId } from "./plugins/virtualEntryConstants.mjs"; import { createVirtualEntry } from "./plugins/virtualEntryPlugin.mjs"; events.setMaxListeners(1e3); globalThis.__vxrnEnableNativeEnv = !0; function one(options = {}) { if (!globalThis.__oneOptions) return setOneOptions(options), globalThis.__vxrnPluginConfig__ = options, []; clearCompilerCache(), options.config?.ensureTSConfig !== !1 && ensureTSConfig(); const { optimizeDeps } = getOptimizeDeps("build"), optimizeIds = optimizeDeps.include, optimizeIdRegex = new RegExp( // santize ids for regex // https://stackoverflow.com/questions/6300183/sanitize-string-of-regex-characters-before-regexp-build `${optimizeIds.map(id => id.replace(/[#-.]|[[-^]|[?|{}]/g, "\\$&")).join("|")}`); let tsConfigPathsPlugin = null; const vxrnOptions = getOptionsFilled(), root = vxrnOptions?.root || process.cwd(), barrelOption = options.optimization?.barrel, compiler = options.react?.compiler; compiler && configureVXRNCompilerPlugin({ enableCompiler: compiler === "native" ? ["ios", "android"] : compiler === "web" ? ["ssr", "client"] : !0 }); const autoDepsOptions = options.ssr?.autoDepsOptimization, devAndProdPlugins = [{ name: "one:config", // @ts-ignore __get: options }, barrelOption === !1 ? null : barrel({ packages: Array.isArray(barrelOption) ? barrelOption : ["@tamagui/lucide-icons"] }), { name: "one-define-client-env", async config(userConfig) { const { clientEnvDefine } = await loadEnv(vxrnOptions?.mode ?? "development", process.cwd(), userConfig?.envPrefix); return { define: clientEnvDefine }; } }, ...(autoDepsOptions === !1 ? [] : [autoDepOptimizePlugin({ onScannedDeps({ hasReanimated, hasNativewind }) { configureVXRNCompilerPlugin({ enableReanimated: hasReanimated, enableNativeCSS: options.native?.css ?? hasNativewind, enableNativewind: hasNativewind }); }, root, include: /node_modules/, ...(autoDepsOptions === !0 ? {} : autoDepsOptions) })]), // proxy because you cant add a plugin inside a plugin new Proxy({ name: "one:tsconfig-paths", config(configIncoming) { const pathsConfig = options.config?.tsConfigPaths; pathsConfig !== !1 && (configIncoming.plugins?.flat().some(p => p && p.name === "vite-tsconfig-paths") || (tsConfigPathsPlugin = tsconfigPaths(pathsConfig && typeof pathsConfig == "object" ? pathsConfig : {}))); }, configResolved() {}, resolveId() {} }, { get(target, key, thisArg) { if (key === "config" || key === "name") return Reflect.get(target, key, thisArg); if (tsConfigPathsPlugin) return Reflect.get(tsConfigPathsPlugin, key, thisArg); } }), { name: "one-aliases", enforce: "pre", config() { let tslibLitePath = ""; try { tslibLitePath = resolvePath("@vxrn/tslib-lite", process.cwd()); } catch (err) { console.info("Can't find tslib-lite, falling back to tslib"), process.env.DEBUG && console.error(err); } return { resolve: { alias: { // testing getting transition between routes working // 'use-sync-external-store/with-selector': resolvePath( // 'use-sync-external-store/shim/with-selector' // ), ...(tslibLitePath && { tslib: tslibLitePath }) } // [ // { // find: /tslib/, // replacement: resolvePath('@vxrn/tslib-lite'), // }, // // not working but would save ~30Kb stat // // { // // find: /@react-navigation\/core.*\/getStateFromPath/, // // replacement: join(forkPath, 'fork', 'getStateFromPath.mjs'), // // }, // // { // // find: /@react-navigation\/core.*\/getPathFromState/, // // replacement: join(forkPath, 'fork', 'getPathFromState.mjs'), // // }, // ], } }; } }, { name: "one:init-config", config() { return { define: { ...(options.web?.defaultRenderMode && { "process.env.ONE_DEFAULT_RENDER_MODE": JSON.stringify(options.web.defaultRenderMode), "import.meta.env.ONE_DEFAULT_RENDER_MODE": JSON.stringify(options.web.defaultRenderMode) }), ...(options.setupFile && { "process.env.ONE_SETUP_FILE": JSON.stringify(options.setupFile) }), ...(process.env.NODE_ENV !== "production" && vxrnOptions && { "process.env.ONE_SERVER_URL": JSON.stringify(vxrnOptions.server.url), "import.meta.env.ONE_SERVER_URL": JSON.stringify(vxrnOptions.server.url) }) }, environments: { client: { define: { "process.env.VITE_ENVIRONMENT": '"client"', "process.env.TAMAGUI_ENVIRONMENT": '"client"', "import.meta.env.VITE_ENVIRONMENT": '"client"', "process.env.EXPO_OS": '"web"' } }, ssr: { define: { "process.env.VITE_ENVIRONMENT": '"ssr"', "process.env.TAMAGUI_ENVIRONMENT": '"ssr"', "import.meta.env.VITE_ENVIRONMENT": '"ssr"', "process.env.EXPO_OS": '"web"' } }, ios: { define: { "process.env.VITE_ENVIRONMENT": '"ios"', "process.env.TAMAGUI_ENVIRONMENT": '"ios"', "import.meta.env.VITE_ENVIRONMENT": '"ios"', "process.env.EXPO_OS": '"ios"' } }, android: { define: { "process.env.VITE_ENVIRONMENT": '"android"', "process.env.TAMAGUI_ENVIRONMENT": '"android"', "import.meta.env.VITE_ENVIRONMENT": '"android"', "process.env.EXPO_OS": '"android"' } } } }; } }, { name: "one:tamagui", config() { return { define: { // safe to set because it only affects web in tamagui, and one is always react 19 "process.env.TAMAGUI_REACT_19": '"1"', "process.env.TAMAGUI_SKIP_THEME_OPTIMIZATION": '"1"' }, environments: { ssr: { define: { "process.env.TAMAGUI_IS_SERVER": '"1"', "process.env.TAMAGUI_KEEP_THEMES": '"1"' } }, ios: { define: { "process.env.TAMAGUI_KEEP_THEMES": '"1"' } }, android: { define: { "process.env.TAMAGUI_KEEP_THEMES": '"1"' } } } }; } }, { name: "route-module-hmr-fix", hotUpdate({ server, modules }) { return modules.map(m => { const { id } = m; return id && path.relative(server.config.root, id).split(path.sep)[0] === "app" && (m.acceptedHmrExports = /* @__PURE__ */new Set()), m; }); } }, // Plugins may transform the source code and add imports of `react/jsx-dev-runtime`, which won't be discovered by Vite's initial `scanImports` since the implementation is using ESbuild where such plugins are not executed. // Thus, if the project has a valid `react/jsx-dev-runtime` import, we tell Vite to optimize it, so Vite won't only discover it on the next page load and trigger a full reload. { name: "one:optimize-dev-deps", config(_, env) { if (env.mode === "development") return { optimizeDeps: { include: ["react/jsx-dev-runtime", "react/compiler-runtime"] } }; } }, { name: "one:remove-server-from-client", enforce: "pre", transform(code, id) { if (this.environment.name === "client" && id.includes("one-server-only")) return code.replace('import { AsyncLocalStorage } from "node:async_hooks"', "class AsyncLocalStorage {}"); } }], scan = options.react?.scan, reactScanPlugin = { name: "one:react-scan", config() { return reactScanConfig; } }; devAndProdPlugins.push(reactScanPlugin); const reactScanConfig = (() => { const stringify = obj => JSON.stringify(JSON.stringify(obj)), configs = { disabled: { define: { "process.env.ONE_ENABLE_REACT_SCAN": '""' } }, enabled: { define: { "process.env.ONE_ENABLE_REACT_SCAN": stringify({ enabled: !0, animationSpeed: "slow", showToolbar: !1 }) } } }, getConfigFor = platform => { if (process.env.NODE_ENV === "production" || !scan) return configs.disabled; if (scan === !0) return configs.enabled; if (typeof scan == "string") return scan === "native" && platform === "client" || scan === "web" && platform !== "client" ? configs.disabled : configs.enabled; const defaultConfig = scan.options || configs.enabled, perPlatformConfig = platform === "ios" || platform === "android" ? scan.native : scan.web; return { define: { "process.env.ONE_ENABLE_REACT_SCAN": stringify({ ...defaultConfig, ...perPlatformConfig }) } }; }; return { environments: { client: getConfigFor("client"), ios: getConfigFor("ios"), android: getConfigFor("android") } }; })(), nativeWebDevAndProdPlugsin = [clientTreeShakePlugin(), reactScanPlugin]; globalThis.__vxrnAddNativePlugins = nativeWebDevAndProdPlugsin, globalThis.__vxrnAddWebPluginsProd = devAndProdPlugins; const flags = { experimentalPreventLayoutRemounting: options.router?.experimental?.preventLayoutRemounting }, routerRoot = getRouterRootFromOneOptions(options); return [...devAndProdPlugins, ...nativeWebDevAndProdPlugsin, /** * This is really the meat of one, where it handles requests: */ createFileSystemRouterPlugin(options), generateFileSystemRouteTypesPlugin(options), fixDependenciesPlugin(options.deps), createVirtualEntry({ ...options, flags, root: routerRoot }), { name: "one-define-environment", config() { return { define: { ...(options.native?.key && { "process.env.ONE_APP_NAME": JSON.stringify(options.native.key), "import.meta.env.ONE_APP_NAME": JSON.stringify(options.native.key) }), "process.env.ONE_CACHE_KEY": JSON.stringify(CACHE_KEY), "import.meta.env.ONE_CACHE_KEY": JSON.stringify(CACHE_KEY) } }; } }, SSRCSSPlugin({ entries: [virtualEntryId] })]; } export { one }; //# sourceMappingURL=one.mjs.map