"use strict";
/**********************************************************************
 * Copyright (c) 2018-2020 Red Hat, Inc.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 ***********************************************************************/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Production = void 0;
const fs = require("fs-extra");
const glob = require("glob-promise");
const path = require("path");
const cli_error_1 = require("./cli-error");
const command_1 = require("./command");
const logger_1 = require("./logger");
const yarn_1 = require("./yarn");
/**
 * Generates the assembly directory, copying only subset of files and cleaning up some folders
 * @author Florent Benoit
 */
class Production {
    constructor(rootFolder, assemblyFolder, productionDirectory) {
        this.rootFolder = rootFolder;
        this.assemblyFolder = assemblyFolder;
        this.productionDirectory = productionDirectory;
        this.dependencies = [];
        this.toCopyFiles = [];
        this.dependencies = [];
        this.command = new command_1.Command(productionDirectory);
    }
    async create() {
        logger_1.Logger.info('🗂  Get dependencies...');
        // get dependencies
        await this.getDependencies();
        logger_1.Logger.info('🗃  Resolving files...');
        await this.resolveFiles();
        logger_1.Logger.info('✍️  Copying files...');
        await this.copyFiles();
        logger_1.Logger.info('✂️  Cleaning-up files...');
        await this.cleanup();
        logger_1.Logger.info(`🎉  Theia production-ready available in ${this.productionDirectory}.`);
        return path.resolve(this.productionDirectory);
    }
    async copyFiles() {
        const assemblyLength = this.assemblyFolder.length;
        const rootDirLength = this.rootFolder.length;
        await Promise.all(this.toCopyFiles.map(file => {
            let destFile;
            if (file.startsWith(this.assemblyFolder)) {
                destFile = file.substring(assemblyLength);
            }
            else {
                destFile = file.substring(rootDirLength);
            }
            return fs.copy(file, path.join(this.productionDirectory, destFile));
        }));
    }
    async cleanup() {
        const sizeBefore = await this.getSize();
        await this.yarnClean();
        await this.cleanupFind();
        const sizeAfter = await this.getSize();
        logger_1.Logger.info('Removed :' + (sizeBefore - sizeAfter) + ' KiB');
    }
    async getSize() {
        return parseInt(await this.command.exec('du -s -k . | cut -f1'), 10);
    }
    async yarnClean() {
        const yarnCleanFolder = path.resolve(__dirname, '../src/conf');
        const yarnCleanPath = path.join(yarnCleanFolder, '.yarnclean');
        await fs.copy(path.join(this.rootFolder, 'yarn.lock'), path.join(this.productionDirectory, 'yarn.lock'));
        const yarnCleanDestPath = path.join(this.productionDirectory, '.yarnclean');
        await fs.copy(yarnCleanPath, yarnCleanDestPath);
        const output = await this.command.exec('yarn autoclean --force');
        await fs.remove(yarnCleanDestPath);
        logger_1.Logger.info(output);
    }
    async cleanupFind() {
        const cleanupFindFolder = path.resolve(__dirname, '../src/conf');
        const cleanupFindContent = await fs.readFile(path.join(cleanupFindFolder, 'cleanup-find'));
        const command = new command_1.Command(this.productionDirectory);
        await Promise.all(cleanupFindContent
            .toString()
            .split('\n')
            .map(async (line) => {
            if (line.length > 0 && !line.startsWith('#')) {
                await command.exec(`find . -name ${line} | xargs rm -rf {}`);
            }
        }));
    }
    async resolveFiles() {
        // check dependency folders are there
        this.dependencies.forEach(dependency => {
            if (!fs.existsSync(dependency)) {
                throw new cli_error_1.CliError('The dependency ' + dependency + ' is referenced but is not available on the filesystem');
            }
        });
        // ok now, add all files from these dependencies
        const globOptions = { nocase: true, nosort: true, nodir: true, dot: true };
        this.toCopyFiles = this.toCopyFiles.concat.apply([], await Promise.all(this.dependencies.map(dependencyDirectory => glob
            .promise('**', Object.assign(globOptions, { cwd: dependencyDirectory }))
            .then(data => data.map(name => path.join(dependencyDirectory, name))))));
        // add as well the lib folder
        this.toCopyFiles = this.toCopyFiles.concat(await glob
            .promise('lib/**', Object.assign(globOptions, { cwd: this.assemblyFolder }))
            .then(data => data.map(name => path.join(this.assemblyFolder, name))));
        this.toCopyFiles = this.toCopyFiles.concat(await glob
            .promise('src-gen/**', Object.assign(globOptions, { cwd: this.assemblyFolder }))
            .then(data => data.map(name => path.join(this.assemblyFolder, name))));
        this.toCopyFiles = this.toCopyFiles.concat(path.join(this.assemblyFolder, 'package.json'));
        return Promise.resolve(true);
    }
    async getDependencies() {
        this.dependencies = await new yarn_1.Yarn('', Production.ASSEMBLY_DIRECTORY, Production.FORBIDDEN_PACKAGES, Production.EXCLUDED_PACKAGES).getDependencies('@eclipse-che/theia-assembly');
        return Promise.resolve(true);
    }
}
exports.Production = Production;
/**
 * Ensure we've no dependencies to these packages that bring a lot of dependencies !
 */
Production.FORBIDDEN_PACKAGES = ['webpack', 'webpack-cli', '@theia/application-manager'];
/**
 * Remove these client dependencies as they're already bundled with webpack
 */
Production.EXCLUDED_PACKAGES = [
    'electron',
    'react',
    'react-virtualized',
    'onigasm',
    'oniguruma',
    '@theia/monaco',
    'react-dom',
    'font-awesome',
    '@theia/monaco-editor-core',
    'acorn-import-assertions',
];
Production.ASSEMBLY_DIRECTORY = path.resolve('examples/assembly');
//# sourceMappingURL=production.js.map