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

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,85 @@ 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;
24
91
  }
25
92
  async function getMiddlewareConfig(ctx) {
26
93
  try {
@@ -46,9 +113,14 @@ async function getMiddlewareConfig(ctx) {
46
113
  matcher: normalizedMatchers.map((item) => ({ source: item.source }))
47
114
  };
48
115
  }
116
+ const nextDistDir = ctx.nextDistDir || ".next";
49
117
  const possibleFunctionsConfigPaths = [
50
- path.join(process.cwd(), ".next/server/functions-config-manifest.json"),
51
- path.join(ctx.distDir, "server/functions-config-manifest.json")
118
+ // 1. 使用配置的 distDir
119
+ path.join(ctx.distDir, "server/functions-config-manifest.json"),
120
+ // 2. 相对于当前工作目录(使用配置的 nextDistDir)
121
+ path.join(process.cwd(), nextDistDir, "server/functions-config-manifest.json"),
122
+ // 3. 兼容默认 .next 目录
123
+ path.join(process.cwd(), ".next/server/functions-config-manifest.json")
52
124
  ];
53
125
  let functionsConfigPath = "";
54
126
  for (const p of possibleFunctionsConfigPaths) {
@@ -86,8 +158,8 @@ async function getMiddlewareConfig(ctx) {
86
158
  return null;
87
159
  }
88
160
  }
89
- function updateEdgeFunctionsMetaJson(middlewareConfig) {
90
- const metaJsonPath = path.join(process.cwd(), ".edgeone/edge-functions/meta.json");
161
+ function updateEdgeFunctionsConfigJson(middlewareConfig) {
162
+ const metaJsonPath = path.join(process.cwd(), ".edgeone/edge-functions/config.json");
91
163
  let meta = { routes: [] };
92
164
  if (fs.existsSync(metaJsonPath)) {
93
165
  try {
@@ -105,101 +177,129 @@ function updateEdgeFunctionsMetaJson(middlewareConfig) {
105
177
  }
106
178
  fs.writeFileSync(metaJsonPath, JSON.stringify(meta, null, 2), "utf-8");
107
179
  }
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
180
  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
- }
181
+ const routes = [];
182
+ const routesManifest = await ctx.getRoutesManifest();
183
+ const appPathRoutesManifest = await ctx.getAppPathRoutesManifest();
184
+ const isAppRouter = hasAppRouter(appPathRoutesManifest);
185
+ const basePath = routesManifest?.basePath || "";
186
+ const trailingSlash = ctx.requiredServerFiles?.config?.trailingSlash ?? false;
187
+ const redirects = routesManifest?.redirects || [];
188
+ const headers = routesManifest?.headers || [];
189
+ const rewrites = routesManifest?.rewrites || { beforeFiles: [], afterFiles: [], fallback: [] };
190
+ let beforeFilesRewrites = [];
191
+ let afterFilesRewrites = [];
192
+ let fallbackRewrites = [];
193
+ if (Array.isArray(rewrites)) {
194
+ afterFilesRewrites = rewrites;
195
+ } else {
196
+ beforeFilesRewrites = rewrites.beforeFiles || [];
197
+ afterFilesRewrites = rewrites.afterFiles || [];
198
+ fallbackRewrites = rewrites.fallback || [];
143
199
  }
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
- }
200
+ const dynamicRoutes = routesManifest?.dynamicRoutes || [];
201
+ const dataRoutes = routesManifest?.dataRoutes || [];
202
+ const buildId = getBuildId(ctx);
203
+ 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)/.+`;
204
+ routes.push({
205
+ src: staticCacheRegex,
206
+ headers: {
207
+ "cache-control": "public,max-age=31536000,immutable"
208
+ },
209
+ continue: true
210
+ });
211
+ if (trailingSlash) {
212
+ routes.push(...convertTrailingSlash(true));
213
+ } else {
214
+ routes.push(...convertTrailingSlash(false));
215
+ }
216
+ if (headers.length > 0) {
217
+ try {
218
+ routes.push(...convertHeaders(headers));
219
+ } catch (e) {
220
+ console.warn("[opennext] Warning: Failed to convert some headers:", e);
153
221
  }
154
222
  }
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
- }
223
+ const userRedirects = redirects.filter((r) => !r.internal);
224
+ if (userRedirects.length > 0) {
225
+ try {
226
+ routes.push(...convertRedirects(userRedirects));
227
+ } catch (e) {
228
+ console.warn("[opennext] Warning: Failed to convert some redirects:", e);
161
229
  }
162
230
  }
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
- }
231
+ if (beforeFilesRewrites.length > 0) {
232
+ try {
233
+ routes.push(...convertRewrites(beforeFilesRewrites));
234
+ } catch (e) {
235
+ console.warn("[opennext] Warning: Failed to convert beforeFiles rewrites:", e);
172
236
  }
173
237
  }
174
- const imagesManifest = await ctx.getImagesManifest();
175
- if (imagesManifest) {
176
- if (imagesManifest.images) {
177
- const imageConfig = imagesManifest.images;
178
- routeMap[imageConfig.path] = {};
238
+ if (isAppRouter) {
239
+ const rscVary = "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch";
240
+ const rscContentType = "text/x-component";
241
+ routes.push({
242
+ src: `^${basePath}/?$`,
243
+ has: [{ type: "header", key: "rsc", value: "1" }],
244
+ dest: `${basePath}/index.rsc`,
245
+ headers: { vary: rscVary, "content-type": rscContentType },
246
+ continue: true
247
+ });
248
+ routes.push({
249
+ src: `^${basePath}/(.+?)(?:/)?$`,
250
+ exclude: "\\.rsc/?$",
251
+ has: [{ type: "header", key: "rsc", value: "1" }],
252
+ dest: `${basePath}/$1.rsc`,
253
+ headers: { vary: rscVary, "content-type": rscContentType },
254
+ continue: true
255
+ });
256
+ }
257
+ routes.push({
258
+ src: `^${basePath}/404/?$`,
259
+ status: 404,
260
+ continue: true,
261
+ missing: [{ type: "header", key: "x-prerender-revalidate" }]
262
+ });
263
+ routes.push({
264
+ src: `^${basePath}/500$`,
265
+ status: 500,
266
+ continue: true
267
+ });
268
+ routes.push({
269
+ src: `^${basePath}/((?:[^/]+/)*[^/.]+)/$`,
270
+ dest: `${basePath}/$1`,
271
+ continue: true
272
+ });
273
+ routes.push({ handle: "filesystem" });
274
+ if (afterFilesRewrites.length > 0) {
275
+ try {
276
+ routes.push(...convertRewrites(afterFilesRewrites));
277
+ } catch (e) {
278
+ console.warn("[opennext] Warning: Failed to convert afterFiles rewrites:", e);
179
279
  }
180
280
  }
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;
281
+ if (fallbackRewrites.length > 0) {
282
+ try {
283
+ routes.push(...convertRewrites(fallbackRewrites));
284
+ } catch (e) {
285
+ console.warn("[opennext] Warning: Failed to convert fallback rewrites:", e);
188
286
  }
189
287
  }
190
- for (const pathToDelete of pathsToDelete) {
191
- delete routeMap[pathToDelete];
288
+ if (dynamicRoutes.length > 0) {
289
+ routes.push(...getDynamicRoutes(dynamicRoutes, basePath));
192
290
  }
193
- Object.assign(routeMap, convertedRouteMap);
194
- const routesArray = Object.entries(routeMap).map(([path2, config]) => ({
195
- path: path2,
196
- ...config
197
- }));
291
+ if (dataRoutes.length > 0) {
292
+ routes.push(...getDataRoutes(dataRoutes, basePath));
293
+ }
294
+ routes.push({
295
+ src: `^${basePath}/.*$`
296
+ });
198
297
  const serverHandlerDir = ctx.serverHandlerRootDir;
199
298
  if (!fs.existsSync(serverHandlerDir)) {
200
299
  fs.mkdirSync(serverHandlerDir, { recursive: true });
201
300
  }
202
- const metaFilePath = path.join(serverHandlerDir, "meta.json");
301
+ const nextVersion = ctx.nextVersion || null;
302
+ const imagesManifest = await ctx.getImagesManifest();
203
303
  const updatedRedirects = [];
204
304
  if (imagesManifest?.images?.path) {
205
305
  const imagePath = imagesManifest.images.path;
@@ -210,21 +310,24 @@ var createRouteMeta = async (ctx) => {
210
310
  };
211
311
  updatedRedirects.push(nextImageRedirect);
212
312
  }
213
- const metaData = {
313
+ const config = {
314
+ version: 3,
315
+ routes,
214
316
  conf: {
215
317
  redirects: updatedRedirects
216
318
  },
217
- nextRoutes: routesArray
319
+ ...nextVersion ? { framework: { version: nextVersion } } : {}
218
320
  };
321
+ const configFilePath = path.join(serverHandlerDir, "config.json");
219
322
  fs.writeFileSync(
220
- metaFilePath,
221
- JSON.stringify(metaData, null, 2),
323
+ configFilePath,
324
+ JSON.stringify(config, null, 2),
222
325
  "utf-8"
223
326
  );
327
+ console.log(`[opennext] Generated ${configFilePath} with ${routes.length} routes`);
224
328
  const middlewareConfig = await getMiddlewareConfig(ctx);
225
- updateEdgeFunctionsMetaJson(middlewareConfig);
329
+ updateEdgeFunctionsConfigJson(middlewareConfig);
226
330
  };
227
331
  export {
228
- convertNextRoutePattern,
229
332
  createRouteMeta
230
333
  };
@@ -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.1";
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.1",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",