@platform/react.ssr
Version:
A lightweight SSR (server-side-rendering) system for react apps bundled with ParcelJS and hosted on S3.
112 lines (111 loc) • 3.93 kB
JavaScript
import { bundler } from '../bundler';
import { Config } from '../config';
import { exec, fs, log, npm } from './common';
import * as push from './cmd.push';
export async function run(args) {
const { cli } = args;
const config = args.config || (await Config.create());
const { endpoint } = config.s3;
let manifest;
await fs.ensureDir(config.builder.bundles);
const pkgPath = await fs.ancestor(config.builder.bundles).first('package.json');
const pkg = npm.pkg(pkgPath);
log.info.gray(fs.dirname(pkgPath));
log.info();
const version = args.version ||
(await npm.prompt.incrementVersion({ path: pkgPath, noChange: true, save: true }));
const bundleDir = fs.resolve(fs.join(config.builder.bundles, version));
const isPush = args.push !== undefined
? args.push
: (await cli.prompt.list({ message: 'push to S3', items: ['yes', 'no'] })) === 'yes';
if (isPush && !endpoint) {
throw new Error(`The S3 endpoint has not been configured in [ssr.yml].`);
}
log.info();
const tasks = cli
.task('build', async (e) => execScript(pkg, e, 'build'), { skip: args.manifest })
.task('bundle', async (e) => execScript(pkg, e, 'bundle'), { skip: args.manifest })
.task('manifest', async (e) => {
const { entries, error } = await getEntries(config);
if (error) {
throw error;
}
else {
const res = await bundler.prepare({ bundleDir, entries, silent: true });
manifest = res.manifest;
}
});
const res = await tasks.run({ concurrent: false, exitOnError: true });
if (!res.ok) {
log.info();
res.errors.forEach(item => {
log.error(`ERROR ${item.title}`);
log.warn(item.error);
log.info();
});
return cli.exit(1);
}
if (isPush) {
await push.bundle({ cli, config, version });
}
else if (manifest) {
bundler.log.bundle({ bundleDir, manifest });
}
log.info();
}
async function execScript(pkg, e, scriptName) {
const exists = Boolean(pkg.scripts.bundle);
if (!exists) {
e.error(`Package.json does not have a "${scriptName}" script.`);
return;
}
const cwd = pkg.dir;
const res = await exec.command(`yarn ${scriptName}`).run({ cwd, silent: true });
if (!res.ok) {
throw new Error(`Failed while executing '${scriptName}' script of package '${pkg.name}'`);
}
}
const getRootDir = async (source) => {
let path = '';
await fs.ancestor(source).walk(async (e) => {
if ((await fs.readdir(e.dir)).includes('package.json')) {
return e.stop();
}
else {
path = e.dir;
}
});
return path;
};
const getEntries = async (config) => {
const done = (entries, errorMessage) => {
const error = errorMessage ? new Error(errorMessage) : undefined;
return { ok: !Boolean(error), entries, error };
};
let source = config.builder.entries;
if (!source) {
return done([]);
}
source = fs.resolve(source);
const sourceRoot = await getRootDir(source);
const sourcePath = source.substring(sourceRoot.length);
const localRoot = fs.resolve(`tmp/${fs.basename(sourceRoot)}`);
const localPath = fs.join(localRoot, sourcePath);
await fs.ensureDir(fs.dirname(localRoot));
await fs.remove(localRoot);
await fs.copy(sourceRoot, localRoot);
const err = `Failed to load bundle entries at path: ${source}.`;
try {
const res = require(localPath);
if (Array.isArray(res.default)) {
return done(res.default);
}
return done([], `${err} Ensure the array is exported as the module default.`);
}
catch (error) {
return done([], `${err} ${error.message}`);
}
finally {
await fs.remove(fs.resolve('tmp'));
}
};