Skip to content

Commit

Permalink
feat: options.target and full-static export (#6159)
Browse files Browse the repository at this point in the history
* feat: add options.target

* fix(lint): lint

* fix(test): update snapshots

* fix(builder): default value for target

* fix(test): fix test

* fix(test): test fixing

* fix: use this.options.target

* fix: final test

* Update packages/vue-renderer/src/renderer.js

Co-Authored-By: Alexander Lichter <manniL@gmx.net>

* feat: Add target option and update banner

* fix(lint): fix

* feat: Add warning when using serverMiddleware in static target

* chore(utils): add TARGETS and MODES as constants

* hotfix: lint

* chore(module): add filename as alias of fileName

* feat: introducing nuxt export and router/routes.json

* hotfix: Fix the linting lord

* chore(core): add comment for filename vs fileName

* fix: use targets constant

* chore: remove warning

* fix: unit testing

* wip: refactor and use TARGETS

* fix: lint

* feat: add target as alias for first arg value

* fix: generate only for SPA

* chore: explain to use nuxt static X

* fix: render SPA fallback on redirect for static target

* fix: lint issue

* fix: only target is useful for now

* wip

* wip: nuxt static export is looking good

* Update packages/generator/src/generator.js

Co-Authored-By: Devon Rueckner <indirectlylit@users.noreply.github.com>

* Update packages/cli/src/options/common.js

Co-Authored-By: Alexander Lichter <manniL@gmx.net>

* feat: add options.target

* fix(lint): lint

* fix(test): update snapshots

* fix(builder): default value for target

* fix(test): fix test

* fix(test): test fixing

* fix: use this.options.target

* fix: final test

* Update packages/vue-renderer/src/renderer.js

Co-Authored-By: Alexander Lichter <manniL@gmx.net>

* feat: Add target option and update banner

* fix(lint): fix

* feat: Add warning when using serverMiddleware in static target

* chore(utils): add TARGETS and MODES as constants

* hotfix: lint

* chore(module): add filename as alias of fileName

* feat: introducing nuxt export and router/routes.json

* hotfix: Fix the linting lord

* chore(core): add comment for filename vs fileName

* fix: use targets constant

* chore: remove warning

* fix: unit testing

* wip: refactor and use TARGETS

* fix: lint

* feat: add target as alias for first arg value

* chore: explain to use nuxt static X

* fix: render SPA fallback on redirect for static target

* fix: lint issue

* fix: only target is useful for now

* wip

* wip: nuxt static export is looking good

* Update packages/generator/src/generator.js

Co-Authored-By: Devon Rueckner <indirectlylit@users.noreply.github.com>

* Update packages/cli/src/options/common.js

Co-Authored-By: Alexander Lichter <manniL@gmx.net>

* fix: duplicate imports

* chore: don't server render if an error happens on static target

* test: update unit and add export

* lint: fix

* lint: fix

* fix: e2e test

* fix: fallback only for static target

* fix: dev test

* feat: add generate.crawler

* fix: full static is when generate.static is given

* chore: improvements

* fix: Add isFullStatic in nuxt/config.json

* feat: handle fetch for full static

* feat: router.prefetchPayloads for full static

* chore: use fetch in async-data example

* fix: add target only if given

* fix: use created to have access to props in fetchOnServer

* chore: add console.error in dev for easy debugging

* feat: payload smart pre-fetching

* fix: remove alias for target

* fix: increment payloadFetchIndex is static set to false

* chore: lint

* chore: add serve command

* chore: rename universal to server-side

* fix: handle payloadPath on SPA fallback

* fix: lint

* chore lint again

* feat: handle spa fallback

* feat: support string for exclude

* fix: fallback only if no extension or html

* chore: use JSON.stringify() for static target

* chore: lint again, dammit

* chore: fix tests and remove too early return

* fix: early return only for server target

* fix: update tests

* fix: unit tests

* chore: add ssr option

* chore: add logic for ssr option

* fix: #6682

* chore(dx): add next command to run

* fix: lint

* fix: tests

* chore: keep old behaviour for nuxt build in spa

* fix: test again, oh boy

* fix: alright this is good now

* chore: add comment for spa fallback

* chore: move routes.json to dot nuxt dir

* chore: simplify check for promise

* chore: unique lock id

* chore: refactor isFullStatic

* fix: dont set default in build context

* chore: add test for serve

* chore: update tests

* hotfix: lint tests

* chore(dx): improve message for bundling

* feat: js payload extraction with jsonp

* fix: keep serialized session script for legacy generate

* fix: call to setPagePayload from fetchPayload

* use devalue for payload chunks

* feat: add initial load state chunk

* feat: preload payload and state scripts

* fix(vue-app): don't re-render the app if trailing slash on SSG

* hotfix: remove console.log

* chore(dx): add deploy infos for nuxt export

Co-authored-by: Pooya Parsa <pyapar@gmail.com>

* chore: handle fetching payload.js for nuxt state

* chore(dx): error when using nuxt generate and static

* chore: remove static option for clarity

* chore: remove serverless target

* hotfix: lint

* hotfix: unit tests

* chore: update legacy js resource

* chore: remove query params from url in static target

* fix: use globalName and urlJoin

* chore: typo

* feat: previewMode 👀

* chore: rename to enablePreview

* fix: wait next tick to avoid error on spa

* chore: try 1 sec

* hotfix: test only for linux, wtf azure

* refactor: static assets

- generalize logic for modules need emit export static assets
- allow customization for version, dir and base
- serialization logic is only in ssr now

* feat: smart state chunk creates

* fix(client): ignore payload load error

* perf: avoide payload loading for spa initial

* perf: avoid loading failed chunks again

* chore(cli): add simple compression for nuxt serve

* test: update snapshots

* fix version snapshot

* fix(generator): set staticAssetsBase on context only for full static

* fix tests

* fix: honor shouldHashCspScriptSrc

* chore(dx): add log for client-side fallback creation

Co-authored-by: Xin Du (Clark) <clark.duxin@gmail.com>
Co-authored-by: Alexander Lichter <manniL@gmx.net>
Co-authored-by: Pooya Parsa <pooya@pi0.ir>
Co-authored-by: Devon Rueckner <indirectlylit@users.noreply.github.com>
Co-authored-by: Pooya Parsa <pyapar@gmail.com>
  • Loading branch information
6 people committed May 7, 2020
1 parent a0db364 commit 917adc0
Show file tree
Hide file tree
Showing 72 changed files with 1,105 additions and 186 deletions.
7 changes: 3 additions & 4 deletions examples/async-data/pages/posts/_id.vue
Expand Up @@ -12,13 +12,12 @@
</template>

<script>
import axios from 'axios'
export default {
async asyncData ({ params }) {
// We can use async/await ES6 feature
const { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${params.id}`)
return { post: data }
const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`).then(res => res.json())
return { post }
},
head () {
return {
Expand Down
12 changes: 7 additions & 5 deletions examples/async-data/pages/posts/index.vue
Expand Up @@ -7,6 +7,9 @@
<NuxtLink :to="{ name: 'posts-id', params: { id: post.id } }">
{{ post.title }}
</NuxtLink>
<NuxtLink :to="{ name: 'posts-id', params: { id: post.id } }">

This comment has been minimized.

Copy link
@Akryum

Akryum May 7, 2020

😕

{{ post.title }}
</NuxtLink>
</li>
</ul>
<p>
Expand All @@ -18,14 +21,13 @@
</template>

<script>
import axios from 'axios'
export default {
asyncData ({ req, params }) {
// We can return a Promise instead of calling the callback
return axios.get('https://jsonplaceholder.typicode.com/posts')
.then((res) => {
return { posts: res.data.slice(0, 5) }
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then((data) => {
return { posts: data.slice(0, 5) }
})
},
head: {
Expand Down
14 changes: 12 additions & 2 deletions packages/builder/src/builder.js
@@ -1,4 +1,5 @@
import path from 'path'
import chalk from 'chalk'
import chokidar from 'chokidar'
import consola from 'consola'
import fsExtra from 'fs-extra'
Expand All @@ -7,7 +8,6 @@ import hash from 'hash-sum'
import pify from 'pify'
import upath from 'upath'
import semver from 'semver'
import chalk from 'chalk'

import debounce from 'lodash/debounce'
import omit from 'lodash/omit'
Expand All @@ -23,7 +23,9 @@ import {
determineGlobals,
stripWhitespace,
isIndexFileAndFolder,
scanRequireTree
scanRequireTree,
TARGETS,
isFullStatic
} from '@nuxt/utils'

import Ignore from './ignore'
Expand Down Expand Up @@ -102,6 +104,7 @@ export default class Builder {
}

forGenerate () {
this.options.target = TARGETS.static
this.bundleBuilder.forGenerate()
}

Expand All @@ -122,6 +125,13 @@ export default class Builder {
consola.info('Initial build may take a while')
} else {
consola.info('Production build')
if (this.options.render.ssr) {
consola.info(`Bundling for ${chalk.bold.yellow('server')} and ${chalk.bold.green('client')} side`)
} else {
consola.info(`Bundling only for ${chalk.bold.green('client')} side`)
}
const target = isFullStatic(this.options) ? 'full static' : this.options.target
consola.info(`Target: ${chalk.bold.cyan(target)}`)
}

// Wait for nuxt ready
Expand Down
2 changes: 1 addition & 1 deletion packages/builder/src/context/build.js
Expand Up @@ -3,7 +3,7 @@ export default class BuildContext {
this._builder = builder
this.nuxt = builder.nuxt
this.options = builder.nuxt.options
this.isStatic = false
this.target = builder.nuxt.options.target
}

get buildOptions () {
Expand Down
3 changes: 2 additions & 1 deletion packages/builder/src/context/template.js
Expand Up @@ -4,7 +4,7 @@ import uniqBy from 'lodash/uniqBy'
import serialize from 'serialize-javascript'

import devalue from '@nuxt/devalue'
import { r, wp, wChunk, serializeFunction } from '@nuxt/utils'
import { r, wp, wChunk, serializeFunction, isFullStatic } from '@nuxt/utils'

export default class TemplateContext {
constructor (builder, options) {
Expand All @@ -20,6 +20,7 @@ export default class TemplateContext {
uniqBy,
isDev: options.dev,
isTest: options.test,
isFullStatic: isFullStatic(options),
debug: options.debug,
buildIndicator: options.dev && options.build.indicator,
vue: { config: options.vue.config },
Expand Down
3 changes: 3 additions & 0 deletions packages/builder/test/__utils__/index.js
Expand Up @@ -5,6 +5,9 @@ export const createNuxt = () => ({
build: {
watch: []
},
render: {
ssr: true
},
router: {},
dir: {
app: 'app'
Expand Down
4 changes: 3 additions & 1 deletion packages/builder/test/builder.build.test.js
Expand Up @@ -35,6 +35,7 @@ describe('builder: builder build', () => {
nuxt.options.dir = { pages: '/var/nuxt/src/pages' }
nuxt.options.build.template = { dir: '/var/nuxt/src/template' }
nuxt.options.build.createRoutes = jest.fn()
nuxt.options.render = { ssr: true }

const bundleBuilder = { build: jest.fn() }
const builder = new Builder(nuxt, bundleBuilder)
Expand All @@ -47,7 +48,7 @@ describe('builder: builder build', () => {

const buildReturn = await builder.build()

expect(consola.info).toBeCalledTimes(1)
expect(consola.info).toBeCalledTimes(3)
expect(consola.info).toBeCalledWith('Production build')
expect(nuxt.ready).toBeCalledTimes(1)
expect(nuxt.callHook).toBeCalledTimes(3)
Expand Down Expand Up @@ -117,6 +118,7 @@ describe('builder: builder build', () => {
nuxt.options.buildDir = '/var/nuxt/build'
nuxt.options.dir = { pages: '/var/nuxt/src/pages' }
nuxt.options.build.createRoutes = jest.fn()
nuxt.options.render = { ssr: true }

const bundleBuilder = { build: jest.fn() }
const builder = new Builder(nuxt, bundleBuilder)
Expand Down
Expand Up @@ -30,6 +30,7 @@ TemplateContext {
],
"head": "test_head",
"isDev": "test_dev",
"isFullStatic": false,
"isTest": "test_test",
"layoutTransition": Object {
"name": "test_layout_trans",
Expand Down
9 changes: 7 additions & 2 deletions packages/builder/test/context/build.test.js
@@ -1,15 +1,20 @@
import { TARGETS } from '@nuxt/utils'
import BuildContext from '../../src/context/build'

describe('builder: buildContext', () => {
test('should construct context', () => {
const builder = {
nuxt: { options: {} }
nuxt: {
options: {
target: TARGETS.server
}
}
}
const context = new BuildContext(builder)
expect(context._builder).toEqual(builder)
expect(context.nuxt).toEqual(builder.nuxt)
expect(context.options).toEqual(builder.nuxt.options)
expect(context.isStatic).toEqual(false)
expect(context.target).toEqual('server')
})

test('should return builder plugins context', () => {
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/commands/build.js
@@ -1,3 +1,5 @@
import consola from 'consola'
import { MODES, TARGETS } from '@nuxt/utils'
import { common, locking } from '../options'
import { createLock } from '../utils'

Expand Down Expand Up @@ -62,7 +64,7 @@ export default {
},
async run (cmd) {
const config = await cmd.getNuxtConfig({ dev: false, server: false, _build: true })
config.server = config.mode === 'spa' && cmd.argv.generate !== false
config.server = (config.mode === MODES.spa || config.ssr === false) && cmd.argv.generate !== false
const nuxt = await cmd.getNuxt(config)

if (cmd.argv.lock) {
Expand All @@ -73,14 +75,18 @@ export default {
}))
}

if (nuxt.options.mode === 'spa' && cmd.argv.generate !== false) {
// TODO: remove if in Nuxt 3
if (nuxt.options.mode === MODES.spa && nuxt.options.target === TARGETS.server && cmd.argv.generate !== false) {
// Build + Generate for static deployment
const generator = await cmd.getGenerator(nuxt)
await generator.generate({ build: true })
} else {
// Build only
const builder = await cmd.getBuilder(nuxt)
await builder.build()

const nextCommand = nuxt.options.target === TARGETS.static ? 'nuxt export' : 'nuxt start'
consola.info('Ready to run `' + (nextCommand) + '`')
}
}
}
50 changes: 50 additions & 0 deletions packages/cli/src/commands/export.js
@@ -0,0 +1,50 @@
import path from 'path'
import consola from 'consola'
import { TARGETS } from '@nuxt/utils'
import { common, locking } from '../options'
import { createLock } from '../utils'

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,
_build: cmd.argv.build
})
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()

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')
}
}
22 changes: 18 additions & 4 deletions packages/cli/src/commands/generate.js
@@ -1,3 +1,4 @@
import { TARGETS } from '@nuxt/utils'
import { common, locking } from '../options'
import { normalizeArg, createLock } from '../utils'

Expand Down Expand Up @@ -53,14 +54,25 @@ export default {
}
},
async run (cmd) {
const config = await cmd.getNuxtConfig({ dev: false, _generate: true, _build: cmd.argv.build })
const config = await cmd.getNuxtConfig({
dev: false,
_build: cmd.argv.build
})

// Disable analyze if set by the nuxt config
if (!config.build) {
config.build = {}
if (config.target === TARGETS.static) {
throw new Error("Please use `nuxt export` when using `target: 'static'`")
}

// Forcing static target anyway
config.target = TARGETS.static

// Disable analyze if set by the nuxt config
config.build = config.build || {}
config.build.analyze = false

// Set flag to keep the prerendering behaviour
config._legacyGenerate = true

const nuxt = await cmd.getNuxt(config)

if (cmd.argv.lock) {
Expand All @@ -82,12 +94,14 @@ export default {
}

const generator = await cmd.getGenerator(nuxt)
await nuxt.server.listen()

const { errors } = await generator.generate({
init: true,
build: cmd.argv.build
})

await nuxt.close()
if (cmd.argv['fail-on-error'] && errors.length > 0) {
throw new Error('Error generating pages, exiting with non-zero code')
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/index.js
@@ -1,8 +1,10 @@
const commands = {
start: () => import('./start'),
serve: () => import('./serve'),
dev: () => import('./dev'),
build: () => import('./build'),
generate: () => import('./generate'),
export: () => import('./export'),
webpack: () => import('./webpack'),
help: () => import('./help')
}
Expand Down

0 comments on commit 917adc0

Please sign in to comment.