From c5a446557297b735103f322b83df74efc189734d Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Thu, 16 Jul 2020 17:10:54 +0200 Subject: [PATCH] feat: static target DX improvements (#7712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [release] Co-authored-by: Sébastien Chopin Co-authored-by: pimlie --- jest.config.js | 3 +- packages/cli/package.json | 3 + packages/cli/src/commands/build.js | 2 +- packages/cli/src/commands/export.js | 50 +----- packages/cli/src/commands/generate.js | 24 +-- packages/cli/src/commands/serve.js | 86 +--------- packages/cli/src/commands/start.js | 7 +- packages/cli/src/utils/generate.js | 161 ++++++++++++++++++ packages/cli/src/utils/serve.js | 73 ++++++++ packages/cli/test/unit/export.test.js | 12 +- packages/cli/test/unit/serve.test.js | 8 +- packages/cli/test/unit/start.test.js | 8 - packages/config/src/config/generate.js | 7 +- packages/config/src/options.js | 4 +- .../test/__snapshots__/options.test.js.snap | 6 + .../config/__snapshots__/index.test.js.snap | 12 ++ packages/core/src/nuxt.js | 12 +- packages/generator/src/generator.js | 42 ++--- packages/types/config/generate.d.ts | 6 + packages/vue-app/src/index.js | 1 - packages/vue-app/template/nuxt/config.json | 5 - test/utils/setup-env.js | 23 +++ test/utils/setup.js | 28 +-- 23 files changed, 364 insertions(+), 219 deletions(-) create mode 100644 packages/cli/src/utils/generate.js create mode 100644 packages/cli/src/utils/serve.js delete mode 100644 packages/vue-app/template/nuxt/config.json create mode 100644 test/utils/setup-env.js diff --git a/jest.config.js b/jest.config.js index 68e437d910..abda43df22 100644 --- a/jest.config.js +++ b/jest.config.js @@ -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', diff --git a/packages/cli/package.json b/packages/cli/package.json index 608ee65593..1b6c34375c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/packages/cli/src/commands/build.js b/packages/cli/src/commands/build.js index 060c028a25..5224654c7c 100644 --- a/packages/cli/src/commands/build.js +++ b/packages/cli/src/commands/build.js @@ -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) + '`') } } diff --git a/packages/cli/src/commands/export.js b/packages/cli/src/commands/export.js index a77ebb6046..c1fd3656d2 100644 --- a/packages/cli/src/commands/export.js +++ b/packages/cli/src/commands/export.js @@ -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 ', - 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) } } diff --git a/packages/cli/src/commands/generate.js b/packages/cli/src/commands/generate.js index b77a8d09e7..68f248c1d0 100644 --- a/packages/cli/src/commands/generate.js +++ b/packages/cli/src/commands/generate.js @@ -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 diff --git a/packages/cli/src/commands/serve.js b/packages/cli/src/commands/serve.js index 6867898aea..864cfd54c7 100644 --- a/packages/cli/src/commands/serve.js +++ b/packages/cli/src/commands/serve.js @@ -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 ', - 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) } } diff --git a/packages/cli/src/commands/start.js b/packages/cli/src/commands/start.js index e462b6207b..a120a405b1 100644 --- a/packages/cli/src/commands/start.js +++ b/packages/cli/src/commands/start.js @@ -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 diff --git a/packages/cli/src/utils/generate.js b/packages/cli/src/utils/generate.js new file mode 100644 index 0000000000..830cee85da --- /dev/null +++ b/packages/cli/src/utils/generate.js @@ -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 +} diff --git a/packages/cli/src/utils/serve.js b/packages/cli/src/utils/serve.js new file mode 100644 index 0000000000..c272d7f4a4 --- /dev/null +++ b/packages/cli/src/utils/serve.js @@ -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) +} diff --git a/packages/cli/test/unit/export.test.js b/packages/cli/test/unit/export.test.js index 5651237b9f..c75b116c7c 100644 --- a/packages/cli/test/unit/export.test.js +++ b/packages/cli/test/unit/export.test.js @@ -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 () => { diff --git a/packages/cli/test/unit/serve.test.js b/packages/cli/test/unit/serve.test.js index 21f88f6823..c229113160 100644 --- a/packages/cli/test/unit/serve.test.js +++ b/packages/cli/test/unit/serve.test.js @@ -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 () => { diff --git a/packages/cli/test/unit/start.test.js b/packages/cli/test/unit/start.test.js index f3781d921e..ce09f3b58b 100644 --- a/packages/cli/test/unit/start.test.js +++ b/packages/cli/test/unit/start.test.js @@ -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() diff --git a/packages/config/src/config/generate.js b/packages/config/src/config/generate.js index 5ac7fe11ce..8177238e3d 100644 --- a/packages/config/src/config/generate.js +++ b/packages/config/src/config/generate.js @@ -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}"" diff --git a/packages/config/src/options.js b/packages/config/src/options.js index 0697e16138..16953da53c 100644 --- a/packages/config/src/options.js +++ b/packages/config/src/options.js @@ -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 diff --git a/packages/config/test/__snapshots__/options.test.js.snap b/packages/config/test/__snapshots__/options.test.js.snap index 1df80864a6..5872aa6c7a 100644 --- a/packages/config/test/__snapshots__/options.test.js.snap +++ b/packages/config/test/__snapshots__/options.test.js.snap @@ -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", diff --git a/packages/config/test/config/__snapshots__/index.test.js.snap b/packages/config/test/config/__snapshots__/index.test.js.snap index 4b231057bb..8c12ed613a 100644 --- a/packages/config/test/config/__snapshots__/index.test.js.snap +++ b/packages/config/test/config/__snapshots__/index.test.js.snap @@ -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", diff --git a/packages/core/src/nuxt.js b/packages/core/src/nuxt.js index 1d7c791d4e..6e55a6d998 100644 --- a/packages/core/src/nuxt.js +++ b/packages/core/src/nuxt.js @@ -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 diff --git a/packages/generator/src/generator.js b/packages/generator/src/generator.js index 52f96e6942..194f54f375 100644 --- a/packages/generator/src/generator.js +++ b/packages/generator/src/generator.js @@ -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 diff --git a/packages/types/config/generate.d.ts b/packages/types/config/generate.d.ts index f0fdfebb89..632165eb77 100644 --- a/packages/types/config/generate.d.ts +++ b/packages/types/config/generate.d.ts @@ -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 + } } diff --git a/packages/vue-app/src/index.js b/packages/vue-app/src/index.js index 5cf5d1d85e..11e0d65a7b 100644 --- a/packages/vue-app/src/index.js +++ b/packages/vue-app/src/index.js @@ -5,7 +5,6 @@ export const template = { dependencies, dir: path.join(__dirname, '..', 'template'), files: [ - 'nuxt/config.json', 'App.js', 'client.js', 'index.js', diff --git a/packages/vue-app/template/nuxt/config.json b/packages/vue-app/template/nuxt/config.json deleted file mode 100644 index 4d88401d15..0000000000 --- a/packages/vue-app/template/nuxt/config.json +++ /dev/null @@ -1,5 +0,0 @@ -<%= JSON.stringify({ - isFullStatic: isFullStatic, - ssr: nuxtOptions.render.ssr, - target: nuxtOptions.target -}, null, 2) %> diff --git a/test/utils/setup-env.js b/test/utils/setup-env.js new file mode 100644 index 0000000000..95dc33e23b --- /dev/null +++ b/test/utils/setup-env.js @@ -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) diff --git a/test/utils/setup.js b/test/utils/setup.js index 04ffa50337..72ba9147c4 100644 --- a/test/utils/setup.js +++ b/test/utils/setup.js @@ -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