<commit_before>import * as path from 'path';
import * as glob from 'glob';
import {Promise} from 'es6-promise';
import Generator from '../../models/generator';
import {pluralize} from '../../utils/utils';

export default class BaseGenerator extends Generator {
    options: generator.IOptions;

    protected needsProject: boolean = true;
    protected type: string;
    protected ext: string;
    protected allowExtends: boolean;
    protected allowExtendsWithoutBase: boolean;
    protected fileExtension: boolean;
    protected declaration: boolean;

    constructor(options: any, config: generator.IConfig) {
        super(options);
        this.type = config.type;
        this.ext = config.ext.toLowerCase();
        this.allowExtends = !!config.allowExtends;
        var allowExtendsWithoutBase = config.allowExtendsWithoutBase;

        if (allowExtendsWithoutBase === false) {
            this.allowExtendsWithoutBase = false;
        } else {
            this.allowExtendsWithoutBase = true;
        }

        this.fileExtension = !config.noFileExtension;
        this.declaration = !!config.declaration;

        var type = this.type.toLowerCase();

        this.srcRoot(type);
        this.destRoot(`app/src/${pluralize(type) }`);
    }

    defineOptions(): void {
        this.option('name', {
            aliases: ['n'],
            description: `The name of the ${this.type}`
        });

        this.option('dir', {
            aliases: ['d'],
            description: `Specify the path relative to \`app/src/${pluralize(this.type.toLowerCase()) }\` in which to store the ${this.type}`,
            defaults: ''
        });

        if (this.allowExtends || this.allowExtendsWithoutBase) {
            this.option('extends', {
                aliases: ['x'],
                description: `Specify the relative import path to the ${this.type} to extend`
            });
        }

        if (this.declaration) {
            this.option('declaration', {
                aliases: ['dts'],
                description: 'Do not create a declaration file.',
                defaults: true
            });
        }

        this.option('register', {
            aliases: ['r'],
            description: `Do not register the ${this.type} with the framework`,
            defaults: true
        });
    }

    askQuestions(): any {
        var options = this.options;

        if (this.utils.isString(options.name)) {
            return;
        }

        return this.ui.prompt([
            { name: 'name', type: 'input', message: `What is the name of your ${this.type}?` }
        ]).then((answer: { name: string; }) => {
            options.name = answer.name;
        });
    }

