one
Version:
One is a new React Framework that makes Vite serve both native and web.
323 lines (322 loc) • 12.5 kB
JavaScript
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