2022-08-13 11:52:03 +00:00
|
|
|
import type { AddressInfo } from 'node:net'
|
2022-12-11 21:44:52 +00:00
|
|
|
import type { RequestListener } from 'node:http'
|
2023-04-07 16:02:47 +00:00
|
|
|
import { relative, resolve } from 'pathe'
|
2021-04-15 19:17:44 +00:00
|
|
|
import chokidar from 'chokidar'
|
2022-03-16 11:11:30 +00:00
|
|
|
import { debounce } from 'perfect-debounce'
|
2021-11-21 16:14:46 +00:00
|
|
|
import type { Nuxt } from '@nuxt/schema'
|
2023-04-08 10:16:06 +00:00
|
|
|
import { consola } from 'consola'
|
2022-03-15 10:56:16 +00:00
|
|
|
import { withTrailingSlash } from 'ufo'
|
2022-07-25 12:01:18 +00:00
|
|
|
import { setupDotenv } from 'c12'
|
2022-09-01 09:34:56 +00:00
|
|
|
import { showBanner, showVersions } from '../utils/banner'
|
2021-10-07 13:53:31 +00:00
|
|
|
import { writeTypes } from '../utils/prepare'
|
2021-10-13 10:44:54 +00:00
|
|
|
import { loadKit } from '../utils/kit'
|
2023-03-10 14:55:01 +00:00
|
|
|
import { importModule } from '../utils/esm'
|
2022-06-12 21:26:36 +00:00
|
|
|
import { overrideEnv } from '../utils/env'
|
2023-04-07 16:02:47 +00:00
|
|
|
import { cleanupNuxtDirs, loadNuxtManifest, writeNuxtManifest } from '../utils/nuxt'
|
2021-07-26 14:04:35 +00:00
|
|
|
import { defineNuxtCommand } from './index'
|
2021-04-09 15:52:45 +00:00
|
|
|
|
2021-07-26 14:04:35 +00:00
|
|
|
export default defineNuxtCommand({
|
|
|
|
meta: {
|
|
|
|
name: 'dev',
|
2023-03-07 12:18:47 +00:00
|
|
|
usage: 'npx nuxi dev [rootDir] [--dotenv] [--log-level] [--clipboard] [--open, -o] [--port, -p] [--host, -h] [--https] [--ssl-cert] [--ssl-key]',
|
2021-07-26 14:04:35 +00:00
|
|
|
description: 'Run nuxt development server'
|
|
|
|
},
|
2023-03-08 11:32:00 +00:00
|
|
|
async invoke (args, options = {}) {
|
2022-06-12 21:26:36 +00:00
|
|
|
overrideEnv('development')
|
2022-04-07 11:28:04 +00:00
|
|
|
|
|
|
|
const { listen } = await import('listhen')
|
2022-10-15 18:42:57 +00:00
|
|
|
const { toNodeListener } = await import('h3')
|
2022-08-26 15:47:29 +00:00
|
|
|
let currentHandler: RequestListener | undefined
|
2022-04-07 11:28:04 +00:00
|
|
|
let loadingMessage = 'Nuxt is starting...'
|
2022-08-26 15:47:29 +00:00
|
|
|
const loadingHandler: RequestListener = async (_req, res) => {
|
2022-04-07 11:28:04 +00:00
|
|
|
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 }))
|
|
|
|
}
|
2022-08-26 15:47:29 +00:00
|
|
|
const serverHandler: RequestListener = (req, res) => {
|
2022-04-07 11:28:04 +00:00
|
|
|
return currentHandler ? currentHandler(req, res) : loadingHandler(req, res)
|
|
|
|
}
|
|
|
|
|
2022-07-25 12:01:18 +00:00
|
|
|
const rootDir = resolve(args._[0] || '.')
|
2022-09-01 09:34:56 +00:00
|
|
|
showVersions(rootDir)
|
|
|
|
|
2022-09-26 10:36:10 +00:00
|
|
|
await setupDotenv({ cwd: rootDir, fileName: args.dotenv })
|
2022-07-25 12:01:18 +00:00
|
|
|
|
2023-02-16 13:17:56 +00:00
|
|
|
const { loadNuxt, loadNuxtConfig, buildNuxt } = await loadKit(rootDir)
|
|
|
|
|
|
|
|
const config = await loadNuxtConfig({
|
|
|
|
cwd: rootDir,
|
2023-03-07 12:18:47 +00:00
|
|
|
overrides: {
|
|
|
|
dev: true,
|
2023-03-08 11:32:00 +00:00
|
|
|
logLevel: args['log-level'],
|
|
|
|
...(options.overrides || {})
|
2023-03-07 12:18:47 +00:00
|
|
|
}
|
2023-02-16 13:17:56 +00:00
|
|
|
})
|
|
|
|
|
2022-04-07 11:28:04 +00:00
|
|
|
const listener = await listen(serverHandler, {
|
2022-07-12 12:12:33 +00:00
|
|
|
showURL: false,
|
2021-07-26 14:04:35 +00:00
|
|
|
clipboard: args.clipboard,
|
2021-10-13 10:33:21 +00:00
|
|
|
open: args.open || args.o,
|
2023-02-16 13:17:56 +00:00
|
|
|
port: args.port || args.p || process.env.NUXT_PORT || config.devServer.port,
|
|
|
|
hostname: args.host || args.h || process.env.NUXT_HOST || config.devServer.host,
|
|
|
|
https: (args.https !== false && (args.https || config.devServer.https))
|
|
|
|
? {
|
|
|
|
cert: args['ssl-cert'] || (config.devServer.https && config.devServer.https.cert) || undefined,
|
|
|
|
key: args['ssl-key'] || (config.devServer.https && config.devServer.https.key) || undefined
|
|
|
|
}
|
|
|
|
: false
|
2021-07-26 14:04:35 +00:00
|
|
|
})
|
2021-04-09 15:52:45 +00:00
|
|
|
|
2021-07-26 14:04:35 +00:00
|
|
|
let currentNuxt: Nuxt
|
2023-02-20 10:56:17 +00:00
|
|
|
let distWatcher: chokidar.FSWatcher
|
|
|
|
|
2022-07-12 12:12:33 +00:00
|
|
|
const showURL = () => {
|
|
|
|
listener.showURL({
|
|
|
|
// TODO: Normalize URL with trailing slash within schema
|
|
|
|
baseURL: withTrailingSlash(currentNuxt?.options.app.baseURL) || '/'
|
|
|
|
})
|
|
|
|
}
|
2021-08-10 16:53:46 +00:00
|
|
|
const load = async (isRestart: boolean, reason?: string) => {
|
2021-07-26 14:04:35 +00:00
|
|
|
try {
|
2022-04-07 11:28:04 +00:00
|
|
|
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
2022-08-26 15:47:29 +00:00
|
|
|
currentHandler = undefined
|
2021-07-26 14:04:35 +00:00
|
|
|
if (isRestart) {
|
2022-04-07 11:28:04 +00:00
|
|
|
consola.info(loadingMessage)
|
2021-07-26 14:04:35 +00:00
|
|
|
}
|
|
|
|
if (currentNuxt) {
|
|
|
|
await currentNuxt.close()
|
|
|
|
}
|
2023-02-20 10:56:17 +00:00
|
|
|
if (distWatcher) {
|
|
|
|
await distWatcher.close()
|
|
|
|
}
|
|
|
|
|
2023-03-07 12:18:47 +00:00
|
|
|
currentNuxt = await loadNuxt({
|
|
|
|
rootDir,
|
|
|
|
dev: true,
|
|
|
|
ready: false,
|
|
|
|
overrides: {
|
2023-03-08 11:32:00 +00:00
|
|
|
logLevel: args['log-level'],
|
|
|
|
...(options.overrides || {})
|
2023-03-07 12:18:47 +00:00
|
|
|
}
|
|
|
|
})
|
2023-03-03 19:24:49 +00:00
|
|
|
|
2022-07-12 12:12:33 +00:00
|
|
|
if (!isRestart) {
|
|
|
|
showURL()
|
|
|
|
}
|
|
|
|
|
2022-08-16 13:14:26 +00:00
|
|
|
// Write manifest and also check if we need cache invalidation
|
|
|
|
if (!isRestart) {
|
|
|
|
const previousManifest = await loadNuxtManifest(currentNuxt.options.buildDir)
|
|
|
|
const newManifest = await writeNuxtManifest(currentNuxt)
|
|
|
|
if (previousManifest && newManifest && previousManifest._hash !== newManifest._hash) {
|
|
|
|
await cleanupNuxtDirs(currentNuxt.options.rootDir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 14:04:35 +00:00
|
|
|
await currentNuxt.ready()
|
2022-08-13 11:52:03 +00:00
|
|
|
|
2023-03-18 13:39:06 +00:00
|
|
|
distWatcher = chokidar.watch(resolve(currentNuxt.options.buildDir, 'dist'), { ignoreInitial: true, depth: 0 })
|
|
|
|
distWatcher.on('unlinkDir', () => {
|
|
|
|
dLoad(true, '.nuxt/dist directory has been removed')
|
|
|
|
})
|
|
|
|
|
|
|
|
const unsub = currentNuxt.hooks.hook('restart', async (options) => {
|
|
|
|
unsub() // we use this instead of `hookOnce` for Nuxt Bridge support
|
|
|
|
if (options?.hard && process.send) {
|
|
|
|
await listener.close().catch(() => {})
|
|
|
|
await currentNuxt.close().catch(() => {})
|
|
|
|
await watcher.close().catch(() => {})
|
|
|
|
await distWatcher.close().catch(() => {})
|
|
|
|
process.send({ type: 'nuxt:restart' })
|
|
|
|
} else {
|
|
|
|
await load(true)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-04-14 08:39:27 +00:00
|
|
|
await currentNuxt.hooks.callHook('listen', listener.server, listener)
|
2023-03-03 19:24:49 +00:00
|
|
|
const address = (listener.server.address() || {}) as AddressInfo
|
2022-10-27 10:36:37 +00:00
|
|
|
currentNuxt.options.devServer.url = listener.url
|
|
|
|
currentNuxt.options.devServer.port = address.port
|
|
|
|
currentNuxt.options.devServer.host = address.address
|
|
|
|
currentNuxt.options.devServer.https = listener.https
|
2022-08-13 11:52:03 +00:00
|
|
|
|
2022-03-15 10:56:16 +00:00
|
|
|
await Promise.all([
|
|
|
|
writeTypes(currentNuxt).catch(console.error),
|
|
|
|
buildNuxt(currentNuxt)
|
|
|
|
])
|
2022-10-15 18:42:57 +00:00
|
|
|
currentHandler = toNodeListener(currentNuxt.server.app)
|
2021-07-26 14:04:35 +00:00
|
|
|
if (isRestart && args.clear !== false) {
|
|
|
|
showBanner()
|
2022-07-12 12:12:33 +00:00
|
|
|
showURL()
|
2021-07-26 14:04:35 +00:00
|
|
|
}
|
|
|
|
} catch (err) {
|
2021-10-07 10:15:15 +00:00
|
|
|
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
2022-08-26 15:47:29 +00:00
|
|
|
currentHandler = undefined
|
2022-04-07 11:28:04 +00:00
|
|
|
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
|
2021-04-15 19:17:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 14:04:35 +00:00
|
|
|
// Watch for config changes
|
|
|
|
// TODO: Watcher service, modules, and requireTree
|
2022-03-16 11:11:30 +00:00
|
|
|
const dLoad = debounce(load)
|
2023-03-09 11:46:08 +00:00
|
|
|
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 0 })
|
|
|
|
watcher.on('all', (_event, _file) => {
|
|
|
|
const file = relative(rootDir, _file)
|
|
|
|
if (file.match(/^(nuxt\.config\.(js|ts|mjs|cjs)|\.nuxtignore|\.env|\.nuxtrc)$/)) {
|
|
|
|
dLoad(true, `${file} updated`)
|
2021-09-29 18:09:43 +00:00
|
|
|
}
|
2021-07-26 14:04:35 +00:00
|
|
|
})
|
2021-04-09 15:52:45 +00:00
|
|
|
|
2021-07-26 14:04:35 +00:00
|
|
|
await load(false)
|
2022-04-09 10:02:56 +00:00
|
|
|
|
2022-04-29 09:38:22 +00:00
|
|
|
return 'wait' as const
|
2021-07-26 14:04:35 +00:00
|
|
|
}
|
|
|
|
})
|