"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addProvider = exports.addProviderDecorator = exports.addSymbolToDecoratorMetadata = exports.prettierConstructorParameters = exports.addImport = exports.getTsSourceFile = void 0;
const schematics_1 = require("@angular-devkit/schematics");
const ts = require("typescript");
const vendored_ast_utils_1 = require("./vendored-ast-utils");
const change_1 = require("@schematics/angular/utility/change");
const schematics_2 = require("@angular/cdk/schematics");
function getTsSourceFile(host, path) {
    const buffer = host.read(path);
    if (!buffer) {
        throw new schematics_1.SchematicsException(`Could not read file (${path}).`);
    }
    const content = buffer.toString();
    const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
    return source;
}
exports.getTsSourceFile = getTsSourceFile;
function addImport(host, filePath, importModule, path) {
    let importModules;
    if (typeof importModule === 'string') {
        importModules = [importModule];
    }
    else {
        importModules = importModule;
    }
    // add import theme
    importModules.forEach((val) => {
        const fileSource = getTsSourceFile(host, filePath);
        const importPath = path;
        if (!vendored_ast_utils_1.isImported(fileSource, val, importPath)) {
            const change = vendored_ast_utils_1.insertImport(fileSource, filePath, val, importPath);
            if (change) {
                const recorder = host.beginUpdate(filePath);
                recorder.insertLeft(change.pos, change.toAdd);
                host.commitUpdate(recorder);
            }
        }
    });
}
exports.addImport = addImport;
function prettierConstructorParameters(host, filePath, constructor) {
    const parenToken = constructor.getChildren()
        .filter(prop => prop.kind === ts.SyntaxKind.OpenParenToken || prop.kind === ts.SyntaxKind.CloseParenToken);
    // .map(prop => prop.getFullText().trim());
    const buffer = host.read(filePath);
    if (buffer === null) {
        throw new schematics_1.SchematicsException(`Could not read index file: ${filePath}`);
    }
    const syntaxList = constructor.getChildren()
        .filter(prop => prop.kind === ts.SyntaxKind.SyntaxList)[0];
    const contructorBefore = `${parenToken[0].getFullText()}${syntaxList.getFullText()}${parenToken[1].getFullText()}`;
    const parameters = syntaxList.getChildren()
        .filter(prop => prop.kind === ts.SyntaxKind.Parameter)
        .map((parameter, index) => {
        const param = parameter.getText();
        let comment = parameter.getFullText().replace(param, '').trim();
        if (comment) {
            comment += `\n`;
            if (index === 0) {
                comment += `\n${comment}`;
            }
        }
        return `${comment}${param}`;
    });
    const parametersStr = parameters.join(`,\n${` `.repeat(14)}`);
    const result = `${parenToken[0].getFullText().trim()}${parametersStr}${parenToken[1].getFullText().trim()}`;
    host.overwrite(filePath, buffer.toString().replace(contructorBefore, result));
}
exports.prettierConstructorParameters = prettierConstructorParameters;
function addSymbolToDecoratorMetadata(source, ngModulePath, decorator, metadataField, symbolName, importPath = null) {
    const nodes = schematics_2.getDecoratorMetadata(source, decorator, '@angular/core');
    let node = nodes[0]; // tslint:disable-line:no-any
    // Find the decorator declaration.
    if (!node) {
        return [];
    }
    // Get all the children property assignment of object literals.
    const matchingProperties = schematics_2.getMetadataField(node, metadataField);
    // Get the last node of the array literal.
    if (!matchingProperties) {
        return [];
    }
    // tslint:disable-next-line: triple-equals
    if (matchingProperties.length == 0) {
        // We haven't found the field in the metadata declaration. Insert a new field.
        const expr = node;
        // tslint:disable-next-line: no-shadowed-variable
        let position;
        // tslint:disable-next-line: no-shadowed-variable
        let toInsert;
        // tslint:disable-next-line: triple-equals
        if (expr.properties.length == 0) {
            position = expr.getEnd() - 1;
            toInsert = `  ${metadataField}: [${symbolName}]\n`;
        }
        else {
            node = expr.properties[expr.properties.length - 1];
            position = node.getEnd();
            // Get the indentation of the last element, if any.
            const text = node.getFullText(source);
            const matches = text.match(/^\r?\n\s*/);
            if (matches && matches.length > 0) {
                toInsert = `,${matches[0]}${metadataField}: [${symbolName}]`;
            }
            else {
                toInsert = `, ${metadataField}: [${symbolName}]`;
            }
        }
        if (importPath !== null) {
            return [
                new change_1.InsertChange(ngModulePath, position, toInsert),
                vendored_ast_utils_1.insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
            ];
        }
        else {
            return [new change_1.InsertChange(ngModulePath, position, toInsert)];
        }
    }
    const assignment = matchingProperties[0];
    // If it's not an array, nothing we can do really.
    if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
        return [];
    }
    const arrLiteral = assignment.initializer;
    // tslint:disable-next-line: triple-equals
    if (arrLiteral.elements.length == 0) {
        // Forward the property.
        node = arrLiteral;
    }
    else {
        node = arrLiteral.elements;
    }
    if (!node) {
        // tslint:disable-next-line: no-console
        console.error('No app module found. Please add your new class to your component.');
        return [];
    }
    if (Array.isArray(node)) {
        const nodeArray = node;
        // tslint:disable-next-line: no-shadowed-variable
        const symbolsArray = nodeArray.map(node => node.getText());
        if (symbolsArray.includes(symbolName)) {
            return [];
        }
        node = node[node.length - 1];
    }
    let toInsert;
    let position = node.getEnd();
    // tslint:disable-next-line: triple-equals
    if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
        // We haven't found the field in the metadata declaration. Insert a new
        // field.
        const expr = node;
        // tslint:disable-next-line: triple-equals
        if (expr.properties.length == 0) {
            position = expr.getEnd() - 1;
            toInsert = `  ${symbolName}\n`;
        }
        else {
            // Get the indentation of the last element, if any.
            const text = node.getFullText(source);
            if (text.match(/^\r?\r?\n/)) {
                toInsert = `,${text.match(/^\r?\n\s*/)[0]}${symbolName}`;
            }
            else {
                toInsert = `, ${symbolName}`;
            }
        }
        // tslint:disable-next-line: triple-equals
    }
    else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
        // We found the field but it's empty. Insert it just before the `]`.
        position--;
        toInsert = `${symbolName}`;
    }
    else {
        // Get the indentation of the last element, if any.
        const text = node.getFullText(source);
        if (text.match(/^\r?\n/)) {
            toInsert = `,${text.match(/^\r?\n(\r?)\s*/)[0]}${symbolName}`;
        }
        else {
            toInsert = `, ${symbolName}`;
        }
    }
    if (importPath !== null) {
        return [
            new change_1.InsertChange(ngModulePath, position, toInsert),
            vendored_ast_utils_1.insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
        ];
    }
    return [new change_1.InsertChange(ngModulePath, position, toInsert)];
}
exports.addSymbolToDecoratorMetadata = addSymbolToDecoratorMetadata;
/**
 * Custom function to insert a provider into Decorator. It also imports it.
 */
function addProviderDecorator(source, modulePath, decorator, classifiedName, importPath) {
    return addSymbolToDecoratorMetadata(source, modulePath, decorator, 'providers', classifiedName, importPath);
}
exports.addProviderDecorator = addProviderDecorator;
function addProvider(host, modulePath, decorator, classifiedName, src) {
    const moduleSource = schematics_2.parseSourceFile(host, modulePath);
    if (!moduleSource) {
        throw new schematics_1.SchematicsException(`Module not found: ${modulePath}`);
    }
    const changes = addProviderDecorator(moduleSource, modulePath, decorator, classifiedName, src);
    const recorder = host.beginUpdate(modulePath);
    changes.forEach((change) => {
        if (change instanceof change_1.InsertChange) {
            recorder.insertLeft(change.pos, change.toAdd);
        }
    });
    host.commitUpdate(recorder);
}
exports.addProvider = addProvider;
//# sourceMappingURL=ast.js.map