    run(): any {
        var options = this.options,
            name = options.name,
            config: any = {
                name: name,
                type: name,
                register: options.register !== false
            },
            dest: string = this.getDestination();

        var ext = options.extends,
            src = this.ext;

        if (ext === false && this.allowExtends) {
            src = `base.${src}`;
        } else {
            config.ext = this.findExtends(dest).replace(/\\/g, '/').replace(/'/g, '');
        }

        if (this.allowExtends) {
            var root = this.destRoot();

            config.type = path.relative(path.resolve(root), path.resolve(root, dest, '..')).replace(/\\|\//g, '-');
        }

        return this._render(src, dest, config).then(() => {
            return Promise.all([
                this.processMainTs(),
                this.processMainLess()
            ]);
        });
    }

    protected _render(src: string, dest: string, config: any): Thenable<any> {
        var promises = [
            this.render(`${src}.ts`, `${dest}.ts`, config)
        ];

        if (this.declaration && this.options.declaration) {
            var dDest = dest.substring(0, dest.lastIndexOf('/')) + '/i' + dest.substring(dest.lastIndexOf('/') + 1);
            promises.push(this.render(`i${src}.d.ts`, `${dDest}.d.ts`, config));
        }

        return Promise.all(promises);
    }

    protected processMainTs(): Thenable<any> {
        var destRoot = path.resolve(this.destRoot(), '..'),
            root = path.relative(this.project.root, destRoot),
            paths = [
                root + '/app/app.ts',
                root + '/injectables/**/*.ts',
                root + '/attributecontrols/**/*.ts',
                root + '/templatecontrols/**/*.ts'
            ],
            ignore = [
                root + '/injectables/**/*.d.ts',
                root + '/attributecontrols/**/*.d.ts',
                root + '/templatecontrols/**/*.d.ts'
            ];

        var file = path.resolve(destRoot, 'main.ts');

        return Promise.all([
            this.file.read(file),
            this.glob(root, paths, ignore)
        ]).then((results: Array<any>) => {
            var data: string = results[0],
                files: Array<string> = results[1],
                req = `import '`,
                append: Array<string> = [];

            files.forEach((file) => {
                if (data.indexOf(file) === -1) {
                    append.push(`${req}${file}';`);
                }
            });

            if (append.length > 0) {
                append.push('');
            }

            return this.formatMain(data, append);
        }).then((data) => {
            return this.file.write(file, data);
        });
    }

    protected processMainLess(): Thenable<any> {
        var destRoot = path.resolve(this.destRoot(), '..'),
            root = path.relative(this.project.root, destRoot),
            paths = [
                root + '/attributecontrols/**/*.less',
                root + '/templatecontrols/**/*.less',
                root + '/viewcontrols/**/*.less',
            ];

        var file = path.resolve(destRoot, '../styles/main.less');

        return Promise.all([
            this.file.read(file),
            this.glob(root, paths, undefined, '../src/')
        ]).then((results: Array<any>) => {
            var data: string = results[0],
                files: Array<string> = results[1],
                imprt = `@import '`,
                append: Array<string> = [];

            files.forEach((file) => {
                if (data.indexOf(file) === -1) {
                    append.push(`${imprt}${file}';`);
                }
            });

            if (append.length > 0) {
                append.push('');
            }

            return this.formatMain(data, append);
        }).then((data) => {
            return this.file.write(file, data);
        });
    }

    protected glob(root: string, files: Array<string>, ignore: Array<string> = [], replace: string = './'): Thenable<any> {
        if (!this.utils.isArray(ignore)) {
            ignore = [];
        }

        files = files.slice(0);
        var firstFile = files.shift();
        return new Promise<Array<string>>((resolve, reject) => {
            glob(firstFile, {
                ignore: ignore.map((file) => {
                    if (file[0] === '!') {
                        return file.slice(1);
                    }

                    return file;
                })
            }, (err, matches) => {
                var out = matches.map((file) => {
                    return replace + path.relative(root, file).replace(/\\/g, '/').replace(/\.ts$/, '');
                });

                resolve(out);
            });
        }).then((out) => {
            if (files.length > 0) {
                return this.glob(root, files, ignore, replace).then((result) => {
                    return out.concat(result);
                });
            }

            return out;
        });
    }

    protected getDestination(): string {
        var options = this.options,
            name = options.name,
            dir = options.dir,
            dest: string;

        if (this.utils.isEmpty(dir)) {
            dest = name;
        } else {
            if (dir[dir.length - 1] === '/') {
                dir = dir.slice(0, -1);
            }

            dest = dir + '/' + name;
        }

        dest += `/${name}`;

        if (this.fileExtension) {
            dest += `.${this.ext}`;
        }

        return dest.toLowerCase();
    }

    protected findExtends(root: string): string {
        var ext: string = this.options.extends;

        if (this.utils.isString(ext)) {
            return ext;
        }

        if (!this.allowExtends && this.allowExtendsWithoutBase) {
            return '';
        }

        var dest = this.destRoot();

        return path.relative(path.resolve(dest, root, '..'), path.resolve(dest, `base/base.${this.ext}`));
    }

    private formatMain(data: string, append: Array<string>): string {
        var eol = this.file.eol(data),
            appFound: boolean = false,
            acFound: boolean = false,
            injFound: boolean = false,
            tcFound: boolean = false;

        var sorted = this.sortMain(data.split(eol)
            .concat(append)
            .filter((value) => {
                return !this.utils.isEmpty(value.trim());
            }))
            .map((value, index, lines) => {
                if (value.indexOf('/app/') > -1 && !appFound) {
                    appFound = true;
                    value = eol + value;
                } else if (value.indexOf('/attributecontrols') > -1 && !acFound) {
                    acFound = true;
                    value = eol + value;
                } else if (value.indexOf('/injectables') > -1 && !injFound) {
                    injFound = true;
                    value = eol + value;
                } else if (value.indexOf('/templatecontrols') > -1 && !tcFound) {
                    tcFound = true;
                    value = eol + value;
                }

                return value;
            })
            .concat(['']);

        if (sorted[sorted.length - 2].trim() === '' && sorted[sorted.length - 1].trim() === '') {
            sorted.pop();
        }

        return sorted.join(eol)
    }

    private sortMain(lines: Array<string>): Array<string> {
        var imports: Array<string> = [],
            others: Array<string> = [],
            trim: string;

        lines.forEach((line) => {
            trim = line.trim();
            if (trim.indexOf('require') === 0 || trim.indexOf('@import') === 0) {
                imports.push(line);
            } else {
                others.push(line);
            }
        });

        if (others[0] !== '') {
            others.unshift('');
        }

        return imports.sort(this.sort.bind(this)).concat(others);
    }
<commit_msg>fixing lint errors<commit_after>import * as path from 'path';
import * as glob from 'glob';
import {Promise} from 'es6-promise';
import Generator from '../../models/generator';
import {pluralize} from '../../utils/utils';

export default class BaseGenerator extends Generator {
    options: generator.IOptions;

    protected needsProject: boolean = true;
    protected type: string;
    protected ext: string;
    protected allowExtends: boolean;
    protected allowExtendsWithoutBase: boolean;
    protected fileExtension: boolean;
    protected declaration: boolean;

    constructor(options: any, config: generator.IConfig) {
        super(options);
        this.type = config.type;
        this.ext = config.ext.toLowerCase();
        this.allowExtends = !!config.allowExtends;
        var allowExtendsWithoutBase = config.allowExtendsWithoutBase;

        if (allowExtendsWithoutBase === false) {
            this.allowExtendsWithoutBase = false;
        } else {
            this.allowExtendsWithoutBase = true;
        }

        this.fileExtension = !config.noFileExtension;
        this.declaration = !!config.declaration;

        var type = this.type.toLowerCase();

        this.srcRoot(type);
        this.destRoot(`app/src/${pluralize(type) }`);
    }

    defineOptions(): void {
        this.option('name', {
            aliases: ['n'],
            description: `The name of the ${this.type}`
        });

        this.option('dir', {
            aliases: ['d'],
            description: `Specify the path relative to \`app/src/${pluralize(this.type.toLowerCase()) }\` in which to store the ${this.type}`,
            defaults: ''
        });

        if (this.allowExtends || this.allowExtendsWithoutBase) {
            this.option('extends', {
                aliases: ['x'],
                description: `Specify the relative import path to the ${this.type} to extend`
            });
        }

        if (this.declaration) {
            this.option('declaration', {
                aliases: ['dts'],
                description: 'Do not create a declaration file.',
                defaults: true
            });
        }

        this.option('register', {
            aliases: ['r'],
            description: `Do not register the ${this.type} with the framework`,
            defaults: true
        });
    }

    askQuestions(): any {
        var options = this.options;

        if (this.utils.isString(options.name)) {
            return;
        }

        return this.ui.prompt([
            { name: 'name', type: 'input', message: `What is the name of your ${this.type}?` }
        ]).then((answer: { name: string; }) => {
            options.name = answer.name;
        });
    }

    run(): any {
        var options = this.options,
            name = options.name,
            config: any = {
                name: name,
                type: name,
                register: options.register !== false
            },
            dest: string = this.getDestination();

        var ext = options.extends,
            src = this.ext;

        if (ext === false && this.allowExtends) {
            src = `base.${src}`;
        } else {
            config.ext = this.findExtends(dest).replace(/\\/g, '/').replace(/'/g, '');
        }

        if (this.allowExtends) {
            var root = this.destRoot();

            config.type = path.relative(path.resolve(root), path.resolve(root, dest, '..')).replace(/\\|\//g, '-');
        }

        return this._render(src, dest, config).then(() => {
            return Promise.all([
                this.processMainTs(),
                this.processMainLess()
            ]);
        });
    }

    protected _render(src: string, dest: string, config: any): Thenable<any> {
        var promises = [
            this.render(`${src}.ts`, `${dest}.ts`, config)
        ];

        if (this.declaration && this.options.declaration) {
            var dDest = dest.substring(0, dest.lastIndexOf('/')) + '/i' + dest.substring(dest.lastIndexOf('/') + 1);
            promises.push(this.render(`i${src}.d.ts`, `${dDest}.d.ts`, config));
        }

        return Promise.all(promises);
    }

    protected processMainTs(): Thenable<any> {
        var destRoot = path.resolve(this.destRoot(), '..'),
            root = path.relative(this.project.root, destRoot),
            paths = [
                root + '/app/app.ts',
                root + '/injectables/**/*.ts',
                root + '/attributecontrols/**/*.ts',
                root + '/templatecontrols/**/*.ts'
            ],
            ignore = [
                root + '/injectables/**/*.d.ts',
                root + '/attributecontrols/**/*.d.ts',
                root + '/templatecontrols/**/*.d.ts'
            ];

        var file = path.resolve(destRoot, 'main.ts');

        return Promise.all([
            this.file.read(file),
            this.glob(root, paths, ignore)
        ]).then((results: Array<any>) => {
            var data: string = results[0],
                files: Array<string> = results[1],
                req = `import '`,
                append: Array<string> = [];

            files.forEach((file) => {
                if (data.indexOf(file) === -1) {
                    append.push(`${req}${file}';`);
                }
            });

            if (append.length > 0) {
                append.push('');
            }

            return this.formatMain(data, append);
        }).then((data) => {
            return this.file.write(file, data);
        });
    }

    protected processMainLess(): Thenable<any> {
        var destRoot = path.resolve(this.destRoot(), '..'),
            root = path.relative(this.project.root, destRoot),
            paths = [
                root + '/attributecontrols/**/*.less',
                root + '/templatecontrols/**/*.less',
                root + '/viewcontrols/**/*.less',
            ];

        var file = path.resolve(destRoot, '../styles/main.less');

        return Promise.all([
            this.file.read(file),
            this.glob(root, paths, undefined, '../src/')
        ]).then((results: Array<any>) => {
            var data: string = results[0],
                files: Array<string> = results[1],
                imprt = `@import '`,
                append: Array<string> = [];

            files.forEach((file) => {
                if (data.indexOf(file) === -1) {
                    append.push(`${imprt}${file}';`);
                }
            });

            if (append.length > 0) {
                append.push('');
            }

            return this.formatMain(data, append);
        }).then((data) => {
            return this.file.write(file, data);
        });
    }

    protected glob(root: string, files: Array<string>, ignore: Array<string> = [], replace: string = './'): Thenable<any> {
        if (!this.utils.isArray(ignore)) {
            ignore = [];
        }

        files = files.slice(0);
        var firstFile = files.shift();
        return new Promise<Array<string>>((resolve, reject) => {
            glob(firstFile, {
                ignore: ignore.map((file) => {
                    if (file[0] === '!') {
                        return file.slice(1);
                    }

                    return file;
                })
            }, (err, matches) => {
                var out = matches.map((file) => {
                    return replace + path.relative(root, file).replace(/\\/g, '/').replace(/\.ts$/, '');
                });

                resolve(out);
            });
        }).then((out) => {
            if (files.length > 0) {
                return this.glob(root, files, ignore, replace).then((result) => {
                    return out.concat(result);
                });
            }

            return out;
        });
    }

    protected getDestination(): string {
        var options = this.options,
            name = options.name,
            dir = options.dir,
            dest: string;

        if (this.utils.isEmpty(dir)) {
            dest = name;
        } else {
            if (dir[dir.length - 1] === '/') {
                dir = dir.slice(0, -1);
            }

            dest = dir + '/' + name;
        }

        dest += `/${name}`;

        if (this.fileExtension) {
            dest += `.${this.ext}`;
        }

        return dest.toLowerCase();
    }

    protected findExtends(root: string): string {
        var ext: string = this.options.extends;

        if (this.utils.isString(ext)) {
            return ext;
        }

        if (!this.allowExtends && this.allowExtendsWithoutBase) {
            return '';
        }

        var dest = this.destRoot();

        return path.relative(path.resolve(dest, root, '..'), path.resolve(dest, `base/base.${this.ext}`));
    }

    private formatMain(data: string, append: Array<string>): string {
        var eol = this.file.eol(data),
            appFound: boolean = false,
            acFound: boolean = false,
            injFound: boolean = false,
            tcFound: boolean = false;

        var sorted = this.sortMain(data.split(eol)
            .concat(append)
            .filter((value) => {
                return !this.utils.isEmpty(value.trim());
            }))
            .map((value, index, lines) => {
                if (value.indexOf('/app/') > -1 && !appFound) {
                    appFound = true;
                    value = eol + value;
                } else if (value.indexOf('/attributecontrols') > -1 && !acFound) {
                    acFound = true;
                    value = eol + value;
                } else if (value.indexOf('/injectables') > -1 && !injFound) {
                    injFound = true;
                    value = eol + value;
                } else if (value.indexOf('/templatecontrols') > -1 && !tcFound) {
                    tcFound = true;
                    value = eol + value;
                }

                return value;
            })
            .concat(['']);

        if (sorted[sorted.length - 2].trim() === '' && sorted[sorted.length - 1].trim() === '') {
            sorted.pop();
        }

        return sorted.join(eol);
    }

    private sortMain(lines: Array<string>): Array<string> {
        var imports: Array<string> = [],
            others: Array<string> = [],
            trim: string;

        lines.forEach((line) => {
            trim = line.trim();
            if (trim.indexOf('require') === 0 || trim.indexOf('@import') === 0) {
                imports.push(line);
            } else {
                others.push(line);
            }
        });

        if (others[0] !== '') {
            others.unshift('');
        }

        return imports.sort(this.sort.bind(this)).concat(others);
    }

{'fraction_non_alphanumeric': 0.15488700827675997, 'fraction_numerical': 0.00251092718311169, 'mean_word_length': 1.473490511788384, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3719295', 'n_tokens_mistral': 5993, 'n_tokens_neox': 5762, 'n_words': 1210}