feat: configurable global name (#4012)

Co-authored-by: JuliaNeumann <jn.julianeumann@gmail.com>
This commit is contained in:
Jonas Galvez 2018-10-09 09:07:23 -03:00 committed by Pooya Parsa
parent c2fde1958a
commit a3dd7dad6b
27 changed files with 184 additions and 90 deletions

View File

@ -37,7 +37,7 @@ export default {
return h('div',{ return h('div',{
domProps: { domProps: {
id: '__nuxt' id: '<%= globals.id %>'
} }
}, [ }, [
<% if (loading) { %>loadingEl,<% } %> <% if (loading) { %>loadingEl,<% } %>
@ -53,10 +53,13 @@ export default {
}, },
created () { created () {
// Add this.$nuxt in child instances // Add this.$nuxt in child instances
Vue.prototype.$nuxt = this Vue.prototype.<%= globals.nuxt %> = this
// add to window so we can listen when ready // add to window so we can listen when ready
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.$nuxt = this window.<%= globals.nuxt %> = this
<% if (globals.nuxt !== '$nuxt') { %>
window.$nuxt = true
<% } %>
} }
// Add $nuxt.error() // Add $nuxt.error()
this.error = this.nuxt.error this.error = this.nuxt.error
@ -100,8 +103,8 @@ export default {
return resolvedLayouts[_layout] return resolvedLayouts[_layout]
}) })
.catch((e) => { .catch((e) => {
if (this.$nuxt) { if (this.<%= globals.nuxt %>) {
return this.$nuxt.error({ statusCode: 500, message: e.message }) return this.<%= globals.nuxt %>.error({ statusCode: 500, message: e.message })
} }
}) })
} }

View File

