diff --git a/.gitignore b/.gitignore
index 25f68e93ed..6ad514e017 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
node_modules
jspm_packages
+# Nitro
+nitro.d.ts
+
package-lock.json
# */**/yarn.lock
diff --git a/README.md b/README.md
index 8530ea5992..f0ee144d83 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
- Clone repository
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable` (use `npm i -g corepack` for Node.js < 16.10)
- Install dependencies using `yarn install`
+- Start playground with `yarn dev` and open http://localhost:3000
## License
diff --git a/build.config.ts b/build.config.ts
index a7a1e099fb..2145295769 100644
--- a/build.config.ts
+++ b/build.config.ts
@@ -4,6 +4,7 @@ export default defineBuildConfig({
declaration: true,
entries: [
'src/index',
+ 'src/cli',
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
],
dependencies: [
diff --git a/package.json b/package.json
index c05cc0d5b6..45bf1a1aa1 100644
--- a/package.json
+++ b/package.json
@@ -3,22 +3,31 @@
"version": "0.0.0",
"license": "MIT",
"type": "module",
+ "exports": {
+ ".": "./dist/index.mjs",
+ "./cli": "./dist/cli.mjs"
+ },
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
+ "bin": {
+ "nitro": "./dist/cli.mjs",
+ "nitropack": "./dist/cli.mjs"
+ },
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
+ "dev": "yarn nitro dev playground",
+ "dev:build": "yarn nitro build playground",
"lint": "eslint --ext .ts,.mjs,.cjs .",
+ "nitro": "jiti ./src/cli.ts",
"prepack": "yarn build"
},
"dependencies": {
"@cloudflare/kv-asset-handler": "^0.2.0",
"@netlify/functions": "^0.11.0",
- "@nuxt/design": "0.1.5",
"@nuxt/devalue": "^2.0.0",
- "@nuxt/kit": "npm:@nuxt/kit-edge@latest",
"@rollup/plugin-alias": "^3.1.9",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-inject": "^4.0.4",
@@ -52,6 +61,7 @@
"listhen": "^0.2.6",
"mime": "^3.0.0",
"mlly": "^0.4.1",
+ "mri": "^1.2.0",
"node-fetch": "^3.2.0",
"ohmyfetch": "^0.4.15",
"ora": "^6.0.1",
@@ -73,7 +83,6 @@
"vue-server-renderer": "^2.6.14"
},
"devDependencies": {
- "@nuxt/schema": "npm:@nuxt/schema-edge@latest",
"@nuxtjs/eslint-config-typescript": "^8.0.0",
"@types/fs-extra": "^9.0.13",
"@types/http-proxy": "^1.17.8",
diff --git a/playground/public/index.html b/playground/public/index.html
new file mode 100644
index 0000000000..00acbc26c5
--- /dev/null
+++ b/playground/public/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Welcome to Nitro!
+
+
+
+ Welcome to Nitro!
+
+
+
+
diff --git a/playground/server/api/test.ts b/playground/server/api/test.ts
new file mode 100644
index 0000000000..02e438d09e
--- /dev/null
+++ b/playground/server/api/test.ts
@@ -0,0 +1 @@
+export default () => 'Nitro works!'
diff --git a/playground/server/middleware/test.ts b/playground/server/middleware/test.ts
new file mode 100644
index 0000000000..f96bfd9244
--- /dev/null
+++ b/playground/server/middleware/test.ts
@@ -0,0 +1,5 @@
+
+export default (_req, _res, next) => {
+ console.log('middleware!')
+ next()
+}
diff --git a/src/build.ts b/src/build.ts
index 1e8e9e5f7b..62049b6111 100644
--- a/src/build.ts
+++ b/src/build.ts
@@ -4,21 +4,19 @@ import * as rollup from 'rollup'
import fse from 'fs-extra'
import { printFSTree } from './utils/tree'
import { getRollupConfig } from './rollup/config'
-import { hl, prettyPath, serializeTemplate, writeFile, isDirectory, replaceAll } from './utils'
-import { NitroContext } from './context'
+import { prettyPath, writeFile, isDirectory, replaceAll } from './utils'
import { scanMiddleware } from './server/middleware'
+import type { Nitro } from './types'
-export async function prepare (nitroContext: NitroContext) {
- consola.info(`Nitro preset is ${hl(nitroContext.preset)}`)
+export async function prepare (nitro: Nitro) {
+ await cleanupDir(nitro.options.output.dir)
- await cleanupDir(nitroContext.output.dir)
-
- if (!nitroContext.output.publicDir.startsWith(nitroContext.output.dir)) {
- await cleanupDir(nitroContext.output.publicDir)
+ if (!nitro.options.output.publicDir.startsWith(nitro.options.output.dir)) {
+ await cleanupDir(nitro.options.output.publicDir)
}
- if (!nitroContext.output.serverDir.startsWith(nitroContext.output.dir)) {
- await cleanupDir(nitroContext.output.serverDir)
+ if (!nitro.options.output.serverDir.startsWith(nitro.options.output.dir)) {
+ await cleanupDir(nitro.options.output.serverDir)
}
}
@@ -27,58 +25,55 @@ async function cleanupDir (dir: string) {
await fse.emptyDir(dir)
}
-export async function generate (nitroContext: NitroContext) {
+export async function generate (nitro: Nitro) {
consola.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(nitro.options.buildDir, 'dist/client')
+ if (await isDirectory(clientDist)) {
+ await fse.copy(clientDist, join(nitro.options.output.publicDir, nitro.options.publicPath))
}
- 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)
+ const publicDir = nitro.options.publicDir
+ if (await isDirectory(publicDir)) {
+ await fse.copy(publicDir, nitro.options.output.publicDir)
}
- consola.success('Generated public ' + prettyPath(nitroContext.output.publicDir))
+ consola.success('Generated public ' + prettyPath(nitro.options.output.publicDir))
}
-export async function build (nitroContext: NitroContext) {
+export async function build (nitro: Nitro) {
// 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)
+ // const htmlSrc = resolve(nitro.options.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 = nitro.vfs[htmlTemplate.src] || await fse.readFile(htmlTemplate.src, 'utf-8')
+ // await nitro.hooks.callHook('nitro:document', htmlTemplate)
+ // const compiled = 'export default ' + serializeTemplate(htmlTemplate.contents)
+ // await writeFile(htmlTemplate.dst, compiled)
+
+ nitro.options.rollupConfig = getRollupConfig(nitro)
+ await nitro.hooks.callHook('nitro:rollup:before', nitro)
+ return nitro.options.dev ? _watch(nitro) : _build(nitro)
}
-export async function writeTypes (nitroContext: NitroContext) {
+async function writeTypes (nitro: Nitro) {
const routeTypes: Record = {}
const middleware = [
- ...nitroContext.scannedMiddleware,
- ...nitroContext.middleware
+ ...nitro.scannedMiddleware,
+ ...nitro.options.middleware
]
for (const mw of middleware) {
if (typeof mw.handle !== 'string') { continue }
- const relativePath = relative(nitroContext._nuxt.buildDir, mw.handle).replace(/\.[a-z]+$/, '')
+ const relativePath = relative(nitro.options.buildDir, mw.handle).replace(/\.[a-z]+$/, '')
routeTypes[mw.route] = routeTypes[mw.route] || []
routeTypes[mw.route].push(`Awaited>`)
}
const lines = [
'// Generated by nitro',
- 'declare module \'@nuxt/nitro\' {',
+ 'declare module \'nitopack\' {',
' type Awaited = T extends PromiseLike ? Awaited : T',
' interface InternalApi {',
...Object.entries(routeTypes).map(([path, types]) => ` '${path}': ${types.join(' | ')}`),
@@ -88,58 +83,59 @@ export async function writeTypes (nitroContext: NitroContext) {
'export {}'
]
- await writeFile(join(nitroContext._nuxt.buildDir, 'nitro.d.ts'), lines.join('\n'))
+ await writeFile(join(nitro.options.buildDir, 'nitro.d.ts'), lines.join('\n'))
}
-async function _build (nitroContext: NitroContext) {
- nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir)
- await writeTypes(nitroContext)
+async function _build (nitro: Nitro) {
+ nitro.scannedMiddleware = await scanMiddleware(nitro.options.serverDir)
+ await writeTypes(nitro)
consola.start('Building server...')
- const build = await rollup.rollup(nitroContext.rollupConfig).catch((error) => {
+ const build = await rollup.rollup(nitro.options.rollupConfig).catch((error) => {
consola.error('Rollup error: ' + error.message)
throw error
})
consola.start('Writing server bundle...')
- await build.write(nitroContext.rollupConfig.output)
+ await build.write(nitro.options.rollupConfig.output)
const rewriteBuildPaths = (input: unknown, to: string) =>
- typeof input === 'string' ? replaceAll(input, nitroContext.output.dir, to) : undefined
+ typeof input === 'string' ? replaceAll(input, nitro.options.output.dir, to) : undefined
// Write build info
- const nitroConfigPath = resolve(nitroContext.output.dir, 'nitro.json')
+ const nitroConfigPath = resolve(nitro.options.output.dir, 'nitro.json')
const buildInfo = {
date: new Date(),
- preset: nitroContext.preset,
+ // preset: nitro.options.preset,
commands: {
- preview: rewriteBuildPaths(nitroContext.commands.preview, '.'),
- deploy: rewriteBuildPaths(nitroContext.commands.deploy, '.')
+ preview: rewriteBuildPaths(nitro.options.commands.preview, '.'),
+ deploy: rewriteBuildPaths(nitro.options.commands.deploy, '.')
}
}
await writeFile(nitroConfigPath, JSON.stringify(buildInfo, null, 2))
consola.success('Server built')
- await printFSTree(nitroContext.output.serverDir)
- await nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
+ await printFSTree(nitro.options.output.serverDir)
+ await nitro.hooks.callHook('nitro:compiled', nitro)
// Show deploy and preview hints
- const rOutDir = relative(process.cwd(), nitroContext.output.dir)
- if (nitroContext.commands.preview) {
+ // TODO
+ // const rOutDir = relative(process.cwd(), nitro.options.output.dir)
+ if (nitro.options.commands.preview) {
// consola.info(`You can preview this build using \`${rewriteBuildPaths(nitroContext.commands.preview, rOutDir)}\``)
- consola.info('You can preview this build using `nuxi preview`')
+ // consola.info('You can preview this build using `nuxi preview`')
}
- if (nitroContext.commands.deploy) {
- consola.info(`You can deploy this build using \`${rewriteBuildPaths(nitroContext.commands.deploy, rOutDir)}\``)
+ if (nitro.options.commands.deploy) {
+ // consola.info(`You can deploy this build using \`${rewriteBuildPaths(nitro.options.commands.deploy, rOutDir)}\``)
}
return {
- entry: resolve(nitroContext.rollupConfig.output.dir, nitroContext.rollupConfig.output.entryFileNames as string)
+ entry: resolve(nitro.options.rollupConfig.output.dir, nitro.options.rollupConfig.output.entryFileNames as string)
}
}
-function startRollupWatcher (nitroContext: NitroContext) {
- const watcher = rollup.watch(nitroContext.rollupConfig)
+function startRollupWatcher (nitro: Nitro) {
+ const watcher = rollup.watch(nitro.options.rollupConfig)
let start: number
watcher.on('event', (event) => {
@@ -155,31 +151,31 @@ function startRollupWatcher (nitroContext: NitroContext) {
// Finished building all bundles
case 'END':
- nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
+ nitro.hooks.callHook('nitro:compiled', nitro)
consola.success('Nitro built', start ? `in ${Date.now() - start} ms` : '')
return
// Encountered an error while bundling
case 'ERROR':
consola.error('Rollup error: ' + event.error)
- // consola.error(event.error)
+ // consola.error(event.error)
}
})
return watcher
}
-async function _watch (nitroContext: NitroContext) {
- let watcher = startRollupWatcher(nitroContext)
+async function _watch (nitro: Nitro) {
+ let watcher = startRollupWatcher(nitro)
- nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir,
+ nitro.scannedMiddleware = await scanMiddleware(nitro.options.serverDir,
(middleware, event) => {
- nitroContext.scannedMiddleware = middleware
+ nitro.scannedMiddleware = middleware
if (['add', 'addDir'].includes(event)) {
watcher.close()
- writeTypes(nitroContext).catch(console.error)
- watcher = startRollupWatcher(nitroContext)
+ writeTypes(nitro).catch(console.error)
+ watcher = startRollupWatcher(nitro)
}
}
)
- await writeTypes(nitroContext)
+ await writeTypes(nitro)
}
diff --git a/src/cli.ts b/src/cli.ts
new file mode 100644
index 0000000000..9e992d7e1c
--- /dev/null
+++ b/src/cli.ts
@@ -0,0 +1,46 @@
+#!/usr/bin/env node
+
+import mri from 'mri'
+import { resolve } from 'pathe'
+import { createNitro } from './nitro'
+import { build, prepare } from './build'
+import { createDevServer } from './server/dev'
+
+async function main () {
+ const args = mri(process.argv.slice(2))
+ const command = args._[0]
+ const rootDir = resolve(args._[1] || '.')
+
+ if (command === 'dev') {
+ const nitro = createNitro({
+ rootDir,
+ dev: true,
+ preset: 'dev'
+ })
+ const server = createDevServer(nitro)
+ await server.listen({})
+ await prepare(nitro)
+ await build(nitro)
+ await server.reload()
+ return
+ }
+
+ if (command === 'build') {
+ const nitro = createNitro({
+ rootDir,
+ dev: false,
+ preset: 'server'
+ })
+ await prepare(nitro)
+ await build(nitro)
+ process.exit(0)
+ }
+
+ console.error(`Unknown command ${command}! Usage: nitro dev|build [rootDir]`)
+ process.exit(1)
+}
+
+main().catch((err) => {
+ console.error(err)
+ process.exit(1)
+})
diff --git a/src/context.ts b/src/context.ts
deleted file mode 100644
index bb13c97f22..0000000000
--- a/src/context.ts
+++ /dev/null
@@ -1,210 +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
- 'nitro:compiled': (context: NitroContext) => void
- 'nitro:generate': (context: NitroContext) => void | Promise
- 'close': () => void
-}
-
-export interface NitroContext {
- alias: Record
- 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
- nuxtHooks: NestedHooks
- ignore: string[]
- env: Preset
- vfs: Record
- 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
- }
-}
-
-type DeepPartial = T extends Record ? { [P in keyof T]?: DeepPartial | T[P] } : T
-
-export interface NitroInput extends DeepPartial {}
-
-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()
- }
- }
-
- 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
-}
diff --git a/src/index.ts b/src/index.ts
index d8bc866cee..9d5bfe4498 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,4 @@
export * from './build'
-export * from './context'
+export * from './nitro'
export * from './server/middleware'
export * from './server/dev'
-export { wpfs } from './utils/wpfs'
diff --git a/src/nitro.ts b/src/nitro.ts
new file mode 100644
index 0000000000..2196b12de8
--- /dev/null
+++ b/src/nitro.ts
@@ -0,0 +1,131 @@
+import { resolve } from 'pathe'
+import defu from 'defu'
+import { createHooks } from 'hookable'
+import { tryImport, resolvePath, detectTarget } from './utils'
+import * as PRESETS from './presets'
+import type { Nitro, NitroOptions, NitroConfig } from './types'
+// import { mergeHooks } from 'hookable'
+import { pkgDir, runtimeDir } from './dirs'
+
+const nitroDefaults: NitroConfig = {
+ alias: {
+ '#nitro': runtimeDir
+ },
+ unenv: {},
+ analyze: false,
+ experiments: {},
+ moduleSideEffects: ['unenv/runtime/polyfill/'],
+ middleware: [],
+ modulesDir: [],
+ ignore: [],
+ hooks: {},
+ output: {
+ dir: '{{ rootDir }}/.output',
+ serverDir: '{{ output.dir }}/server',
+ publicDir: '{{ output.dir }}/public'
+ },
+ storage: { mounts: {} },
+ commands: {},
+ assets: {
+ // inline: !config.dev,
+ dirs: {}
+ },
+ publicDir: 'public',
+ serverDir: 'server',
+ routerBase: '/',
+ publicPath: '/',
+ runtimeConfig: {
+ public: {
+ app: {
+ baseURL: '/',
+ cdnURL: undefined,
+ buildAssetsDir: '_dist'
+ }
+ },
+ private: {}
+ }
+}
+
+export function createNitro (config: NitroConfig = {}): Nitro {
+ // Apply nitro defaults
+ config = defu(config, nitroDefaults)
+
+ // Apply preset defaults
+ config.extends = config.preset = process.env.NITRO_PRESET || config.extends || config.preset || detectTarget() || 'server'
+ config = extendConfig(config)
+
+ // Normalize options
+ const options = config as NitroOptions
+ options.rootDir = resolve(options.rootDir || '.')
+ for (const key of ['srcDir', 'publicDir', 'serverDir', 'generateDir', 'buildDir']) {
+ options[key] = resolve(options.rootDir, options[key])
+ }
+ options.modulesDir.push(resolve(options.rootDir, 'node_modules'))
+ options.modulesDir.push(resolve(pkgDir, 'node_modules'))
+
+ // Create context
+ const nitro: Nitro = {
+ options,
+ hooks: createHooks(),
+ vfs: {},
+ scannedMiddleware: []
+ }
+
+ // Init hooks
+ nitro.hooks.addHooks(nitro.options.hooks)
+
+ // Resolve output dir
+ options.output.dir = resolvePath(nitro, nitro.options.output.dir)
+ options.output.publicDir = resolvePath(nitro, nitro.options.output.publicDir)
+ options.output.serverDir = resolvePath(nitro, nitro.options.output.serverDir)
+
+ // Dev-only storage
+ if (nitro.options.dev) {
+ const fsMounts = {
+ root: resolve(nitro.options.rootDir),
+ src: resolve(nitro.options.srcDir),
+ build: resolve(nitro.options.buildDir),
+ cache: resolve(nitro.options.rootDir, 'node_modules/.nitro/cache')
+ }
+ for (const p in fsMounts) {
+ nitro.options.storage.mounts[p] = nitro.options.storage.mounts[p] || {
+ driver: 'fs',
+ driverOptions: { base: fsMounts[p] }
+ }
+ }
+ }
+
+ // Assets
+ nitro.options.assets.dirs.server = {
+ dir: resolve(nitro.options.srcDir, 'server/assets'), meta: true
+ }
+
+ return nitro
+}
+
+function extendConfig (config: NitroConfig): NitroConfig {
+ if (!config.extends) {
+ return config
+ }
+
+ let _extends = config.extends
+ if (typeof config.extends === 'string') {
+ type Preset = NitroConfig['preset']
+ _extends = (PRESETS as Record)[config.extends] || tryImport(config.rootDir, config.extends) || {}
+ if (!_extends) {
+ throw new Error('Cannot resolve config: ' + config.extends)
+ }
+ _extends = (_extends as any).default || _extends
+ }
+ if (typeof _extends === 'function') {
+ _extends = _extends(config)
+ }
+
+ // TODO: Merge hooks
+ const preset = extendConfig(_extends as NitroConfig)
+ return defu(config, preset)
+}
+
+export function defineNitroPreset (preset: NitroConfig['extends']) {
+ return preset
+}
diff --git a/src/presets/azure.ts b/src/presets/azure.ts
index 1b5c0dd91c..d9b9c4a41a 100644
--- a/src/presets/azure.ts
+++ b/src/presets/azure.ts
@@ -2,10 +2,11 @@ import fse from 'fs-extra'
import { globby } from 'globby'
import { join, resolve } from 'pathe'
import { writeFile } from '../utils'
-import { NitroPreset, NitroContext } from '../context'
+import { defineNitroPreset } from '../nitro'
+import type { Nitro } from '../types'
-export const azure: NitroPreset = {
- entry: '{{ _internal.runtimeDir }}/entries/azure',
+export const azure = defineNitroPreset({
+ entry: '#nitro/entries/azure',
externals: true,
output: {
serverDir: '{{ output.dir }}/server/functions'
@@ -14,13 +15,13 @@ export const azure: NitroPreset = {
preview: 'npx @azure/static-web-apps-cli start {{ output.publicDir }} --api-location {{ output.serverDir }}/..'
},
hooks: {
- async 'nitro:compiled' (ctx: NitroContext) {
+ async 'nitro:compiled' (ctx: Nitro) {
await writeRoutes(ctx)
}
}
-}
+})
-async function writeRoutes ({ output }: NitroContext) {
+async function writeRoutes (nitro) {
const host = {
version: '2.0'
}
@@ -32,7 +33,7 @@ async function writeRoutes ({ output }: NitroContext) {
}
}
- const indexPath = resolve(output.publicDir, 'index.html')
+ const indexPath = resolve(nitro.options.output.publicDir, 'index.html')
const indexFileExists = fse.existsSync(indexPath)
if (!indexFileExists) {
config.routes.unshift(
@@ -48,10 +49,10 @@ async function writeRoutes ({ output }: NitroContext) {
}
const folderFiles = await globby([
- join(output.publicDir, 'index.html'),
- join(output.publicDir, '**/index.html')
+ join(nitro.options.output.publicDir, 'index.html'),
+ join(nitro.options.output.publicDir, '**/index.html')
])
- const prefix = output.publicDir.length
+ const prefix = nitro.options.output.publicDir.length
const suffix = '/index.html'.length
folderFiles.forEach(file =>
config.routes.unshift({
@@ -60,7 +61,7 @@ async function writeRoutes ({ output }: NitroContext) {
})
)
- const otherFiles = await globby([join(output.publicDir, '**/*.html'), join(output.publicDir, '*.html')])
+ const otherFiles = await globby([join(nitro.options.output.publicDir, '**/*.html'), join(nitro.options.output.publicDir, '*.html')])
otherFiles.forEach((file) => {
if (file.endsWith('index.html')) {
return
@@ -97,9 +98,9 @@ async function writeRoutes ({ output }: NitroContext) {
]
}
- 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))
+ await writeFile(resolve(nitro.options.output.serverDir, 'function.json'), JSON.stringify(functionDefinition))
+ await writeFile(resolve(nitro.options.output.serverDir, '../host.json'), JSON.stringify(host))
+ await writeFile(resolve(nitro.options.output.publicDir, 'staticwebapp.config.json'), JSON.stringify(config))
if (!indexFileExists) {
await writeFile(indexPath, '')
}
diff --git a/src/presets/azure_functions.ts b/src/presets/azure_functions.ts
index 30d67fec16..bfb792e0e8 100644
--- a/src/presets/azure_functions.ts
+++ b/src/presets/azure_functions.ts
@@ -2,22 +2,23 @@ import { createWriteStream } from 'fs'
import archiver from 'archiver'
import { join, resolve } from 'pathe'
import { writeFile } from '../utils'
-import { NitroPreset, NitroContext } from '../context'
+import { defineNitroPreset } from '../nitro'
+import type { Nitro } from '../types'
// eslint-disable-next-line
-export const azure_functions: NitroPreset = {
+export const azure_functions = defineNitroPreset({
serveStatic: true,
- entry: '{{ _internal.runtimeDir }}/entries/azure_functions',
+ entry: '#nitro/entries/azure_functions',
externals: true,
commands: {
deploy: 'az functionapp deployment source config-zip -g -n --src {{ output.dir }}/deploy.zip'
},
hooks: {
- async 'nitro:compiled' (ctx: NitroContext) {
+ async 'nitro:compiled' (ctx: Nitro) {
await writeRoutes(ctx)
}
}
-}
+})
function zipDirectory (dir: string, outfile: string): Promise {
const archive = archiver('zip', { zlib: { level: 9 } })
@@ -34,7 +35,7 @@ function zipDirectory (dir: string, outfile: string): Promise {
})
}
-async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
+async function writeRoutes (nitro: Nitro) {
const host = {
version: '2.0',
extensions: { http: { routePrefix: '' } }
@@ -67,7 +68,7 @@ async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
]
}
- 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'))
+ await writeFile(resolve(nitro.options.serverDir, 'function.json'), JSON.stringify(functionDefinition))
+ await writeFile(resolve(nitro.options.output.dir, 'host.json'), JSON.stringify(host))
+ await zipDirectory(nitro.options.output.dir, join(nitro.options.output.dir, 'deploy.zip'))
}
diff --git a/src/presets/browser.ts b/src/presets/browser.ts
index 5cc7a5d840..ca046c241c 100644
--- a/src/presets/browser.ts
+++ b/src/presets/browser.ts
@@ -2,13 +2,12 @@ import { existsSync, promises as fsp } from 'fs'
import { resolve } from 'pathe'
import consola from 'consola'
import { joinURL } from 'ufo'
-import { extendPreset, prettyPath } from '../utils'
-import { NitroPreset, NitroContext, NitroInput } from '../context'
-import { worker } from './worker'
+import { prettyPath } from '../utils'
+import { defineNitroPreset } from '../nitro'
-export const browser: NitroPreset = extendPreset(worker, (input: NitroInput) => {
- // TODO: Join base at runtime
- const baseURL = input._nuxt.baseURL
+export const browser = defineNitroPreset((_input) => {
+ // TODO
+ const baseURL = '/'
const script = `