mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat!: migrate to nitropack (#3956)
Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
parent
7d1ff39077
commit
11626eea4f
@ -95,10 +95,10 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
|
||||
### API routes
|
||||
|
||||
Within the API routes, you can access runtime config by directly importing from virtual `#config`.
|
||||
Within the API routes, you can access runtime config by directly importing from virtual `#nitro`.
|
||||
|
||||
```ts
|
||||
import config from '#config'
|
||||
import { useRuntimeConfig } from '#nitro'
|
||||
|
||||
export default async () => {
|
||||
const result = await $fetch('https://my.api.com/test', {
|
||||
|
@ -20,7 +20,7 @@ How to deploy Nuxt to Azure Static Web Apps or Azure Functions.
|
||||
```ts [nuxt.config.js|ts]
|
||||
export default {
|
||||
nitro: {
|
||||
preset: 'azure_functions'
|
||||
preset: 'azure-functions'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -21,7 +21,7 @@ Make sure another preset isn't set in `nuxt.config`.
|
||||
export default {
|
||||
nitro: {
|
||||
// this is the default preset so you can also just omit it entirely
|
||||
// preset: 'server'
|
||||
// preset: 'node-server'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -33,7 +33,7 @@ export default {
|
||||
You can also define a preset in a separate file (or publish as a separate npm package).
|
||||
|
||||
```ts [my-preset/index.ts]
|
||||
import type { NitroPreset } from '@nuxt/nitro'
|
||||
import type { NitroPreset } from 'nitropack'
|
||||
|
||||
const myPreset: NitroPreset = {
|
||||
// Your custom configuration
|
||||
|
@ -13,7 +13,7 @@ You can use the [Nuxt config](/guide/directory-structure/nuxt.config) to explici
|
||||
```ts [nuxt.config.js|ts]
|
||||
export default {
|
||||
nitro: {
|
||||
preset: 'lambda'
|
||||
preset: 'aws-lambda'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -20,7 +20,7 @@ You can use the [Nuxt config](/guide/directory-structure/nuxt.config) to explici
|
||||
```js [nuxt.config.js|ts]
|
||||
export default {
|
||||
nitro: {
|
||||
preset: 'server'
|
||||
preset: 'node-server'
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -61,7 +61,7 @@ You can enable the `nitro.timing` option to have the logs about the chunk loadin
|
||||
```js [nuxt.config.js|ts]
|
||||
export default {
|
||||
nitro: {
|
||||
preset: 'server',
|
||||
preset: 'node-server',
|
||||
timing: true
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ You can use the [Nuxt config](/guide/directory-structure/nuxt.config) to explici
|
||||
```js [nuxt.config.js|ts]
|
||||
export default {
|
||||
nitro: {
|
||||
preset: 'worker'
|
||||
'browser-worker'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -7,7 +7,7 @@ head.titleTemplate: ''
|
||||
|
||||
If you wish to reference environment variables within your Nuxt 3 app, you will need to use runtime config.
|
||||
|
||||
When referencing these variables within your components, you will have to use the `useRuntimeConfig` composable in your setup method (or Nuxt plugin). In the `server/` portion of your app, you can import directly from `#config`.
|
||||
When referencing these variables within your components, you will have to use the `useRuntimeConfig` composable in your setup method (or Nuxt plugin). In the `server/` portion of your app, you can import `useRuntimeConfig` directly from `#nitro`.
|
||||
|
||||
[Read more about runtime config](/guide/features/runtime-config).
|
||||
|
||||
@ -41,7 +41,7 @@ export default defineNuxtConfig({
|
||||
```
|
||||
|
||||
```ts [server/api/hello.ts]
|
||||
import config from '#config'
|
||||
import { useRuntimeConfig } from '#nitro';
|
||||
|
||||
export default (req, res) => {
|
||||
// you can now access config.BASE_URL
|
||||
|
@ -22,10 +22,10 @@
|
||||
"dev:build": "yarn run nuxi build playground",
|
||||
"release": "yarn && yarn lint && FORCE_COLOR=1 lerna publish -m \"chore: release\" && yarn stub",
|
||||
"stub": "lerna run prepack -- --stub",
|
||||
"test:fixtures": "yarn nuxi prepare test/fixtures/basic && JITI_ESM_RESOLVE=1 vitest --dir test",
|
||||
"test:fixtures": "yarn nuxi prepare test/fixtures/basic && JITI_ESM_RESOLVE=1 vitest run --dir test",
|
||||
"test:fixtures:webpack": "TEST_WITH_WEBPACK=1 yarn test:fixtures",
|
||||
"test:types": "yarn run nuxi prepare test/fixtures/basic && cd test/fixtures/basic && npx vue-tsc --noEmit",
|
||||
"test:unit": "JITI_ESM_RESOLVE=1 yarn vitest --dir packages",
|
||||
"test:unit": "JITI_ESM_RESOLVE=1 yarn vitest run --dir packages",
|
||||
"version": "yarn && git add yarn.lock"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@ -22,7 +22,6 @@
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||
"@babel/plugin-transform-typescript": "^7.16.8",
|
||||
"@nuxt/kit": "3.0.0",
|
||||
"@nuxt/nitro": "3.0.0",
|
||||
"@nuxt/postcss8": "^1.1.3",
|
||||
"@nuxt/schema": "3.0.0",
|
||||
"@vitejs/plugin-legacy": "^1.8.0",
|
||||
@ -38,12 +37,13 @@
|
||||
"externality": "^0.2.1",
|
||||
"fs-extra": "^10.0.1",
|
||||
"globby": "^13.1.1",
|
||||
"h3": "^0.4.2",
|
||||
"h3": "^0.7.1",
|
||||
"hash-sum": "^2.0.0",
|
||||
"knitwork": "^0.1.1",
|
||||
"magic-string": "^0.26.1",
|
||||
"mlly": "^0.5.1",
|
||||
"murmurhash-es": "^0.1.1",
|
||||
"nitropack": "^0.1.0",
|
||||
"node-fetch": "^3.2.3",
|
||||
"nuxi": "3.0.0",
|
||||
"ohash": "^0.1.0",
|
||||
@ -63,6 +63,7 @@
|
||||
"untyped": "^0.4.4",
|
||||
"vite": "^2.9.1",
|
||||
"vite-plugin-vue2": "^1.9.3",
|
||||
"vue-bundle-renderer": "^0.3.5",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useNuxt, addTemplate, resolveAlias, addWebpackPlugin, addVitePlugin } from '@nuxt/kit'
|
||||
import { useNuxt, addTemplate, resolveAlias, addWebpackPlugin, addVitePlugin, addPlugin } from '@nuxt/kit'
|
||||
import { NuxtModule } from '@nuxt/schema'
|
||||
import { resolve } from 'pathe'
|
||||
import { componentsTypeTemplate } from '../../nuxt3/src/components/templates'
|
||||
@ -94,4 +94,9 @@ export function setupAppBridge (_options: any) {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
addPlugin({
|
||||
src: resolve(distDir, 'runtime/error.plugin.server.mjs'),
|
||||
mode: 'server'
|
||||
})
|
||||
}
|
||||
|
@ -49,7 +49,9 @@ export default defineNuxtModule({
|
||||
}]
|
||||
|
||||
if (opts.nitro) {
|
||||
await setupNitroBridge()
|
||||
nuxt.hook('modules:done', async () => {
|
||||
await setupNitroBridge()
|
||||
})
|
||||
}
|
||||
if (opts.app) {
|
||||
await setupAppBridge(opts.app)
|
||||
|
@ -1,19 +1,23 @@
|
||||
import { promises as fsp } from 'fs'
|
||||
import { promises as fsp, existsSync } from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import fse from 'fs-extra'
|
||||
import { addPluginTemplate, useNuxt } from '@nuxt/kit'
|
||||
import fsExtra from 'fs-extra'
|
||||
import { addPluginTemplate, resolvePath, useNuxt } from '@nuxt/kit'
|
||||
import { joinURL, stringifyQuery, withoutTrailingSlash } from 'ufo'
|
||||
import { resolve, join } from 'pathe'
|
||||
import { build, generate, prepare, getNitroContext, NitroContext, createDevServer, wpfs, resolveMiddleware, scanMiddleware, writeTypes } from '@nuxt/nitro'
|
||||
import { createNitro, createDevServer, build, writeTypes, prepare, copyPublicAssets, prerender } from 'nitropack'
|
||||
import { dynamicEventHandler, toEventHandler } from 'h3'
|
||||
import type { Nitro, NitroEventHandler, NitroDevEventHandler, NitroConfig } from 'nitropack'
|
||||
import { Nuxt } from '@nuxt/schema'
|
||||
import defu from 'defu'
|
||||
import { AsyncLoadingPlugin } from './async-loading'
|
||||
import { distDir } from './dirs'
|
||||
import { isDirectory, readDirRecursively } from './vite/utils/fs'
|
||||
|
||||
export function setupNitroBridge () {
|
||||
export async function setupNitroBridge () {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
// Ensure we're not just building with 'static' target
|
||||
if (!nuxt.options.dev && nuxt.options.target === 'static' && !nuxt.options._prepare && !nuxt.options._export && !nuxt.options._legacyGenerate) {
|
||||
if (!nuxt.options.dev && nuxt.options.target === 'static' && !nuxt.options._prepare && !(nuxt.options as any)._export && !nuxt.options._legacyGenerate) {
|
||||
throw new Error('[nitro] Please use `nuxt generate` for static target')
|
||||
}
|
||||
|
||||
@ -21,6 +25,7 @@ export function setupNitroBridge () {
|
||||
nuxt.options.app.buildAssetsDir = nuxt.options.app.buildAssetsDir || nuxt.options.app.assetsPath
|
||||
nuxt.options.app.assetsPath = nuxt.options.app.buildAssetsDir
|
||||
nuxt.options.app.baseURL = nuxt.options.app.baseURL || (nuxt.options.app as any).basePath
|
||||
nuxt.options.app.cdnURL = nuxt.options.app.cdnURL || ''
|
||||
// Nitro expects app config on `config.app` rather than `config._app`
|
||||
nuxt.options.publicRuntimeConfig.app = nuxt.options.publicRuntimeConfig.app || {}
|
||||
Object.assign(nuxt.options.publicRuntimeConfig.app, nuxt.options.publicRuntimeConfig._app)
|
||||
@ -40,28 +45,89 @@ export function setupNitroBridge () {
|
||||
}
|
||||
}
|
||||
|
||||
// Create contexts
|
||||
const nitroOptions = (nuxt.options as any).nitro || {}
|
||||
const nitroContext = getNitroContext(nuxt.options, nitroOptions)
|
||||
const nitroDevContext = getNitroContext(nuxt.options, { ...nitroOptions, preset: 'dev' })
|
||||
// Resolve config
|
||||
const _nitroConfig = (nuxt.options as any).nitro || {} as NitroConfig
|
||||
const nitroConfig: NitroConfig = defu(_nitroConfig, <NitroConfig>{
|
||||
rootDir: resolve(nuxt.options.rootDir),
|
||||
srcDir: resolve(nuxt.options.srcDir, 'server'),
|
||||
dev: nuxt.options.dev,
|
||||
preset: nuxt.options.dev ? 'nitro-dev' : undefined,
|
||||
buildDir: resolve(nuxt.options.buildDir),
|
||||
scanDirs: nuxt.options._layers.map(layer => join(layer.config.srcDir, 'server')),
|
||||
renderer: resolve(distDir, 'runtime/nitro/renderer'),
|
||||
nodeModulesDirs: nuxt.options.modulesDir,
|
||||
handlers: [],
|
||||
devHandlers: [],
|
||||
runtimeConfig: {
|
||||
// Private
|
||||
...nuxt.options.publicRuntimeConfig,
|
||||
...nuxt.options.privateRuntimeConfig,
|
||||
// Public
|
||||
public: nuxt.options.publicRuntimeConfig,
|
||||
// Nitro
|
||||
nitro: {
|
||||
envPrefix: 'NUXT_'
|
||||
}
|
||||
},
|
||||
typescript: {
|
||||
generateTsConfig: false
|
||||
},
|
||||
publicAssets: [
|
||||
{
|
||||
baseURL: nuxt.options.app.buildAssetsDir,
|
||||
dir: resolve(nuxt.options.buildDir, 'dist/client')
|
||||
},
|
||||
...nuxt.options._layers
|
||||
.map(layer => join(layer.config.srcDir, 'public'))
|
||||
.filter(dir => existsSync(dir))
|
||||
.map(dir => ({ dir }))
|
||||
],
|
||||
prerender: {
|
||||
crawlLinks: nuxt.options.generate.crawler,
|
||||
routes: nuxt.options.generate.routes
|
||||
},
|
||||
output: {
|
||||
dir: nuxt.options.dev ? join(nuxt.options.buildDir, 'nitro') : resolve(nuxt.options.rootDir, '.output')
|
||||
},
|
||||
externals: {
|
||||
inline: nuxt.options.dev ? [] : [nuxt.options.buildDir]
|
||||
},
|
||||
alias: {
|
||||
// Vue 2 mocks
|
||||
encoding: 'unenv/runtime/mock/proxy',
|
||||
he: 'unenv/runtime/mock/proxy',
|
||||
resolve: 'unenv/runtime/mock/proxy',
|
||||
'source-map': 'unenv/runtime/mock/proxy',
|
||||
'lodash.template': 'unenv/runtime/mock/proxy',
|
||||
'serialize-javascript': 'unenv/runtime/mock/proxy',
|
||||
|
||||
// Normalize Nuxt directories
|
||||
for (const context of [nitroContext, nitroDevContext]) {
|
||||
context._nuxt.rootDir = resolve(context._nuxt.rootDir)
|
||||
context._nuxt.srcDir = resolve(context._nuxt.srcDir)
|
||||
context._nuxt.buildDir = resolve(context._nuxt.buildDir)
|
||||
context._nuxt.generateDir = resolve(context._nuxt.generateDir)
|
||||
}
|
||||
// Renderer
|
||||
'#vue-renderer': resolve(distDir, 'runtime/nitro/vue2'),
|
||||
'#vue2-server-renderer': 'vue-server-renderer/' + (nuxt.options.dev ? 'build.dev.js' : 'build.prod.js'),
|
||||
|
||||
// Error renderer
|
||||
'#nitro/error': resolve(distDir, 'runtime/nitro/error'),
|
||||
|
||||
// Paths
|
||||
'#paths': resolve(distDir, 'runtime/nitro/paths')
|
||||
}
|
||||
})
|
||||
|
||||
// Extend nitro config with hook
|
||||
await nuxt.callHook('nitro:config', nitroConfig)
|
||||
|
||||
// Initiate nitro
|
||||
const nitro = await createNitro(nitroConfig)
|
||||
|
||||
// Expose nitro to modules
|
||||
await nuxt.callHook('nitro:init', nitro)
|
||||
|
||||
// Shared vfs storage
|
||||
nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {}
|
||||
|
||||
// Connect hooks
|
||||
nuxt.addHooks(nitroContext.nuxtHooks)
|
||||
nuxt.hook('close', () => nitroContext._internal.hooks.callHook('close'))
|
||||
nitroContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
nitroContext._internal.hooks.hook('nitro:generate', ctx => nuxt.callHook('nitro:generate', ctx))
|
||||
|
||||
nuxt.addHooks(nitroDevContext.nuxtHooks)
|
||||
nuxt.hook('close', () => nitroDevContext._internal.hooks.callHook('close'))
|
||||
nitroDevContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
nuxt.hook('close', () => nitro.hooks.callHook('close'))
|
||||
nitro.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
|
||||
// Use custom document template if provided
|
||||
if (nuxt.options.appTemplatePath) {
|
||||
@ -82,7 +148,7 @@ export function setupNitroBridge () {
|
||||
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
||||
for (const file of publicFiles) {
|
||||
try {
|
||||
fse.rmSync(join(clientDist, file))
|
||||
fsExtra.rmSync(join(clientDist, file))
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
@ -93,26 +159,17 @@ export function setupNitroBridge () {
|
||||
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, nuxt.options.app.buildAssetsDir))
|
||||
|
||||
if (await isDirectory(nestedAssetsPath)) {
|
||||
await fse.copy(nestedAssetsPath, clientDist, { recursive: true })
|
||||
await fse.remove(nestedAssetsPath)
|
||||
await fsExtra.copy(nestedAssetsPath, clientDist, { recursive: true })
|
||||
await fsExtra.remove(nestedAssetsPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
nuxt.hook('nitro:generate', updateViteBase)
|
||||
nuxt.hook('generate:before', updateViteBase)
|
||||
|
||||
// Expose process.env.NITRO_PRESET
|
||||
nuxt.options.env.NITRO_PRESET = nitroContext.preset
|
||||
|
||||
// .ts is supported for serverMiddleware
|
||||
nuxt.options.extensions.push('ts')
|
||||
|
||||
// Replace nuxt server
|
||||
if (nuxt.server) {
|
||||
nuxt.server.__closed = true
|
||||
nuxt.server = createNuxt2DevServer(nitroDevContext)
|
||||
}
|
||||
|
||||
// Disable server sourceMap, esbuild will generate for it.
|
||||
nuxt.hook('webpack:config', (webpackConfigs) => {
|
||||
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
||||
@ -134,8 +191,8 @@ export function setupNitroBridge () {
|
||||
|
||||
// Nitro client plugin
|
||||
addPluginTemplate({
|
||||
filename: 'nitro.client.mjs',
|
||||
src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs')
|
||||
filename: 'nitro-bridge.client.mjs',
|
||||
src: resolve(distDir, 'runtime/nitro-bridge.client.mjs')
|
||||
})
|
||||
|
||||
// Nitro server plugin (for vue-meta)
|
||||
@ -167,21 +224,27 @@ export function setupNitroBridge () {
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for all modules to be ready
|
||||
nuxt.hook('modules:done', async () => {
|
||||
// Extend nitro with modules
|
||||
await nuxt.callHook('nitro:context', nitroContext)
|
||||
await nuxt.callHook('nitro:context', nitroDevContext)
|
||||
|
||||
// Resolve middleware
|
||||
const { middleware, legacyMiddleware } = await resolveMiddleware(nuxt)
|
||||
if (nuxt.server) {
|
||||
nuxt.server.setLegacyMiddleware(legacyMiddleware)
|
||||
}
|
||||
nitroContext.middleware.push(...middleware)
|
||||
nitroDevContext.middleware.push(...middleware)
|
||||
// Setup handlers
|
||||
const devMidlewareHandler = dynamicEventHandler()
|
||||
nitro.options.devHandlers.unshift({ handler: devMidlewareHandler })
|
||||
const { handlers, devHandlers } = await resolveHandlers(nuxt)
|
||||
nitro.options.handlers.push(...handlers)
|
||||
nitro.options.devHandlers.push(...devHandlers)
|
||||
nitro.options.handlers.unshift({
|
||||
route: '/_nitro',
|
||||
lazy: true,
|
||||
handler: resolve(distDir, 'runtime/nitro/renderer')
|
||||
})
|
||||
|
||||
// Create dev server
|
||||
if (nuxt.server) {
|
||||
nuxt.server.__closed = true
|
||||
nuxt.server = createNuxt2DevServer(nitro)
|
||||
nuxt.hook('build:resources', () => {
|
||||
nuxt.server.reload()
|
||||
})
|
||||
}
|
||||
|
||||
// Add typed route responses
|
||||
nuxt.hook('prepare:types', (opts) => {
|
||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
|
||||
@ -189,58 +252,70 @@ export function setupNitroBridge () {
|
||||
|
||||
// nuxt prepare
|
||||
nuxt.hook('build:done', async () => {
|
||||
nitroDevContext.scannedMiddleware = await scanMiddleware(nitroDevContext._nuxt.serverDir)
|
||||
await writeTypes(nitroDevContext)
|
||||
await writeTypes(nitro)
|
||||
})
|
||||
|
||||
// nuxt build/dev
|
||||
// @ts-ignore
|
||||
nuxt.options.build._minifyServer = false
|
||||
nuxt.options.build.standalone = false
|
||||
|
||||
const waitUntilCompile = new Promise<void>(resolve => nitro.hooks.hook('nitro:compiled', () => resolve()))
|
||||
nuxt.hook('build:done', async () => {
|
||||
if (nuxt.options._prepare) { return }
|
||||
if (nuxt.options.dev) {
|
||||
await build(nitroDevContext)
|
||||
} else if (!nitroContext._nuxt.isStatic) {
|
||||
await prepare(nitroContext)
|
||||
await generate(nitroContext)
|
||||
await build(nitroContext)
|
||||
await build(nitro)
|
||||
await waitUntilCompile
|
||||
// nitro.hooks.callHook('nitro:dev:reload')
|
||||
} else {
|
||||
await prepare(nitro)
|
||||
await copyPublicAssets(nitro)
|
||||
if (nuxt.options.target === 'static') {
|
||||
await prerender(nitro)
|
||||
}
|
||||
await build(nitro)
|
||||
if (nuxt.options._generate) {
|
||||
await prerender(nitro)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// nuxt dev
|
||||
if (nuxt.options.dev) {
|
||||
nitroDevContext._internal.hooks.hook('nitro:compiled', () => { nuxt.server.watch() })
|
||||
nuxt.hook('build:compile', ({ compiler }) => { compiler.outputFileSystem = wpfs })
|
||||
nuxt.hook('server:devMiddleware', (m) => { nuxt.server.setDevMiddleware(m) })
|
||||
nuxt.hook('build:compile', ({ compiler }) => {
|
||||
compiler.outputFileSystem = { ...fsExtra, join } as any
|
||||
})
|
||||
nuxt.hook('server:devMiddleware', (m) => { devMidlewareHandler.set(toEventHandler(m)) })
|
||||
}
|
||||
|
||||
// nuxt generate
|
||||
nuxt.options.generate.dir = nitroContext.output.publicDir
|
||||
nuxt.options.generate.dir = nitro.options.output.publicDir
|
||||
nuxt.options.generate.manifest = false
|
||||
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
||||
ignore.push(nitroContext.output.dir)
|
||||
ignore.push(nitroContext.output.serverDir)
|
||||
if (nitroContext.output.publicDir) {
|
||||
ignore.push(nitroContext.output.publicDir)
|
||||
ignore.push(nitro.options.output.dir)
|
||||
ignore.push(nitro.options.output.serverDir)
|
||||
if (nitro.options.output.publicDir) {
|
||||
ignore.push(nitro.options.output.publicDir)
|
||||
}
|
||||
ignore.push(...nitroContext.ignore)
|
||||
})
|
||||
nuxt.hook('generate:before', async () => {
|
||||
await prepare(nitroContext)
|
||||
console.log('generate:before')
|
||||
await prepare(nitro)
|
||||
})
|
||||
nuxt.hook('generate:extendRoutes', async () => {
|
||||
await build(nitroDevContext)
|
||||
console.log('generate:extendRoutes')
|
||||
await build(nitro)
|
||||
await nuxt.server.reload()
|
||||
})
|
||||
nuxt.hook('generate:done', async () => {
|
||||
console.log('generate:done')
|
||||
await nuxt.server.close()
|
||||
await build(nitroContext)
|
||||
await build(nitro)
|
||||
})
|
||||
}
|
||||
|
||||
function createNuxt2DevServer (nitroContext: NitroContext) {
|
||||
const server = createDevServer(nitroContext)
|
||||
function createNuxt2DevServer (nitro: Nitro) {
|
||||
const server = createDevServer(nitro)
|
||||
|
||||
const listeners = []
|
||||
async function listen (port) {
|
||||
@ -277,3 +352,30 @@ function createNuxt2DevServer (nitroContext: NitroContext) {
|
||||
ready () { }
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveHandlers (nuxt: Nuxt) {
|
||||
const handlers: NitroEventHandler[] = []
|
||||
const devHandlers: NitroDevEventHandler[] = []
|
||||
|
||||
for (let m of nuxt.options.serverMiddleware) {
|
||||
if (typeof m === 'string' || typeof m === 'function' /* legacy middleware */) { m = { handler: m } }
|
||||
const route = m.path || m.route || '/'
|
||||
const handler = m.handler || m.handle
|
||||
if (typeof handler !== 'string' || typeof route !== 'string') {
|
||||
devHandlers.push({ route, handler })
|
||||
} else {
|
||||
delete m.handler
|
||||
delete m.path
|
||||
handlers.push({
|
||||
...m,
|
||||
route,
|
||||
handler: await resolvePath(handler)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handlers,
|
||||
devHandlers
|
||||
}
|
||||
}
|
||||
|
5
packages/bridge/src/runtime/error.plugin.server.mjs
Normal file
5
packages/bridge/src/runtime/error.plugin.server.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
export default (ctx) => {
|
||||
if (ctx.ssrContext.error) {
|
||||
ctx.error(ctx.ssrContext.error)
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
../../../nuxt3/src/head/runtime/
|
||||
../../../nuxt3/src/head/runtime
|
1
packages/bridge/src/runtime/nitro
Symbolic link
1
packages/bridge/src/runtime/nitro
Symbolic link
@ -0,0 +1 @@
|
||||
../../../nuxt3/src/core/runtime/nitro
|
@ -11,7 +11,7 @@ import { ViteBuildContext, ViteOptions } from './types'
|
||||
|
||||
export async function buildClient (ctx: ViteBuildContext) {
|
||||
const alias = {
|
||||
'#_config': resolve(ctx.nuxt.options.buildDir, 'config.client.mjs')
|
||||
'#nitro': resolve(ctx.nuxt.options.buildDir, 'nitro.client.mjs')
|
||||
}
|
||||
for (const p of ctx.builder.plugins) {
|
||||
alias[p.name] = p.mode === 'server'
|
||||
|
@ -58,8 +58,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
ssr: ctx.nuxt.options.ssr ?? true,
|
||||
ssrManifest: true,
|
||||
rollupOptions: {
|
||||
// Private nitro alias: packages/nitro/src/rollup/config.ts#L234
|
||||
external: ['#_config'],
|
||||
external: ['#nitro'],
|
||||
input: resolve(ctx.nuxt.options.buildDir, 'server.js'),
|
||||
output: {
|
||||
entryFileNames: 'server.mjs',
|
||||
|
2
packages/bridge/types.d.ts
vendored
2
packages/bridge/types.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import type {} from '@nuxt/nitro'
|
||||
/// <reference types="nitropack" />
|
||||
import type { NuxtConfig as _NuxtConfig } from '@nuxt/schema'
|
||||
import type { MetaInfo } from 'vue-meta'
|
||||
import type { PluginOptions as ScriptSetupPluginOptions } from 'unplugin-vue2-script-setup/dist'
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Nitro
|
||||
|
||||
**Notice:** This package is being deprecated. Read more: <https://github.com/nuxt/framework/issues/3161>
|
@ -1,24 +0,0 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
declaration: true,
|
||||
entries: [
|
||||
'src/index',
|
||||
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' },
|
||||
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'cjs', declaration: false }
|
||||
],
|
||||
dependencies: [
|
||||
'@cloudflare/kv-asset-handler',
|
||||
'@netlify/functions',
|
||||
'@nuxt/devalue',
|
||||
'connect',
|
||||
'destr',
|
||||
'ohmyfetch',
|
||||
'ora',
|
||||
'vue-bundle-renderer',
|
||||
'vue-server-renderer'
|
||||
],
|
||||
externals: [
|
||||
'@nuxt/schema'
|
||||
]
|
||||
})
|
14
packages/nitro/index.d.ts
vendored
14
packages/nitro/index.d.ts
vendored
@ -1,14 +0,0 @@
|
||||
declare module '#build/dist/server/client.manifest.mjs' {
|
||||
type ClientManifest = any // TODO: export from vue-bundle-renderer
|
||||
const clientManifest: ClientManifest
|
||||
export default clientManifest
|
||||
}
|
||||
|
||||
declare module '#build/dist/server/server.mjs' {
|
||||
const _default: any
|
||||
export default _default
|
||||
}
|
||||
|
||||
declare module '#nitro-renderer' {
|
||||
export const renderToString: Function
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
{
|
||||
"name": "@nuxt/nitro",
|
||||
"version": "3.0.0",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/index.mjs",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"types"
|
||||
],
|
||||
"scripts": {
|
||||
"prepack": "unbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudflare/kv-asset-handler": "^0.2.0",
|
||||
"@netlify/functions": "^1.0.0",
|
||||
"@nuxt/devalue": "^2.0.0",
|
||||
"@nuxt/kit": "3.0.0",
|
||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
||||
"@rollup/plugin-alias": "^3.1.9",
|
||||
"@rollup/plugin-commonjs": "^21.0.3",
|
||||
"@rollup/plugin-inject": "^4.0.4",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-virtual": "^2.1.0",
|
||||
"@rollup/plugin-wasm": "^5.1.2",
|
||||
"@rollup/pluginutils": "^4.2.0",
|
||||
"@types/jsdom": "^16.2.14",
|
||||
"@vercel/nft": "^0.18.0",
|
||||
"archiver": "^5.3.0",
|
||||
"chalk": "^5.0.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"connect": "^3.7.0",
|
||||
"consola": "^2.15.3",
|
||||
"defu": "^6.0.0",
|
||||
"destr": "^1.1.0",
|
||||
"dot-prop": "^7.2.0",
|
||||
"esbuild": "^0.14.34",
|
||||
"etag": "^1.8.1",
|
||||
"fs-extra": "^10.0.1",
|
||||
"globby": "^13.1.1",
|
||||
"gzip-size": "^7.0.0",
|
||||
"h3": "^0.4.2",
|
||||
"hasha": "^5.2.2",
|
||||
"hookable": "^5.1.1",
|
||||
"http-proxy": "^1.18.1",
|
||||
"is-primitive": "^3.0.1",
|
||||
"jiti": "^1.13.0",
|
||||
"knitwork": "^0.1.1",
|
||||
"listhen": "^0.2.6",
|
||||
"mime": "^3.0.0",
|
||||
"mlly": "^0.5.1",
|
||||
"node-fetch": "^3.2.3",
|
||||
"ohmyfetch": "^0.4.15",
|
||||
"ora": "^6.1.0",
|
||||
"pathe": "^0.2.0",
|
||||
"perfect-debounce": "^0.1.3",
|
||||
"pkg-types": "^0.3.2",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.6.0",
|
||||
"serve-placeholder": "^1.2.4",
|
||||
"serve-static": "^1.15.0",
|
||||
"std-env": "^3.0.1",
|
||||
"table": "^6.8.0",
|
||||
"ufo": "^0.8.3",
|
||||
"unenv": "^0.4.5",
|
||||
"unstorage": "^0.3.3",
|
||||
"vue-bundle-renderer": "^0.3.5",
|
||||
"vue-server-renderer": "^2.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/schema": "3.0.0",
|
||||
"@types/aws-lambda": "^8.10.93",
|
||||
"@types/etag": "^1.8.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/http-proxy": "^1.17.8",
|
||||
"@types/mime": "^2.0.3",
|
||||
"@types/node-fetch": "^3.0.2",
|
||||
"@types/serve-static": "^1.13.10",
|
||||
"unbuild": "latest",
|
||||
"vue": "3.2.31"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
import { relative, resolve, join } from 'pathe'
|
||||
import { logger } from '@nuxt/kit'
|
||||
import * as rollup from 'rollup'
|
||||
import fse from 'fs-extra'
|
||||
import { genDynamicImport } from 'knitwork'
|
||||
import { printFSTree } from './utils/tree'
|
||||
import { getRollupConfig } from './rollup/config'
|
||||
import { hl, prettyPath, serializeTemplate, writeFile, isDirectory, replaceAll } from './utils'
|
||||
import { NitroContext } from './context'
|
||||
import { scanMiddleware } from './server/middleware'
|
||||
|
||||
export async function prepare (nitroContext: NitroContext) {
|
||||
logger.info(`Nitro preset is ${hl(nitroContext.preset)}`)
|
||||
|
||||
await cleanupDir(nitroContext.output.dir)
|
||||
|
||||
if (!nitroContext.output.publicDir.startsWith(nitroContext.output.dir)) {
|
||||
await cleanupDir(nitroContext.output.publicDir)
|
||||
}
|
||||
|
||||
if (!nitroContext.output.serverDir.startsWith(nitroContext.output.dir)) {
|
||||
await cleanupDir(nitroContext.output.serverDir)
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupDir (dir: string) {
|
||||
logger.info('Cleaning up', prettyPath(dir))
|
||||
await fse.emptyDir(dir)
|
||||
}
|
||||
|
||||
export async function generate (nitroContext: NitroContext) {
|
||||
logger.start('Generating public...')
|
||||
|
||||
await nitroContext._internal.hooks.callHook('nitro:generate', nitroContext)
|
||||
|
||||
const publicDir = nitroContext._nuxt.publicDir
|
||||
if (await isDirectory(publicDir)) {
|
||||
await fse.copy(publicDir, nitroContext.output.publicDir)
|
||||
}
|
||||
|
||||
const clientDist = resolve(nitroContext._nuxt.buildDir, 'dist/client')
|
||||
if (await isDirectory(clientDist)) {
|
||||
const buildAssetsDir = join(nitroContext.output.publicDir, nitroContext._nuxt.buildAssetsDir)
|
||||
await fse.copy(clientDist, buildAssetsDir)
|
||||
}
|
||||
|
||||
logger.success('Generated public ' + prettyPath(nitroContext.output.publicDir))
|
||||
}
|
||||
|
||||
export async function build (nitroContext: NitroContext) {
|
||||
// Compile html template
|
||||
const htmlSrc = resolve(nitroContext._nuxt.buildDir, `views/${{ 2: 'app', 3: 'document' }[2]}.template.html`)
|
||||
const htmlTemplate = { src: htmlSrc, contents: '', dst: '' }
|
||||
htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.mjs').replace('app.template.mjs', 'document.template.mjs')
|
||||
htmlTemplate.contents = nitroContext.vfs[htmlTemplate.src] || await fse.readFile(htmlTemplate.src, 'utf-8')
|
||||
await nitroContext._internal.hooks.callHook('nitro:document', htmlTemplate)
|
||||
const compiled = 'export default ' + serializeTemplate(htmlTemplate.contents)
|
||||
await writeFile(htmlTemplate.dst, compiled)
|
||||
|
||||
nitroContext.rollupConfig = getRollupConfig(nitroContext)
|
||||
await nitroContext._internal.hooks.callHook('nitro:rollup:before', nitroContext)
|
||||
return nitroContext._nuxt.dev ? _watch(nitroContext) : _build(nitroContext)
|
||||
}
|
||||
|
||||
export async function writeTypes (nitroContext: NitroContext) {
|
||||
const routeTypes: Record<string, string[]> = {}
|
||||
|
||||
const middleware = [
|
||||
...nitroContext.scannedMiddleware,
|
||||
...nitroContext.middleware
|
||||
]
|
||||
|
||||
for (const mw of middleware) {
|
||||
if (typeof mw.handle !== 'string') { continue }
|
||||
const relativePath = relative(join(nitroContext._nuxt.buildDir, 'types'), mw.handle).replace(/\.[a-z]+$/, '')
|
||||
routeTypes[mw.route] = routeTypes[mw.route] || []
|
||||
routeTypes[mw.route].push(`Awaited<ReturnType<typeof ${genDynamicImport(relativePath, { wrapper: false })}.default>>`)
|
||||
}
|
||||
|
||||
const lines = [
|
||||
'// Generated by nitro',
|
||||
'declare module \'@nuxt/nitro\' {',
|
||||
' type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T',
|
||||
' interface InternalApi {',
|
||||
...Object.entries(routeTypes).map(([path, types]) => ` '${path}': ${types.join(' | ')}`),
|
||||
' }',
|
||||
'}',
|
||||
// Makes this a module for augmentation purposes
|
||||
'export {}'
|
||||
]
|
||||
|
||||
await writeFile(join(nitroContext._nuxt.buildDir, 'types/nitro.d.ts'), lines.join('\n'))
|
||||
}
|
||||
|
||||
async function _build (nitroContext: NitroContext) {
|
||||
const serverDirs = nitroContext._layers.map(layer => layer.serverDir)
|
||||
|
||||
nitroContext.scannedMiddleware = (
|
||||
await Promise.all(serverDirs.map(async dir => await scanMiddleware(dir)))
|
||||
).flat()
|
||||
|
||||
await writeTypes(nitroContext)
|
||||
|
||||
logger.start('Building server...')
|
||||
const build = await rollup.rollup(nitroContext.rollupConfig).catch((error) => {
|
||||
logger.error('Rollup error: ' + error.message)
|
||||
throw error
|
||||
})
|
||||
|
||||
logger.start('Writing server bundle...')
|
||||
await build.write(nitroContext.rollupConfig.output)
|
||||
|
||||
const rewriteBuildPaths = (input: unknown, to: string) =>
|
||||
typeof input === 'string' ? replaceAll(input, nitroContext.output.dir, to) : undefined
|
||||
|
||||
// Write build info
|
||||
const nitroConfigPath = resolve(nitroContext.output.dir, 'nitro.json')
|
||||
const buildInfo = {
|
||||
date: new Date(),
|
||||
preset: nitroContext.preset,
|
||||
commands: {
|
||||
preview: rewriteBuildPaths(nitroContext.commands.preview, '.'),
|
||||
deploy: rewriteBuildPaths(nitroContext.commands.deploy, '.')
|
||||
}
|
||||
}
|
||||
await writeFile(nitroConfigPath, JSON.stringify(buildInfo, null, 2))
|
||||
|
||||
logger.success('Server built')
|
||||
await printFSTree(nitroContext.output.serverDir)
|
||||
await nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
|
||||
|
||||
// Show deploy and preview hints
|
||||
const rOutDir = relative(process.cwd(), nitroContext.output.dir)
|
||||
if (nitroContext.commands.preview) {
|
||||
// consola.info(`You can preview this build using \`${rewriteBuildPaths(nitroContext.commands.preview, rOutDir)}\``)
|
||||
logger.info('You can preview this build using `nuxi preview`')
|
||||
}
|
||||
if (nitroContext.commands.deploy) {
|
||||
logger.info(`You can deploy this build using \`${rewriteBuildPaths(nitroContext.commands.deploy, rOutDir)}\``)
|
||||
}
|
||||
|
||||
return {
|
||||
entry: resolve(nitroContext.rollupConfig.output.dir, nitroContext.rollupConfig.output.entryFileNames as string)
|
||||
}
|
||||
}
|
||||
|
||||
function startRollupWatcher (nitroContext: NitroContext) {
|
||||
const watcher = rollup.watch(nitroContext.rollupConfig)
|
||||
let start: number
|
||||
|
||||
watcher.on('event', (event) => {
|
||||
switch (event.code) {
|
||||
// The watcher is (re)starting
|
||||
case 'START':
|
||||
return
|
||||
|
||||
// Building an individual bundle
|
||||
case 'BUNDLE_START':
|
||||
start = Date.now()
|
||||
return
|
||||
|
||||
// Finished building all bundles
|
||||
case 'END':
|
||||
nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
|
||||
logger.success('Nitro built', start ? `in ${Date.now() - start} ms` : '')
|
||||
return
|
||||
|
||||
// Encountered an error while bundling
|
||||
case 'ERROR':
|
||||
logger.error('Rollup error: ' + event.error)
|
||||
// consola.error(event.error)
|
||||
}
|
||||
})
|
||||
return watcher
|
||||
}
|
||||
|
||||
async function _watch (nitroContext: NitroContext) {
|
||||
let watcher = startRollupWatcher(nitroContext)
|
||||
|
||||
const serverDirs = nitroContext._layers.map(layer => layer.serverDir)
|
||||
|
||||
nitroContext.scannedMiddleware = (
|
||||
await Promise.all(serverDirs.map(async dir => await scanMiddleware(dir,
|
||||
(middleware, event) => {
|
||||
nitroContext.scannedMiddleware = middleware
|
||||
if (['add', 'addDir'].includes(event)) {
|
||||
watcher.close()
|
||||
writeTypes(nitroContext).catch(console.error)
|
||||
watcher = startRollupWatcher(nitroContext)
|
||||
}
|
||||
}
|
||||
)))
|
||||
).flat()
|
||||
|
||||
await writeTypes(nitroContext)
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { resolve } from 'pathe'
|
||||
import defu from 'defu'
|
||||
import { createHooks, Hookable, NestedHooks } from 'hookable'
|
||||
import type { Preset } from 'unenv'
|
||||
import type { NuxtHooks, NuxtOptions } from '@nuxt/schema'
|
||||
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
|
||||
import { tryImport, resolvePath, detectTarget, extendPreset, evalTemplate } from './utils'
|
||||
import * as PRESETS from './presets'
|
||||
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
||||
import type { StorageOptions } from './rollup/plugins/storage'
|
||||
import type { AssetOptions } from './rollup/plugins/assets'
|
||||
import type { ServerMiddleware } from './server/middleware'
|
||||
import type { RollupConfig } from './rollup/config'
|
||||
import type { Options as EsbuildOptions } from './rollup/plugins/esbuild'
|
||||
import { runtimeDir } from './dirs'
|
||||
|
||||
export interface NitroHooks {
|
||||
'nitro:document': (htmlTemplate: { src: string, contents: string, dst: string }) => void
|
||||
'nitro:rollup:before': (context: NitroContext) => void | Promise<void>
|
||||
'nitro:compiled': (context: NitroContext) => void
|
||||
'nitro:generate': (context: NitroContext) => void | Promise<void>
|
||||
'close': () => void
|
||||
}
|
||||
|
||||
export interface NitroContext {
|
||||
alias: Record<string, string>
|
||||
timing: boolean
|
||||
inlineDynamicImports: boolean
|
||||
minify: boolean
|
||||
sourceMap: boolean
|
||||
externals: boolean | NodeExternalsOptions
|
||||
analyze: false | PluginVisualizerOptions
|
||||
entry: string
|
||||
node: boolean
|
||||
preset: string
|
||||
rollupConfig?: RollupConfig
|
||||
esbuild?: {
|
||||
options?: EsbuildOptions
|
||||
}
|
||||
experiments?: {
|
||||
wasm?: boolean
|
||||
}
|
||||
commands: {
|
||||
preview: string | ((config: NitroContext) => string)
|
||||
deploy: string | ((config: NitroContext) => string)
|
||||
},
|
||||
moduleSideEffects: string[]
|
||||
renderer: string
|
||||
serveStatic: boolean
|
||||
middleware: ServerMiddleware[]
|
||||
scannedMiddleware: ServerMiddleware[]
|
||||
hooks: NestedHooks<NitroHooks>
|
||||
nuxtHooks: NestedHooks<NuxtHooks>
|
||||
ignore: string[]
|
||||
env: Preset
|
||||
vfs: Record<string, string>
|
||||
output: {
|
||||
dir: string
|
||||
serverDir: string
|
||||
publicDir: string
|
||||
}
|
||||
storage: StorageOptions,
|
||||
assets: AssetOptions,
|
||||
_nuxt: {
|
||||
majorVersion: number
|
||||
dev: boolean
|
||||
ssr: boolean
|
||||
rootDir: string
|
||||
srcDir: string
|
||||
buildDir: string
|
||||
generateDir: string
|
||||
publicDir: string
|
||||
serverDir: string
|
||||
baseURL: string
|
||||
buildAssetsDir: string
|
||||
isStatic: boolean
|
||||
fullStatic: boolean
|
||||
staticAssets: any
|
||||
modulesDir: string[]
|
||||
runtimeConfig: { public: any, private: any }
|
||||
}
|
||||
_internal: {
|
||||
runtimeDir: string
|
||||
hooks: Hookable<NitroHooks>
|
||||
},
|
||||
_layers: Array<{
|
||||
serverDir: string
|
||||
}>
|
||||
}
|
||||
|
||||
type DeepPartial<T> = T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> | T[P] } : T
|
||||
|
||||
export interface NitroInput extends DeepPartial<NitroContext> {}
|
||||
|
||||
export type NitroPreset = NitroInput | ((input: NitroInput) => NitroInput)
|
||||
|
||||
export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): NitroContext {
|
||||
const defaults: NitroContext = {
|
||||
alias: {},
|
||||
timing: undefined,
|
||||
inlineDynamicImports: undefined,
|
||||
minify: undefined,
|
||||
sourceMap: undefined,
|
||||
externals: undefined,
|
||||
analyze: nuxtOptions.build.analyze as any,
|
||||
entry: undefined,
|
||||
node: undefined,
|
||||
preset: undefined,
|
||||
rollupConfig: undefined,
|
||||
experiments: {},
|
||||
moduleSideEffects: ['unenv/runtime/polyfill/'],
|
||||
renderer: undefined,
|
||||
serveStatic: undefined,
|
||||
commands: {
|
||||
preview: undefined,
|
||||
deploy: undefined
|
||||
},
|
||||
middleware: [],
|
||||
scannedMiddleware: [],
|
||||
ignore: [],
|
||||
env: {},
|
||||
vfs: {},
|
||||
hooks: {},
|
||||
nuxtHooks: {},
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.output',
|
||||
serverDir: '{{ output.dir }}/server',
|
||||
publicDir: '{{ output.dir }}/public'
|
||||
},
|
||||
storage: { mounts: { } },
|
||||
assets: {
|
||||
inline: !nuxtOptions.dev,
|
||||
dirs: {}
|
||||
},
|
||||
_nuxt: {
|
||||
majorVersion: nuxtOptions._majorVersion || 2,
|
||||
dev: nuxtOptions.dev,
|
||||
ssr: nuxtOptions.ssr,
|
||||
rootDir: nuxtOptions.rootDir,
|
||||
srcDir: nuxtOptions.srcDir,
|
||||
buildDir: nuxtOptions.buildDir,
|
||||
generateDir: nuxtOptions.generate.dir,
|
||||
publicDir: resolve(nuxtOptions.srcDir, nuxtOptions.dir.public || nuxtOptions.dir.static),
|
||||
serverDir: resolve(nuxtOptions.srcDir, (nuxtOptions.dir as any).server || 'server'),
|
||||
baseURL: nuxtOptions.app.baseURL || '/',
|
||||
buildAssetsDir: nuxtOptions.app.buildAssetsDir,
|
||||
isStatic: nuxtOptions.target === 'static' && !nuxtOptions.dev,
|
||||
fullStatic: nuxtOptions.target === 'static' && !nuxtOptions._legacyGenerate,
|
||||
staticAssets: nuxtOptions.generate.staticAssets,
|
||||
modulesDir: nuxtOptions.modulesDir,
|
||||
runtimeConfig: {
|
||||
public: nuxtOptions.publicRuntimeConfig,
|
||||
private: nuxtOptions.privateRuntimeConfig
|
||||
}
|
||||
},
|
||||
_internal: {
|
||||
runtimeDir,
|
||||
hooks: createHooks<NitroHooks>()
|
||||
},
|
||||
_layers: nuxtOptions._layers.map(layer => ({
|
||||
serverDir: resolve(layer.config.srcDir, (layer.config.dir as any)?.server || 'server')
|
||||
}))
|
||||
}
|
||||
|
||||
defaults.preset = input.preset || process.env.NITRO_PRESET || detectTarget() || 'server'
|
||||
// eslint-disable-next-line import/namespace
|
||||
let presetDefaults = PRESETS[defaults.preset] || tryImport(nuxtOptions.rootDir, defaults.preset)
|
||||
if (!presetDefaults) {
|
||||
throw new Error('Cannot resolve preset: ' + defaults.preset)
|
||||
}
|
||||
presetDefaults = presetDefaults.default || presetDefaults
|
||||
|
||||
const _presetInput = defu(input, defaults)
|
||||
const _preset = (extendPreset(presetDefaults /* base */, input) as Function)(_presetInput)
|
||||
const nitroContext: NitroContext = defu(_preset, defaults) as any
|
||||
|
||||
nitroContext.output.dir = resolvePath(nitroContext, nitroContext.output.dir)
|
||||
nitroContext.output.publicDir = resolvePath(nitroContext, nitroContext.output.publicDir)
|
||||
nitroContext.output.serverDir = resolvePath(nitroContext, nitroContext.output.serverDir)
|
||||
|
||||
if (nitroContext.commands.preview) {
|
||||
nitroContext.commands.preview = evalTemplate(nitroContext, nitroContext.commands.preview)
|
||||
}
|
||||
if (nitroContext.commands.deploy) {
|
||||
nitroContext.commands.deploy = evalTemplate(nitroContext, nitroContext.commands.deploy)
|
||||
}
|
||||
|
||||
nitroContext._internal.hooks.addHooks(nitroContext.hooks)
|
||||
|
||||
// Dev-only storage
|
||||
if (nitroContext._nuxt.dev) {
|
||||
const fsMounts = {
|
||||
root: resolve(nitroContext._nuxt.rootDir),
|
||||
src: resolve(nitroContext._nuxt.srcDir),
|
||||
build: resolve(nitroContext._nuxt.buildDir),
|
||||
cache: resolve(nitroContext._nuxt.rootDir, '.nuxt/nitro/cache')
|
||||
}
|
||||
for (const p in fsMounts) {
|
||||
nitroContext.storage.mounts[p] = nitroContext.storage.mounts[p] || {
|
||||
driver: 'fs',
|
||||
driverOptions: { base: fsMounts[p] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assets
|
||||
nitroContext.assets.dirs.server = {
|
||||
dir: resolve(nitroContext._nuxt.srcDir, 'server/assets'), meta: true
|
||||
}
|
||||
|
||||
// console.log(nitroContext)
|
||||
// process.exit(1)
|
||||
|
||||
return nitroContext
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
|
||||
export const distDir = dirname(fileURLToPath(import.meta.url))
|
||||
export const pkgDir = resolve(distDir, '..')
|
||||
export const runtimeDir = resolve(distDir, 'runtime')
|
@ -1,5 +0,0 @@
|
||||
export * from './build'
|
||||
export * from './context'
|
||||
export * from './server/middleware'
|
||||
export * from './server/dev'
|
||||
export { wpfs } from './utils/wpfs'
|
@ -1,106 +0,0 @@
|
||||
import fse from 'fs-extra'
|
||||
import { globby } from 'globby'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { writeFile } from '../utils'
|
||||
import { NitroPreset, NitroContext } from '../context'
|
||||
|
||||
export const azure: NitroPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/azure',
|
||||
externals: true,
|
||||
output: {
|
||||
serverDir: '{{ output.dir }}/server/functions'
|
||||
},
|
||||
commands: {
|
||||
preview: 'npx @azure/static-web-apps-cli start {{ output.publicDir }} --api-location {{ output.serverDir }}/..'
|
||||
},
|
||||
hooks: {
|
||||
async 'nitro:compiled' (ctx: NitroContext) {
|
||||
await writeRoutes(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function writeRoutes ({ output }: NitroContext) {
|
||||
const host = {
|
||||
version: '2.0'
|
||||
}
|
||||
|
||||
const config = {
|
||||
routes: [],
|
||||
navigationFallback: {
|
||||
rewrite: '/api/server'
|
||||
}
|
||||
}
|
||||
|
||||
const indexPath = resolve(output.publicDir, 'index.html')
|
||||
const indexFileExists = fse.existsSync(indexPath)
|
||||
if (!indexFileExists) {
|
||||
config.routes.unshift(
|
||||
{
|
||||
route: '/index.html',
|
||||
redirect: '/'
|
||||
},
|
||||
{
|
||||
route: '/',
|
||||
rewrite: '/api/server'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const folderFiles = await globby([
|
||||
join(output.publicDir, 'index.html'),
|
||||
join(output.publicDir, '**/index.html')
|
||||
])
|
||||
const prefix = output.publicDir.length
|
||||
const suffix = '/index.html'.length
|
||||
folderFiles.forEach(file =>
|
||||
config.routes.unshift({
|
||||
route: file.slice(prefix, -suffix) || '/',
|
||||
rewrite: file.slice(prefix)
|
||||
})
|
||||
)
|
||||
|
||||
const otherFiles = await globby([join(output.publicDir, '**/*.html'), join(output.publicDir, '*.html')])
|
||||
otherFiles.forEach((file) => {
|
||||
if (file.endsWith('index.html')) {
|
||||
return
|
||||
}
|
||||
const route = file.slice(prefix, '.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)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
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(output.serverDir, 'function.json'), JSON.stringify(functionDefinition))
|
||||
await writeFile(resolve(output.serverDir, '../host.json'), JSON.stringify(host))
|
||||
await writeFile(resolve(output.publicDir, 'staticwebapp.config.json'), JSON.stringify(config))
|
||||
if (!indexFileExists) {
|
||||
await writeFile(indexPath, '')
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import { createWriteStream } from 'fs'
|
||||
import archiver from 'archiver'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { writeFile } from '../utils'
|
||||
import { NitroPreset, NitroContext } from '../context'
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const azure_functions: NitroPreset = {
|
||||
serveStatic: true,
|
||||
entry: '{{ _internal.runtimeDir }}/entries/azure_functions',
|
||||
externals: true,
|
||||
commands: {
|
||||
deploy: 'az functionapp deployment source config-zip -g <resource-group> -n <app-name> --src {{ output.dir }}/deploy.zip'
|
||||
},
|
||||
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'))
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'fs'
|
||||
import { resolve } from 'pathe'
|
||||
import { logger } from '@nuxt/kit'
|
||||
import { joinURL } from 'ufo'
|
||||
import { genString } from 'knitwork'
|
||||
import { extendPreset, prettyPath } from '../utils'
|
||||
import { NitroPreset, NitroContext, NitroInput } from '../context'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const browser: NitroPreset = extendPreset(worker, (input: NitroInput) => {
|
||||
// TODO: Join base at runtime
|
||||
const baseURL = input._nuxt.baseURL
|
||||
|
||||
const script = `<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register(${genString(joinURL(baseURL, 'sw.js'))});
|
||||
});
|
||||
}
|
||||
</script>`
|
||||
|
||||
// TEMP FIX
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="prefetch" href="${joinURL(baseURL, 'sw.js')}">
|
||||
<link rel="prefetch" href="${joinURL(baseURL, '_server/index.mjs')}">
|
||||
<script>
|
||||
async function register () {
|
||||
const registration = await navigator.serviceWorker.register(${genString(joinURL(baseURL, 'sw.js'))})
|
||||
await navigator.serviceWorker.ready
|
||||
registration.active.addEventListener('statechange', (event) => {
|
||||
if (event.target.state === 'activated') {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (location.hostname !== 'localhost' && location.protocol === 'http:') {
|
||||
location.replace(location.href.replace('http://', 'https://'))
|
||||
} else {
|
||||
register()
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
Loading...
|
||||
</body>
|
||||
|
||||
</html>`
|
||||
|
||||
return <NitroInput> {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/service-worker',
|
||||
output: {
|
||||
serverDir: '{{ output.dir }}/public/_server'
|
||||
},
|
||||
nuxtHooks: {
|
||||
'generate:page' (page) {
|
||||
page.html = page.html.replace('</body>', script + '</body>')
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
'nitro:document' (tmpl) {
|
||||
tmpl.contents = tmpl.contents.replace('</body>', script + '</body>')
|
||||
},
|
||||
async 'nitro:compiled' ({ output }: NitroContext) {
|
||||
await fsp.writeFile(resolve(output.publicDir, 'sw.js'), `self.importScripts(${genString(joinURL(baseURL, '_server/index.mjs'))});`, 'utf8')
|
||||
|
||||
// Temp fix
|
||||
if (!existsSync(resolve(output.publicDir, 'index.html'))) {
|
||||
await fsp.writeFile(resolve(output.publicDir, 'index.html'), html, 'utf8')
|
||||
}
|
||||
if (!existsSync(resolve(output.publicDir, '200.html'))) {
|
||||
await fsp.writeFile(resolve(output.publicDir, '200.html'), html, 'utf8')
|
||||
}
|
||||
if (!existsSync(resolve(output.publicDir, '404.html'))) {
|
||||
await fsp.writeFile(resolve(output.publicDir, '404.html'), html, 'utf8')
|
||||
}
|
||||
logger.info('Ready to deploy to static hosting:', prettyPath(output.publicDir as string))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -1,13 +0,0 @@
|
||||
import consola from 'consola'
|
||||
import { extendPreset, prettyPath } from '../utils'
|
||||
import { NitroPreset, NitroContext } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const cli: NitroPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/cli',
|
||||
hooks: {
|
||||
'nitro:compiled' ({ output }: NitroContext) {
|
||||
consola.info('Run with `node ' + prettyPath(output.serverDir) + ' [route]`')
|
||||
}
|
||||
}
|
||||
})
|
@ -1,21 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { extendPreset, writeFile } from '../utils'
|
||||
import { NitroContext, NitroPreset } from '../context'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const cloudflare: NitroPreset = extendPreset(worker, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/cloudflare',
|
||||
ignore: [
|
||||
'wrangler.toml'
|
||||
],
|
||||
commands: {
|
||||
preview: 'npx miniflare {{ output.serverDir }}/index.mjs --site {{ output.publicDir }}',
|
||||
deploy: 'cd {{ output.serverDir }} && npx wrangler publish'
|
||||
},
|
||||
hooks: {
|
||||
async 'nitro:compiled' ({ output, _nuxt }: NitroContext) {
|
||||
await writeFile(resolve(output.dir, 'package.json'), JSON.stringify({ private: true, main: './server/index.mjs' }, null, 2))
|
||||
await writeFile(resolve(output.dir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
|
||||
}
|
||||
}
|
||||
})
|
@ -1,13 +0,0 @@
|
||||
import { extendPreset } from '../utils'
|
||||
import { NitroPreset } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const dev: NitroPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/dev',
|
||||
output: {
|
||||
serverDir: '{{ _nuxt.buildDir }}/nitro'
|
||||
},
|
||||
externals: { trace: false },
|
||||
inlineDynamicImports: true, // externals plugin limitation
|
||||
sourceMap: true
|
||||
})
|
@ -1,90 +0,0 @@
|
||||
import { createRequire } from 'module'
|
||||
import { join, relative, resolve } from 'pathe'
|
||||
import fse from 'fs-extra'
|
||||
import { globby } from 'globby'
|
||||
import { readPackageJSON } from 'pkg-types'
|
||||
import { writeFile } from '../utils'
|
||||
import { NitroPreset, NitroContext } from '../context'
|
||||
|
||||
export const firebase: NitroPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/firebase',
|
||||
externals: true,
|
||||
commands: {
|
||||
deploy: 'npx firebase deploy'
|
||||
},
|
||||
hooks: {
|
||||
async 'nitro:compiled' (ctx: NitroContext) {
|
||||
await writeRoutes(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function writeRoutes ({ output: { publicDir, serverDir }, _nuxt: { rootDir, modulesDir } }: NitroContext) {
|
||||
if (!fse.existsSync(join(rootDir, 'firebase.json'))) {
|
||||
const firebase = {
|
||||
functions: {
|
||||
source: relative(rootDir, serverDir)
|
||||
},
|
||||
hosting: [
|
||||
{
|
||||
site: '<your_project_id>',
|
||||
public: relative(rootDir, publicDir),
|
||||
cleanUrls: true,
|
||||
rewrites: [
|
||||
{
|
||||
source: '**',
|
||||
function: 'server'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
await writeFile(resolve(rootDir, 'firebase.json'), JSON.stringify(firebase))
|
||||
}
|
||||
|
||||
const _require = createRequire(import.meta.url)
|
||||
|
||||
const jsons = await globby(`${serverDir}/node_modules/**/package.json`)
|
||||
const prefixLength = `${serverDir}/node_modules/`.length
|
||||
const suffixLength = '/package.json'.length
|
||||
const dependencies = jsons.reduce((obj, packageJson) => {
|
||||
const dirname = packageJson.slice(prefixLength, -suffixLength)
|
||||
if (!dirname.includes('node_modules')) {
|
||||
obj[dirname] = _require(packageJson).version
|
||||
}
|
||||
return obj
|
||||
}, {} as Record<string, string>)
|
||||
|
||||
let nodeVersion = '14'
|
||||
try {
|
||||
const currentNodeVersion = fse.readJSONSync(join(rootDir, 'package.json')).engines.node
|
||||
if (['16', '14'].includes(currentNodeVersion)) {
|
||||
nodeVersion = currentNodeVersion
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const getPackageVersion = async (id) => {
|
||||
const pkg = await readPackageJSON(id, { url: modulesDir })
|
||||
return pkg.version
|
||||
}
|
||||
|
||||
await writeFile(
|
||||
resolve(serverDir, 'package.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
private: true,
|
||||
type: 'module',
|
||||
main: './index.mjs',
|
||||
dependencies,
|
||||
devDependencies: {
|
||||
'firebase-functions-test': 'latest',
|
||||
'firebase-admin': await getPackageVersion('firebase-admin'),
|
||||
'firebase-functions': await getPackageVersion('firebase-functions')
|
||||
},
|
||||
engines: { node: nodeVersion }
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
export * from './azure_functions'
|
||||
export * from './azure'
|
||||
export * from './browser'
|
||||
export * from './cloudflare'
|
||||
export * from './firebase'
|
||||
export * from './lambda'
|
||||
export * from './netlify'
|
||||
export * from './node'
|
||||
export * from './dev'
|
||||
export * from './server'
|
||||
export * from './cli'
|
||||
export * from './vercel'
|
||||
export * from './worker'
|
@ -1,7 +0,0 @@
|
||||
|
||||
import { NitroPreset } from '../context'
|
||||
|
||||
export const lambda: NitroPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/lambda',
|
||||
externals: true
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'fs'
|
||||
import { join } from 'pathe'
|
||||
import consola from 'consola'
|
||||
import { extendPreset } from '../utils'
|
||||
import { NitroContext, NitroPreset } from '../context'
|
||||
import { lambda } from './lambda'
|
||||
|
||||
export const netlify: NitroPreset = extendPreset(lambda, {
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.netlify/functions-internal',
|
||||
publicDir: '{{ _nuxt.rootDir }}/dist'
|
||||
},
|
||||
hooks: {
|
||||
async 'nitro:compiled' (ctx: NitroContext) {
|
||||
const redirectsPath = join(ctx.output.publicDir, '_redirects')
|
||||
let contents = '/* /.netlify/functions/server 200'
|
||||
if (existsSync(redirectsPath)) {
|
||||
const currentRedirects = await fsp.readFile(redirectsPath, 'utf-8')
|
||||
if (currentRedirects.match(/^\/\* /m)) {
|
||||
consola.info('Not adding Nitro fallback to `_redirects` (as an existing fallback was found).')
|
||||
return
|
||||
}
|
||||
consola.info('Adding Nitro fallback to `_redirects` to handle all unmatched routes.')
|
||||
contents = currentRedirects + '\n' + contents
|
||||
}
|
||||
await fsp.writeFile(redirectsPath, contents)
|
||||
},
|
||||
'nitro:rollup:before' (ctx: NitroContext) {
|
||||
ctx.rollupConfig.output.entryFileNames = 'server.ts'
|
||||
}
|
||||
},
|
||||
ignore: [
|
||||
'netlify.toml',
|
||||
'_redirects'
|
||||
]
|
||||
})
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const netlify_builder: NitroPreset = extendPreset(netlify, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/netlify_builder'
|
||||
})
|
@ -1,6 +0,0 @@
|
||||
import { NitroPreset } from '../context'
|
||||
|
||||
export const node: NitroPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/node',
|
||||
externals: true
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { extendPreset } from '../utils'
|
||||
import { NitroPreset } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const server: NitroPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/server',
|
||||
serveStatic: true,
|
||||
commands: {
|
||||
preview: 'node {{ output.serverDir }}/index.mjs'
|
||||
}
|
||||
})
|
@ -1,49 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { extendPreset, writeFile } from '../utils'
|
||||
import { NitroPreset, NitroContext } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const vercel: NitroPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/vercel',
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.vercel_build_output',
|
||||
serverDir: '{{ output.dir }}/functions/node/server',
|
||||
publicDir: '{{ output.dir }}/static'
|
||||
},
|
||||
ignore: [
|
||||
'vercel.json'
|
||||
],
|
||||
hooks: {
|
||||
async 'nitro:compiled' (ctx: NitroContext) {
|
||||
await writeRoutes(ctx)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function writeRoutes ({ output }: NitroContext) {
|
||||
const routes = [
|
||||
{
|
||||
src: '/sw.js',
|
||||
headers: {
|
||||
'cache-control': 'public, max-age=0, must-revalidate'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
src: '/_nuxt/(.*)',
|
||||
headers: {
|
||||
'cache-control': 'public,max-age=31536000,immutable'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
handle: 'filesystem'
|
||||
},
|
||||
{
|
||||
src: '(.*)',
|
||||
dest: '/.vercel/functions/server/index'
|
||||
}
|
||||
]
|
||||
|
||||
await writeFile(resolve(output.dir, 'config/routes.json'), JSON.stringify(routes, null, 2))
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { NitroPreset } from '../context'
|
||||
|
||||
export const worker: NitroPreset = {
|
||||
entry: null, // Abstract
|
||||
node: false,
|
||||
minify: true,
|
||||
externals: false,
|
||||
inlineDynamicImports: true // iffe does not support code-splitting
|
||||
}
|
@ -1,347 +0,0 @@
|
||||
import { pathToFileURL } from 'url'
|
||||
import { createRequire } from 'module'
|
||||
import { dirname, join, relative, resolve } from 'pathe'
|
||||
import type { InputOptions, OutputOptions } from 'rollup'
|
||||
import defu from 'defu'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve'
|
||||
import alias from '@rollup/plugin-alias'
|
||||
import json from '@rollup/plugin-json'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import wasmPlugin from '@rollup/plugin-wasm'
|
||||
import inject from '@rollup/plugin-inject'
|
||||
import { visualizer } from 'rollup-plugin-visualizer'
|
||||
import * as unenv from 'unenv'
|
||||
import devalue from '@nuxt/devalue'
|
||||
|
||||
import type { Preset } from 'unenv'
|
||||
import { sanitizeFilePath } from 'mlly'
|
||||
import { genImport } from 'knitwork'
|
||||
import { NitroContext } from '../context'
|
||||
import { resolvePath } from '../utils'
|
||||
import { pkgDir } from '../dirs'
|
||||
|
||||
import { dynamicRequire } from './plugins/dynamic-require'
|
||||
import { externals, NodeExternalsOptions } from './plugins/externals'
|
||||
import { timing } from './plugins/timing'
|
||||
// import { autoMock } from './plugins/automock'
|
||||
import { staticAssets, dirnames } from './plugins/static'
|
||||
import { assets } from './plugins/assets'
|
||||
import { middleware } from './plugins/middleware'
|
||||
import { esbuild } from './plugins/esbuild'
|
||||
import { raw } from './plugins/raw'
|
||||
import { storage } from './plugins/storage'
|
||||
|
||||
export type RollupConfig = InputOptions & { output: OutputOptions }
|
||||
|
||||
export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
const extensions: string[] = ['.ts', '.mjs', '.js', '.json', '.node']
|
||||
|
||||
const nodePreset = nitroContext.node === false ? unenv.nodeless : unenv.node
|
||||
|
||||
const builtinPreset: Preset = {
|
||||
alias: {
|
||||
// General
|
||||
debug: 'unenv/runtime/npm/debug',
|
||||
consola: 'unenv/runtime/npm/consola',
|
||||
// Vue 2
|
||||
encoding: 'unenv/runtime/mock/proxy',
|
||||
he: 'unenv/runtime/mock/proxy',
|
||||
resolve: 'unenv/runtime/mock/proxy',
|
||||
'source-map': 'unenv/runtime/mock/proxy',
|
||||
'lodash.template': 'unenv/runtime/mock/proxy',
|
||||
'serialize-javascript': 'unenv/runtime/mock/proxy',
|
||||
// Vue 3
|
||||
'estree-walker': 'unenv/runtime/mock/proxy',
|
||||
'@babel/parser': 'unenv/runtime/mock/proxy',
|
||||
'@vue/compiler-core': 'unenv/runtime/mock/proxy',
|
||||
'@vue/compiler-dom': 'unenv/runtime/mock/proxy',
|
||||
'@vue/compiler-ssr': 'unenv/runtime/mock/proxy',
|
||||
...nitroContext.alias
|
||||
}
|
||||
}
|
||||
|
||||
const env = unenv.env(nodePreset, builtinPreset, nitroContext.env)
|
||||
|
||||
if (nitroContext.sourceMap) {
|
||||
env.polyfill.push('source-map-support/register.js')
|
||||
}
|
||||
|
||||
// TODO: #590
|
||||
const _require = createRequire(import.meta.url)
|
||||
if (nitroContext._nuxt.majorVersion === 3) {
|
||||
env.alias['vue/server-renderer'] = 'vue/server-renderer'
|
||||
env.alias['vue/compiler-sfc'] = 'vue/compiler-sfc'
|
||||
env.alias.vue = _require.resolve(`vue/dist/vue.cjs${nitroContext._nuxt.dev ? '' : '.prod'}.js`)
|
||||
}
|
||||
|
||||
const buildServerDir = join(nitroContext._nuxt.buildDir, 'dist/server')
|
||||
const runtimeAppDir = join(nitroContext._internal.runtimeDir, 'app')
|
||||
|
||||
const rollupConfig: RollupConfig = {
|
||||
input: resolvePath(nitroContext, nitroContext.entry),
|
||||
output: {
|
||||
dir: nitroContext.output.serverDir,
|
||||
entryFileNames: 'index.mjs',
|
||||
chunkFileNames (chunkInfo) {
|
||||
let prefix = ''
|
||||
const modules = Object.keys(chunkInfo.modules)
|
||||
const lastModule = modules[modules.length - 1]
|
||||
if (lastModule.startsWith(buildServerDir)) {
|
||||
prefix = join('app', relative(buildServerDir, dirname(lastModule)))
|
||||
} else if (lastModule.startsWith(runtimeAppDir)) {
|
||||
prefix = 'app'
|
||||
} else if (lastModule.startsWith(nitroContext._nuxt.buildDir)) {
|
||||
prefix = 'nuxt'
|
||||
} else if (lastModule.startsWith(nitroContext._internal.runtimeDir)) {
|
||||
prefix = 'nitro'
|
||||
} else if (nitroContext.middleware.find(m => lastModule.startsWith(m.handle as string))) {
|
||||
prefix = 'middleware'
|
||||
} else if (lastModule.includes('assets')) {
|
||||
prefix = 'assets'
|
||||
}
|
||||
return join('chunks', prefix, '[name].mjs')
|
||||
},
|
||||
inlineDynamicImports: nitroContext.inlineDynamicImports,
|
||||
format: 'esm',
|
||||
exports: 'auto',
|
||||
intro: '',
|
||||
outro: '',
|
||||
preferConst: true,
|
||||
sanitizeFileName: sanitizeFilePath,
|
||||
sourcemap: !!nitroContext.sourceMap,
|
||||
sourcemapExcludeSources: true,
|
||||
sourcemapPathTransform (relativePath, sourcemapPath) {
|
||||
return resolve(dirname(sourcemapPath), relativePath)
|
||||
}
|
||||
},
|
||||
external: env.external,
|
||||
// https://github.com/rollup/rollup/pull/4021#issuecomment-809985618
|
||||
// https://github.com/nuxt/framework/issues/160
|
||||
makeAbsoluteExternalsRelative: false,
|
||||
plugins: [],
|
||||
onwarn (warning, rollupWarn) {
|
||||
if (
|
||||
!['CIRCULAR_DEPENDENCY', 'EVAL'].includes(warning.code) &&
|
||||
!warning.message.includes('Unsupported source map comment')
|
||||
) {
|
||||
rollupWarn(warning)
|
||||
}
|
||||
},
|
||||
treeshake: {
|
||||
moduleSideEffects (id) {
|
||||
return nitroContext.moduleSideEffects.some(match => id.startsWith(match))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nitroContext.timing) {
|
||||
rollupConfig.plugins.push(timing())
|
||||
}
|
||||
|
||||
// Raw asset loader
|
||||
rollupConfig.plugins.push(raw())
|
||||
|
||||
// WASM import support
|
||||
if (nitroContext.experiments.wasm) {
|
||||
rollupConfig.plugins.push(wasmPlugin())
|
||||
}
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/replace
|
||||
rollupConfig.plugins.push(replace({
|
||||
sourceMap: !!nitroContext.sourceMap,
|
||||
preventAssignment: true,
|
||||
values: {
|
||||
'process.env.NODE_ENV': nitroContext._nuxt.dev ? '"development"' : '"production"',
|
||||
'typeof window': '"undefined"',
|
||||
...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])),
|
||||
'process.server': 'true',
|
||||
'process.client': 'false',
|
||||
'process.dev': String(nitroContext._nuxt.dev),
|
||||
'process.env.NUXT_NO_SSR': JSON.stringify(!nitroContext._nuxt.ssr),
|
||||
'process.env.NUXT_STATIC_BASE': JSON.stringify(nitroContext._nuxt.staticAssets.base),
|
||||
'process.env.NUXT_STATIC_VERSION': JSON.stringify(nitroContext._nuxt.staticAssets.version),
|
||||
'process.env.NUXT_FULL_STATIC': nitroContext._nuxt.fullStatic as unknown as string,
|
||||
'process.env.NITRO_PRESET': JSON.stringify(nitroContext.preset),
|
||||
'process.env.RUNTIME_CONFIG': devalue(nitroContext._nuxt.runtimeConfig),
|
||||
'process.env.DEBUG': JSON.stringify(nitroContext._nuxt.dev),
|
||||
// Needed for vue 2 server build
|
||||
'commonjsGlobal.process.env.VUE_ENV': '"server"',
|
||||
'global["process"].env.VUE_ENV': '"server"'
|
||||
}
|
||||
}))
|
||||
|
||||
// ESBuild
|
||||
rollupConfig.plugins.push(esbuild({
|
||||
target: 'es2019',
|
||||
sourceMap: !!nitroContext.sourceMap,
|
||||
...nitroContext.esbuild?.options
|
||||
}))
|
||||
|
||||
// Dynamic Require Support
|
||||
rollupConfig.plugins.push(dynamicRequire({
|
||||
dir: resolve(nitroContext._nuxt.buildDir, 'dist/server'),
|
||||
inline: nitroContext.node === false || nitroContext.inlineDynamicImports,
|
||||
ignore: [
|
||||
'client.manifest.mjs',
|
||||
'server.js',
|
||||
'server.cjs',
|
||||
'server.mjs',
|
||||
'server.manifest.mjs'
|
||||
]
|
||||
}))
|
||||
|
||||
// Assets
|
||||
rollupConfig.plugins.push(assets(nitroContext.assets))
|
||||
|
||||
// Static
|
||||
// TODO: use assets plugin
|
||||
if (nitroContext.serveStatic) {
|
||||
rollupConfig.plugins.push(dirnames())
|
||||
rollupConfig.plugins.push(staticAssets(nitroContext))
|
||||
}
|
||||
|
||||
// Storage
|
||||
rollupConfig.plugins.push(storage(nitroContext.storage))
|
||||
|
||||
// Middleware
|
||||
rollupConfig.plugins.push(middleware(() => {
|
||||
const _middleware = [
|
||||
...nitroContext.scannedMiddleware,
|
||||
...nitroContext.middleware
|
||||
]
|
||||
if (nitroContext.serveStatic) {
|
||||
_middleware.unshift({ route: '/', handle: '#nitro/server/static' })
|
||||
}
|
||||
return _middleware
|
||||
}))
|
||||
|
||||
// Polyfill
|
||||
rollupConfig.plugins.push(virtual({
|
||||
'#polyfill': env.polyfill.map(p => genImport(p)).join('\n')
|
||||
}))
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/alias
|
||||
const renderer = nitroContext.renderer || (nitroContext._nuxt.majorVersion === 3 ? 'vue3' : 'vue2')
|
||||
const vue2ServerRenderer = 'vue-server-renderer/' + (nitroContext._nuxt.dev ? 'build.dev.js' : 'build.prod.js')
|
||||
rollupConfig.plugins.push(alias({
|
||||
entries: {
|
||||
'#nitro': nitroContext._internal.runtimeDir,
|
||||
'#nitro-renderer': resolve(nitroContext._internal.runtimeDir, 'app', renderer),
|
||||
'#paths': resolve(nitroContext._internal.runtimeDir, 'app/paths'),
|
||||
'#config': resolve(nitroContext._internal.runtimeDir, 'app/config'),
|
||||
'#_config': resolve(nitroContext._internal.runtimeDir, 'app/config'),
|
||||
'#nitro-vue-renderer': vue2ServerRenderer,
|
||||
// Only file and data URLs are supported by the default ESM loader on Windows (#427)
|
||||
'#build': nitroContext._nuxt.dev && process.platform === 'win32'
|
||||
? pathToFileURL(nitroContext._nuxt.buildDir).href
|
||||
: nitroContext._nuxt.buildDir,
|
||||
'~': nitroContext._nuxt.srcDir,
|
||||
'@/': nitroContext._nuxt.srcDir,
|
||||
'~~': nitroContext._nuxt.rootDir,
|
||||
'@@/': nitroContext._nuxt.rootDir,
|
||||
...env.alias
|
||||
}
|
||||
}))
|
||||
|
||||
const moduleDirectories = [
|
||||
resolve(nitroContext._nuxt.rootDir, 'node_modules'),
|
||||
...nitroContext._nuxt.modulesDir,
|
||||
resolve(pkgDir, '../node_modules'),
|
||||
'node_modules'
|
||||
]
|
||||
|
||||
// Externals Plugin
|
||||
if (nitroContext.externals) {
|
||||
rollupConfig.plugins.push(externals(defu(nitroContext.externals as NodeExternalsOptions, {
|
||||
outDir: nitroContext.output.serverDir,
|
||||
moduleDirectories,
|
||||
external: [
|
||||
...(nitroContext._nuxt.dev ? [nitroContext._nuxt.buildDir] : [])
|
||||
],
|
||||
inline: [
|
||||
'#',
|
||||
'~',
|
||||
'@/',
|
||||
'~~',
|
||||
'@@/',
|
||||
'virtual:',
|
||||
nitroContext._internal.runtimeDir,
|
||||
nitroContext._nuxt.srcDir,
|
||||
nitroContext._nuxt.rootDir,
|
||||
nitroContext._nuxt.serverDir,
|
||||
...nitroContext.middleware.map(m => m.handle).filter(i => typeof i === 'string') as string[],
|
||||
...(nitroContext._nuxt.dev ? [] : ['vue', '@vue/', '@nuxt/'])
|
||||
],
|
||||
traceOptions: {
|
||||
base: '/',
|
||||
processCwd: nitroContext._nuxt.rootDir,
|
||||
exportsOnly: true
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/node-resolve
|
||||
rollupConfig.plugins.push(nodeResolve({
|
||||
extensions,
|
||||
preferBuiltins: true,
|
||||
rootDir: nitroContext._nuxt.rootDir,
|
||||
moduleDirectories,
|
||||
// 'module' is intentionally not supported because of externals
|
||||
mainFields: ['main'],
|
||||
exportConditions: [
|
||||
'default',
|
||||
'module',
|
||||
'node',
|
||||
'import'
|
||||
]
|
||||
}))
|
||||
|
||||
// Automatically mock unresolved externals
|
||||
// rollupConfig.plugins.push(autoMock())
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
rollupConfig.plugins.push(commonjs({
|
||||
sourceMap: !!nitroContext.sourceMap,
|
||||
esmExternals: id => !id.startsWith('unenv/'),
|
||||
requireReturnsDefault: 'auto'
|
||||
}))
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/json
|
||||
rollupConfig.plugins.push(json())
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/inject
|
||||
rollupConfig.plugins.push(inject({
|
||||
// TODO: https://github.com/rollup/plugins/pull/1066
|
||||
// @ts-ignore
|
||||
sourceMap: !!nitroContext.sourceMap,
|
||||
...env.inject
|
||||
}))
|
||||
|
||||
// https://github.com/TrySound/rollup-plugin-terser
|
||||
// https://github.com/terser/terser#minify-nitroContext
|
||||
if (nitroContext.minify) {
|
||||
rollupConfig.plugins.push(terser({
|
||||
mangle: {
|
||||
keep_fnames: true,
|
||||
keep_classnames: true
|
||||
},
|
||||
format: {
|
||||
comments: false
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if (nitroContext.analyze) {
|
||||
// https://github.com/btd/rollup-plugin-visualizer
|
||||
rollupConfig.plugins.push(visualizer({
|
||||
...nitroContext.analyze,
|
||||
filename: nitroContext.analyze.filename.replace('{name}', 'nitro'),
|
||||
title: 'Nitro Server bundle stats'
|
||||
}))
|
||||
}
|
||||
|
||||
return rollupConfig
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
import { promises as fsp } from 'fs'
|
||||
import type { Plugin } from 'rollup'
|
||||
import createEtag from 'etag'
|
||||
import mime from 'mime'
|
||||
import { resolve } from 'pathe'
|
||||
import { globby } from 'globby'
|
||||
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork'
|
||||
import virtual from './virtual'
|
||||
|
||||
export interface AssetOptions {
|
||||
inline: boolean
|
||||
dirs: {
|
||||
[assetdir: string]: {
|
||||
dir: string
|
||||
meta?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Asset {
|
||||
fsPath: string
|
||||
meta: {
|
||||
type?: string
|
||||
etag?: string
|
||||
mtime?: string
|
||||
}
|
||||
}
|
||||
|
||||
export function assets (opts: AssetOptions): Plugin {
|
||||
if (!opts.inline) {
|
||||
// Development: Use filesystem
|
||||
return virtual({ '#assets': getAssetsDev(opts.dirs) })
|
||||
}
|
||||
|
||||
// Production: Bundle assets
|
||||
return virtual({
|
||||
'#assets': {
|
||||
async load () {
|
||||
// Scan all assets
|
||||
const assets: Record<string, Asset> = {}
|
||||
for (const assetdir in opts.dirs) {
|
||||
const dirOpts = opts.dirs[assetdir]
|
||||
const files = await globby('**/*.*', { cwd: dirOpts.dir, absolute: false })
|
||||
for (const _id of files) {
|
||||
const fsPath = resolve(dirOpts.dir, _id)
|
||||
const id = assetdir + '/' + _id
|
||||
assets[id] = { fsPath, meta: {} }
|
||||
if (dirOpts.meta) {
|
||||
// @ts-ignore TODO: Use mime@2 types
|
||||
let type = mime.getType(id) || 'text/plain'
|
||||
if (type.startsWith('text')) { type += '; charset=utf-8' }
|
||||
const etag = createEtag(await fsp.readFile(fsPath))
|
||||
const mtime = await fsp.stat(fsPath).then(s => s.mtime.toJSON())
|
||||
assets[id].meta = { type, etag, mtime }
|
||||
}
|
||||
}
|
||||
}
|
||||
return getAssetProd(assets)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getAssetsDev (dirs) {
|
||||
return `
|
||||
import { createStorage } from 'unstorage'
|
||||
import fsDriver from 'unstorage/drivers/fs'
|
||||
|
||||
const dirs = ${JSON.stringify(dirs)}
|
||||
|
||||
export const assets = createStorage()
|
||||
|
||||
for (const [dirname, dirOpts] of Object.entries(dirs)) {
|
||||
assets.mount(dirname, fsDriver({ base: dirOpts.dir }))
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
function normalizeKey (key) {
|
||||
return key.replace(/[/\\]/g, ':').replace(/^:|:$/g, '')
|
||||
}
|
||||
|
||||
function getAssetProd (assets: Record<string, Asset>) {
|
||||
return `
|
||||
const _assets = ${genObjectFromRawEntries(
|
||||
Object.entries(assets).map(([id, asset]) => [normalizeKey(id), {
|
||||
import: genDynamicImport(asset.fsPath, { interopDefault: true }),
|
||||
meta: asset.meta
|
||||
}])
|
||||
)}
|
||||
|
||||
${normalizeKey.toString()}
|
||||
|
||||
export const assets = {
|
||||
getKeys() {
|
||||
return Object.keys(_assets)
|
||||
},
|
||||
hasItem (id) {
|
||||
id = normalizeKey(id)
|
||||
return id in _assets
|
||||
},
|
||||
getItem (id) {
|
||||
id = normalizeKey(id)
|
||||
return _assets[id] ? _assets[id].import() : null
|
||||
},
|
||||
getMeta (id) {
|
||||
id = normalizeKey(id)
|
||||
return _assets[id] ? _assets[id].meta : {}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import consola from 'consola'
|
||||
|
||||
const internalRegex = /^\.|\?|\.[mc]?js$|.ts$|.json$/
|
||||
|
||||
export function autoMock () {
|
||||
return {
|
||||
name: 'auto-mock',
|
||||
resolveId (src: string) {
|
||||
if (src && !internalRegex.test(src)) {
|
||||
consola.warn('Auto mock external ', src)
|
||||
return {
|
||||
id: 'unenv/runtime/mock/proxy'
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
import { pathToFileURL } from 'url'
|
||||
import { resolve } from 'pathe'
|
||||
import { globby } from 'globby'
|
||||
import type { Plugin } from 'rollup'
|
||||
import { genDynamicImport, genObjectFromRawEntries, genImport } from 'knitwork'
|
||||
import { serializeImportName } from '../../utils'
|
||||
|
||||
const PLUGIN_NAME = 'dynamic-require'
|
||||
const HELPER_DYNAMIC = `\0${PLUGIN_NAME}.mjs`
|
||||
const DYNAMIC_REQUIRE_RE = /import\("\.\/" ?\+(.*)\).then/g
|
||||
|
||||
interface Options {
|
||||
dir: string
|
||||
inline: boolean
|
||||
ignore: string[]
|
||||
outDir?: string
|
||||
prefix?: string
|
||||
}
|
||||
|
||||
interface Chunk {
|
||||
id: string
|
||||
src: string
|
||||
name: string
|
||||
meta?: {
|
||||
id?: string
|
||||
ids?: string[]
|
||||
moduleIds?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
interface TemplateContext {
|
||||
chunks: Chunk[]
|
||||
}
|
||||
|
||||
export function dynamicRequire ({ dir, ignore, inline }: Options): Plugin {
|
||||
return {
|
||||
name: PLUGIN_NAME,
|
||||
transform (code: string, _id: string) {
|
||||
return {
|
||||
code: code.replace(DYNAMIC_REQUIRE_RE, `${genDynamicImport(HELPER_DYNAMIC, { wrapper: false, interopDefault: true })}.then(dynamicRequire => dynamicRequire($1)).then`),
|
||||
map: null
|
||||
}
|
||||
},
|
||||
resolveId (id: string) {
|
||||
return id === HELPER_DYNAMIC ? id : null
|
||||
},
|
||||
// TODO: Async chunk loading over network!
|
||||
// renderDynamicImport () {
|
||||
// return {
|
||||
// left: 'fetch(', right: ')'
|
||||
// }
|
||||
// },
|
||||
async load (_id: string) {
|
||||
if (_id !== HELPER_DYNAMIC) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Scan chunks
|
||||
let files = []
|
||||
try {
|
||||
const wpManifest = resolve(dir, './server.manifest.json')
|
||||
files = await import(pathToFileURL(wpManifest).href).then(r => Object.keys(r.files).filter(file => !ignore.includes(file)))
|
||||
} catch {
|
||||
files = await globby('**/*.{cjs,mjs,js}', { cwd: dir, absolute: false, ignore })
|
||||
}
|
||||
|
||||
const chunks = (await Promise.all(files.map(async id => ({
|
||||
id,
|
||||
src: resolve(dir, id).replace(/\\/g, '/'),
|
||||
name: serializeImportName(id),
|
||||
meta: await getWebpackChunkMeta(resolve(dir, id))
|
||||
})))).filter(chunk => chunk.meta)
|
||||
|
||||
return inline ? TMPL_INLINE({ chunks }) : TMPL_LAZY({ chunks })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getWebpackChunkMeta (src: string) {
|
||||
const chunk = await import(pathToFileURL(src).href).then(r => r.default || r || {})
|
||||
const { id, ids, modules } = chunk
|
||||
if (!id && !ids) {
|
||||
return null // Not a webpack chunk
|
||||
}
|
||||
return {
|
||||
id,
|
||||
ids,
|
||||
moduleIds: Object.keys(modules || {})
|
||||
}
|
||||
}
|
||||
|
||||
function TMPL_INLINE ({ chunks }: TemplateContext) {
|
||||
return `${chunks.map(i => genImport(i.src, { name: '*', as: i.name })).join('\n')}
|
||||
const dynamicChunks = ${genObjectFromRawEntries(chunks.map(i => [i.id, i.name]))};
|
||||
|
||||
export default function dynamicRequire(id) {
|
||||
return Promise.resolve(dynamicChunks[id]);
|
||||
};`
|
||||
}
|
||||
|
||||
function TMPL_LAZY ({ chunks }: TemplateContext) {
|
||||
return `
|
||||
const dynamicChunks = ${genObjectFromRawEntries(chunks.map(i => [i.id, genDynamicImport(i.src)]))};
|
||||
|
||||
export default function dynamicRequire(id) {
|
||||
return dynamicChunks[id]();
|
||||
};`
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
// Based on https://github.com/egoist/rollup-plugin-esbuild (MIT)
|
||||
|
||||
import { extname, relative } from 'pathe'
|
||||
import type { Plugin, PluginContext } from 'rollup'
|
||||
import { Loader, TransformResult, transform } from 'esbuild'
|
||||
import { createFilter } from '@rollup/pluginutils'
|
||||
import type { FilterPattern } from '@rollup/pluginutils'
|
||||
|
||||
const defaultLoaders: { [ext: string]: Loader } = {
|
||||
'.ts': 'ts',
|
||||
'.js': 'js'
|
||||
}
|
||||
|
||||
export type Options = {
|
||||
include?: FilterPattern
|
||||
exclude?: FilterPattern
|
||||
sourceMap?: boolean
|
||||
minify?: boolean
|
||||
target?: string | string[]
|
||||
jsxFactory?: string
|
||||
jsxFragment?: string
|
||||
define?: {
|
||||
[k: string]: string
|
||||
}
|
||||
/**
|
||||
* Use this tsconfig file instead
|
||||
* Disable it by setting to `false`
|
||||
*/
|
||||
tsconfig?: string | false
|
||||
/**
|
||||
* Map extension to esbuild loader
|
||||
* Note that each entry (the extension) needs to start with a dot
|
||||
*/
|
||||
loaders?: {
|
||||
[ext: string]: Loader | false
|
||||
}
|
||||
}
|
||||
|
||||
export function esbuild (options: Options = {}): Plugin {
|
||||
let target: string | string[]
|
||||
|
||||
const loaders = {
|
||||
...defaultLoaders
|
||||
}
|
||||
|
||||
if (options.loaders) {
|
||||
for (const key of Object.keys(options.loaders)) {
|
||||
const value = options.loaders[key]
|
||||
if (typeof value === 'string') {
|
||||
loaders[key] = value
|
||||
} else if (value === false) {
|
||||
delete loaders[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const extensions: string[] = Object.keys(loaders)
|
||||
const INCLUDE_REGEXP = new RegExp(
|
||||
`\\.(${extensions.map(ext => ext.slice(1)).join('|')})$`
|
||||
)
|
||||
const EXCLUDE_REGEXP = /node_modules/
|
||||
|
||||
const filter = createFilter(
|
||||
options.include || INCLUDE_REGEXP,
|
||||
options.exclude || EXCLUDE_REGEXP
|
||||
)
|
||||
|
||||
return {
|
||||
name: 'esbuild',
|
||||
|
||||
async transform (code, id) {
|
||||
if (!filter(id)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const ext = extname(id)
|
||||
const loader = loaders[ext]
|
||||
|
||||
if (!loader) {
|
||||
return null
|
||||
}
|
||||
|
||||
target = options.target || 'node12'
|
||||
|
||||
const result = await transform(code, {
|
||||
loader,
|
||||
target,
|
||||
define: options.define,
|
||||
sourcemap: options.sourceMap !== false,
|
||||
sourcefile: id
|
||||
})
|
||||
|
||||
printWarnings(id, result, this)
|
||||
|
||||
return (
|
||||
result.code && {
|
||||
code: result.code,
|
||||
map: result.map || null
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
async renderChunk (code) {
|
||||
if (options.minify) {
|
||||
const result = await transform(code, {
|
||||
loader: 'js',
|
||||
minify: true,
|
||||
target
|
||||
})
|
||||
if (result.code) {
|
||||
return {
|
||||
code: result.code,
|
||||
map: result.map || null
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printWarnings (
|
||||
id: string,
|
||||
result: TransformResult,
|
||||
plugin: PluginContext
|
||||
) {
|
||||
if (result.warnings) {
|
||||
for (const warning of result.warnings) {
|
||||
let message = '[esbuild]'
|
||||
if (warning.location) {
|
||||
message += ` (${relative(process.cwd(), id)}:${warning.location.line}:${warning.location.column
|
||||
})`
|
||||
}
|
||||
message += ` ${warning.text}`
|
||||
plugin.warn(message)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
import { promises as fsp } from 'fs'
|
||||
import { resolve, dirname } from 'pathe'
|
||||
import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft'
|
||||
import type { Plugin } from 'rollup'
|
||||
|
||||
export interface NodeExternalsOptions {
|
||||
inline?: string[]
|
||||
external?: string[]
|
||||
outDir?: string
|
||||
trace?: boolean
|
||||
traceOptions?: NodeFileTraceOptions
|
||||
moduleDirectories?: string[]
|
||||
/** additional packages to include in `.output/server/node_modules` */
|
||||
traceInclude?: string[]
|
||||
}
|
||||
|
||||
export function externals (opts: NodeExternalsOptions): Plugin {
|
||||
const trackedExternals = new Set<string>()
|
||||
|
||||
return {
|
||||
name: 'node-externals',
|
||||
async resolveId (id, importer, options) {
|
||||
// Internals
|
||||
if (!id || id.startsWith('\x00') || id.includes('?') || id.startsWith('#')) {
|
||||
return null
|
||||
}
|
||||
|
||||
const originalId = id
|
||||
|
||||
// Normalize path on windows
|
||||
if (process.platform === 'win32') {
|
||||
if (id.startsWith('/')) {
|
||||
// Add back C: prefix on Windows
|
||||
id = resolve(id)
|
||||
}
|
||||
id = id.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
// Normalize from node_modules
|
||||
const _id = id.split('node_modules/').pop()
|
||||
|
||||
const externalPath = opts.external.find(i => _id.startsWith(i) || id.startsWith(i))
|
||||
// Skip checks if is an explicit external
|
||||
if (!externalPath) {
|
||||
// Resolve relative paths and exceptions
|
||||
// Ensure to take absolute and relative id
|
||||
if (_id.startsWith('.') || opts.inline.find(i => _id.startsWith(i) || id.startsWith(i))) {
|
||||
return null
|
||||
}
|
||||
// Bundle typescript, json and wasm (see https://github.com/nuxt/framework/discussions/692)
|
||||
if (/\.(ts|wasm|json)$/.test(_id)) {
|
||||
return null
|
||||
}
|
||||
// Check for subpaths
|
||||
} else if (opts.inline.find(i => i.startsWith(externalPath) && (_id.startsWith(i) || id.startsWith(i)))) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Track externals
|
||||
if (opts.trace !== false) {
|
||||
const resolved = await this.resolve(originalId, importer, { ...options, skipSelf: true })
|
||||
if (!resolved) {
|
||||
console.warn(`Could not resolve \`${originalId}\`. Have you installed it?`)
|
||||
} else {
|
||||
trackedExternals.add(resolved.id)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: _id,
|
||||
external: true
|
||||
}
|
||||
},
|
||||
async buildEnd () {
|
||||
if (opts.trace !== false) {
|
||||
for (const pkgName of opts.traceInclude || []) {
|
||||
const path = await this.resolve(pkgName)
|
||||
if (path?.id) {
|
||||
trackedExternals.add(path.id)
|
||||
}
|
||||
}
|
||||
const tracedFiles = await nodeFileTrace(Array.from(trackedExternals), opts.traceOptions)
|
||||
.then(r => Array.from(r.fileList).map(f => resolve(opts.traceOptions.base, f)))
|
||||
.then(r => r.filter(file => file.includes('node_modules')))
|
||||
|
||||
// // Find all unique package names
|
||||
const pkgs = new Set<string>()
|
||||
for (const file of tracedFiles) {
|
||||
const [, baseDir, pkgName, _importPath] = /^(.+\/node_modules\/)([^@/]+|@[^/]+\/[^/]+)(\/?.*?)?$/.exec(file)
|
||||
pkgs.add(resolve(baseDir, pkgName, 'package.json'))
|
||||
}
|
||||
|
||||
for (const pkg of pkgs) {
|
||||
if (!tracedFiles.includes(pkg)) {
|
||||
tracedFiles.push(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
const writeFile = async (file) => {
|
||||
if (!await isFile(file)) { return }
|
||||
const src = resolve(opts.traceOptions.base, file)
|
||||
const dst = resolve(opts.outDir, 'node_modules', file.replace(/^.*?node_modules[\\/](.*)$/, '$1'))
|
||||
await fsp.mkdir(dirname(dst), { recursive: true })
|
||||
await fsp.copyFile(src, dst)
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
// Workaround for EBUSY on windows (#424)
|
||||
for (const file of tracedFiles) {
|
||||
await writeFile(file)
|
||||
}
|
||||
} else {
|
||||
await Promise.all(tracedFiles.map(writeFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function isFile (file: string) {
|
||||
try {
|
||||
const stat = await fsp.stat(file)
|
||||
return stat.isFile()
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') { return false }
|
||||
throw err
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
import hasha from 'hasha'
|
||||
import { relative } from 'pathe'
|
||||
import table from 'table'
|
||||
import isPrimitive from 'is-primitive'
|
||||
import { isDebug } from 'std-env'
|
||||
import { genArrayFromRaw, genDynamicImport, genImport } from 'knitwork'
|
||||
import type { ServerMiddleware } from '../../server/middleware'
|
||||
import virtual from './virtual'
|
||||
|
||||
const unique = (arr: any[]) => Array.from(new Set(arr))
|
||||
|
||||
export function middleware (getMiddleware: () => ServerMiddleware[]) {
|
||||
const getImportId = p => '_' + hasha(p).slice(0, 6)
|
||||
|
||||
let lastDump = ''
|
||||
|
||||
return virtual({
|
||||
'#server-middleware': {
|
||||
load: () => {
|
||||
const middleware = getMiddleware()
|
||||
|
||||
if (isDebug) {
|
||||
const dumped = dumpMiddleware(middleware)
|
||||
if (dumped !== lastDump) {
|
||||
lastDump = dumped
|
||||
if (middleware.length) {
|
||||
console.log(dumped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Imports take priority
|
||||
const imports = unique(middleware.filter(m => m.lazy === false).map(m => m.handle))
|
||||
|
||||
// Lazy imports should fill in the gaps
|
||||
const lazyImports = unique(middleware.filter(m => m.lazy !== false && !imports.includes(m.handle)).map(m => m.handle))
|
||||
|
||||
return `
|
||||
${imports.map(handle => `${genImport(handle, getImportId(handle))};`).join('\n')}
|
||||
|
||||
${lazyImports.map(handle => `const ${getImportId(handle)} = ${genDynamicImport(handle)};`).join('\n')}
|
||||
|
||||
const middleware = ${genArrayFromRaw(middleware.map(m => ({
|
||||
route: JSON.stringify(m.route),
|
||||
handle: getImportId(m.handle),
|
||||
lazy: m.lazy || true,
|
||||
promisify: m.promisify !== undefined ? m.promisify : true
|
||||
})))};
|
||||
|
||||
export default middleware
|
||||
`
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function dumpMiddleware (middleware: ServerMiddleware[]) {
|
||||
const data = middleware.map(({ route, handle, ...props }) => {
|
||||
return [
|
||||
(route && route !== '/') ? route : '*',
|
||||
relative(process.cwd(), handle as string),
|
||||
dumpObject(props)
|
||||
]
|
||||
})
|
||||
return table.table([
|
||||
['Route', 'Handle', 'Options'],
|
||||
...data
|
||||
], {
|
||||
singleLine: true,
|
||||
border: table.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(', ')
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { extname } from 'pathe'
|
||||
import type { Plugin } from 'rollup'
|
||||
|
||||
export interface RawOptions {
|
||||
extensions?: string[]
|
||||
}
|
||||
|
||||
export function raw (opts: RawOptions = {}): Plugin {
|
||||
const extensions = new Set(['.md', '.mdx', '.yml', '.txt', '.css', '.htm', '.html']
|
||||
.concat(opts.extensions || []))
|
||||
|
||||
return {
|
||||
name: 'raw',
|
||||
transform (code, id) {
|
||||
if (id[0] !== '\0' && extensions.has(extname(id))) {
|
||||
return {
|
||||
code: `// ${id}\nexport default ${JSON.stringify(code)}`,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
import { readFileSync, statSync } from 'fs'
|
||||
import createEtag from 'etag'
|
||||
import mime from 'mime'
|
||||
import { relative, resolve } from 'pathe'
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import { globbySync } from 'globby'
|
||||
import type { Plugin } from 'rollup'
|
||||
import type { NitroContext } from '../../context'
|
||||
|
||||
export function staticAssets (context: NitroContext) {
|
||||
const assets: Record<string, { type: string, etag: string, mtime: string, path: string }> = {}
|
||||
|
||||
const files = globbySync('**/*.*', { cwd: context.output.publicDir, absolute: false })
|
||||
|
||||
for (const id of files) {
|
||||
let type = mime.getType(id) || 'text/plain'
|
||||
if (type.startsWith('text')) { type += '; charset=utf-8' }
|
||||
const fullPath = resolve(context.output.publicDir, id)
|
||||
const etag = createEtag(readFileSync(fullPath))
|
||||
const stat = statSync(fullPath)
|
||||
|
||||
assets['/' + decodeURIComponent(id)] = {
|
||||
type,
|
||||
etag,
|
||||
mtime: stat.mtime.toJSON(),
|
||||
path: relative(context.output.serverDir, fullPath)
|
||||
}
|
||||
}
|
||||
|
||||
return virtual({
|
||||
'#static-assets': `export default ${JSON.stringify(assets, null, 2)};`,
|
||||
'#static': `
|
||||
import { promises } from 'fs'
|
||||
import { resolve } from 'pathe'
|
||||
import { dirname } from 'pathe'
|
||||
import { fileURLToPath } from 'url'
|
||||
import assets from '#static-assets'
|
||||
|
||||
const mainDir = dirname(fileURLToPath(globalThis.entryURL))
|
||||
|
||||
export function readAsset (id) {
|
||||
return promises.readFile(resolve(mainDir, getAsset(id).path))
|
||||
}
|
||||
|
||||
export function getAsset (id) {
|
||||
return assets[id]
|
||||
}
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export function dirnames (): Plugin {
|
||||
return {
|
||||
name: 'dirnames',
|
||||
renderChunk (code, chunk) {
|
||||
return {
|
||||
code: (chunk.isEntry ? 'globalThis.entryURL = import.meta.url;' : '') + code,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import { genImport, genString } from 'knitwork'
|
||||
import { serializeImportName } from '../../utils'
|
||||
|
||||
export interface StorageOptions {
|
||||
mounts: {
|
||||
[path: string]: {
|
||||
driver: 'fs' | 'http' | 'memory',
|
||||
driverOptions?: Record<string, any>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const drivers = {
|
||||
fs: 'unstorage/drivers/fs',
|
||||
http: 'unstorage/drivers/http',
|
||||
memory: 'unstorage/drivers/memory'
|
||||
}
|
||||
|
||||
export function storage (opts: StorageOptions) {
|
||||
const mounts: { path: string, driver: string, opts: object }[] = []
|
||||
|
||||
for (const path in opts.mounts) {
|
||||
const mount = opts.mounts[path]
|
||||
mounts.push({
|
||||
path,
|
||||
driver: drivers[mount.driver] || mount.driver,
|
||||
opts: mount.driverOptions || {}
|
||||
})
|
||||
}
|
||||
|
||||
const driverImports = Array.from(new Set(mounts.map(m => m.driver)))
|
||||
|
||||
return virtual({
|
||||
'#storage': `
|
||||
import { createStorage } from 'unstorage'
|
||||
import { assets } from '#assets'
|
||||
|
||||
${driverImports.map(i => genImport(i, serializeImportName(i))).join('\n')}
|
||||
|
||||
export const storage = createStorage({})
|
||||
|
||||
storage.mount('/assets', assets)
|
||||
|
||||
${mounts.map(m => `storage.mount(${genString(m.path)}, ${serializeImportName(m.driver)}(${JSON.stringify(m.opts)}))`).join('\n')}
|
||||
`
|
||||
})
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import { extname } from 'pathe'
|
||||
import type { Plugin, RenderedChunk } from 'rollup'
|
||||
|
||||
export interface Options { }
|
||||
|
||||
const TIMING = 'globalThis.__timing__'
|
||||
|
||||
const iife = code => `(function() { ${code.trim()} })();`.replace(/\n/g, '')
|
||||
|
||||
const HELPER = iife(`
|
||||
const start = () => Date.now();
|
||||
const end = s => Date.now() - s;
|
||||
const _s = {};
|
||||
const metrics = [];
|
||||
const logStart = id => { _s[id] = Date.now(); };
|
||||
const logEnd = id => { const t = end(_s[id]); delete _s[id]; metrics.push([id, t]); console.debug('>', id + ' (' + t + 'ms)'); };
|
||||
${TIMING} = { start, end, metrics, logStart, logEnd };
|
||||
`)
|
||||
|
||||
const HELPERIMPORT = "import './timing.js';"
|
||||
|
||||
export function timing (_opts: Options = {}): Plugin {
|
||||
return {
|
||||
name: 'timing',
|
||||
generateBundle () {
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'timing.js',
|
||||
source: HELPER
|
||||
})
|
||||
},
|
||||
renderChunk (code, chunk: RenderedChunk) {
|
||||
let name = chunk.fileName || ''
|
||||
name = name.replace(extname(name), '')
|
||||
|
||||
const logName = name === 'index' ? 'Nitro Start' : ('Load ' + name)
|
||||
|
||||
return {
|
||||
code: (chunk.isEntry ? HELPERIMPORT : '') + `${TIMING}.logStart('${logName}');` + code + `;${TIMING}.logEnd('${logName}');`,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import { resolve, dirname } from 'pathe'
|
||||
import type { Plugin } from 'rollup'
|
||||
|
||||
// Based on https://github.com/rollup/plugins/blob/master/packages/virtual/src/index.ts
|
||||
|
||||
type VirtualModule = string | { load: () => string | Promise<string> }
|
||||
|
||||
export interface RollupVirtualOptions {
|
||||
[id: string]: VirtualModule;
|
||||
}
|
||||
|
||||
const PREFIX = '\0virtual:'
|
||||
|
||||
export default function virtual (modules: RollupVirtualOptions): Plugin {
|
||||
const _modules = new Map<string, VirtualModule>()
|
||||
|
||||
for (const [id, mod] of Object.entries(modules)) {
|
||||
_modules.set(id, mod)
|
||||
_modules.set(resolve(id), mod)
|
||||
}
|
||||
|
||||
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 = resolve(dirname(importerNoPrefix), id)
|
||||
if (_modules.has(resolved)) { return PREFIX + resolved }
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
async load (id) {
|
||||
if (!id.startsWith(PREFIX)) { return null }
|
||||
|
||||
const idNoPrefix = id.slice(PREFIX.length)
|
||||
if (!_modules.has(idNoPrefix)) { return null }
|
||||
|
||||
let m = _modules.get(idNoPrefix)
|
||||
if (typeof m !== 'string' && typeof m.load === 'function') {
|
||||
m = await m.load()
|
||||
}
|
||||
|
||||
// console.log('[virtual]', idNoPrefix, '\n', m)
|
||||
|
||||
return {
|
||||
code: m as string,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import destr from 'destr'
|
||||
import defu from 'defu'
|
||||
|
||||
// Bundled runtime config (injected by nitro)
|
||||
const _runtimeConfig = process.env.RUNTIME_CONFIG as any
|
||||
|
||||
// Allow override from process.env and deserialize
|
||||
for (const type of ['private', 'public']) {
|
||||
for (const key in _runtimeConfig[type]) {
|
||||
_runtimeConfig[type][key] = destr(process.env[key] || _runtimeConfig[type][key])
|
||||
}
|
||||
}
|
||||
|
||||
// Load dynamic app configuration
|
||||
const appConfig = _runtimeConfig.public.app
|
||||
appConfig.baseURL = process.env.NUXT_APP_BASE_URL || appConfig.baseURL
|
||||
appConfig.cdnURL = process.env.NUXT_APP_CDN_URL || appConfig.cdnURL
|
||||
appConfig.buildAssetsDir = process.env.NUXT_APP_BUILD_ASSETS_DIR || appConfig.buildAssetsDir
|
||||
|
||||
// Named exports
|
||||
export const privateConfig = deepFreeze(defu(_runtimeConfig.private, _runtimeConfig.public))
|
||||
export const publicConfig = deepFreeze(_runtimeConfig.public)
|
||||
|
||||
// Default export (usable for server)
|
||||
export default privateConfig
|
||||
|
||||
// Utils
|
||||
function deepFreeze (object: Record<string, any>) {
|
||||
const propNames = Object.getOwnPropertyNames(object)
|
||||
for (const name of propNames) {
|
||||
const value = object[name]
|
||||
if (value && typeof value === 'object') {
|
||||
deepFreeze(value)
|
||||
}
|
||||
}
|
||||
return Object.freeze(object)
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { joinURL } from 'ufo'
|
||||
import config from '#config'
|
||||
|
||||
export function baseURL () {
|
||||
return config.app.baseURL
|
||||
}
|
||||
|
||||
export function buildAssetsDir () {
|
||||
return config.app.buildAssetsDir
|
||||
}
|
||||
|
||||
export function buildAssetsURL (...path: string[]) {
|
||||
return joinURL(publicAssetsURL(), config.app.buildAssetsDir, ...path)
|
||||
}
|
||||
|
||||
export function publicAssetsURL (...path: string[]) {
|
||||
const publicBase = config.app.cdnURL || config.app.baseURL
|
||||
return path.length ? joinURL(publicBase, ...path) : publicBase
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import _renderToString from 'vue-server-renderer/basic'
|
||||
|
||||
export function renderToString (component, context) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_renderToString(component, context, (err, result) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
return resolve(result)
|
||||
})
|
||||
})
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { parseURL } from 'ufo'
|
||||
import { localCall } from '../server'
|
||||
|
||||
export async function handle (context, req) {
|
||||
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({
|
||||
url,
|
||||
headers: req.headers,
|
||||
method: req.method,
|
||||
body: req.body
|
||||
})
|
||||
|
||||
context.res = {
|
||||
status,
|
||||
headers,
|
||||
body: body ? body.toString() : statusText
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { localCall } from '../server'
|
||||
|
||||
export 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
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { localCall } from '../server'
|
||||
|
||||
async function cli () {
|
||||
const url = process.argv[2] || '/'
|
||||
const debug = (label, ...args) => console.debug(`> ${label}:`, ...args)
|
||||
const r = await localCall({ url })
|
||||
|
||||
debug('URL', url)
|
||||
debug('StatusCode', r.status)
|
||||
debug('StatusMessage', r.statusText)
|
||||
// @ts-ignore
|
||||
for (const header of r.headers.entries()) {
|
||||
debug(header[0], header[1])
|
||||
}
|
||||
console.log('\n', r.body.toString())
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
cli().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
|
||||
import { withoutBase } from 'ufo'
|
||||
import { localCall } from '../server'
|
||||
import { requestHasBody, useRequestBody } from '../server/utils'
|
||||
import { buildAssetsURL, baseURL } from '#paths'
|
||||
|
||||
addEventListener('fetch', (event: any) => {
|
||||
event.respondWith(handleEvent(event))
|
||||
})
|
||||
|
||||
async function handleEvent (event) {
|
||||
try {
|
||||
return await getAssetFromKV(event, { cacheControl: assetsCacheControl, mapRequestToAsset: baseURLModifier })
|
||||
} catch (_err) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
const url = new URL(event.request.url)
|
||||
let body
|
||||
if (requestHasBody(event.request)) {
|
||||
body = await useRequestBody(event.request)
|
||||
}
|
||||
|
||||
const r = await localCall({
|
||||
event,
|
||||
url: url.pathname + url.search,
|
||||
host: url.hostname,
|
||||
protocol: url.protocol,
|
||||
headers: event.request.headers,
|
||||
method: event.request.method,
|
||||
redirect: event.request.redirect,
|
||||
body
|
||||
})
|
||||
|
||||
return new Response(r.body, {
|
||||
// @ts-ignore
|
||||
headers: r.headers,
|
||||
status: r.status,
|
||||
statusText: r.statusText
|
||||
})
|
||||
}
|
||||
|
||||
function assetsCacheControl (request) {
|
||||
if (request.url.startsWith(buildAssetsURL())) {
|
||||
return {
|
||||
browserTTL: 31536000,
|
||||
edgeTTL: 31536000
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const baseURLModifier = (request: Request) => {
|
||||
const url = withoutBase(request.url, baseURL())
|
||||
return mapRequestToAsset(new Request(url, request))
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { Server } from 'http'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { mkdirSync } from 'fs'
|
||||
import { threadId, parentPort } from 'worker_threads'
|
||||
import { isWindows, provider } from 'std-env'
|
||||
import { handle } from '../server'
|
||||
|
||||
const server = new Server(handle)
|
||||
|
||||
function getAddress () {
|
||||
// https://github.com/nuxt/framework/issues/1636
|
||||
if (provider === 'stackblitz' || process.env.NITRO_NO_UNIX_SOCKET) {
|
||||
return '0'
|
||||
}
|
||||
const socketName = `worker-${process.pid}-${threadId}.sock`
|
||||
if (isWindows) {
|
||||
return join('\\\\.\\pipe\\nitro', socketName)
|
||||
} else {
|
||||
const socketDir = join(tmpdir(), 'nitro')
|
||||
mkdirSync(socketDir, { recursive: true })
|
||||
return join(socketDir, socketName)
|
||||
}
|
||||
}
|
||||
|
||||
const listenAddress = getAddress()
|
||||
server.listen(listenAddress, () => {
|
||||
const _address = server.address()
|
||||
parentPort.postMessage({
|
||||
event: 'listen',
|
||||
address: typeof _address === 'string'
|
||||
? { socketPath: _address }
|
||||
: `http://localhost:${_address.port}`
|
||||
})
|
||||
})
|
@ -1,7 +0,0 @@
|
||||
import '#polyfill'
|
||||
|
||||
// @ts-ignore
|
||||
import functions from 'firebase-functions'
|
||||
import { handle } from '../server'
|
||||
|
||||
export const server = functions.https.onRequest(handle)
|
@ -1,38 +0,0 @@
|
||||
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, APIGatewayProxyEventV2, APIGatewayProxyResult, APIGatewayProxyResultV2, Context } from 'aws-lambda'
|
||||
import '#polyfill'
|
||||
import { withQuery } from 'ufo'
|
||||
import type { HeadersObject } from 'unenv/runtime/_internal/types'
|
||||
import { localCall } from '../server'
|
||||
|
||||
export const handler = async function handler (event: APIGatewayProxyEvent | APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResult | APIGatewayProxyResultV2> {
|
||||
const url = withQuery((event as APIGatewayProxyEvent).path || (event as APIGatewayProxyEventV2).rawPath, event.queryStringParameters)
|
||||
const method = (event as APIGatewayProxyEvent).httpMethod || (event as APIGatewayProxyEventV2).requestContext?.http?.method || 'get'
|
||||
|
||||
if ('cookies' in event) {
|
||||
event.headers.cookie = event.cookies.join(',')
|
||||
}
|
||||
|
||||
const r = await localCall({
|
||||
event,
|
||||
url,
|
||||
context,
|
||||
headers: normalizeIncomingHeaders(event.headers),
|
||||
method,
|
||||
query: event.queryStringParameters,
|
||||
body: event.body // TODO: handle event.isBase64Encoded
|
||||
})
|
||||
|
||||
return {
|
||||
statusCode: r.status,
|
||||
headers: normalizeOutgoingHeaders(r.headers),
|
||||
body: r.body.toString()
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeIncomingHeaders (headers: APIGatewayProxyEventHeaders) {
|
||||
return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]))
|
||||
}
|
||||
|
||||
function normalizeOutgoingHeaders (headers: HeadersObject) {
|
||||
return Object.fromEntries(Object.entries(headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(',') : v]))
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { builder } from '@netlify/functions'
|
||||
// @ts-ignore
|
||||
import { handler as _handler } from '#nitro/entries/lambda'
|
||||
|
||||
export const handler = builder(_handler)
|
@ -1,2 +0,0 @@
|
||||
import '#polyfill'
|
||||
export * from '../server'
|
@ -1,26 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { Server as HttpServer } from 'http'
|
||||
import { Server as HttpsServer } from 'https'
|
||||
import destr from 'destr'
|
||||
import { handle } from '../server'
|
||||
import { baseURL } from '#paths'
|
||||
|
||||
const cert = process.env.NITRO_SSL_CERT
|
||||
const key = process.env.NITRO_SSL_KEY
|
||||
|
||||
const server = cert && key ? new HttpsServer({ key, cert }, handle) : new HttpServer(handle)
|
||||
|
||||
const port = (destr(process.env.NUXT_PORT || process.env.PORT) || 3000) as number
|
||||
const hostname = process.env.NUXT_HOST || process.env.HOST || 'localhost'
|
||||
|
||||
// @ts-ignore
|
||||
server.listen(port, hostname, (err) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
const protocol = cert && key ? 'https' : 'http'
|
||||
console.log(`Listening on ${protocol}://${hostname}:${port}${baseURL()}`)
|
||||
})
|
||||
|
||||
export default {}
|
@ -1,48 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import '#polyfill'
|
||||
import { localCall } from '../server'
|
||||
import { requestHasBody, useRequestBody } from '../server/utils'
|
||||
|
||||
const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION
|
||||
|
||||
addEventListener('fetch', (event: any) => {
|
||||
const url = new URL(event.request.url)
|
||||
|
||||
if (url.pathname.includes('.') && !url.pathname.startsWith(STATIC_ASSETS_BASE) && !url.pathname.startsWith('/api')) {
|
||||
return
|
||||
}
|
||||
|
||||
event.respondWith(handleEvent(url, event))
|
||||
})
|
||||
|
||||
async function handleEvent (url, event) {
|
||||
let body
|
||||
if (requestHasBody(event.request)) {
|
||||
body = await useRequestBody(event.request)
|
||||
}
|
||||
|
||||
const r = await localCall({
|
||||
event,
|
||||
url: url.pathname + url.search,
|
||||
host: url.hostname,
|
||||
protocol: url.protocol,
|
||||
headers: event.request.headers,
|
||||
method: event.request.method,
|
||||
redirect: event.request.redirect,
|
||||
body
|
||||
})
|
||||
|
||||
return new Response(r.body, {
|
||||
headers: r.headers,
|
||||
status: r.status,
|
||||
statusText: r.statusText
|
||||
})
|
||||
}
|
||||
|
||||
self.addEventListener('install', () => {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
@ -1,4 +0,0 @@
|
||||
import '#polyfill'
|
||||
import { handle } from '../server'
|
||||
|
||||
export default handle
|
@ -1,64 +0,0 @@
|
||||
// import ansiHTML from 'ansi-html'
|
||||
import type { IncomingMessage, ServerResponse } from 'http'
|
||||
import { withQuery } from 'ufo'
|
||||
import { $fetch } from '.'
|
||||
const cwd = process.cwd()
|
||||
|
||||
const hasReqHeader = (req, header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes)
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
export async function handleError (error, req: IncomingMessage, res: ServerResponse) {
|
||||
const isJsonRequest = hasReqHeader(req, 'accept', 'application/json') || hasReqHeader(req, 'user-agent', 'curl/') || hasReqHeader(req, 'user-agent', 'httpie/')
|
||||
|
||||
const stack = (error.stack || '')
|
||||
.split('\n')
|
||||
.splice(1)
|
||||
.filter(line => line.includes('at '))
|
||||
.map((line) => {
|
||||
const text = line
|
||||
.replace(cwd + '/', './')
|
||||
.replace('webpack:/', '')
|
||||
.replace('.vue', '.js') // TODO: Support sourcemap
|
||||
.trim()
|
||||
return {
|
||||
text,
|
||||
internal: (line.includes('node_modules') && !line.includes('.cache')) ||
|
||||
line.includes('internal') ||
|
||||
line.includes('new Promise')
|
||||
}
|
||||
})
|
||||
|
||||
const is404 = error.statusCode === 404
|
||||
|
||||
const errorObject = {
|
||||
url: req.url,
|
||||
statusCode: error.statusCode || 500,
|
||||
statusMessage: error.statusMessage ?? is404 ? 'Page Not Found' : 'Internal Server Error',
|
||||
message: error.message || error.toString(),
|
||||
description: isDev && !is404
|
||||
? `<pre>${stack.map(i => `<span class="stack${i.internal ? ' internal' : ''}">${i.text}</span>`).join('\n')}</pre>`
|
||||
: ''
|
||||
}
|
||||
|
||||
res.statusCode = errorObject.statusCode
|
||||
res.statusMessage = errorObject.statusMessage
|
||||
|
||||
// Console output
|
||||
if (!is404) {
|
||||
console.error(error.message + '\n' + stack.map(l => ' ' + l.text).join(' \n'))
|
||||
}
|
||||
|
||||
// JSON response
|
||||
if (isJsonRequest) {
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
return res.end(JSON.stringify(errorObject))
|
||||
}
|
||||
|
||||
// HTML response
|
||||
const url = withQuery('/_nitro/__error', errorObject)
|
||||
const html = await $fetch(url).catch(() => errorObject.statusMessage)
|
||||
|
||||
res.setHeader('Content-Type', 'text/html;charset=UTF-8')
|
||||
res.end(html)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import { createApp, lazyHandle, useBase } from 'h3'
|
||||
import { createFetch, Headers } from 'ohmyfetch'
|
||||
import destr from 'destr'
|
||||
import { createCall, createFetch as createLocalFetch } from 'unenv/runtime/fetch/index'
|
||||
import { baseURL } from '../app/paths'
|
||||
import { timingMiddleware } from './timing'
|
||||
import { handleError } from './error'
|
||||
// @ts-ignore
|
||||
import serverMiddleware from '#server-middleware'
|
||||
|
||||
const app = createApp({
|
||||
debug: destr(process.env.DEBUG),
|
||||
onError: handleError
|
||||
})
|
||||
|
||||
const renderMiddleware = lazyHandle(() => import('../app/render').then(e => e.renderMiddleware))
|
||||
|
||||
app.use('/_nitro', renderMiddleware)
|
||||
app.use(timingMiddleware)
|
||||
app.use(serverMiddleware)
|
||||
app.use(renderMiddleware)
|
||||
|
||||
export const stack = app.stack
|
||||
export const handle = useBase(baseURL(), app)
|
||||
export const localCall = createCall(handle)
|
||||
export const localFetch = createLocalFetch(localCall, globalThis.fetch)
|
||||
|
||||
export const $fetch = createFetch({ fetch: localFetch, Headers })
|
||||
|
||||
globalThis.$fetch = $fetch as any
|
@ -1,73 +0,0 @@
|
||||
import { createError } from 'h3'
|
||||
import { withoutTrailingSlash, withLeadingSlash, parseURL } from 'ufo'
|
||||
// @ts-ignore
|
||||
import { getAsset, readAsset } from '#static'
|
||||
import { buildAssetsDir } from '#paths'
|
||||
|
||||
const METHODS = ['HEAD', 'GET']
|
||||
|
||||
const TWO_DAYS = 2 * 60 * 60 * 24
|
||||
const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION
|
||||
|
||||
export default async function serveStatic (req, res) {
|
||||
if (!METHODS.includes(req.method)) {
|
||||
return
|
||||
}
|
||||
|
||||
let id = decodeURIComponent(withLeadingSlash(withoutTrailingSlash(parseURL(req.url).pathname)))
|
||||
let asset: string
|
||||
|
||||
for (const _id of [id, id + '/index.html']) {
|
||||
const _asset = getAsset(_id)
|
||||
if (_asset) {
|
||||
asset = _asset
|
||||
id = _id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const isBuildAsset = id.startsWith(buildAssetsDir())
|
||||
|
||||
if (!asset) {
|
||||
if (isBuildAsset && !id.startsWith(STATIC_ASSETS_BASE)) {
|
||||
throw createError({
|
||||
statusMessage: 'Cannot find static asset ' + id,
|
||||
statusCode: 404
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const ifNotMatch = req.headers['if-none-match'] === asset.etag
|
||||
if (ifNotMatch) {
|
||||
res.statusCode = 304
|
||||
return res.end('Not Modified (etag)')
|
||||
}
|
||||
|
||||
const ifModifiedSinceH = req.headers['if-modified-since']
|
||||
if (ifModifiedSinceH && asset.mtime) {
|
||||
if (new Date(ifModifiedSinceH) >= new Date(asset.mtime)) {
|
||||
res.statusCode = 304
|
||||
return res.end('Not Modified (mtime)')
|
||||
}
|
||||
}
|
||||
|
||||
if (asset.type) {
|
||||
res.setHeader('Content-Type', asset.type)
|
||||
}
|
||||
|
||||
if (asset.etag) {
|
||||
res.setHeader('ETag', asset.etag)
|
||||
}
|
||||
|
||||
if (asset.mtime) {
|
||||
res.setHeader('Last-Modified', asset.mtime)
|
||||
}
|
||||
|
||||
if (isBuildAsset) {
|
||||
res.setHeader('Cache-Control', `max-age=${TWO_DAYS}, immutable`)
|
||||
}
|
||||
|
||||
const contents = await readAsset(id)
|
||||
return res.end(contents)
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
export const globalTiming = globalThis.__timing__ || {
|
||||
start: () => 0,
|
||||
end: () => 0,
|
||||
metrics: []
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing
|
||||
export function timingMiddleware (_req, res, next) {
|
||||
const start = globalTiming.start()
|
||||
|
||||
const _end = res.end
|
||||
res.end = (data, encoding, callback) => {
|
||||
const metrics = [['Generate', globalTiming.end(start)], ...globalTiming.metrics]
|
||||
const serverTiming = metrics.map(m => `-;dur=${m[1]};desc="${encodeURIComponent(m[0])}"`).join(', ')
|
||||
if (!res.headersSent) {
|
||||
res.setHeader('Server-Timing', serverTiming)
|
||||
}
|
||||
_end.call(res, data, encoding, callback)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
const METHOD_WITH_BODY_RE = /post|put|patch/i
|
||||
const TEXT_MIME_RE = /application\/text|text\/html/
|
||||
const JSON_MIME_RE = /application\/json/
|
||||
|
||||
export function requestHasBody (request: globalThis.Request) : boolean {
|
||||
return METHOD_WITH_BODY_RE.test(request.method)
|
||||
}
|
||||
|
||||
export async function useRequestBody (request: globalThis.Request): Promise<any> {
|
||||
const contentType = request.headers.get('content-type') || ''
|
||||
if (contentType.includes('form')) {
|
||||
const formData = await request.formData()
|
||||
const body = Object.create(null)
|
||||
for (const entry of formData.entries()) {
|
||||
body[entry[0]] = entry[1]
|
||||
}
|
||||
return body
|
||||
} else if (JSON_MIME_RE.test(contentType)) {
|
||||
return request.json()
|
||||
} else if (TEXT_MIME_RE.test(contentType)) {
|
||||
return request.text()
|
||||
} else {
|
||||
const blob = await request.blob()
|
||||
return URL.createObjectURL(blob)
|
||||
}
|
||||
}
|
11
packages/nitro/src/runtime/types.d.ts
vendored
11
packages/nitro/src/runtime/types.d.ts
vendored
@ -1,11 +0,0 @@
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Global {
|
||||
__timing__: any
|
||||
$config: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// export required to turn this into a module for TS augmentation purposes
|
||||
export { }
|
@ -1,182 +0,0 @@
|
||||
import { Worker } from 'worker_threads'
|
||||
|
||||
import { IncomingMessage, ServerResponse } from 'http'
|
||||
import { existsSync, promises as fsp } from 'fs'
|
||||
import { loading as loadingTemplate } from '@nuxt/ui-templates'
|
||||
import chokidar, { FSWatcher } from 'chokidar'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { promisifyHandle, createApp, Middleware, useBase } from 'h3'
|
||||
import httpProxy from 'http-proxy'
|
||||
import { listen, Listener, ListenOptions } from 'listhen'
|
||||
import servePlaceholder from 'serve-placeholder'
|
||||
import serveStatic from 'serve-static'
|
||||
import { resolve } from 'pathe'
|
||||
import connect from 'connect'
|
||||
import { joinURL } from 'ufo'
|
||||
import type { NitroContext } from '../context'
|
||||
import { handleVfs } from './vfs'
|
||||
|
||||
export interface NitroWorker {
|
||||
worker: Worker,
|
||||
address: string
|
||||
}
|
||||
|
||||
function initWorker (filename): Promise<NitroWorker> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = new Worker(filename)
|
||||
worker.once('exit', (code) => {
|
||||
if (code) {
|
||||
reject(new Error('[worker] exited with code: ' + code))
|
||||
}
|
||||
})
|
||||
worker.on('error', (err) => {
|
||||
console.error('[worker]', err)
|
||||
err.message = '[worker] ' + err.message
|
||||
reject(err)
|
||||
})
|
||||
worker.on('message', (event) => {
|
||||
if (event && event.address) {
|
||||
resolve({
|
||||
worker,
|
||||
address: event.address
|
||||
} as NitroWorker)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function killWorker (worker?: NitroWorker) {
|
||||
if (!worker) {
|
||||
return
|
||||
}
|
||||
await worker.worker?.terminate()
|
||||
worker.worker = null
|
||||
if (worker.address && existsSync(worker.address)) {
|
||||
await fsp.rm(worker.address).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
export function createDevServer (nitroContext: NitroContext) {
|
||||
// Worker
|
||||
const workerEntry = resolve(nitroContext.output.dir, nitroContext.output.serverDir, 'index.mjs')
|
||||
|
||||
let currentWorker: NitroWorker
|
||||
|
||||
async function reload () {
|
||||
// Create a new worker
|
||||
const newWorker = await initWorker(workerEntry)
|
||||
|
||||
// Kill old worker in background
|
||||
killWorker(currentWorker).catch(err => console.error(err))
|
||||
|
||||
// Replace new worker as current
|
||||
currentWorker = newWorker
|
||||
}
|
||||
|
||||
// App
|
||||
const app = createApp()
|
||||
|
||||
// _nuxt and static
|
||||
const buildAssetsURL = joinURL(nitroContext._nuxt.baseURL, nitroContext._nuxt.buildAssetsDir)
|
||||
app.use(buildAssetsURL, serveStatic(resolve(nitroContext._nuxt.buildDir, 'dist/client')))
|
||||
app.use(nitroContext._nuxt.baseURL, serveStatic(resolve(nitroContext._nuxt.publicDir)))
|
||||
|
||||
// debugging endpoint to view vfs
|
||||
app.use('/_vfs', useBase('/_vfs', handleVfs(nitroContext)))
|
||||
|
||||
// Dynamic Middlwware
|
||||
const legacyMiddleware = createDynamicMiddleware()
|
||||
const devMiddleware = createDynamicMiddleware()
|
||||
app.use(legacyMiddleware.middleware)
|
||||
app.use(devMiddleware.middleware)
|
||||
|
||||
// serve placeholder 404 assets instead of hitting SSR
|
||||
app.use(buildAssetsURL, servePlaceholder())
|
||||
|
||||
// SSR Proxy
|
||||
const proxy = httpProxy.createProxy()
|
||||
const proxyHandle = promisifyHandle((req: IncomingMessage, res: ServerResponse) => {
|
||||
proxy.web(req, res, { target: currentWorker.address }, (error: unknown) => {
|
||||
console.error('[proxy]', error)
|
||||
})
|
||||
})
|
||||
app.use((req, res) => {
|
||||
if (currentWorker?.address) {
|
||||
// Workaround to pass legacy req.spa to proxy
|
||||
// @ts-ignore
|
||||
if (req.spa) {
|
||||
req.headers['x-nuxt-no-ssr'] = 'true'
|
||||
}
|
||||
return proxyHandle(req, res)
|
||||
} else {
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.end(loadingTemplate({}))
|
||||
}
|
||||
})
|
||||
|
||||
// Listen
|
||||
let listeners: Listener[] = []
|
||||
const _listen = async (port: ListenOptions['port'], opts?: Partial<ListenOptions>) => {
|
||||
const listener = await listen(app, { port, ...opts })
|
||||
listeners.push(listener)
|
||||
return listener
|
||||
}
|
||||
|
||||
// Watch for dist and reload worker
|
||||
const pattern = '**/*.{js,json,cjs,mjs}'
|
||||
const events = ['add', 'change']
|
||||
let watcher: FSWatcher
|
||||
function watch () {
|
||||
if (watcher) { return }
|
||||
const dReload = debounce(() => reload().catch(console.warn))
|
||||
watcher = chokidar.watch([
|
||||
resolve(nitroContext.output.serverDir, pattern),
|
||||
resolve(nitroContext._nuxt.buildDir, 'dist/server', pattern)
|
||||
]).on('all', event => events.includes(event) && dReload())
|
||||
}
|
||||
|
||||
// Close handler
|
||||
async function close () {
|
||||
if (watcher) {
|
||||
await watcher.close()
|
||||
}
|
||||
await killWorker(currentWorker)
|
||||
await Promise.all(listeners.map(l => l.close()))
|
||||
listeners = []
|
||||
}
|
||||
nitroContext._internal.hooks.hook('close', close)
|
||||
|
||||
return {
|
||||
reload,
|
||||
listen: _listen,
|
||||
app,
|
||||
close,
|
||||
watch,
|
||||
setLegacyMiddleware: legacyMiddleware.set,
|
||||
setDevMiddleware: devMiddleware.set
|
||||
}
|
||||
}
|
||||
|
||||
interface DynamicMiddleware {
|
||||
set: (input: Middleware) => void
|
||||
middleware: Middleware
|
||||
}
|
||||
|
||||
function createDynamicMiddleware (): DynamicMiddleware {
|
||||
let middleware: Middleware
|
||||
return {
|
||||
set: (input) => {
|
||||
if (!Array.isArray(input)) {
|
||||
middleware = input
|
||||
return
|
||||
}
|
||||
const app = connect()
|
||||
for (const m of input) {
|
||||
app.use(m.path || m.route || '/', m.handler || m.handle!)
|
||||
}
|
||||
middleware = app
|
||||
},
|
||||
middleware: (req, res, next) =>
|
||||
middleware ? middleware(req, res, next) : next()
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
import { resolve, join, extname } from 'pathe'
|
||||
import { joinURL } from 'ufo'
|
||||
import { globby } from 'globby'
|
||||
import { watch } from 'chokidar'
|
||||
import { resolvePath } from '@nuxt/kit'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import type { Middleware } from 'h3'
|
||||
|
||||
export interface ServerMiddleware {
|
||||
route: string
|
||||
/**
|
||||
* @deprecated use route
|
||||
*/
|
||||
path?: string
|
||||
|
||||
handle?: Middleware | string
|
||||
/**
|
||||
* @deprecated use handle
|
||||
*/
|
||||
handler?: Middleware | string
|
||||
|
||||
lazy?: boolean // Default is true
|
||||
promisify?: boolean // Default is true
|
||||
}
|
||||
|
||||
function filesToMiddleware (files: string[], baseDir: string, baseURL: string, overrides?: Partial<ServerMiddleware>): ServerMiddleware[] {
|
||||
return files.map((file) => {
|
||||
const route = joinURL(
|
||||
baseURL,
|
||||
file
|
||||
.slice(0, file.length - extname(file).length)
|
||||
.replace(/\/index$/, '')
|
||||
)
|
||||
const handle = resolve(baseDir, file)
|
||||
return {
|
||||
route,
|
||||
handle
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.route.localeCompare(a.route))
|
||||
.map(m => ({ ...m, ...overrides }))
|
||||
}
|
||||
|
||||
export function scanMiddleware (serverDir: string, onChange?: (results: ServerMiddleware[], event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string) => void): Promise<ServerMiddleware[]> {
|
||||
const pattern = '**/*.{ts,mjs,js,cjs}'
|
||||
const globalDir = resolve(serverDir, 'middleware')
|
||||
const apiDir = resolve(serverDir, 'api')
|
||||
|
||||
const scan = async () => {
|
||||
const globalFiles = await globby(pattern, { cwd: globalDir, dot: true })
|
||||
const apiFiles = await globby(pattern, { cwd: apiDir, dot: true })
|
||||
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 async function resolveMiddleware (nuxt: Nuxt) {
|
||||
const middleware: ServerMiddleware[] = []
|
||||
const legacyMiddleware: ServerMiddleware[] = []
|
||||
|
||||
for (let m of nuxt.options.serverMiddleware) {
|
||||
if (typeof m === 'string' || typeof m === 'function' /* legacy middleware */) { 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 {
|
||||
delete m.handler
|
||||
delete m.path
|
||||
middleware.push({
|
||||
...m,
|
||||
handle: await resolvePath(handle),
|
||||
route
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
middleware,
|
||||
legacyMiddleware
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import { createError, Handle } from 'h3'
|
||||
import { NitroContext } from '..'
|
||||
|
||||
export function handleVfs (ctx: NitroContext): Handle {
|
||||
return (req) => {
|
||||
if (req.url === '/') {
|
||||
const items = Object.keys(ctx.vfs)
|
||||
.filter(i => !i.startsWith('#'))
|
||||
.map(key => `<li><a href="/_vfs/${encodeURIComponent(key)}">${key.replace(ctx._nuxt.rootDir, '')}</a></li>`)
|
||||
.join('\n')
|
||||
return `<!doctype html><html><body><ul>${items}</ul></body></html>`
|
||||
}
|
||||
const param = decodeURIComponent(req.url.slice(1))
|
||||
if (param in ctx.vfs) {
|
||||
return editorTemplate({
|
||||
readOnly: true,
|
||||
language: param.endsWith('html') ? 'html' : 'javascript',
|
||||
theme: 'vs-dark',
|
||||
value: ctx.vfs[param]
|
||||
})
|
||||
}
|
||||
return createError({ message: 'File not found', statusCode: 404 })
|
||||
}
|
||||
}
|
||||
|
||||
const monacoVersion = '0.30.0'
|
||||
const monacoUrl = `https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/${monacoVersion}/min`
|
||||
const vsUrl = `${monacoUrl}/vs`
|
||||
|
||||
const editorTemplate = (options: Record<string, any>) => `
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" data-name="vs/editor/editor.main" href="${vsUrl}/editor/editor.main.min.css">
|
||||
</head>
|
||||
<body style="margin: 0">
|
||||
<div id="editor" style="height:100vh"></div>
|
||||
<script src="${vsUrl}/loader.min.js"></script>
|
||||
<script>
|
||||
require.config({ paths: { vs: '${vsUrl}' } })
|
||||
|
||||
const proxy = URL.createObjectURL(new Blob([\`
|
||||
self.MonacoEnvironment = { baseUrl: '${monacoUrl}' }
|
||||
importScripts('${vsUrl}/base/worker/workerMain.min.js')
|
||||
\`], { type: 'text/javascript' }))
|
||||
window.MonacoEnvironment = { getWorkerUrl: () => proxy }
|
||||
|
||||
require(['vs/editor/editor.main'], function () {
|
||||
monaco.editor.create(document.getElementById('editor'), ${JSON.stringify(options)})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
@ -1,154 +0,0 @@
|
||||
import { createRequire } from 'module'
|
||||
import { relative, dirname, resolve } from 'pathe'
|
||||
import fse from 'fs-extra'
|
||||
import jiti from 'jiti'
|
||||
import defu from 'defu'
|
||||
import { mergeHooks } from 'hookable'
|
||||
import consola from 'consola'
|
||||
import chalk from 'chalk'
|
||||
import { getProperty } from 'dot-prop'
|
||||
import type { NitroPreset, NitroInput } from '../context'
|
||||
|
||||
export function hl (str: string) {
|
||||
return chalk.cyan(str)
|
||||
}
|
||||
|
||||
export function prettyPath (p: string, highlight = true) {
|
||||
p = relative(process.cwd(), p)
|
||||
return highlight ? hl(p) : p
|
||||
}
|
||||
|
||||
export function compileTemplate (contents: string) {
|
||||
return (params: Record<string, any>) => contents.replace(/{{ ?([\w.]+) ?}}/g, (_, match) => {
|
||||
const val = getProperty(params, match)
|
||||
if (!val) {
|
||||
consola.warn(`cannot resolve template param '${match}' in ${contents.slice(0, 20)}`)
|
||||
}
|
||||
return val as string || `${match}`
|
||||
})
|
||||
}
|
||||
|
||||
export function serializeTemplate (contents: string) {
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
return `(params) => \`${contents.replace(/{{ (\w+) }}/g, '${params.$1}')}\``
|
||||
}
|
||||
|
||||
export function jitiImport (dir: string, path: string) {
|
||||
return jiti(dir, { interopDefault: true })(path)
|
||||
}
|
||||
|
||||
export function tryImport (dir: string, path: string) {
|
||||
try {
|
||||
return jitiImport(dir, path)
|
||||
} catch (_err) { }
|
||||
}
|
||||
|
||||
export async function writeFile (file: string, contents: string, log = false) {
|
||||
await fse.mkdirp(dirname(file))
|
||||
await fse.writeFile(file, contents, 'utf-8')
|
||||
if (log) {
|
||||
consola.info('Generated', prettyPath(file))
|
||||
}
|
||||
}
|
||||
|
||||
export function evalTemplate (ctx, input: string | ((ctx) => string)): string {
|
||||
if (typeof input === 'function') {
|
||||
input = input(ctx)
|
||||
}
|
||||
if (typeof input !== 'string') {
|
||||
throw new TypeError('Invalid template: ' + input)
|
||||
}
|
||||
return compileTemplate(input)(ctx)
|
||||
}
|
||||
|
||||
export function resolvePath (nitroContext: NitroInput, input: string | ((nitroContext: NitroInput) => string), resolveBase: string = ''): string {
|
||||
return resolve(resolveBase, evalTemplate(nitroContext, input))
|
||||
}
|
||||
|
||||
export function replaceAll (input: string, from: string, to: string) {
|
||||
return input.replace(new RegExp(from, 'g'), to)
|
||||
}
|
||||
|
||||
export function detectTarget () {
|
||||
if (process.env.NETLIFY || process.env.NETLIFY_LOCAL) {
|
||||
return 'netlify'
|
||||
}
|
||||
|
||||
if (process.env.NOW_BUILDER) {
|
||||
return 'vercel'
|
||||
}
|
||||
|
||||
if (process.env.INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN) {
|
||||
return 'azure'
|
||||
}
|
||||
}
|
||||
|
||||
export async function isDirectory (path: string) {
|
||||
try {
|
||||
return (await fse.stat(path)).isDirectory()
|
||||
} catch (_err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function extendPreset (base: NitroPreset, preset: NitroPreset): NitroPreset {
|
||||
return (config: NitroInput) => {
|
||||
if (typeof preset === 'function') {
|
||||
preset = preset(config)
|
||||
}
|
||||
if (typeof base === 'function') {
|
||||
base = base(config)
|
||||
}
|
||||
return defu({
|
||||
hooks: mergeHooks(base.hooks, preset.hooks)
|
||||
}, preset, base)
|
||||
}
|
||||
}
|
||||
|
||||
const _getDependenciesMode = {
|
||||
dev: ['devDependencies'],
|
||||
prod: ['dependencies'],
|
||||
all: ['devDependencies', 'dependencies']
|
||||
}
|
||||
const _require = createRequire(import.meta.url)
|
||||
export function getDependencies (dir: string, mode: keyof typeof _getDependenciesMode = 'all') {
|
||||
const fields = _getDependenciesMode[mode]
|
||||
const pkg = _require(resolve(dir, 'package.json'))
|
||||
const dependencies = []
|
||||
for (const field of fields) {
|
||||
if (pkg[field]) {
|
||||
for (const name in pkg[field]) {
|
||||
dependencies.push(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dependencies
|
||||
}
|
||||
|
||||
// TODO: Refactor to scule (https://github.com/unjs/scule/issues/6)
|
||||
export function serializeImportName (id: string) {
|
||||
return '_' + id.replace(/[^a-zA-Z0-9_$]/g, '_')
|
||||
}
|
||||
|
||||
export function readPackageJson (
|
||||
packageName: string,
|
||||
_require: NodeRequire = createRequire(import.meta.url)
|
||||
) {
|
||||
try {
|
||||
return _require(`${packageName}/package.json`)
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
|
||||
const pkgModulePaths = /^(.*\/node_modules\/).*$/.exec(_require.resolve(packageName))
|
||||
for (const pkgModulePath of pkgModulePaths) {
|
||||
const path = resolve(pkgModulePath, packageName, 'package.json')
|
||||
if (fse.existsSync(path)) {
|
||||
return fse.readJSONSync(path)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import { promises as fsp } from 'fs'
|
||||
import { resolve, dirname, relative } from 'pathe'
|
||||
import { globby } from 'globby'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import { gzipSize } from 'gzip-size'
|
||||
import chalk from 'chalk'
|
||||
import { isTest } from 'std-env'
|
||||
|
||||
export async function printFSTree (dir: string) {
|
||||
if (isTest) {
|
||||
return
|
||||
}
|
||||
|
||||
const files = await globby('**/*.*', { cwd: dir })
|
||||
|
||||
const items = (await Promise.all(files.map(async (file) => {
|
||||
const path = resolve(dir, file)
|
||||
const src = await fsp.readFile(path)
|
||||
const size = src.byteLength
|
||||
const gzip = await gzipSize(src)
|
||||
return { file, path, size, gzip }
|
||||
}))).sort((a, b) => b.path.localeCompare(a.path))
|
||||
|
||||
let totalSize = 0
|
||||
let totalGzip = 0
|
||||
|
||||
let totalNodeModulesSize = 0
|
||||
let totalNodeModulesGzip = 0
|
||||
|
||||
items.forEach((item, index) => {
|
||||
let dir = dirname(item.file)
|
||||
if (dir === '.') { dir = '' }
|
||||
const rpath = relative(process.cwd(), item.path)
|
||||
const treeChar = index === items.length - 1 ? '└─' : '├─'
|
||||
|
||||
const isNodeModules = item.file.includes('node_modules')
|
||||
|
||||
if (isNodeModules) {
|
||||
totalNodeModulesSize += item.size
|
||||
totalNodeModulesGzip += item.gzip
|
||||
return
|
||||
}
|
||||
|
||||
process.stdout.write(chalk.gray(` ${treeChar} ${rpath} (${prettyBytes(item.size)}) (${prettyBytes(item.gzip)} gzip)\n`))
|
||||
totalSize += item.size
|
||||
totalGzip += item.gzip
|
||||
})
|
||||
|
||||
process.stdout.write(`${chalk.cyan('Σ Total size:')} ${prettyBytes(totalSize + totalNodeModulesSize)} (${prettyBytes(totalGzip + totalNodeModulesGzip)} gzip)\n`)
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { join } from 'pathe'
|
||||
import fsExtra from 'fs-extra'
|
||||
|
||||
export const wpfs = {
|
||||
...fsExtra,
|
||||
join
|
||||
} as any
|
41
packages/nitro/types/fetch.d.ts
vendored
41
packages/nitro/types/fetch.d.ts
vendored
@ -1,41 +0,0 @@
|
||||
import type { FetchRequest, FetchOptions, FetchResponse } from 'ohmyfetch'
|
||||
|
||||
// An interface to extend in a local project
|
||||
export declare interface InternalApi { }
|
||||
|
||||
export declare type ValueOf<C> = C extends Record<any, any> ? C[keyof C] : never
|
||||
|
||||
export declare type MatchedRoutes<Route extends string> = ValueOf<{
|
||||
// exact match, prefix match or root middleware
|
||||
[key in keyof InternalApi]: Route extends key | `${key}/${string}` | '/' ? key : never
|
||||
}>
|
||||
|
||||
export declare type MiddlewareOf<Route extends string> = Exclude<InternalApi[MatchedRoutes<Route>], Error | void>
|
||||
|
||||
export declare type TypedInternalResponse<Route, Default> =
|
||||
Default extends string | boolean | number | null | void | object
|
||||
// Allow user overrides
|
||||
? Default
|
||||
: Route extends string
|
||||
? MiddlewareOf<Route> extends never
|
||||
// Bail if only types are Error or void (for example, from middleware)
|
||||
? Default
|
||||
: MiddlewareOf<Route>
|
||||
: Default
|
||||
|
||||
export declare interface $Fetch<DefaultT = unknown, DefaultR extends FetchRequest = FetchRequest> {
|
||||
<T = DefaultT, R extends FetchRequest = DefaultR> (request: R, opts?: FetchOptions): Promise<TypedInternalResponse<R, T>>
|
||||
raw<T = DefaultT, R extends FetchRequest = DefaultR> (request: R, opts?: FetchOptions): Promise<FetchResponse<TypedInternalResponse<R, T>>>
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var $fetch: $Fetch
|
||||
namespace NodeJS {
|
||||
interface Global {
|
||||
$fetch: $Fetch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { }
|
11
packages/nitro/types/index.d.ts
vendored
11
packages/nitro/types/index.d.ts
vendored
@ -1,11 +0,0 @@
|
||||
import './shims'
|
||||
|
||||
declare module '@nuxt/schema' {
|
||||
import type { NitroInput } from '../dist'
|
||||
interface NuxtConfig {
|
||||
nitro?: NitroInput
|
||||
}
|
||||
}
|
||||
|
||||
export * from './fetch'
|
||||
export * from '../dist'
|
31
packages/nitro/types/shims.d.ts
vendored
31
packages/nitro/types/shims.d.ts
vendored
@ -1,31 +0,0 @@
|
||||
declare module '#storage' {
|
||||
import type { Storage } from 'unstorage'
|
||||
export const storage: Storage
|
||||
}
|
||||
|
||||
declare module '#assets' {
|
||||
export interface AssetMeta { type?: string, etag?: string, mtime?: string }
|
||||
|
||||
export const assets: {
|
||||
getKeys(): Promise<string[]>
|
||||
hasItem(id: string): Promise<boolean>
|
||||
getItem<T = any> (id: string): Promise<T>
|
||||
getMeta(id: string): Promise<AssetMeta>
|
||||
}
|
||||
}
|
||||
|
||||
declare module '#config' {
|
||||
import type { PublicRuntimeConfig, PrivateRuntimeConfig } from '@nuxt/schema'
|
||||
export const privateConfig: PrivateRuntimeConfig
|
||||
export const publicConfig: PublicRuntimeConfig
|
||||
const runtimeConfig: PrivateRuntimeConfig & PublicRuntimeConfig
|
||||
export default runtimeConfig
|
||||
}
|
||||
|
||||
declare module '#paths' {
|
||||
export const baseURL: () => string
|
||||
export const buildAssetsDir: () => string
|
||||
|
||||
export const buildAssetsURL: (...path: string[]) => string
|
||||
export const publicAssetsURL: (...path: string[]) => string
|
||||
}
|
@ -19,7 +19,6 @@
|
||||
"devDependencies": {
|
||||
"@nuxt/kit": "3.0.0",
|
||||
"@nuxt/schema": "3.0.0",
|
||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
||||
"@types/clear": "^0",
|
||||
"@types/mri": "^1.1.1",
|
||||
"@types/rimraf": "^3",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { promises as fsp } from 'fs'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { createApp, lazyHandle } from 'h3'
|
||||
import { createServer } from '../utils/server'
|
||||
import { listen } from 'listhen'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
@ -35,7 +35,6 @@ export default defineNuxtCommand({
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
const app = createApp()
|
||||
const server = createServer(app)
|
||||
|
||||
const serveFile = (filePath: string) => lazyHandle(async () => {
|
||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||
@ -65,6 +64,6 @@ export default defineNuxtCommand({
|
||||
</ul>
|
||||
</html>`)
|
||||
|
||||
await server.listen()
|
||||
await listen(app)
|
||||
}
|
||||
})
|
||||
|
@ -8,7 +8,7 @@ import { defineNuxtCommand } from './index'
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'build',
|
||||
usage: 'npx nuxi build [rootDir]',
|
||||
usage: 'npx nuxi build [--prerender] [rootDir]',
|
||||
description: 'Build nuxt for production deployment'
|
||||
},
|
||||
async invoke (args) {
|
||||
@ -17,7 +17,12 @@ export default defineNuxtCommand({
|
||||
|
||||
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({ rootDir })
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
_generate: args.prerender
|
||||
}
|
||||
})
|
||||
|
||||
await clearDir(nuxt.options.buildDir)
|
||||
|
||||
|
@ -4,11 +4,10 @@ import { debounce } from 'perfect-debounce'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import consola from 'consola'
|
||||
import { withTrailingSlash } from 'ufo'
|
||||
import { createServer, createLoadingHandler } from '../utils/server'
|
||||
import { showBanner } from '../utils/banner'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { importModule } from '../utils/cjs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
@ -19,8 +18,21 @@ export default defineNuxtCommand({
|
||||
},
|
||||
async invoke (args) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
|
||||
const server = createServer()
|
||||
const listener = await server.listen({
|
||||
|
||||
const { listen } = await import('listhen')
|
||||
let currentHandler
|
||||
let loadingMessage = 'Nuxt is starting...'
|
||||
const loadingHandler = async (_req, res) => {
|
||||
const { loading: loadingTemplate } = await importModule('@nuxt/ui-templates')
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.statusCode = 503 // Service Unavailable
|
||||
res.end(loadingTemplate({ loading: loadingMessage }))
|
||||
}
|
||||
const serverHandler = (req, res) => {
|
||||
return currentHandler ? currentHandler(req, res) : loadingHandler(req, res)
|
||||
}
|
||||
|
||||
const listener = await listen(serverHandler, {
|
||||
clipboard: args.clipboard,
|
||||
open: args.open || args.o,
|
||||
port: args.port || args.p || process.env.NUXT_PORT,
|
||||
@ -39,31 +51,29 @@ export default defineNuxtCommand({
|
||||
let currentNuxt: Nuxt
|
||||
const load = async (isRestart: boolean, reason?: string) => {
|
||||
try {
|
||||
const message = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
||||
server.setApp(createLoadingHandler(message))
|
||||
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
||||
currentHandler = null
|
||||
if (isRestart) {
|
||||
consola.info(message)
|
||||
consola.info(loadingMessage)
|
||||
}
|
||||
if (currentNuxt) {
|
||||
await currentNuxt.close()
|
||||
}
|
||||
currentNuxt = await loadNuxt({ rootDir, dev: true, ready: false })
|
||||
await clearDir(currentNuxt.options.buildDir)
|
||||
await currentNuxt.ready()
|
||||
await Promise.all([
|
||||
writeTypes(currentNuxt).catch(console.error),
|
||||
buildNuxt(currentNuxt)
|
||||
])
|
||||
server.setApp(currentNuxt.server.app)
|
||||
currentHandler = currentNuxt.server.app
|
||||
if (isRestart && args.clear !== false) {
|
||||
showBanner()
|
||||
listener.showURL()
|
||||
}
|
||||
} catch (err) {
|
||||
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
||||
server.setApp(createLoadingHandler(
|
||||
'Error while loading nuxt. Please check console and fix errors.'
|
||||
))
|
||||
currentHandler = null
|
||||
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,14 @@
|
||||
import { execa } from 'execa'
|
||||
import { resolve } from 'pathe'
|
||||
import { isNuxt3 } from '@nuxt/kit'
|
||||
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import buildCommand from './build'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'generate',
|
||||
usage: 'npx nuxi generate [rootDir]',
|
||||
description: ''
|
||||
description: 'Build Nuxt and prerender static routes'
|
||||
},
|
||||
async invoke (args) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
const { loadNuxt } = await loadKit(rootDir)
|
||||
const nuxt = await loadNuxt({ rootDir, config: { _export: true } })
|
||||
|
||||
if (isNuxt3(nuxt)) {
|
||||
throw new Error('`nuxt generate` is not supported in Nuxt 3. Please follow this RFC: https://git.io/JKfvx')
|
||||
} else {
|
||||
// Generate types and close nuxt instance
|
||||
await writeTypes(nuxt)
|
||||
await nuxt.close()
|
||||
// Forwards argv to `nuxt generate`
|
||||
await execa('npx', ['nuxt', ...process.argv.slice(2)], { stdio: 'inherit' })
|
||||
}
|
||||
args.prerender = true
|
||||
await buildCommand.invoke(args)
|
||||
}
|
||||
})
|
||||
|
@ -33,11 +33,7 @@ export const writeTypes = async (nuxt: Nuxt) => {
|
||||
|
||||
const aliases = {
|
||||
...nuxt.options.alias,
|
||||
'#build': nuxt.options.buildDir,
|
||||
// The `@nuxt/nitro` types will be overwritten by packages/nitro/types/shims.d.ts
|
||||
'#config': '@nuxt/nitro',
|
||||
'#storage': '@nuxt/nitro',
|
||||
'#assets': '@nuxt/nitro'
|
||||
'#build': nuxt.options.buildDir
|
||||
}
|
||||
|
||||
// Exclude bridge alias types to support Volar
|
||||
|
@ -1,33 +0,0 @@
|
||||
import type { RequestListener } from 'http'
|
||||
import type { ListenOptions } from 'listhen'
|
||||
import { loading } from '@nuxt/ui-templates'
|
||||
|
||||
export function createServer (defaultApp?) {
|
||||
const listener = createDynamicFunction(defaultApp || createLoadingHandler('Loading...'))
|
||||
|
||||
async function listen (opts?: Partial<ListenOptions>) {
|
||||
const { listen } = await import('listhen')
|
||||
return listen(listener.call, opts)
|
||||
}
|
||||
|
||||
return {
|
||||
setApp: (app: RequestListener) => listener.set(app),
|
||||
listen
|
||||
}
|
||||
}
|
||||
|
||||
export function createLoadingHandler (message: string): RequestListener {
|
||||
return (_req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.statusCode = 503 /* Service Unavailable */
|
||||
res.end(loading({ loading: message }))
|
||||
}
|
||||
}
|
||||
|
||||
function createDynamicFunction<T extends (...args: any[]) => any> (initialValue: T) {
|
||||
let fn = initialValue
|
||||
return {
|
||||
set: (newFn: T) => { fn = newFn },
|
||||
call: ((...args: Parameters<T>) => fn(...args)) as T
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ export default defineBuildConfig({
|
||||
{ input: 'src/app/', outDir: 'dist/app/' },
|
||||
// Runtime dirs
|
||||
...[
|
||||
'core',
|
||||
'head',
|
||||
'pages'
|
||||
].map(name => ({ input: `src/${name}/runtime/`, outDir: `dist/${name}/runtime`, format: 'esm' } as BuildEntry))
|
||||
|
@ -31,7 +31,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "3.0.0",
|
||||
"@nuxt/nitro": "3.0.0",
|
||||
"@nuxt/schema": "3.0.0",
|
||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
||||
"@nuxt/vite-builder": "3.0.0",
|
||||
@ -45,13 +44,13 @@
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"globby": "^13.1.1",
|
||||
"h3": "^0.4.2",
|
||||
"h3": "^0.7.1",
|
||||
"hash-sum": "^2.0.0",
|
||||
"hookable": "^5.1.1",
|
||||
"knitwork": "^0.1.1",
|
||||
"magic-string": "^0.26.1",
|
||||
"mlly": "^0.5.1",
|
||||
"nitropack": "npm:nitropack-edge@latest",
|
||||
"nitropack": "^0.1.0",
|
||||
"nuxi": "3.0.0",
|
||||
"ohash": "^0.1.0",
|
||||
"ohmyfetch": "^0.4.15",
|
||||
@ -60,10 +59,12 @@
|
||||
"scule": "^0.2.1",
|
||||
"ufo": "^0.8.3",
|
||||
"unctx": "^1.1.4",
|
||||
"unenv": "^0.4.3",
|
||||
"unimport": "^0.1.4",
|
||||
"unplugin": "^0.6.1",
|
||||
"untyped": "^0.4.4",
|
||||
"vue": "^3.2.31",
|
||||
"vue-bundle-renderer": "^0.3.5",
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { ServerResponse } from 'http'
|
||||
import { Ref, ref, watch } from 'vue'
|
||||
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||
import { appendHeader } from 'h3'
|
||||
import type { NuxtApp } from '@nuxt/schema'
|
||||
import type { CompatibilityEvent } from 'h3'
|
||||
import destr from 'destr'
|
||||
import { useRequestEvent } from './ssr'
|
||||
import { useNuxtApp } from '#app'
|
||||
|
||||
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
|
||||
@ -34,8 +34,7 @@ export function useCookie <T=string> (name: string, _opts?: CookieOptions<T>): C
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.hooks.hookOnce('app:rendered', () => {
|
||||
if (cookie.value !== initialValue) {
|
||||
// @ts-ignore
|
||||
writeServerCookie(useSSRRes(nuxtApp), name, cookie.value, opts)
|
||||
writeServerCookie(useRequestEvent(nuxtApp), name, cookie.value, opts)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -43,15 +42,9 @@ export function useCookie <T=string> (name: string, _opts?: CookieOptions<T>): C
|
||||
return cookie as CookieRef<T>
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
function useSSRReq (nuxtApp?: NuxtApp = useNuxtApp()) { return nuxtApp.ssrContext?.req }
|
||||
|
||||
// @ts-ignore
|
||||
function useSSRRes (nuxtApp?: NuxtApp = useNuxtApp()) { return nuxtApp.ssrContext?.res }
|
||||
|
||||
function readRawCookies (opts: CookieOptions = {}): Record<string, string> {
|
||||
if (process.server) {
|
||||
return parse(useSSRReq().headers.cookie || '', opts)
|
||||
return parse(useRequestEvent()?.req.headers.cookie || '', opts)
|
||||
} else if (process.client) {
|
||||
return parse(document.cookie, opts)
|
||||
}
|
||||
@ -70,9 +63,9 @@ function writeClientCookie (name: string, value: any, opts: CookieSerializeOptio
|
||||
}
|
||||
}
|
||||
|
||||
function writeServerCookie (res: ServerResponse, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
||||
if (res) {
|
||||
function writeServerCookie (event: CompatibilityEvent, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
||||
if (event) {
|
||||
// TODO: Try to smart join with existing Set-Cookie headers
|
||||
appendHeader(res, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
appendHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FetchOptions, FetchRequest } from 'ohmyfetch'
|
||||
import type { TypedInternalResponse } from '@nuxt/nitro'
|
||||
import type { TypedInternalResponse } from 'nitropack'
|
||||
import { hash } from 'ohash'
|
||||
import { computed, isRef, Ref } from 'vue'
|
||||
import type { AsyncDataOptions, _Transform, KeyOfRes } from './asyncData'
|
||||
|
@ -60,10 +60,11 @@ export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}
|
||||
}
|
||||
const router = useRouter()
|
||||
if (process.server && useNuxtApp().ssrContext) {
|
||||
// Server-side redirection using h3 res from ssrContext
|
||||
const res = useNuxtApp().ssrContext?.res
|
||||
const redirectLocation = router.resolve(to).fullPath
|
||||
return sendRedirect(res, redirectLocation)
|
||||
const { ssrContext } = useNuxtApp()
|
||||
if (ssrContext && ssrContext.event) {
|
||||
const redirectLocation = router.resolve(to).fullPath
|
||||
return sendRedirect(ssrContext.event, redirectLocation)
|
||||
}
|
||||
}
|
||||
// Client-side redirection using vue-router
|
||||
return options.replace ? router.replace(to) : router.push(to)
|
||||
|
@ -1,11 +1,17 @@
|
||||
/* eslint-disable no-redeclare */
|
||||
import type { CompatibilityEvent } from 'h3'
|
||||
import { useNuxtApp } from '#app'
|
||||
import { NuxtApp } from '#app/nuxt'
|
||||
|
||||
export function useRequestHeaders<K extends string = string> (include: K[]): Record<K, string>;
|
||||
export function useRequestHeaders (): Readonly<Record<string, string>>;
|
||||
export function useRequestHeaders (include?) {
|
||||
if (process.client) { return {} }
|
||||
const headers: Record<string, string> = useNuxtApp().ssrContext?.req.headers ?? {}
|
||||
const headers: Record<string, string> = useNuxtApp().ssrContext?.event.req.headers ?? {}
|
||||
if (!include) { return headers }
|
||||
return Object.fromEntries(include.filter(key => headers[key]).map(key => [key, headers[key]]))
|
||||
}
|
||||
|
||||
export function useRequestEvent (nuxtApp: NuxtApp = useNuxtApp()): CompatibilityEvent {
|
||||
return nuxtApp.ssrContext?.event as CompatibilityEvent
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// We set __webpack_public_path via this import with webpack builder
|
||||
import '#build/paths.mjs'
|
||||
import { createSSRApp, createApp, nextTick } from 'vue'
|
||||
import { $fetch } from 'ohmyfetch'
|
||||
import { createNuxtApp, applyPlugins, normalizePlugins, CreateOptions } from '#app'
|
||||
import '#build/css'
|
||||
// @ts-ignore
|
||||
@ -10,6 +11,10 @@ import RootComponent from '#build/root-component.mjs'
|
||||
// @ts-ignore
|
||||
import AppComponent from '#build/app-component.mjs'
|
||||
|
||||
if (!globalThis.$fetch) {
|
||||
globalThis.$fetch = $fetch as any
|
||||
}
|
||||
|
||||
let entry: Function
|
||||
|
||||
const plugins = normalizePlugins(_plugins)
|
||||
|
@ -34,6 +34,7 @@ export const appPreset = defineUnimportPreset({
|
||||
'useLazyFetch',
|
||||
'useCookie',
|
||||
'useRequestHeaders',
|
||||
'useRequestEvent',
|
||||
'useRouter',
|
||||
'useRoute',
|
||||
'useActiveRoute',
|
||||
|
@ -1,95 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { wpfs, getNitroContext, createDevServer, resolveMiddleware, build, prepare, generate, writeTypes, scanMiddleware } from '@nuxt/nitro'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { ImportProtectionPlugin } from './plugins/import-protection'
|
||||
|
||||
export function initNitro (nuxt: Nuxt) {
|
||||
// Create contexts
|
||||
const nitroOptions = (nuxt.options as any).nitro || {}
|
||||
const nitroContext = getNitroContext(nuxt.options, nitroOptions)
|
||||
const nitroDevContext = getNitroContext(nuxt.options, { ...nitroOptions, preset: 'dev' })
|
||||
|
||||
nuxt.server = createDevServer(nitroDevContext)
|
||||
|
||||
if (nuxt.vfs) {
|
||||
nitroContext.vfs = nuxt.vfs
|
||||
nitroDevContext.vfs = nuxt.vfs
|
||||
}
|
||||
|
||||
// Connect hooks
|
||||
// @ts-ignore
|
||||
nuxt.hooks.addHooks(nitroContext.nuxtHooks)
|
||||
nuxt.hook('close', () => nitroContext._internal.hooks.callHook('close'))
|
||||
nitroContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
nitroContext._internal.hooks.hook('nitro:generate', ctx => nuxt.callHook('nitro:generate', ctx))
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.hooks.addHooks(nitroDevContext.nuxtHooks)
|
||||
nuxt.hook('close', () => nitroDevContext._internal.hooks.callHook('close'))
|
||||
nitroDevContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
|
||||
// Register nuxt3 protection patterns
|
||||
nitroDevContext._internal.hooks.hook('nitro:rollup:before', (ctx) => {
|
||||
ctx.rollupConfig.plugins.push(ImportProtectionPlugin.rollup({
|
||||
rootDir: nuxt.options.rootDir,
|
||||
patterns: [
|
||||
...['#app', /^#build(\/|$)/]
|
||||
.map(p => [p, 'Vue app aliases are not allowed in server routes.']) as [RegExp | string, string][]
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
// Add typed route responses
|
||||
nuxt.hook('prepare:types', (opts) => {
|
||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
|
||||
})
|
||||
|
||||
// Add nitro client plugin (to inject $fetch helper)
|
||||
nuxt.hook('app:resolve', (app) => {
|
||||
app.plugins.push({ src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs') })
|
||||
})
|
||||
|
||||
// Expose process.env.NITRO_PRESET
|
||||
nuxt.options.env.NITRO_PRESET = nitroContext.preset
|
||||
|
||||
// Wait for all modules to be ready
|
||||
nuxt.hook('modules:done', async () => {
|
||||
// Extend nitro with modules
|
||||
await nuxt.callHook('nitro:context', nitroContext)
|
||||
await nuxt.callHook('nitro:context', nitroDevContext)
|
||||
|
||||
// Resolve middleware
|
||||
const { middleware, legacyMiddleware } = await resolveMiddleware(nuxt)
|
||||
nuxt.server.setLegacyMiddleware(legacyMiddleware)
|
||||
nitroContext.middleware.push(...middleware)
|
||||
nitroDevContext.middleware.push(...middleware)
|
||||
})
|
||||
|
||||
// nuxt build/dev
|
||||
nuxt.hook('build:done', async () => {
|
||||
if (nuxt.options.dev) {
|
||||
await build(nitroDevContext)
|
||||
} else if (!nitroContext._nuxt.isStatic) {
|
||||
await prepare(nitroContext)
|
||||
await generate(nitroContext)
|
||||
await build(nitroContext)
|
||||
}
|
||||
})
|
||||
|
||||
nuxt.hook('build:before', async () => {
|
||||
const serverDirs = nitroDevContext._layers.map(layer => layer.serverDir)
|
||||
|
||||
nitroDevContext.scannedMiddleware = (
|
||||
await Promise.all(serverDirs.map(async dir => await scanMiddleware(dir)))
|
||||
).flat().sort((a, b) => b.route.localeCompare(a.route))
|
||||
|
||||
await writeTypes(nitroDevContext)
|
||||
})
|
||||
|
||||
// nuxt dev
|
||||
if (nuxt.options.dev) {
|
||||
nitroDevContext._internal.hooks.hook('nitro:compiled', () => { nuxt.server.watch() })
|
||||
nuxt.hook('build:compile', ({ compiler }) => { compiler.outputFileSystem = wpfs })
|
||||
nuxt.hook('server:devMiddleware', (m) => { nuxt.server.setDevMiddleware(m) })
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user