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
|
### 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
|
```ts
|
||||||
import config from '#config'
|
import { useRuntimeConfig } from '#nitro'
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
const result = await $fetch('https://my.api.com/test', {
|
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]
|
```ts [nuxt.config.js|ts]
|
||||||
export default {
|
export default {
|
||||||
nitro: {
|
nitro: {
|
||||||
preset: 'azure_functions'
|
preset: 'azure-functions'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -21,7 +21,7 @@ Make sure another preset isn't set in `nuxt.config`.
|
|||||||
export default {
|
export default {
|
||||||
nitro: {
|
nitro: {
|
||||||
// this is the default preset so you can also just omit it entirely
|
// 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).
|
You can also define a preset in a separate file (or publish as a separate npm package).
|
||||||
|
|
||||||
```ts [my-preset/index.ts]
|
```ts [my-preset/index.ts]
|
||||||
import type { NitroPreset } from '@nuxt/nitro'
|
import type { NitroPreset } from 'nitropack'
|
||||||
|
|
||||||
const myPreset: NitroPreset = {
|
const myPreset: NitroPreset = {
|
||||||
// Your custom configuration
|
// 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]
|
```ts [nuxt.config.js|ts]
|
||||||
export default {
|
export default {
|
||||||
nitro: {
|
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]
|
```js [nuxt.config.js|ts]
|
||||||
export default {
|
export default {
|
||||||
nitro: {
|
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]
|
```js [nuxt.config.js|ts]
|
||||||
export default {
|
export default {
|
||||||
nitro: {
|
nitro: {
|
||||||
preset: 'server',
|
preset: 'node-server',
|
||||||
timing: true
|
timing: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ You can use the [Nuxt config](/guide/directory-structure/nuxt.config) to explici
|
|||||||
```js [nuxt.config.js|ts]
|
```js [nuxt.config.js|ts]
|
||||||
export default {
|
export default {
|
||||||
nitro: {
|
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.
|
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).
|
[Read more about runtime config](/guide/features/runtime-config).
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export default defineNuxtConfig({
|
|||||||
```
|
```
|
||||||
|
|
||||||
```ts [server/api/hello.ts]
|
```ts [server/api/hello.ts]
|
||||||
import config from '#config'
|
import { useRuntimeConfig } from '#nitro';
|
||||||
|
|
||||||
export default (req, res) => {
|
export default (req, res) => {
|
||||||
// you can now access config.BASE_URL
|
// you can now access config.BASE_URL
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
"dev:build": "yarn run nuxi build playground",
|
"dev:build": "yarn run nuxi build playground",
|
||||||
"release": "yarn && yarn lint && FORCE_COLOR=1 lerna publish -m \"chore: release\" && yarn stub",
|
"release": "yarn && yarn lint && FORCE_COLOR=1 lerna publish -m \"chore: release\" && yarn stub",
|
||||||
"stub": "lerna run prepack -- --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: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: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"
|
"version": "yarn && git add yarn.lock"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||||
"@babel/plugin-transform-typescript": "^7.16.8",
|
"@babel/plugin-transform-typescript": "^7.16.8",
|
||||||
"@nuxt/kit": "3.0.0",
|
"@nuxt/kit": "3.0.0",
|
||||||
"@nuxt/nitro": "3.0.0",
|
|
||||||
"@nuxt/postcss8": "^1.1.3",
|
"@nuxt/postcss8": "^1.1.3",
|
||||||
"@nuxt/schema": "3.0.0",
|
"@nuxt/schema": "3.0.0",
|
||||||
"@vitejs/plugin-legacy": "^1.8.0",
|
"@vitejs/plugin-legacy": "^1.8.0",
|
||||||
@ -38,12 +37,13 @@
|
|||||||
"externality": "^0.2.1",
|
"externality": "^0.2.1",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
"globby": "^13.1.1",
|
"globby": "^13.1.1",
|
||||||
"h3": "^0.4.2",
|
"h3": "^0.7.1",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"knitwork": "^0.1.1",
|
"knitwork": "^0.1.1",
|
||||||
"magic-string": "^0.26.1",
|
"magic-string": "^0.26.1",
|
||||||
"mlly": "^0.5.1",
|
"mlly": "^0.5.1",
|
||||||
"murmurhash-es": "^0.1.1",
|
"murmurhash-es": "^0.1.1",
|
||||||
|
"nitropack": "^0.1.0",
|
||||||
"node-fetch": "^3.2.3",
|
"node-fetch": "^3.2.3",
|
||||||
"nuxi": "3.0.0",
|
"nuxi": "3.0.0",
|
||||||
"ohash": "^0.1.0",
|
"ohash": "^0.1.0",
|
||||||
@ -63,6 +63,7 @@
|
|||||||
"untyped": "^0.4.4",
|
"untyped": "^0.4.4",
|
||||||
"vite": "^2.9.1",
|
"vite": "^2.9.1",
|
||||||
"vite-plugin-vue2": "^1.9.3",
|
"vite-plugin-vue2": "^1.9.3",
|
||||||
|
"vue-bundle-renderer": "^0.3.5",
|
||||||
"vue-template-compiler": "^2.6.14"
|
"vue-template-compiler": "^2.6.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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 { NuxtModule } from '@nuxt/schema'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { componentsTypeTemplate } from '../../nuxt3/src/components/templates'
|
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) {
|
if (opts.nitro) {
|
||||||
await setupNitroBridge()
|
nuxt.hook('modules:done', async () => {
|
||||||
|
await setupNitroBridge()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (opts.app) {
|
if (opts.app) {
|
||||||
await setupAppBridge(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 fetch from 'node-fetch'
|
||||||
import fse from 'fs-extra'
|
import fsExtra from 'fs-extra'
|
||||||
import { addPluginTemplate, useNuxt } from '@nuxt/kit'
|
import { addPluginTemplate, resolvePath, useNuxt } from '@nuxt/kit'
|
||||||
import { joinURL, stringifyQuery, withoutTrailingSlash } from 'ufo'
|
import { joinURL, stringifyQuery, withoutTrailingSlash } from 'ufo'
|
||||||
import { resolve, join } from 'pathe'
|
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 { AsyncLoadingPlugin } from './async-loading'
|
||||||
import { distDir } from './dirs'
|
import { distDir } from './dirs'
|
||||||
import { isDirectory, readDirRecursively } from './vite/utils/fs'
|
import { isDirectory, readDirRecursively } from './vite/utils/fs'
|
||||||
|
|
||||||
export function setupNitroBridge () {
|
export async function setupNitroBridge () {
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
// Ensure we're not just building with 'static' target
|
// 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')
|
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.buildAssetsDir = nuxt.options.app.buildAssetsDir || nuxt.options.app.assetsPath
|
||||||
nuxt.options.app.assetsPath = nuxt.options.app.buildAssetsDir
|
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.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`
|
// Nitro expects app config on `config.app` rather than `config._app`
|
||||||
nuxt.options.publicRuntimeConfig.app = nuxt.options.publicRuntimeConfig.app || {}
|
nuxt.options.publicRuntimeConfig.app = nuxt.options.publicRuntimeConfig.app || {}
|
||||||
Object.assign(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
|
// Resolve config
|
||||||
const nitroOptions = (nuxt.options as any).nitro || {}
|
const _nitroConfig = (nuxt.options as any).nitro || {} as NitroConfig
|
||||||
const nitroContext = getNitroContext(nuxt.options, nitroOptions)
|
const nitroConfig: NitroConfig = defu(_nitroConfig, <NitroConfig>{
|
||||||
const nitroDevContext = getNitroContext(nuxt.options, { ...nitroOptions, preset: 'dev' })
|
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
|
// Renderer
|
||||||
for (const context of [nitroContext, nitroDevContext]) {
|
'#vue-renderer': resolve(distDir, 'runtime/nitro/vue2'),
|
||||||
context._nuxt.rootDir = resolve(context._nuxt.rootDir)
|
'#vue2-server-renderer': 'vue-server-renderer/' + (nuxt.options.dev ? 'build.dev.js' : 'build.prod.js'),
|
||||||
context._nuxt.srcDir = resolve(context._nuxt.srcDir)
|
|
||||||
context._nuxt.buildDir = resolve(context._nuxt.buildDir)
|
// Error renderer
|
||||||
context._nuxt.generateDir = resolve(context._nuxt.generateDir)
|
'#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
|
// Connect hooks
|
||||||
nuxt.addHooks(nitroContext.nuxtHooks)
|
nuxt.hook('close', () => nitro.hooks.callHook('close'))
|
||||||
nuxt.hook('close', () => nitroContext._internal.hooks.callHook('close'))
|
nitro.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||||
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))
|
|
||||||
|
|
||||||
// Use custom document template if provided
|
// Use custom document template if provided
|
||||||
if (nuxt.options.appTemplatePath) {
|
if (nuxt.options.appTemplatePath) {
|
||||||
@ -82,7 +148,7 @@ export function setupNitroBridge () {
|
|||||||
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
||||||
for (const file of publicFiles) {
|
for (const file of publicFiles) {
|
||||||
try {
|
try {
|
||||||
fse.rmSync(join(clientDist, file))
|
fsExtra.rmSync(join(clientDist, file))
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,26 +159,17 @@ export function setupNitroBridge () {
|
|||||||
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, nuxt.options.app.buildAssetsDir))
|
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, nuxt.options.app.buildAssetsDir))
|
||||||
|
|
||||||
if (await isDirectory(nestedAssetsPath)) {
|
if (await isDirectory(nestedAssetsPath)) {
|
||||||
await fse.copy(nestedAssetsPath, clientDist, { recursive: true })
|
await fsExtra.copy(nestedAssetsPath, clientDist, { recursive: true })
|
||||||
await fse.remove(nestedAssetsPath)
|
await fsExtra.remove(nestedAssetsPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nuxt.hook('nitro:generate', updateViteBase)
|
nuxt.hook('nitro:generate', updateViteBase)
|
||||||
nuxt.hook('generate:before', updateViteBase)
|
nuxt.hook('generate:before', updateViteBase)
|
||||||
|
|
||||||
// Expose process.env.NITRO_PRESET
|
|
||||||
nuxt.options.env.NITRO_PRESET = nitroContext.preset
|
|
||||||
|
|
||||||
// .ts is supported for serverMiddleware
|
// .ts is supported for serverMiddleware
|
||||||
nuxt.options.extensions.push('ts')
|
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.
|
// Disable server sourceMap, esbuild will generate for it.
|
||||||
nuxt.hook('webpack:config', (webpackConfigs) => {
|
nuxt.hook('webpack:config', (webpackConfigs) => {
|
||||||
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
||||||
@ -134,8 +191,8 @@ export function setupNitroBridge () {
|
|||||||
|
|
||||||
// Nitro client plugin
|
// Nitro client plugin
|
||||||
addPluginTemplate({
|
addPluginTemplate({
|
||||||
filename: 'nitro.client.mjs',
|
filename: 'nitro-bridge.client.mjs',
|
||||||
src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs')
|
src: resolve(distDir, 'runtime/nitro-bridge.client.mjs')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Nitro server plugin (for vue-meta)
|
// Nitro server plugin (for vue-meta)
|
||||||
@ -167,21 +224,27 @@ export function setupNitroBridge () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Wait for all modules to be ready
|
// Setup handlers
|
||||||
nuxt.hook('modules:done', async () => {
|
const devMidlewareHandler = dynamicEventHandler()
|
||||||
// Extend nitro with modules
|
nitro.options.devHandlers.unshift({ handler: devMidlewareHandler })
|
||||||
await nuxt.callHook('nitro:context', nitroContext)
|
const { handlers, devHandlers } = await resolveHandlers(nuxt)
|
||||||
await nuxt.callHook('nitro:context', nitroDevContext)
|
nitro.options.handlers.push(...handlers)
|
||||||
|
nitro.options.devHandlers.push(...devHandlers)
|
||||||
// Resolve middleware
|
nitro.options.handlers.unshift({
|
||||||
const { middleware, legacyMiddleware } = await resolveMiddleware(nuxt)
|
route: '/_nitro',
|
||||||
if (nuxt.server) {
|
lazy: true,
|
||||||
nuxt.server.setLegacyMiddleware(legacyMiddleware)
|
handler: resolve(distDir, 'runtime/nitro/renderer')
|
||||||
}
|
|
||||||
nitroContext.middleware.push(...middleware)
|
|
||||||
nitroDevContext.middleware.push(...middleware)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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
|
// Add typed route responses
|
||||||
nuxt.hook('prepare:types', (opts) => {
|
nuxt.hook('prepare:types', (opts) => {
|
||||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
|
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
|
||||||
@ -189,58 +252,70 @@ export function setupNitroBridge () {
|
|||||||
|
|
||||||
// nuxt prepare
|
// nuxt prepare
|
||||||
nuxt.hook('build:done', async () => {
|
nuxt.hook('build:done', async () => {
|
||||||
nitroDevContext.scannedMiddleware = await scanMiddleware(nitroDevContext._nuxt.serverDir)
|
await writeTypes(nitro)
|
||||||
await writeTypes(nitroDevContext)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// nuxt build/dev
|
// nuxt build/dev
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nuxt.options.build._minifyServer = false
|
nuxt.options.build._minifyServer = false
|
||||||
nuxt.options.build.standalone = false
|
nuxt.options.build.standalone = false
|
||||||
|
|
||||||
|
const waitUntilCompile = new Promise<void>(resolve => nitro.hooks.hook('nitro:compiled', () => resolve()))
|
||||||
nuxt.hook('build:done', async () => {
|
nuxt.hook('build:done', async () => {
|
||||||
if (nuxt.options._prepare) { return }
|
if (nuxt.options._prepare) { return }
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
await build(nitroDevContext)
|
await build(nitro)
|
||||||
} else if (!nitroContext._nuxt.isStatic) {
|
await waitUntilCompile
|
||||||
await prepare(nitroContext)
|
// nitro.hooks.callHook('nitro:dev:reload')
|
||||||
await generate(nitroContext)
|
} else {
|
||||||
await build(nitroContext)
|
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
|
// nuxt dev
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
nitroDevContext._internal.hooks.hook('nitro:compiled', () => { nuxt.server.watch() })
|
nuxt.hook('build:compile', ({ compiler }) => {
|
||||||
nuxt.hook('build:compile', ({ compiler }) => { compiler.outputFileSystem = wpfs })
|
compiler.outputFileSystem = { ...fsExtra, join } as any
|
||||||
nuxt.hook('server:devMiddleware', (m) => { nuxt.server.setDevMiddleware(m) })
|
})
|
||||||
|
nuxt.hook('server:devMiddleware', (m) => { devMidlewareHandler.set(toEventHandler(m)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// nuxt generate
|
// nuxt generate
|
||||||
nuxt.options.generate.dir = nitroContext.output.publicDir
|
nuxt.options.generate.dir = nitro.options.output.publicDir
|
||||||
nuxt.options.generate.manifest = false
|
nuxt.options.generate.manifest = false
|
||||||
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
||||||
ignore.push(nitroContext.output.dir)
|
ignore.push(nitro.options.output.dir)
|
||||||
ignore.push(nitroContext.output.serverDir)
|
ignore.push(nitro.options.output.serverDir)
|
||||||
if (nitroContext.output.publicDir) {
|
if (nitro.options.output.publicDir) {
|
||||||
ignore.push(nitroContext.output.publicDir)
|
ignore.push(nitro.options.output.publicDir)
|
||||||
}
|
}
|
||||||
ignore.push(...nitroContext.ignore)
|
|
||||||
})
|
})
|
||||||
nuxt.hook('generate:before', async () => {
|
nuxt.hook('generate:before', async () => {
|
||||||
await prepare(nitroContext)
|
console.log('generate:before')
|
||||||
|
await prepare(nitro)
|
||||||
})
|
})
|
||||||
nuxt.hook('generate:extendRoutes', async () => {
|
nuxt.hook('generate:extendRoutes', async () => {
|
||||||
await build(nitroDevContext)
|
console.log('generate:extendRoutes')
|
||||||
|
await build(nitro)
|
||||||
await nuxt.server.reload()
|
await nuxt.server.reload()
|
||||||
})
|
})
|
||||||
nuxt.hook('generate:done', async () => {
|
nuxt.hook('generate:done', async () => {
|
||||||
|
console.log('generate:done')
|
||||||
await nuxt.server.close()
|
await nuxt.server.close()
|
||||||
await build(nitroContext)
|
await build(nitro)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNuxt2DevServer (nitroContext: NitroContext) {
|
function createNuxt2DevServer (nitro: Nitro) {
|
||||||
const server = createDevServer(nitroContext)
|
const server = createDevServer(nitro)
|
||||||
|
|
||||||
const listeners = []
|
const listeners = []
|
||||||
async function listen (port) {
|
async function listen (port) {
|
||||||
@ -277,3 +352,30 @@ function createNuxt2DevServer (nitroContext: NitroContext) {
|
|||||||
ready () { }
|
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) {
|
export async function buildClient (ctx: ViteBuildContext) {
|
||||||
const alias = {
|
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) {
|
for (const p of ctx.builder.plugins) {
|
||||||
alias[p.name] = p.mode === 'server'
|
alias[p.name] = p.mode === 'server'
|
||||||
|
@ -58,8 +58,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
ssr: ctx.nuxt.options.ssr ?? true,
|
ssr: ctx.nuxt.options.ssr ?? true,
|
||||||
ssrManifest: true,
|
ssrManifest: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// Private nitro alias: packages/nitro/src/rollup/config.ts#L234
|
external: ['#nitro'],
|
||||||
external: ['#_config'],
|
|
||||||
input: resolve(ctx.nuxt.options.buildDir, 'server.js'),
|
input: resolve(ctx.nuxt.options.buildDir, 'server.js'),
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: 'server.mjs',
|
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 { NuxtConfig as _NuxtConfig } from '@nuxt/schema'
|
||||||
import type { MetaInfo } from 'vue-meta'
|
import type { MetaInfo } from 'vue-meta'
|
||||||
import type { PluginOptions as ScriptSetupPluginOptions } from 'unplugin-vue2-script-setup/dist'
|
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": {
|
"devDependencies": {
|
||||||
"@nuxt/kit": "3.0.0",
|
"@nuxt/kit": "3.0.0",
|
||||||
"@nuxt/schema": "3.0.0",
|
"@nuxt/schema": "3.0.0",
|
||||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
|
||||||
"@types/clear": "^0",
|
"@types/clear": "^0",
|
||||||
"@types/mri": "^1.1.1",
|
"@types/mri": "^1.1.1",
|
||||||
"@types/rimraf": "^3",
|
"@types/rimraf": "^3",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { promises as fsp } from 'fs'
|
import { promises as fsp } from 'fs'
|
||||||
import { join, resolve } from 'pathe'
|
import { join, resolve } from 'pathe'
|
||||||
import { createApp, lazyHandle } from 'h3'
|
import { createApp, lazyHandle } from 'h3'
|
||||||
import { createServer } from '../utils/server'
|
import { listen } from 'listhen'
|
||||||
import { writeTypes } from '../utils/prepare'
|
import { writeTypes } from '../utils/prepare'
|
||||||
import { loadKit } from '../utils/kit'
|
import { loadKit } from '../utils/kit'
|
||||||
import { clearDir } from '../utils/fs'
|
import { clearDir } from '../utils/fs'
|
||||||
@ -35,7 +35,6 @@ export default defineNuxtCommand({
|
|||||||
await buildNuxt(nuxt)
|
await buildNuxt(nuxt)
|
||||||
|
|
||||||
const app = createApp()
|
const app = createApp()
|
||||||
const server = createServer(app)
|
|
||||||
|
|
||||||
const serveFile = (filePath: string) => lazyHandle(async () => {
|
const serveFile = (filePath: string) => lazyHandle(async () => {
|
||||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||||
@ -65,6 +64,6 @@ export default defineNuxtCommand({
|
|||||||
</ul>
|
</ul>
|
||||||
</html>`)
|
</html>`)
|
||||||
|
|
||||||
await server.listen()
|
await listen(app)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,7 @@ import { defineNuxtCommand } from './index'
|
|||||||
export default defineNuxtCommand({
|
export default defineNuxtCommand({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'build',
|
name: 'build',
|
||||||
usage: 'npx nuxi build [rootDir]',
|
usage: 'npx nuxi build [--prerender] [rootDir]',
|
||||||
description: 'Build nuxt for production deployment'
|
description: 'Build nuxt for production deployment'
|
||||||
},
|
},
|
||||||
async invoke (args) {
|
async invoke (args) {
|
||||||
@ -17,7 +17,12 @@ export default defineNuxtCommand({
|
|||||||
|
|
||||||
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
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)
|
await clearDir(nuxt.options.buildDir)
|
||||||
|
|
||||||
|
@ -4,11 +4,10 @@ import { debounce } from 'perfect-debounce'
|
|||||||
import type { Nuxt } from '@nuxt/schema'
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { withTrailingSlash } from 'ufo'
|
import { withTrailingSlash } from 'ufo'
|
||||||
import { createServer, createLoadingHandler } from '../utils/server'
|
|
||||||
import { showBanner } from '../utils/banner'
|
import { showBanner } from '../utils/banner'
|
||||||
import { writeTypes } from '../utils/prepare'
|
import { writeTypes } from '../utils/prepare'
|
||||||
import { loadKit } from '../utils/kit'
|
import { loadKit } from '../utils/kit'
|
||||||
import { clearDir } from '../utils/fs'
|
import { importModule } from '../utils/cjs'
|
||||||
import { defineNuxtCommand } from './index'
|
import { defineNuxtCommand } from './index'
|
||||||
|
|
||||||
export default defineNuxtCommand({
|
export default defineNuxtCommand({
|
||||||
@ -19,8 +18,21 @@ export default defineNuxtCommand({
|
|||||||
},
|
},
|
||||||
async invoke (args) {
|
async invoke (args) {
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
|
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,
|
clipboard: args.clipboard,
|
||||||
open: args.open || args.o,
|
open: args.open || args.o,
|
||||||
port: args.port || args.p || process.env.NUXT_PORT,
|
port: args.port || args.p || process.env.NUXT_PORT,
|
||||||
@ -39,31 +51,29 @@ export default defineNuxtCommand({
|
|||||||
let currentNuxt: Nuxt
|
let currentNuxt: Nuxt
|
||||||
const load = async (isRestart: boolean, reason?: string) => {
|
const load = async (isRestart: boolean, reason?: string) => {
|
||||||
try {
|
try {
|
||||||
const message = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
||||||
server.setApp(createLoadingHandler(message))
|
currentHandler = null
|
||||||
if (isRestart) {
|
if (isRestart) {
|
||||||
consola.info(message)
|
consola.info(loadingMessage)
|
||||||
}
|
}
|
||||||
if (currentNuxt) {
|
if (currentNuxt) {
|
||||||
await currentNuxt.close()
|
await currentNuxt.close()
|
||||||
}
|
}
|
||||||
currentNuxt = await loadNuxt({ rootDir, dev: true, ready: false })
|
currentNuxt = await loadNuxt({ rootDir, dev: true, ready: false })
|
||||||
await clearDir(currentNuxt.options.buildDir)
|
|
||||||
await currentNuxt.ready()
|
await currentNuxt.ready()
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
writeTypes(currentNuxt).catch(console.error),
|
writeTypes(currentNuxt).catch(console.error),
|
||||||
buildNuxt(currentNuxt)
|
buildNuxt(currentNuxt)
|
||||||
])
|
])
|
||||||
server.setApp(currentNuxt.server.app)
|
currentHandler = currentNuxt.server.app
|
||||||
if (isRestart && args.clear !== false) {
|
if (isRestart && args.clear !== false) {
|
||||||
showBanner()
|
showBanner()
|
||||||
listener.showURL()
|
listener.showURL()
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
||||||
server.setApp(createLoadingHandler(
|
currentHandler = null
|
||||||
'Error while loading nuxt. Please check console and fix errors.'
|
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,32 +1,14 @@
|
|||||||
import { execa } from 'execa'
|
import buildCommand from './build'
|
||||||
import { resolve } from 'pathe'
|
|
||||||
import { isNuxt3 } from '@nuxt/kit'
|
|
||||||
|
|
||||||
import { loadKit } from '../utils/kit'
|
|
||||||
import { writeTypes } from '../utils/prepare'
|
|
||||||
import { defineNuxtCommand } from './index'
|
import { defineNuxtCommand } from './index'
|
||||||
|
|
||||||
export default defineNuxtCommand({
|
export default defineNuxtCommand({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'generate',
|
name: 'generate',
|
||||||
usage: 'npx nuxi generate [rootDir]',
|
usage: 'npx nuxi generate [rootDir]',
|
||||||
description: ''
|
description: 'Build Nuxt and prerender static routes'
|
||||||
},
|
},
|
||||||
async invoke (args) {
|
async invoke (args) {
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
args.prerender = true
|
||||||
const rootDir = resolve(args._[0] || '.')
|
await buildCommand.invoke(args)
|
||||||
|
|
||||||
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' })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -33,11 +33,7 @@ export const writeTypes = async (nuxt: Nuxt) => {
|
|||||||
|
|
||||||
const aliases = {
|
const aliases = {
|
||||||
...nuxt.options.alias,
|
...nuxt.options.alias,
|
||||||
'#build': nuxt.options.buildDir,
|
'#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'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude bridge alias types to support Volar
|
// 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/' },
|
{ input: 'src/app/', outDir: 'dist/app/' },
|
||||||
// Runtime dirs
|
// Runtime dirs
|
||||||
...[
|
...[
|
||||||
|
'core',
|
||||||
'head',
|
'head',
|
||||||
'pages'
|
'pages'
|
||||||
].map(name => ({ input: `src/${name}/runtime/`, outDir: `dist/${name}/runtime`, format: 'esm' } as BuildEntry))
|
].map(name => ({ input: `src/${name}/runtime/`, outDir: `dist/${name}/runtime`, format: 'esm' } as BuildEntry))
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/kit": "3.0.0",
|
"@nuxt/kit": "3.0.0",
|
||||||
"@nuxt/nitro": "3.0.0",
|
|
||||||
"@nuxt/schema": "3.0.0",
|
"@nuxt/schema": "3.0.0",
|
||||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
||||||
"@nuxt/vite-builder": "3.0.0",
|
"@nuxt/vite-builder": "3.0.0",
|
||||||
@ -45,13 +44,13 @@
|
|||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
"globby": "^13.1.1",
|
"globby": "^13.1.1",
|
||||||
"h3": "^0.4.2",
|
"h3": "^0.7.1",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"hookable": "^5.1.1",
|
"hookable": "^5.1.1",
|
||||||
"knitwork": "^0.1.1",
|
"knitwork": "^0.1.1",
|
||||||
"magic-string": "^0.26.1",
|
"magic-string": "^0.26.1",
|
||||||
"mlly": "^0.5.1",
|
"mlly": "^0.5.1",
|
||||||
"nitropack": "npm:nitropack-edge@latest",
|
"nitropack": "^0.1.0",
|
||||||
"nuxi": "3.0.0",
|
"nuxi": "3.0.0",
|
||||||
"ohash": "^0.1.0",
|
"ohash": "^0.1.0",
|
||||||
"ohmyfetch": "^0.4.15",
|
"ohmyfetch": "^0.4.15",
|
||||||
@ -60,10 +59,12 @@
|
|||||||
"scule": "^0.2.1",
|
"scule": "^0.2.1",
|
||||||
"ufo": "^0.8.3",
|
"ufo": "^0.8.3",
|
||||||
"unctx": "^1.1.4",
|
"unctx": "^1.1.4",
|
||||||
|
"unenv": "^0.4.3",
|
||||||
"unimport": "^0.1.4",
|
"unimport": "^0.1.4",
|
||||||
"unplugin": "^0.6.1",
|
"unplugin": "^0.6.1",
|
||||||
"untyped": "^0.4.4",
|
"untyped": "^0.4.4",
|
||||||
"vue": "^3.2.31",
|
"vue": "^3.2.31",
|
||||||
|
"vue-bundle-renderer": "^0.3.5",
|
||||||
"vue-router": "^4.0.14"
|
"vue-router": "^4.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { ServerResponse } from 'http'
|
|
||||||
import { Ref, ref, watch } from 'vue'
|
import { Ref, ref, watch } from 'vue'
|
||||||
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||||
import { appendHeader } from 'h3'
|
import { appendHeader } from 'h3'
|
||||||
import type { NuxtApp } from '@nuxt/schema'
|
import type { CompatibilityEvent } from 'h3'
|
||||||
import destr from 'destr'
|
import destr from 'destr'
|
||||||
|
import { useRequestEvent } from './ssr'
|
||||||
import { useNuxtApp } from '#app'
|
import { useNuxtApp } from '#app'
|
||||||
|
|
||||||
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
|
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()
|
const nuxtApp = useNuxtApp()
|
||||||
nuxtApp.hooks.hookOnce('app:rendered', () => {
|
nuxtApp.hooks.hookOnce('app:rendered', () => {
|
||||||
if (cookie.value !== initialValue) {
|
if (cookie.value !== initialValue) {
|
||||||
// @ts-ignore
|
writeServerCookie(useRequestEvent(nuxtApp), name, cookie.value, opts)
|
||||||
writeServerCookie(useSSRRes(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>
|
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> {
|
function readRawCookies (opts: CookieOptions = {}): Record<string, string> {
|
||||||
if (process.server) {
|
if (process.server) {
|
||||||
return parse(useSSRReq().headers.cookie || '', opts)
|
return parse(useRequestEvent()?.req.headers.cookie || '', opts)
|
||||||
} else if (process.client) {
|
} else if (process.client) {
|
||||||
return parse(document.cookie, opts)
|
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 = {}) {
|
function writeServerCookie (event: CompatibilityEvent, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
||||||
if (res) {
|
if (event) {
|
||||||
// TODO: Try to smart join with existing Set-Cookie headers
|
// 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 { FetchOptions, FetchRequest } from 'ohmyfetch'
|
||||||
import type { TypedInternalResponse } from '@nuxt/nitro'
|
import type { TypedInternalResponse } from 'nitropack'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import { computed, isRef, Ref } from 'vue'
|
import { computed, isRef, Ref } from 'vue'
|
||||||
import type { AsyncDataOptions, _Transform, KeyOfRes } from './asyncData'
|
import type { AsyncDataOptions, _Transform, KeyOfRes } from './asyncData'
|
||||||
|
@ -60,10 +60,11 @@ export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}
|
|||||||
}
|
}
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
if (process.server && useNuxtApp().ssrContext) {
|
if (process.server && useNuxtApp().ssrContext) {
|
||||||
// Server-side redirection using h3 res from ssrContext
|
const { ssrContext } = useNuxtApp()
|
||||||
const res = useNuxtApp().ssrContext?.res
|
if (ssrContext && ssrContext.event) {
|
||||||
const redirectLocation = router.resolve(to).fullPath
|
const redirectLocation = router.resolve(to).fullPath
|
||||||
return sendRedirect(res, redirectLocation)
|
return sendRedirect(ssrContext.event, redirectLocation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Client-side redirection using vue-router
|
// Client-side redirection using vue-router
|
||||||
return options.replace ? router.replace(to) : router.push(to)
|
return options.replace ? router.replace(to) : router.push(to)
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
/* eslint-disable no-redeclare */
|
/* eslint-disable no-redeclare */
|
||||||
|
import type { CompatibilityEvent } from 'h3'
|
||||||
import { useNuxtApp } from '#app'
|
import { useNuxtApp } from '#app'
|
||||||
|
import { NuxtApp } from '#app/nuxt'
|
||||||
|
|
||||||
export function useRequestHeaders<K extends string = string> (include: K[]): Record<K, string>;
|
export function useRequestHeaders<K extends string = string> (include: K[]): Record<K, string>;
|
||||||
export function useRequestHeaders (): Readonly<Record<string, string>>;
|
export function useRequestHeaders (): Readonly<Record<string, string>>;
|
||||||
export function useRequestHeaders (include?) {
|
export function useRequestHeaders (include?) {
|
||||||
if (process.client) { return {} }
|
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 }
|
if (!include) { return headers }
|
||||||
return Object.fromEntries(include.filter(key => headers[key]).map(key => [key, headers[key]]))
|
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
|
// We set __webpack_public_path via this import with webpack builder
|
||||||
import '#build/paths.mjs'
|
import '#build/paths.mjs'
|
||||||
import { createSSRApp, createApp, nextTick } from 'vue'
|
import { createSSRApp, createApp, nextTick } from 'vue'
|
||||||
|
import { $fetch } from 'ohmyfetch'
|
||||||
import { createNuxtApp, applyPlugins, normalizePlugins, CreateOptions } from '#app'
|
import { createNuxtApp, applyPlugins, normalizePlugins, CreateOptions } from '#app'
|
||||||
import '#build/css'
|
import '#build/css'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -10,6 +11,10 @@ import RootComponent from '#build/root-component.mjs'
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import AppComponent from '#build/app-component.mjs'
|
import AppComponent from '#build/app-component.mjs'
|
||||||
|
|
||||||
|
if (!globalThis.$fetch) {
|
||||||
|
globalThis.$fetch = $fetch as any
|
||||||
|
}
|
||||||
|
|
||||||
let entry: Function
|
let entry: Function
|
||||||
|
|
||||||
const plugins = normalizePlugins(_plugins)
|
const plugins = normalizePlugins(_plugins)
|
||||||
|
@ -34,6 +34,7 @@ export const appPreset = defineUnimportPreset({
|
|||||||
'useLazyFetch',
|
'useLazyFetch',
|
||||||
'useCookie',
|
'useCookie',
|
||||||
'useRequestHeaders',
|
'useRequestHeaders',
|
||||||
|
'useRequestEvent',
|
||||||
'useRouter',
|
'useRouter',
|
||||||
'useRoute',
|
'useRoute',
|
||||||
'useActiveRoute',
|
'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