one
Version:
One is a new React Framework that makes Vite serve both native and web.
431 lines (418 loc) • 14.7 kB
JavaScript
import { mkdirSync, writeFileSync } from "fs";
import { join, resolve } from "path";
import { pathToFileURL } from "url";
import { resolvePath } from "@vxrn/resolve";
import { cloudflare } from "@cloudflare/vite-plugin";
import { getPlatformEnvDefine } from "@vxrn/vite-plugin-metro";
import { exactRegex } from "rolldown/filter";
import { CACHE_KEY } from "../../constants.native.js";
import { virtalEntryIdClient, virtualEntryId } from "./virtualEntryConstants.native.js";
import { getManifest } from "../getManifest.native.js";
import { getRouterRootFromOneOptions } from "../../utils/getRouterRootFromOneOptions.native.js";
import { createCloudflareWranglerConfig, getCloudflareProjectNameSync, loadUserWranglerConfigSync, shouldEnableWorkerdDev } from "../cloudflareWranglerConfig.native.js";
var workerdDevEntryId = "virtual:one-workerd-dev-entry";
var resolvedWorkerdDevEntryId = `\0${workerdDevEntryId}`;
var WORKERD_DEV_DIR = "node_modules/.one-workerd-dev";
function serveOnly(plugin) {
var 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) {
var rel = routeFile.replace(/^\.\//, "");
return pathToFileURL(resolve(root, routerRoot, rel)).href;
}
function serializeImportMap(entries) {
return entries.map(function (param) {
var [key, href] = param;
return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(href)})`;
}).join(",\n");
}
function stripRouteForWorker(route) {
var {
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 === null || layouts === void 0 ? void 0 : layouts.map(function (layout) {
return {
contextKey: layout.contextKey
};
}),
middlewares: middlewares === null || middlewares === void 0 ? void 0 : middlewares.map(function (middleware) {
return {
contextKey: middleware.contextKey
};
})
};
}
function stubRouteBuildInfo(route, preloads) {
var _ref;
var _route_middlewares;
var pageType = route.type === "ssg" ? "ssr" : route.type;
return {
type: pageType,
path: route.page,
routeFile: route.file,
middlewares: (_ref = (_route_middlewares = route.middlewares) === null || _route_middlewares === void 0 ? void 0 : _route_middlewares.map(function (middleware) {
return middleware.contextKey;
})) !== null && _ref !== void 0 ? _ref : [],
preloadPath: "",
cssPreloadPath: "",
loaderPath: "",
cleanPath: route.urlCleanPath || route.page,
htmlPath: "",
clientJsPath: "",
serverJsPath: "",
params: {},
preloads,
css: [],
layoutCSS: []
};
}
function generateWorkerdDevEntryModule(args) {
var _options_router;
var {
root,
options,
routerRoot,
routePaths,
preloads
} = args;
var manifest = getManifest({
routerRoot: resolve(root, routerRoot),
ignoredRouteFiles: (_options_router = options.router) === null || _options_router === void 0 ? void 0 : _options_router.ignoredRouteFiles,
routePaths
});
if (!manifest) {
throw new Error("[one] workerd dev: no routes manifest");
}
var apiImports = /* @__PURE__ */new Map();
var middlewareImports = /* @__PURE__ */new Map();
var routeToBuildInfo = {};
var _iteratorNormalCompletion = true,
_didIteratorError = false,
_iteratorError = void 0;
try {
for (var _iterator = manifest.pageRoutes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var route = _step.value;
var _iteratorNormalCompletion1 = true,
_didIteratorError1 = false,
_iteratorError1 = void 0;
try {
for (var _iterator1 = (route.middlewares || [])[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true) {
var middleware = _step1.value;
if (!middleware.contextKey || middlewareImports.has(middleware.contextKey)) continue;
middlewareImports.set(middleware.contextKey, toImportHref(root, routerRoot, middleware.contextKey));
}
} catch (err) {
_didIteratorError1 = true;
_iteratorError1 = err;
} finally {
try {
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
_iterator1.return();
}
} finally {
if (_didIteratorError1) {
throw _iteratorError1;
}
}
}
if (route.file) {
routeToBuildInfo[route.file] = stubRouteBuildInfo(route, preloads);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var _iteratorNormalCompletion2 = true,
_didIteratorError2 = false,
_iteratorError2 = void 0;
try {
for (var _iterator2 = manifest.apiRoutes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var route1 = _step2.value;
if (route1.file) {
apiImports.set(route1.page, toImportHref(root, routerRoot, route1.file));
}
var _iteratorNormalCompletion3 = true,
_didIteratorError3 = false,
_iteratorError3 = void 0;
try {
for (var _iterator3 = (route1.middlewares || [])[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var middleware1 = _step3.value;
if (!middleware1.contextKey || middlewareImports.has(middleware1.contextKey)) continue;
middlewareImports.set(middleware1.contextKey, toImportHref(root, routerRoot, middleware1.contextKey));
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var pageRoutes = manifest.pageRoutes.map(stripRouteForWorker);
var apiRoutes = manifest.apiRoutes.map(stripRouteForWorker);
var buildInfo = {
constants: {
CACHE_KEY
},
oneOptions: options,
routeToBuildInfo,
pathToRoute: {},
routeMap: {},
manifest: {
pageRoutes,
apiRoutes,
allRoutes: [...pageRoutes, ...apiRoutes]
},
preloads: Object.fromEntries(preloads.map(function (preload) {
return [preload, true];
})),
cssPreloads: {},
loaders: {}
};
var spaPreloadScripts = preloads.map(function (preload) {
return `<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(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) {
var _options_web, _options_web1;
if (!shouldEnableWorkerdDev((_options_web = options.web) === null || _options_web === void 0 ? void 0 : _options_web.deploy, root)) {
return [];
}
var cacheDir = join(root, WORKERD_DEV_DIR);
var wranglerInputPath = join(cacheDir, "_wrangler.input.jsonc");
var workerEntryPath = join(cacheDir, "worker-entry.js");
mkdirSync(cacheDir, {
recursive: true
});
writeFileSync(workerEntryPath, `export { default } from ${JSON.stringify(workerdDevEntryId)}
`);
var userWranglerConfig = loadUserWranglerConfigSync(root);
var projectName = getCloudflareProjectNameSync(root);
var wranglerInputConfig = createCloudflareWranglerConfig(projectName, userWranglerConfig === null || userWranglerConfig === void 0 ? void 0 : userWranglerConfig.config);
wranglerInputConfig.main = "worker-entry.js";
writeFileSync(wranglerInputPath, `${JSON.stringify(wranglerInputConfig, null, 2)}
`);
var preloads = ((_options_web1 = options.web) === null || _options_web1 === void 0 ? void 0 : _options_web1.experimentalBundledDev) ? ["/bundledDevClient.mjs", "/assets/_virtual_one-entry.js"] : ["/@vite/client", virtalEntryIdClient];
var empty = resolvePath("@vxrn/vite-plugin-metro/empty", root);
var rnWebPkg = resolvePath("react-native-web/package.json", root);
var rnWeb = resolvePath("react-native-web", root);
var safeArea = resolvePath("@vxrn/safe-area", root);
var preparePlugin = {
name: "one:workerd-dev-prepare",
apply: "serve",
enforce: "pre",
config() {
return {
environments: {
worker: {
define: {
...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(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 /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)");
}
};
var virtualPlugin = {
name: "one:workerd-dev-entry",
apply: "serve",
resolveId: {
filter: {
id: exactRegex(workerdDevEntryId)
},
handler(id) {
if (this.environment.name !== "worker") return;
if (id === workerdDevEntryId) return resolvedWorkerdDevEntryId;
}
},
load: {
filter: {
id: exactRegex(resolvedWorkerdDevEntryId)
},
handler() {
if (this.environment.name !== "worker") return;
return generateWorkerdDevEntryModule({
root,
options,
routerRoot: getRouterRootFromOneOptions(options),
routePaths: routeIndex.getPaths(),
preloads
});
}
},
hotUpdate(param) {
var {
file
} = param;
if (this.environment.name !== "worker") return;
var graph = this.environment.moduleGraph;
var entry = graph.getModuleById(resolvedWorkerdDevEntryId);
if (!entry) return;
var modules = graph.getModulesByFile(file);
if (!(modules === null || modules === void 0 ? void 0 : modules.size)) return;
graph.invalidateModule(entry);
}
};
var cloudflarePlugins = cloudflare({
configPath: wranglerInputPath,
viteEnvironment: {
name: "worker"
}
}).map(serveOnly);
return [preparePlugin, virtualPlugin, ...cloudflarePlugins];
}
export { createWorkerdDevPlugins, generateWorkerdDevEntryModule, shouldEnableWorkerdDev, workerdDevEntryId };
//# sourceMappingURL=workerdDevPlugin.native.js.map