mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-26 23:52:06 +00:00
feat: static target DX improvements (#7712)
[release] Co-authored-by: Sébastien Chopin <seb@nuxtjs.com> Co-authored-by: pimlie <pimlie@hotmail.com>
This commit is contained in:
parent
f81c588bf6
commit
c5a4465572
@ -19,7 +19,8 @@ module.exports = {
|
||||
// But its performance overhead is pretty bad (30+%).
|
||||
// detectOpenHandles: true
|
||||
|
||||
setupFilesAfterEnv: ['./test/utils/setup'],
|
||||
setupFilesAfterEnv: ['./test/utils/setup-env'],
|
||||
setupFiles: ['./test/utils/setup'],
|
||||
|
||||
coverageDirectory: './coverage',
|
||||
|
||||
|
@ -20,10 +20,13 @@
|
||||
"compression": "^1.7.4",
|
||||
"connect": "^3.7.0",
|
||||
"consola": "^2.14.0",
|
||||
"crc": "^3.8.0",
|
||||
"destr": "^1.0.0",
|
||||
"esm": "^3.2.25",
|
||||
"execa": "^3.4.0",
|
||||
"exit": "^0.1.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"globby": "^11.0.1",
|
||||
"hable": "^3.0.0",
|
||||
"minimist": "^1.2.5",
|
||||
"opener": "1.5.1",
|
||||
|
@ -85,7 +85,7 @@ export default {
|
||||
const builder = await cmd.getBuilder(nuxt)
|
||||
await builder.build()
|
||||
|
||||
const nextCommand = nuxt.options.target === TARGETS.static ? 'nuxt export' : 'nuxt start'
|
||||
const nextCommand = nuxt.options.target === TARGETS.static ? 'nuxt generate' : 'nuxt start'
|
||||
consola.info('Ready to run `' + (nextCommand) + '`')
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,10 @@
|
||||
import path from 'path'
|
||||
import consola from 'consola'
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
import { common, locking } from '../options'
|
||||
import { createLock } from '../utils'
|
||||
import generate from './generate'
|
||||
|
||||
export default {
|
||||
name: 'export',
|
||||
description: 'Export a static generated web application',
|
||||
usage: 'export <dir>',
|
||||
options: {
|
||||
...common,
|
||||
...locking,
|
||||
'fail-on-error': {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Exit with non-zero status code if there are errors when exporting pages'
|
||||
}
|
||||
},
|
||||
async run (cmd) {
|
||||
const config = await cmd.getNuxtConfig({
|
||||
dev: false,
|
||||
target: TARGETS.static,
|
||||
_export: true
|
||||
})
|
||||
const nuxt = await cmd.getNuxt(config)
|
||||
|
||||
if (cmd.argv.lock) {
|
||||
await cmd.setLock(await createLock({
|
||||
id: 'export',
|
||||
dir: nuxt.options.generate.dir,
|
||||
root: config.rootDir
|
||||
}))
|
||||
}
|
||||
|
||||
const generator = await cmd.getGenerator(nuxt)
|
||||
await nuxt.server.listen(0)
|
||||
|
||||
const { errors } = await generator.generate({
|
||||
init: true,
|
||||
build: false
|
||||
})
|
||||
|
||||
await nuxt.close()
|
||||
if (cmd.argv['fail-on-error'] && errors.length > 0) {
|
||||
throw new Error('Error exporting pages, exiting with non-zero code')
|
||||
}
|
||||
consola.info('Ready to run `nuxt serve` or deploy `' + path.basename(nuxt.options.generate.dir) + '/` directory')
|
||||
...generate,
|
||||
run (...args) {
|
||||
consola.warn('`nuxt export` has been deprecated! Please use `nuxt generate`.')
|
||||
return generate.run.call(this, ...args)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
import { common, locking } from '../options'
|
||||
import { normalizeArg, createLock } from '../utils'
|
||||
import { ensureBuild, generate } from '../utils/generate'
|
||||
|
||||
export default {
|
||||
name: 'generate',
|
||||
@ -54,23 +55,22 @@ export default {
|
||||
}
|
||||
},
|
||||
async run (cmd) {
|
||||
const config = await cmd.getNuxtConfig({
|
||||
dev: false,
|
||||
_build: cmd.argv.build,
|
||||
_generate: true
|
||||
})
|
||||
|
||||
if (config.target === TARGETS.static) {
|
||||
throw new Error("Please use `nuxt export` when using `target: 'static'`")
|
||||
}
|
||||
|
||||
// Forcing static target anyway
|
||||
config.target = TARGETS.static
|
||||
const config = await cmd.getNuxtConfig({ dev: false })
|
||||
|
||||
// Disable analyze if set by the nuxt config
|
||||
config.build = config.build || {}
|
||||
config.build.analyze = false
|
||||
|
||||
// Full static
|
||||
if (config.target === TARGETS.static) {
|
||||
await ensureBuild(cmd)
|
||||
await generate(cmd)
|
||||
return
|
||||
}
|
||||
|
||||
// Forcing static target anyway
|
||||
config.target = TARGETS.static
|
||||
|
||||
// Set flag to keep the prerendering behaviour
|
||||
config._legacyGenerate = true
|
||||
|
||||
|
@ -1,84 +1,10 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import { join, extname, basename } from 'path'
|
||||
import connect from 'connect'
|
||||
import serveStatic from 'serve-static'
|
||||
import compression from 'compression'
|
||||
import { getNuxtConfig } from '@nuxt/config'
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
import { common, server } from '../options'
|
||||
import { showBanner } from '../utils/banner'
|
||||
import * as imports from '../imports'
|
||||
import consola from 'consola'
|
||||
import start from './start'
|
||||
|
||||
export default {
|
||||
name: 'serve',
|
||||
description: 'Serve the exported static application (should be compiled with `nuxt build` and `nuxt export` first)',
|
||||
usage: 'serve <dir>',
|
||||
options: {
|
||||
'config-file': common['config-file'],
|
||||
version: common.version,
|
||||
help: common.help,
|
||||
...server
|
||||
},
|
||||
async run (cmd) {
|
||||
let options = await cmd.getNuxtConfig({ dev: false })
|
||||
// add default options
|
||||
options = getNuxtConfig(options)
|
||||
try {
|
||||
// overwrites with build config
|
||||
const buildConfig = require(join(options.buildDir, 'nuxt/config.json'))
|
||||
options.target = buildConfig.target
|
||||
} catch (err) {}
|
||||
|
||||
if (options.target === TARGETS.server) {
|
||||
throw new Error('You cannot use `nuxt serve` with ' + TARGETS.server + ' target, please use `nuxt start`')
|
||||
}
|
||||
const distStat = await fs.stat(options.generate.dir).catch(err => null) // eslint-disable-line handle-callback-err
|
||||
if (!distStat || !distStat.isDirectory()) {
|
||||
throw new Error('Output directory `' + basename(options.generate.dir) + '/` does not exists, please run `nuxt export` before `nuxt serve`.')
|
||||
}
|
||||
const app = connect()
|
||||
app.use(compression({ threshold: 0 }))
|
||||
app.use(
|
||||
options.router.base,
|
||||
serveStatic(options.generate.dir, {
|
||||
extensions: ['html']
|
||||
})
|
||||
)
|
||||
if (options.generate.fallback) {
|
||||
const fallbackFile = await fs.readFile(join(options.generate.dir, options.generate.fallback), 'utf-8')
|
||||
app.use((req, res, next) => {
|
||||
const ext = extname(req.url) || '.html'
|
||||
|
||||
if (ext !== '.html') {
|
||||
return next()
|
||||
}
|
||||
res.writeHeader(200, {
|
||||
'Content-Type': 'text/html'
|
||||
})
|
||||
res.write(fallbackFile)
|
||||
res.end()
|
||||
})
|
||||
}
|
||||
|
||||
const { port, host, socket, https } = options.server
|
||||
const { Listener } = await imports.server()
|
||||
const listener = new Listener({
|
||||
port,
|
||||
host,
|
||||
socket,
|
||||
https,
|
||||
app,
|
||||
dev: true, // try another port if taken
|
||||
baseURL: options.router.base
|
||||
})
|
||||
await listener.listen()
|
||||
const { Nuxt } = await imports.core()
|
||||
showBanner({
|
||||
constructor: Nuxt,
|
||||
options,
|
||||
server: {
|
||||
listeners: [listener]
|
||||
}
|
||||
}, false)
|
||||
...start,
|
||||
run (...args) {
|
||||
consola.warn('`nuxt serve` has been deprecated! Please use `nuxt start`.')
|
||||
return start.run.call(this, ...args)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
import consola from 'consola'
|
||||
import { common, server } from '../options'
|
||||
import { showBanner } from '../utils/banner'
|
||||
import { serve } from '../utils/serve'
|
||||
|
||||
export default {
|
||||
name: 'start',
|
||||
@ -12,9 +14,12 @@ export default {
|
||||
},
|
||||
async run (cmd) {
|
||||
const config = await cmd.getNuxtConfig({ dev: false, _start: true })
|
||||
|
||||
if (config.target === TARGETS.static) {
|
||||
throw new Error('You cannot use `nuxt start` with ' + TARGETS.static + ' target, please use `nuxt export` and `nuxt serve`')
|
||||
consola.info('Serving static dist')
|
||||
return serve(cmd)
|
||||
}
|
||||
|
||||
const nuxt = await cmd.getNuxt(config)
|
||||
|
||||
// Listen and show ready banner
|
||||
|
161
packages/cli/src/utils/generate.js
Normal file
161
packages/cli/src/utils/generate.js
Normal file
@ -0,0 +1,161 @@
|
||||
import fs from 'fs'
|
||||
import path, { relative } from 'path'
|
||||
import crc32 from 'crc/lib/crc32'
|
||||
import consola from 'consola'
|
||||
import globby from 'globby'
|
||||
import destr from 'destr'
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
|
||||
export async function generate (cmd) {
|
||||
const nuxt = await getNuxt({ server: true }, cmd)
|
||||
const generator = await cmd.getGenerator(nuxt)
|
||||
|
||||
await nuxt.server.listen(0)
|
||||
await generator.generate({ build: false })
|
||||
await nuxt.close()
|
||||
}
|
||||
|
||||
export async function ensureBuild (cmd) {
|
||||
const nuxt = await getNuxt({ _build: true, server: false }, cmd)
|
||||
const { options } = nuxt
|
||||
|
||||
if (options.generate.cache === false || process.env.NUXT_BUILD) {
|
||||
const builder = await cmd.getBuilder(nuxt)
|
||||
await builder.build()
|
||||
await nuxt.close()
|
||||
}
|
||||
|
||||
// Default build ignore files
|
||||
const ignore = [
|
||||
options.buildDir,
|
||||
options.dir.static,
|
||||
options.generate.dir,
|
||||
'node_modules',
|
||||
'.**/*',
|
||||
'.*',
|
||||
'README.md'
|
||||
]
|
||||
|
||||
// Extend ignore
|
||||
const { generate } = options
|
||||
if (generate.cache.ignore === 'function') {
|
||||
generate.cache.ignore = generate.cache.ignore(ignore)
|
||||
} else if (Array.isArray(generate.cache.ignore)) {
|
||||
generate.cache.ignore = generate.cache.ignore.concat(ignore)
|
||||
}
|
||||
await nuxt.callHook('generate:cache:ignore', generate.cache.ignore)
|
||||
|
||||
// Take a snapshot of current project
|
||||
const snapshotOptions = {
|
||||
rootDir: nuxt.options.rootDir,
|
||||
ignore: nuxt.options.generate.cache.ignore,
|
||||
globbyOptions: nuxt.options.generate.cache.globbyOptions
|
||||
}
|
||||
|
||||
const currentBuildSnapshot = await snapshot(snapshotOptions)
|
||||
|
||||
// Current build meta
|
||||
const currentBuild = {
|
||||
// @ts-ignore
|
||||
nuxtVersion: nuxt.constructor.version,
|
||||
ssr: nuxt.options.ssr,
|
||||
target: nuxt.options.target,
|
||||
snapshot: currentBuildSnapshot
|
||||
}
|
||||
|
||||
// Check if build can be skipped
|
||||
const nuxtBuildFile = path.resolve(nuxt.options.buildDir, 'build.json')
|
||||
if (fs.existsSync(nuxtBuildFile)) {
|
||||
const previousBuild = destr(fs.readFileSync(nuxtBuildFile, 'utf-8')) || {}
|
||||
|
||||
// Quick diff
|
||||
const needBuild = false
|
||||
for (const field of ['nuxtVersion', 'ssr', 'target']) {
|
||||
if (previousBuild[field] !== currentBuild[field]) {
|
||||
consola.info(`Doing webpack rebuild because ${field} changed`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Full snapshot diff
|
||||
if (!needBuild) {
|
||||
const changed = compareSnapshots(previousBuild.snapshot, currentBuild.snapshot)
|
||||
if (!changed) {
|
||||
consola.success('Skipping webpack build as no changes detected')
|
||||
return
|
||||
} else {
|
||||
consola.info(`Doing webpack rebuild because ${changed} modified`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Webpack build
|
||||
const builder = await cmd.getBuilder(nuxt)
|
||||
await builder.build()
|
||||
|
||||
// Write build.json
|
||||
fs.writeFileSync(nuxtBuildFile, JSON.stringify(currentBuild, null, 2), 'utf-8')
|
||||
|
||||
await nuxt.close()
|
||||
}
|
||||
|
||||
async function getNuxt (args, cmd) {
|
||||
const config = await cmd.getNuxtConfig({ dev: false, ...args })
|
||||
|
||||
if (config.target === TARGETS.static) {
|
||||
config._export = true
|
||||
} else {
|
||||
config._legacyGenerate = true
|
||||
}
|
||||
config.buildDir = (config.static && config.static.cacheDir) || path.resolve(config.rootDir, 'node_modules/.cache/nuxt')
|
||||
config.build = config.build || {}
|
||||
config.build.transpile = config.build.transpile || []
|
||||
config.build.transpile.push(config.buildDir)
|
||||
|
||||
const nuxt = await cmd.getNuxt(config)
|
||||
|
||||
return nuxt
|
||||
}
|
||||
|
||||
export function compareSnapshots (from, to) {
|
||||
const allKeys = Array.from(new Set([
|
||||
...Object.keys(from).sort(),
|
||||
...Object.keys(to).sort()
|
||||
]))
|
||||
|
||||
for (const key of allKeys) {
|
||||
if (JSON.stringify(from[key]) !== JSON.stringify(to[key])) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export async function snapshot ({ globbyOptions, ignore, rootDir }) {
|
||||
const snapshot = {}
|
||||
|
||||
const files = await globby('**/*.*', {
|
||||
...globbyOptions,
|
||||
ignore,
|
||||
cwd: rootDir,
|
||||
absolute: true
|
||||
})
|
||||
|
||||
await Promise.all(files.map(async (p) => {
|
||||
const key = relative(rootDir, p)
|
||||
try {
|
||||
const fileContent = await fs.readFile(p)
|
||||
snapshot[key] = {
|
||||
checksum: await crc32(fileContent).toString(16)
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO: Check for other errors like permission denied
|
||||
snapshot[key] = {
|
||||
exists: false
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
return snapshot
|
||||
}
|
73
packages/cli/src/utils/serve.js
Normal file
73
packages/cli/src/utils/serve.js
Normal file
@ -0,0 +1,73 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import { join, extname, basename } from 'path'
|
||||
import connect from 'connect'
|
||||
import serveStatic from 'serve-static'
|
||||
import compression from 'compression'
|
||||
import { getNuxtConfig } from '@nuxt/config'
|
||||
import { showBanner } from '../utils/banner'
|
||||
import * as imports from '../imports'
|
||||
|
||||
export async function serve (cmd) {
|
||||
const _config = await cmd.getNuxtConfig({ dev: false })
|
||||
|
||||
// add default options
|
||||
const options = getNuxtConfig(_config)
|
||||
|
||||
try {
|
||||
// overwrites with build config
|
||||
const buildConfig = require(join(options.buildDir, 'nuxt/config.json'))
|
||||
options.target = buildConfig.target
|
||||
} catch (err) { }
|
||||
|
||||
const distStat = await fs.stat(options.generate.dir).catch(err => null) // eslint-disable-line handle-callback-err
|
||||
if (!distStat || !distStat.isDirectory()) {
|
||||
throw new Error('Output directory `' + basename(options.generate.dir) + '/` does not exists, please use `nuxt generate` before `nuxt start` for static target.')
|
||||
}
|
||||
const app = connect()
|
||||
app.use(compression({ threshold: 0 }))
|
||||
app.use(
|
||||
options.router.base,
|
||||
serveStatic(options.generate.dir, {
|
||||
extensions: ['html']
|
||||
})
|
||||
)
|
||||
if (options.generate.fallback) {
|
||||
const fallbackFile = await fs.readFile(join(options.generate.dir, options.generate.fallback), 'utf-8')
|
||||
app.use((req, res, next) => {
|
||||
const ext = extname(req.url) || '.html'
|
||||
|
||||
if (ext !== '.html') {
|
||||
return next()
|
||||
}
|
||||
res.writeHeader(200, {
|
||||
'Content-Type': 'text/html'
|
||||
})
|
||||
res.write(fallbackFile)
|
||||
res.end()
|
||||
})
|
||||
}
|
||||
|
||||
const { port, host, socket, https } = options.server
|
||||
const { Listener } = await imports.server()
|
||||
const listener = new Listener({
|
||||
port,
|
||||
host,
|
||||
socket,
|
||||
https,
|
||||
app,
|
||||
dev: true, // try another port if taken
|
||||
baseURL: options.router.base
|
||||
})
|
||||
|
||||
await listener.listen()
|
||||
|
||||
const { Nuxt } = await imports.core()
|
||||
|
||||
showBanner({
|
||||
constructor: Nuxt,
|
||||
options,
|
||||
server: {
|
||||
listeners: [listener]
|
||||
}
|
||||
}, false)
|
||||
}
|
@ -29,29 +29,29 @@ describe('export', () => {
|
||||
|
||||
expect(generator).toHaveBeenCalled()
|
||||
expect(generator.mock.calls[0][0].init).toBe(true)
|
||||
expect(generator.mock.calls[0][0].build).toBe(false)
|
||||
// expect(generator.mock.calls[0][0].build).toBe(false)
|
||||
})
|
||||
|
||||
test('force-exits by default', async () => {
|
||||
mockGetNuxt({ generate: { dir: 'dist' } })
|
||||
mockGetGenerator()
|
||||
|
||||
const cmd = NuxtCommand.from(exportCommand, ['export', '.'])
|
||||
const cmd = NuxtCommand.from(exportCommand, ['generate', '.'])
|
||||
await cmd.run()
|
||||
|
||||
expect(utils.forceExit).toHaveBeenCalledTimes(1)
|
||||
expect(utils.forceExit).toHaveBeenCalledWith('export', 5)
|
||||
expect(utils.forceExit).toHaveBeenCalledWith('generate', 5)
|
||||
})
|
||||
|
||||
test('can set force exit explicitly', async () => {
|
||||
mockGetNuxt({ generate: { dir: 'dist' } })
|
||||
mockGetGenerator()
|
||||
|
||||
const cmd = NuxtCommand.from(exportCommand, ['export', '.', '--force-exit'])
|
||||
const cmd = NuxtCommand.from(exportCommand, ['generate', '.', '--force-exit'])
|
||||
await cmd.run()
|
||||
|
||||
expect(utils.forceExit).toHaveBeenCalledTimes(1)
|
||||
expect(utils.forceExit).toHaveBeenCalledWith('export', false)
|
||||
expect(utils.forceExit).toHaveBeenCalledWith('generate', false)
|
||||
})
|
||||
|
||||
test('can disable force exit explicitly', async () => {
|
||||
@ -97,7 +97,7 @@ describe('export', () => {
|
||||
mockGetGenerator(() => ({ errors: [{ type: 'dummy' }] }))
|
||||
|
||||
const cmd = NuxtCommand.from(exportCommand, ['export', '.', '--fail-on-error'])
|
||||
await expect(cmd.run()).rejects.toThrow('Error exporting pages, exiting with non-zero code')
|
||||
await expect(cmd.run()).rejects.toThrow('Error generating pages, exiting with non-zero code')
|
||||
})
|
||||
|
||||
test('do not throw an error when fail-on-error disabled and page errors', async () => {
|
||||
|
@ -19,16 +19,10 @@ describe('serve', () => {
|
||||
expect(typeof serve.run).toBe('function')
|
||||
})
|
||||
|
||||
test('error if starts with server target', () => {
|
||||
mockGetNuxtConfig({ target: TARGETS.server })
|
||||
const cmd = NuxtCommand.from(serve)
|
||||
expect(cmd.run()).rejects.toThrow(new Error('You cannot use `nuxt serve` with server target, please use `nuxt start`'))
|
||||
})
|
||||
|
||||
test('error if dist/ does not exists', () => {
|
||||
mockGetNuxtConfig({ target: TARGETS.static })
|
||||
const cmd = NuxtCommand.from(serve)
|
||||
expect(cmd.run()).rejects.toThrow(new Error('Output directory `dist/` does not exists, please run `nuxt export` before `nuxt serve`.'))
|
||||
expect(cmd.run()).rejects.toThrow(new Error('Output directory `dist/` does not exists, please use `nuxt generate` before `nuxt start` for static target.'))
|
||||
})
|
||||
|
||||
test('no error if dist/ dir exists', async () => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import fs from 'fs-extra'
|
||||
import { TARGETS } from '@nuxt/utils'
|
||||
import * as utils from '../../src/utils/'
|
||||
import { consola, mockGetNuxtStart, mockGetNuxtConfig, NuxtCommand } from '../utils'
|
||||
|
||||
@ -36,13 +35,6 @@ describe('start', () => {
|
||||
expect(consola.fatal).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('error if starts with static target', () => {
|
||||
mockGetNuxtStart()
|
||||
mockGetNuxtConfig({ target: TARGETS.static })
|
||||
const cmd = NuxtCommand.from(start)
|
||||
expect(cmd.run()).rejects.toThrow(new Error('You cannot use `nuxt start` with static target, please use `nuxt export` and `nuxt serve`'))
|
||||
})
|
||||
|
||||
test('start doesnt force-exit by default', async () => {
|
||||
mockGetNuxtStart()
|
||||
mockGetNuxtConfig()
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export default () => ({
|
||||
dir: 'dist',
|
||||
routes: [],
|
||||
@ -8,6 +7,12 @@ export default () => ({
|
||||
subFolders: true,
|
||||
fallback: '200.html',
|
||||
crawler: true,
|
||||
cache: {
|
||||
ignore: [],
|
||||
globbyOptions: {
|
||||
gitignore: true
|
||||
}
|
||||
},
|
||||
staticAssets: {
|
||||
base: undefined, // Default: "/_nuxt/static:
|
||||
versionBase: undefined, // Default: "_nuxt/static/{version}""
|
||||
|
@ -115,9 +115,9 @@ export function getNuxtConfig (_options) {
|
||||
options.router.base += '/'
|
||||
}
|
||||
|
||||
// Alias export to generate
|
||||
// TODO: switch to export by default for nuxt3
|
||||
// Legacy support for export
|
||||
if (options.export) {
|
||||
consola.warn('export option is deprecated and will be removed in a future version! Please switch to generate')
|
||||
options.generate = defu(options.export, options.generate)
|
||||
}
|
||||
exports.export = options.generate
|
||||
|
@ -196,6 +196,12 @@ Object {
|
||||
"server": true,
|
||||
},
|
||||
"generate": Object {
|
||||
"cache": Object {
|
||||
"globbyOptions": Object {
|
||||
"gitignore": true,
|
||||
},
|
||||
"ignore": Array [],
|
||||
},
|
||||
"concurrency": 500,
|
||||
"crawler": true,
|
||||
"dir": "/var/nuxt/test/dist",
|
||||
|
@ -175,6 +175,12 @@ Object {
|
||||
"server": true,
|
||||
},
|
||||
"generate": Object {
|
||||
"cache": Object {
|
||||
"globbyOptions": Object {
|
||||
"gitignore": true,
|
||||
},
|
||||
"ignore": Array [],
|
||||
},
|
||||
"concurrency": 500,
|
||||
"crawler": true,
|
||||
"dir": "dist",
|
||||
@ -548,6 +554,12 @@ Object {
|
||||
"server": true,
|
||||
},
|
||||
"generate": Object {
|
||||
"cache": Object {
|
||||
"globbyOptions": Object {
|
||||
"gitignore": true,
|
||||
},
|
||||
"ignore": Array [],
|
||||
},
|
||||
"concurrency": 500,
|
||||
"crawler": true,
|
||||
"dir": "dist",
|
||||
|
@ -35,7 +35,17 @@ export default class Nuxt extends Hookable {
|
||||
to: '_render:context',
|
||||
message: '`render:routeContext(nuxt)` is deprecated, Please use `vue-renderer:ssr:context(context)`'
|
||||
},
|
||||
showReady: 'webpack:done'
|
||||
showReady: 'webpack:done',
|
||||
// Introduced in 2.13
|
||||
'export:done': 'generate:done',
|
||||
'export:before': 'generate:before',
|
||||
'export:extendRoutes': 'generate:extendRoutes',
|
||||
'export:distRemoved': 'generate:distRemoved',
|
||||
'export:distCopied': 'generate:distCopied',
|
||||
'export:route': 'generate:route',
|
||||
'export:routeFailed': 'generate:routeFailed',
|
||||
'export:page': 'generate:page',
|
||||
'export:routeCreated': 'generate:routeCreated'
|
||||
})
|
||||
|
||||
// Add Legacy aliases
|
||||
|
@ -6,16 +6,16 @@ import defu from 'defu'
|
||||
import htmlMinifier from 'html-minifier'
|
||||
import { parse } from 'node-html-parser'
|
||||
|
||||
import { isFullStatic, flatRoutes, isString, isUrl, promisifyRoute, waitFor, TARGETS } from '@nuxt/utils'
|
||||
import { isFullStatic, flatRoutes, isString, isUrl, promisifyRoute, waitFor } from '@nuxt/utils'
|
||||
|
||||
export default class Generator {
|
||||
constructor (nuxt, builder) {
|
||||
this.nuxt = nuxt
|
||||
this.options = nuxt.options
|
||||
this.builder = builder
|
||||
this.isFullStatic = false
|
||||
|
||||
// Set variables
|
||||
this.isFullStatic = isFullStatic(this.options)
|
||||
this.staticRoutes = path.resolve(this.options.srcDir, this.options.dir.static)
|
||||
this.srcBuiltPath = path.resolve(this.options.buildDir, 'dist', 'client')
|
||||
this.distPath = this.options.generate.dir
|
||||
@ -23,6 +23,12 @@ export default class Generator {
|
||||
this.distPath,
|
||||
isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath
|
||||
)
|
||||
// Payloads for full static
|
||||
if (this.isFullStatic) {
|
||||
const { staticAssets } = this.options.generate
|
||||
this.staticAssetsDir = path.resolve(this.distNuxtPath, staticAssets.dir, staticAssets.version)
|
||||
this.staticAssetsBase = this.options.generate.staticAssets.versionBase
|
||||
}
|
||||
|
||||
// Shared payload
|
||||
this._payload = null
|
||||
@ -35,18 +41,10 @@ export default class Generator {
|
||||
consola.debug('Initializing generator...')
|
||||
await this.initiate({ build, init })
|
||||
|
||||
// Payloads for full static
|
||||
if (this.isFullStatic) {
|
||||
consola.info('Full static mode activated')
|
||||
const { staticAssets } = this.options.generate
|
||||
this.staticAssetsDir = path.resolve(this.distNuxtPath, staticAssets.dir, staticAssets.version)
|
||||
this.staticAssetsBase = this.options.generate.staticAssets.versionBase
|
||||
}
|
||||
|
||||
consola.debug('Preparing routes for generate...')
|
||||
const routes = await this.initRoutes()
|
||||
|
||||
consola.info('Generating pages')
|
||||
consola.info('Generating pages' + (this.isFullStatic ? ' with full static mode' : ''))
|
||||
const errors = await this.generateRoutes(routes)
|
||||
|
||||
await this.afterGenerate()
|
||||
@ -72,23 +70,13 @@ export default class Generator {
|
||||
|
||||
// Start build process
|
||||
await this.builder.build()
|
||||
this.isFullStatic = isFullStatic(this.options)
|
||||
} else {
|
||||
const hasBuilt = await fsExtra.exists(path.resolve(this.options.buildDir, 'dist', 'server', 'client.manifest.json'))
|
||||
if (!hasBuilt) {
|
||||
const fullStaticArgs = isFullStatic(this.options) ? ' --target static' : ''
|
||||
throw new Error(
|
||||
`No build files found in ${this.srcBuiltPath}.\nPlease run \`nuxt build${fullStaticArgs}\` before calling \`nuxt export\``
|
||||
`No build files found in ${this.srcBuiltPath}.\nPlease run \`nuxt build\``
|
||||
)
|
||||
}
|
||||
const config = this.getBuildConfig()
|
||||
if (!config || (config.target !== TARGETS.static && !this.options._legacyGenerate)) {
|
||||
throw new Error(
|
||||
`In order to use \`nuxt export\`, you need to run \`nuxt build --target static\``
|
||||
)
|
||||
}
|
||||
this.isFullStatic = config.isFullStatic
|
||||
this.options.render.ssr = config.ssr
|
||||
}
|
||||
|
||||
// Initialize dist directory
|
||||
@ -366,9 +354,15 @@ export default class Generator {
|
||||
}
|
||||
|
||||
// Call hook to let user update the path & html
|
||||
const page = { route, path: fileName, html, exclude: false }
|
||||
const page = {
|
||||
route,
|
||||
path: fileName,
|
||||
html,
|
||||
exclude: false,
|
||||
errors: pageErrors
|
||||
}
|
||||
page.page = page // Backward compatibility for export:page hook
|
||||
await this.nuxt.callHook('generate:page', page)
|
||||
await this.nuxt.callHook('export:page', { page, errors: pageErrors })
|
||||
|
||||
if (page.exclude) {
|
||||
return false
|
||||
|
6
packages/types/config/generate.d.ts
vendored
6
packages/types/config/generate.d.ts
vendored
@ -1,3 +1,5 @@
|
||||
import { GlobbyOptions } from 'globby'
|
||||
|
||||
/**
|
||||
* NuxtOptionsGenerate
|
||||
* Documentation: https://nuxtjs.org/api/configuration-generate
|
||||
@ -18,4 +20,8 @@ export interface NuxtOptionsGenerate {
|
||||
interval?: number
|
||||
routes?: NuxtOptionsGenerateRoute[] | NuxtOptionsGenerateRoutesFunction | NuxtOptionsGenerateRoutesFunctionWithCallback
|
||||
subFolders?: boolean
|
||||
cache?: false | {
|
||||
ignore?: string[] | function,
|
||||
globbyOptions?: GlobbyOptions
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ export const template = {
|
||||
dependencies,
|
||||
dir: path.join(__dirname, '..', 'template'),
|
||||
files: [
|
||||
'nuxt/config.json',
|
||||
'App.js',
|
||||
'client.js',
|
||||
'index.js',
|
||||
|
@ -1,5 +0,0 @@
|
||||
<%= JSON.stringify({
|
||||
isFullStatic: isFullStatic,
|
||||
ssr: nuxtOptions.render.ssr,
|
||||
target: nuxtOptions.target
|
||||
}, null, 2) %>
|
23
test/utils/setup-env.js
Normal file
23
test/utils/setup-env.js
Normal file
@ -0,0 +1,23 @@
|
||||
import consola from 'consola'
|
||||
import env from 'std-env'
|
||||
import exit from 'exit'
|
||||
|
||||
const isWin = env.windows
|
||||
|
||||
describe.win = isWin ? describe : describe.skip
|
||||
test.win = isWin ? test : test.skip
|
||||
|
||||
describe.posix = !isWin ? describe : describe.skip
|
||||
test.posix = !isWin ? test : test.skip
|
||||
|
||||
jest.setTimeout(60000)
|
||||
|
||||
consola.mockTypes(() => jest.fn())
|
||||
|
||||
function errorTrap (error) {
|
||||
process.stderr.write('\n' + error.stack + '\n')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', errorTrap)
|
||||
process.on('uncaughtException', errorTrap)
|
@ -1,26 +1,6 @@
|
||||
import consola from 'consola'
|
||||
import chalk from 'chalk'
|
||||
import env from 'std-env'
|
||||
import exit from 'exit'
|
||||
|
||||
const isWin = env.windows
|
||||
|
||||
describe.win = isWin ? describe : describe.skip
|
||||
test.win = isWin ? test : test.skip
|
||||
|
||||
describe.posix = !isWin ? describe : describe.skip
|
||||
test.posix = !isWin ? test : test.skip
|
||||
process.env.FORCE_COLOR = 0
|
||||
|
||||
const chalk = require('chalk')
|
||||
chalk.level = 0
|
||||
|
||||
jest.setTimeout(60000)
|
||||
|
||||
consola.mockTypes(() => jest.fn())
|
||||
|
||||
function errorTrap (error) {
|
||||
process.stderr.write('\n' + error.stack + '\n')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', errorTrap)
|
||||
process.on('uncaughtException', errorTrap)
|
||||
chalk.supportsColor = false
|
||||
process.env.FORCE_COLOR = 0
|
||||
|
Loading…
Reference in New Issue
Block a user