mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 21:55:11 +00:00
feat: add support for Azure static web apps (#92)
This commit is contained in:
parent
97efab4270
commit
31a9bc2d18
@ -1,14 +1,16 @@
|
|||||||
import archiver from 'archiver'
|
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { createWriteStream } from 'fs-extra'
|
import fse from 'fs-extra'
|
||||||
|
import globby from 'globby'
|
||||||
import { join, resolve } from 'upath'
|
import { join, resolve } from 'upath'
|
||||||
import { prettyPath, writeFile } from '../utils'
|
import { writeFile } from '../utils'
|
||||||
import { NitroPreset, NitroContext } from '../context'
|
import { NitroPreset, NitroContext } from '../context'
|
||||||
|
|
||||||
export const azure: NitroPreset = {
|
export const azure: NitroPreset = {
|
||||||
inlineChunks: false,
|
inlineChunks: false,
|
||||||
serveStatic: true,
|
|
||||||
entry: '{{ _internal.runtimeDir }}/entries/azure',
|
entry: '{{ _internal.runtimeDir }}/entries/azure',
|
||||||
|
output: {
|
||||||
|
serverDir: '{{ output.dir }}/server/functions'
|
||||||
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
async 'nitro:compiled' (ctx: NitroContext) {
|
async 'nitro:compiled' (ctx: NitroContext) {
|
||||||
await writeRoutes(ctx)
|
await writeRoutes(ctx)
|
||||||
@ -16,27 +18,64 @@ export const azure: NitroPreset = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function zipDirectory (dir: string, outfile: string): Promise<undefined> {
|
async function writeRoutes ({ output: { serverDir, publicDir } }: NitroContext) {
|
||||||
const archive = archiver('zip', { zlib: { level: 9 } })
|
|
||||||
const stream = createWriteStream(outfile)
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
archive
|
|
||||||
.directory(dir, false)
|
|
||||||
.on('error', (err: Error) => reject(err))
|
|
||||||
.pipe(stream)
|
|
||||||
|
|
||||||
stream.on('close', () => resolve(undefined))
|
|
||||||
archive.finalize()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
|
|
||||||
const host = {
|
const host = {
|
||||||
version: '2.0',
|
version: '2.0'
|
||||||
extensions: { http: { routePrefix: '' } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
route: '/*',
|
||||||
|
serve: '/api/server'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const indexPath = resolve(publicDir, 'index.html')
|
||||||
|
const indexFileExists = fse.existsSync(indexPath)
|
||||||
|
if (!indexFileExists) {
|
||||||
|
routes.unshift(
|
||||||
|
{
|
||||||
|
route: '/',
|
||||||
|
serve: '/api/server'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
route: '/index.html',
|
||||||
|
serve: '/api/server'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderFiles = await globby([
|
||||||
|
join(publicDir, 'index.html'),
|
||||||
|
join(publicDir, '**/index.html')
|
||||||
|
])
|
||||||
|
const prefix = publicDir.length
|
||||||
|
const suffix = '/index.html'.length
|
||||||
|
folderFiles.forEach(file =>
|
||||||
|
routes.unshift({
|
||||||
|
route: file.slice(prefix, -suffix) || '/',
|
||||||
|
serve: file.slice(prefix)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const otherFiles = await globby([join(publicDir, '**/*.html'), join(publicDir, '*.html')])
|
||||||
|
otherFiles.forEach((file) => {
|
||||||
|
if (file.endsWith('index.html')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const route = file.slice(prefix, -5)
|
||||||
|
const existingRouteIndex = routes.findIndex(_route => _route.route === route)
|
||||||
|
if (existingRouteIndex > -1) {
|
||||||
|
routes.splice(existingRouteIndex, 1)
|
||||||
|
}
|
||||||
|
routes.unshift(
|
||||||
|
{
|
||||||
|
route,
|
||||||
|
serve: file.slice(prefix)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const functionDefinition = {
|
const functionDefinition = {
|
||||||
entryPoint: 'handle',
|
entryPoint: 'handle',
|
||||||
bindings: [
|
bindings: [
|
||||||
@ -46,15 +85,7 @@ async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
|
|||||||
direction: 'in',
|
direction: 'in',
|
||||||
name: 'req',
|
name: 'req',
|
||||||
route: '{*url}',
|
route: '{*url}',
|
||||||
methods: [
|
methods: ['delete', 'get', 'head', 'options', 'patch', 'post', 'put']
|
||||||
'delete',
|
|
||||||
'get',
|
|
||||||
'head',
|
|
||||||
'options',
|
|
||||||
'patch',
|
|
||||||
'post',
|
|
||||||
'put'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'http',
|
type: 'http',
|
||||||
@ -65,10 +96,11 @@ async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await writeFile(resolve(serverDir, 'function.json'), JSON.stringify(functionDefinition))
|
await writeFile(resolve(serverDir, 'function.json'), JSON.stringify(functionDefinition))
|
||||||
await writeFile(resolve(dir, 'host.json'), JSON.stringify(host))
|
await writeFile(resolve(serverDir, '../host.json'), JSON.stringify(host))
|
||||||
|
await writeFile(resolve(publicDir, 'routes.json'), JSON.stringify({ routes }))
|
||||||
await zipDirectory(dir, join(dir, 'deploy.zip'))
|
if (!indexFileExists) {
|
||||||
const zipPath = prettyPath(resolve(dir, 'deploy.zip'))
|
await writeFile(indexPath, '')
|
||||||
|
}
|
||||||
consola.success(`Ready to run \`az functionapp deployment source config-zip -g <resource-group> -n <app-name> --src ${zipPath}\``)
|
|
||||||
|
consola.success('Ready to deploy.')
|
||||||
}
|
}
|
||||||
|
75
packages/nitro/src/presets/azure_functions.ts
Normal file
75
packages/nitro/src/presets/azure_functions.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import archiver from 'archiver'
|
||||||
|
import consola from 'consola'
|
||||||
|
import { createWriteStream } from 'fs-extra'
|
||||||
|
import { join, resolve } from 'upath'
|
||||||
|
import { prettyPath, writeFile } from '../utils'
|
||||||
|
import { NitroPreset, NitroContext } from '../context'
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const azure_functions: NitroPreset = {
|
||||||
|
inlineChunks: false,
|
||||||
|
serveStatic: true,
|
||||||
|
entry: '{{ _internal.runtimeDir }}/entries/azure_functions',
|
||||||
|
hooks: {
|
||||||
|
async 'nitro:compiled' (ctx: NitroContext) {
|
||||||
|
await writeRoutes(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zipDirectory (dir: string, outfile: string): Promise<undefined> {
|
||||||
|
const archive = archiver('zip', { zlib: { level: 9 } })
|
||||||
|
const stream = createWriteStream(outfile)
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
archive
|
||||||
|
.directory(dir, false)
|
||||||
|
.on('error', (err: Error) => reject(err))
|
||||||
|
.pipe(stream)
|
||||||
|
|
||||||
|
stream.on('close', () => resolve(undefined))
|
||||||
|
archive.finalize()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
|
||||||
|
const host = {
|
||||||
|
version: '2.0',
|
||||||
|
extensions: { http: { routePrefix: '' } }
|
||||||
|
}
|
||||||
|
|
||||||
|
const functionDefinition = {
|
||||||
|
entryPoint: 'handle',
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
authLevel: 'anonymous',
|
||||||
|
type: 'httpTrigger',
|
||||||
|
direction: 'in',
|
||||||
|
name: 'req',
|
||||||
|
route: '{*url}',
|
||||||
|
methods: [
|
||||||
|
'delete',
|
||||||
|
'get',
|
||||||
|
'head',
|
||||||
|
'options',
|
||||||
|
'patch',
|
||||||
|
'post',
|
||||||
|
'put'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'http',
|
||||||
|
direction: 'out',
|
||||||
|
name: 'res'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeFile(resolve(serverDir, 'function.json'), JSON.stringify(functionDefinition))
|
||||||
|
await writeFile(resolve(dir, 'host.json'), JSON.stringify(host))
|
||||||
|
|
||||||
|
await zipDirectory(dir, join(dir, 'deploy.zip'))
|
||||||
|
const zipPath = prettyPath(resolve(dir, 'deploy.zip'))
|
||||||
|
|
||||||
|
consola.success(`Ready to run \`az functionapp deployment source config-zip -g <resource-group> -n <app-name> --src ${zipPath}\``)
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './azure_functions'
|
||||||
export * from './azure'
|
export * from './azure'
|
||||||
export * from './browser'
|
export * from './browser'
|
||||||
export * from './cloudflare'
|
export * from './cloudflare'
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import '~polyfill'
|
import '~polyfill'
|
||||||
|
import { parseURL } from 'ufo'
|
||||||
import { localCall } from '../server'
|
import { localCall } from '../server'
|
||||||
|
|
||||||
export default async function handle (context, req) {
|
export default async function handle (context, req) {
|
||||||
const url = '/' + (req.params.url || '')
|
let url: string
|
||||||
|
if (req.headers['x-ms-original-url']) {
|
||||||
|
// This URL has been proxied as there was no static file matching it.
|
||||||
|
url = parseURL(req.headers['x-ms-original-url']).pathname
|
||||||
|
} else {
|
||||||
|
// Because Azure SWA handles /api/* calls differently they
|
||||||
|
// never hit the proxy and we have to reconstitute the URL.
|
||||||
|
url = '/api/' + (req.params.url || '')
|
||||||
|
}
|
||||||
|
|
||||||
const { body, status, statusText, headers } = await localCall({
|
const { body, status, statusText, headers } = await localCall({
|
||||||
url,
|
url,
|
||||||
|
19
packages/nitro/src/runtime/entries/azure_functions.ts
Normal file
19
packages/nitro/src/runtime/entries/azure_functions.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { localCall } from '../server'
|
||||||
|
|
||||||
|
export default async function handle (context, req) {
|
||||||
|
const url = '/' + (req.params.url || '')
|
||||||
|
|
||||||
|
const { body, status, statusText, headers } = await localCall({
|
||||||
|
url,
|
||||||
|
headers: req.headers,
|
||||||
|
method: req.method,
|
||||||
|
body: req.body
|
||||||
|
})
|
||||||
|
|
||||||
|
context.res = {
|
||||||
|
status,
|
||||||
|
headers,
|
||||||
|
body: body ? body.toString() : statusText
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,10 @@ export function detectTarget () {
|
|||||||
if (process.env.NOW_BUILDER) {
|
if (process.env.NOW_BUILDER) {
|
||||||
return 'vercel'
|
return 'vercel'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN) {
|
||||||
|
return 'azure'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isDirectory (path: string) {
|
export async function isDirectory (path: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user