UNPKG

one

Version:

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

364 lines (352 loc) 13 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var workerdDevPlugin_exports = {}; __export(workerdDevPlugin_exports, { createWorkerdDevPlugins: () => createWorkerdDevPlugins, generateWorkerdDevEntryModule: () => generateWorkerdDevEntryModule, shouldEnableWorkerdDev: () => import_cloudflareWranglerConfig.shouldEnableWorkerdDev, workerdDevEntryId: () => workerdDevEntryId }); module.exports = __toCommonJS(workerdDevPlugin_exports); var import_node_fs = require("node:fs"); var import_node_path = require("node:path"); var import_node_url = require("node:url"); var import_resolve = require("@vxrn/resolve"); var import_vite_plugin = require("@cloudflare/vite-plugin"); var import_vite_plugin_metro = require("@vxrn/vite-plugin-metro"); var import_filter = require("rolldown/filter"); var import_constants = require("../../constants.cjs"); var import_virtualEntryConstants = require("./virtualEntryConstants.cjs"); var import_getManifest = require("../getManifest.cjs"); var import_getRouterRootFromOneOptions = require("../../utils/getRouterRootFromOneOptions.cjs"); var import_cloudflareWranglerConfig = require("../cloudflareWranglerConfig.cjs"); const workerdDevEntryId = "virtual:one-workerd-dev-entry"; const resolvedWorkerdDevEntryId = `\0${workerdDevEntryId}`; const WORKERD_DEV_DIR = "node_modules/.one-workerd-dev"; function serveOnly(plugin) { const existing = plugin.apply; return { ...plugin, apply(config, env) { if (env.command !== "serve") return false; if (typeof existing === "function") return existing(config, env); if (existing === "build") return false; return true; } }; } function toImportHref(root, routerRoot, routeFile) { const rel = routeFile.replace(/^\.\//, ""); return (0, import_node_url.pathToFileURL)((0, import_node_path.resolve)(root, routerRoot, rel)).href; } function serializeImportMap(entries) { return entries.map(([key, href]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(href)})`).join(",\n"); } function stripRouteForWorker(route) { const { layouts, middlewares, ...rest } = route; return { ...rest, // live-render SSG in dev the same way the node middleware does type: route.type === "ssg" ? "ssr" : route.type, layouts: layouts?.map(layout => ({ contextKey: layout.contextKey })), middlewares: middlewares?.map(middleware => ({ contextKey: middleware.contextKey })) }; } function stubRouteBuildInfo(route, preloads) { const pageType = route.type === "ssg" ? "ssr" : route.type; return { type: pageType, path: route.page, routeFile: route.file, middlewares: route.middlewares?.map(middleware => middleware.contextKey) ?? [], preloadPath: "", cssPreloadPath: "", loaderPath: "", cleanPath: route.urlCleanPath || route.page, htmlPath: "", clientJsPath: "", serverJsPath: "", params: {}, preloads, css: [], layoutCSS: [] }; } function generateWorkerdDevEntryModule(args) { const { root, options, routerRoot, routePaths, preloads } = args; const manifest = (0, import_getManifest.getManifest)({ routerRoot: (0, import_node_path.resolve)(root, routerRoot), ignoredRouteFiles: options.router?.ignoredRouteFiles, routePaths }); if (!manifest) { throw new Error("[one] workerd dev: no routes manifest"); } const apiImports = /* @__PURE__ */new Map(); const middlewareImports = /* @__PURE__ */new Map(); const routeToBuildInfo = {}; for (const route of manifest.pageRoutes) { for (const middleware of route.middlewares || []) { if (!middleware.contextKey || middlewareImports.has(middleware.contextKey)) continue; middlewareImports.set(middleware.contextKey, toImportHref(root, routerRoot, middleware.contextKey)); } if (route.file) { routeToBuildInfo[route.file] = stubRouteBuildInfo(route, preloads); } } for (const route of manifest.apiRoutes) { if (route.file) { apiImports.set(route.page, toImportHref(root, routerRoot, route.file)); } for (const middleware of route.middlewares || []) { if (!middleware.contextKey || middlewareImports.has(middleware.contextKey)) continue; middlewareImports.set(middleware.contextKey, toImportHref(root, routerRoot, middleware.contextKey)); } } const pageRoutes = manifest.pageRoutes.map(stripRouteForWorker); const apiRoutes = manifest.apiRoutes.map(stripRouteForWorker); const buildInfo = { constants: { CACHE_KEY: import_constants.CACHE_KEY }, oneOptions: options, routeToBuildInfo, pathToRoute: {}, routeMap: {}, manifest: { pageRoutes, apiRoutes, allRoutes: [...pageRoutes, ...apiRoutes] }, preloads: Object.fromEntries(preloads.map(preload => [preload, true])), cssPreloads: {}, loaders: {} }; const spaPreloadScripts = preloads.map(preload => `<script type="module" src="${preload}"></script>`).join(""); return `if (typeof MessageChannel === 'undefined') { globalThis.MessageChannel = class MessageChannel { constructor() { this.port1 = { postMessage: () => {}, onmessage: null, close: () => {} } this.port2 = { postMessage: () => {}, onmessage: null, close: () => {} } } } } import { serve, setFetchStaticHtml } from 'one/serve-worker' const lazyRoutes = { serverEntry: () => import(${JSON.stringify(import_virtualEntryConstants.virtualEntryId)}), api: { ${serializeImportMap([...apiImports])} }, middlewares: { ${serializeImportMap([...middlewareImports])} } } const buildInfo = ${JSON.stringify(buildInfo)} let server export default { async fetch(request, env, ctx) { const url = new URL(request.url) const pathname = url.pathname if (pathname === '/status' || pathname.startsWith('/status?')) { return new Response('packager-status:running', { headers: { 'content-type': 'text/plain' }, }) } if (!server) { server = await serve(buildInfo, lazyRoutes, { disableModuleCache: true }) } if (env && env.ASSETS) { setFetchStaticHtml(async (path) => { try { const assetUrl = new URL(request.url) assetUrl.pathname = path const assetResponse = await env.ASSETS.fetch(new Request(assetUrl)) if (assetResponse && assetResponse.ok) { return await assetResponse.text() } } catch {} return null }) } try { const response = await server.fetch(request, env, ctx) if (!response || response.status === 404) { if (env && env.ASSETS) { try { const assetResponse = await env.ASSETS.fetch(request) if (assetResponse && assetResponse.status !== 404) { return assetResponse } } catch {} } } if (!response) { if (request.method === 'GET' || request.method === 'HEAD') { return new Response( '<!DOCTYPE html><html><head>' + ${JSON.stringify(spaPreloadScripts)} + '</head></html>', { headers: { 'content-type': 'text/html' } } ) } return new Response('Not Found', { status: 404 }) } return response } finally { setFetchStaticHtml(null) } } } `; } function createWorkerdDevPlugins(options, root, routeIndex) { if (!(0, import_cloudflareWranglerConfig.shouldEnableWorkerdDev)(options.web?.deploy, root)) { return []; } const cacheDir = (0, import_node_path.join)(root, WORKERD_DEV_DIR); const wranglerInputPath = (0, import_node_path.join)(cacheDir, "_wrangler.input.jsonc"); const workerEntryPath = (0, import_node_path.join)(cacheDir, "worker-entry.js"); (0, import_node_fs.mkdirSync)(cacheDir, { recursive: true }); (0, import_node_fs.writeFileSync)(workerEntryPath, `export { default } from ${JSON.stringify(workerdDevEntryId)} `); const userWranglerConfig = (0, import_cloudflareWranglerConfig.loadUserWranglerConfigSync)(root); const projectName = (0, import_cloudflareWranglerConfig.getCloudflareProjectNameSync)(root); const wranglerInputConfig = (0, import_cloudflareWranglerConfig.createCloudflareWranglerConfig)(projectName, userWranglerConfig?.config); wranglerInputConfig.main = "worker-entry.js"; (0, import_node_fs.writeFileSync)(wranglerInputPath, `${JSON.stringify(wranglerInputConfig, null, 2)} `); const preloads = options.web?.experimentalBundledDev ? ["/bundledDevClient.mjs", "/assets/_virtual_one-entry.js"] : ["/@vite/client", import_virtualEntryConstants.virtalEntryIdClient]; const empty = (0, import_resolve.resolvePath)("@vxrn/vite-plugin-metro/empty", root); const rnWebPkg = (0, import_resolve.resolvePath)("react-native-web/package.json", root); const rnWeb = (0, import_resolve.resolvePath)("react-native-web", root); const safeArea = (0, import_resolve.resolvePath)("@vxrn/safe-area", root); const preparePlugin = { name: "one:workerd-dev-prepare", apply: "serve", enforce: "pre", config() { return { environments: { worker: { define: { ...(0, import_vite_plugin_metro.getPlatformEnvDefine)("ssr"), "process.env.TAMAGUI_IS_SERVER": JSON.stringify("1"), "process.env.TAMAGUI_KEEP_THEMES": JSON.stringify("1"), "process.env.ONE_CACHE_KEY": JSON.stringify(import_constants.CACHE_KEY) }, optimizeDeps: { include: ["react", "react-dom", "react-dom/client", "react-dom/server.browser", "react/jsx-runtime", "react/jsx-dev-runtime"] }, build: { rolldownOptions: { shimMissingExports: true } } } } }; }, // environment resolve options have no alias field; match the production // worker aliases with a worker-only resolveId (rolldown cannot parse RN Flow) resolveId: { filter: { id: /^(react-native(\/|$)|react-native-safe-area-context$)/ }, handler(source) { if (this.environment.name !== "worker") return; if (/^react-native\/Libraries\//.test(source)) return empty; if (source === "react-native/package.json") return rnWebPkg; if (source === "react-native") return rnWeb; if (source === "react-native-safe-area-context") return safeArea; } }, configureServer(server) { if (!server.environments.worker) { throw new Error(`[one] ONE_EXPERIMENTAL_WORKER_DEV=1 is set but @cloudflare/vite-plugin did not register a "worker" environment. Expected wrangler config at ${wranglerInputPath}.`); } console.info("[one] experimental workerd dev enabled (ONE_EXPERIMENTAL_WORKER_DEV=1)"); } }; const virtualPlugin = { name: "one:workerd-dev-entry", apply: "serve", resolveId: { filter: { id: (0, import_filter.exactRegex)(workerdDevEntryId) }, handler(id) { if (this.environment.name !== "worker") return; if (id === workerdDevEntryId) return resolvedWorkerdDevEntryId; } }, load: { filter: { id: (0, import_filter.exactRegex)(resolvedWorkerdDevEntryId) }, handler() { if (this.environment.name !== "worker") return; return generateWorkerdDevEntryModule({ root, options, routerRoot: (0, import_getRouterRootFromOneOptions.getRouterRootFromOneOptions)(options), routePaths: routeIndex.getPaths(), preloads }); } }, hotUpdate({ file }) { if (this.environment.name !== "worker") return; const graph = this.environment.moduleGraph; const entry = graph.getModuleById(resolvedWorkerdDevEntryId); if (!entry) return; const modules = graph.getModulesByFile(file); if (!modules?.size) return; graph.invalidateModule(entry); } }; const cloudflarePlugins = (0, import_vite_plugin.cloudflare)({ configPath: wranglerInputPath, viteEnvironment: { name: "worker" } }).map(serveOnly); return [preparePlugin, virtualPlugin, ...cloudflarePlugins]; }