Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
node_modules
jspm_packages

# Nitro
nitro.d.ts

package-lock.json
# */**/yarn.lock

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default defineBuildConfig({
declaration: true,
entries: [
'src/index',
'src/cli',
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
],
dependencies: [
Expand Down
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
16 changes: 16 additions & 0 deletions playground/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>Welcome to Nitro!</title>
</head>

<body>
<h1>Welcome to Nitro!</h1>
<ul>
<a href="/api/test">/api/test</a>
</ul>
</body>

</html>
1 change: 1 addition & 0 deletions playground/server/api/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'Nitro works!'
5 changes: 5 additions & 0 deletions playground/server/middleware/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

export default (_req, _res, next) => {
console.log('middleware!')
next()
}
132 changes: 64 additions & 68 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand All @@ -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<string, string[]> = {}

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<ReturnType<typeof import('${relativePath}').default>>`)
}

const lines = [
'// Generated by nitro',
'declare module \'@nuxt/nitro\' {',
'declare module \'nitopack\' {',
' type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T',
' interface InternalApi {',
...Object.entries(routeTypes).map(([path, types]) => ` '${path}': ${types.join(' | ')}`),
Expand All @@ -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) => {
Expand All @@ -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)
}
46 changes: 46 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -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)
})
Loading