├── .gitignore ├── .idea ├── modules.xml ├── with-sockets.iml └── workspace.xml ├── .nuxt ├── App.vue ├── client.js ├── components │ ├── no-ssr.js │ ├── nuxt-child.js │ ├── nuxt-error.vue │ ├── nuxt-link.js │ ├── nuxt-loading.vue │ └── nuxt.vue ├── empty.js ├── index.js ├── loading.html ├── middleware.js ├── router.js ├── server.js ├── utils.js └── views │ ├── app.template.html │ └── error.html ├── README.md ├── assets └── css │ └── main.css ├── backpack.config.js ├── components └── Logo.vue ├── io └── index.js ├── layouts └── default.vue ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages ├── chat │ └── index.vue ├── index.vue └── user │ └── _id.vue ├── plugins ├── axios.js └── socket.io.js └── server ├── api ├── index.js └── users.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/with-sockets.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | layout 114 | p 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | true 138 | DEFINITION_ORDER 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 1515447003615 212 | 213 | 214 | 1515447003615 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | -------------------------------------------------------------------------------- /.nuxt/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 90 | 91 | -------------------------------------------------------------------------------- /.nuxt/client.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import middleware from './middleware' 3 | import { createApp, NuxtError } from './index' 4 | import { 5 | applyAsyncData, 6 | sanitizeComponent, 7 | getMatchedComponents, 8 | getMatchedComponentsInstances, 9 | flatMapComponents, 10 | getContext, 11 | middlewareSeries, 12 | promisify, 13 | getLocation, 14 | compile 15 | } from './utils' 16 | 17 | const noopData = () => { return {} } 18 | const noopFetch = () => {} 19 | 20 | // Global shared references 21 | let _lastPaths = [] 22 | let _lastComponentsFiles = [] 23 | let app 24 | let router 25 | 26 | 27 | // Try to rehydrate SSR data from window 28 | const NUXT = window.__NUXT__ || {} 29 | 30 | 31 | // Setup global Vue error handler 32 | const defaultErrorHandler = Vue.config.errorHandler 33 | Vue.config.errorHandler = function (err, vm, info) { 34 | err.statusCode = err.statusCode || err.name || 'Whoops!' 35 | err.message = err.message || err.toString() 36 | 37 | // Show Nuxt Error Page 38 | if(vm && vm.$root && vm.$root.$nuxt && vm.$root.$nuxt.error && info !== 'render function') { 39 | vm.$root.$nuxt.error(err) 40 | } 41 | 42 | // Call other handler if exist 43 | if (typeof defaultErrorHandler === 'function') { 44 | return defaultErrorHandler(...arguments) 45 | } 46 | 47 | // Log to console 48 | if (process.env.NODE_ENV !== 'production') { 49 | console.error(err) 50 | } else { 51 | console.error(err.message) 52 | } 53 | } 54 | 55 | 56 | // Create and mount App 57 | createApp() 58 | .then(mountApp) 59 | .catch(err => { 60 | console.error('[nuxt] Error while initializing app', err) 61 | }) 62 | 63 | function componentOption(component, key, ...args) { 64 | if (!component || !component.options || !component.options[key]) { 65 | return {} 66 | } 67 | const option = component.options[key] 68 | if (typeof option === 'function') { 69 | return option(...args) 70 | } 71 | return option 72 | } 73 | 74 | function mapTransitions(Components, to, from) { 75 | const componentTransitions = component => { 76 | const transition = componentOption(component, 'transition', to, from) || {} 77 | return (typeof transition === 'string' ? { name: transition } : transition) 78 | } 79 | 80 | return Components.map(Component => { 81 | // Clone original object to prevent overrides 82 | const transitions = Object.assign({}, componentTransitions(Component)) 83 | 84 | // Combine transitions & prefer `leave` transitions of 'from' route 85 | if (from && from.matched.length && from.matched[0].components.default) { 86 | const from_transitions = componentTransitions(from.matched[0].components.default) 87 | Object.keys(from_transitions) 88 | .filter(key => from_transitions[key] && key.toLowerCase().indexOf('leave') !== -1) 89 | .forEach(key => { transitions[key] = from_transitions[key] }) 90 | } 91 | 92 | return transitions 93 | }) 94 | } 95 | 96 | async function loadAsyncComponents (to, from, next) { 97 | // Check if route hash changed 98 | const fromPath = from.fullPath.split('#')[0] 99 | const toPath = to.fullPath.split('#')[0] 100 | this._hashChanged = fromPath === toPath 101 | 102 | 103 | if (!this._hashChanged && this.$loading.start) { 104 | this.$loading.start() 105 | } 106 | 107 | 108 | try { 109 | await Promise.all(flatMapComponents(to, (Component, _, match, key) => { 110 | // If component already resolved 111 | if (typeof Component !== 'function' || Component.options) { 112 | const _Component = sanitizeComponent(Component) 113 | match.components[key] = _Component 114 | return _Component 115 | } 116 | 117 | // Resolve component 118 | return Component().then(Component => { 119 | const _Component = sanitizeComponent(Component) 120 | match.components[key] = _Component 121 | return _Component 122 | }) 123 | })) 124 | 125 | next() 126 | } catch (err) { 127 | if (!err) err = {} 128 | const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500 129 | this.error({ statusCode, message: err.message }) 130 | next(false) 131 | } 132 | } 133 | 134 | function applySSRData(Component, ssrData) { 135 | if (NUXT.serverRendered && ssrData) { 136 | applyAsyncData(Component, ssrData) 137 | } 138 | Component._Ctor = Component 139 | return Component 140 | } 141 | 142 | // Get matched components 143 | function resolveComponents(router) { 144 | const path = getLocation(router.options.base, router.options.mode) 145 | 146 | return flatMapComponents(router.match(path), (Component, _, match, key, index) => { 147 | // If component already resolved 148 | if (typeof Component !== 'function' || Component.options) { 149 | const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null) 150 | match.components[key] = _Component 151 | return _Component 152 | } 153 | 154 | // Resolve component 155 | return Component().then(Component => { 156 | const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null) 157 | match.components[key] = _Component 158 | return _Component 159 | }) 160 | }) 161 | } 162 | 163 | function callMiddleware (Components, context, layout) { 164 | let midd = [] 165 | let unknownMiddleware = false 166 | 167 | // If layout is undefined, only call global middleware 168 | if (typeof layout !== 'undefined') { 169 | midd = [] // Exclude global middleware if layout defined (already called before) 170 | if (layout.middleware) { 171 | midd = midd.concat(layout.middleware) 172 | } 173 | Components.forEach(Component => { 174 | if (Component.options.middleware) { 175 | midd = midd.concat(Component.options.middleware) 176 | } 177 | }) 178 | } 179 | 180 | midd = midd.map(name => { 181 | if (typeof middleware[name] !== 'function') { 182 | unknownMiddleware = true 183 | this.error({ statusCode: 500, message: 'Unknown middleware ' + name }) 184 | } 185 | return middleware[name] 186 | }) 187 | 188 | if (unknownMiddleware) return 189 | return middlewareSeries(midd, context) 190 | } 191 | 192 | async function render (to, from, next) { 193 | if (this._hashChanged) return next() 194 | 195 | // nextCalled is true when redirected 196 | let nextCalled = false 197 | const _next = path => { 198 | if(this.$loading.finish) this.$loading.finish() 199 | if (nextCalled) return 200 | nextCalled = true 201 | next(path) 202 | } 203 | 204 | // Update context 205 | const context = getContext({ 206 | to, 207 | from, 208 | 209 | isClient: true, 210 | next: _next.bind(this), 211 | error: this.error.bind(this) 212 | }, app) 213 | this._context = context 214 | this._dateLastError = this.$options._nuxt.dateErr 215 | this._hadError = !!this.$options._nuxt.err 216 | 217 | // Get route's matched components 218 | const Components = getMatchedComponents(to) 219 | 220 | // If no Components matched, generate 404 221 | if (!Components.length) { 222 | // Default layout 223 | await callMiddleware.call(this, Components, context) 224 | if (context._redirected) return 225 | 226 | // Load layout for error page 227 | const layout = await this.loadLayout(typeof NuxtError.layout === 'function' ? NuxtError.layout(context) : NuxtError.layout) 228 | await callMiddleware.call(this, Components, context, layout) 229 | if (context._redirected) return 230 | 231 | this.error({ statusCode: 404, message: 'This page could not be found' }) 232 | return next() 233 | } 234 | 235 | // Update ._data and other properties if hot reloaded 236 | Components.forEach(Component => { 237 | if (Component._Ctor && Component._Ctor.options) { 238 | Component.options.asyncData = Component._Ctor.options.asyncData 239 | Component.options.fetch = Component._Ctor.options.fetch 240 | } 241 | }) 242 | 243 | // Apply transitions 244 | this.setTransitions(mapTransitions(Components, to, from)) 245 | 246 | try { 247 | // Call middleware 248 | await callMiddleware.call(this, Components, context) 249 | if (context._redirected) return 250 | 251 | // Set layout 252 | let layout = Components[0].options.layout 253 | if (typeof layout === 'function') { 254 | layout = layout(context) 255 | } 256 | layout = await this.loadLayout(layout) 257 | 258 | // Call middleware for layout 259 | await callMiddleware.call(this, Components, context, layout) 260 | if (context._redirected) return 261 | 262 | // Call .validate() 263 | let isValid = true 264 | Components.forEach(Component => { 265 | if (!isValid) return 266 | if (typeof Component.options.validate !== 'function') return 267 | isValid = Component.options.validate({ 268 | params: to.params || {}, 269 | query : to.query || {}, 270 | 271 | }) 272 | }) 273 | // ...If .validate() returned false 274 | if (!isValid) { 275 | this.error({ statusCode: 404, message: 'This page could not be found' }) 276 | return next() 277 | } 278 | 279 | // Call asyncData & fetch hooks on components matched by the route. 280 | await Promise.all(Components.map((Component, i) => { 281 | // Check if only children route changed 282 | Component._path = compile(to.matched[i].path)(to.params) 283 | if (!this._hadError && this._isMounted && Component._path === _lastPaths[i] && (i + 1) !== Components.length) { 284 | return Promise.resolve() 285 | } 286 | 287 | let promises = [] 288 | 289 | const hasAsyncData = Component.options.asyncData && typeof Component.options.asyncData === 'function' 290 | const hasFetch = !!Component.options.fetch 291 | const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45 292 | 293 | // Call asyncData(context) 294 | if (hasAsyncData) { 295 | const promise = promisify(Component.options.asyncData, context) 296 | .then(asyncDataResult => { 297 | applyAsyncData(Component, asyncDataResult) 298 | if(this.$loading.increase) this.$loading.increase(loadingIncrease) 299 | }) 300 | promises.push(promise) 301 | } 302 | 303 | // Call fetch(context) 304 | if (hasFetch) { 305 | let p = Component.options.fetch(context) 306 | if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { 307 | p = Promise.resolve(p) 308 | } 309 | p.then(fetchResult => { 310 | if(this.$loading.increase) this.$loading.increase(loadingIncrease) 311 | }) 312 | promises.push(p) 313 | } 314 | 315 | return Promise.all(promises) 316 | })) 317 | 318 | _lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params)) 319 | 320 | if(this.$loading.finish) this.$loading.finish() 321 | 322 | // If not redirected 323 | if (!nextCalled) next() 324 | 325 | } catch (error) { 326 | if (!error) error = {} 327 | _lastPaths = [] 328 | error.statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500 329 | 330 | // Load error layout 331 | let layout = NuxtError.layout 332 | if (typeof layout === 'function') { 333 | layout = layout(context) 334 | } 335 | await this.loadLayout(layout) 336 | 337 | this.error(error) 338 | next(false) 339 | } 340 | } 341 | 342 | // Fix components format in matched, it's due to code-splitting of vue-router 343 | function normalizeComponents (to, ___) { 344 | flatMapComponents(to, (Component, _, match, key) => { 345 | if (typeof Component === 'object' && !Component.options) { 346 | // Updated via vue-router resolveAsyncComponents() 347 | Component = Vue.extend(Component) 348 | Component._Ctor = Component 349 | match.components[key] = Component 350 | } 351 | return Component 352 | }) 353 | } 354 | 355 | // When navigating on a different route but the same component is used, Vue.js 356 | // Will not update the instance data, so we have to update $data ourselves 357 | function fixPrepatch (to, ___) { 358 | if (this._hashChanged) return 359 | 360 | Vue.nextTick(() => { 361 | const instances = getMatchedComponentsInstances(to) 362 | 363 | _lastComponentsFiles = instances.map((instance, i) => { 364 | if (!instance) return ''; 365 | 366 | if (_lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') { 367 | const newData = instance.constructor.options.data.call(instance) 368 | for (let key in newData) { 369 | Vue.set(instance.$data, key, newData[key]) 370 | } 371 | } 372 | 373 | return instance.constructor.options.__file 374 | }) 375 | 376 | // Hide error component if no error 377 | if (this._hadError && this._dateLastError === this.$options._nuxt.dateErr) { 378 | this.error() 379 | } 380 | 381 | // Set layout 382 | let layout = this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout 383 | if (typeof layout === 'function') { 384 | layout = layout(this._context) 385 | } 386 | this.setLayout(layout) 387 | 388 | 389 | // Hot reloading 390 | setTimeout(() => hotReloadAPI(this), 100) 391 | 392 | }) 393 | } 394 | 395 | function nuxtReady (app) { 396 | window._nuxtReadyCbs.forEach((cb) => { 397 | if (typeof cb === 'function') { 398 | cb(app) 399 | } 400 | }) 401 | // Special JSDOM 402 | if (typeof window._onNuxtLoaded === 'function') { 403 | window._onNuxtLoaded(app) 404 | } 405 | // Add router hooks 406 | router.afterEach(function (to, from) { 407 | app.$nuxt.$emit('routeChanged', to, from) 408 | }) 409 | } 410 | 411 | 412 | // Special hot reload with asyncData(context) 413 | function hotReloadAPI (_app) { 414 | if (!module.hot) return 415 | 416 | let $components = [] 417 | let $nuxt = _app.$nuxt 418 | 419 | while ($nuxt && $nuxt.$children && $nuxt.$children.length) { 420 | $nuxt.$children.forEach((child, i) => { 421 | if (child.$vnode.data.nuxtChild) { 422 | let hasAlready = false 423 | $components.forEach(component => { 424 | if (component.$options.__file === child.$options.__file) { 425 | hasAlready = true 426 | } 427 | }) 428 | if (!hasAlready) { 429 | $components.push(child) 430 | } 431 | } 432 | $nuxt = child 433 | }) 434 | } 435 | 436 | $components.forEach(addHotReload.bind(_app)) 437 | } 438 | 439 | function addHotReload ($component, depth) { 440 | if ($component.$vnode.data._hasHotReload) return 441 | $component.$vnode.data._hasHotReload = true 442 | 443 | var _forceUpdate = $component.$forceUpdate.bind($component.$parent) 444 | 445 | $component.$vnode.context.$forceUpdate = () => { 446 | let Components = getMatchedComponents(router.currentRoute) 447 | let Component = Components[depth] 448 | if (!Component) return _forceUpdate() 449 | if (typeof Component === 'object' && !Component.options) { 450 | // Updated via vue-router resolveAsyncComponents() 451 | Component = Vue.extend(Component) 452 | Component._Ctor = Component 453 | } 454 | this.error() 455 | let promises = [] 456 | const next = function (path) { 457 | this.$loading.finish && this.$loading.finish() 458 | router.push(path) 459 | } 460 | let context = getContext({ route: router.currentRoute, isClient: true, isHMR: true, next: next.bind(this), error: this.error }, app) 461 | this.$loading.start && this.$loading.start() 462 | callMiddleware.call(this, Components, context) 463 | .then(() => { 464 | // If layout changed 465 | if (depth !== 0) return Promise.resolve() 466 | let layout = Component.options.layout || 'default' 467 | if (typeof layout === 'function') { 468 | layout = layout(context) 469 | } 470 | if (this.layoutName === layout) return Promise.resolve() 471 | let promise = this.loadLayout(layout) 472 | promise.then(() => { 473 | this.setLayout(layout) 474 | Vue.nextTick(() => hotReloadAPI(this)) 475 | }) 476 | return promise 477 | }) 478 | .then(() => { 479 | return callMiddleware.call(this, Components, context, this.layout) 480 | }) 481 | .then(() => { 482 | // Call asyncData(context) 483 | let pAsyncData = promisify(Component.options.asyncData || noopData, context) 484 | pAsyncData.then((asyncDataResult) => { 485 | applyAsyncData(Component, asyncDataResult) 486 | this.$loading.increase && this.$loading.increase(30) 487 | }) 488 | promises.push(pAsyncData) 489 | // Call fetch() 490 | Component.options.fetch = Component.options.fetch || noopFetch 491 | let pFetch = Component.options.fetch(context) 492 | if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) } 493 | pFetch.then(() => this.$loading.increase && this.$loading.increase(30)) 494 | promises.push(pFetch) 495 | return Promise.all(promises) 496 | }) 497 | .then(() => { 498 | this.$loading.finish && this.$loading.finish() 499 | _forceUpdate() 500 | setTimeout(() => hotReloadAPI(this), 100) 501 | }) 502 | } 503 | } 504 | 505 | 506 | async function mountApp(__app) { 507 | // Set global variables 508 | app = __app.app 509 | router = __app.router 510 | 511 | 512 | // Resolve route components 513 | const Components = await Promise.all(resolveComponents(router)) 514 | 515 | // Create Vue instance 516 | const _app = new Vue(app) 517 | 518 | // Load layout 519 | const layout = NUXT.layout || 'default' 520 | await _app.loadLayout(layout) 521 | _app.setLayout(layout) 522 | 523 | // Mounts Vue app to DOM element 524 | const mountApp = () => { 525 | _app.$mount('#__nuxt') 526 | 527 | // Listen for first Vue update 528 | Vue.nextTick(() => { 529 | // Call window.onNuxtReady callbacks 530 | nuxtReady(_app) 531 | 532 | // Enable hot reloading 533 | hotReloadAPI(_app) 534 | 535 | }) 536 | } 537 | 538 | // Enable transitions 539 | _app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app) 540 | if (Components.length) { 541 | _app.setTransitions(mapTransitions(Components, router.currentRoute)) 542 | _lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params)) 543 | _lastComponentsFiles = Components.map(Component => Component.options.__file) 544 | } 545 | 546 | // Initialize error handler 547 | _app.error = _app.$options._nuxt.error.bind(_app) 548 | _app.$loading = {} // To avoid error while _app.$nuxt does not exist 549 | if (NUXT.error) _app.error(NUXT.error) 550 | 551 | // Add router hooks 552 | router.beforeEach(loadAsyncComponents.bind(_app)) 553 | router.beforeEach(render.bind(_app)) 554 | router.afterEach(normalizeComponents) 555 | router.afterEach(fixPrepatch.bind(_app)) 556 | 557 | // If page already is server rendered 558 | if (NUXT.serverRendered) { 559 | mountApp() 560 | return 561 | } 562 | 563 | render.call(_app, router.currentRoute, router.currentRoute, path => { 564 | if (!path) { 565 | normalizeComponents(router.currentRoute, router.currentRoute) 566 | fixPrepatch.call(_app, router.currentRoute, router.currentRoute) 567 | mountApp() 568 | return 569 | } 570 | 571 | // Push the path and then mount app 572 | let mounted = false 573 | router.afterEach(() => { 574 | if (mounted) return 575 | mounted = true 576 | mountApp() 577 | }) 578 | router.push(path) 579 | }) 580 | } 581 | -------------------------------------------------------------------------------- /.nuxt/components/no-ssr.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** From https://github.com/egoist/vue-no-ssr 3 | ** With the authorization of @egoist 4 | */ 5 | export default { 6 | name: 'no-ssr', 7 | props: ['placeholder'], 8 | data () { 9 | return { 10 | canRender: false 11 | } 12 | }, 13 | mounted () { 14 | this.canRender = true 15 | }, 16 | render (h) { 17 | if (this.canRender) { 18 | if ( 19 | process.env.NODE_ENV === 'development' && 20 | this.$slots.default.length > 1 21 | ) { 22 | throw new Error(' You cannot use multiple child components') 23 | } 24 | return this.$slots.default[0] 25 | } 26 | return h('div', { class: { 'no-ssr-placeholder': true } }, this.placeholder) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.nuxt/components/nuxt-child.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | const transitionsKeys = [ 4 | 'name', 5 | 'mode', 6 | 'appear', 7 | 'css', 8 | 'type', 9 | 'duration', 10 | 'enterClass', 11 | 'leaveClass', 12 | 'appearClass', 13 | 'enterActiveClass', 14 | 'enterActiveClass', 15 | 'leaveActiveClass', 16 | 'appearActiveClass', 17 | 'enterToClass', 18 | 'leaveToClass', 19 | 'appearToClass' 20 | ] 21 | const listenersKeys = [ 22 | 'beforeEnter', 23 | 'enter', 24 | 'afterEnter', 25 | 'enterCancelled', 26 | 'beforeLeave', 27 | 'leave', 28 | 'afterLeave', 29 | 'leaveCancelled', 30 | 'beforeAppear', 31 | 'appear', 32 | 'afterAppear', 33 | 'appearCancelled' 34 | ] 35 | 36 | export default { 37 | name: 'nuxt-child', 38 | functional: true, 39 | render (h, { parent, data }) { 40 | data.nuxtChild = true 41 | const _parent = parent 42 | const transitions = parent.$nuxt.nuxt.transitions 43 | const defaultTransition = parent.$nuxt.nuxt.defaultTransition 44 | let depth = 0 45 | while (parent) { 46 | if (parent.$vnode && parent.$vnode.data.nuxtChild) { 47 | depth++ 48 | } 49 | parent = parent.$parent 50 | } 51 | data.nuxtChildDepth = depth 52 | const transition = transitions[depth] || defaultTransition 53 | let transitionProps = {} 54 | transitionsKeys.forEach((key) => { 55 | if (typeof transition[key] !== 'undefined') { 56 | transitionProps[key] = transition[key] 57 | } 58 | }) 59 | let listeners = {} 60 | listenersKeys.forEach((key) => { 61 | if (typeof transition[key] === 'function') { 62 | listeners[key] = transition[key].bind(_parent) 63 | } 64 | }) 65 | return h('transition', { 66 | props: transitionProps, 67 | on: listeners 68 | }, [ 69 | h('router-view', data) 70 | ]) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.nuxt/components/nuxt-error.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ message }} 7 | 8 | Back to the home page 9 | 10 | 11 | An error occurred while rendering the page. Check developer tools console for details. 12 | 13 | 14 | 15 | Nuxt.js 16 | 17 | 18 | 19 | 20 | 21 | 67 | 68 | 113 | -------------------------------------------------------------------------------- /.nuxt/components/nuxt-link.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export default { 4 | name: 'nuxt-link', 5 | functional: true, 6 | render (h, { data, children }) { 7 | return h('router-link', data, children) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.nuxt/components/nuxt-loading.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 89 | 90 | 104 | -------------------------------------------------------------------------------- /.nuxt/components/nuxt.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 33 | -------------------------------------------------------------------------------- /.nuxt/empty.js: -------------------------------------------------------------------------------- 1 | // This file is intentionally left empty for noop aliases 2 | -------------------------------------------------------------------------------- /.nuxt/index.js: -------------------------------------------------------------------------------- 1 | import 'es6-promise/auto' 2 | import Vue from 'vue' 3 | import Meta from 'vue-meta' 4 | import { createRouter } from './router.js' 5 | import NoSSR from './components/no-ssr.js' 6 | import NuxtChild from './components/nuxt-child.js' 7 | import NuxtLink from './components/nuxt-link.js' 8 | import NuxtError from './components/nuxt-error.vue' 9 | import Nuxt from './components/nuxt.vue' 10 | import App from './App.vue' 11 | import { getContext, getLocation } from './utils' 12 | 13 | 14 | 15 | // Component: 16 | Vue.component(NoSSR.name, NoSSR) 17 | 18 | // Component: 19 | Vue.component(NuxtChild.name, NuxtChild) 20 | 21 | // Component: 22 | Vue.component(NuxtLink.name, NuxtLink) 23 | 24 | // Component: ` 25 | Vue.component(Nuxt.name, Nuxt) 26 | 27 | // vue-meta configuration 28 | Vue.use(Meta, { 29 | keyName: 'head', // the component option name that vue-meta looks for meta info on. 30 | attribute: 'data-n-head', // the attribute name vue-meta adds to the tags it observes 31 | ssrAttribute: 'data-n-head-ssr', // the attribute name that lets vue-meta know that meta info has already been server-rendered 32 | tagIDKeyName: 'hid' // the property name that vue-meta uses to determine whether to overwrite or append a tag 33 | }) 34 | 35 | const defaultTransition = {"name":"page","mode":"out-in","appear":false,"appearClass":"appear","appearActiveClass":"appear-active","appearToClass":"appear-to"} 36 | 37 | async function createApp (ssrContext) { 38 | const router = createRouter() 39 | 40 | 41 | 42 | // Create Root instance 43 | // here we inject the router and store to all child components, 44 | // making them available everywhere as `this.$router` and `this.$store`. 45 | const app = { 46 | router, 47 | 48 | _nuxt: { 49 | defaultTransition, 50 | transitions: [ defaultTransition ], 51 | setTransitions (transitions) { 52 | if (!Array.isArray(transitions)) { 53 | transitions = [ transitions ] 54 | } 55 | transitions = transitions.map((transition) => { 56 | if (!transition) { 57 | transition = defaultTransition 58 | } else if (typeof transition === 'string') { 59 | transition = Object.assign({}, defaultTransition, { name: transition }) 60 | } else { 61 | transition = Object.assign({}, defaultTransition, transition) 62 | } 63 | return transition 64 | }) 65 | this.$options._nuxt.transitions = transitions 66 | return transitions 67 | }, 68 | err: null, 69 | dateErr: null, 70 | error (err) { 71 | err = err || null 72 | if (typeof err === 'string') { 73 | err = { statusCode: 500, message: err } 74 | } 75 | const _nuxt = this._nuxt || this.$options._nuxt 76 | _nuxt.dateErr = Date.now() 77 | _nuxt.err = err 78 | return err 79 | } 80 | }, 81 | ...App 82 | } 83 | 84 | const next = ssrContext ? ssrContext.next : location => app.router.push(location) 85 | let route 86 | if (ssrContext) { 87 | route = router.resolve(ssrContext.url).route 88 | } else { 89 | const path = getLocation(router.options.base) 90 | route = router.resolve(path).route 91 | } 92 | const ctx = getContext({ 93 | isServer: !!ssrContext, 94 | isClient: !ssrContext, 95 | route, 96 | next, 97 | error: app._nuxt.error.bind(app), 98 | 99 | req: ssrContext ? ssrContext.req : undefined, 100 | res: ssrContext ? ssrContext.res : undefined, 101 | beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined 102 | }, app) 103 | 104 | const inject = function (key, value) { 105 | if (!key) throw new Error('inject(key, value) has no key provided') 106 | if (!value) throw new Error('inject(key, value) has no value provided') 107 | key = '$' + key 108 | // Add into app 109 | app[key] = value 110 | // Add into vm 111 | Vue.use(() => { 112 | const installKey = '__nuxt_' + key + '_installed__' 113 | if (Vue[installKey]) return 114 | Vue[installKey] = true 115 | if (!Vue.prototype.hasOwnProperty(key)) { 116 | Object.defineProperty(Vue.prototype, key, { 117 | get () { 118 | return this.$root.$options[key] 119 | } 120 | }) 121 | } 122 | }) 123 | 124 | } 125 | 126 | 127 | 128 | 129 | 130 | 131 | if (process.server && ssrContext && ssrContext.url) { 132 | await new Promise((resolve, reject) => { 133 | router.push(ssrContext.url, resolve, reject) 134 | }) 135 | } 136 | 137 | return { 138 | app, 139 | router, 140 | 141 | } 142 | } 143 | 144 | export { createApp, NuxtError } 145 | -------------------------------------------------------------------------------- /.nuxt/loading.html: -------------------------------------------------------------------------------- 1 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /.nuxt/middleware.js: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /.nuxt/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | const _6ca9121f = () => import('../pages/index.vue' /* webpackChunkName: "pages/index" */).then(m => m.default || m) 7 | const _ef507444 = () => import('../pages/chat/index.vue' /* webpackChunkName: "pages/chat/index" */).then(m => m.default || m) 8 | const _1927dd39 = () => import('../pages/user/_id.vue' /* webpackChunkName: "pages/user/_id" */).then(m => m.default || m) 9 | 10 | 11 | 12 | const scrollBehavior = (to, from, savedPosition) => { 13 | // SavedPosition is only available for popstate navigations. 14 | if (savedPosition) { 15 | return savedPosition 16 | } else { 17 | let position = {} 18 | // If no children detected 19 | if (to.matched.length < 2) { 20 | // Scroll to the top of the page 21 | position = { x: 0, y: 0 } 22 | } 23 | else if (to.matched.some((r) => r.components.default.options.scrollToTop)) { 24 | // If one of the children has scrollToTop option set to true 25 | position = { x: 0, y: 0 } 26 | } 27 | // If link has anchor, scroll to anchor by returning the selector 28 | if (to.hash) { 29 | position = { selector: to.hash } 30 | } 31 | return position 32 | } 33 | } 34 | 35 | 36 | export function createRouter () { 37 | return new Router({ 38 | mode: 'history', 39 | base: '/', 40 | linkActiveClass: 'nuxt-link-active', 41 | linkExactActiveClass: 'nuxt-link-exact-active', 42 | scrollBehavior, 43 | routes: [ 44 | { 45 | path: "/", 46 | component: _6ca9121f, 47 | name: "index" 48 | }, 49 | { 50 | path: "/chat", 51 | component: _ef507444, 52 | name: "chat" 53 | }, 54 | { 55 | path: "/user/:id?", 56 | component: _1927dd39, 57 | name: "user-id" 58 | } 59 | ], 60 | fallback: false 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /.nuxt/server.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import clone from 'clone' 3 | import { stringify } from 'querystring' 4 | import { omit } from 'lodash' 5 | import middleware from './middleware' 6 | import { createApp, NuxtError } from './index' 7 | import { applyAsyncData, sanitizeComponent, getMatchedComponents, getContext, middlewareSeries, promisify, urlJoin } from './utils' 8 | 9 | const debug = require('debug')('nuxt:render') 10 | debug.color = 4 // force blue color 11 | 12 | const isDev = true 13 | 14 | const noopApp = () => new Vue({ render: (h) => h('div') }) 15 | 16 | const createNext = context => opts => { 17 | context.redirected = opts 18 | // If nuxt generate 19 | if (!context.res) { 20 | context.nuxt.serverRendered = false 21 | return 22 | } 23 | opts.query = stringify(opts.query) 24 | opts.path = opts.path + (opts.query ? '?' + opts.query : '') 25 | if (opts.path.indexOf('http') !== 0 && ('/' !== '/' && opts.path.indexOf('/') !== 0)) { 26 | opts.path = urlJoin('/', opts.path) 27 | } 28 | // Avoid loop redirect 29 | if (opts.path === context.url) { 30 | context.redirected = false 31 | return 32 | } 33 | context.res.writeHead(opts.status, { 34 | 'Location': opts.path 35 | }) 36 | context.res.end() 37 | } 38 | 39 | // This exported function will be called by `bundleRenderer`. 40 | // This is where we perform data-prefetching to determine the 41 | // state of our application before actually rendering it. 42 | // Since data fetching is async, this function is expected to 43 | // return a Promise that resolves to the app instance. 44 | export default async context => { 45 | // Create context.next for simulate next() of beforeEach() when wanted to redirect 46 | context.redirected = false 47 | context.next = createNext(context) 48 | context.beforeRenderFns = [] 49 | 50 | const { app, router } = await createApp(context) 51 | const _app = new Vue(app) 52 | 53 | 54 | 55 | // Add route to the context 56 | context.route = router.currentRoute 57 | 58 | // Nuxt object 59 | context.nuxt = { layout: 'default', data: [], error: null, serverRendered: true } 60 | 61 | // Add meta infos 62 | context.meta = _app.$meta() 63 | 64 | // Error function 65 | context.error = _app.$options._nuxt.error.bind(_app) 66 | 67 | // Keep asyncData for each matched component in context 68 | context.asyncData = {} 69 | 70 | // Create shared ctx 71 | const ctx = getContext(context, app) 72 | 73 | const s = isDev && Date.now() 74 | 75 | // Resolve components 76 | let Components = [] 77 | try { 78 | Components = await Promise.all(getMatchedComponents(router.match(context.url)).map(Component => { 79 | if (typeof Component !== 'function' || Component.cid) { 80 | return sanitizeComponent(Component) 81 | } 82 | return Component().then(Component => sanitizeComponent(Component)) 83 | })) 84 | } catch (err) { 85 | // Throw back error to renderRoute() 86 | throw err 87 | } 88 | 89 | 90 | 91 | // Call global middleware (nuxt.config.js) 92 | let midd = [] 93 | midd = midd.map((name) => { 94 | if (typeof middleware[name] !== 'function') { 95 | context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name }) 96 | } 97 | return middleware[name] 98 | }) 99 | if (!context.nuxt.error) { 100 | await middlewareSeries(midd, ctx) 101 | } 102 | // ...If there is a redirect 103 | if (context.redirected) return noopApp() 104 | 105 | // Set layout 106 | let layout = Components.length ? Components[0].options.layout : NuxtError.layout 107 | if (typeof layout === 'function') layout = layout(ctx) 108 | await _app.loadLayout(layout) 109 | layout = _app.setLayout(layout) 110 | // ...Set layout to __NUXT__ 111 | context.nuxt.layout = _app.layoutName 112 | 113 | // Call middleware (layout + pages) 114 | if (!context.nuxt.error) { 115 | midd = [] 116 | if (layout.middleware) midd = midd.concat(layout.middleware) 117 | Components.forEach((Component) => { 118 | if (Component.options.middleware) { 119 | midd = midd.concat(Component.options.middleware) 120 | } 121 | }) 122 | midd = midd.map((name) => { 123 | if (typeof middleware[name] !== 'function') { 124 | context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name }) 125 | } 126 | return middleware[name] 127 | }) 128 | 129 | await middlewareSeries(midd, ctx) 130 | 131 | // If there is a redirect 132 | if (context.redirected) return noopApp() 133 | } 134 | 135 | // Call .validate() 136 | let isValid = true 137 | Components.forEach((Component) => { 138 | if (!isValid) return 139 | if (typeof Component.options.validate !== 'function') return 140 | isValid = Component.options.validate({ 141 | params: context.route.params || {}, 142 | query: context.route.query || {}, 143 | 144 | }) 145 | }) 146 | // ...If .validate() returned false 147 | if (!isValid) { 148 | // Don't server-render the page in generate mode 149 | if (context._generate) { 150 | context.nuxt.serverRendered = false 151 | } 152 | // Call the 404 error by making the Components array empty 153 | Components = [] 154 | } 155 | 156 | // Call asyncData & fetch hooks on components matched by the route. 157 | let asyncDatas = await Promise.all(Components.map(Component => { 158 | let promises = [] 159 | 160 | // Call asyncData(context) 161 | if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { 162 | let promise = promisify(Component.options.asyncData, ctx) 163 | promise.then(asyncDataResult => { 164 | context.asyncData[Component.cid] = asyncDataResult 165 | applyAsyncData(Component) 166 | return asyncDataResult 167 | }) 168 | promises.push(promise) 169 | } else { 170 | promises.push(null) 171 | } 172 | 173 | // Call fetch(context) 174 | if (Component.options.fetch) { 175 | promises.push(Component.options.fetch(ctx)) 176 | } 177 | else { 178 | promises.push(null) 179 | } 180 | 181 | return Promise.all(promises) 182 | })) 183 | 184 | // If no Components found, returns 404 185 | if (!Components.length) { 186 | context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found' }) 187 | } 188 | 189 | if (asyncDatas.length) debug('Data fetching ' + context.url + ': ' + (Date.now() - s) + 'ms') 190 | 191 | // datas are the first row of each 192 | context.nuxt.data = asyncDatas.map(r => r[0] || {}) 193 | 194 | // If an error occured in the execution 195 | if (_app.$options._nuxt.err) { 196 | context.nuxt.error = _app.$options._nuxt.err 197 | } 198 | 199 | 200 | 201 | await Promise.all(context.beforeRenderFns.map((fn) => promisify(fn, { Components, nuxtState: context.nuxt }))) 202 | 203 | // If no error, return main app 204 | if (!context.nuxt.error) { 205 | return _app 206 | } 207 | 208 | // Load layout for error page 209 | layout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(ctx) : NuxtError.layout) 210 | context.nuxt.layout = layout || '' 211 | await _app.loadLayout(layout) 212 | _app.setLayout(layout) 213 | 214 | return _app 215 | } 216 | -------------------------------------------------------------------------------- /.nuxt/utils.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | const noopData = () => ({}) 4 | 5 | // window.onNuxtReady(() => console.log('Ready')) hook 6 | // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading) 7 | if (process.browser) { 8 | window._nuxtReadyCbs = [] 9 | window.onNuxtReady = function (cb) { 10 | window._nuxtReadyCbs.push(cb) 11 | } 12 | } 13 | 14 | export function applyAsyncData (Component, asyncData) { 15 | const ComponentData = Component.options.data || noopData 16 | // Prevent calling this method for each request on SSR context 17 | if (!asyncData && Component.options.hasAsyncData) { 18 | return 19 | } 20 | Component.options.hasAsyncData = true 21 | Component.options.data = function () { 22 | const data = ComponentData.call(this) 23 | if (this.$ssrContext) { 24 | asyncData = this.$ssrContext.asyncData[Component.cid] 25 | } 26 | return { ...data, ...asyncData } 27 | } 28 | if (Component._Ctor && Component._Ctor.options) { 29 | Component._Ctor.options.data = Component.options.data 30 | } 31 | } 32 | 33 | export function sanitizeComponent (Component) { 34 | if (!Component.options) { 35 | Component = Vue.extend(Component) // fix issue #6 36 | Component._Ctor = Component 37 | } else { 38 | Component._Ctor = Component 39 | Component.extendOptions = Component.options 40 | } 41 | // For debugging purpose 42 | if (!Component.options.name && Component.options.__file) { 43 | Component.options.name = Component.options.__file 44 | } 45 | return Component 46 | } 47 | 48 | export function getMatchedComponents (route) { 49 | return [].concat.apply([], route.matched.map(function (m) { 50 | return Object.keys(m.components).map(function (key) { 51 | return m.components[key] 52 | }) 53 | })) 54 | } 55 | 56 | export function getMatchedComponentsInstances (route) { 57 | return [].concat.apply([], route.matched.map(function (m) { 58 | return Object.keys(m.instances).map(function (key) { 59 | return m.instances[key] 60 | }) 61 | })) 62 | } 63 | 64 | export function flatMapComponents (route, fn) { 65 | return Array.prototype.concat.apply([], route.matched.map(function (m, index) { 66 | return Object.keys(m.components).map(function (key) { 67 | return fn(m.components[key], m.instances[key], m, key, index) 68 | }) 69 | })) 70 | } 71 | 72 | export function getContext (context, app) { 73 | let ctx = { 74 | isServer: !!context.isServer, 75 | isClient: !!context.isClient, 76 | isStatic: process.static, 77 | isDev: true, 78 | isHMR: context.isHMR || false, 79 | app: app, 80 | 81 | route: (context.to ? context.to : context.route), 82 | payload: context.payload, 83 | error: context.error, 84 | base: '/', 85 | env: {"WS_URL":"http://localhost:3000"} 86 | } 87 | const next = context.next 88 | ctx.params = ctx.route.params || {} 89 | ctx.query = ctx.route.query || {} 90 | ctx.redirect = function (status, path, query) { 91 | if (!status) return 92 | ctx._redirected = true // Used in middleware 93 | // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' }) 94 | if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) { 95 | query = path || {} 96 | path = status 97 | status = 302 98 | } 99 | next({ 100 | path: path, 101 | query: query, 102 | status: status 103 | }) 104 | } 105 | if (context.req) ctx.req = context.req 106 | if (context.res) ctx.res = context.res 107 | if (context.from) ctx.from = context.from 108 | if (ctx.isServer && context.beforeRenderFns) { 109 | ctx.beforeNuxtRender = (fn) => context.beforeRenderFns.push(fn) 110 | } 111 | if (ctx.isClient && window.__NUXT__) { 112 | ctx.nuxtState = window.__NUXT__ 113 | } 114 | return ctx 115 | } 116 | 117 | export function middlewareSeries (promises, context) { 118 | if (!promises.length || context._redirected) { 119 | return Promise.resolve() 120 | } 121 | return promisify(promises[0], context) 122 | .then(() => { 123 | return middlewareSeries(promises.slice(1), context) 124 | }) 125 | } 126 | 127 | export function promisify (fn, context) { 128 | let promise 129 | if (fn.length === 2) { 130 | // fn(context, callback) 131 | promise = new Promise((resolve) => { 132 | fn(context, function (err, data) { 133 | if (err) { 134 | context.error(err) 135 | } 136 | data = data || {} 137 | resolve(data) 138 | }) 139 | }) 140 | } else { 141 | promise = fn(context) 142 | } 143 | if (!promise || (!(promise instanceof Promise) && (typeof promise.then !== 'function'))) { 144 | promise = Promise.resolve(promise) 145 | } 146 | return promise 147 | } 148 | 149 | // Imported from vue-router 150 | export function getLocation (base, mode) { 151 | var path = window.location.pathname 152 | if (mode === 'hash') { 153 | return window.location.hash.replace(/^#\//, '') 154 | } 155 | if (base && path.indexOf(base) === 0) { 156 | path = path.slice(base.length) 157 | } 158 | return (path || '/') + window.location.search + window.location.hash 159 | } 160 | 161 | export function urlJoin () { 162 | return [].slice.call(arguments).join('/').replace(/\/+/g, '/') 163 | } 164 | 165 | // Imported from path-to-regexp 166 | 167 | /** 168 | * Compile a string to a template function for the path. 169 | * 170 | * @param {string} str 171 | * @param {Object=} options 172 | * @return {!function(Object=, Object=)} 173 | */ 174 | export function compile (str, options) { 175 | return tokensToFunction(parse(str, options)) 176 | } 177 | 178 | /** 179 | * The main path matching regexp utility. 180 | * 181 | * @type {RegExp} 182 | */ 183 | const PATH_REGEXP = new RegExp([ 184 | // Match escaped characters that would otherwise appear in future matches. 185 | // This allows the user to escape special characters that won't transform. 186 | '(\\\\.)', 187 | // Match Express-style parameters and un-named parameters with a prefix 188 | // and optional suffixes. Matches appear as: 189 | // 190 | // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] 191 | // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] 192 | // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] 193 | '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' 194 | ].join('|'), 'g') 195 | 196 | /** 197 | * Parse a string for the raw tokens. 198 | * 199 | * @param {string} str 200 | * @param {Object=} options 201 | * @return {!Array} 202 | */ 203 | function parse (str, options) { 204 | var tokens = [] 205 | var key = 0 206 | var index = 0 207 | var path = '' 208 | var defaultDelimiter = options && options.delimiter || '/' 209 | var res 210 | 211 | while ((res = PATH_REGEXP.exec(str)) != null) { 212 | var m = res[0] 213 | var escaped = res[1] 214 | var offset = res.index 215 | path += str.slice(index, offset) 216 | index = offset + m.length 217 | 218 | // Ignore already escaped sequences. 219 | if (escaped) { 220 | path += escaped[1] 221 | continue 222 | } 223 | 224 | var next = str[index] 225 | var prefix = res[2] 226 | var name = res[3] 227 | var capture = res[4] 228 | var group = res[5] 229 | var modifier = res[6] 230 | var asterisk = res[7] 231 | 232 | // Push the current path onto the tokens. 233 | if (path) { 234 | tokens.push(path) 235 | path = '' 236 | } 237 | 238 | var partial = prefix != null && next != null && next !== prefix 239 | var repeat = modifier === '+' || modifier === '*' 240 | var optional = modifier === '?' || modifier === '*' 241 | var delimiter = res[2] || defaultDelimiter 242 | var pattern = capture || group 243 | 244 | tokens.push({ 245 | name: name || key++, 246 | prefix: prefix || '', 247 | delimiter: delimiter, 248 | optional: optional, 249 | repeat: repeat, 250 | partial: partial, 251 | asterisk: !!asterisk, 252 | pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') 253 | }) 254 | } 255 | 256 | // Match any characters still remaining. 257 | if (index < str.length) { 258 | path += str.substr(index) 259 | } 260 | 261 | // If the path exists, push it onto the end. 262 | if (path) { 263 | tokens.push(path) 264 | } 265 | 266 | return tokens 267 | } 268 | 269 | /** 270 | * Prettier encoding of URI path segments. 271 | * 272 | * @param {string} 273 | * @return {string} 274 | */ 275 | function encodeURIComponentPretty (str) { 276 | return encodeURI(str).replace(/[\/?#]/g, function (c) { 277 | return '%' + c.charCodeAt(0).toString(16).toUpperCase() 278 | }) 279 | } 280 | 281 | /** 282 | * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. 283 | * 284 | * @param {string} 285 | * @return {string} 286 | */ 287 | function encodeAsterisk (str) { 288 | return encodeURI(str).replace(/[?#]/g, function (c) { 289 | return '%' + c.charCodeAt(0).toString(16).toUpperCase() 290 | }) 291 | } 292 | 293 | /** 294 | * Expose a method for transforming tokens into the path function. 295 | */ 296 | function tokensToFunction (tokens) { 297 | // Compile all the tokens into regexps. 298 | var matches = new Array(tokens.length) 299 | 300 | // Compile all the patterns before compilation. 301 | for (var i = 0; i < tokens.length; i++) { 302 | if (typeof tokens[i] === 'object') { 303 | matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$') 304 | } 305 | } 306 | 307 | return function (obj, opts) { 308 | var path = '' 309 | var data = obj || {} 310 | var options = opts || {} 311 | var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent 312 | 313 | for (var i = 0; i < tokens.length; i++) { 314 | var token = tokens[i] 315 | 316 | if (typeof token === 'string') { 317 | path += token 318 | 319 | continue 320 | } 321 | 322 | var value = data[token.name] 323 | var segment 324 | 325 | if (value == null) { 326 | if (token.optional) { 327 | // Prepend partial segment prefixes. 328 | if (token.partial) { 329 | path += token.prefix 330 | } 331 | 332 | continue 333 | } else { 334 | throw new TypeError('Expected "' + token.name + '" to be defined') 335 | } 336 | } 337 | 338 | if (Array.isArray(value)) { 339 | if (!token.repeat) { 340 | throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') 341 | } 342 | 343 | if (value.length === 0) { 344 | if (token.optional) { 345 | continue 346 | } else { 347 | throw new TypeError('Expected "' + token.name + '" to not be empty') 348 | } 349 | } 350 | 351 | for (var j = 0; j < value.length; j++) { 352 | segment = encode(value[j]) 353 | 354 | if (!matches[i].test(segment)) { 355 | throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') 356 | } 357 | 358 | path += (j === 0 ? token.prefix : token.delimiter) + segment 359 | } 360 | 361 | continue 362 | } 363 | 364 | segment = token.asterisk ? encodeAsterisk(value) : encode(value) 365 | 366 | if (!matches[i].test(segment)) { 367 | throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') 368 | } 369 | 370 | path += token.prefix + segment 371 | } 372 | 373 | return path 374 | } 375 | } 376 | 377 | /** 378 | * Escape a regular expression string. 379 | * 380 | * @param {string} str 381 | * @return {string} 382 | */ 383 | function escapeString (str) { 384 | return str.replace(/([.+*?=^!:()[\]|\/\\])/g, '\\$1') 385 | } 386 | 387 | /** 388 | * Escape the capturing group by escaping special characters and meaning. 389 | * 390 | * @param {string} group 391 | * @return {string} 392 | */ 393 | function escapeGroup (group) { 394 | return group.replace(/([=!:$\/()])/g, '\\$1') 395 | } 396 | -------------------------------------------------------------------------------- /.nuxt/views/app.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ HEAD }} 5 | 6 | 7 | {{ APP }} 8 | 9 | 10 | -------------------------------------------------------------------------------- /.nuxt/views/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Server error 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | Server error 16 | {{ message }} 17 | 18 | 19 | Nuxt.js 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt.js + Express + Socket.io Template 2 | 3 | > A Nuxt.js project with Express and Socket.io 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ npm install # Or yarn install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ npm run dev 13 | 14 | # build for production and launch server 15 | $ npm start 16 | ``` 17 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexitaylor/Nuxt-Express-Socket.io-Template/faa40b67f9d5a4b0e184dffa57de52f82bfb16e4/assets/css/main.css -------------------------------------------------------------------------------- /backpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: (config, options, webpack) => { 3 | config.entry.main = './server/index.js' 4 | return config 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /components/Logo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 80 | -------------------------------------------------------------------------------- /io/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | const server = require('http').createServer(this.nuxt.renderer.app) 3 | const io = require('socket.io')(server) 4 | 5 | // overwrite nuxt.listen() 6 | this.nuxt.listen = (port, host) => new Promise((resolve) => server.listen(port || 3000, host || 'localhost', resolve)) 7 | // close this server on 'close' event 8 | this.nuxt.plugin('close', () => new Promise((resolve) => server.close(resolve))) 9 | 10 | // Add `socket.io-client` in vendor 11 | this.addVendor('socket.io-client') 12 | 13 | // Add socket.io events 14 | let messages = [] 15 | io.on('connection', (socket) => { 16 | socket.on('last-messages', function (fn) { 17 | fn(messages.slice(-50)) 18 | }) 19 | socket.on('send-message', function (message) { 20 | messages.push(message) 21 | socket.broadcast.emit('new-message', message) 22 | }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 40 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | head: { 3 | meta: [ 4 | { charset: 'utf-8' }, 5 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 6 | { hid: 'description', name: 'description', content: 'Nuxt.js project' } 7 | ], 8 | link: [ 9 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 10 | ] 11 | }, 12 | /* 13 | ** Global CSS 14 | */ 15 | css: ['~/assets/css/main.css'], 16 | modules: ['~/io'], 17 | env: { 18 | WS_URL: process.env.WS_URL || 'http://localhost:3000' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-socketio", 3 | "scripts": { 4 | "dev": "nodemon -w ./server/server.js -w nuxt.config.js ./server/server.js", 5 | "build": "nuxt build", 6 | "start": "NODE_ENV=production node ./server/server.js" 7 | }, 8 | "dependencies": { 9 | "express": "^4.14.0", 10 | "nuxt": "latest", 11 | "socket.io": "^1.7.2", 12 | "socket.io-client": "^1.7.2" 13 | }, 14 | "devDependencies": { 15 | "nodemon": "^1.11.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pages/chat/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ message.date.split('T')[1].slice(0, -2) }}: {{ message.text }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 63 | 64 | 156 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | cryptomarket 7 | 8 | 9 | Nuxt.js project 10 | 11 | 12 | CHAT 13 | 14 | 15 | 16 | {{ user.name }} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 44 | 45 | 90 | -------------------------------------------------------------------------------- /pages/user/_id.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | User 7 | 8 | 9 | {{ user.name }} 10 | 11 | 12 | 13 | Users 14 | 15 | 16 | 17 | 18 | 19 | 20 | 45 | 46 | 62 | -------------------------------------------------------------------------------- /plugins/axios.js: -------------------------------------------------------------------------------- 1 | import * as axios from 'axios' 2 | 3 | let options = {} 4 | // The server-side needs a full url to works 5 | if (process.server) { 6 | options.baseURL = `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}` 7 | } 8 | 9 | export default axios.create(options) 10 | -------------------------------------------------------------------------------- /plugins/socket.io.js: -------------------------------------------------------------------------------- 1 | import io from 'socket.io-client' 2 | const socket = io(process.env.WS_URL) 3 | 4 | export default socket 5 | -------------------------------------------------------------------------------- /server/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _express = require('express'); 4 | 5 | var _users = require('./users'); 6 | 7 | var router = _express.Router(); 8 | 9 | // Add USERS Routes 10 | router.use(_users); 11 | 12 | module.exports = router; -------------------------------------------------------------------------------- /server/api/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _express = require('express'); 4 | 5 | var router = _express.Router(); 6 | 7 | // Mock Users 8 | const users = [ 9 | { name: 'Alexandre' }, 10 | { name: 'Pooya' }, 11 | { name: 'Sébastien' }, 12 | ]; 13 | 14 | /* GET users listing. */ 15 | router.get('/users', function (req, res, next) { 16 | res.json(users) 17 | }); 18 | 19 | /* GET user by ID. */ 20 | router.get('/users/:id', function (req, res, next) { 21 | const id = parseInt(req.params.id) 22 | if (id >= 0 && id < users.length) { 23 | res.json(users[id]) 24 | } else { 25 | res.sendStatus(404) 26 | } 27 | }) 28 | 29 | module.exports = router; 30 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const { Nuxt, Builder } = require('nuxt') 2 | const app = require('express')() 3 | const server = require('http').createServer(app) 4 | const io = require('socket.io')(server) 5 | const port = process.env.PORT || 3000 6 | const isProd = process.env.NODE_ENV === 'production' 7 | 8 | const api = require('./api'); 9 | 10 | // Import API Routes 11 | app.use('/api', api); 12 | 13 | // We instantiate Nuxt.js with the options 14 | var config = require('../nuxt.config.js') 15 | config.dev = !isProd 16 | 17 | const nuxt = new Nuxt(config) 18 | // Start build process in dev mode 19 | if (config.dev) { 20 | const builder = new Builder(nuxt) 21 | builder.build() 22 | } 23 | app.use(nuxt.render) 24 | 25 | // Listen the server 26 | server.listen(port, '0.0.0.0') 27 | console.log('Server listening on localhost:' + port) // eslint-disable-line no-console 28 | 29 | // Socket.io 30 | var messages = [] 31 | io.on('connection', (socket) => { 32 | socket.on('last-messages', function (fn) { 33 | fn(messages.slice(-50)) 34 | }) 35 | socket.on('send-message', function (message) { 36 | messages.push(message) 37 | socket.broadcast.emit('new-message', message) 38 | }) 39 | }) 40 | --------------------------------------------------------------------------------
8 | Back to the home page 9 |
An error occurred while rendering the page. Check developer tools console for details.