123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- const { resolve } = require('path')
- const { readFileSync } = require('fs')
- const connect = require('connect')
- const serveStatic = require('serve-static')
- const getPort = require('get-port-please')
- const { json, end, header } = require('node-res')
- const { parseStack } = require('./utils')
- const SSE = require('./sse')
- class LoadingUI {
- constructor (options) {
- this.options = options
- this._lastBroadcast = 0
- this.states = []
- this.allDone = true
- this.hasErrors = false
- this.serveIndex = this.serveIndex.bind(this)
- this._init()
- }
- _init () {
- // Create a connect middleware stack
- this.app = connect()
- // Create an SSE handler instance
- this.sse = new SSE()
- // Fix CORS
- this.app.use((req, res, next) => {
- res.setHeader('Access-Control-Allow-Origin', '*')
- next()
- })
- // Subscribe to SSR channel
- this.app.use('/sse', (req, res) => this.sse.subscribe(req, res))
- // Serve state with JSON
- this.app.use('/json', (req, res) => json(req, res, this.state))
- // Load indexTemplate
- const distPath = resolve(__dirname, '../app-dist')
- this.indexTemplate = readFileSync(resolve(distPath, 'index.html'), 'utf-8')
- // Serve assets
- this.app.use('/assets', serveStatic(resolve(distPath, 'assets')))
- }
- async initAlt ({ url }) {
- if (this._server || this.options.baseURLAlt) {
- return
- }
- // Redirect users directly open this port
- this.app.use('/', (req, res) => {
- res.setHeader('Location', url)
- res.statusCode = 307
- res.end(url)
- })
- // Start listening on alternative port
- const port = await getPort({ random: true, name: 'nuxt_loading' })
- return new Promise((resolve, reject) => {
- this._server = this.app.listen(port, (err) => {
- if (err) { return reject(err) }
- this.options.baseURLAlt = `http://localhost:${port}`
- resolve()
- })
- })
- }
- close () {
- if (this._server) {
- return new Promise((resolve, reject) => {
- this._server.close((err) => {
- if (err) {
- return reject(err)
- }
- resolve()
- })
- })
- }
- }
- get state () {
- return {
- error: this.error,
- states: this.states,
- allDone: this.allDone,
- hasErrors: this.hasErrors
- }
- }
- setStates (states) {
- this.clearError()
- this.states = states
- this.allDone = this.states.every(state => state.progress === 0 || state.progress === 100)
- this.hasErrors = this.states.some(state => state.hasErrors === true)
- this.broadcastState()
- }
- setError (error) {
- this.clearStates(true)
- this.error = {
- description: error.toString(),
- stack: parseStack(error.stack).join('\n')
- }
- this.broadcastState()
- }
- clearError () {
- this.error = undefined
- }
- clearStates (hasErrors) {
- this.states = []
- this.allDone = false
- this.hasErrors = !!hasErrors
- }
- broadcastState () {
- const now = new Date()
- if ((now - this._lastBroadcast > 500) || this.allDone || this.hasErrors) {
- this.sse.broadcast('state', this.state)
- this._lastBroadcast = now
- }
- }
- serveIndex (req, res) {
- const html = this.indexTemplate
- .replace('__STATE__', JSON.stringify(this.state))
- .replace('__OPTIONS__', JSON.stringify(this.options))
- .replace(/__BASE_URL__/g, this.options.baseURL)
- header(res, 'Content-Type', 'text/html')
- end(res, html)
- }
- }
- module.exports = LoadingUI
|