cubic-ui
Version:
UI node for Cubic.
156 lines (138 loc) • 4.95 kB
JavaScript
const fs = require('fs')
const { promisify } = require('util')
const path = require('path')
class WebpackServer {
constructor () {
this.config = {
client: require(cubic.config.ui.webpack.clientConfig),
server: require(cubic.config.ui.webpack.serverConfig)
}
this.init()
}
/**
* Entrypoint for hook on cubic-core
*/
async init () {
await this.registerEndpoints()
if (!cubic.config.ui.webpack.skipBuild) {
await this.initBuild()
}
}
/**
* Register routes in vue-router file. It can't be done at runtime, so
* we gotta ensure the file is ready before rendering anything.
*/
async registerEndpoints () {
const routes = await this.getViewConstants()
const writeFile = promisify(fs.writeFile)
let routeOutput = `/**
* Auto-generated routes from cubic view node. We can't
* get them at runtime, so we need to save them like a config
* file pre-build.
*/
export default ${JSON.stringify(routes, null, 2)}
`
// Lazy cleanup for stringified functions
/* eslint no-useless-escape: "off" */
routeOutput = routeOutput.replace(/"\(\) \=\>/g, '() =>').replace(/`\)"/g, '`)')
// Save to file
await writeFile(path.join(__dirname, '../vue/router/routes.js'), routeOutput)
}
/**
* Generate plaintext constants which will be saved in the router file.
*/
async getViewConstants () {
const endpoints = cubic.nodes.ui.api.server.http.endpoints.endpoints
let routes = []
for (const endpoint of endpoints) {
if (!endpoint.view && !endpoint.custom) continue
let component
if (endpoint.view) {
component = `() => import(\`src/${endpoint.view}\`)`
} else {
component = `() => import(\`${endpoint.custom}\`)`
}
routes.push({
path: endpoint.route,
component,
props: true
})
}
return routes
}
/**
* Initialize Webpack here if we're in production. Production assumes that
* core workers and API nodes are on different machines. During develop-
* ment we'll run webpack from an API middleware, because hot module
* replacement requires a webpack instance on the same process.
*/
async initBuild () {
cubic.log.monitor('Started Webpack build process. This may take a while...', true, '')
if (cubic.config.local.environment === 'production') {
this.done = await this.initWebpackProd()
} else {
this.done = await this.initWebpackDev()
}
}
/**
* Run webpack locally, assuming production environment.
*/
async initWebpackProd () {
const webpack = require('webpack')
const timer = new Date()
const build = webpack([this.config.client, this.config.server])
const compile = promisify(build.run).bind(build)
await compile()
cubic.log.monitor('Webpack build successful', true, `${new Date() - timer}ms`)
}
/**
* Hook HMR middleware into API node and bundle from there
*/
async initWebpackDev () {
const DevMiddleware = require('webpack-dev-middleware')
const HotMiddleware = require('webpack-hot-middleware')
const mkdirp = require('mkdirp')
const webpack = require('webpack')
const publicPath = this.config.client.output.path
const readFile = (mfs, file) => mfs.readFileSync(path.join(publicPath, file), 'utf-8')
const copyFile = (mfs, file) => promisify(fs.writeFile)(path.join(publicPath, file), readFile(mfs, file))
mkdirp(publicPath)
// Modify client config to work with hot middleware
this.addHmrPlugins()
const compiler = webpack([this.config.client, this.config.server])
const devMiddleware = DevMiddleware(compiler, {
logLevel: 'warn',
stats: 'errors-only',
noInfo: true,
publicPath,
watchOptions: { aggregateTimeout: 0 }
})
const hotMiddleware = HotMiddleware(compiler, { heartbeat: 100 })
this.addMiddleware(devMiddleware)
this.addMiddleware(hotMiddleware)
compiler.plugin('done', stats => {
stats = stats.toJson()
if (stats.errors.length) {
console.error(stats.error)
}
stats.children.forEach(bundle => {
bundle.assets.forEach(asset => {
if (asset.name.includes('bundle') || asset.name.includes('manifest')) {
copyFile(devMiddleware.fileSystem, asset.name)
}
})
})
})
}
addHmrPlugins () {
const webpack = require('webpack')
this.config.client.entry.client = ['webpack-hot-middleware/client', this.config.client.entry.client]
this.config.client.output.filename = 'dev-[name].bundle.js'
this.config.client.plugins.push(new webpack.HotModuleReplacementPlugin())
}
addMiddleware (middleware) {
const server = cubic.nodes.ui.api.server
server.http.app.wares.unshift(middleware)
}
}
module.exports = WebpackServer