mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 16:12:12 +00:00
feat(nuxt): migrate to new nuxt/cli
(#22799)
This commit is contained in:
parent
da117ec616
commit
abd5d85770
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ jspm_packages
|
||||
|
||||
package-lock.json
|
||||
packages/*/README.md
|
||||
!packages/nuxi/README.md
|
||||
packages/*/LICENSE
|
||||
*/**/yarn.lock
|
||||
/.yarn
|
||||
|
@ -6,7 +6,6 @@
|
||||
"@nuxt/test-utils": "./packages/test-utils",
|
||||
"@nuxt/vite": "./packages/vite",
|
||||
"@nuxt/webpack": "./packages/webpack",
|
||||
"nuxi": "./packages/nuxi",
|
||||
"nuxt": "./packages/nuxt"
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@
|
||||
"@nuxt/test-utils": "workspace:*",
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"nuxi": "workspace:*",
|
||||
"nuxt": "workspace:*",
|
||||
"vite": "4.4.9",
|
||||
"vue": "3.3.4",
|
||||
@ -65,7 +64,7 @@
|
||||
"jiti": "1.19.3",
|
||||
"markdownlint-cli": "^0.33.0",
|
||||
"nitropack": "2.6.0",
|
||||
"nuxi": "workspace:*",
|
||||
"nuxi": "npm:nuxi-ng@0.3.0-1692970235.c259efa",
|
||||
"nuxt": "workspace:*",
|
||||
"nuxt-vitest": "0.10.2",
|
||||
"ofetch": "1.3.3",
|
||||
|
5
packages/nuxi/README.md
Normal file
5
packages/nuxi/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Nuxt CLI (nuxi)
|
||||
|
||||
⚡️ Next Generation CLI Experience for [Nuxt](https://nuxt.com/).
|
||||
|
||||
- 👉 View on GitHub at https://github.com/nuxt/cli
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import('../dist/cli-wrapper.mjs')
|
@ -1,31 +0,0 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
declaration: true,
|
||||
rollup: {
|
||||
inlineDependencies: true,
|
||||
resolve: {
|
||||
exportConditions: ['production', 'node'] as any
|
||||
}
|
||||
},
|
||||
entries: [
|
||||
'src/cli',
|
||||
'src/cli-run',
|
||||
'src/cli-wrapper',
|
||||
'src/index'
|
||||
],
|
||||
externals: [
|
||||
'@nuxt/kit',
|
||||
'@nuxt/schema',
|
||||
'@nuxt/test-utils',
|
||||
'fsevents',
|
||||
// TODO: Fix rollup/unbuild issue
|
||||
'node:url',
|
||||
'node:buffer',
|
||||
'node:path',
|
||||
'node:child_process',
|
||||
'node:process',
|
||||
'node:path',
|
||||
'node:os'
|
||||
]
|
||||
})
|
@ -1,60 +0,0 @@
|
||||
{
|
||||
"name": "nuxi",
|
||||
"version": "3.6.5",
|
||||
"repository": "nuxt/nuxt",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
"./cli": "./bin/nuxi.mjs"
|
||||
},
|
||||
"bin": "./bin/nuxi.mjs",
|
||||
"files": [
|
||||
"bin",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"prepack": "unbuild"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/kit": "workspace:../kit",
|
||||
"@nuxt/schema": "workspace:../schema",
|
||||
"@types/clear": "0.1.2",
|
||||
"@types/flat": "5.0.2",
|
||||
"@types/mri": "1.1.1",
|
||||
"@types/semver": "7.5.0",
|
||||
"c12": "1.4.2",
|
||||
"chokidar": "3.5.3",
|
||||
"clear": "0.1.0",
|
||||
"clipboardy": "3.0.0",
|
||||
"colorette": "2.0.20",
|
||||
"consola": "3.2.3",
|
||||
"deep-object-diff": "1.1.9",
|
||||
"defu": "6.1.2",
|
||||
"destr": "2.0.1",
|
||||
"execa": "7.2.0",
|
||||
"flat": "5.0.2",
|
||||
"giget": "1.1.2",
|
||||
"h3": "1.8.0",
|
||||
"jiti": "1.19.3",
|
||||
"listhen": "1.4.0",
|
||||
"mlly": "1.4.0",
|
||||
"mri": "1.2.0",
|
||||
"nitropack": "2.6.0",
|
||||
"ohash": "1.1.3",
|
||||
"pathe": "1.1.1",
|
||||
"perfect-debounce": "1.0.0",
|
||||
"pkg-types": "1.0.3",
|
||||
"scule": "1.0.0",
|
||||
"semver": "7.5.4",
|
||||
"ufo": "1.3.0",
|
||||
"unbuild": "latest"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// @ts-expect-error internal property for tracking start time
|
||||
process._startTime = Date.now()
|
||||
|
||||
// @ts-expect-error `default` property is not declared
|
||||
import('./cli').then(r => (r.default || r).main())
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* This file is used to wrap the CLI entrypoint in a restartable process.
|
||||
*/
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { fork } from 'node:child_process'
|
||||
import type { ChildProcess } from 'node:child_process'
|
||||
|
||||
const cliEntry = new URL('../dist/cli-run.mjs', import.meta.url)
|
||||
|
||||
// Only enable wrapper for nuxt dev command
|
||||
if (process.argv[2] === 'dev') {
|
||||
process.env.__CLI_ARGV__ = JSON.stringify(process.argv)
|
||||
startSubprocess()
|
||||
} else {
|
||||
import(cliEntry.href)
|
||||
}
|
||||
|
||||
function startSubprocess () {
|
||||
let childProc: ChildProcess | undefined
|
||||
|
||||
const onShutdown = () => {
|
||||
if (childProc) {
|
||||
childProc.kill()
|
||||
childProc = undefined
|
||||
}
|
||||
}
|
||||
|
||||
process.on('exit', onShutdown)
|
||||
process.on('SIGTERM', onShutdown) // Graceful shutdown
|
||||
process.on('SIGINT', onShutdown) // Ctrl-C
|
||||
process.on('SIGQUIT', onShutdown) // Ctrl-\
|
||||
|
||||
start()
|
||||
|
||||
function start () {
|
||||
const _argv: string[] = (process.env.__CLI_ARGV__ ? JSON.parse(process.env.__CLI_ARGV__) : process.argv).slice(2)
|
||||
const execArguments: string[] = getInspectArgs()
|
||||
|
||||
childProc = fork(fileURLToPath(cliEntry), [], { execArgv: execArguments })
|
||||
childProc.on('close', (code) => { if (code) { process.exit(code) } })
|
||||
childProc.on('message', (message) => {
|
||||
if ((message as { type: string })?.type === 'nuxt:restart') {
|
||||
childProc?.kill()
|
||||
startSubprocess()
|
||||
}
|
||||
})
|
||||
|
||||
function getInspectArgs (): string[] {
|
||||
const inspectArgv = _argv.find(argvItem => argvItem.includes('--inspect'))
|
||||
|
||||
if (!inspectArgv) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [inspectArgv]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
import mri from 'mri'
|
||||
import { red } from 'colorette'
|
||||
import type { ConsolaReporter } from 'consola'
|
||||
import { consola } from 'consola'
|
||||
import { checkEngines } from './utils/engines'
|
||||
import type { Command, NuxtCommand } from './commands'
|
||||
import { commands } from './commands'
|
||||
import { showHelp } from './utils/help'
|
||||
import { showBanner } from './utils/banner'
|
||||
|
||||
async function _main () {
|
||||
const _argv = (process.env.__CLI_ARGV__ ? JSON.parse(process.env.__CLI_ARGV__) : process.argv).slice(2)
|
||||
const args = mri(_argv, {
|
||||
boolean: [
|
||||
'no-clear'
|
||||
]
|
||||
})
|
||||
const command = args._.shift() || 'usage'
|
||||
|
||||
showBanner(command === 'dev' && args.clear !== false && !args.help)
|
||||
|
||||
if (!(command in commands)) {
|
||||
console.log('\n' + red('Invalid command ' + command))
|
||||
|
||||
await commands.usage().then(r => r.invoke())
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Check Node.js version in background
|
||||
setTimeout(() => { checkEngines().catch(() => {}) }, 1000)
|
||||
|
||||
const cmd = await commands[command as Command]() as NuxtCommand
|
||||
if (args.h || args.help) {
|
||||
showHelp(cmd.meta)
|
||||
} else {
|
||||
const result = await cmd.invoke(args)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap all console logs with consola for better DX
|
||||
consola.wrapAll()
|
||||
|
||||
// Filter out unwanted logs
|
||||
// TODO: Use better API from consola for intercepting logs
|
||||
const wrapReporter = (reporter: ConsolaReporter) => ({
|
||||
log (logObj, ctx) {
|
||||
if (!logObj.args || !logObj.args.length) { return }
|
||||
const msg = logObj.args[0]
|
||||
if (typeof msg === 'string' && !process.env.DEBUG) {
|
||||
// Hide vue-router 404 warnings
|
||||
if (msg.startsWith('[Vue Router warn]: No match found for location with path')) {
|
||||
return
|
||||
}
|
||||
// Suppress warning about native Node.js fetch
|
||||
if (msg.includes('ExperimentalWarning: The Fetch API is an experimental feature')) {
|
||||
return
|
||||
}
|
||||
// TODO: resolve upstream in Vite
|
||||
// Hide sourcemap warnings related to node_modules
|
||||
if (msg.startsWith('Sourcemap') && msg.includes('node_modules')) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return reporter.log(logObj, ctx)
|
||||
}
|
||||
}) satisfies ConsolaReporter
|
||||
|
||||
consola.options.reporters = consola.options.reporters.map(wrapReporter)
|
||||
|
||||
process.on('unhandledRejection', err => consola.error('[unhandledRejection]', err))
|
||||
process.on('uncaughtException', err => consola.error('[uncaughtException]', err))
|
||||
|
||||
export function main () {
|
||||
_main()
|
||||
.then((result) => {
|
||||
if (result === 'error') {
|
||||
process.exit(1)
|
||||
} else if (result !== 'wait') {
|
||||
process.exit()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
consola.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { templates } from '../utils/templates'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'add',
|
||||
usage: `npx nuxi add [--cwd] [--force] ${Object.keys(templates).join('|')} <name>`,
|
||||
description: 'Create a new template file.'
|
||||
},
|
||||
async invoke (args) {
|
||||
const cwd = resolve(args.cwd || '.')
|
||||
|
||||
const template = args._[0]
|
||||
const name = args._[1]
|
||||
|
||||
// Validate template name
|
||||
if (!templates[template]) {
|
||||
consola.error(`Template ${template} is not supported. Possible values: ${Object.keys(templates).join(', ')}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Validate options
|
||||
if (!name) {
|
||||
consola.error('name argument is missing!')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Load config in order to respect srcDir
|
||||
const kit = await loadKit(cwd)
|
||||
const config = await kit.loadNuxtConfig({ cwd })
|
||||
|
||||
// Resolve template
|
||||
const res = templates[template]({ name, args })
|
||||
|
||||
// Resolve full path to generated file
|
||||
const path = resolve(config.srcDir, res.path)
|
||||
|
||||
// Ensure not overriding user code
|
||||
if (!args.force && existsSync(path)) {
|
||||
consola.error(`File exists: ${path} . Use --force to override or use a different name.`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
const parentDir = dirname(path)
|
||||
if (!existsSync(parentDir)) {
|
||||
consola.info('Creating directory', parentDir)
|
||||
if (template === 'page') {
|
||||
consola.info('This enables vue-router functionality!')
|
||||
}
|
||||
await fsp.mkdir(parentDir, { recursive: true })
|
||||
}
|
||||
|
||||
// Write file
|
||||
await fsp.writeFile(path, res.contents.trim() + '\n')
|
||||
consola.info(`🪄 Generated a new ${template} in ${path}`)
|
||||
}
|
||||
})
|
@ -1,110 +0,0 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { createApp, eventHandler, lazyEventHandler, send, toNodeListener } from 'h3'
|
||||
import { listen } from 'listhen'
|
||||
import type { NuxtAnalyzeMeta } from '@nuxt/schema'
|
||||
import { defu } from 'defu'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'analyze',
|
||||
usage: 'npx nuxi analyze [--log-level] [--name] [--no-serve] [rootDir]',
|
||||
description: 'Build nuxt and analyze production bundle (experimental)'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('production')
|
||||
|
||||
const name = args.name || 'default'
|
||||
const slug = name.trim().replace(/[^a-z0-9_-]/gi, '_')
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
let analyzeDir = join(rootDir, '.nuxt/analyze', slug)
|
||||
let buildDir = join(analyzeDir, '.nuxt')
|
||||
let outDir = join(analyzeDir, '.output')
|
||||
|
||||
const startTime = Date.now()
|
||||
|
||||
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: defu(options.overrides, {
|
||||
build: {
|
||||
analyze: true
|
||||
},
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
nitro: {
|
||||
output: {
|
||||
dir: outDir
|
||||
}
|
||||
},
|
||||
logLevel: args['log-level']
|
||||
})
|
||||
})
|
||||
|
||||
analyzeDir = nuxt.options.analyzeDir
|
||||
buildDir = nuxt.options.buildDir
|
||||
outDir = nuxt.options.nitro.output?.dir || outDir
|
||||
|
||||
await clearDir(analyzeDir)
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
const endTime = Date.now()
|
||||
|
||||
const meta: NuxtAnalyzeMeta = {
|
||||
name,
|
||||
slug,
|
||||
startTime,
|
||||
endTime,
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
outDir
|
||||
}
|
||||
|
||||
await nuxt.callHook('build:analyze:done', meta)
|
||||
await fsp.writeFile(join(analyzeDir, 'meta.json'), JSON.stringify(meta, null, 2), 'utf-8')
|
||||
|
||||
console.info('Analyze results are available at: `' + analyzeDir + '`')
|
||||
console.warn('Do not deploy analyze results! Use `nuxi build` before deploying.')
|
||||
|
||||
if (args.serve !== false && !process.env.CI) {
|
||||
const app = createApp()
|
||||
|
||||
const serveFile = (filePath: string) => lazyEventHandler(async () => {
|
||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||
return eventHandler(event => send(event, contents))
|
||||
})
|
||||
|
||||
console.info('Starting stats server...')
|
||||
|
||||
app.use('/client', serveFile(join(analyzeDir, 'client.html')))
|
||||
app.use('/nitro', serveFile(join(analyzeDir, 'nitro.html')))
|
||||
app.use(eventHandler(() => `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Nuxt Bundle Stats (experimental)</title>
|
||||
</head>
|
||||
<h1>Nuxt Bundle Stats (experimental)</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/nitro">Nitro server bundle stats</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/client">Client bundle stats</a>
|
||||
</li>
|
||||
</ul>
|
||||
</html>
|
||||
`))
|
||||
|
||||
await listen(toNodeListener(app))
|
||||
|
||||
return 'wait' as const
|
||||
}
|
||||
}
|
||||
})
|
@ -1,34 +0,0 @@
|
||||
import { execa } from 'execa'
|
||||
import { consola } from 'consola'
|
||||
import { resolve } from 'pathe'
|
||||
import { tryResolveModule } from '../utils/esm'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
const MODULE_BUILDER_PKG = '@nuxt/module-builder'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'build-module',
|
||||
usage: 'npx nuxi build-module [--stub] [rootDir]',
|
||||
description: `Helper command for using ${MODULE_BUILDER_PKG}`
|
||||
},
|
||||
async invoke (args) {
|
||||
// Find local installed version
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const hasLocal = await tryResolveModule(`${MODULE_BUILDER_PKG}/package.json`, rootDir)
|
||||
|
||||
const execArgs = Object.entries({
|
||||
'--stub': args.stub,
|
||||
'--prepare': args.prepare
|
||||
}).filter(([, value]) => value).map(([key]) => key)
|
||||
|
||||
let cmd = 'nuxt-module-build'
|
||||
if (!hasLocal) {
|
||||
consola.warn(`Cannot find locally installed version of \`${MODULE_BUILDER_PKG}\` (>=0.2.0). Falling back to \`npx ${MODULE_BUILDER_PKG}\``)
|
||||
cmd = 'npx'
|
||||
execArgs.unshift(MODULE_BUILDER_PKG)
|
||||
}
|
||||
|
||||
await execa(cmd, execArgs, { preferLocal: true, stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
})
|
@ -1,70 +0,0 @@
|
||||
import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import type { Nitro } from 'nitropack'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { showVersions } from '../utils/banner'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'build',
|
||||
usage: 'npx nuxi build [--prerender] [--dotenv] [--log-level] [rootDir]',
|
||||
description: 'Build nuxt for production deployment'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('production')
|
||||
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
showVersions(rootDir)
|
||||
|
||||
const { loadNuxt, buildNuxt, useNitro, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
dotenv: {
|
||||
cwd: rootDir,
|
||||
fileName: args.dotenv
|
||||
},
|
||||
overrides: {
|
||||
logLevel: args['log-level'],
|
||||
// TODO: remove in 3.8
|
||||
_generate: args.prerender,
|
||||
...(args.prerender ? { nitro: { static: true } } : {}),
|
||||
...(options?.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
let nitro: Nitro | undefined
|
||||
// In Bridge, if nitro is not enabled, useNitro will throw an error
|
||||
try {
|
||||
// Use ? for backward compatibility for Nuxt <= RC.10
|
||||
nitro = useNitro?.()
|
||||
} catch {}
|
||||
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await writeTypes(nuxt)
|
||||
|
||||
nuxt.hook('build:error', (err) => {
|
||||
consola.error('Nuxt Build Error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
if (args.prerender) {
|
||||
if (!nuxt.options.ssr) {
|
||||
consola.warn('HTML content not prerendered because `ssr: false` was set. You can read more in `https://nuxt.com/docs/getting-started/deployment#static-hosting`.')
|
||||
}
|
||||
// TODO: revisit later if/when nuxt build --prerender will output hybrid
|
||||
const dir = nitro?.options.output.publicDir
|
||||
const publicDir = dir ? relative(process.cwd(), dir) : '.output/public'
|
||||
consola.success(`You can now deploy \`${publicDir}\` to any static hosting!`)
|
||||
}
|
||||
}
|
||||
})
|
@ -1,15 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { cleanupNuxtDirs } from '../utils/nuxt'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'cleanup',
|
||||
usage: 'npx nuxi clean|cleanup',
|
||||
description: 'Cleanup generated nuxt files and caches'
|
||||
},
|
||||
async invoke (args) {
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
await cleanupNuxtDirs(rootDir)
|
||||
}
|
||||
})
|
@ -1,190 +0,0 @@
|
||||
import type { AddressInfo } from 'node:net'
|
||||
import type { RequestListener } from 'node:http'
|
||||
import { relative, resolve } from 'pathe'
|
||||
import chokidar from 'chokidar'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { consola } from 'consola'
|
||||
import { withTrailingSlash } from 'ufo'
|
||||
import { setupDotenv } from 'c12'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { showBanner, showVersions } from '../utils/banner'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { importModule } from '../utils/esm'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { loadNuxtManifest, writeNuxtManifest } from '../utils/nuxt'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'dev',
|
||||
usage: 'npx nuxi dev [rootDir] [--dotenv] [--log-level] [--clipboard] [--open, -o] [--port, -p] [--host, -h] [--https] [--ssl-cert] [--ssl-key]',
|
||||
description: 'Run nuxt development server'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('development')
|
||||
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
showVersions(rootDir)
|
||||
|
||||
await setupDotenv({ cwd: rootDir, fileName: args.dotenv })
|
||||
|
||||
const { loadNuxt, loadNuxtConfig, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
|
||||
const config = await loadNuxtConfig({
|
||||
cwd: rootDir,
|
||||
overrides: {
|
||||
dev: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
const { listen } = await import('listhen')
|
||||
const { toNodeListener } = await import('h3')
|
||||
let currentHandler: RequestListener | undefined
|
||||
let loadingMessage = 'Nuxt is starting...'
|
||||
const loadingHandler: RequestListener = async (_req, res) => {
|
||||
const loadingTemplate = config.devServer.loadingTemplate ?? await importModule('@nuxt/ui-templates', config.modulesDir).then(r => r.loading)
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.statusCode = 503 // Service Unavailable
|
||||
res.end(loadingTemplate({ loading: loadingMessage }))
|
||||
}
|
||||
const serverHandler: RequestListener = (req, res) => {
|
||||
return currentHandler ? currentHandler(req, res) : loadingHandler(req, res)
|
||||
}
|
||||
|
||||
const listener = await listen(serverHandler, {
|
||||
showURL: false,
|
||||
clipboard: args.clipboard,
|
||||
open: args.open || args.o,
|
||||
port: args.port || args.p || process.env.NUXT_PORT || process.env.NITRO_PORT || config.devServer.port,
|
||||
hostname: args.host || args.h || process.env.NUXT_HOST || process.env.NITRO_HOST || config.devServer.host,
|
||||
https: (args.https !== false && (args.https || config.devServer.https))
|
||||
? {
|
||||
cert: args['ssl-cert'] || process.env.NUXT_SSL_CERT || process.env.NITRO_SSL_CERT || (typeof config.devServer.https !== 'boolean' && config.devServer.https.cert) || undefined,
|
||||
key: args['ssl-key'] || process.env.NUXT_SSL_KEY || process.env.NITRO_SSL_KEY || (typeof config.devServer.https !== 'boolean' && config.devServer.https.key) || undefined
|
||||
}
|
||||
: false
|
||||
})
|
||||
|
||||
let currentNuxt: Nuxt
|
||||
let distWatcher: chokidar.FSWatcher
|
||||
|
||||
const showURL = () => {
|
||||
listener.showURL({
|
||||
// TODO: Normalize URL with trailing slash within schema
|
||||
baseURL: withTrailingSlash(currentNuxt?.options.app.baseURL) || '/'
|
||||
})
|
||||
}
|
||||
async function hardRestart (reason?: string) {
|
||||
if (process.send) {
|
||||
await listener.close().catch(() => {})
|
||||
await currentNuxt.close().catch(() => {})
|
||||
await watcher.close().catch(() => {})
|
||||
await distWatcher.close().catch(() => {})
|
||||
consola.info(`${reason ? reason + '. ' : ''}Restarting nuxt...`)
|
||||
process.send({ type: 'nuxt:restart' })
|
||||
} else {
|
||||
await load(true, reason)
|
||||
}
|
||||
}
|
||||
const load = async (isRestart: boolean, reason?: string) => {
|
||||
try {
|
||||
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
||||
currentHandler = undefined
|
||||
if (isRestart) {
|
||||
consola.info(loadingMessage)
|
||||
}
|
||||
if (currentNuxt) {
|
||||
await currentNuxt.close()
|
||||
}
|
||||
if (distWatcher) {
|
||||
await distWatcher.close()
|
||||
}
|
||||
|
||||
currentNuxt = await loadNuxt({
|
||||
rootDir,
|
||||
dev: true,
|
||||
ready: false,
|
||||
overrides: {
|
||||
logLevel: args['log-level'],
|
||||
vite: {
|
||||
clearScreen: args.clear
|
||||
},
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
if (!isRestart) {
|
||||
showURL()
|
||||
}
|
||||
|
||||
// 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 clearBuildDir(currentNuxt.options.buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
await currentNuxt.ready()
|
||||
|
||||
const unsub = currentNuxt.hooks.hook('restart', async (options) => {
|
||||
unsub() // we use this instead of `hookOnce` for Nuxt Bridge support
|
||||
if (options?.hard) { return hardRestart() }
|
||||
await load(true)
|
||||
})
|
||||
|
||||
await currentNuxt.hooks.callHook('listen', listener.server, listener)
|
||||
const address = (listener.server.address() || {}) as AddressInfo
|
||||
currentNuxt.options.devServer.url = listener.url
|
||||
currentNuxt.options.devServer.port = address.port
|
||||
currentNuxt.options.devServer.host = address.address
|
||||
currentNuxt.options.devServer.https = listener.https
|
||||
|
||||
await Promise.all([
|
||||
writeTypes(currentNuxt).catch(console.error),
|
||||
buildNuxt(currentNuxt)
|
||||
])
|
||||
|
||||
distWatcher = chokidar.watch(resolve(currentNuxt.options.buildDir, 'dist'), { ignoreInitial: true, depth: 0 })
|
||||
distWatcher.on('unlinkDir', () => {
|
||||
dLoad(true, '.nuxt/dist directory has been removed')
|
||||
})
|
||||
|
||||
currentHandler = toNodeListener(currentNuxt.server.app)
|
||||
if (isRestart && args.clear !== false) {
|
||||
showBanner()
|
||||
showURL()
|
||||
}
|
||||
} catch (err) {
|
||||
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
||||
currentHandler = undefined
|
||||
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for config changes
|
||||
// TODO: Watcher service, modules, and requireTree
|
||||
const dLoad = debounce(load)
|
||||
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 0 })
|
||||
watcher.on('all', (_event, _file) => {
|
||||
const file = relative(rootDir, _file)
|
||||
if (file === (args.dotenv || '.env')) { return hardRestart('.env updated') }
|
||||
if (RESTART_RE.test(file)) {
|
||||
dLoad(true, `${file} updated`)
|
||||
}
|
||||
})
|
||||
|
||||
await load(false)
|
||||
|
||||
return 'wait' as const
|
||||
}
|
||||
})
|
||||
|
||||
const RESTART_RE = /^(nuxt\.config\.(js|ts|mjs|cjs)|\.nuxtignore|\.nuxtrc)$/
|
@ -1,24 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { execa } from 'execa'
|
||||
import { showHelp } from '../utils/help'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'enable',
|
||||
usage: 'npx nuxi devtools enable|disable [rootDir]',
|
||||
description: 'Enable or disable features in a Nuxt project'
|
||||
},
|
||||
async invoke (args) {
|
||||
const [command, _rootDir = '.'] = args._
|
||||
const rootDir = resolve(_rootDir)
|
||||
|
||||
if (!['enable', 'disable'].includes(command)) {
|
||||
console.error(`Unknown command \`${command}\`.`)
|
||||
showHelp(this.meta)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await execa('npx', ['@nuxt/devtools-wizard@latest', command, rootDir], { stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
})
|
@ -1,14 +0,0 @@
|
||||
import buildCommand from './build'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'generate',
|
||||
usage: 'npx nuxi generate [rootDir] [--dotenv]',
|
||||
description: 'Build Nuxt and prerender static routes'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
args.prerender = true
|
||||
await buildCommand.invoke(args, options)
|
||||
}
|
||||
})
|
@ -1,46 +0,0 @@
|
||||
import type { Argv } from 'mri'
|
||||
|
||||
const _rDefault = (r: any) => r.default || r
|
||||
|
||||
export const commands = {
|
||||
dev: () => import('./dev').then(_rDefault),
|
||||
build: () => import('./build').then(_rDefault),
|
||||
'build-module': () => import('./build-module').then(_rDefault),
|
||||
cleanup: () => import('./cleanup').then(_rDefault),
|
||||
clean: () => import('./cleanup').then(_rDefault),
|
||||
preview: () => import('./preview').then(_rDefault),
|
||||
start: () => import('./preview').then(_rDefault),
|
||||
analyze: () => import('./analyze').then(_rDefault),
|
||||
generate: () => import('./generate').then(_rDefault),
|
||||
prepare: () => import('./prepare').then(_rDefault),
|
||||
typecheck: () => import('./typecheck').then(_rDefault),
|
||||
usage: () => import('./usage').then(_rDefault),
|
||||
info: () => import('./info').then(_rDefault),
|
||||
init: () => import('./init').then(_rDefault),
|
||||
create: () => import('./init').then(_rDefault),
|
||||
devtools: () => import('./devtools').then(_rDefault),
|
||||
upgrade: () => import('./upgrade').then(_rDefault),
|
||||
test: () => import('./test').then(_rDefault),
|
||||
add: () => import('./add').then(_rDefault),
|
||||
new: () => import('./add').then(_rDefault)
|
||||
}
|
||||
|
||||
export type Command = keyof typeof commands
|
||||
|
||||
export interface NuxtCommandMeta {
|
||||
name: string;
|
||||
usage: string;
|
||||
description: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type CLIInvokeResult = void | 'error' | 'wait'
|
||||
|
||||
export interface NuxtCommand {
|
||||
invoke(args: Argv, options?: Record<string, any>): Promise<CLIInvokeResult> | CLIInvokeResult
|
||||
meta: NuxtCommandMeta
|
||||
}
|
||||
|
||||
export function defineNuxtCommand (command: NuxtCommand): NuxtCommand {
|
||||
return command
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
import os from 'node:os'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { resolve } from 'pathe'
|
||||
import jiti from 'jiti'
|
||||
import destr from 'destr'
|
||||
import type { PackageJson } from 'pkg-types'
|
||||
import { splitByCase } from 'scule'
|
||||
import clipboardy from 'clipboardy'
|
||||
import type { NuxtModule } from '@nuxt/schema'
|
||||
import type { packageManagerLocks } from '../utils/packageManagers'
|
||||
import { getPackageManager, getPackageManagerVersion } from '../utils/packageManagers'
|
||||
import { findup } from '../utils/fs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'info',
|
||||
usage: 'npx nuxi info [rootDir]',
|
||||
description: 'Get information about nuxt project'
|
||||
},
|
||||
async invoke (args) {
|
||||
// Resolve rootDir
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
// Load nuxt.config
|
||||
const nuxtConfig = getNuxtConfig(rootDir)
|
||||
|
||||
// Find nearest package.json
|
||||
const { dependencies = {}, devDependencies = {} } = findPackage(rootDir)
|
||||
|
||||
// Utils to query a dependency version
|
||||
const getDepVersion = (name: string) => getPkg(name, rootDir)?.version || dependencies[name] || devDependencies[name]
|
||||
|
||||
const listModules = (arr = []) => arr
|
||||
.map(m => normalizeConfigModule(m, rootDir))
|
||||
.filter(Boolean)
|
||||
.map((name) => {
|
||||
const npmName = name!.split('/').splice(0, 2).join('/') // @foo/bar/baz => @foo/bar
|
||||
const v = getDepVersion(npmName)
|
||||
return '`' + (v ? `${name}@${v}` : name) + '`'
|
||||
})
|
||||
.join(', ')
|
||||
|
||||
// Check nuxt version
|
||||
const nuxtVersion = getDepVersion('nuxt') || getDepVersion('nuxt-edge') || getDepVersion('nuxt3') || '0.0.0'
|
||||
const isNuxt3 = nuxtVersion.startsWith('3')
|
||||
const builder = isNuxt3
|
||||
? nuxtConfig.builder /* latest schema */ || (nuxtConfig.vite !== false ? 'vite' : 'webpack') /* previous schema */
|
||||
: nuxtConfig.bridge?.vite
|
||||
? 'vite' /* bridge vite implementation */
|
||||
: (nuxtConfig.buildModules?.includes('nuxt-vite')
|
||||
? 'vite' /* nuxt-vite */
|
||||
: 'webpack')
|
||||
|
||||
let packageManager: keyof typeof packageManagerLocks | 'unknown' | null = getPackageManager(rootDir)
|
||||
if (packageManager) {
|
||||
packageManager += '@' + getPackageManagerVersion(packageManager)
|
||||
} else {
|
||||
packageManager = 'unknown'
|
||||
}
|
||||
|
||||
const infoObj = {
|
||||
OperatingSystem: os.type(),
|
||||
NodeVersion: process.version,
|
||||
NuxtVersion: nuxtVersion,
|
||||
NitroVersion: getDepVersion('nitropack'),
|
||||
PackageManager: packageManager,
|
||||
Builder: builder,
|
||||
UserConfig: Object.keys(nuxtConfig).map(key => '`' + key + '`').join(', '),
|
||||
RuntimeModules: listModules(nuxtConfig.modules),
|
||||
BuildModules: listModules(nuxtConfig.buildModules || [])
|
||||
}
|
||||
|
||||
console.log('RootDir:', rootDir)
|
||||
|
||||
let maxLength = 0
|
||||
const entries = Object.entries(infoObj).map(([key, val]) => {
|
||||
const label = splitByCase(key).join(' ')
|
||||
if (label.length > maxLength) { maxLength = label.length }
|
||||
return [label, val || '-']
|
||||
})
|
||||
let infoStr = ''
|
||||
for (const [label, value] of entries) {
|
||||
infoStr += '- ' + (label + ': ').padEnd(maxLength + 2) + (value.includes('`') ? value : '`' + value + '`') + '\n'
|
||||
}
|
||||
|
||||
const copied = await clipboardy.write(infoStr).then(() => true).catch(() => false)
|
||||
const splitter = '------------------------------'
|
||||
console.log(`Nuxt project info: ${copied ? '(copied to clipboard)' : ''}\n\n${splitter}\n${infoStr}${splitter}\n`)
|
||||
|
||||
const isNuxt3OrBridge = infoObj.NuxtVersion.startsWith('3') || infoObj.BuildModules.includes('bridge')
|
||||
console.log([
|
||||
'👉 Report an issue: https://github.com/nuxt/nuxt/issues/new',
|
||||
'👉 Suggest an improvement: https://github.com/nuxt/nuxt/discussions/new',
|
||||
`👉 Read documentation: ${isNuxt3OrBridge ? 'https://nuxt.com' : 'https://v2.nuxt.com'}`
|
||||
].join('\n\n') + '\n')
|
||||
}
|
||||
})
|
||||
|
||||
function normalizeConfigModule (module: NuxtModule | string | null | undefined, rootDir: string): string | null {
|
||||
if (!module) {
|
||||
return null
|
||||
}
|
||||
if (typeof module === 'string') {
|
||||
return module
|
||||
.split(rootDir).pop()! // Strip rootDir
|
||||
.split('node_modules').pop()! // Strip node_modules
|
||||
.replace(/^\//, '')
|
||||
}
|
||||
if (typeof module === 'function') {
|
||||
return `${module.name}()`
|
||||
}
|
||||
if (Array.isArray(module)) {
|
||||
return normalizeConfigModule(module[0], rootDir)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function getNuxtConfig (rootDir: string) {
|
||||
try {
|
||||
(globalThis as any).defineNuxtConfig = (c: any) => c
|
||||
const result = jiti(rootDir, { interopDefault: true, esmResolve: true })('./nuxt.config')
|
||||
delete (globalThis as any).defineNuxtConfig
|
||||
return result
|
||||
} catch (err) {
|
||||
// TODO: Show error as warning if it is not 404
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function getPkg (name: string, rootDir: string) {
|
||||
// Assume it is in {rootDir}/node_modules/${name}/package.json
|
||||
let pkgPath = resolve(rootDir, 'node_modules', name, 'package.json')
|
||||
|
||||
// Try to resolve for more accuracy
|
||||
const _require = createRequire(rootDir)
|
||||
try { pkgPath = _require.resolve(name + '/package.json') } catch (_err) {
|
||||
// console.log('not found:', name)
|
||||
}
|
||||
|
||||
return readJSONSync(pkgPath) as PackageJson
|
||||
}
|
||||
|
||||
function findPackage (rootDir: string) {
|
||||
return findup(rootDir, (dir) => {
|
||||
const p = resolve(dir, 'package.json')
|
||||
if (existsSync(p)) {
|
||||
return readJSONSync(p) as PackageJson
|
||||
}
|
||||
}) || {}
|
||||
}
|
||||
|
||||
function readJSONSync (filePath: string) {
|
||||
try {
|
||||
return destr(readFileSync(filePath, 'utf-8'))
|
||||
} catch (err) {
|
||||
// TODO: Warn error
|
||||
return null
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import { downloadTemplate, startShell } from 'giget'
|
||||
import { relative } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
const rpath = (p: string) => relative(process.cwd(), p)
|
||||
|
||||
const DEFAULT_REGISTRY = 'https://raw.githubusercontent.com/nuxt/starter/templates/templates'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'init',
|
||||
usage: 'npx nuxi init|create [--template,-t] [--force] [--offline] [--prefer-offline] [--shell] [dir]',
|
||||
description: 'Initialize a fresh project'
|
||||
},
|
||||
async invoke (args) {
|
||||
// Clone template
|
||||
const template = args.template || args.t || 'v3'
|
||||
|
||||
if (typeof template === 'boolean') {
|
||||
consola.error('Please specify a template!')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let t
|
||||
|
||||
try {
|
||||
t = await downloadTemplate(template, {
|
||||
dir: args._[0] as string,
|
||||
force: args.force,
|
||||
offline: args.offline,
|
||||
preferOffline: args['prefer-offline'],
|
||||
registry: process.env.NUXI_INIT_REGISTRY || DEFAULT_REGISTRY
|
||||
})
|
||||
} catch (err) {
|
||||
if (process.env.DEBUG) {
|
||||
throw err
|
||||
}
|
||||
consola.error((err as Error).toString())
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Show next steps
|
||||
const relativeDist = rpath(t.dir)
|
||||
|
||||
// Write .nuxtrc with `shamefully-hoist=true` for pnpm
|
||||
const usingPnpm = (process.env.npm_config_user_agent || '').includes('pnpm')
|
||||
if (usingPnpm) {
|
||||
await writeFile(`${relativeDist}/.npmrc`, 'shamefully-hoist=true')
|
||||
}
|
||||
|
||||
const nextSteps = [
|
||||
!args.shell && relativeDist.length > 1 && `\`cd ${relativeDist}\``,
|
||||
'Install dependencies with `npm install` or `yarn install` or `pnpm install`',
|
||||
'Start development server with `npm run dev` or `yarn dev` or `pnpm run dev`'
|
||||
].filter(Boolean)
|
||||
|
||||
consola.log(`✨ Nuxt project is created with \`${t.name}\` template. Next steps:`)
|
||||
for (const step of nextSteps) {
|
||||
consola.log(` › ${step}`)
|
||||
}
|
||||
|
||||
if (args.shell) {
|
||||
startShell(t.dir)
|
||||
}
|
||||
}
|
||||
})
|
@ -1,35 +0,0 @@
|
||||
import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'prepare',
|
||||
usage: 'npx nuxi prepare [--log-level] [rootDir]',
|
||||
description: 'Prepare nuxt for development/build'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
const { loadNuxt, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
_prepare: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await buildNuxt(nuxt)
|
||||
await writeTypes(nuxt)
|
||||
consola.success('Types generated in', relative(process.cwd(), nuxt.options.buildDir))
|
||||
}
|
||||
})
|
@ -1,55 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, relative } from 'node:path'
|
||||
import { execa } from 'execa'
|
||||
import { setupDotenv } from 'c12'
|
||||
import { resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { loadKit } from '../utils/kit'
|
||||
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'preview',
|
||||
usage: 'npx nuxi preview|start [--dotenv] [rootDir]',
|
||||
description: 'Launches nitro server for local testing after `nuxi build`.'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const { loadNuxtConfig } = await loadKit(rootDir)
|
||||
const config = await loadNuxtConfig({ cwd: rootDir, overrides: options?.overrides || {} })
|
||||
|
||||
const resolvedOutputDir = resolve(config.srcDir || rootDir, config.nitro.srcDir || 'server', config.nitro.output?.dir || '.output', 'nitro.json')
|
||||
const defaultOutput = resolve(rootDir, '.output', 'nitro.json') // for backwards compatibility
|
||||
|
||||
const nitroJSONPaths = [resolvedOutputDir, defaultOutput]
|
||||
const nitroJSONPath = nitroJSONPaths.find(p => existsSync(p))
|
||||
if (!nitroJSONPath) {
|
||||
consola.error('Cannot find `nitro.json`. Did you run `nuxi build` first? Search path:\n', nitroJSONPaths)
|
||||
process.exit(1)
|
||||
}
|
||||
const outputPath = dirname(nitroJSONPath)
|
||||
const nitroJSON = JSON.parse(await fsp.readFile(nitroJSONPath, 'utf-8'))
|
||||
|
||||
consola.info('Node.js version:', process.versions.node)
|
||||
consola.info('Preset:', nitroJSON.preset)
|
||||
consola.info('Working dir:', relative(process.cwd(), outputPath))
|
||||
|
||||
if (!nitroJSON.commands.preview) {
|
||||
consola.error('Preview is not supported for this build.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const envExists = args.dotenv ? existsSync(resolve(rootDir, args.dotenv)) : existsSync(rootDir)
|
||||
if (envExists) {
|
||||
consola.info('Loading `.env`. This will not be loaded when running the server in production.')
|
||||
await setupDotenv({ cwd: rootDir, fileName: args.dotenv })
|
||||
}
|
||||
|
||||
consola.info('Starting preview command:', nitroJSON.commands.preview)
|
||||
const [command, ...commandArgs] = nitroJSON.commands.preview.split(' ')
|
||||
consola.log('')
|
||||
await execa(command, commandArgs, { stdio: 'inherit', cwd: outputPath })
|
||||
}
|
||||
})
|
@ -1,43 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'test',
|
||||
usage: 'npx nuxi test [--dev] [--watch] [rootDir]',
|
||||
description: 'Run tests'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'test'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const { runTests } = await importTestUtils()
|
||||
await runTests({
|
||||
rootDir,
|
||||
dev: !!args.dev,
|
||||
watch: !!args.watch,
|
||||
...(options || {})
|
||||
})
|
||||
|
||||
if (args.watch) {
|
||||
return 'wait' as const
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function importTestUtils (): Promise<typeof import('@nuxt/test-utils')> {
|
||||
let err
|
||||
for (const pkg of ['@nuxt/test-utils-edge', '@nuxt/test-utils']) {
|
||||
try {
|
||||
const exports = await import(pkg)
|
||||
// Detect old @nuxt/test-utils
|
||||
if (!exports.runTests) {
|
||||
throw new Error('Invalid version of `@nuxt/test-utils` is installed!')
|
||||
}
|
||||
return exports
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
}
|
||||
console.error(err)
|
||||
throw new Error('`@nuxt/test-utils-edge` seems missing. Run `npm i -D @nuxt/test-utils-edge` or `yarn add -D @nuxt/test-utils-edge` to install.')
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { execa } from 'execa'
|
||||
import { resolve } from 'pathe'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { tryResolveModule } from '../utils/esm'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'typecheck',
|
||||
usage: 'npx nuxi typecheck [--log-level] [rootDir]',
|
||||
description: 'Runs `vue-tsc` to check types throughout your app.'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
const { loadNuxt, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
_prepare: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options?.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
// Generate types and build nuxt instance
|
||||
await writeTypes(nuxt)
|
||||
await buildNuxt(nuxt)
|
||||
await nuxt.close()
|
||||
|
||||
// Prefer local install if possible
|
||||
const hasLocalInstall = await tryResolveModule('typescript', rootDir) && await tryResolveModule('vue-tsc/package.json', rootDir)
|
||||
if (hasLocalInstall) {
|
||||
await execa('vue-tsc', ['--noEmit'], { preferLocal: true, stdio: 'inherit', cwd: rootDir })
|
||||
} else {
|
||||
await execa('npx', '-p vue-tsc -p typescript vue-tsc --noEmit'.split(' '), { stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
}
|
||||
})
|
@ -1,74 +0,0 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import { consola } from 'consola'
|
||||
import { resolve } from 'pathe'
|
||||
import { readPackageJSON } from 'pkg-types'
|
||||
import { getPackageManager, packageManagerLocks } from '../utils/packageManagers'
|
||||
import { rmRecursive, touchFile } from '../utils/fs'
|
||||
import { cleanupNuxtDirs, nuxtVersionToGitIdentifier } from '../utils/nuxt'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
async function getNuxtVersion (path: string): Promise<string | null> {
|
||||
try {
|
||||
const pkg = await readPackageJSON('nuxt', { url: path })
|
||||
if (!pkg.version) {
|
||||
consola.warn('Cannot find any installed nuxt versions in ', path)
|
||||
}
|
||||
return pkg.version || null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'upgrade',
|
||||
usage: 'npx nuxi upgrade [--force|-f]',
|
||||
description: 'Upgrade nuxt'
|
||||
},
|
||||
async invoke (args) {
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
// Check package manager
|
||||
const packageManager = getPackageManager(rootDir)
|
||||
if (!packageManager) {
|
||||
console.error('Cannot detect Package Manager in', rootDir)
|
||||
process.exit(1)
|
||||
}
|
||||
const packageManagerVersion = execSync(`${packageManager} --version`).toString('utf8').trim()
|
||||
consola.info('Package Manager:', packageManager, packageManagerVersion)
|
||||
|
||||
// Check currently installed nuxt version
|
||||
const currentVersion = await getNuxtVersion(rootDir) || '[unknown]'
|
||||
consola.info('Current nuxt version:', currentVersion)
|
||||
|
||||
// Force install
|
||||
if (args.force || args.f) {
|
||||
consola.info('Removing lock-file and node_modules...')
|
||||
const pmLockFile = resolve(rootDir, packageManagerLocks[packageManager])
|
||||
await rmRecursive([pmLockFile, resolve(rootDir, 'node_modules')])
|
||||
await touchFile(pmLockFile)
|
||||
}
|
||||
|
||||
// Install latest version
|
||||
consola.info('Installing latest Nuxt 3 release...')
|
||||
execSync(`${packageManager} ${packageManager === 'yarn' ? 'add' : 'install'} -D nuxt`, { stdio: 'inherit', cwd: rootDir })
|
||||
|
||||
// Cleanup after upgrade
|
||||
await cleanupNuxtDirs(rootDir)
|
||||
|
||||
// Check installed nuxt version again
|
||||
const upgradedVersion = await getNuxtVersion(rootDir) || '[unknown]'
|
||||
consola.info('Upgraded nuxt version:', upgradedVersion)
|
||||
|
||||
if (upgradedVersion === currentVersion) {
|
||||
consola.success('You\'re already using the latest version of nuxt.')
|
||||
} else {
|
||||
consola.success('Successfully upgraded nuxt from', currentVersion, 'to', upgradedVersion)
|
||||
const commitA = nuxtVersionToGitIdentifier(currentVersion)
|
||||
const commitB = nuxtVersionToGitIdentifier(upgradedVersion)
|
||||
if (commitA && commitB) {
|
||||
consola.info('Changelog:', `https://github.com/nuxt/nuxt/compare/${commitA}...${commitB}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -1,21 +0,0 @@
|
||||
import { cyan } from 'colorette'
|
||||
import { showHelp } from '../utils/help'
|
||||
import { commands, defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'help',
|
||||
usage: 'nuxt help',
|
||||
description: 'Show help'
|
||||
},
|
||||
invoke (_args) {
|
||||
const sections: string[] = []
|
||||
|
||||
sections.push(`Usage: ${cyan(`npx nuxi ${Object.keys(commands).join('|')} [args]`)}`)
|
||||
|
||||
console.log(sections.join('\n\n') + '\n')
|
||||
|
||||
// Reuse the same wording as in `-h` commands
|
||||
showHelp({})
|
||||
}
|
||||
})
|
@ -1 +0,0 @@
|
||||
export * from './run'
|
@ -1,13 +0,0 @@
|
||||
import mri from 'mri'
|
||||
import type { Command, NuxtCommand } from './commands'
|
||||
import { commands } from './commands'
|
||||
|
||||
export async function runCommand (command: string, argv = process.argv.slice(2), options: Record<string, any> = {}) {
|
||||
const args = mri(argv)
|
||||
args.clear = false // used by dev
|
||||
const cmd = await commands[command as Command]() as NuxtCommand
|
||||
if (!cmd) {
|
||||
throw new Error(`Invalid command ${command}`)
|
||||
}
|
||||
await cmd.invoke(args, options)
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import clear from 'clear'
|
||||
import { bold, gray, green } from 'colorette'
|
||||
import { version } from '../../package.json'
|
||||
import { tryRequireModule } from './cjs'
|
||||
|
||||
export function showBanner (_clear?: boolean) {
|
||||
if (_clear) { clear() }
|
||||
console.log(gray(`Nuxi ${(bold(version))}`))
|
||||
}
|
||||
|
||||
export function showVersions (cwd: string) {
|
||||
const getPkgVersion = (pkg: string) => {
|
||||
return tryRequireModule(`${pkg}/package.json`, cwd)?.version || ''
|
||||
}
|
||||
const nuxtVersion = getPkgVersion('nuxt') || getPkgVersion('nuxt-edge')
|
||||
const nitroVersion = getPkgVersion('nitropack')
|
||||
console.log(gray(
|
||||
green(`Nuxt ${bold(nuxtVersion)}`) +
|
||||
(nitroVersion ? ` with Nitro ${(bold(nitroVersion))}` : '')
|
||||
))
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { normalize } from 'pathe'
|
||||
|
||||
function getModulePaths (paths?: string | string[]): string[] {
|
||||
return ([] as Array<string | undefined>)
|
||||
.concat(
|
||||
global.__NUXT_PREPATHS__,
|
||||
paths,
|
||||
process.cwd(),
|
||||
global.__NUXT_PATHS__
|
||||
)
|
||||
.filter(Boolean) as string[]
|
||||
}
|
||||
|
||||
const _require = createRequire(process.cwd())
|
||||
|
||||
function resolveModule (id: string, paths?: string | string[]) {
|
||||
return normalize(_require.resolve(id, { paths: getModulePaths(paths) }))
|
||||
}
|
||||
|
||||
function requireModule (id: string, paths?: string | string[]) {
|
||||
return _require(resolveModule(id, paths))
|
||||
}
|
||||
|
||||
export function tryRequireModule (id: string, paths?: string | string[]) {
|
||||
try { return requireModule(id, paths) } catch { return null }
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import flatten from 'flat'
|
||||
import { detailedDiff } from 'deep-object-diff'
|
||||
import { blue, cyan, green, red } from 'colorette'
|
||||
|
||||
function normalizeDiff (diffObj: any, type: 'added' | 'deleted' | 'updated', ignore: string[]) {
|
||||
return Object.entries(flatten(diffObj) as Record<string, any>)
|
||||
.map(([key, value]) => ({ key, value, type }))
|
||||
.filter(item => !ignore.includes(item.key) && typeof item.value !== 'function')
|
||||
}
|
||||
|
||||
export function diff (a: any, b: any, ignore: string[]) {
|
||||
const _diff: any = detailedDiff(a, b)
|
||||
return [
|
||||
...normalizeDiff(_diff.added, 'added', ignore),
|
||||
...normalizeDiff(_diff.deleted, 'deleted', ignore),
|
||||
...normalizeDiff(_diff.updated, 'updated', ignore)
|
||||
]
|
||||
}
|
||||
|
||||
const typeMap = {
|
||||
added: green('added'),
|
||||
deleted: red('deleted'),
|
||||
updated: blue('updated')
|
||||
}
|
||||
|
||||
export function printDiff (diff: any) {
|
||||
for (const item of diff) {
|
||||
console.log(' ', typeMap[item.type as keyof typeof typeMap] || item.type, cyan(item.key), item.value ? `~> ${cyan(item.value)}` : '')
|
||||
}
|
||||
console.log()
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { engines } from '../../package.json'
|
||||
|
||||
export async function checkEngines () {
|
||||
const satisfies = await import('semver/functions/satisfies.js')
|
||||
.then(r => r.default || r as any as typeof import('semver/functions/satisfies.js')) // npm/node-semver#381
|
||||
const currentNode = process.versions.node
|
||||
const nodeRange = engines.node
|
||||
|
||||
if (!satisfies(currentNode, nodeRange)) {
|
||||
console.warn(`Current version of Node.js (\`${currentNode}\`) is unsupported and might cause issues.\n Please upgrade to a compatible version (${nodeRange}).`)
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
export const overrideEnv = (targetEnv: string) => {
|
||||
const currentEnv = process.env.NODE_ENV
|
||||
if (currentEnv && currentEnv !== targetEnv) {
|
||||
console.warn(`Changing \`NODE_ENV\` from \`${currentEnv}\` to \`${targetEnv}\`, to avoid unintended behavior.`)
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = targetEnv
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { interopDefault, resolvePath } from 'mlly'
|
||||
|
||||
export async function tryResolveModule (id: string, url = import.meta.url) {
|
||||
try {
|
||||
return await resolvePath(id, { url })
|
||||
} catch { }
|
||||
}
|
||||
|
||||
export async function importModule (id: string, url: string | string[] = import.meta.url) {
|
||||
const resolvedPath = await resolvePath(id, { url })
|
||||
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, join } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
|
||||
export async function clearDir (path: string, exclude?: string[]) {
|
||||
if (!exclude) {
|
||||
await fsp.rm(path, { recursive: true, force: true })
|
||||
} else if (existsSync(path)) {
|
||||
const files = await fsp.readdir(path)
|
||||
await Promise.all(files.map(async (name) => {
|
||||
if (!exclude.includes(name)) {
|
||||
await fsp.rm(join(path, name), { recursive: true, force: true })
|
||||
}
|
||||
}))
|
||||
}
|
||||
await fsp.mkdir(path, { recursive: true })
|
||||
}
|
||||
|
||||
export function clearBuildDir (path: string) {
|
||||
return clearDir(path, ['cache', 'analyze'])
|
||||
}
|
||||
|
||||
export async function rmRecursive (paths: string[]) {
|
||||
await Promise.all(paths.filter(p => typeof p === 'string').map(async (path) => {
|
||||
consola.debug('Removing recursive path', path)
|
||||
await fsp.rm(path, { recursive: true, force: true }).catch(() => {})
|
||||
}))
|
||||
}
|
||||
|
||||
export async function touchFile (path: string) {
|
||||
const time = new Date()
|
||||
await fsp.utimes(path, time, time).catch(() => {})
|
||||
}
|
||||
|
||||
export function findup<T> (rootDir: string, fn: (dir: string) => T | undefined): T | null {
|
||||
let dir = rootDir
|
||||
while (dir !== dirname(dir)) {
|
||||
const res = fn(dir)
|
||||
if (res) {
|
||||
return res
|
||||
}
|
||||
dir = dirname(dir)
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { cyan, magenta } from 'colorette'
|
||||
import type { NuxtCommandMeta } from '../commands'
|
||||
|
||||
export function showHelp (meta?: Partial<NuxtCommandMeta>) {
|
||||
const sections: string[] = []
|
||||
|
||||
if (meta) {
|
||||
if (meta.usage) {
|
||||
sections.push(magenta('> ') + 'Usage: ' + cyan(meta.usage))
|
||||
}
|
||||
|
||||
if (meta.description) {
|
||||
sections.push(magenta('⋮ ') + meta.description)
|
||||
}
|
||||
}
|
||||
|
||||
sections.push(`Use ${cyan('npx nuxi [command] --help')} to see help for each command`)
|
||||
|
||||
console.log(sections.join('\n\n') + '\n')
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { importModule, tryResolveModule } from './esm'
|
||||
|
||||
export const loadKit = async (rootDir: string): Promise<typeof import('@nuxt/kit')> => {
|
||||
try {
|
||||
// Without PNP (or if users have a local install of kit, we bypass resolving from nuxt)
|
||||
const localKit = await tryResolveModule('@nuxt/kit', rootDir)
|
||||
// Otherwise, we resolve Nuxt _first_ as it is Nuxt's kit dependency that will be used
|
||||
const rootURL = localKit ? rootDir : await tryResolveNuxt() || rootDir
|
||||
return await importModule('@nuxt/kit', rootURL) as typeof import('@nuxt/kit')
|
||||
} catch (e: any) {
|
||||
if (e.toString().includes("Cannot find module '@nuxt/kit'")) {
|
||||
throw new Error('nuxi requires `@nuxt/kit` to be installed in your project. Try installing `nuxt` v3 or `@nuxt/bridge` first.')
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function tryResolveNuxt () {
|
||||
for (const pkg of ['nuxt3', 'nuxt', 'nuxt-edge']) {
|
||||
const path = await tryResolveModule(pkg)
|
||||
if (path) { return path }
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { hash } from 'ohash'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { rmRecursive } from './fs'
|
||||
|
||||
interface NuxtProjectManifest {
|
||||
_hash: string | null
|
||||
project: {
|
||||
rootDir: string
|
||||
},
|
||||
versions: {
|
||||
nuxt: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function cleanupNuxtDirs (rootDir: string) {
|
||||
consola.info('Cleaning up generated nuxt files and caches...')
|
||||
|
||||
await rmRecursive([
|
||||
'.nuxt',
|
||||
'.output',
|
||||
'dist',
|
||||
'node_modules/.vite',
|
||||
'node_modules/.cache'
|
||||
].map(dir => resolve(rootDir, dir)))
|
||||
}
|
||||
|
||||
export function nuxtVersionToGitIdentifier (version: string) {
|
||||
// match the git identifier in the release, for example: 3.0.0-rc.8-27677607.a3a8706
|
||||
const id = /\.([0-9a-f]{7,8})$/.exec(version)
|
||||
if (id?.[1]) {
|
||||
return id[1]
|
||||
}
|
||||
// match github tag, for example 3.0.0-rc.8
|
||||
return `v${version}`
|
||||
}
|
||||
|
||||
function resolveNuxtManifest (nuxt: Nuxt): NuxtProjectManifest {
|
||||
const manifest: NuxtProjectManifest = {
|
||||
_hash: null,
|
||||
project: {
|
||||
rootDir: nuxt.options.rootDir
|
||||
},
|
||||
versions: {
|
||||
nuxt: nuxt._version
|
||||
}
|
||||
}
|
||||
manifest._hash = hash(manifest)
|
||||
return manifest
|
||||
}
|
||||
|
||||
export async function writeNuxtManifest (nuxt: Nuxt): Promise<NuxtProjectManifest> {
|
||||
const manifest = resolveNuxtManifest(nuxt)
|
||||
const manifestPath = resolve(nuxt.options.buildDir, 'nuxt.json')
|
||||
await fsp.mkdir(dirname(manifestPath), { recursive: true })
|
||||
await fsp.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
||||
return manifest
|
||||
}
|
||||
|
||||
export async function loadNuxtManifest (buildDir: string): Promise<NuxtProjectManifest | null> {
|
||||
const manifestPath = resolve(buildDir, 'nuxt.json')
|
||||
const manifest: NuxtProjectManifest | null = await fsp.readFile(manifestPath, 'utf-8')
|
||||
.then(data => JSON.parse(data) as NuxtProjectManifest)
|
||||
.catch(() => null)
|
||||
return manifest
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { resolve } from 'pathe'
|
||||
import { findup } from './fs'
|
||||
|
||||
export const packageManagerLocks = {
|
||||
yarn: 'yarn.lock',
|
||||
npm: 'package-lock.json',
|
||||
pnpm: 'pnpm-lock.yaml',
|
||||
bun: 'bun.lockb'
|
||||
}
|
||||
|
||||
type PackageManager = keyof typeof packageManagerLocks
|
||||
|
||||
export function getPackageManager (rootDir: string) {
|
||||
return findup(rootDir, (dir) => {
|
||||
for (const name in packageManagerLocks) {
|
||||
const path = packageManagerLocks[name as PackageManager]
|
||||
if (path && existsSync(resolve(dir, path))) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
}) as PackageManager | null
|
||||
}
|
||||
|
||||
export function getPackageManagerVersion (name: string) {
|
||||
return execSync(`${name} --version`).toString('utf8').trim()
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
import { upperFirst } from 'scule'
|
||||
|
||||
interface TemplateOptions {
|
||||
name: string,
|
||||
args: Record<string, any>
|
||||
}
|
||||
|
||||
interface Template {
|
||||
(options: TemplateOptions): { path: string, contents: string }
|
||||
}
|
||||
|
||||
const httpMethods = ['connect', 'delete', 'get', 'head', 'options', 'post', 'put', 'trace', 'patch']
|
||||
const api: Template = ({ name, args }) => ({
|
||||
path: `server/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`,
|
||||
contents: `
|
||||
export default defineEventHandler((event) => {
|
||||
return 'Hello ${name}'
|
||||
})
|
||||
`
|
||||
})
|
||||
|
||||
const plugin: Template = ({ name, args }) => ({
|
||||
path: `plugins/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`,
|
||||
contents: `
|
||||
export default defineNuxtPlugin((nuxtApp) => {})
|
||||
`
|
||||
})
|
||||
|
||||
const component: Template = ({ name, args }) => ({
|
||||
path: `components/${name}${applySuffix(args, ['client', 'server'], 'mode')}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Component: ${name}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
const composable: Template = ({ name }) => {
|
||||
const nameWithUsePrefix = name.startsWith('use') ? name : `use${upperFirst(name)}`
|
||||
return {
|
||||
path: `composables/${name}.ts`,
|
||||
contents: `
|
||||
export const ${nameWithUsePrefix} = () => {
|
||||
return ref()
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
const middleware: Template = ({ name, args }) => ({
|
||||
path: `middleware/${name}${applySuffix(args, ['global'])}.ts`,
|
||||
contents: `
|
||||
export default defineNuxtRouteMiddleware((to, from) => {})
|
||||
`
|
||||
})
|
||||
|
||||
const layout: Template = ({ name }) => ({
|
||||
path: `layouts/${name}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Layout: ${name}
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
const page: Template = ({ name }) => ({
|
||||
path: `pages/${name}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Page: foo
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
export const templates = {
|
||||
api,
|
||||
plugin,
|
||||
component,
|
||||
composable,
|
||||
middleware,
|
||||
layout,
|
||||
page
|
||||
} as Record<string, Template>
|
||||
|
||||
// -- internal utils --
|
||||
|
||||
function applySuffix (args: TemplateOptions['args'], suffixes: string[], unwrapFrom?: string): string {
|
||||
let suffix = ''
|
||||
// --client
|
||||
for (const s of suffixes) {
|
||||
if (args[s]) {
|
||||
suffix += '.' + s
|
||||
}
|
||||
}
|
||||
// --mode=server
|
||||
if (unwrapFrom && args[unwrapFrom] && suffixes.includes(args[unwrapFrom])) {
|
||||
suffix += '.' + args[unwrapFrom]
|
||||
}
|
||||
return suffix
|
||||
}
|
@ -82,7 +82,7 @@
|
||||
"magic-string": "^0.30.3",
|
||||
"mlly": "^1.4.0",
|
||||
"nitropack": "^2.6.0",
|
||||
"nuxi": "workspace:../nuxi",
|
||||
"nuxi": "npm:nuxi-ng@0.3.0-1692970235.c259efa",
|
||||
"nypm": "^0.3.1",
|
||||
"ofetch": "^1.3.3",
|
||||
"ohash": "^1.1.3",
|
||||
|
@ -17,11 +17,12 @@ export async function startServer () {
|
||||
ctx.url = 'http://127.0.0.1:' + port
|
||||
if (ctx.options.dev) {
|
||||
const nuxiCLI = await kit.resolvePath('nuxi/cli')
|
||||
ctx.serverProcess = execa(nuxiCLI, ['dev'], {
|
||||
ctx.serverProcess = execa(nuxiCLI, ['_dev'], {
|
||||
cwd: ctx.nuxt!.options.rootDir,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
_PORT: String(port),
|
||||
PORT: String(port),
|
||||
NITRO_PORT: String(port),
|
||||
NODE_ENV: 'development'
|
||||
@ -29,7 +30,7 @@ export async function startServer () {
|
||||
})
|
||||
await waitForPort(port, { retries: 32 })
|
||||
let lastError
|
||||
for (let i = 0; i < 50; i++) {
|
||||
for (let i = 0; i < 150; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
try {
|
||||
const res = await $fetch(ctx.nuxt!.options.app.baseURL)
|
||||
|
136
pnpm-lock.yaml
136
pnpm-lock.yaml
@ -10,7 +10,6 @@ overrides:
|
||||
'@nuxt/test-utils': workspace:*
|
||||
'@nuxt/vite-builder': workspace:*
|
||||
'@nuxt/webpack-builder': workspace:*
|
||||
nuxi: workspace:*
|
||||
nuxt: workspace:*
|
||||
vite: 4.4.9
|
||||
vue: 3.3.4
|
||||
@ -96,8 +95,8 @@ importers:
|
||||
specifier: 2.6.0
|
||||
version: 2.6.0
|
||||
nuxi:
|
||||
specifier: workspace:*
|
||||
version: link:packages/nuxi
|
||||
specifier: npm:nuxi-ng@0.3.0-1692970235.c259efa
|
||||
version: /nuxi-ng@0.3.0-1692970235.c259efa
|
||||
nuxt:
|
||||
specifier: workspace:*
|
||||
version: link:packages/nuxt
|
||||
@ -241,109 +240,6 @@ importers:
|
||||
specifier: 5.88.2
|
||||
version: 5.88.2
|
||||
|
||||
packages/nuxi:
|
||||
optionalDependencies:
|
||||
fsevents:
|
||||
specifier: ~2.3.3
|
||||
version: 2.3.3
|
||||
devDependencies:
|
||||
'@nuxt/kit':
|
||||
specifier: workspace:*
|
||||
version: link:../kit
|
||||
'@nuxt/schema':
|
||||
specifier: workspace:*
|
||||
version: link:../schema
|
||||
'@types/clear':
|
||||
specifier: 0.1.2
|
||||
version: 0.1.2
|
||||
'@types/flat':
|
||||
specifier: 5.0.2
|
||||
version: 5.0.2
|
||||
'@types/mri':
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1
|
||||
'@types/semver':
|
||||
specifier: 7.5.0
|
||||
version: 7.5.0
|
||||
c12:
|
||||
specifier: 1.4.2
|
||||
version: 1.4.2
|
||||
chokidar:
|
||||
specifier: 3.5.3
|
||||
version: 3.5.3
|
||||
clear:
|
||||
specifier: 0.1.0
|
||||
version: 0.1.0
|
||||
clipboardy:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
colorette:
|
||||
specifier: 2.0.20
|
||||
version: 2.0.20
|
||||
consola:
|
||||
specifier: 3.2.3
|
||||
version: 3.2.3
|
||||
deep-object-diff:
|
||||
specifier: 1.1.9
|
||||
version: 1.1.9
|
||||
defu:
|
||||
specifier: 6.1.2
|
||||
version: 6.1.2
|
||||
destr:
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1
|
||||
execa:
|
||||
specifier: 7.2.0
|
||||
version: 7.2.0
|
||||
flat:
|
||||
specifier: 5.0.2
|
||||
version: 5.0.2
|
||||
giget:
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2
|
||||
h3:
|
||||
specifier: 1.8.0
|
||||
version: 1.8.0
|
||||
jiti:
|
||||
specifier: 1.19.3
|
||||
version: 1.19.3
|
||||
listhen:
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0
|
||||
mlly:
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0
|
||||
mri:
|
||||
specifier: 1.2.0
|
||||
version: 1.2.0
|
||||
nitropack:
|
||||
specifier: 2.6.0
|
||||
version: 2.6.0
|
||||
ohash:
|
||||
specifier: 1.1.3
|
||||
version: 1.1.3
|
||||
pathe:
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1
|
||||
perfect-debounce:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
pkg-types:
|
||||
specifier: 1.0.3
|
||||
version: 1.0.3
|
||||
scule:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
semver:
|
||||
specifier: 7.5.4
|
||||
version: 7.5.4
|
||||
ufo:
|
||||
specifier: 1.3.0
|
||||
version: 1.3.0
|
||||
unbuild:
|
||||
specifier: latest
|
||||
version: 2.0.0(typescript@5.2.2)
|
||||
|
||||
packages/nuxt:
|
||||
dependencies:
|
||||
'@nuxt/devalue':
|
||||
@ -440,8 +336,8 @@ importers:
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
nuxi:
|
||||
specifier: workspace:*
|
||||
version: link:../nuxi
|
||||
specifier: npm:nuxi-ng@0.3.0-1692970235.c259efa
|
||||
version: /nuxi-ng@0.3.0-1692970235.c259efa
|
||||
nypm:
|
||||
specifier: ^0.3.1
|
||||
version: 0.3.1
|
||||
@ -2630,6 +2526,7 @@ packages:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
micromatch: 4.0.5
|
||||
napi-wasm: 1.1.0
|
||||
bundledDependencies:
|
||||
- napi-wasm
|
||||
|
||||
@ -2921,10 +2818,6 @@ packages:
|
||||
'@types/webpack': 4.41.33
|
||||
dev: true
|
||||
|
||||
/@types/flat@5.0.2:
|
||||
resolution: {integrity: sha512-3zsplnP2djeps5P9OyarTxwRpMLoe5Ash8aL9iprw0JxB+FAHjY+ifn4yZUuW4/9hqtnmor6uvjSRzJhiVbrEQ==}
|
||||
dev: true
|
||||
|
||||
/@types/fs-extra@11.0.1:
|
||||
resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==}
|
||||
dependencies:
|
||||
@ -2995,10 +2888,6 @@ packages:
|
||||
'@types/unist': 2.0.7
|
||||
dev: true
|
||||
|
||||
/@types/mri@1.1.1:
|
||||
resolution: {integrity: sha512-nJOuiTlsvmClSr3+a/trTSx4DTuY/VURsWGKSf/eeavh0LRMqdsK60ti0TlwM5iHiGOK3/Ibkxsbr7i9rzGreA==}
|
||||
dev: true
|
||||
|
||||
/@types/ms@0.7.31:
|
||||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||
dev: true
|
||||
@ -4573,6 +4462,7 @@ packages:
|
||||
|
||||
/clear@0.1.0:
|
||||
resolution: {integrity: sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==}
|
||||
dev: false
|
||||
|
||||
/clipboardy@3.0.0:
|
||||
resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==}
|
||||
@ -4992,10 +4882,6 @@ packages:
|
||||
/deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
/deep-object-diff@1.1.9:
|
||||
resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==}
|
||||
dev: true
|
||||
|
||||
/deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -8062,6 +7948,9 @@ packages:
|
||||
engines: {node: ^14 || ^16 || >=18}
|
||||
hasBin: true
|
||||
|
||||
/napi-wasm@1.1.0:
|
||||
resolution: {integrity: sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==}
|
||||
|
||||
/natural-compare-lite@1.4.0:
|
||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||
dev: true
|
||||
@ -8274,6 +8163,13 @@ packages:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
/nuxi-ng@0.3.0-1692970235.c259efa:
|
||||
resolution: {integrity: sha512-/yp+V+Vpype1fau39yktrkFu2UZ2jo5bSdvDB8lI3LEdYT7sF9zg+6HqP+0OAV4Vnmst6ATxwY+pZiK+ds5mig==}
|
||||
engines: {node: ^14.18.0 || >=16.10.0}
|
||||
hasBin: true
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
/nuxt-component-meta@0.5.3:
|
||||
resolution: {integrity: sha512-+MHUrESdr+Si9PdbkxQrzQv+X6RdRd/ffmFWVVsZAHA7X9vGoNAYxwvoB1Pbs15TaPtFBWA1P5a4VGqtp+bhAg==}
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user