mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat: support server directory (#132)
* feat: support server directory * fix sorting and global * lazy load api * pretty print opts * fix: hide table when no middleware
This commit is contained in:
parent
27aef1489f
commit
85da52d390
@ -1,12 +1,12 @@
|
||||
import { resolve, join } from 'upath'
|
||||
import consola from 'consola'
|
||||
import { rollup, watch as rollupWatch } from 'rollup'
|
||||
import ora from 'ora'
|
||||
import { readFile, emptyDir, copy } from 'fs-extra'
|
||||
import { printFSTree } from './utils/tree'
|
||||
import { getRollupConfig } from './rollup/config'
|
||||
import { hl, prettyPath, serializeTemplate, writeFile, isDirectory } from './utils'
|
||||
import { NitroContext } from './context'
|
||||
import { scanMiddleware } from './server/middleware'
|
||||
|
||||
export async function prepare (nitroContext: NitroContext) {
|
||||
consola.info(`Nitro preset is ${hl(nitroContext.preset)}`)
|
||||
@ -28,8 +28,7 @@ async function cleanupDir (dir: string) {
|
||||
}
|
||||
|
||||
export async function generate (nitroContext: NitroContext) {
|
||||
const spinner = ora()
|
||||
spinner.start('Generating public...')
|
||||
consola.start('Generating public...')
|
||||
|
||||
const clientDist = resolve(nitroContext._nuxt.buildDir, 'dist/client')
|
||||
if (await isDirectory(clientDist)) {
|
||||
@ -41,7 +40,7 @@ export async function generate (nitroContext: NitroContext) {
|
||||
await copy(staticDir, nitroContext.output.publicDir)
|
||||
}
|
||||
|
||||
spinner.succeed('Generated public ' + prettyPath(nitroContext.output.publicDir))
|
||||
consola.success('Generated public ' + prettyPath(nitroContext.output.publicDir))
|
||||
}
|
||||
|
||||
export async function build (nitroContext: NitroContext) {
|
||||
@ -60,18 +59,18 @@ export async function build (nitroContext: NitroContext) {
|
||||
}
|
||||
|
||||
async function _build (nitroContext: NitroContext) {
|
||||
const spinner = ora()
|
||||
nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir)
|
||||
|
||||
spinner.start('Building server...')
|
||||
consola.start('Building server...')
|
||||
const build = await rollup(nitroContext.rollupConfig).catch((error) => {
|
||||
spinner.fail('Rollup error: ' + error.message)
|
||||
consola.error('Rollup error: ' + error.message)
|
||||
throw error
|
||||
})
|
||||
|
||||
spinner.start('Writing server bundle...')
|
||||
consola.start('Writing server bundle...')
|
||||
await build.write(nitroContext.rollupConfig.output)
|
||||
|
||||
spinner.succeed('Server built')
|
||||
consola.success('Server built')
|
||||
await printFSTree(nitroContext.output.serverDir)
|
||||
await nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
|
||||
|
||||
@ -80,11 +79,16 @@ async function _build (nitroContext: NitroContext) {
|
||||
}
|
||||
}
|
||||
|
||||
function _watch (nitroContext: NitroContext) {
|
||||
const spinner = ora()
|
||||
|
||||
async function _watch (nitroContext: NitroContext) {
|
||||
const watcher = rollupWatch(nitroContext.rollupConfig)
|
||||
|
||||
nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir,
|
||||
(middleware, event, file) => {
|
||||
nitroContext.scannedMiddleware = middleware
|
||||
watcher.emit(event, file)
|
||||
}
|
||||
)
|
||||
|
||||
let start
|
||||
|
||||
watcher.on('event', (event) => {
|
||||
@ -96,17 +100,17 @@ function _watch (nitroContext: NitroContext) {
|
||||
// Building an individual bundle
|
||||
case 'BUNDLE_START':
|
||||
start = Date.now()
|
||||
spinner.start('Building Nitro...')
|
||||
return
|
||||
|
||||
// Finished building all bundles
|
||||
case 'END':
|
||||
nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
|
||||
return spinner.succeed(`Nitro built in ${Date.now() - start} ms`)
|
||||
consola.success('Nitro built', start ? `in ${Date.now() - start} ms` : '')
|
||||
return
|
||||
|
||||
// Encountered an error while bundling
|
||||
case 'ERROR':
|
||||
spinner.fail('Rollup error: ' + event.error)
|
||||
consola.error('Rollup error: ' + event.error)
|
||||
// consola.error(event.error)
|
||||
}
|
||||
})
|
||||
|
@ -2,9 +2,9 @@ import fetch from 'node-fetch'
|
||||
import { resolve } from 'upath'
|
||||
import { build, generate, prepare } from './build'
|
||||
import { getNitroContext, NitroContext } from './context'
|
||||
import { createDevServer } from './server'
|
||||
import { createDevServer } from './server/dev'
|
||||
import { wpfs } from './utils/wpfs'
|
||||
import { resolveMiddleware } from './middleware'
|
||||
import { resolveMiddleware } from './server/middleware'
|
||||
|
||||
export default function nuxt2CompatModule () {
|
||||
const { nuxt } = this
|
||||
|
@ -6,13 +6,7 @@ import type { Preset } from '@nuxt/un'
|
||||
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
|
||||
import * as PRESETS from './presets'
|
||||
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
||||
|
||||
export interface ServerMiddleware {
|
||||
route: string
|
||||
handle: string
|
||||
lazy?: boolean // Default is true
|
||||
promisify?: boolean // Default is true
|
||||
}
|
||||
import type { ServerMiddleware } from './server/middleware'
|
||||
|
||||
export interface NitroContext {
|
||||
timing: boolean
|
||||
@ -28,6 +22,7 @@ export interface NitroContext {
|
||||
renderer: string
|
||||
serveStatic: boolean
|
||||
middleware: ServerMiddleware[]
|
||||
scannedMiddleware: ServerMiddleware[]
|
||||
hooks: configHooksT
|
||||
nuxtHooks: configHooksT
|
||||
ignore: string[]
|
||||
@ -45,6 +40,7 @@ export interface NitroContext {
|
||||
buildDir: string
|
||||
generateDir: string
|
||||
staticDir: string
|
||||
serverDir: string
|
||||
routerBase: string
|
||||
publicPath: string
|
||||
isStatic: boolean
|
||||
@ -79,6 +75,7 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
||||
renderer: undefined,
|
||||
serveStatic: false,
|
||||
middleware: [],
|
||||
scannedMiddleware: [],
|
||||
ignore: [],
|
||||
env: {},
|
||||
hooks: {},
|
||||
@ -96,6 +93,7 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
||||
buildDir: nuxtOptions.buildDir,
|
||||
generateDir: nuxtOptions.generate.dir,
|
||||
staticDir: nuxtOptions.dir.static,
|
||||
serverDir: resolve(nuxtOptions.srcDir, (nuxtOptions.dir as any).server || 'server'),
|
||||
routerBase: nuxtOptions.router.base,
|
||||
publicPath: nuxtOptions.build.publicPath,
|
||||
isStatic: nuxtOptions.target === 'static' && !nuxtOptions.dev,
|
||||
|
@ -1,6 +1,6 @@
|
||||
export * from './build'
|
||||
export * from './context'
|
||||
export * from './middleware'
|
||||
export * from './server'
|
||||
export * from './server/middleware'
|
||||
export * from './server/dev'
|
||||
export * from './types'
|
||||
export { wpfs } from './utils/wpfs'
|
||||
|
@ -1,29 +0,0 @@
|
||||
export interface Middleware {
|
||||
handle: string
|
||||
route: string
|
||||
}
|
||||
|
||||
export function resolveMiddleware (serverMiddleware: any[], resolvePath: (string) => string) {
|
||||
const middleware: Middleware[] = []
|
||||
const legacyMiddleware: Middleware[] = []
|
||||
|
||||
for (let m of serverMiddleware) {
|
||||
if (typeof m === 'string') { m = { handler: m } }
|
||||
const route = m.path || m.route || '/'
|
||||
const handle = m.handler || m.handle
|
||||
if (typeof handle !== 'string' || typeof route !== 'string') {
|
||||
legacyMiddleware.push(m)
|
||||
} else {
|
||||
middleware.push({
|
||||
...m,
|
||||
handle: resolvePath(handle),
|
||||
route
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
middleware,
|
||||
legacyMiddleware
|
||||
}
|
||||
}
|
@ -148,11 +148,16 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
}
|
||||
|
||||
// Middleware
|
||||
const _middleware = [...nitroContext.middleware]
|
||||
if (nitroContext.serveStatic) {
|
||||
_middleware.unshift({ route: '/', handle: '~runtime/server/static' })
|
||||
}
|
||||
rollupConfig.plugins.push(middleware(_middleware))
|
||||
rollupConfig.plugins.push(middleware(() => {
|
||||
const _middleware = [
|
||||
...nitroContext.scannedMiddleware,
|
||||
...nitroContext.middleware
|
||||
]
|
||||
if (nitroContext.serveStatic) {
|
||||
_middleware.unshift({ route: '/', handle: '~runtime/server/static' })
|
||||
}
|
||||
return _middleware
|
||||
}))
|
||||
|
||||
// Polyfill
|
||||
rollupConfig.plugins.push(virtual({
|
||||
@ -187,7 +192,8 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
ignore: [
|
||||
nitroContext._internal.runtimeDir,
|
||||
...(nitroContext._nuxt.dev ? [] : [nitroContext._nuxt.buildDir]),
|
||||
...nitroContext.middleware.map(m => m.handle)
|
||||
...nitroContext.middleware.map(m => m.handle),
|
||||
nitroContext._nuxt.serverDir
|
||||
],
|
||||
traceOptions: {
|
||||
base: nitroContext._nuxt.rootDir
|
||||
|
@ -1,12 +1,26 @@
|
||||
import hasha from 'hasha'
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import type { ServerMiddleware } from '../../context'
|
||||
import { relative } from 'upath'
|
||||
import { table, getBorderCharacters } from 'table'
|
||||
import isPrimitive from 'is-primitive'
|
||||
import type { ServerMiddleware } from '../../server/middleware'
|
||||
import virtual from './virtual'
|
||||
|
||||
export function middleware (middleware: ServerMiddleware[]) {
|
||||
export function middleware (getMiddleware: () => ServerMiddleware[]) {
|
||||
const getImportId = p => '_' + hasha(p).substr(0, 6)
|
||||
|
||||
let lastDump = ''
|
||||
|
||||
return virtual({
|
||||
'~serverMiddleware': `
|
||||
'~serverMiddleware': () => {
|
||||
const middleware = getMiddleware()
|
||||
const dumped = dumpMiddleware(middleware)
|
||||
if (dumped !== lastDump) {
|
||||
lastDump = dumped
|
||||
if (middleware.length) {
|
||||
console.log('\n\nNitro middleware:\n' + dumped)
|
||||
}
|
||||
}
|
||||
return `
|
||||
${middleware.filter(m => m.lazy === false).map(m => `import ${getImportId(m.handle)} from '${m.handle}';`).join('\n')}
|
||||
|
||||
${middleware.filter(m => m.lazy !== false).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')}
|
||||
@ -17,5 +31,31 @@ const middleware = [
|
||||
|
||||
export default middleware
|
||||
`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function dumpMiddleware (middleware: ServerMiddleware[]) {
|
||||
const data = middleware.map(({ route, handle, ...props }) => {
|
||||
return [
|
||||
(route && route !== '/') ? route : '[global]',
|
||||
relative(process.cwd(), handle),
|
||||
dumpObject(props)
|
||||
]
|
||||
})
|
||||
return table([
|
||||
['Route', 'Handle', 'Options'],
|
||||
...data
|
||||
], {
|
||||
border: getBorderCharacters('norc')
|
||||
})
|
||||
}
|
||||
|
||||
function dumpObject (obj: any) {
|
||||
const items = []
|
||||
for (const key in obj) {
|
||||
const val = obj[key]
|
||||
items.push(`${key}: ${isPrimitive(val) ? val : JSON.stringify(val)}`)
|
||||
}
|
||||
return items.join(', ')
|
||||
}
|
||||
|
49
packages/nitro/src/rollup/plugins/virtual.ts
Normal file
49
packages/nitro/src/rollup/plugins/virtual.ts
Normal file
@ -0,0 +1,49 @@
|
||||
// Based on https://github.com/rollup/plugins/blob/master/packages/virtual/src/index.ts
|
||||
import * as path from 'path'
|
||||
|
||||
import { Plugin } from 'rollup'
|
||||
|
||||
type UnresolvedModule = string | (() => string)
|
||||
export interface RollupVirtualOptions {
|
||||
[id: string]: UnresolvedModule;
|
||||
}
|
||||
|
||||
const PREFIX = '\0virtual:'
|
||||
|
||||
const resolveModule = (m: UnresolvedModule) => typeof m === 'function' ? m() : m
|
||||
|
||||
export default function virtual (modules: RollupVirtualOptions): Plugin {
|
||||
const resolvedIds = new Map<string, string |(() => string)>()
|
||||
|
||||
Object.keys(modules).forEach((id) => {
|
||||
resolvedIds.set(path.resolve(id), modules[id])
|
||||
})
|
||||
|
||||
return {
|
||||
name: 'virtual',
|
||||
|
||||
resolveId (id, importer) {
|
||||
if (id in modules) { return PREFIX + id }
|
||||
|
||||
if (importer) {
|
||||
const importerNoPrefix = importer.startsWith(PREFIX)
|
||||
? importer.slice(PREFIX.length)
|
||||
: importer
|
||||
const resolved = path.resolve(path.dirname(importerNoPrefix), id)
|
||||
if (resolvedIds.has(resolved)) { return PREFIX + resolved }
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
load (id) {
|
||||
if (!id.startsWith(PREFIX)) {
|
||||
return null
|
||||
}
|
||||
const idNoPrefix = id.slice(PREFIX.length)
|
||||
return idNoPrefix in modules
|
||||
? resolveModule(modules[idNoPrefix])
|
||||
: resolveModule(resolvedIds.get(idNoPrefix))
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import serveStatic from 'serve-static'
|
||||
import servePlaceholder from 'serve-placeholder'
|
||||
import { createProxy } from 'http-proxy'
|
||||
import { stat } from 'fs-extra'
|
||||
import type { NitroContext } from './context'
|
||||
import type { NitroContext } from '../context'
|
||||
|
||||
export function createDevServer (nitroContext: NitroContext) {
|
||||
// Worker
|
76
packages/nitro/src/server/middleware.ts
Normal file
76
packages/nitro/src/server/middleware.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { resolve, join, extname } from 'upath'
|
||||
import { joinURL } from 'ufo'
|
||||
import globby from 'globby'
|
||||
import { watch } from 'chokidar'
|
||||
|
||||
export interface ServerMiddleware {
|
||||
route: string
|
||||
handle: string
|
||||
lazy?: boolean // Default is true
|
||||
promisify?: boolean // Default is true
|
||||
}
|
||||
|
||||
function filesToMiddleware (files: string[], baseDir: string, basePath: string, overrides?: Partial<ServerMiddleware>): ServerMiddleware[] {
|
||||
return files.map((file) => {
|
||||
const route = joinURL(basePath, file.substr(0, file.length - extname(file).length))
|
||||
const handle = resolve(baseDir, file)
|
||||
return {
|
||||
route,
|
||||
handle
|
||||
}
|
||||
})
|
||||
.sort((a, b) => a.route.localeCompare(b.route))
|
||||
.map(m => ({ ...m, ...overrides }))
|
||||
}
|
||||
|
||||
export function scanMiddleware (serverDir: string, onChange?: Function): Promise<ServerMiddleware[]> {
|
||||
const pattern = '**/*.{js,ts}'
|
||||
const globalDir = resolve(serverDir, 'middleware')
|
||||
const apiDir = resolve(serverDir, 'api')
|
||||
|
||||
const scan = async () => {
|
||||
const globalFiles = await globby(pattern, { cwd: globalDir })
|
||||
const apiFiles = await globby(pattern, { cwd: apiDir })
|
||||
return [
|
||||
...filesToMiddleware(globalFiles, globalDir, '/', { route: '/' }),
|
||||
...filesToMiddleware(apiFiles, apiDir, '/api', { lazy: true })
|
||||
]
|
||||
}
|
||||
|
||||
if (typeof onChange === 'function') {
|
||||
const watcher = watch([
|
||||
join(globalDir, pattern),
|
||||
join(apiDir, pattern)
|
||||
], { ignoreInitial: true })
|
||||
watcher.on('all', async (event, file) => {
|
||||
onChange(await scan(), event, file)
|
||||
})
|
||||
}
|
||||
|
||||
return scan()
|
||||
}
|
||||
|
||||
export function resolveMiddleware (serverMiddleware: any[], resolvePath: (string) => string) {
|
||||
const middleware: ServerMiddleware[] = []
|
||||
const legacyMiddleware: ServerMiddleware[] = []
|
||||
|
||||
for (let m of serverMiddleware) {
|
||||
if (typeof m === 'string') { m = { handler: m } }
|
||||
const route = m.path || m.route || '/'
|
||||
const handle = m.handler || m.handle
|
||||
if (typeof handle !== 'string' || typeof route !== 'string') {
|
||||
legacyMiddleware.push(m)
|
||||
} else {
|
||||
middleware.push({
|
||||
...m,
|
||||
handle: resolvePath(handle),
|
||||
route
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
middleware,
|
||||
legacyMiddleware
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user