@ -27,43 +27,54 @@ let router
<% if (store) { %>let store<% } %> <% if (store) { %>let store<% } %>
// Try to rehydrate SSR data from window // Try to rehydrate SSR data from window
const NUXT = window.__NUXT__ || {} const NUXT = window.<%= globals.context %> || {}
Object.assign(Vue.config, <%= serialize(vue.config) %>) Object.assign(Vue.config, <%= serialize(vue.config) %>)
<% if (debug || mode === 'spa') { %> <% if (debug || mode === 'spa') { %>
// Setup global Vue error handler // Setup global Vue error handler
const defaultErrorHandler = Vue.config.errorHandler if (!Vue.config.$nuxt) {
Vue.config.errorHandler = (err, vm, info, ...rest) => { const defaultErrorHandler = Vue.config.errorHandler
const nuxtError = { Vue.config.errorHandler = (err, vm, info, ...rest) => {
statusCode: err.statusCode || err.name || 'Whoops!', const nuxtError = {
message: err.message || err.toString() statusCode: err.statusCode || err.name || 'Whoops!',
} message: err.message || err.toString()
}
// Call other handler if exist // Call other handler if exist
let handled = null let handled = null
if (typeof defaultErrorHandler === 'function') { if (typeof defaultErrorHandler === 'function') {
handled = defaultErrorHandler(err, vm, info, ...rest) handled = defaultErrorHandler(err, vm, info, ...rest)
} }
if (handled === true){ if (handled === true){
return handled return handled
} }
// Show Nuxt Error Page if (vm && vm.$root) {
if (vm && vm.$root && vm.$root.$nuxt && vm.$root.$nuxt.error && info !== 'render function') { const nuxtApp = Object.keys(Vue.config.$nuxt)
vm.$root.$nuxt.error(nuxtError) .find(nuxtInstance => vm.$root[nuxtInstance])
}
if (typeof defaultErrorHandler === 'function') {
return handled
}
// Log to console // Show Nuxt Error Page
if (process.env.NODE_ENV !== 'production') { if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
console.error(err) vm.$root[nuxtApp].error(nuxtError)
} else { }
console.error(err.message || nuxtError.message) }
if (typeof defaultErrorHandler === 'function') {
return handled
}
// Log to console
if (process.env.NODE_ENV !== 'production') {
console.error(err)
} else {
console.error(err.message || nuxtError.message)
}
} }
Vue.config.$nuxt = {}
} }
Vue.config.$nuxt.<%= globals.nuxt %> = true
<% } %> <% } %>
// Create and mount App // Create and mount App
@ -142,7 +153,7 @@ async function loadAsyncComponents (to, from, next) {
err = err || {} err = err || {}
const statusCode = (err.statusCode || err.status || (err.response && err.response.status) || 500) const statusCode = (err.statusCode || err.status || (err.response && err.response.status) || 500)
this.error({ statusCode, message: err.message }) this.error({ statusCode, message: err.message })
this.$nuxt.$emit('routeChanged', to, from, err) this.<%= globals.nuxt %>.$emit('routeChanged', to, from, err)
next(false) next(false)
} }
} }
@ -401,7 +412,7 @@ async function render (to, from, next) {
if (!error) { if (!error) {
error = {} error = {}
} else if (error.message === 'ERR_REDIRECT') { } else if (error.message === 'ERR_REDIRECT') {
return this.$nuxt.$emit('routeChanged', to, from, error) return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
} }
_lastPaths = [] _lastPaths = []
const errorResponseStatus = (error.response && error.response.status) const errorResponseStatus = (error.response && error.response.status)
@ -417,7 +428,7 @@ async function render (to, from, next) {
await this.loadLayout(layout) await this.loadLayout(layout)
this.error(error) this.error(error)
this.$nuxt.$emit('routeChanged', to, from, error) this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
next(false) next(false)
} }
} }
@ -489,19 +500,19 @@ function fixPrepatch(to, ___) {
} }
function nuxtReady (_app) { function nuxtReady (_app) {
window._nuxtReadyCbs.forEach((cb) => { window.<%= globals.readyCallback %>Cbs.forEach((cb) => {
if (typeof cb === 'function') { if (typeof cb === 'function') {
cb(_app) cb(_app)
} }
}) })
// Special JSDOM // Special JSDOM
if (typeof window._onNuxtLoaded === 'function') { if (typeof window.<%= globals.loadedCallback %> === 'function') {
window._onNuxtLoaded(_app) window.<%= globals.loadedCallback %>(_app)
} }
// Add router hooks // Add router hooks
router.afterEach((to, from) => { router.afterEach((to, from) => {
// Wait for fixPrepatch + $data updates // Wait for fixPrepatch + $data updates
Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from)) Vue.nextTick(() => _app.<%= globals.nuxt %>.$emit('routeChanged', to, from))
}) })
} }
@ -523,7 +534,7 @@ function getNuxtChildComponents($parent, $components = []) {
function hotReloadAPI (_app) { function hotReloadAPI (_app) {
if (!module.hot) return if (!module.hot) return
let $components = getNuxtChildComponents(_app.$nuxt, []) let $components = getNuxtChildComponents(_app.<%= globals.nuxt %>, [])
$components.forEach(addHotReload.bind(_app)) $components.forEach(addHotReload.bind(_app))
} }
@ -623,11 +634,11 @@ async function mountApp(__app) {
// Mounts Vue app to DOM element // Mounts Vue app to DOM element
const mount = () => { const mount = () => {
_app.$mount('#__nuxt') _app.$mount('#<%= globals.id %>')
// Listen for first Vue update // Listen for first Vue update
Vue.nextTick(() => { Vue.nextTick(() => {
// Call window.onNuxtReady callbacks // Call window.{{globals.readyCallback}} callbacks
nuxtReady(_app) nuxtReady(_app)
<% if (isDev) { %> <% if (isDev) { %>
// Enable hot reloading // Enable hot reloading

View File

@ -5,8 +5,8 @@ export default {
render (h, { parent, data, props }) { render (h, { parent, data, props }) {
data.nuxtChild = true data.nuxtChild = true
const _parent = parent const _parent = parent
const transitions = parent.$nuxt.nuxt.transitions const transitions = parent.<%= globals.nuxt %>.nuxt.transitions
const defaultTransition = parent.$nuxt.nuxt.defaultTransition const defaultTransition = parent.<%= globals.nuxt %>.nuxt.defaultTransition
let depth = 0 let depth = 0
while (parent) { while (parent) {
@ -33,8 +33,8 @@ export default {
let beforeEnter = listeners.beforeEnter let beforeEnter = listeners.beforeEnter
listeners.beforeEnter = (el) => { listeners.beforeEnter = (el) => {
// Ensure to trigger scroll event after calling scrollBehavior // Ensure to trigger scroll event after calling scrollBehavior
window.$nuxt.$nextTick(() => { window.<%= globals.nuxt %>.$nextTick(() => {
window.$nuxt.$emit('triggerScroll') window.<%= globals.nuxt %>.$emit('triggerScroll')
}) })
if (beforeEnter) return beforeEnter.call(_parent, el) if (beforeEnter) return beforeEnter.call(_parent, el)
} }

View File

@ -135,7 +135,7 @@ async function createApp (ssrContext) {
store[key] = app[key] store[key] = app[key]
<% } %> <% } %>
// Check if plugin not already installed // Check if plugin not already installed
const installKey = '__nuxt_' + key + '_installed__' const installKey = '__<%= globals.pluginPrefix %>_' + key + '_installed__'
if (Vue[installKey]) return if (Vue[installKey]) return
Vue[installKey] = true Vue[installKey] = true
// Call Vue.use() to install the plugin into vm // Call Vue.use() to install the plugin into vm
@ -153,8 +153,8 @@ async function createApp (ssrContext) {
<% if (store) { %> <% if (store) { %>
if (process.client) { if (process.client) {
// Replace store state before plugins execution // Replace store state before plugins execution
if (window.__NUXT__ && window.__NUXT__.state) { if (window.<%= globals.context %> && window.<%= globals.context %>.state) {
store.replaceState(window.__NUXT__.state) store.replaceState(window.<%= globals.context %>.state)
} }
} }
<% } %> <% } %>

View File

@ -67,7 +67,7 @@ const scrollBehavior = function (to, from, savedPosition) {
return new Promise((resolve) => { return new Promise((resolve) => {
// wait for the out transition to complete (if necessary) // wait for the out transition to complete (if necessary)
window.$nuxt.$once('triggerScroll', () => { window.<%= globals.nuxt %>.$once('triggerScroll', () => {
// coords will be used if no selector is provided, // coords will be used if no selector is provided,
// or if the selector didn't match any element. // or if the selector didn't match any element.
if (to.hash) { if (to.hash) {

View File

@ -46,7 +46,7 @@ export default async (ssrContext) => {
ssrContext.next = createNext(ssrContext) ssrContext.next = createNext(ssrContext)
// Used for beforeNuxtRender({ Components, nuxtState }) // Used for beforeNuxtRender({ Components, nuxtState })
ssrContext.beforeRenderFns = [] ssrContext.beforeRenderFns = []
// Nuxt object (window.__NUXT__) // Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
ssrContext.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true } ssrContext.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
// Create the app definition and the instance (created for each request) // Create the app definition and the instance (created for each request)
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext) const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
@ -125,7 +125,6 @@ export default async (ssrContext) => {
await _app.loadLayout(layout) await _app.loadLayout(layout)
if (ssrContext.nuxt.error) return renderErrorPage() if (ssrContext.nuxt.error) return renderErrorPage()
layout = _app.setLayout(layout) layout = _app.setLayout(layout)
// ...Set layout to __NUXT__
ssrContext.nuxt.layout = _app.layoutName ssrContext.nuxt.layout = _app.layoutName
/* /*

View File

@ -2,12 +2,12 @@ import Vue from 'vue'
const noopData = () => ({}) const noopData = () => ({})
// window.onNuxtReady(() => console.log('Ready')) hook // window.{{globals.loadedCallback}} hook
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading) // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
if (process.client) { if (process.client) {
window._nuxtReadyCbs = [] window.<%= globals.readyCallback %>Cbs = []
window.onNuxtReady = (cb) => { window.<%= globals.readyCallback %> = (cb) => {
window._nuxtReadyCbs.push(cb) window.<%= globals.readyCallback %>Cbs.push(cb)
} }
} }
@ -134,7 +134,6 @@ export async function setContext(app, context) {
if (!status) { if (!status) {
return return
} }
// Used in middleware
app.context._redirected = true app.context._redirected = true
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' }) // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
let pathType = typeof path let pathType = typeof path
@ -171,8 +170,12 @@ export async function setContext(app, context) {
} }
} }
} }
if (process.server) app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn) if (process.server) {
if (process.client) app.context.nuxtState = window.__NUXT__ app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
}
if (process.client) {
app.context.nuxtState = window.<%= globals.context %>
}
} }
// Dynamic keys // Dynamic keys
app.context.next = context.next app.context.next = context.next

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -1,5 +1,5 @@
<style> <style>
body, html, #__nuxt { body, html, #<%= globals.id %> {
background: <%= options.background %>; background: <%= options.background %>;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -16,9 +16,19 @@ import Glob from 'glob'
import upath from 'upath' import upath from 'upath'
import consola from 'consola' import consola from 'consola'
import { r, wp, wChunk, createRoutes, parallel, sequence, relativeTo, waitFor } from '../common/utils' import {
import Options from '../common/options' r,
wp,
wChunk,
createRoutes,
parallel,
sequence,
relativeTo,
waitFor,
determineGlobals
} from '../common/utils'
import Options from '../common/options'
import PerfLoader from './webpack/utils/perf-loader' import PerfLoader from './webpack/utils/perf-loader'
import ClientWebpackConfig from './webpack/client' import ClientWebpackConfig from './webpack/client'
import ServerWebpackConfig from './webpack/server' import ServerWebpackConfig from './webpack/server'
@ -30,6 +40,7 @@ export default class Builder {
this.nuxt = nuxt this.nuxt = nuxt
this.isStatic = false // Flag to know if the build is for a generated app this.isStatic = false // Flag to know if the build is for a generated app
this.options = nuxt.options this.options = nuxt.options
this.globals = determineGlobals(nuxt.options.globalName, nuxt.options.globals)
// Fields that set on build // Fields that set on build
this.compilers = [] this.compilers = []
@ -214,6 +225,8 @@ export default class Builder {
head: this.options.head, head: this.options.head,
middleware: fsExtra.existsSync(path.join(this.options.srcDir, this.options.dir.middleware)), middleware: fsExtra.existsSync(path.join(this.options.srcDir, this.options.dir.middleware)),
store: this.options.store, store: this.options.store,
globalName: this.options.globalName,
globals: this.globals,
css: this.options.css, css: this.options.css,
plugins: this.plugins, plugins: this.plugins,
appPath: './App.js', appPath: './App.js',

View File

@ -1,5 +1,6 @@
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import _ from 'lodash'
import env from 'std-env' import env from 'std-env'
const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', 'package.json')) const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', 'package.json'))
@ -14,6 +15,17 @@ export default {
// Mode // Mode
mode: 'universal', mode: 'universal',
// Global name
globalName: `nuxt`,
globals: {
id: globalName => `__${globalName}`,
nuxt: globalName => `$${globalName}`,
context: globalName => `__${globalName.toUpperCase()}__`,
pluginPrefix: globalName => globalName,
readyCallback: globalName => `on${_.capitalize(globalName)}Ready`,
loadedCallback: globalName => `_on${_.capitalize(globalName)}Loaded`
},
// Server options // Server options
server: { server: {
https: false, https: false,

View File

@ -43,6 +43,10 @@ Options.from = function (_options) {
options.extensions = [options.extensions] options.extensions = [options.extensions]
} }
options.globalName = (_.isString(options.globalName) && /^[a-zA-Z]+$/.test(options.globalName))
? options.globalName.toLowerCase()
: 'nuxt'
// Resolve rootDir // Resolve rootDir
options.rootDir = hasValue(options.rootDir) ? path.resolve(options.rootDir) : process.cwd() options.rootDir = hasValue(options.rootDir) ? path.resolve(options.rootDir) : process.cwd()

View File

@ -349,3 +349,15 @@ export const guardDir = function guardDir(options, key1, key2) {
throw new Error(errorMessage) throw new Error(errorMessage)
} }
} }
export const determineGlobals = function determineGlobals(globalName, globals) {
const _globals = {}
for (const global in globals) {
if (typeof globals[global] === 'function') {
_globals[global] = globals[global](globalName)
} else {
_globals[global] = globals[global]
}
}
return _globals
}

View File

@ -10,7 +10,7 @@ import connect from 'connect'
import launchMiddleware from 'launch-editor-middleware' import launchMiddleware from 'launch-editor-middleware'
import consola from 'consola' import consola from 'consola'
import { isUrl, timeout, waitFor } from '../common/utils' import { isUrl, timeout, waitFor, determineGlobals } from '../common/utils'
import defaults from '../common/nuxt.config' import defaults from '../common/nuxt.config'
import MetaRenderer from './meta' import MetaRenderer from './meta'
@ -23,6 +23,7 @@ export default class Renderer {
constructor(nuxt) { constructor(nuxt) {
this.nuxt = nuxt this.nuxt = nuxt
this.options = nuxt.options this.options = nuxt.options
this.globals = determineGlobals(nuxt.options.globalName, nuxt.options.globals)
// Will be set by createRenderer // Will be set by createRenderer
this.bundleRenderer = null this.bundleRenderer = null
@ -311,7 +312,7 @@ export default class Renderer {
getPreloadFiles getPreloadFiles
} = await this.metaRenderer.render(context) } = await this.metaRenderer.render(context)
const APP = const APP =
`<div id="__nuxt">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS `<div id="${this.globals.id}">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS
// Detect 404 errors // Detect 404 errors
if ( if (
@ -341,7 +342,7 @@ export default class Renderer {
let APP = await this.bundleRenderer.renderToString(context) let APP = await this.bundleRenderer.renderToString(context)
if (!context.nuxt.serverRendered) { if (!context.nuxt.serverRendered) {
APP = '<div id="__nuxt"></div>' APP = `<div id="${this.globals.id}"></div>`
} }
const m = context.meta.inject() const m = context.meta.inject()
let HEAD = let HEAD =
@ -361,7 +362,7 @@ export default class Renderer {
await this.nuxt.callHook('render:routeContext', context.nuxt) await this.nuxt.callHook('render:routeContext', context.nuxt)
const serializedSession = `window.__NUXT__=${devalue(context.nuxt)};` const serializedSession = `window.${this.globals.context}=${devalue(context.nuxt)};`
const cspScriptSrcHashSet = new Set() const cspScriptSrcHashSet = new Set()
if (this.options.render.csp) { if (this.options.render.csp) {
@ -430,7 +431,7 @@ export default class Renderer {
const { window } = await jsdom.JSDOM.fromURL(url, options) const { window } = await jsdom.JSDOM.fromURL(url, options)
// If Nuxt could not be loaded (error from the server-side) // If Nuxt could not be loaded (error from the server-side)
const nuxtExists = window.document.body.innerHTML.includes( const nuxtExists = window.document.body.innerHTML.includes(
this.options.render.ssr ? 'window.__NUXT__' : '<div id="__nuxt">' this.options.render.ssr ? `window.${this.globals.context}` : `<div id="${this.globals.id}">`
) )
/* istanbul ignore if */ /* istanbul ignore if */
if (!nuxtExists) { if (!nuxtExists) {
@ -439,8 +440,9 @@ export default class Renderer {
throw error throw error
} }
// Used by nuxt.js to say when the components are loaded and the app ready // Used by nuxt.js to say when the components are loaded and the app ready
const onNuxtLoaded = this.globals.loadedCallback
await timeout(new Promise((resolve) => { await timeout(new Promise((resolve) => {
window._onNuxtLoaded = () => resolve(window) window[onNuxtLoaded] = () => resolve(window)
}), 20000, 'Components loading in renderAndGetWindow was not completed in 20s') }), 20000, 'Components loading in renderAndGetWindow was not completed in 20s')
if (options.virtualConsole) { if (options.virtualConsole) {
// after window initialized successfully // after window initialized successfully

View File

@ -91,5 +91,9 @@ export default {
static: { static: {
maxAge: '1y' maxAge: '1y'
} }
},
globalName: 'noxxt',
globals: {
id: 'custom-nuxt-id'
} }
} }

View File

@ -21,4 +21,20 @@ describe('basic config defaults', () => {
expect(options.build.vendor).toBeUndefined() expect(options.build.vendor).toBeUndefined()
expect(consola.warn).toHaveBeenCalledWith('vendor has been deprecated due to webpack4 optimization') expect(consola.warn).toHaveBeenCalledWith('vendor has been deprecated due to webpack4 optimization')
}) })
test('globalName uses nuxt as default if not set', () => {
const options = Options.from({})
expect(options.globalName).toEqual('nuxt')
})
test('globalName uses nuxt as default if set to something other than only letters', () => {
let options = Options.from({ globalName: '12foo4' })
expect(options.globalName).toEqual('nuxt')
options = Options.from({ globalName: 'foo bar' })
expect(options.globalName).toEqual('nuxt')
options = Options.from({ globalName: 'foo?' })
expect(options.globalName).toEqual('nuxt')
})
}) })

View File

@ -51,11 +51,22 @@ describe('with-config', () => {
expect(html.includes('::-webkit-input-placeholder')).toBe(true) expect(html.includes('::-webkit-input-placeholder')).toBe(true)
}) })
test('/test/ (custom globalName)', async () => {
const window = await nuxt.renderAndGetWindow(url('/test/'))
const html = window.document.body.innerHTML
expect(html.includes('id="custom-nuxt-id">')).toBe(true)
expect(html.includes('id="__nuxt">')).toBe(false)
expect(window.__NOXXT__).toBeDefined()
expect(window.__NUXT__).toBeUndefined()
expect(window.$noxxt).toBeDefined()
expect(window.$nuxt).toBeDefined() // for Vue Dev Tools detection
})
test('/test/ (router base)', async () => { test('/test/ (router base)', async () => {
const window = await nuxt.renderAndGetWindow(url('/test/')) const window = await nuxt.renderAndGetWindow(url('/test/'))
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
expect(window.__NUXT__.layout).toBe('default') expect(window.__NOXXT__.layout).toBe('default')
expect(html.includes('<h1>Default layout</h1>')).toBe(true) expect(html.includes('<h1>Default layout</h1>')).toBe(true)
expect(html.includes('<h1>I have custom configurations</h1>')).toBe(true) expect(html.includes('<h1>I have custom configurations</h1>')).toBe(true)
@ -65,7 +76,7 @@ describe('with-config', () => {
test('/test/about (custom layout)', async () => { test('/test/about (custom layout)', async () => {
const window = await nuxt.renderAndGetWindow(url('/test/about')) const window = await nuxt.renderAndGetWindow(url('/test/about'))
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
expect(window.__NUXT__.layout).toBe('custom') expect(window.__NOXXT__.layout).toBe('custom')
expect(html.includes('<h1>Custom layout</h1>')).toBe(true) expect(html.includes('<h1>Custom layout</h1>')).toBe(true)
expect(html.includes('<h1>About page</h1>')).toBe(true) expect(html.includes('<h1>About page</h1>')).toBe(true)
}) })
@ -73,7 +84,7 @@ describe('with-config', () => {
test('/test/desktop (custom layout in desktop folder)', async () => { test('/test/desktop (custom layout in desktop folder)', async () => {
const window = await nuxt.renderAndGetWindow(url('/test/desktop')) const window = await nuxt.renderAndGetWindow(url('/test/desktop'))
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
expect(window.__NUXT__.layout).toBe('desktop/default') expect(window.__NOXXT__.layout).toBe('desktop/default')
expect(html.includes('<h1>Default desktop layout</h1>')).toBe(true) expect(html.includes('<h1>Default desktop layout</h1>')).toBe(true)
expect(html.includes('<h1>Desktop page</h1>')).toBe(true) expect(html.includes('<h1>Desktop page</h1>')).toBe(true)
}) })
@ -81,7 +92,7 @@ describe('with-config', () => {
test('/test/mobile (custom layout in mobile folder)', async () => { test('/test/mobile (custom layout in mobile folder)', async () => {
const window = await nuxt.renderAndGetWindow(url('/test/mobile')) const window = await nuxt.renderAndGetWindow(url('/test/mobile'))
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
expect(window.__NUXT__.layout).toBe('mobile/default') expect(window.__NOXXT__.layout).toBe('mobile/default')
expect(html.includes('<h1>Default mobile layout</h1>')).toBe(true) expect(html.includes('<h1>Default mobile layout</h1>')).toBe(true)
expect(html.includes('<h1>Mobile page</h1>')).toBe(true) expect(html.includes('<h1>Mobile page</h1>')).toBe(true)
}) })

View File

@ -19,11 +19,12 @@ export default class Browser {
await this.browser.close() await this.browser.close()
} }
async page(url) { async page(url, globalName = 'nuxt') {
if (!this.browser) throw new Error('Please call start() before page(url)') if (!this.browser) throw new Error('Please call start() before page(url)')
const page = await this.browser.newPage() const page = await this.browser.newPage()
await page.goto(url) await page.goto(url)
await page.waitForFunction('!!window.$nuxt') page.$nuxtGlobalHandle = `window.$${globalName}`
await page.waitForFunction(`!!${page.$nuxtGlobalHandle}`)
page.html = () => page.html = () =>
page.evaluate(() => window.document.documentElement.outerHTML) page.evaluate(() => window.document.documentElement.outerHTML)
page.$text = selector => page.$eval(selector, el => el.textContent) page.$text = selector => page.$eval(selector, el => el.textContent)
@ -37,21 +38,24 @@ export default class Browser {
(els, attr) => els.map(el => el.getAttribute(attr)), (els, attr) => els.map(el => el.getAttribute(attr)),
attr attr
) )
page.$nuxt = await page.evaluateHandle('window.$nuxt')
page.$nuxt = await page.evaluateHandle(page.$nuxtGlobalHandle)
page.nuxt = { page.nuxt = {
async navigate(path, waitEnd = true) { async navigate(path, waitEnd = true) {
const hook = page.evaluate(() => { const hook = page.evaluate(`
return new Promise(resolve => new Promise(resolve =>
window.$nuxt.$once('routeChanged', resolve) ${page.$nuxtGlobalHandle}.$once('routeChanged', resolve)
).then(() => new Promise(resolve => setTimeout(resolve, 50))) ).then(() => new Promise(resolve => setTimeout(resolve, 50)))
}) `)
await page.evaluate( await page.evaluate(
($nuxt, path) => $nuxt.$router.push(path), ($nuxt, path) => $nuxt.$router.push(path),
page.$nuxt, page.$nuxt,
path path
) )
if (waitEnd) await hook if (waitEnd) {
await hook
}
return { hook } return { hook }
}, },
routeData() { routeData() {