"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const path = require("path");
const errors_1 = require("../errors");
const npm_1 = require("../npm");
const util_1 = require("../util");
const tar = require('tar'); // typings are pretty bad for this module.
/**
 * Bundler packages up controls from a project directory into a tarball
 * that can be uploaded to Mixer.
 */
class Bundler {
    constructor(project) {
        this.project = project;
    }
    bundle(progressReporter = () => undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            progressReporter('Reading control metadata');
            const config = yield this.project.packageConfig();
            progressReporter('Running webpack bundler');
            const filename = path.join(process.cwd(), `${config.name}.tar.gz`);
            yield this.runWebpack();
            progressReporter('Verifying basic integrity');
            const outputDir = this.getOutputPath();
            yield this.verifyIntegrity(outputDir);
            progressReporter('Compressing files');
            yield this.createTarball(outputDir, filename);
            return { filename, config };
        });
    }
    /**
     * Throws an PackageIntegrityError if there are any obvious problems with
     * the bundler output.
     */
    verifyIntegrity(output) {
        return __awaiter(this, void 0, void 0, function* () {
            const home = path.join(output, 'index.html');
            if (!(yield util_1.exists(home))) {
                throw new errors_1.PackageIntegrityError(`An index.html is missing in your project output (${home} should exist)`);
            }
            const packageJson = this.project.baseDir('package.json');
            if (!(yield util_1.exists(packageJson))) {
                throw new errors_1.PackageIntegrityError(`An package.json is missing in your project (${packageJson} should exist)`);
            }
        });
    }
    /**
     * Compresses the output dir into the target tarball.
     */
    createTarball(output, target) {
        return __awaiter(this, void 0, void 0, function* () {
            const readme = npm_1.findReadme(this.project.baseDir());
            yield Promise.all([
                readme && util_1.copy(this.project.baseDir('readme.md'), path.resolve(output, 'readme.md')),
                util_1.copy(this.project.baseDir('package.json'), path.resolve(output, 'package.json')),
            ]);
            yield tar.create({
                file: target,
                gzip: { level: 9 },
                cwd: output,
            }, yield util_1.readDir(output));
        });
    }
    /**
     * Returns the webpack output path.
     */
    getOutputPath() {
        // tslint:disable-next-line
        return require(this.project.baseDir('webpack.config.js')).output.path;
    }
    /**
     * Runs webpack to create a production bundle.
     */
    runWebpack() {
        return __awaiter(this, void 0, void 0, function* () {
            const wds = npm_1.getPackageExecutable(this.project.baseDir('node_modules', 'webpack'));
            return util_1.awaitChildProcess(child_process_1.fork(wds, ['--display=minimal'], {
                cwd: process.cwd(),
                env: Object.assign({}, process.env, { ENV: 'production' }),
                silent: true,
            }));
        });
    }
}
exports.Bundler = Bundler;
