@edgeone/opennextjs-pages 0.1.5 → 0.1.6-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,18 +9,130 @@ import "../esm-chunks/chunk-6BT4RYQJ.js";
9
9
  // src/build/routes.ts
10
10
  import * as fs from "fs";
11
11
  import * as path from "path";
12
- function isRE2Compatible(regexSource) {
13
- const unsupported = [
14
- /\(\?[=!<]/,
15
- // 断言(前瞻、后顾)
16
- /\(\?>/,
17
- // 原子组
18
- /\\(\d+)/,
19
- // 反向引用 \1 \2
20
- /\(\?\(/
21
- // 条件表达式
22
- ];
23
- return !unsupported.some((r) => r.test(regexSource));
12
+ import {
13
+ convertRedirects,
14
+ convertRewrites,
15
+ convertHeaders,
16
+ convertTrailingSlash
17
+ } from "./route-utils.js";
18
+ function hasAppRouter(appPathRoutesManifest) {
19
+ return appPathRoutesManifest && Object.keys(appPathRoutesManifest).length > 0;
20
+ }
21
+ function getBuildId(ctx) {
22
+ try {
23
+ const buildIdPath = path.join(ctx.publishDir, "BUILD_ID");
24
+ if (fs.existsSync(buildIdPath)) {
25
+ return fs.readFileSync(buildIdPath, "utf-8").trim();
26
+ }
27
+ } catch {
28
+ }
29
+ return null;
30
+ }
31
+ function isRE2Compatible(regex) {
32
+ if (/\(\?[=!<]/.test(regex)) {
33
+ return false;
34
+ }
35
+ if (/\\[1-9]/.test(regex)) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ function convertNamedRegexToSrc(namedRegex, basePath = "") {
41
+ const regexWithoutNamedGroups = namedRegex.replace(/\(\?<[a-zA-Z][a-zA-Z0-9_]*>/g, "(");
42
+ if (!isRE2Compatible(regexWithoutNamedGroups)) {
43
+ console.warn(`[opennext] Warning: Regex not RE2 compatible, skipping: ${namedRegex}`);
44
+ return null;
45
+ }
46
+ let src = namedRegex.replace(/\(\?<[a-zA-Z][a-zA-Z0-9_]*>/g, "(");
47
+ if (basePath && !src.startsWith(`^${basePath}`)) {
48
+ src = src.replace(/^\^/, `^${basePath}`);
49
+ }
50
+ return src;
51
+ }
52
+ function getDynamicRoutes(dynamicRoutes, basePath = "") {
53
+ const routes = [];
54
+ for (const route of dynamicRoutes) {
55
+ if (route.namedRegex) {
56
+ const src = convertNamedRegexToSrc(route.namedRegex, basePath);
57
+ if (src) {
58
+ routes.push({ src });
59
+ }
60
+ } else if (route.regex) {
61
+ if (isRE2Compatible(route.regex)) {
62
+ let src = route.regex;
63
+ if (basePath && !src.startsWith(`^${basePath}`)) {
64
+ src = src.replace(/^\^/, `^${basePath}`);
65
+ }
66
+ routes.push({ src });
67
+ }
68
+ }
69
+ }
70
+ return routes;
71
+ }
72
+ function getDataRoutes(dataRoutes, basePath = "") {
73
+ const routes = [];
74
+ for (const route of dataRoutes) {
75
+ if (route.namedDataRouteRegex) {
76
+ const src = convertNamedRegexToSrc(route.namedDataRouteRegex, basePath);
77
+ if (src) {
78
+ routes.push({ src });
79
+ }
80
+ } else if (route.dataRouteRegex) {
81
+ if (isRE2Compatible(route.dataRouteRegex)) {
82
+ let src = route.dataRouteRegex;
83
+ if (basePath && !src.startsWith(`^${basePath}`)) {
84
+ src = src.replace(/^\^/, `^${basePath}`);
85
+ }
86
+ routes.push({ src });
87
+ }
88
+ }
89
+ }
90
+ return routes;
91
+ }
92
+ function getServerRoutes(staticRoutes, prerenderManifest, basePath = "") {
93
+ const routes = [];
94
+ const prerenderRoutes = prerenderManifest?.routes || {};
95
+ for (const route of staticRoutes) {
96
+ if (route.page.startsWith("/_")) {
97
+ continue;
98
+ }
99
+ const prerenderInfo = prerenderRoutes[route.page];
100
+ if (prerenderInfo && prerenderInfo.initialRevalidateSeconds === false) {
101
+ continue;
102
+ }
103
+ if (route.namedRegex) {
104
+ let src = convertNamedRegexToSrc(route.namedRegex, basePath);
105
+ if (src) {
106
+ src = src.replace(/\(\?:\/\)\?\$$/, "$");
107
+ routes.push({ src });
108
+ }
109
+ } else if (route.regex) {
110
+ if (isRE2Compatible(route.regex)) {
111
+ let src = route.regex;
112
+ if (basePath && !src.startsWith(`^${basePath}`)) {
113
+ src = src.replace(/^\^/, `^${basePath}`);
114
+ }
115
+ src = src.replace(/\(\?:\/\)\?\$$/, "$");
116
+ routes.push({ src });
117
+ }
118
+ }
119
+ }
120
+ return routes;
121
+ }
122
+ function getApiRoutes(appPathsManifest, basePath = "") {
123
+ if (!appPathsManifest) {
124
+ return [];
125
+ }
126
+ const routes = [];
127
+ for (const routePath of Object.keys(appPathsManifest)) {
128
+ if (routePath.includes("/api/") && routePath.endsWith("/route")) {
129
+ const apiPath = routePath.replace(/\/route$/, "");
130
+ const escapedPath = apiPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
131
+ const src = `^${basePath}${escapedPath}$`;
132
+ routes.push({ src });
133
+ }
134
+ }
135
+ return routes;
24
136
  }
25
137
  async function getMiddlewareConfig(ctx) {
26
138
  try {
@@ -46,9 +158,14 @@ async function getMiddlewareConfig(ctx) {
46
158
  matcher: normalizedMatchers.map((item) => ({ source: item.source }))
47
159
  };
48
160
  }
161
+ const nextDistDir = ctx.nextDistDir || ".next";
49
162
  const possibleFunctionsConfigPaths = [
50
- path.join(process.cwd(), ".next/server/functions-config-manifest.json"),
51
- path.join(ctx.distDir, "server/functions-config-manifest.json")
163
+ // 1. 使用配置的 distDir
164
+ path.join(ctx.distDir, "server/functions-config-manifest.json"),
165
+ // 2. 相对于当前工作目录(使用配置的 nextDistDir)
166
+ path.join(process.cwd(), nextDistDir, "server/functions-config-manifest.json"),
167
+ // 3. 兼容默认 .next 目录
168
+ path.join(process.cwd(), ".next/server/functions-config-manifest.json")
52
169
  ];
53
170
  let functionsConfigPath = "";
54
171
  for (const p of possibleFunctionsConfigPaths) {
@@ -86,8 +203,8 @@ async function getMiddlewareConfig(ctx) {
86
203
  return null;
87
204
  }
88
205
  }
89
- function updateEdgeFunctionsMetaJson(middlewareConfig) {
90
- const metaJsonPath = path.join(process.cwd(), ".edgeone/edge-functions/meta.json");
206
+ function updateEdgeFunctionsConfigJson(middlewareConfig) {
207
+ const metaJsonPath = path.join(process.cwd(), ".edgeone/edge-functions/config.json");
91
208
  let meta = { routes: [] };
92
209
  if (fs.existsSync(metaJsonPath)) {
93
210
  try {
@@ -105,101 +222,139 @@ function updateEdgeFunctionsMetaJson(middlewareConfig) {
105
222
  }
106
223
  fs.writeFileSync(metaJsonPath, JSON.stringify(meta, null, 2), "utf-8");
107
224
  }
108
- var convertNextRoutePattern = (path2) => {
109
- if (!path2.includes("[")) {
110
- return path2;
111
- }
112
- let convertedPath = path2;
113
- const optionalCatchAllMatch = path2.match(/\[\[\.\.\.([^\]]+)\]\]/);
114
- if (optionalCatchAllMatch) {
115
- const paramName = optionalCatchAllMatch[1];
116
- convertedPath = convertedPath.replace(/\[\[\.\.\.([^\]]+)\]\]/g, `:${paramName}*`);
117
- }
118
- const catchAllMatch = path2.match(/\[\.\.\.([^\]]+)\]/);
119
- if (catchAllMatch) {
120
- const paramName = catchAllMatch[1];
121
- convertedPath = convertedPath.replace(/\[\.\.\.([^\]]+)\]/g, `:${paramName}*`);
122
- }
123
- const dynamicMatch = path2.match(/\[([^\]]+)\]/);
124
- if (dynamicMatch) {
125
- const paramName = dynamicMatch[1];
126
- convertedPath = convertedPath.replace(/\[([^\]]+)\]/g, `:${paramName}`);
127
- }
128
- return convertedPath;
129
- };
130
225
  var createRouteMeta = async (ctx) => {
131
- const routeMap = {};
132
- const manifest = await ctx.getPrerenderManifest();
133
- if (manifest?.routes) {
134
- for (const [route, routeInfo] of Object.entries(manifest.routes)) {
135
- routeMap[route] = {
136
- // 提取关键信息到routeMap
137
- isStatic: routeInfo.initialRevalidateSeconds === false,
138
- initialRevalidateSeconds: routeInfo.initialRevalidateSeconds || void 0,
139
- srcRoute: routeInfo.srcRoute || void 0,
140
- dataRoute: routeInfo.dataRoute || void 0
141
- };
142
- }
226
+ const routes = [];
227
+ const routesManifest = await ctx.getRoutesManifest();
228
+ const appPathRoutesManifest = await ctx.getAppPathRoutesManifest();
229
+ const isAppRouter = hasAppRouter(appPathRoutesManifest);
230
+ const basePath = routesManifest?.basePath || "";
231
+ const trailingSlash = ctx.requiredServerFiles?.config?.trailingSlash ?? false;
232
+ const redirects = routesManifest?.redirects || [];
233
+ const headers = routesManifest?.headers || [];
234
+ const rewrites = routesManifest?.rewrites || { beforeFiles: [], afterFiles: [], fallback: [] };
235
+ let beforeFilesRewrites = [];
236
+ let afterFilesRewrites = [];
237
+ let fallbackRewrites = [];
238
+ if (Array.isArray(rewrites)) {
239
+ afterFilesRewrites = rewrites;
240
+ } else {
241
+ beforeFilesRewrites = rewrites.beforeFiles || [];
242
+ afterFilesRewrites = rewrites.afterFiles || [];
243
+ fallbackRewrites = rewrites.fallback || [];
143
244
  }
144
- const pagesManifest = await ctx.getPagesManifest();
145
- if (pagesManifest) {
146
- for (const [route, filePath] of Object.entries(pagesManifest)) {
147
- if (!routeMap[route]) {
148
- routeMap[route] = {};
149
- }
150
- if (filePath.startsWith("pages") && filePath.endsWith(".html")) {
151
- routeMap[route].isStatic = true;
152
- }
245
+ const dynamicRoutes = routesManifest?.dynamicRoutes || [];
246
+ const dataRoutes = routesManifest?.dataRoutes || [];
247
+ const staticRoutes = routesManifest?.staticRoutes || [];
248
+ const appPathsManifest = await ctx.getAppPathsManifest?.() || null;
249
+ const prerenderManifest = await ctx.getPrerenderManifest?.() || null;
250
+ const buildId = getBuildId(ctx);
251
+ const staticCacheRegex = buildId ? `^${basePath}/_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media|${buildId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})/.+` : `^${basePath}/_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media)/.+`;
252
+ routes.push({
253
+ src: staticCacheRegex,
254
+ headers: {
255
+ "cache-control": "public,max-age=31536000,immutable"
256
+ },
257
+ continue: true
258
+ });
259
+ if (trailingSlash) {
260
+ routes.push(...convertTrailingSlash(true));
261
+ } else {
262
+ routes.push(...convertTrailingSlash(false));
263
+ }
264
+ if (headers.length > 0) {
265
+ try {
266
+ routes.push(...convertHeaders(headers));
267
+ } catch (e) {
268
+ console.warn("[opennext] Warning: Failed to convert some headers:", e);
153
269
  }
154
270
  }
155
- const appPathRoutesManifest = await ctx.getAppPathRoutesManifest();
156
- if (appPathRoutesManifest) {
157
- for (const [route, actualRoute] of Object.entries(appPathRoutesManifest)) {
158
- if (!routeMap[actualRoute]) {
159
- routeMap[actualRoute] = {};
160
- }
271
+ const userRedirects = redirects.filter((r) => !r.internal);
272
+ if (userRedirects.length > 0) {
273
+ try {
274
+ routes.push(...convertRedirects(userRedirects));
275
+ } catch (e) {
276
+ console.warn("[opennext] Warning: Failed to convert some redirects:", e);
161
277
  }
162
278
  }
163
- const routesManifest = await ctx.getRoutesManifest();
164
- if (routesManifest) {
165
- const dataRoutes = routesManifest.dataRoutes;
166
- if (dataRoutes) {
167
- for (const { page, dataRouteRegex } of dataRoutes) {
168
- routeMap[dataRouteRegex] = {
169
- isStatic: routeMap[page]?.isStatic || false
170
- };
171
- }
279
+ if (beforeFilesRewrites.length > 0) {
280
+ try {
281
+ routes.push(...convertRewrites(beforeFilesRewrites));
282
+ } catch (e) {
283
+ console.warn("[opennext] Warning: Failed to convert beforeFiles rewrites:", e);
172
284
  }
173
285
  }
174
- const imagesManifest = await ctx.getImagesManifest();
175
- if (imagesManifest) {
176
- if (imagesManifest.images) {
177
- const imageConfig = imagesManifest.images;
178
- routeMap[imageConfig.path] = {};
286
+ if (isAppRouter) {
287
+ const rscVary = "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch";
288
+ const rscContentType = "text/x-component";
289
+ routes.push({
290
+ src: `^${basePath}/?$`,
291
+ has: [{ type: "header", key: "rsc", value: "1" }],
292
+ dest: `${basePath}/index.rsc`,
293
+ headers: { vary: rscVary, "content-type": rscContentType },
294
+ continue: true
295
+ });
296
+ routes.push({
297
+ src: `^${basePath}/(.+?)(?:/)?$`,
298
+ exclude: "\\.rsc/?$",
299
+ has: [{ type: "header", key: "rsc", value: "1" }],
300
+ dest: `${basePath}/$1.rsc`,
301
+ headers: { vary: rscVary, "content-type": rscContentType },
302
+ continue: true
303
+ });
304
+ }
305
+ routes.push({
306
+ src: `^${basePath}/404/?$`,
307
+ status: 404,
308
+ continue: true,
309
+ missing: [{ type: "header", key: "x-prerender-revalidate" }]
310
+ });
311
+ routes.push({
312
+ src: `^${basePath}/500$`,
313
+ status: 500,
314
+ continue: true
315
+ });
316
+ routes.push({
317
+ src: `^${basePath}/((?:[^/]+/)*[^/.]+)/$`,
318
+ dest: `${basePath}/$1`,
319
+ continue: true
320
+ });
321
+ routes.push({ handle: "filesystem" });
322
+ if (afterFilesRewrites.length > 0) {
323
+ try {
324
+ routes.push(...convertRewrites(afterFilesRewrites));
325
+ } catch (e) {
326
+ console.warn("[opennext] Warning: Failed to convert afterFiles rewrites:", e);
179
327
  }
180
328
  }
181
- const convertedRouteMap = {};
182
- const pathsToDelete = [];
183
- for (const [routePath, routeConfig] of Object.entries(routeMap)) {
184
- const convertedPath = convertNextRoutePattern(routePath);
185
- if (convertedPath !== routePath) {
186
- pathsToDelete.push(routePath);
187
- convertedRouteMap[convertedPath] = routeConfig;
329
+ if (fallbackRewrites.length > 0) {
330
+ try {
331
+ routes.push(...convertRewrites(fallbackRewrites));
332
+ } catch (e) {
333
+ console.warn("[opennext] Warning: Failed to convert fallback rewrites:", e);
188
334
  }
189
335
  }
190
- for (const pathToDelete of pathsToDelete) {
191
- delete routeMap[pathToDelete];
336
+ if (staticRoutes.length > 0) {
337
+ routes.push(...getServerRoutes(staticRoutes, prerenderManifest, basePath));
338
+ }
339
+ if (dynamicRoutes.length > 0) {
340
+ routes.push(...getDynamicRoutes(dynamicRoutes, basePath));
192
341
  }
193
- Object.assign(routeMap, convertedRouteMap);
194
- const routesArray = Object.entries(routeMap).map(([path2, config]) => ({
195
- path: path2,
196
- ...config
197
- }));
342
+ if (dataRoutes.length > 0) {
343
+ routes.push(...getDataRoutes(dataRoutes, basePath));
344
+ }
345
+ const apiRoutes = getApiRoutes(appPathsManifest, basePath);
346
+ if (apiRoutes.length > 0) {
347
+ routes.push(...apiRoutes);
348
+ }
349
+ routes.push({
350
+ src: `^${basePath}/.*$`
351
+ });
198
352
  const serverHandlerDir = ctx.serverHandlerRootDir;
199
353
  if (!fs.existsSync(serverHandlerDir)) {
200
354
  fs.mkdirSync(serverHandlerDir, { recursive: true });
201
355
  }
202
- const metaFilePath = path.join(serverHandlerDir, "meta.json");
356
+ const nextVersion = ctx.nextVersion || null;
357
+ const imagesManifest = await ctx.getImagesManifest();
203
358
  const updatedRedirects = [];
204
359
  if (imagesManifest?.images?.path) {
205
360
  const imagePath = imagesManifest.images.path;
@@ -210,21 +365,24 @@ var createRouteMeta = async (ctx) => {
210
365
  };
211
366
  updatedRedirects.push(nextImageRedirect);
212
367
  }
213
- const metaData = {
368
+ const config = {
369
+ version: 3,
370
+ routes,
214
371
  conf: {
215
372
  redirects: updatedRedirects
216
373
  },
217
- nextRoutes: routesArray
374
+ ...nextVersion ? { framework: { version: nextVersion } } : {}
218
375
  };
376
+ const configFilePath = path.join(serverHandlerDir, "config.json");
219
377
  fs.writeFileSync(
220
- metaFilePath,
221
- JSON.stringify(metaData, null, 2),
378
+ configFilePath,
379
+ JSON.stringify(config, null, 2),
222
380
  "utf-8"
223
381
  );
382
+ console.log(`[opennext] Generated ${configFilePath} with ${routes.length} routes`);
224
383
  const middlewareConfig = await getMiddlewareConfig(ctx);
225
- updateEdgeFunctionsMetaJson(middlewareConfig);
384
+ updateEdgeFunctionsConfigJson(middlewareConfig);
226
385
  };
227
386
  export {
228
- convertNextRoutePattern,
229
387
  createRouteMeta
230
388
  };
@@ -28,7 +28,7 @@ module.exports = __toCommonJS(tags_handler_exports);
28
28
 
29
29
  // package.json
30
30
  var name = "@edgeone/opennextjs-pages";
31
- var version = "0.1.5";
31
+ var version = "0.1.6-beta.2";
32
32
 
33
33
  // src/run/handlers/tags-handler.cts
34
34
  var import_request_context = require("./request-context.cjs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgeone/opennextjs-pages",
3
- "version": "0.1.5",
3
+ "version": "0.1.6-beta.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",