feat(nuxi): add nuxi preview command for local testing (#2162)

Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
Daniel Roe 2021-12-21 11:46:42 +00:00 committed by GitHub
parent 279bfdc5b3
commit b5618e976b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 128 additions and 39 deletions

View File

@ -66,6 +66,16 @@ npm run build
Nuxt will create a [`.output`](/docs/directory-structure/output) directory with all your application, server and dependencies ready to be deployed. Check out the [deployment](/docs/deployment) section to learn where and how you can deploy a Nuxt application using Nitro.
## Previewing your production build
Once you've built your Nuxt application, you can preview it locally:
```bash
npx nuxi preview
```
If you're using a supported preset, this will start a local server (for testing purposes only).
## Upgrade Nuxt3 version
To upgrade Nuxt3 version:

View File

@ -4,7 +4,7 @@ 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, readDirRecursively } from './utils'
import { hl, prettyPath, serializeTemplate, writeFile, isDirectory, readDirRecursively, replaceAll } from './utils'
import { NitroContext } from './context'
import { scanMiddleware } from './server/middleware'
@ -107,10 +107,35 @@ async function _build (nitroContext: NitroContext) {
consola.start('Writing server bundle...')
await build.write(nitroContext.rollupConfig.output)
const rewriteBuildPaths = (input: unknown, to: string) =>
typeof input === 'string' ? replaceAll(input, nitroContext.output.dir, to) : undefined
// Write build info
const nitroConfigPath = resolve(nitroContext.output.dir, 'nitro.json')
const buildInfo = {
date: new Date(),
preset: nitroContext.preset,
commands: {
preview: rewriteBuildPaths(nitroContext.commands.preview, '.'),
deploy: rewriteBuildPaths(nitroContext.commands.deploy, '.')
}
}
await writeFile(nitroConfigPath, JSON.stringify(buildInfo, null, 2))
consola.success('Server built')
await printFSTree(nitroContext.output.serverDir)
await nitroContext._internal.hooks.callHook('nitro:compiled', nitroContext)
// Show deploy and preview hints
const rOutDir = relative(process.cwd(), nitroContext.output.dir)
if (nitroContext.commands.preview) {
// consola.info(`You can preview this build using \`${rewriteBuildPaths(nitroContext.commands.preview, rOutDir)}\``)
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)}\``)
}
return {
entry: resolve(nitroContext.rollupConfig.output.dir, nitroContext.rollupConfig.output.entryFileNames as string)
}

View File

