@edgeone/nuxt-pages 1.1.0-beta.4 → 1.1.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.
- package/dist/build/content/module-inject.js +2 -2
- package/dist/build/functions/server.js +1 -1
- package/dist/build/plugin-context.js +3 -3
- package/dist/build/routes.js +4 -3
- package/dist/build/tag-manifest.js +15 -0
- package/dist/build/templates/isr-runtime.js +215 -0
- package/dist/build/templates/nuxt-handler-monorepo.tmpl.js +14 -1
- package/dist/esm-chunks/{chunk-PJWA566S.js → chunk-4B7OVONM.js} +6 -0
- package/dist/esm-chunks/{chunk-3HA3DZ3G.js → chunk-D5KM3RWE.js} +54 -1
- package/dist/esm-chunks/{chunk-AZLY3JO2.js → chunk-ODJXW6KT.js} +1 -1
- package/dist/esm-chunks/{chunk-ZR3DYEBK.js → chunk-PHIWUHZJ.js} +1 -1
- package/dist/esm-chunks/chunk-QUFJCWGJ.js +38 -0
- package/dist/esm-chunks/{chunk-VDBASNGL.js → chunk-VLRIK4OQ.js} +1 -1
- package/dist/esm-chunks/{chunk-VCJ5U4PV.js → chunk-VPL326TW.js} +447 -448
- package/dist/index.js +10 -9
- package/dist/utils.js +2 -2
- package/package.json +9 -8
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
cleanupEdgeoneModule,
|
|
9
9
|
injectEdgeoneModule,
|
|
10
10
|
removeTempModule
|
|
11
|
-
} from "../../esm-chunks/chunk-
|
|
12
|
-
import "../../esm-chunks/chunk-
|
|
11
|
+
} from "../../esm-chunks/chunk-PHIWUHZJ.js";
|
|
12
|
+
import "../../esm-chunks/chunk-VPL326TW.js";
|
|
13
13
|
import "../../esm-chunks/chunk-6BT4RYQJ.js";
|
|
14
14
|
export {
|
|
15
15
|
cleanupEdgeoneModule,
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
import {
|
|
8
8
|
PluginContext,
|
|
9
9
|
SERVER_HANDLER_NAME
|
|
10
|
-
} from "../esm-chunks/chunk-
|
|
11
|
-
import "../esm-chunks/chunk-
|
|
12
|
-
import "../esm-chunks/chunk-
|
|
10
|
+
} from "../esm-chunks/chunk-VLRIK4OQ.js";
|
|
11
|
+
import "../esm-chunks/chunk-ODJXW6KT.js";
|
|
12
|
+
import "../esm-chunks/chunk-VPL326TW.js";
|
|
13
13
|
import "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
14
14
|
export {
|
|
15
15
|
PluginContext,
|
package/dist/build/routes.js
CHANGED
|
@@ -8,9 +8,10 @@ import {
|
|
|
8
8
|
createNuxtApiRoutesMeta,
|
|
9
9
|
createNuxtPagesRouteMeta,
|
|
10
10
|
createNuxtRoutesMeta
|
|
11
|
-
} from "../esm-chunks/chunk-
|
|
12
|
-
import "../esm-chunks/chunk-
|
|
13
|
-
import "../esm-chunks/chunk-
|
|
11
|
+
} from "../esm-chunks/chunk-D5KM3RWE.js";
|
|
12
|
+
import "../esm-chunks/chunk-ODJXW6KT.js";
|
|
13
|
+
import "../esm-chunks/chunk-QUFJCWGJ.js";
|
|
14
|
+
import "../esm-chunks/chunk-VPL326TW.js";
|
|
14
15
|
import "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
15
16
|
export {
|
|
16
17
|
createNuxtApiRoutesMeta,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
var require = await (async () => {
|
|
3
|
+
var { createRequire } = await import("node:module");
|
|
4
|
+
return createRequire(import.meta.url);
|
|
5
|
+
})();
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildTagManifest,
|
|
9
|
+
extractCacheTags
|
|
10
|
+
} from "../esm-chunks/chunk-QUFJCWGJ.js";
|
|
11
|
+
import "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
12
|
+
export {
|
|
13
|
+
buildTagManifest,
|
|
14
|
+
extractCacheTags
|
|
15
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ISR (Incremental Static Regeneration) runtime for the EdgeOne Nuxt adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the handler template so the handler stays thin. This module is
|
|
5
|
+
* copied next to handler.js in the server-handler directory at build time and
|
|
6
|
+
* imported from there; it reads the build-time manifests (isr-manifest.json /
|
|
7
|
+
* tag-manifest.json) that sit in the same directory.
|
|
8
|
+
*
|
|
9
|
+
* ISR on EdgeOne is a "CDN cache + purge" model:
|
|
10
|
+
* - applyIsr() tags cacheable responses with `eo-cdn-cache-control` /
|
|
11
|
+
* `cache-tag` so the edge caches + revalidates them.
|
|
12
|
+
* - handleRevalidate() purges the edge cache on demand (POST endpoint).
|
|
13
|
+
*/
|
|
14
|
+
import { resolve, dirname } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import { readFileSync, existsSync } from 'fs';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
const ISR_SWR_WINDOW = 31536000; // 1 year stale-while-revalidate window
|
|
21
|
+
export const REVALIDATE_ENDPOINT = '/__edgeone_revalidate';
|
|
22
|
+
|
|
23
|
+
let _isrRoutes = null; // [{ re: RegExp, revalidate, tags }]
|
|
24
|
+
function getIsrRoutes() {
|
|
25
|
+
if (_isrRoutes) return _isrRoutes;
|
|
26
|
+
_isrRoutes = [];
|
|
27
|
+
try {
|
|
28
|
+
const p = resolve(__dirname, 'isr-manifest.json');
|
|
29
|
+
if (existsSync(p)) {
|
|
30
|
+
const manifest = JSON.parse(readFileSync(p, 'utf-8'));
|
|
31
|
+
for (const r of manifest?.routes || []) {
|
|
32
|
+
if (!r?.src) continue;
|
|
33
|
+
try {
|
|
34
|
+
_isrRoutes.push({ re: new RegExp(r.src), revalidate: r.revalidate, tags: r.tags || [] });
|
|
35
|
+
} catch { /* skip invalid pattern */ }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.warn('[nuxt-pages] Failed to load isr-manifest.json:', err?.message || err);
|
|
40
|
+
}
|
|
41
|
+
return _isrRoutes;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let _tagManifest = null; // { tag: [paths] }
|
|
45
|
+
function getTagManifest() {
|
|
46
|
+
if (_tagManifest) return _tagManifest;
|
|
47
|
+
_tagManifest = { version: 1, tags: {} };
|
|
48
|
+
try {
|
|
49
|
+
const p = resolve(__dirname, 'tag-manifest.json');
|
|
50
|
+
if (existsSync(p)) {
|
|
51
|
+
_tagManifest = JSON.parse(readFileSync(p, 'utf-8'));
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn('[nuxt-pages] Failed to load tag-manifest.json:', err?.message || err);
|
|
55
|
+
}
|
|
56
|
+
return _tagManifest;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Find the first ISR route whose pattern matches the given pathname. */
|
|
60
|
+
function matchIsrRoute(pathname) {
|
|
61
|
+
for (const route of getIsrRoutes()) {
|
|
62
|
+
if (route.re.test(pathname)) return route;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Set EdgeOne CDN cache-control for an ISR response (time-based revalidation). */
|
|
68
|
+
function setIsrCacheHeaders(headers, route) {
|
|
69
|
+
const revalidate = typeof route.revalidate === 'number' ? route.revalidate : ISR_SWR_WINDOW;
|
|
70
|
+
headers.set(
|
|
71
|
+
'eo-cdn-cache-control',
|
|
72
|
+
`s-maxage=${revalidate}, stale-while-revalidate=${ISR_SWR_WINDOW}, durable`,
|
|
73
|
+
);
|
|
74
|
+
// Keep the browser from caching a stale copy; the edge holds the shared cache.
|
|
75
|
+
if (!headers.has('cache-control')) {
|
|
76
|
+
headers.set('cache-control', 'public, max-age=0, must-revalidate');
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(route.tags) && route.tags.length > 0) {
|
|
79
|
+
headers.set('cache-tag', route.tags.join(','));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Real user-facing host. EdgeOne rewrites Host to the origin, real host is in `eo-pages-host`. */
|
|
84
|
+
function getRealHost(headers) {
|
|
85
|
+
const get = (k) => {
|
|
86
|
+
if (headers && typeof headers.get === 'function') return headers.get(k) || '';
|
|
87
|
+
return (headers && (headers[k] || headers[k.toLowerCase()])) || '';
|
|
88
|
+
};
|
|
89
|
+
return get('eo-pages-host') || get('host') || process.env.SITE_HOST || '';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Expand cache tags to concrete paths using the tag manifest (unknown tag -> treated as a path). */
|
|
93
|
+
function expandTagsToPaths(tags) {
|
|
94
|
+
const manifest = getTagManifest();
|
|
95
|
+
const paths = new Set();
|
|
96
|
+
for (const tag of tags) {
|
|
97
|
+
const mapped = manifest?.tags?.[tag];
|
|
98
|
+
if (Array.isArray(mapped) && mapped.length) {
|
|
99
|
+
for (const p of mapped) paths.add(p);
|
|
100
|
+
} else {
|
|
101
|
+
paths.add(tag); // revalidatePath scenario
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return Array.from(paths);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Purge the EdgeOne CDN cache for a set of paths on the given host. */
|
|
108
|
+
async function purgeEdgeCache(paths, host) {
|
|
109
|
+
if (!paths || paths.length === 0) return { purged: 0 };
|
|
110
|
+
if (!host) {
|
|
111
|
+
console.error('[nuxt-pages] Cannot purge: no host (eo-pages-host header or SITE_HOST env)');
|
|
112
|
+
return { purged: 0, error: 'no-host' };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Only concrete paths can be purged as URLs. Route patterns (containing
|
|
116
|
+
// dynamic segments like `**`, `:id`, `[id]`) can't be expanded to a single
|
|
117
|
+
// URL here — skip them and warn.
|
|
118
|
+
const isConcrete = (p) => typeof p === 'string' && !/[*:\[\]]/.test(p);
|
|
119
|
+
const skipped = paths.filter((p) => !isConcrete(p));
|
|
120
|
+
if (skipped.length) {
|
|
121
|
+
console.warn(
|
|
122
|
+
'[nuxt-pages] Skipped non-concrete purge target(s) — pass explicit paths to revalidate dynamic routes:',
|
|
123
|
+
skipped,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const fullUrls = paths
|
|
128
|
+
.filter(isConcrete)
|
|
129
|
+
.map((p) => `https://${host}${p.startsWith('/') ? p : `/${p}`}`);
|
|
130
|
+
|
|
131
|
+
if (fullUrls.length === 0) return { purged: 0, skipped };
|
|
132
|
+
|
|
133
|
+
const endpoint = process.env.IS_MAINLAND === 'true'
|
|
134
|
+
? 'https://pages-api.cloud.tencent.com/eo/pages/hook/eo_purge'
|
|
135
|
+
: 'https://pages-api.edgeone.ai/eo/pages/hook/eo_purge?site=intl';
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const res = await fetch(endpoint, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify({
|
|
142
|
+
RequestId: `purge-${Date.now()}`,
|
|
143
|
+
Token: process.env.PURGE_TOKEN,
|
|
144
|
+
paths: fullUrls,
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
const text = await res.text();
|
|
148
|
+
let data;
|
|
149
|
+
try { data = JSON.parse(text); } catch {
|
|
150
|
+
console.error(`[nuxt-pages] Purge API non-JSON (${res.status}): ${text.slice(0, 200)}`);
|
|
151
|
+
return { purged: 0, error: 'bad-response' };
|
|
152
|
+
}
|
|
153
|
+
if (data?.error) {
|
|
154
|
+
console.error('[nuxt-pages] Purge API error:', JSON.stringify(data.error));
|
|
155
|
+
return { purged: 0, error: data.error };
|
|
156
|
+
}
|
|
157
|
+
return { purged: fullUrls.length, urls: fullUrls };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error('[nuxt-pages] Purge request failed:', error?.message || error);
|
|
160
|
+
return { purged: 0, error: String(error?.message || error) };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Tag a cacheable response with the ISR CDN cache directives, if its path
|
|
166
|
+
* matches an ISR route. Mutates `headers` in place. Skips personalized
|
|
167
|
+
* (Set-Cookie) or explicitly non-shared (no-store/private) responses.
|
|
168
|
+
*/
|
|
169
|
+
export function applyIsr(headers, { method, status, pathname }) {
|
|
170
|
+
if (method !== 'GET' && method !== 'HEAD') return;
|
|
171
|
+
if (status !== 200) return;
|
|
172
|
+
if (headers.has('set-cookie')) return;
|
|
173
|
+
if (/no-store|private/i.test(headers.get('cache-control') || '')) return;
|
|
174
|
+
|
|
175
|
+
// Nuxt client navigation fetches `<route>/_payload.json`; match it to the
|
|
176
|
+
// page route so the payload is cached with the same window.
|
|
177
|
+
const matchPath = pathname.replace(/\/_payload\.json$/, '') || '/';
|
|
178
|
+
const route = matchIsrRoute(matchPath);
|
|
179
|
+
if (route) setIsrCacheHeaders(headers, route);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* On-demand revalidation endpoint handler. POST /__edgeone_revalidate
|
|
184
|
+
* Body: { token, tags?: string[], paths?: string[] }
|
|
185
|
+
* Purges the CDN cache for the resolved paths so the next request regenerates.
|
|
186
|
+
*/
|
|
187
|
+
export async function handleRevalidate(req, headers, body) {
|
|
188
|
+
const token = process.env.PURGE_TOKEN;
|
|
189
|
+
// Secure by default: without a configured token the endpoint is disabled.
|
|
190
|
+
if (!token) {
|
|
191
|
+
return new Response(JSON.stringify({ revalidated: false, error: 'purge-not-configured' }), {
|
|
192
|
+
status: 403, headers: { 'Content-Type': 'application/json' },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
let payload = {};
|
|
196
|
+
try { payload = body ? JSON.parse(body) : {}; } catch { /* ignore */ }
|
|
197
|
+
|
|
198
|
+
const provided = payload.token
|
|
199
|
+
|| (headers && typeof headers.get === 'function' ? headers.get('x-revalidate-token') : headers?.['x-revalidate-token']);
|
|
200
|
+
if (provided !== token) {
|
|
201
|
+
return new Response(JSON.stringify({ revalidated: false, error: 'invalid-token' }), {
|
|
202
|
+
status: 401, headers: { 'Content-Type': 'application/json' },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const tags = Array.isArray(payload.tags) ? payload.tags : [];
|
|
207
|
+
const explicitPaths = Array.isArray(payload.paths) ? payload.paths : [];
|
|
208
|
+
const paths = Array.from(new Set([...expandTagsToPaths(tags), ...explicitPaths]));
|
|
209
|
+
|
|
210
|
+
const result = await purgeEdgeCache(paths, getRealHost(headers));
|
|
211
|
+
return new Response(JSON.stringify({ revalidated: !result.error, ...result }), {
|
|
212
|
+
status: result.error ? 500 : 200,
|
|
213
|
+
headers: { 'Content-Type': 'application/json' },
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -2,6 +2,7 @@ import { resolve, dirname } from 'path';
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { readFileSync, existsSync, statSync } from 'fs';
|
|
4
4
|
import { extname } from 'path';
|
|
5
|
+
import { REVALIDATE_ENDPOINT, applyIsr, handleRevalidate } from './isr-runtime.js';
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = dirname(__filename);
|
|
@@ -172,6 +173,13 @@ export default async function handler(req, res, context) {
|
|
|
172
173
|
const headers = req.headers || new Headers();
|
|
173
174
|
const body = await getBody(req);
|
|
174
175
|
|
|
176
|
+
const pathname = url.split('?')[0];
|
|
177
|
+
|
|
178
|
+
// On-demand ISR revalidation endpoint (purges CDN cache).
|
|
179
|
+
if (pathname === REVALIDATE_ENDPOINT && method === 'POST') {
|
|
180
|
+
return await handleRevalidate(req, headers, body);
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
// First try to handle static assets
|
|
176
184
|
if (method === 'GET') {
|
|
177
185
|
const staticResponse = handleStaticFile(url);
|
|
@@ -221,8 +229,13 @@ export default async function handler(req, res, context) {
|
|
|
221
229
|
}
|
|
222
230
|
|
|
223
231
|
// console.log('responseHeaders.getSetCookie() --->', responseHeaders.getSetCookie());
|
|
232
|
+
const status = response.status || response.statusCode;
|
|
233
|
+
|
|
234
|
+
// ISR: tag cacheable responses with EdgeOne CDN cache directives.
|
|
235
|
+
applyIsr(responseHeaders, { method, status, pathname });
|
|
236
|
+
|
|
224
237
|
return new Response(response.body, {
|
|
225
|
-
status
|
|
238
|
+
status,
|
|
226
239
|
headers: responseHeaders
|
|
227
240
|
});
|
|
228
241
|
} catch (nitroError) {
|
|
@@ -92,6 +92,12 @@ var getHandlerFile = async (ctx) => {
|
|
|
92
92
|
var writeHandlerFile = async (ctx) => {
|
|
93
93
|
const handler = await getHandlerFile(ctx);
|
|
94
94
|
await writeFile(join(ctx.serverHandlerRootDir, `handler.js`), handler);
|
|
95
|
+
const templatesDir = join(ctx.pluginDir, "dist/build/templates");
|
|
96
|
+
await cp(
|
|
97
|
+
join(templatesDir, "isr-runtime.js"),
|
|
98
|
+
join(ctx.serverHandlerRootDir, "isr-runtime.js"),
|
|
99
|
+
{ force: true }
|
|
100
|
+
);
|
|
95
101
|
};
|
|
96
102
|
var createServerHandler = async (ctx) => {
|
|
97
103
|
await mkdir(join(ctx.serverHandlerRuntimeModulesDir), { recursive: true });
|
|
@@ -7,13 +7,19 @@
|
|
|
7
7
|
import {
|
|
8
8
|
getHandlersArrayWithAST,
|
|
9
9
|
getRouteRulesWithAST
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ODJXW6KT.js";
|
|
11
|
+
import {
|
|
12
|
+
buildTagManifest,
|
|
13
|
+
extractCacheTags
|
|
14
|
+
} from "./chunk-QUFJCWGJ.js";
|
|
11
15
|
|
|
12
16
|
// src/build/routes.ts
|
|
13
17
|
import * as fs from "fs";
|
|
14
18
|
import * as path from "path";
|
|
15
19
|
var CONFIG_VERSION = 3;
|
|
16
20
|
var CONFIG_FILE_NAME = "config.json";
|
|
21
|
+
var ISR_MANIFEST_FILE_NAME = "isr-manifest.json";
|
|
22
|
+
var TAG_MANIFEST_FILE_NAME = "tag-manifest.json";
|
|
17
23
|
function escapeRegExp(str) {
|
|
18
24
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19
25
|
}
|
|
@@ -37,6 +43,14 @@ function writeConfig(config) {
|
|
|
37
43
|
const configPath = getConfigPath();
|
|
38
44
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
39
45
|
}
|
|
46
|
+
function writeIsrManifest(manifest) {
|
|
47
|
+
const p = path.join(ensureServerHandlerDir(), ISR_MANIFEST_FILE_NAME);
|
|
48
|
+
fs.writeFileSync(p, JSON.stringify(manifest, null, 2), "utf-8");
|
|
49
|
+
}
|
|
50
|
+
function writeTagManifest(manifest) {
|
|
51
|
+
const p = path.join(ensureServerHandlerDir(), TAG_MANIFEST_FILE_NAME);
|
|
52
|
+
fs.writeFileSync(p, JSON.stringify(manifest, null, 2), "utf-8");
|
|
53
|
+
}
|
|
40
54
|
function normalizeHeaders(headers) {
|
|
41
55
|
if (!headers || typeof headers !== "object") return null;
|
|
42
56
|
const out = {};
|
|
@@ -240,12 +254,51 @@ async function buildExplicitSsrAndSwrRoutes(ctx) {
|
|
|
240
254
|
tmp.sort((a, b) => b.score - a.score);
|
|
241
255
|
return tmp.map((x) => x.rule);
|
|
242
256
|
}
|
|
257
|
+
async function buildIsrEntries(ctx) {
|
|
258
|
+
const routesManifest = await ctx.getRoutesManifest();
|
|
259
|
+
const manifestRoutes = Array.isArray(routesManifest?.routes) ? routesManifest.routes : [];
|
|
260
|
+
const routeRules = await getRouteRulesFromBuildOutput(ctx) || {};
|
|
261
|
+
const seen = /* @__PURE__ */ new Set();
|
|
262
|
+
const scored = [];
|
|
263
|
+
for (const r of manifestRoutes) {
|
|
264
|
+
const p = typeof r?.path === "string" ? r.path : "";
|
|
265
|
+
if (!p) continue;
|
|
266
|
+
const swr = r?.swr;
|
|
267
|
+
const isIsr = swr === true || typeof swr === "number" && swr > 0;
|
|
268
|
+
if (!isIsr || r?.prerender === true) continue;
|
|
269
|
+
const normalized = normalizeRoutePath(p);
|
|
270
|
+
if (normalized.startsWith("/api")) continue;
|
|
271
|
+
const rule = nuxtRoutePathToRouteRule(normalized);
|
|
272
|
+
const src = rule?.src;
|
|
273
|
+
if (!src || seen.has(src)) continue;
|
|
274
|
+
seen.add(src);
|
|
275
|
+
const tags = extractCacheTags(routeRules?.[p]?.headers ?? routeRules?.[normalized]?.headers);
|
|
276
|
+
const score = (isCatchAllLikeRoutePath(normalized) ? -1e4 : 0) + normalized.split("/").length;
|
|
277
|
+
scored.push({
|
|
278
|
+
score,
|
|
279
|
+
entry: {
|
|
280
|
+
src,
|
|
281
|
+
path: normalized,
|
|
282
|
+
revalidate: typeof swr === "number" ? swr : false,
|
|
283
|
+
tags
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
scored.sort((a, b) => b.score - a.score);
|
|
288
|
+
return scored.map((x) => x.entry);
|
|
289
|
+
}
|
|
243
290
|
var createNuxtRoutesMeta = async (ctx) => {
|
|
244
291
|
const routes = await buildStage1Routes(ctx);
|
|
245
292
|
routes.push(...await buildExplicitApiRoutes(ctx));
|
|
246
293
|
routes.push(...await buildExplicitSsrAndSwrRoutes(ctx));
|
|
247
294
|
routes.push({ src: "^/(.*)$" });
|
|
248
295
|
writeConfig({ version: CONFIG_VERSION, routes });
|
|
296
|
+
const isrEntries = await buildIsrEntries(ctx);
|
|
297
|
+
writeIsrManifest({ version: 1, routes: isrEntries });
|
|
298
|
+
writeTagManifest(buildTagManifest(isrEntries));
|
|
299
|
+
console.log(
|
|
300
|
+
`[nuxt-pages] Generated ISR manifest with ${isrEntries.length} route(s) and ${Object.keys(buildTagManifest(isrEntries).tags).length} cache tag(s)`
|
|
301
|
+
);
|
|
249
302
|
};
|
|
250
303
|
var createNuxtPagesRouteMeta = async (ctx) => {
|
|
251
304
|
await createNuxtRoutesMeta(ctx);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
var require = await (async () => {
|
|
3
|
+
var { createRequire } = await import("node:module");
|
|
4
|
+
return createRequire(import.meta.url);
|
|
5
|
+
})();
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// src/build/tag-manifest.ts
|
|
9
|
+
function extractCacheTags(headers) {
|
|
10
|
+
if (!headers || typeof headers !== "object") return [];
|
|
11
|
+
let raw;
|
|
12
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
13
|
+
if (typeof k === "string" && k.toLowerCase() === "cache-tag") {
|
|
14
|
+
raw = v;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (raw == null) return [];
|
|
19
|
+
const parts = Array.isArray(raw) ? raw : String(raw).split(",");
|
|
20
|
+
return parts.map((t) => String(t).trim()).filter((t) => t.length > 0);
|
|
21
|
+
}
|
|
22
|
+
function buildTagManifest(entries) {
|
|
23
|
+
const tags = {};
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
for (const tag of entry.tags) {
|
|
26
|
+
if (!tags[tag]) tags[tag] = [];
|
|
27
|
+
if (!tags[tag].includes(entry.path)) {
|
|
28
|
+
tags[tag].push(entry.path);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { version: 1, tags };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
extractCacheTags,
|
|
37
|
+
buildTagManifest
|
|
38
|
+
};
|