Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/2.deploy/20.providers/edgeone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# EdgeOne

> Deploy Nitro apps to EdgeOne.

**Preset:** `edgeone-pages`

:read-more{to="https://pages.edgeone.ai/"}


## Using the control panel

1. In the [EdgeOne pages control panel](https://console.tencentcloud.com/edgeone/pages), click **Create project**.
2. Choose **Import Git repository** as your deployment method. We support deployment on GitHub, GitLab, Gitee, and CNB.
3. Choose the GitHub **repository** and **branch** containing your application code.
4. Complete your project setup.
5. Click the **Deploy** button.

## Using the EdgeOne CLI

You can also install the Pages scaffolding tool. For detailed installation and usage, refer to [EdgeOne CLI](https://pages.edgeone.ai/document/edgeone-cli).

Once configured, use the edgeone pages deploy command to deploy the project. During deployment, the CLI will first automatically build the project, then upload and publish the build artifacts.
2 changes: 2 additions & 0 deletions src/presets/_all.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import _cleavr from "./cleavr/preset.ts";
import _cloudflare from "./cloudflare/preset.ts";
import _deno from "./deno/preset.ts";
import _digitalocean from "./digitalocean/preset.ts";
import _edgeone from "./edgeone/preset.ts";
import _firebase from "./firebase/preset.ts";
import _flightcontrol from "./flightcontrol/preset.ts";
import _genezio from "./genezio/preset.ts";
Expand Down Expand Up @@ -40,6 +41,7 @@ export default [
..._cloudflare,
..._deno,
..._digitalocean,
..._edgeone,
..._firebase,
..._flightcontrol,
..._genezio,
Expand Down
4 changes: 2 additions & 2 deletions src/presets/_types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ export interface PresetOptions {

export const presetsWithConfig = ["awsAmplify","awsLambda","azure","cloudflare","firebase","netlify","vercel"] as const;

export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cloudflare-dev" | "cloudflare-durable" | "cloudflare-module" | "cloudflare-pages" | "cloudflare-pages-static" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "firebase-app-hosting" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis-handler" | "iis-node" | "koyeb" | "netlify" | "netlify-edge" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-middleware" | "node-server" | "platform-sh" | "render-com" | "standard" | "static" | "stormkit" | "vercel" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";
export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cloudflare-dev" | "cloudflare-durable" | "cloudflare-module" | "cloudflare-pages" | "cloudflare-pages-static" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "edgeone-pages" | "firebase-app-hosting" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis-handler" | "iis-node" | "koyeb" | "netlify" | "netlify-edge" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-middleware" | "node-server" | "platform-sh" | "render-com" | "standard" | "static" | "stormkit" | "vercel" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";

export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cloudflare-dev" | "cloudflareDev" | "cloudflare_dev" | "cloudflare-durable" | "cloudflareDurable" | "cloudflare_durable" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "firebase-app-hosting" | "firebaseAppHosting" | "firebase_app_hosting" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "netlify" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-middleware" | "nodeMiddleware" | "node_middleware" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "standard" | "static" | "stormkit" | "vercel" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cloudflare-dev" | "cloudflareDev" | "cloudflare_dev" | "cloudflare-durable" | "cloudflareDurable" | "cloudflare_durable" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "edgeone-pages" | "edgeonePages" | "edgeone_pages" | "firebase-app-hosting" | "firebaseAppHosting" | "firebase_app_hosting" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "netlify" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-middleware" | "nodeMiddleware" | "node_middleware" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "standard" | "static" | "stormkit" | "vercel" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
31 changes: 31 additions & 0 deletions src/presets/edgeone/preset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineNitroPreset } from "../_utils/preset.ts";
import { writeEdgeOneRoutes } from "./utils.ts";
import type { Nitro } from "nitro/types";

const edgeone = defineNitroPreset(
{
entry: "./edgeone/runtime/edgeone",
extends: "node-server",
serveStatic: true,
output: {
dir: "{{ rootDir }}/.edgeone",
serverDir: "{{ output.dir }}/server-handler",
publicDir: "{{ output.dir }}/assets",
},
rollupConfig: {
output: {
entryFileNames: "handler.js",
},
},
hooks: {
async compiled(nitro: Nitro) {
await writeEdgeOneRoutes(nitro);
},
},
},
{
name: "edgeone-pages" as const,
}
);

export default [edgeone] as const;
20 changes: 20 additions & 0 deletions src/presets/edgeone/runtime/edgeone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import "#nitro/virtual/polyfills";
import { NodeRequest } from "srvx/node";
import { useNitroApp } from "nitro/app";
import type { IncomingMessage } from "node:http";

const nitroApp = useNitroApp();

interface EdgeOneRequest extends IncomingMessage {
url: string;
method: string;
headers: Record<string, string | string[] | undefined>;
}

// EdgeOne bootstrap expects: async (req, context) => Response
export default async function handle(req: EdgeOneRequest) {
// Use srvx NodeRequest to convert Node.js request to Web Request
const request = new NodeRequest({ req });

return nitroApp.fetch(request);
}
128 changes: 128 additions & 0 deletions src/presets/edgeone/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import type { Nitro } from "nitro/types";
import fsp from "node:fs/promises";
import { relative, dirname, join } from "pathe";
import consola from "consola";
import { colors } from "consola/utils";
interface FrameworkRoute {
path: string;
isStatic?: boolean;
isr?: number;
}

export async function writeEdgeOneRoutes(nitro: Nitro) {
// Ensure routes are synced
nitro.routing.sync();
const meta = {
conf: {
ssr404: true,
},
frameworkRoutes: [] as FrameworkRoute[],
};

// 1. Get all API routes (server-side route handlers)
const apiRoutes = nitro.routing.routes.routes
.filter((route) => {
// Filter out middleware and wildcard routes (e.g., /**)
const handler = Array.isArray(route.data) ? route.data[0] : route.data;
return handler && !handler.middleware && route.route !== "/**";
})
.map((route) => ({
path: route.route,
method: route.method || "*",
handler: Array.isArray(route.data) ? route.data[0] : route.data,
}));
for (const route of apiRoutes) {
meta.frameworkRoutes.push({
path: route.path,
});
}

// 2. Get all page routes (prerendered routes)
const pageRoutes = (nitro._prerenderedRoutes || []).map((route) => ({
path: route.route,
fileName: route.fileName,
contentType: route.contentType,
}));

// 3. Get user-defined prerender routes
const userPrerenderRoutes = nitro.options.prerender?.routes || [];
// 4. Get all routes marked as prerender in route rules
const prerenderRouteRules = Object.entries(nitro.options.routeRules || {})
.filter(([_, rules]) => rules.prerender)
.map(([path]) => path);

// 5. Get all routes with SWR/cache settings from route rules
// Note: `swr: true` shortcut is normalized to `cache: { swr: true }` after config resolution
const swrRouteRules = Object.entries(nitro.options.routeRules || {})
.filter(([_, rules]) => {
// Check if cache.swr is enabled (normalized form)
if (rules.cache && typeof rules.cache === "object" && rules.cache.swr) {
return true;
}
return false;
})
.map(([path, rules]) => ({
path,
cache: rules.cache as { swr?: boolean; maxAge?: number },
}));
for (const route of swrRouteRules) {
const maxAge = route.cache.maxAge;
for (const frameworkRoute of meta.frameworkRoutes) {
if (frameworkRoute.path === route.path) {
Reflect.set(frameworkRoute, "isStatic", false);
Reflect.set(frameworkRoute, "isr", maxAge);
}
}
}

// Merge all prerender routes
const allPrerenderRoutes = [
...new Set([
...userPrerenderRoutes,
...prerenderRouteRules,
...pageRoutes.map((r) => r.path),
]),
];
for (const route of allPrerenderRoutes) {
meta.frameworkRoutes.push({
path: route,
isStatic: true,
});
}

await writeFile(
join(nitro.options.output.dir, "meta.json"),
JSON.stringify(meta, null, 2)
);
await writeFile(
join(nitro.options.output.serverDir, "meta.json"),
JSON.stringify(meta, null, 2)
);

// Return all route information
return {
apiRoutes,
pageRoutes,
userPrerenderRoutes,
prerenderRouteRules,
allPrerenderRoutes,
swrRouteRules,
};
}

function prettyPath(p: string, highlight = true) {
p = relative(process.cwd(), p);
return highlight ? colors.cyan(p) : p;
}

async function writeFile(file: string, contents: Buffer | string, log = false) {
await fsp.mkdir(dirname(file), { recursive: true });
await fsp.writeFile(
file,
contents,
typeof contents === "string" ? "utf8" : undefined
);
if (log) {
consola.info("Generated", prettyPath(file));
}
}