1 | import { stringify } from 'querystring'
|
2 | import Vue from 'vue'
|
3 | <% if (fetch.server) { %>import fetch from 'node-fetch'<% } %>
|
4 | <% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
|
5 | import {
|
6 | <% if (features.asyncData) { %>applyAsyncData,<% } %>
|
7 | <% if (features.middleware) { %>middlewareSeries,<% } %>
|
8 | <% if (features.middleware && features.layouts) { %>sanitizeComponent,<% } %>
|
9 | getMatchedComponents,
|
10 | promisify
|
11 | } from './utils.js'
|
12 | <% if (features.fetch) { %>import fetchMixin from './mixins/fetch.server'<% } %>
|
13 | import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
14 | import NuxtLink from './components/nuxt-link.server.js'
|
15 |
|
16 | <% if (features.fetch) { %>
|
17 |
|
18 | Vue.config.optionMergeStrategies.serverPrefetch = Vue.config.optionMergeStrategies.created
|
19 |
|
20 |
|
21 | if (!Vue.__nuxt__fetch__mixin__) {
|
22 | Vue.mixin(fetchMixin)
|
23 | Vue.__nuxt__fetch__mixin__ = true
|
24 | }
|
25 | <% } %>
|
26 |
|
27 |
|
28 | Vue.component(NuxtLink.name, NuxtLink)
|
29 | <% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
30 |
|
31 | <% if (fetch.server) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
32 |
|
33 | const noopApp = () => new Vue({ render: h => h('div') })
|
34 |
|
35 | function urlJoin () {
|
36 | return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
|
37 | }
|
38 |
|
39 | const createNext = ssrContext => (opts) => {
|
40 |
|
41 | ssrContext.redirected = opts
|
42 | if (ssrContext.target === 'static' || !ssrContext.res) {
|
43 | ssrContext.nuxt.serverRendered = false
|
44 | return
|
45 | }
|
46 | opts.query = stringify(opts.query)
|
47 | opts.path = opts.path + (opts.query ? '?' + opts.query : '')
|
48 | const routerBase = '<%= router.base %>'
|
49 | if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) {
|
50 | opts.path = urlJoin(routerBase, opts.path)
|
51 | }
|
52 |
|
53 | if (opts.path === ssrContext.url) {
|
54 | ssrContext.redirected = false
|
55 | return
|
56 | }
|
57 | ssrContext.res.writeHead(opts.status, {
|
58 | Location: opts.path
|
59 | })
|
60 | ssrContext.res.end()
|
61 | }
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | export default async (ssrContext) => {
|
69 |
|
70 | ssrContext.redirected = false
|
71 | ssrContext.next = createNext(ssrContext)
|
72 |
|
73 | ssrContext.beforeRenderFns = []
|
74 |
|
75 | ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], <% if (features.fetch) { %>fetch: [], <% } %>error: null<%= (store ? ', state: null' : '') %>, serverRendered: true, routePath: '' }
|
76 |
|
77 | if (process.static && ssrContext.url) {
|
78 | ssrContext.url = ssrContext.url.split('?')[0]
|
79 | }
|
80 |
|
81 | ssrContext.nuxt.config = ssrContext.runtimeConfig.public
|
82 |
|
83 | const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext, { ...ssrContext.runtimeConfig.public, ...ssrContext.runtimeConfig.private })
|
84 | const _app = new Vue(app)
|
85 |
|
86 | ssrContext.nuxt.routePath = app.context.route.path
|
87 |
|
88 | <% if (features.meta) { %>
|
89 |
|
90 | ssrContext.meta = _app.$meta()
|
91 | <% } %>
|
92 | <% if (features.asyncData) { %>
|
93 |
|
94 | ssrContext.asyncData = {}
|
95 | <% } %>
|
96 |
|
97 | const beforeRender = async () => {
|
98 |
|
99 | await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
|
100 | <% if (store) { %>
|
101 | ssrContext.rendered = () => {
|
102 |
|
103 | ssrContext.nuxt.state = store.state
|
104 | <% if (isFullStatic && store) { %>
|
105 |
|
106 | ssrContext.unsetMutationObserver()
|
107 | <% } %>
|
108 | }
|
109 | <% } %>
|
110 | }
|
111 |
|
112 | const renderErrorPage = async () => {
|
113 |
|
114 | if (ssrContext.target === 'static') {
|
115 | ssrContext.nuxt.serverRendered = false
|
116 | }
|
117 | <% if (features.layouts) { %>
|
118 |
|
119 | const layout = (NuxtError.options || NuxtError).layout
|
120 | const errLayout = typeof layout === 'function' ? layout.call(NuxtError, app.context) : layout
|
121 | ssrContext.nuxt.layout = errLayout || 'default'
|
122 | await _app.loadLayout(errLayout)
|
123 | _app.setLayout(errLayout)
|
124 | <% } %>
|
125 | await beforeRender()
|
126 | return _app
|
127 | }
|
128 | const render404Page = () => {
|
129 | app.context.error({ statusCode: 404, path: ssrContext.url, message: '<%= messages.error_404 %>' })
|
130 | return renderErrorPage()
|
131 | }
|
132 |
|
133 | <% if (debug) { %>const s = Date.now()<% } %>
|
134 |
|
135 |
|
136 | const Components = getMatchedComponents(router.match(ssrContext.url))
|
137 |
|
138 | <% if (store) { %>
|
139 | |
140 |
|
141 |
|
142 | if (store._actions && store._actions.nuxtServerInit) {
|
143 | try {
|
144 | await store.dispatch('nuxtServerInit', app.context)
|
145 | } catch (err) {
|
146 | console.debug('Error occurred when calling nuxtServerInit: ', err.message)<%= isTest ? '// eslint-disable-line no-console' : '' %>
|
147 | throw err
|
148 | }
|
149 | }
|
150 |
|
151 | if (ssrContext.redirected) {
|
152 | return noopApp()
|
153 | }
|
154 | if (ssrContext.nuxt.error) {
|
155 | return renderErrorPage()
|
156 | }
|
157 | <% } %>
|
158 |
|
159 | <% if (features.middleware) { %>
|
160 | |
161 |
|
162 |
|
163 | let midd = <%= serialize(router.middleware).replace('middleware(', 'function(') %><%= isTest ? '// eslint-disable-line' : '' %>
|
164 | midd = midd.map((name) => {
|
165 | if (typeof name === 'function') {
|
166 | return name
|
167 | }
|
168 | if (typeof middleware[name] !== 'function') {
|
169 | app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
170 | }
|
171 | return middleware[name]
|
172 | })
|
173 | await middlewareSeries(midd, app.context)
|
174 | // ...If there is a redirect or an error, stop the process
|
175 | if (ssrContext.redirected) {
|
176 | return noopApp()
|
177 | }
|
178 | if (ssrContext.nuxt.error) {
|
179 | return renderErrorPage()
|
180 | }
|
181 | <% } %>
|
182 |
|
183 | <% if (isFullStatic && store) { %>
|
184 | // Record store mutations for full-static after nuxtServerInit and Middleware
|
185 | ssrContext.nuxt.mutations =[]
|
186 | ssrContext.unsetMutationObserver = store.subscribe(m => { ssrContext.nuxt.mutations.push([m.type, m.payload]) })
|
187 | <% } %>
|
188 |
|
189 | <% if (features.layouts) { %>
|
190 | /*
|
191 | ** Set layout
|
192 | */
|
193 | let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
194 | if (typeof layout === 'function') {
|
195 | layout = layout(app.context)
|
196 | }
|
197 | await _app.loadLayout(layout)
|
198 | if (ssrContext.nuxt.error) {
|
199 | return renderErrorPage()
|
200 | }
|
201 | layout = _app.setLayout(layout)
|
202 | ssrContext.nuxt.layout = _app.layoutName
|
203 | <% } %>
|
204 |
|
205 | <% if (features.middleware) { %>
|
206 | /*
|
207 | ** Call middleware (layout + pages)
|
208 | */
|
209 | midd = []
|
210 | <% if (features.layouts) { %>
|
211 | layout = sanitizeComponent(layout)
|
212 | if (layout.options.middleware) {
|
213 | midd = midd.concat(layout.options.middleware)
|
214 | }
|
215 | <% } %>
|
216 | Components.forEach((Component) => {
|
217 | if (Component.options.middleware) {
|
218 | midd = midd.concat(Component.options.middleware)
|
219 | }
|
220 | })
|
221 | midd = midd.map((name) => {
|
222 | if (typeof name === 'function') {
|
223 | return name
|
224 | }
|
225 | if (typeof middleware[name] !== 'function') {
|
226 | app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
227 | }
|
228 | return middleware[name]
|
229 | })
|
230 | await middlewareSeries(midd, app.context)
|
231 | // ...If there is a redirect or an error, stop the process
|
232 | if (ssrContext.redirected) {
|
233 | return noopApp()
|
234 | }
|
235 | if (ssrContext.nuxt.error) {
|
236 | return renderErrorPage()
|
237 | }
|
238 | <% } %>
|
239 |
|
240 | <% if (features.validate) { %>
|
241 | /*
|
242 | ** Call .validate()
|
243 | */
|
244 | let isValid = true
|
245 | try {
|
246 | for (const Component of Components) {
|
247 | if (typeof Component.options.validate !== 'function') {
|
248 | continue
|
249 | }
|
250 |
|
251 | isValid = await Component.options.validate(app.context)
|
252 |
|
253 | if (!isValid) {
|
254 | break
|
255 | }
|
256 | }
|
257 | } catch (validationError) {
|
258 | // ...If .validate() threw an error
|
259 | app.context.error({
|
260 | statusCode: validationError.statusCode || '500',
|
261 | message: validationError.message
|
262 | })
|
263 | return renderErrorPage()
|
264 | }
|
265 |
|
266 | // ...If .validate() returned false
|
267 | if (!isValid) {
|
268 | // Render a 404 error page
|
269 | return render404Page()
|
270 | }
|
271 | <% } %>
|
272 |
|
273 | // If no Components found, returns 404
|
274 | if (!Components.length) {
|
275 | return render404Page()
|
276 | }
|
277 |
|
278 | <% if (features.asyncData || features.fetch) { %>
|
279 | // Call asyncData & fetch hooks on components matched by the route.
|
280 | const asyncDatas = await Promise.all(Components.map((Component) => {
|
281 | const promises = []
|
282 |
|
283 | <% if (features.asyncData) { %>
|
284 | // Call asyncData(context)
|
285 | if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
286 | const promise = promisify(Component.options.asyncData, app.context)
|
287 | promise.then((asyncDataResult) => {
|
288 | ssrContext.asyncData[Component.cid] = asyncDataResult
|
289 | applyAsyncData(Component)
|
290 | return asyncDataResult
|
291 | })
|
292 | promises.push(promise)
|
293 | } else {
|
294 | promises.push(null)
|
295 | }
|
296 | <% } %>
|
297 |
|
298 | <% if (features.fetch) { %>
|
299 | // Call fetch(context)
|
300 | if (Component.options.fetch && Component.options.fetch.length) {
|
301 | promises.push(Component.options.fetch(app.context))
|
302 | } else {
|
303 | promises.push(null)
|
304 | }
|
305 | <% } %>
|
306 |
|
307 | return Promise.all(promises)
|
308 | }))
|
309 |
|
310 | <% if (debug) { %>if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')<% } %>
|
311 |
|
312 | // datas are the first row of each
|
313 | ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
|
314 | <% } %>
|
315 |
|
316 | // ...If there is a redirect or an error, stop the process
|
317 | if (ssrContext.redirected) {
|
318 | return noopApp()
|
319 | }
|
320 | if (ssrContext.nuxt.error) {
|
321 | return renderErrorPage()
|
322 | }
|
323 |
|
324 | // Call beforeNuxtRender methods & add store state
|
325 | await beforeRender()
|
326 |
|
327 | return _app
|
328 | }
|
329 |
|
\ | No newline at end of file |