Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ Temporary Items
.env
.netlify
.vercel
staticwebapp.config.json
2 changes: 2 additions & 0 deletions src/prerender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export async function prerender (nitro: Nitro) {
}
// Build with prerender preset
nitro.logger.info('Initializing prerenderer')
nitro._prerenderedRoutes = []
const nitroRenderer = await createNitro({
...nitro.options._config,
rootDir: nitro.options.rootDir,
Expand Down Expand Up @@ -89,6 +90,7 @@ export async function prerender (nitro: Nitro) {

const filePath = join(nitro.options.output.publicDir, _route.fileName)
await writeFile(filePath, Buffer.from(_route.data))
nitro._prerenderedRoutes.push(_route)

// Crawl route links
if (!_route.error && isImplicitHTML) {
Expand Down
53 changes: 23 additions & 30 deletions src/presets/azure.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import fse from 'fs-extra'
import { globby } from 'globby'
import { join, resolve } from 'pathe'
import { writeFile } from '../utils'
import { defineNitroPreset } from '../preset'
Expand All @@ -20,7 +19,7 @@ export const azure = defineNitroPreset({
}
})

async function writeRoutes (nitro) {
async function writeRoutes (nitro: Nitro) {
const host = {
version: '2.0'
}
Expand Down Expand Up @@ -48,8 +47,9 @@ async function writeRoutes (nitro) {
}
}

const indexPath = resolve(nitro.options.output.publicDir, 'index.html')
const indexFileExists = fse.existsSync(indexPath)
const routeFiles = nitro._prerenderedRoutes || []

const indexFileExists = routeFiles.some(route => route.fileName === '/index.html')
if (!indexFileExists) {
config.routes.unshift(
{
Expand All @@ -63,36 +63,29 @@ async function writeRoutes (nitro) {
)
}

const folderFiles = await globby([
join(nitro.options.output.publicDir, 'index.html'),
join(nitro.options.output.publicDir, '**/index.html')
])
const prefix = nitro.options.output.publicDir.length
const suffix = '/index.html'.length
folderFiles.forEach(file =>
for (const { fileName } of routeFiles) {
if (!fileName.endsWith('/index.html')) { continue }

config.routes.unshift({
route: file.slice(prefix, -suffix) || '/',
rewrite: file.slice(prefix)
route: fileName.slice(0, -suffix) || '/',
rewrite: fileName
})
)
}

const otherFiles = await globby([join(nitro.options.output.publicDir, '**/*.html'), join(nitro.options.output.publicDir, '*.html')])
otherFiles.forEach((file) => {
if (file.endsWith('index.html')) {
return
}
const route = file.slice(prefix, -'.html'.length)
for (const { fileName } of routeFiles) {
if (!fileName.endsWith('.html') || fileName.endsWith('index.html')) { continue }

const route = fileName.slice(0, -'.html'.length)
const existingRouteIndex = config.routes.findIndex(_route => _route.route === route)
if (existingRouteIndex > -1) {
config.routes.splice(existingRouteIndex, 1)
}
config.routes.unshift(
{
route,
rewrite: file.slice(prefix)
}
)
})
config.routes.unshift({
route,
rewrite: fileName
})
}

const functionDefinition = {
entryPoint: 'handle',
Expand All @@ -113,12 +106,12 @@ async function writeRoutes (nitro) {
]
}

await writeFile(resolve(nitro.options.output.serverDir, 'function.json'), JSON.stringify(functionDefinition))
await writeFile(resolve(nitro.options.output.serverDir, '../host.json'), JSON.stringify(host))
await writeFile(resolve(nitro.options.output.serverDir, 'function.json'), JSON.stringify(functionDefinition, null, 2))
await writeFile(resolve(nitro.options.output.serverDir, '../host.json'), JSON.stringify(host, null, 2))
const stubPackageJson = resolve(nitro.options.output.serverDir, '../package.json')
await writeFile(stubPackageJson, JSON.stringify({ private: true }))
await writeFile(resolve(nitro.options.rootDir, 'staticwebapp.config.json'), JSON.stringify(config))
await writeFile(resolve(nitro.options.rootDir, 'staticwebapp.config.json'), JSON.stringify(config, null, 2))
if (!indexFileExists) {
await writeFile(indexPath, '')
await writeFile(resolve(nitro.options.output.publicDir, 'index.html'), '')
}
}
83 changes: 37 additions & 46 deletions src/presets/vercel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { resolve } from 'pathe'
import { defu } from 'defu'
import { withoutLeadingSlash } from 'ufo'
import { writeFile } from '../utils'
import { defineNitroPreset } from '../preset'
import type { Nitro } from '../types'
Expand All @@ -11,7 +12,7 @@ export const vercel = defineNitroPreset({
entry: '#internal/nitro/entries/vercel',
output: {
dir: '{{ rootDir }}/.vercel/output',
serverDir: '{{ output.dir }}/functions/index.func',
serverDir: '{{ output.dir }}/functions/__nitro.func',
publicDir: '{{ output.dir }}/static'
},
commands: {
Expand All @@ -21,28 +22,7 @@ export const vercel = defineNitroPreset({
hooks: {
async 'compiled' (nitro: Nitro) {
const buildConfigPath = resolve(nitro.options.output.dir, 'config.json')
const buildConfig = defu(nitro.options.vercel?.config, {
version: 3,
routes: [
...nitro.options.publicAssets
.filter(asset => !asset.fallthrough)
.map(asset => asset.baseURL)
.map(baseURL => ({
src: baseURL + '(.*)',
headers: {
'cache-control': 'public,max-age=31536000,immutable'
},
continue: true
})),
{
handle: 'filesystem'
},
{
src: '/(.*)',
dest: '/'
}
]
})
const buildConfig = generateBuildConfig(nitro)
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2))

const functionConfigPath = resolve(nitro.options.output.serverDir, '.vc-config.json')
Expand All @@ -62,7 +42,7 @@ export const vercelEdge = defineNitroPreset({
entry: '#internal/nitro/entries/vercel-edge',
output: {
dir: '{{ rootDir }}/.vercel/output',
serverDir: '{{ output.dir }}/functions/index.func',
serverDir: '{{ output.dir }}/functions/__nitro.func',
publicDir: '{{ output.dir }}/static'
},
commands: {
Expand All @@ -77,28 +57,7 @@ export const vercelEdge = defineNitroPreset({
hooks: {
async 'compiled' (nitro: Nitro) {
const buildConfigPath = resolve(nitro.options.output.dir, 'config.json')
const buildConfig = defu(nitro.options.vercel?.config, {
version: 3,
routes: [
...nitro.options.publicAssets
.filter(asset => !asset.fallthrough)
.map(asset => asset.baseURL)
.map(baseURL => ({
src: baseURL + '(.*)',
headers: {
'cache-control': 'public,max-age=31536000,immutable'
},
continue: true
})),
{
handle: 'filesystem'
},
{
src: '/(.*)',
dest: '/'
}
]
})
const buildConfig = generateBuildConfig(nitro)
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2))

const functionConfigPath = resolve(nitro.options.output.serverDir, '.vc-config.json')
Expand All @@ -110,3 +69,35 @@ export const vercelEdge = defineNitroPreset({
}
}
})

function generateBuildConfig (nitro: Nitro) {
// const overrides = generateOverrides(nitro._prerenderedRoutes?.filter(r => r.fileName !== r.route) || [])
return defu(nitro.options.vercel?.config, {
version: 3,
overrides: Object.fromEntries(
(nitro._prerenderedRoutes?.filter(r => r.fileName !== r.route) || [])
.map(({ route, fileName }) =>
[withoutLeadingSlash(fileName), { path: withoutLeadingSlash(route) }]
)
),
routes: [
...nitro.options.publicAssets
.filter(asset => !asset.fallthrough)
.map(asset => asset.baseURL)
.map(baseURL => ({
src: baseURL + '(.*)',
headers: {
'cache-control': 'public,max-age=31536000,immutable'
},
continue: true
})),
{
handle: 'filesystem'
},
{
src: '/(.*)',
dest: '/__nitro'
}
]
})
}
3 changes: 3 additions & 0 deletions src/types/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export interface Nitro {
logger: Consola
storage: Storage
close: () => Promise<void>

/* @internal */
_prerenderedRoutes?: PrerenderGenerateRoute[]
}

export interface PrerenderRoute {
Expand Down
4 changes: 2 additions & 2 deletions test/presets/vercel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { setupTest, startServer, testNitro } from '../tests'
describe('nitro:preset:vercel', async () => {
const ctx = await setupTest('vercel')
testNitro(ctx, async () => {
const handle = await import(resolve(ctx.outDir, 'functions/index.func/index.mjs'))
const handle = await import(resolve(ctx.outDir, 'functions/__nitro.func/index.mjs'))
.then(r => r.default || r)
await startServer(ctx, handle)
return async ({ url }) => {
Expand All @@ -21,7 +21,7 @@ describe.skip('nitro:preset:vercel-edge', async () => {
const ctx = await setupTest('vercel-edge')
testNitro(ctx, async () => {
// TODO: Add add-event-listener
const entry = resolve(ctx.outDir, 'functions/index.func/index.mjs')
const entry = resolve(ctx.outDir, 'functions/__nitro.func/index.mjs')
const entryCode = await fsp.readFile(entry, 'utf8')
const runtime = new EdgeRuntime({ initialCode: entryCode })
return async ({ url }) => {
Expand Down