ssr.js 3.84 KB
const express = require('express');
const vm = require('vm');
const fs = require('fs');
const path = require('path');
const MemoryFs = require('memory-fs');
const nativeModule = require('module');


const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');

const clientConfig = require('../build/webpack.client.conf');
const serverConfig = require('../build/webpack.server.conf');
const bundlePath = path.join(serverConfig.output.path, serverConfig.output.filename);
const manifestPath = path.join(clientConfig.output.path, '../../manifest.json');

const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV;

let manifestProd;

if (!isDev) {
    manifestProd = require(manifestPath);
}

const getModuleFromString = (bundle, filename) => {
    const m = { exports: {} };
    const wrapper = nativeModule.wrap(bundle);
    const script = new vm.Script(wrapper, {
        filename,
        displayErrors: true,
    });
    const result = script.runInThisContext();

    result.call(m.exports, m.exports, require, m);

    return m;
}

exports.devServer = (cb) => {
    const app = express();

    let bundle, manifest;
    let resolve;

    let realyPromise = new Promise(r => {
        resolve = r;
    });

    let ready = (...args) => {
        resolve();
        cb(...args);
    };

    if (isDev) {
        const webpack = require('webpack');

        clientConfig.entry.app = ['./build/client-hot.js', clientConfig.entry.app];
        clientConfig.output.publicPath = '//127.0.0.1:6012/';
        clientConfig.plugins.push(
            new webpack.HotModuleReplacementPlugin(),
            new webpack.NoEmitOnErrorsPlugin()
        );
        const clientCompiler = webpack(clientConfig);

        devMiddleware = webpackDevMiddleware(clientCompiler, {
            publicPath: clientConfig.output.publicPath,
            quiet: true,
            headers: {
            'Access-Control-Allow-Origin': '*'
            }
        });

        app.use(devMiddleware);

        clientCompiler.plugin('done', stats => {
            stats = stats.toJson();
            stats.errors.forEach(error => console.log(error));
            stats.warnings.forEach(warning => console.log(warning));
            if (stats.errors.length) {
                return;
            }

            try {
                manifest = JSON.parse(devMiddleware.fileSystem.readFileSync(manifestPath, 'utf-8'));
            } catch (e) {
                console.error('manifest load failure!');
            }

            if (bundle) {
                ready({bundle, manifest});
            }
        });
        hotMiddleware = webpackHotMiddleware(clientCompiler, { heartbeat: 5000 });

        app.use(hotMiddleware);

        const mfs = new MemoryFs();
        const serverCompiler = webpack(serverConfig);

        serverCompiler.outputFileSystem = mfs;
        serverCompiler.watch({}, (err, stats) => {
            if (err) throw err;

            const info = stats.toJson();

            if (stats.hasErrors()) {
                console.log('错误');
                console.error(info.errors);
            }

            if (stats.hasWarnings()) {
                console.log('警告');
                console.warn(info.warnings);
            }

            const bundleSource = mfs.readFileSync(bundlePath, 'utf8');
            const m = getModuleFromString(bundleSource, 'server-entry.js');

            bundle = m.exports;

            if (manifest) {
                ready({bundle, manifest});
            }
        });
    } else {
        const bundleSource = fs.readFileSync(bundlePath, 'utf8');
        const m = getModuleFromString(bundleSource, 'server-entry.js');

        ready({ bundle: m.exports, manifest: manifestProd});
    }

    app.listen(6012, () => {
        console.log('static server is started');
    });

    return realyPromise;
}