@ -5,7 +5,7 @@ import { createHooks, Hookable, NestedHooks } from 'hookable'
import type { Preset } from 'unenv'
import type { NuxtHooks, NuxtOptions } from '@nuxt/schema'
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
import { tryImport, resolvePath, detectTarget, extendPreset, evalTemplate } from './utils'
import * as PRESETS from './presets'
import type { NodeExternalsOptions } from './rollup/plugins/externals'
import type { StorageOptions } from './rollup/plugins/storage'
@ -40,6 +40,10 @@ export interface NitroContext {
experiments?: {
wasm?: boolean
}
commands: {
preview: string | ((config: NitroContext) => string)
deploy: string | ((config: NitroContext) => string)
},
moduleSideEffects: string[]
renderer: string
serveStatic: boolean
@ -104,6 +108,10 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
moduleSideEffects: ['unenv/runtime/polyfill/'],
renderer: undefined,
serveStatic: undefined,
commands: {
preview: undefined,
deploy: undefined
},
middleware: [],
scannedMiddleware: [],
ignore: [],
@ -164,6 +172,13 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
nitroContext.output.publicDir = resolvePath(nitroContext, nitroContext.output.publicDir)
nitroContext.output.serverDir = resolvePath(nitroContext, nitroContext.output.serverDir)
if (nitroContext.commands.preview) {
nitroContext.commands.preview = evalTemplate(nitroContext, nitroContext.commands.preview)
}
if (nitroContext.commands.deploy) {
nitroContext.commands.deploy = evalTemplate(nitroContext, nitroContext.commands.deploy)
}
nitroContext._internal.hooks.addHooks(nitroContext.hooks)
// Dev-only storage

View File

@ -1,8 +1,7 @@
import consola from 'consola'
import fse from 'fs-extra'
import globby from 'globby'
import { join, resolve } from 'pathe'
import { hl, prettyPath, writeFile } from '../utils'
import { writeFile } from '../utils'
import { NitroPreset, NitroContext } from '../context'
export const azure: NitroPreset = {
@ -11,6 +10,9 @@ export const azure: NitroPreset = {
output: {
serverDir: '{{ output.dir }}/server/functions'
},
commands: {
preview: 'npx @azure/static-web-apps-cli start {{ output.publicDir }} --api-location {{ output.serverDir }}/..'
},
hooks: {
async 'nitro:compiled' (ctx: NitroContext) {
await writeRoutes(ctx)
@ -101,8 +103,4 @@ async function writeRoutes ({ output }: NitroContext) {
if (!indexFileExists) {
await writeFile(indexPath, '')
}
const apiDir = resolve(output.serverDir, '..')
consola.success('Ready to run', hl('npx @azure/static-web-apps-cli start ' + prettyPath(output.publicDir) + ' --api-location ' + prettyPath(apiDir)), 'for local testing')
}

View File

@ -1,8 +1,7 @@
import { createWriteStream } from 'fs'
import archiver from 'archiver'
import consola from 'consola'
import { join, resolve } from 'pathe'
import { prettyPath, writeFile } from '../utils'
import { writeFile } from '../utils'
import { NitroPreset, NitroContext } from '../context'
// eslint-disable-next-line
@ -10,6 +9,9 @@ export const azure_functions: NitroPreset = {
serveStatic: true,
entry: '{{ _internal.runtimeDir }}/entries/azure_functions',
externals: true,
commands: {
deploy: 'az functionapp deployment source config-zip -g <resource-group> -n <app-name> --src {{ output.dir }}/deploy.zip'
},
hooks: {
async 'nitro:compiled' (ctx: NitroContext) {
await writeRoutes(ctx)
@ -67,9 +69,5 @@ async function writeRoutes ({ output: { dir, serverDir } }: NitroContext) {
await writeFile(resolve(serverDir, 'function.json'), JSON.stringify(functionDefinition))
await writeFile(resolve(dir, 'host.json'), JSON.stringify(host))
await zipDirectory(dir, join(dir, 'deploy.zip'))
const zipPath = prettyPath(resolve(dir, 'deploy.zip'))
consola.success(`Ready to run \`az functionapp deployment source config-zip -g <resource-group> -n <app-name> --src ${zipPath}\``)
}

View File

@ -1,6 +1,5 @@
import { resolve } from 'pathe'
import consola from 'consola'
import { extendPreset, writeFile, prettyPath, hl } from '../utils'
import { extendPreset, writeFile } from '../utils'
import { NitroContext, NitroPreset } from '../context'
import { worker } from './worker'
@ -9,15 +8,14 @@ export const cloudflare: NitroPreset = extendPreset(worker, {
ignore: [
'wrangler.toml'
],
commands: {
preview: 'npx miniflare {{ output.serverDir }}/index.mjs --site {{ output.publicDir }}',
deploy: 'cd {{ output.serverDir }} && npx wrangler publish'
},
hooks: {
async 'nitro:compiled' ({ output, _nuxt }: NitroContext) {
await writeFile(resolve(output.dir, 'package.json'), JSON.stringify({ private: true, main: './server/index.mjs' }, null, 2))
await writeFile(resolve(output.dir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
let inDir = prettyPath(_nuxt.rootDir)
if (inDir) {
inDir = 'in ' + inDir
}
consola.success('Ready to run', hl('npx wrangler publish ' + inDir), 'or', hl('npx miniflare ' + prettyPath(output.serverDir) + '/index.mjs --site ' + prettyPath(output.publicDir)), 'for local testing')
}
}
})

View File

@ -1,7 +1,6 @@
import { createRequire } from 'module'
import { join, relative, resolve } from 'pathe'
import fse from 'fs-extra'
import consola from 'consola'
import globby from 'globby'
import { readPackageJSON } from 'pkg-types'
import { writeFile } from '../utils'
@ -10,6 +9,9 @@ import { NitroPreset, NitroContext } from '../context'
export const firebase: NitroPreset = {
entry: '{{ _internal.runtimeDir }}/entries/firebase',
externals: true,
commands: {
deploy: 'npx firebase deploy'
},
hooks: {
async 'nitro:compiled' (ctx: NitroContext) {
await writeRoutes(ctx)
@ -85,6 +87,4 @@ async function writeRoutes ({ output: { publicDir, serverDir }, _nuxt: { rootDir
2
)
)
consola.success('Ready to run `firebase deploy`')
}

View File

@ -1,14 +1,11 @@
import consola from 'consola'
import { extendPreset, hl, prettyPath } from '../utils'
import { NitroPreset, NitroContext } from '../context'
import { extendPreset } from '../utils'
import { NitroPreset } from '../context'
import { node } from './node'
export const server: NitroPreset = extendPreset(node, {
entry: '{{ _internal.runtimeDir }}/entries/server',
serveStatic: true,
hooks: {
'nitro:compiled' ({ output }: NitroContext) {
consola.success('Ready to run', hl('node ' + prettyPath(output.serverDir) + '/index.mjs'))
}
commands: {
preview: 'node {{ output.serverDir }}/index.mjs'
}
})

View File

@ -51,18 +51,22 @@ export async function writeFile (file: string, contents: string, log = false) {
}
}
export function resolvePath (nitroContext: NitroInput, path: string | ((nitroContext: NitroInput) => string), resolveBase: string = ''): string {
if (typeof path === 'function') {
path = path(nitroContext)
export function evalTemplate (ctx, input: string | ((ctx) => string)): string {
if (typeof input === 'function') {
input = input(ctx)
}
if (typeof path !== 'string') {
throw new TypeError('Invalid path: ' + path)
if (typeof input !== 'string') {
throw new TypeError('Invalid template: ' + input)
}
return compileTemplate(input)(ctx)
}
path = compileTemplate(path)(nitroContext)
export function resolvePath (nitroContext: NitroInput, input: string | ((nitroContext: NitroInput) => string), resolveBase: string = ''): string {
return resolve(resolveBase, evalTemplate(nitroContext, input))
}
return resolve(resolveBase, path)
export function replaceAll (input: string, from: string, to: string) {
return input.replace(new RegExp(from, 'g'), to)
}
export function detectTarget () {

View File

@ -5,6 +5,8 @@ const _rDefault = r => r.default || r
export const commands = {
dev: () => import('./dev').then(_rDefault),
build: () => import('./build').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),

View File

@ -0,0 +1,42 @@
import { existsSync, promises as fsp } from 'fs'
import { dirname, relative } from 'path'
import { execa } from 'execa'
import { resolve } from 'pathe'
import consola from 'consola'
import { defineNuxtCommand } from './index'
export default defineNuxtCommand({
meta: {
name: 'preview',
usage: 'npx nuxi preview|start [rootDir]',
description: 'Launches nitro server for local testing after `nuxi build`.'
},
async invoke (args) {
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
const rootDir = resolve(args._[0] || '.')
const nitroJSONPaths = ['.output/nitro.json', 'nitro.json'].map(p => resolve(rootDir, p))
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)
}
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 })
}
})