Skip to content

Commit

Permalink
feat(vercel, netlify): introduce isr route rule (#1124)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Apr 11, 2023
1 parent de7e59e commit 30c2c14
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 198 deletions.
5 changes: 0 additions & 5 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,6 @@ export async function loadOptions(
routeRules.cache.maxAge = routeConfig.swr;
}
}
// Cache: static
if (routeConfig.static) {
routeRules.cache = routeRules.cache || {};
routeRules.cache.static = true;
}
// Cache: false
if (routeConfig.cache === false) {
routeRules.cache = false;
Expand Down
18 changes: 7 additions & 11 deletions src/presets/netlify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,15 @@ async function writeRedirects(nitro: Nitro) {
(a, b) => a[0].split(/\/(?!\*)/).length - b[0].split(/\/(?!\*)/).length
);

// Rewrite static cached paths to builder functions
// Rewrite static ISR paths to builder functions
for (const [key, value] of rules.filter(
([_, value]) =>
value.cache === false ||
(value.cache && value.cache.swr === false) ||
(value.cache && (value.cache?.static || value.cache?.swr))
([_, value]) => value.isr !== undefined
)) {
contents =
value.cache === false || value.cache.swr === false
? `${key.replace("/**", "/*")}\t/.netlify/functions/server 200\n` +
contents
: `${key.replace("/**", "/*")}\t/.netlify/builders/server 200\n` +
contents;
contents = value.isr
? `${key.replace("/**", "/*")}\t/.netlify/builders/server 200\n` +
contents
: `${key.replace("/**", "/*")}\t/.netlify/functions/server 200\n` +
contents;
}

for (const [key, routeRules] of rules.filter(
Expand Down
69 changes: 26 additions & 43 deletions src/presets/vercel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,11 @@ export const vercel = defineNitroPreset({
JSON.stringify(functionConfig, null, 2)
);

// Write prerender functions
const rules = Object.entries(nitro.options.routeRules).filter(
([_, value]) => value.cache && (value.cache.swr || value.cache.static)
);

for (const [key, value] of rules) {
if (!value.cache) {
// Write ISR functions
for (const [key, value] of Object.entries(nitro.options.routeRules)) {
if (!value.isr) {
continue;
} // for type support
}
const funcPrefix = resolve(
nitro.options.output.serverDir,
".." + generateEndpoint(key)
Expand All @@ -67,18 +63,10 @@ export const vercel = defineNitroPreset({
funcPrefix + ".func",
"junction"
);

let expiration: boolean | number = 60;
if (value.cache.static) {
expiration = false;
} else if (typeof value.cache.swr === "number") {
expiration = value.cache.swr;
}

await writeFile(
funcPrefix + ".prerender-config.json",
JSON.stringify({
expiration,
expiration: value.isr === true ? false : value.isr,
allowQuery: key.includes("/**") ? ["url"] : undefined,
})
);
Expand Down Expand Up @@ -133,15 +121,19 @@ function generateBuildConfig(nitro: Nitro) {

return defu(nitro.options.vercel?.config, <VercelBuildConfigV3>{
version: 3,
overrides: Object.fromEntries(
(
nitro._prerenderedRoutes?.filter((r) => r.fileName !== r.route) || []
).map(({ route, fileName }) => [
withoutLeadingSlash(fileName),
{ path: route.replace(/^\//, "") },
])
),
overrides: {
// Nitro static prerendered route overrides
...Object.fromEntries(
(
nitro._prerenderedRoutes?.filter((r) => r.fileName !== r.route) || []
).map(({ route, fileName }) => [
withoutLeadingSlash(fileName),
{ path: route.replace(/^\//, "") },
])
),
},
routes: [
// Redirect and header rules
...rules
.filter(([_, routeRules]) => routeRules.redirect || routeRules.headers)
.map(([path, routeRules]) => {
Expand All @@ -159,6 +151,7 @@ function generateBuildConfig(nitro: Nitro) {
}
return route;
}),
// Public asset rules
...nitro.options.publicAssets
.filter((asset) => !asset.fallthrough)
.map((asset) => asset.baseURL)
Expand All @@ -170,21 +163,16 @@ function generateBuildConfig(nitro: Nitro) {
continue: true,
})),
{ handle: "filesystem" },
// ISR rules
...rules
.filter(
([key, value]) =>
value.cache === false ||
(value.cache && value.cache.swr === false) ||
(value.cache &&
(value.cache.swr || value.cache.static) &&
key.includes("/**"))
// value.isr === false || (value.isr && key.includes("/**"))
value.isr !== undefined
)
.map(([key, value]) => {
const src = key.replace(/^(.*)\/\*\*/, "(?<url>$1/.*)");
if (
value.cache === false ||
(value.cache && value.cache.swr === false)
) {
if (value.isr === false) {
// we need to write a rule to avoid route being shadowed by another cache rule elsewhere
return {
src,
Expand All @@ -199,22 +187,17 @@ function generateBuildConfig(nitro: Nitro) {
: generateEndpoint(key) + "?url=$url",
};
}),
// If we are using a prerender function for /, then we need to write this explicitly
...(nitro.options.routeRules["/"]?.cache
// If we are using an ISR function for /, then we need to write this explicitly
...(nitro.options.routeRules["/"]?.isr
? [
{
src: "(?<url>/)",
dest: "/__nitro-index",
},
]
: []),
// If we are using a prerender function as a fallback, then we do not need to output
// the below fallback route as well
...(!nitro.options.routeRules["/**"]?.cache ||
!(
nitro.options.routeRules["/**"].cache.swr ||
nitro.options.routeRules["/**"]?.cache.static
)
// If we are using an ISR function as a fallback, then we do not need to output the below fallback route as well
...(!nitro.options.routeRules["/**"]?.isr
? [
{
src: "/(.*)",
Expand Down
1 change: 0 additions & 1 deletion src/runtime/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export interface CacheOptions<T = any> {
group?: string;
integrity?: any;
maxAge?: number;
static?: boolean; // TODO
swr?: boolean;
staleMaxAge?: number;
base?: string;
Expand Down
9 changes: 4 additions & 5 deletions src/runtime/entries/netlify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ export const handler: Handler = async function handler(event, context) {
const url = withQuery(event.path, query);
const routeRules = getRouteRulesForPath(url);

if (routeRules.cache && (routeRules.cache.swr || routeRules.cache.static)) {
if (routeRules.isr) {
const builder = await import("@netlify/functions").then(
(r) => r.builder || r.default.builder
);
const ttl =
typeof routeRules.cache.swr === "number" ? routeRules.cache.swr : 60;
const swrHandler = routeRules.cache.swr
const ttl = typeof routeRules.isr === "number" ? routeRules.isr : false;
const builderHandler = ttl
? (((event, context) =>
lambda(event, context).then((r) => ({ ...r, ttl }))) as Handler)
: lambda;
return builder(swrHandler)(event, context) as any;
return builder(builderHandler)(event, context) as any;
}

return lambda(event, context);
Expand Down
1 change: 1 addition & 0 deletions src/types/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export interface NitroRouteConfig {
redirect?: string | { to: string; statusCode?: HTTPStatusCode };
prerender?: boolean;
proxy?: string | ({ to: string } & ProxyOptions);
isr?: number | boolean;

// Shortcuts
cors?: boolean;
Expand Down
20 changes: 20 additions & 0 deletions test/fixture/nitro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ export default defineNitroConfig({
routeRules: {
"/api/param/prerender4": { prerender: true },
"/api/param/prerender2": { prerender: false },
"/rules/headers": { headers: { "cache-control": "s-maxage=60" } },
"/rules/cors": {
cors: true,
headers: { "access-control-allow-methods": "GET" },
},
"/rules/dynamic": { cache: false, isr: false },
"/rules/redirect": { redirect: "/base" },
"/rules/isr/**": { isr: true },
"/rules/isr-ttl/**": { isr: 60 },
"/rules/swr/**": { swr: true },
"/rules/swr-ttl/**": { swr: 60 },
"/rules/redirect/obj": {
redirect: { to: "https://nitro.unjs.io/", statusCode: 308 },
},
"/rules/nested/**": { redirect: "/base", headers: { "x-test": "test" } },
"/rules/nested/override": { redirect: { to: "/other" } },
"/rules/_/noncached/cached": { swr: true },
"/rules/_/noncached/**": { swr: false, cache: false, isr: false },
"/rules/_/cached/noncached": { cache: false, swr: false, isr: false },
"/rules/_/cached/**": { swr: true },
},
prerender: {
crawlLinks: true,
Expand Down
7 changes: 2 additions & 5 deletions test/presets/netlify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,9 @@ describe("nitro:preset:netlify", async () => {
/rules/nested/* /base 302
/rules/redirect /base 302
/rules/_/cached/noncached /.netlify/functions/server 200
/rules/_/noncached/cached /.netlify/builders/server 200
/rules/_/cached/* /.netlify/builders/server 200
/rules/_/noncached/* /.netlify/functions/server 200
/rules/swr-ttl/* /.netlify/builders/server 200
/rules/swr/* /.netlify/builders/server 200
/rules/static /.netlify/builders/server 200
/rules/isr-ttl/* /.netlify/builders/server 200
/rules/isr/* /.netlify/builders/server 200
/rules/dynamic /.netlify/functions/server 200
/* /.netlify/functions/server 200"
`);
Expand Down

0 comments on commit 30c2c14

Please sign in to comment.