index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import Vue from 'vue'
  2. <% if (store) { %>import Vuex from 'vuex'<% } %>
  3. <% if (features.meta) { %>import Meta from 'vue-meta'<% } %>
  4. <% if (features.componentClientOnly) { %>import ClientOnly from 'vue-client-only'<% } %>
  5. <% if (features.deprecations) { %>import NoSsr from 'vue-no-ssr'<% } %>
  6. import { createRouter } from './router.js'
  7. import NuxtChild from './components/nuxt-child.js'
  8. import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
  9. import Nuxt from './components/nuxt.js'
  10. import App from '<%= appPath %>'
  11. import { setContext, getLocation, getRouteData, normalizeError } from './utils'
  12. <% if (store) { %>import { createStore } from './store.js'<% } %>
  13. /* Plugins */
  14. <%= isTest ? '/* eslint-disable camelcase */' : '' %>
  15. <% plugins.forEach((plugin) => { %>import <%= plugin.name %> from '<%= plugin.name %>' // Source: <%= relativeToBuild(plugin.src) %> (mode: '<%= plugin.mode %>')
  16. <% }) %>
  17. <%= isTest ? '/* eslint-enable camelcase */' : '' %>
  18. <% if (features.componentClientOnly) { %>
  19. // Component: <ClientOnly>
  20. Vue.component(ClientOnly.name, ClientOnly)
  21. <% } %>
  22. <% if (features.deprecations) { %>
  23. // TODO: Remove in Nuxt 3: <NoSsr>
  24. Vue.component(NoSsr.name, {
  25. ...NoSsr,
  26. render (h, ctx) {
  27. if (process.client && !NoSsr._warned) {
  28. NoSsr._warned = true
  29. <%= isTest ? '// eslint-disable-next-line no-console' : '' %>
  30. console.warn('<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead')
  31. }
  32. return NoSsr.render(h, ctx)
  33. }
  34. })
  35. <% } %>
  36. // Component: <NuxtChild>
  37. Vue.component(NuxtChild.name, NuxtChild)
  38. <% if (features.componentAliases) { %>Vue.component('NChild', NuxtChild)<% } %>
  39. // Component NuxtLink is imported in server.js or client.js
  40. // Component: <Nuxt>
  41. Vue.component(Nuxt.name, Nuxt)
  42. Object.defineProperty(Vue.prototype, '<%= globals.nuxt %>', {
  43. get() {
  44. const globalNuxt = this.$root.$options.<%= globals.nuxt %>
  45. if (process.client && !globalNuxt && typeof window !== 'undefined') {
  46. return window.<%= globals.nuxt %>
  47. }
  48. return globalNuxt
  49. },
  50. configurable: true
  51. })
  52. <% if (features.meta) {
  53. // vue-meta configuration
  54. const vueMetaOptions = {
  55. ...nuxtOptions.vueMeta,
  56. keyName: 'head', // the component option name that vue-meta looks for meta info on.
  57. attribute: 'data-n-head', // the attribute name vue-meta adds to the tags it observes
  58. ssrAttribute: 'data-n-head-ssr', // the attribute name that lets vue-meta know that meta info has already been server-rendered
  59. tagIDKeyName: 'hid' // the property name that vue-meta uses to determine whether to overwrite or append a tag
  60. }
  61. %>
  62. Vue.use(Meta, <%= JSON.stringify(vueMetaOptions) %>)<%= isTest ? '// eslint-disable-line' : '' %>
  63. <% } %>
  64. <% if (features.transitions) { %>
  65. const defaultTransition = <%=
  66. serialize(pageTransition)
  67. .replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
  68. .replace('enterCancelled(', 'function(').replace('beforeLeave(', 'function(').replace('leave(', 'function(')
  69. .replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(').replace('beforeAppear(', 'function(')
  70. .replace('appear(', 'function(').replace('afterAppear(', 'function(').replace('appearCancelled(', 'function(')
  71. %><%= isTest ? '// eslint-disable-line' : '' %>
  72. <% } %>
  73. <% if (store) { %>
  74. const originalRegisterModule = Vuex.Store.prototype.registerModule
  75. function registerModule (path, rawModule, options = {}) {
  76. const preserveState = process.client && (
  77. Array.isArray(path)
  78. ? !!path.reduce((namespacedState, path) => namespacedState && namespacedState[path], this.state)
  79. : path in this.state
  80. )
  81. return originalRegisterModule.call(this, path, rawModule, { preserveState, ...options })
  82. }
  83. <% } %>
  84. async function createApp(ssrContext, config = {}) {
  85. const router = await createRouter(ssrContext, config)
  86. <% if (store) { %>
  87. const store = createStore(ssrContext)
  88. // Add this.$router into store actions/mutations
  89. store.$router = router
  90. <% if (mode === 'universal') { %>
  91. // Fix SSR caveat https://github.com/nuxt/nuxt.js/issues/3757#issuecomment-414689141
  92. store.registerModule = registerModule
  93. <% } %>
  94. <% } %>
  95. // Create Root instance
  96. // here we inject the router and store to all child components,
  97. // making them available everywhere as `this.$router` and `this.$store`.
  98. const app = {
  99. <% if (features.meta) { %>
  100. <%= isTest ? '/* eslint-disable array-bracket-spacing, quotes, quote-props, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren, object-shorthand */' : '' %>
  101. head: <%= serializeFunction(head) %>,
  102. <%= isTest ? '/* eslint-enable array-bracket-spacing, quotes, quote-props, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren, object-shorthand */' : '' %>
  103. <% } %>
  104. <% if (store) { %>store,<% } %>
  105. router,
  106. nuxt: {
  107. <% if (features.transitions) { %>
  108. defaultTransition,
  109. transitions: [defaultTransition],
  110. setTransitions (transitions) {
  111. if (!Array.isArray(transitions)) {
  112. transitions = [transitions]
  113. }
  114. transitions = transitions.map((transition) => {
  115. if (!transition) {
  116. transition = defaultTransition
  117. } else if (typeof transition === 'string') {
  118. transition = Object.assign({}, defaultTransition, { name: transition })
  119. } else {
  120. transition = Object.assign({}, defaultTransition, transition)
  121. }
  122. return transition
  123. })
  124. this.$options.nuxt.transitions = transitions
  125. return transitions
  126. },
  127. <% } %>
  128. err: null,
  129. dateErr: null,
  130. error (err) {
  131. err = err || null
  132. app.context._errored = Boolean(err)
  133. err = err ? normalizeError(err) : null
  134. let nuxt = app.nuxt // to work with @vue/composition-api, see https://github.com/nuxt/nuxt.js/issues/6517#issuecomment-573280207
  135. if (this) {
  136. nuxt = this.nuxt || this.$options.nuxt
  137. }
  138. nuxt.dateErr = Date.now()
  139. nuxt.err = err
  140. // Used in src/server.js
  141. if (ssrContext) {
  142. ssrContext.nuxt.error = err
  143. }
  144. return err
  145. }
  146. },
  147. ...App
  148. }
  149. <% if (store) { %>
  150. // Make app available into store via this.app
  151. store.app = app
  152. <% } %>
  153. const next = ssrContext ? ssrContext.next : location => app.router.push(location)
  154. // Resolve route
  155. let route
  156. if (ssrContext) {
  157. route = router.resolve(ssrContext.url).route
  158. } else {
  159. const path = getLocation(router.options.base, router.options.mode)
  160. route = router.resolve(path).route
  161. }
  162. // Set context to app.context
  163. await setContext(app, {
  164. <% if (store) { %>store,<% } %>
  165. route,
  166. next,
  167. error: app.nuxt.error.bind(app),
  168. payload: ssrContext ? ssrContext.payload : undefined,
  169. req: ssrContext ? ssrContext.req : undefined,
  170. res: ssrContext ? ssrContext.res : undefined,
  171. beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined,
  172. ssrContext
  173. })
  174. function inject(key, value) {
  175. if (!key) {
  176. throw new Error('inject(key, value) has no key provided')
  177. }
  178. if (value === undefined) {
  179. throw new Error(`inject('${key}', value) has no value provided`)
  180. }
  181. key = '$' + key
  182. // Add into app
  183. app[key] = value
  184. // Add into context
  185. if (!app.context[key]) {
  186. app.context[key] = value
  187. }
  188. <% if (store) { %>
  189. // Add into store
  190. store[key] = app[key]
  191. <% } %>
  192. // Check if plugin not already installed
  193. const installKey = '__<%= globals.pluginPrefix %>_' + key + '_installed__'
  194. if (Vue[installKey]) {
  195. return
  196. }
  197. Vue[installKey] = true
  198. // Call Vue.use() to install the plugin into vm
  199. Vue.use(() => {
  200. if (!Object.prototype.hasOwnProperty.call(Vue.prototype, key)) {
  201. Object.defineProperty(Vue.prototype, key, {
  202. get () {
  203. return this.$root.$options[key]
  204. }
  205. })
  206. }
  207. })
  208. }
  209. // Inject runtime config as $config
  210. inject('config', config)
  211. <% if (store) { %>
  212. if (process.client) {
  213. // Replace store state before plugins execution
  214. if (window.<%= globals.context %> && window.<%= globals.context %>.state) {
  215. store.replaceState(window.<%= globals.context %>.state)
  216. }
  217. }
  218. <% } %>
  219. // Add enablePreview(previewData = {}) in context for plugins
  220. if (process.static && process.client) {
  221. app.context.enablePreview = function (previewData = {}) {
  222. app.previewData = Object.assign({}, previewData)
  223. inject('preview', previewData)
  224. }
  225. }
  226. // Plugin execution
  227. <%= isTest ? '/* eslint-disable camelcase */' : '' %>
  228. <% plugins.forEach((plugin) => { %>
  229. <% if (plugin.mode == 'client') { %>
  230. if (process.client && typeof <%= plugin.name %> === 'function') {
  231. await <%= plugin.name %>(app.context, inject)
  232. }
  233. <% } else if (plugin.mode == 'server') { %>
  234. if (process.server && typeof <%= plugin.name %> === 'function') {
  235. await <%= plugin.name %>(app.context, inject)
  236. }
  237. <% } else { %>
  238. if (typeof <%= plugin.name %> === 'function') {
  239. await <%= plugin.name %>(app.context, inject)
  240. }
  241. <% } %>
  242. <% }) %>
  243. <%= isTest ? '/* eslint-enable camelcase */' : '' %>
  244. // Lock enablePreview in context
  245. if (process.static && process.client) {
  246. app.context.enablePreview = function () {
  247. console.warn('You cannot call enablePreview() outside a plugin.')
  248. }
  249. }
  250. // Wait for async component to be resolved first
  251. await new Promise((resolve, reject) => {
  252. // Ignore 404s rather than blindly replacing URL in browser
  253. if (process.client) {
  254. const { route } = router.resolve(app.context.route.fullPath)
  255. if (!route.matched.length) {
  256. return resolve()
  257. }
  258. }
  259. router.replace(app.context.route.fullPath, resolve, (err) => {
  260. // https://github.com/vuejs/vue-router/blob/v3.4.3/src/util/errors.js
  261. if (!err._isRouter) return reject(err)
  262. if (err.type !== 2 /* NavigationFailureType.redirected */) return resolve()
  263. // navigated to a different route in router guard
  264. const unregister = router.afterEach(async (to, from) => {
  265. if (process.server && ssrContext && ssrContext.url) {
  266. ssrContext.url = to.fullPath
  267. }
  268. app.context.route = await getRouteData(to)
  269. app.context.params = to.params || {}
  270. app.context.query = to.query || {}
  271. unregister()
  272. resolve()
  273. })
  274. })
  275. })
  276. return {
  277. <% if(store) { %>store,<% } %>
  278. app,
  279. router
  280. }
  281. }
  282. export { createApp, NuxtError }